2020-12-06 11:13:17 +05:00
|
|
|
import { migrations } from "../../migrations";
|
2020-04-09 16:36:57 +05:00
|
|
|
import { areAllEmpty } from "./utils";
|
2021-02-12 10:01:06 +05:00
|
|
|
import SparkMD5 from "spark-md5";
|
2020-04-09 16:36:57 +05:00
|
|
|
|
|
|
|
|
class Merger {
|
|
|
|
|
/**
|
|
|
|
|
*
|
2020-11-25 00:48:02 +05:00
|
|
|
* @param {import("../index").default} db
|
2020-04-09 16:36:57 +05:00
|
|
|
*/
|
2020-04-16 03:04:44 +05:00
|
|
|
constructor(db) {
|
2020-04-09 16:36:57 +05:00
|
|
|
this._db = db;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-06 11:13:17 +05:00
|
|
|
_migrate(item, deserialized) {
|
|
|
|
|
const version = item.v || 0;
|
2020-12-06 14:50:01 +05:00
|
|
|
let type = deserialized.type;
|
2021-02-12 10:15:37 +05:00
|
|
|
if (!type && deserialized.data) type = "tiny";
|
2020-12-06 14:50:01 +05:00
|
|
|
const migrate = migrations[version][type];
|
2020-12-06 11:13:17 +05:00
|
|
|
if (migrate) return migrate(deserialized);
|
|
|
|
|
return deserialized;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-13 12:04:49 +05:00
|
|
|
async _deserialize(item) {
|
|
|
|
|
const deserialized = JSON.parse(
|
2020-04-13 15:55:32 +05:00
|
|
|
await this._db.context.decrypt(this.key, item)
|
2020-04-13 12:04:49 +05:00
|
|
|
);
|
|
|
|
|
deserialized.remote = true;
|
2020-12-06 11:13:17 +05:00
|
|
|
return this._migrate(item, deserialized);
|
2020-04-13 12:04:49 +05:00
|
|
|
}
|
|
|
|
|
|
2020-04-09 16:36:57 +05:00
|
|
|
async _mergeItem(remoteItem, get, add) {
|
|
|
|
|
let localItem = await get(remoteItem.id);
|
2020-04-13 12:04:49 +05:00
|
|
|
remoteItem = await this._deserialize(remoteItem);
|
2020-04-09 16:36:57 +05:00
|
|
|
if (!localItem || remoteItem.dateEdited > localItem.dateEdited) {
|
|
|
|
|
await add(remoteItem);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async _mergeArray(array, get, set) {
|
|
|
|
|
if (!array) return;
|
|
|
|
|
return Promise.all(
|
|
|
|
|
array.map(async (item) => await this._mergeItem(item, get, set))
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async _mergeItemWithConflicts(remoteItem, get, add, resolve) {
|
|
|
|
|
let localItem = await get(remoteItem.id);
|
|
|
|
|
|
2020-04-13 12:04:49 +05:00
|
|
|
remoteItem = await this._deserialize(remoteItem);
|
2020-04-11 11:42:17 +05:00
|
|
|
if (!localItem) {
|
|
|
|
|
await add(remoteItem);
|
|
|
|
|
} else if (!localItem.resolved && localItem.dateEdited > this._lastSynced) {
|
2020-04-09 16:36:57 +05:00
|
|
|
await resolve(localItem, remoteItem);
|
2020-04-11 11:17:58 +05:00
|
|
|
} else if (localItem.resolved) {
|
|
|
|
|
await add({ ...localItem, resolved: false });
|
2020-04-11 12:07:40 +05:00
|
|
|
} else {
|
2020-04-09 16:39:44 +05:00
|
|
|
await add(remoteItem);
|
2020-04-11 12:07:40 +05:00
|
|
|
}
|
2020-04-09 16:36:57 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async _mergeArrayWithConflicts(array, get, set, resolve) {
|
|
|
|
|
if (!array) return;
|
|
|
|
|
return Promise.all(
|
|
|
|
|
array.map(
|
|
|
|
|
async (item) =>
|
|
|
|
|
await this._mergeItemWithConflicts(item, get, set, resolve)
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-16 03:04:44 +05:00
|
|
|
async merge(serverResponse, lastSynced) {
|
2020-04-09 16:36:57 +05:00
|
|
|
if (!serverResponse) return false;
|
2020-04-16 03:04:44 +05:00
|
|
|
this._lastSynced = lastSynced;
|
2020-04-09 16:36:57 +05:00
|
|
|
const {
|
|
|
|
|
notes,
|
|
|
|
|
synced,
|
|
|
|
|
notebooks,
|
2020-11-04 10:17:37 +05:00
|
|
|
content,
|
2020-04-09 16:36:57 +05:00
|
|
|
trash,
|
2020-04-15 14:00:05 +05:00
|
|
|
vaultKey,
|
2020-12-05 11:26:02 +05:00
|
|
|
settings,
|
2020-04-09 16:36:57 +05:00
|
|
|
} = serverResponse;
|
|
|
|
|
|
|
|
|
|
if (synced || areAllEmpty(serverResponse)) return false;
|
2020-12-16 12:06:25 +05:00
|
|
|
this.key = await this._db.user.getEncryptionKey();
|
2020-04-09 16:36:57 +05:00
|
|
|
|
2020-04-15 14:00:05 +05:00
|
|
|
if (vaultKey) {
|
|
|
|
|
await this._db.vault._setKey(await this._deserialize(vaultKey));
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-05 11:26:02 +05:00
|
|
|
await this._mergeArray(
|
|
|
|
|
settings,
|
|
|
|
|
() => this._db.settings.raw,
|
2020-12-10 12:58:04 +05:00
|
|
|
(item) => this._db.settings.merge(item)
|
2020-12-05 11:26:02 +05:00
|
|
|
);
|
|
|
|
|
|
2020-04-09 16:36:57 +05:00
|
|
|
await this._mergeArray(
|
|
|
|
|
notes,
|
|
|
|
|
(id) => this._db.notes.note(id),
|
|
|
|
|
(item) => this._db.notes.add(item)
|
|
|
|
|
);
|
2020-12-05 11:26:02 +05:00
|
|
|
|
2020-04-09 16:36:57 +05:00
|
|
|
await this._mergeArray(
|
|
|
|
|
notebooks,
|
|
|
|
|
(id) => this._db.notebooks.notebook(id),
|
|
|
|
|
(item) => this._db.notebooks.add(item)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
await this._mergeArrayWithConflicts(
|
2020-11-04 10:17:37 +05:00
|
|
|
content,
|
|
|
|
|
(id) => this._db.content.raw(id),
|
|
|
|
|
(item) => this._db.content.add(item),
|
2020-04-09 16:36:57 +05:00
|
|
|
async (local, remote) => {
|
2021-02-12 10:01:06 +05:00
|
|
|
// if hashes are equal do nothing
|
|
|
|
|
if (SparkMD5.hash(local.data) === SparkMD5.hash(remote.data)) return;
|
|
|
|
|
|
2020-04-15 17:50:26 +05:00
|
|
|
// merge conflicts resolver
|
|
|
|
|
const note = this._db.notes.note(local.noteId).data;
|
|
|
|
|
|
2020-04-16 11:21:02 +05:00
|
|
|
if (local.deleted || note.locked) {
|
2020-11-04 10:17:37 +05:00
|
|
|
// if note is locked or content is deleted we keep the most recent version.
|
2020-04-16 11:21:02 +05:00
|
|
|
if (remote.dateEdited > local.dateEdited)
|
2020-11-04 10:17:37 +05:00
|
|
|
await this._db.content.add({ id: local.id, ...remote });
|
2020-04-15 17:50:26 +05:00
|
|
|
} else {
|
|
|
|
|
// otherwise we trigger the conflicts
|
2020-11-04 10:17:37 +05:00
|
|
|
await this._db.content.add({ ...local, conflicted: remote });
|
2020-04-15 17:50:26 +05:00
|
|
|
await this._db.notes.add({ id: local.noteId, conflicted: true });
|
|
|
|
|
await this._db.context.write("hasConflicts", true);
|
|
|
|
|
}
|
2020-04-09 16:36:57 +05:00
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2020-12-29 12:31:26 +05:00
|
|
|
// await this._mergeArray(
|
|
|
|
|
// tags,
|
|
|
|
|
// (id) => this._db.tags.tag(id),
|
|
|
|
|
// (item) => this._db.tags.merge(item)
|
|
|
|
|
// );
|
|
|
|
|
|
|
|
|
|
// await this._mergeArray(
|
|
|
|
|
// colors,
|
|
|
|
|
// (id) => this._db.colors.tag(id),
|
|
|
|
|
// (item) => this._db.colors.merge(item)
|
|
|
|
|
// );
|
2020-04-09 16:36:57 +05:00
|
|
|
|
|
|
|
|
await this._mergeArray(
|
|
|
|
|
trash,
|
|
|
|
|
() => undefined,
|
|
|
|
|
(item) => this._db.trash.add(item)
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
export default Merger;
|