mirror of
https://github.com/laurent22/joplin.git
synced 2024-12-24 10:27:10 +02:00
synchronizer
This commit is contained in:
parent
43f2c6c756
commit
fe277e0cac
@ -171,10 +171,12 @@ class BaseModel {
|
||||
}
|
||||
}
|
||||
}
|
||||
}).then(() => {
|
||||
}).then((r) => {
|
||||
o = Object.assign({}, o);
|
||||
o.id = query.id;
|
||||
return o;
|
||||
}).catch((error) => {
|
||||
Log.error('Cannot save model', error);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -58,6 +58,10 @@ class LoginScreenComponent extends React.Component {
|
||||
this.props.dispatch({
|
||||
type: 'Navigation/BACK',
|
||||
});
|
||||
|
||||
Registry.api().setSession(session.id);
|
||||
|
||||
Registry.synchronizer().start();
|
||||
}).catch((error) => {
|
||||
this.setState({ errorMessage: _('Could not login: %s)', error.message) });
|
||||
});
|
||||
|
@ -117,7 +117,7 @@ class Database {
|
||||
}
|
||||
|
||||
open() {
|
||||
this.db_ = SQLite.openDatabase({ name: '/storage/emulated/0/Download/joplin-21.sqlite' }, (db) => {
|
||||
this.db_ = SQLite.openDatabase({ name: '/storage/emulated/0/Download/joplin-26.sqlite' }, (db) => {
|
||||
Log.info('Database was open successfully');
|
||||
}, (error) => {
|
||||
Log.error('Cannot open database: ', error);
|
||||
@ -333,6 +333,29 @@ class Database {
|
||||
|
||||
Log.info(this.tableFields_);
|
||||
});
|
||||
|
||||
|
||||
|
||||
// }).then(() => {
|
||||
// let p = this.exec('DELETE FROM notes').then(() => {
|
||||
// return this.exec('DELETE FROM folders');
|
||||
// }).then(() => {
|
||||
// return this.exec('DELETE FROM changes');
|
||||
// }).then(() => {
|
||||
// return this.exec('DELETE FROM settings WHERE `key` = "sync.lastRevId"');
|
||||
// });
|
||||
|
||||
// return p.then(() => {
|
||||
// return this.exec('UPDATE settings SET `value` = "' + uuid.create() + '" WHERE `key` = "clientId"');
|
||||
// }).then(() => {
|
||||
// return this.exec('DELETE FROM settings WHERE `key` != "clientId"');
|
||||
// });
|
||||
|
||||
// return p;
|
||||
|
||||
|
||||
|
||||
|
||||
}).catch((error) => {
|
||||
if (error && error.code != 0) {
|
||||
Log.error(error);
|
||||
|
@ -33,6 +33,15 @@ class Registry {
|
||||
return this.db_;
|
||||
}
|
||||
|
||||
static setSynchronizer(s) {
|
||||
this.synchronizer_ = s;
|
||||
}
|
||||
|
||||
static synchronizer() {
|
||||
if (!this.synchronizer_) throw new Error('Accessing synchronizer before it has been initialised');
|
||||
return this.synchronizer_;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Registry };
|
@ -164,7 +164,7 @@ class AppComponent extends React.Component {
|
||||
componentDidMount() {
|
||||
let db = new Database();
|
||||
//db.setDebugEnabled(Registry.debugMode());
|
||||
db.setDebugEnabled(false);
|
||||
db.setDebugEnabled(true);
|
||||
|
||||
BaseModel.dispatch = this.props.dispatch;
|
||||
BaseModel.db_ = db;
|
||||
@ -199,6 +199,7 @@ class AppComponent extends React.Component {
|
||||
});
|
||||
}).then(() => {
|
||||
let synchronizer = new Synchronizer(db, Registry.api());
|
||||
Registry.setSynchronizer(synchronizer);
|
||||
synchronizer.start();
|
||||
}).catch((error) => {
|
||||
Log.error('Initialization error:', error);
|
||||
|
@ -27,12 +27,20 @@ class Synchronizer {
|
||||
return this.api_;
|
||||
}
|
||||
|
||||
switchState(state) {
|
||||
Log.info('Sync: switching state to: ' + state);
|
||||
processState(state) {
|
||||
// if (this.state() == state) {
|
||||
// Log.info('Sync: cannot switch to same state: ' + state);
|
||||
// return;
|
||||
// }
|
||||
|
||||
Log.info('Sync: processing: ' + state);
|
||||
this.state_ = state;
|
||||
|
||||
if (state == 'downloadChanges') {
|
||||
let maxRevId = null;
|
||||
let hasMore = false;
|
||||
this.api().get('synchronizer', { last_id: Setting.value('sync.lastRevId') }).then((syncOperations) => {
|
||||
hasMore = syncOperations.has_more;
|
||||
let chain = [];
|
||||
for (let i = 0; i < syncOperations.items.length; i++) {
|
||||
let syncOp = syncOperations.items[i];
|
||||
@ -57,6 +65,7 @@ class Synchronizer {
|
||||
if (syncOp.type == 'update') {
|
||||
chain.push(() => {
|
||||
return ItemClass.load(syncOp.item_id).then((item) => {
|
||||
if (!item) return;
|
||||
item = ItemClass.applyPatch(item, syncOp.item);
|
||||
return ItemClass.save(item, { trackChanges: false });
|
||||
});
|
||||
@ -71,13 +80,17 @@ class Synchronizer {
|
||||
}
|
||||
return promiseChain(chain);
|
||||
}).then(() => {
|
||||
Log.info('All items synced.');
|
||||
Log.info('All items synced. has_more = ', hasMore);
|
||||
if (maxRevId) {
|
||||
Setting.setValue('sync.lastRevId', maxRevId);
|
||||
return Setting.saveAll();
|
||||
}
|
||||
}).then(() => {
|
||||
this.switchState('uploadingChanges');
|
||||
if (hasMore) {
|
||||
this.processState('downloadChanges');
|
||||
} else {
|
||||
this.processState('uploadingChanges');
|
||||
}
|
||||
}).catch((error) => {
|
||||
Log.warn('Sync error', error);
|
||||
});
|
||||
@ -112,11 +125,13 @@ class Synchronizer {
|
||||
return this.api().patch(path + '/' + item.id, null, item);
|
||||
});
|
||||
} else if (c.type == Change.TYPE_DELETE) {
|
||||
return this.api().delete(path + '/' + c.item_id);
|
||||
p = this.api().delete(path + '/' + c.item_id);
|
||||
}
|
||||
|
||||
return p.then(() => {
|
||||
processedChangeIds = processedChangeIds.concat(c.ids);
|
||||
}).catch((error) => {
|
||||
Log.warn('Failed applying changes', c.ids);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -142,7 +157,7 @@ class Synchronizer {
|
||||
return;
|
||||
}
|
||||
|
||||
this.switchState('downloadChanges');
|
||||
this.processState('downloadChanges');
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -45,10 +45,12 @@ class FoldersController extends ApiController {
|
||||
}
|
||||
|
||||
if ($request->isMethod('PUT')) {
|
||||
if (!$folder) $folder = new Folder();
|
||||
$isNew = !$folder;
|
||||
if ($isNew) $folder = new Folder();
|
||||
$folder->fromPublicArray($this->putParameters());
|
||||
$folder->id = Folder::unhex($id);
|
||||
$folder->owner_id = $this->user()->id;
|
||||
$folder->setIsNew($isNew);
|
||||
$folder->save();
|
||||
return static::successResponse($folder);
|
||||
}
|
||||
|
@ -37,10 +37,12 @@ class NotesController extends ApiController {
|
||||
}
|
||||
|
||||
if ($request->isMethod('PUT')) {
|
||||
if (!$note) $note = new Note();
|
||||
$isNew = !$note;
|
||||
if ($isNew) $note = new Note();
|
||||
$note->fromPublicArray($this->putParameters());
|
||||
$note->id = Note::unhex($id);
|
||||
$note->owner_id = $this->user()->id;
|
||||
$note->setIsNew($isNew);
|
||||
$note->save();
|
||||
return static::successResponse($note);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ class Change extends BaseModel {
|
||||
// - If update, update, delete, update => return 'delete' only
|
||||
// - If update, update, update => return last
|
||||
|
||||
// $limit = 10000;
|
||||
$limit = 100;
|
||||
$changes = self::where('id', '>', $fromChangeId)
|
||||
->where('user_id', '=', $userId)
|
||||
@ -46,8 +47,12 @@ class Change extends BaseModel {
|
||||
}
|
||||
|
||||
$itemIdToChange[$change->item_id] = $change;
|
||||
|
||||
// echo BaseModel::hex($change->item_id) . ' ' . $change->id . ' ' . Change::enumName('type', $change->type) . "\n";
|
||||
}
|
||||
|
||||
// die();
|
||||
|
||||
|
||||
$output = array();
|
||||
foreach ($itemIdToChange as $itemId => $change) {
|
||||
@ -68,11 +73,23 @@ class Change extends BaseModel {
|
||||
$syncItem['type'] = 'delete';
|
||||
} else if (in_array($itemId, $createdItems)) {
|
||||
// Item was created then updated - just return one 'create' event with the latest changes
|
||||
$syncItem['type'] = 'create';
|
||||
$syncItem['item'] = self::requireItemById($change->item_type, $change->item_id);
|
||||
|
||||
// If $item is null it can mean two things:
|
||||
// - The item has been deleted by the client requesting the sync items (which means the "delete" event is not included in the batch)
|
||||
// - The item has been deleted in the next batch - for example, if we're requesting sync items 0 to 100, the "delete" event is in range 101 to 200.
|
||||
// In the both cases we don't need to do anything. In the first case, the client already knows that the item has been deleted.
|
||||
// In the second case, the "delete" event will be sent later on.
|
||||
$item = BaseItem::byTypeAndId($change->item_type, $change->item_id);
|
||||
if ($item) {
|
||||
$syncItem['type'] = 'create';
|
||||
$syncItem['item'] = $item;
|
||||
}
|
||||
} else {
|
||||
$syncItem['item_fields'] = $itemIdToChangedFields[$change->item_id];
|
||||
$syncItem['item'] = self::requireItemById($change->item_type, $change->item_id);
|
||||
$item = BaseItem::byTypeAndId($change->item_type, $change->item_id);
|
||||
if ($item) {
|
||||
$syncItem['item_fields'] = $itemIdToChangedFields[$change->item_id];
|
||||
$syncItem['item'] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
$output[] = $syncItem;
|
||||
@ -108,7 +125,7 @@ class Change extends BaseModel {
|
||||
|
||||
static private function requireItemById($itemTypeId, $itemId) {
|
||||
$item = BaseItem::byTypeAndId($itemTypeId, $itemId);
|
||||
if (!$item) throw new \Exception('No such item: ' . $itemTypeId . ' ' . $itemId);
|
||||
if (!$item) throw new \Exception('No such item: ' . $itemTypeId . ' ' . BaseModel::hex($itemId));
|
||||
return $item;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user