1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-06-12 22:57:38 +02:00

All: Resolves #712: New: Support for note history (#1415)

* Started revisions support

* More rev changes

* More rev changes

* More revs changes

* Fixed deletion algorithm

* More tests and moved updated time to separate field

* Display info when restoring note

* Better handling of existing notes

* wip

* Further improvements and fixed tests

* Better handling of changes created via sync

* Enable chokidar again

* Testing special case

* Further improved logic to handle notes that existed before the revision service

* Added tests

* Better handling of encrypted revisions

* Improved handling of deleted note revisions by moving logic to collectRevision

* Improved handling of old notes by moving logic to collectRevision()

* Handle case when deleting revisions while one is still encrypted

* UI tweaks

* Added revision service to mobile app

* Fixed config screens on mobile and desktop

* Enabled revisions on CLI app
This commit is contained in:
Laurent Cozic
2019-05-06 21:35:29 +01:00
committed by GitHub
parent 9e2982992a
commit 08af9de190
44 changed files with 1873 additions and 243 deletions

View File

@ -2,6 +2,7 @@ const BaseItem = require('lib/models/BaseItem.js');
const Folder = require('lib/models/Folder.js');
const Note = require('lib/models/Note.js');
const Resource = require('lib/models/Resource.js');
const ItemChange = require('lib/models/ItemChange.js');
const ResourceLocalState = require('lib/models/ResourceLocalState.js');
const MasterKey = require('lib/models/MasterKey.js');
const BaseModel = require('lib/BaseModel.js');
@ -325,7 +326,7 @@ class Synchronizer {
if (action == "createRemote" || action == "updateRemote") {
let canSync = true;
try {
if (this.testingHooks_.indexOf("rejectedByTarget") >= 0) throw new JoplinError("Testing rejectedByTarget", "rejectedByTarget");
if (this.testingHooks_.indexOf("notesRejectedByTarget") >= 0 && local.type_ === BaseModel.TYPE_NOTE) throw new JoplinError("Testing rejectedByTarget", "rejectedByTarget");
const content = await ItemClass.serializeForSync(local);
await this.api().put(path, content);
} catch (error) {
@ -370,9 +371,9 @@ class Synchronizer {
local = remoteContent;
const syncTimeQueries = BaseItem.updateSyncTimeQueries(syncTargetId, local, time.unixMs());
await ItemClass.save(local, { autoTimestamp: false, nextQueries: syncTimeQueries });
await ItemClass.save(local, { autoTimestamp: false, changeSource: ItemChange.SOURCE_SYNC, nextQueries: syncTimeQueries });
} else {
await ItemClass.delete(local.id);
await ItemClass.delete(local.id, { changeSource: ItemChange.SOURCE_SYNC });
}
} else if (action == "noteConflict") {
// ------------------------------------------------------------------------------
@ -395,7 +396,7 @@ class Synchronizer {
let conflictedNote = Object.assign({}, local);
delete conflictedNote.id;
conflictedNote.is_conflict = 1;
await Note.save(conflictedNote, { autoTimestamp: false });
await Note.save(conflictedNote, { autoTimestamp: false, changeSource: ItemChange.SOURCE_SYNC });
}
// ------------------------------------------------------------------------------
@ -406,12 +407,12 @@ class Synchronizer {
if (remote) {
local = remoteContent;
const syncTimeQueries = BaseItem.updateSyncTimeQueries(syncTargetId, local, time.unixMs());
await ItemClass.save(local, { autoTimestamp: false, nextQueries: syncTimeQueries });
await ItemClass.save(local, { autoTimestamp: false, changeSource: ItemChange.SOURCE_SYNC, nextQueries: syncTimeQueries });
if (!!local.encryption_applied) this.dispatch({ type: "SYNC_GOT_ENCRYPTED_ITEM" });
} else {
// Remote no longer exists (note deleted) so delete local one too
await ItemClass.delete(local.id);
await ItemClass.delete(local.id, { changeSource: ItemChange.SOURCE_SYNC });
}
}
@ -535,6 +536,8 @@ class Synchronizer {
}
}
if (this.testingHooks_.indexOf('skipRevisions') >= 0 && content && content.type_ === BaseModel.TYPE_REVISION) action = null;
if (!action) continue;
this.logSyncOperation(action, local, remote, reason);
@ -557,30 +560,13 @@ class Synchronizer {
let options = {
autoTimestamp: false,
nextQueries: BaseItem.updateSyncTimeQueries(syncTargetId, content, time.unixMs()),
changeSource: ItemChange.SOURCE_SYNC,
};
if (action == "createLocal") options.isNew = true;
if (action == "updateLocal") options.oldItem = local;
const creatingNewResource = content.type_ == BaseModel.TYPE_RESOURCE && action == "createLocal";
// if (content.type_ == BaseModel.TYPE_RESOURCE && action == "createLocal") {
// let localResourceContentPath = Resource.fullPath(content);
// let remoteResourceContentPath = this.resourceDirName_ + "/" + content.id;
// try {
// await this.api().get(remoteResourceContentPath, { path: localResourceContentPath, target: "file" });
// } catch (error) {
// if (error.code === 'rejectedByTarget') {
// this.progressReport_.errors.push(error);
// this.logger().warn('Rejected by target: ' + path + ': ' + error.message);
// continue;
// } else {
// throw error;
// }
// }
// }
// if (creatingNewResource) content.fetch_status = Resource.FETCH_STATUS_IDLE;
if (creatingNewResource) {
await ResourceLocalState.save({ resource_id: content.id, fetch_status: Resource.FETCH_STATUS_IDLE });
}
@ -608,7 +594,7 @@ class Synchronizer {
}
let ItemClass = BaseItem.itemClass(local.type_);
await ItemClass.delete(local.id, { trackDeleted: false });
await ItemClass.delete(local.id, { trackDeleted: false, changeSource: ItemChange.SOURCE_SYNC });
}
}
@ -653,7 +639,7 @@ class Synchronizer {
// CONFLICT
await Folder.markNotesAsConflict(item.id);
}
await Folder.delete(item.id, { deleteChildren: false, trackDeleted: false });
await Folder.delete(item.id, { deleteChildren: false, changeSource: ItemChange.SOURCE_SYNC, trackDeleted: false });
}
}