1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-02-16 19:47:40 +02:00
joplin/ReactNativeClient/src/synchronizer.js

120 lines
3.3 KiB
JavaScript
Raw Normal View History

2017-06-15 22:46:53 +01:00
require('babel-plugin-transform-runtime');
2017-06-15 19:18:48 +01:00
import { BaseItem } from 'src/models/base-item.js';
2017-06-15 23:12:00 +01:00
import { sprintf } from 'sprintf-js';
2017-06-18 23:06:10 +01:00
import { time } from 'src/time-utils.js';
import { Log } from 'src/log.js'
2017-05-18 19:58:01 +00:00
class Synchronizer {
2017-05-19 19:12:09 +00:00
constructor(db, api) {
this.db_ = db;
this.api_ = api;
2017-05-18 19:58:01 +00:00
}
db() {
2017-05-19 19:12:09 +00:00
return this.db_;
2017-05-18 19:58:01 +00:00
}
api() {
2017-05-19 19:12:09 +00:00
return this.api_;
2017-05-18 19:58:01 +00:00
}
2017-06-18 23:06:10 +01:00
async start() {
// ------------------------------------------------------------------------
// First, find all the items that have been changed since the
// last sync and apply the changes to remote.
// ------------------------------------------------------------------------
2017-06-15 19:18:48 +01:00
2017-06-13 23:39:45 +01:00
let donePaths = [];
2017-06-18 23:06:10 +01:00
while (true) {
let result = await BaseItem.itemsThatNeedSync();
let locals = result.items;
2017-06-13 20:58:17 +00:00
2017-06-18 23:06:10 +01:00
for (let i = 0; i < locals.length; i++) {
let local = locals[i];
let ItemClass = BaseItem.itemClass(local);
let path = BaseItem.systemPath(local);
2017-06-15 19:18:48 +01:00
2017-06-18 23:06:10 +01:00
// Safety check to avoid infinite loops:
if (donePaths.indexOf(path) > 0) throw new Error(sprintf('Processing a path that has already been done: %s. sync_time was not updated?', path));
2017-06-13 23:39:45 +01:00
2017-06-18 23:06:10 +01:00
let remote = await this.api().stat(path);
let content = ItemClass.serialize(local);
let action = null;
2017-06-13 23:39:45 +01:00
2017-06-18 23:06:10 +01:00
if (!remote) {
action = 'createRemote';
2017-06-13 23:39:45 +01:00
} else {
2017-06-18 23:06:10 +01:00
if (remote.updated_time > local.updated_time) {
action = 'conflict';
2017-06-13 23:39:45 +01:00
} else {
2017-06-18 23:06:10 +01:00
action = 'updateRemote';
2017-06-13 23:39:45 +01:00
}
}
2017-06-18 23:06:10 +01:00
if (action == 'createRemote' || action == 'updateRemote') {
await this.api().put(path, content);
await this.api().setTimestamp(path, local.updated_time);
} else if (action == 'conflict') {
console.warn('FOUND CONFLICT', local, remote);
2017-06-13 20:58:17 +00:00
}
2017-06-18 00:49:52 +01:00
2017-06-18 23:06:10 +01:00
let newLocal = { id: local.id, sync_time: time.unixMs(), type_: local.type_ };
await ItemClass.save(newLocal, { autoTimestamp: false });
2017-06-03 17:20:17 +01:00
2017-06-18 23:06:10 +01:00
donePaths.push(path);
}
2017-06-03 17:20:17 +01:00
2017-06-18 23:06:10 +01:00
if (!result.hasMore) break;
2017-05-18 19:58:01 +00:00
}
2017-06-15 23:12:00 +01:00
2017-06-18 23:06:10 +01:00
// ------------------------------------------------------------------------
// Then, loop through all the remote items, find those that
// have been updated, and apply the changes to local.
// ------------------------------------------------------------------------
2017-06-15 00:14:15 +01:00
2017-06-18 23:06:10 +01:00
// At this point all the local items that have changed have been pushed to remote
// or handled as conflicts, so no conflict is possible after this.
2017-06-15 00:14:15 +01:00
2017-06-18 23:06:10 +01:00
let remotes = await this.api().list();
for (let i = 0; i < remotes.length; i++) {
let remote = remotes[i];
let path = remote.path;
if (donePaths.indexOf(path) > 0) continue;
2017-06-15 19:18:48 +01:00
2017-06-18 23:06:10 +01:00
let action = null;
let local = await BaseItem.loadItemByPath(path);
if (!local) {
action = 'createLocal';
} else {
if (remote.updated_time > local.updated_time) {
action = 'updateLocal';
2017-06-15 19:18:48 +01:00
}
}
2017-06-14 20:59:46 +01:00
2017-06-18 23:06:10 +01:00
if (!action) continue;
2017-06-15 00:14:15 +01:00
2017-06-18 23:06:10 +01:00
if (action == 'createLocal' || action == 'updateLocal') {
let content = await this.api().get(path);
if (!content) {
Log.warn('Remote item has been deleted between now and the list() call? In that case it will handled during the next sync: ' + path);
continue;
}
content = BaseItem.unserialize(content);
let ItemClass = BaseItem.itemClass(content);
2017-06-15 19:18:48 +01:00
2017-06-18 23:06:10 +01:00
content.sync_time = time.unixMs();
let options = { autoTimestamp: false };
if (action == 'createLocal') options.isNew = true;
await ItemClass.save(content, options);
2017-06-15 19:18:48 +01:00
}
}
2017-05-18 19:58:01 +00:00
2017-06-18 23:06:10 +01:00
return Promise.resolve();
2017-05-18 19:58:01 +00:00
}
}
export { Synchronizer };