1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-09-05 20:56:22 +02:00

Compare commits

..

7 Commits

Author SHA1 Message Date
Laurent Cozic
a8576a55d6 CLI v0.10.87 2018-01-09 09:31:03 +01:00
Laurent Cozic
eb500cdf9e All: Display sync items being fetched 2018-01-08 21:36:00 +01:00
Laurent Cozic
7b9dc66121 All: Schedule sync after enabling or disabling encryption 2018-01-08 21:25:38 +01:00
Laurent Cozic
bba2c68c6f All: Schedule sync only after 30 seconds 2018-01-08 21:05:08 +01:00
Laurent Cozic
c70d8bea78 All: Fixes #129: Tags are case insensitive 2018-01-08 21:04:44 +01:00
Laurent Cozic
176bda66ad Merge branch 'master' of github.com:laurent22/joplin 2018-01-08 20:09:12 +01:00
Laurent Cozic
78ce10ddf0 All: Fixed race condition when a note is being uploaded while it's being modified in the text editor 2018-01-08 20:09:01 +01:00
11 changed files with 45 additions and 10 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "joplin",
"version": "0.10.86",
"version": "0.10.87",
"lockfileVersion": 1,
"requires": true,
"dependencies": {

View File

@@ -19,7 +19,7 @@
],
"owner": "Laurent Cozic"
},
"version": "0.10.86",
"version": "0.10.87",
"bin": {
"joplin": "./main.js"
},

View File

@@ -830,6 +830,8 @@ describe('Synchronizer', function() {
}));
it('should sync resources', asyncTest(async () => {
while (insideBeforeEach) await time.msleep(100);
let folder1 = await Folder.save({ title: "folder1" });
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
await shim.attachFileToNote(note1, __dirname + '/../tests/support/photo.jpg');

View File

@@ -167,7 +167,7 @@ class NoteTextComponent extends React.Component {
async componentWillReceiveProps(nextProps) {
if ('noteId' in nextProps && nextProps.noteId !== this.props.noteId) {
await this.reloadNote(nextProps);
if(this.editor_){
if (this.editor_){
const session = this.editor_.editor.getSession();
const undoManager = session.getUndoManager();
undoManager.reset();

View File

@@ -277,6 +277,10 @@ class BaseApplication {
type: 'MASTERKEY_REMOVE_NOT_LOADED',
ids: loadedMasterKeyIds,
});
// Schedule a sync operation so that items that need to be encrypted
// are sent to sync target.
reg.scheduleSync();
}
}

View File

@@ -193,8 +193,12 @@ class BaseModel {
});
}
static loadByField(fieldName, fieldValue) {
return this.modelSelectOne('SELECT * FROM `' + this.tableName() + '` WHERE `' + fieldName + '` = ?', [fieldValue]);
static loadByField(fieldName, fieldValue, options = null) {
if (!options) options = {};
if (!('caseInsensitive' in options)) options.caseInsensitive = false;
let sql = 'SELECT * FROM `' + this.tableName() + '` WHERE `' + fieldName + '` = ?';
if (options.caseInsensitive) sql += ' COLLATE NOCASE';
return this.modelSelectOne(sql, [fieldValue]);
}
static loadByTitle(fieldValue) {

View File

@@ -109,7 +109,7 @@ class Tag extends BaseItem {
for (let i = 0; i < tagTitles.length; i++) {
const title = tagTitles[i].trim().toLowerCase();
if (!title) continue;
let tag = await this.loadByField('title', title);
let tag = await this.loadByField('title', title, { caseInsensitive: true });
if (!tag) tag = await Tag.save({ title: title }, { userSideValidation: true });
await this.addNote(tag.id, noteId);
addedTitles.push(title);

View File

@@ -44,7 +44,7 @@ reg.syncTarget = (syncTargetId = null) => {
}
reg.scheduleSync = async (delay = null) => {
if (delay === null) delay = 1000 * 3;
if (delay === null) delay = 1000 * 30;
let promiseResolve = null;
const promise = new Promise((resolve, reject) => {

View File

@@ -83,6 +83,7 @@ class DecryptionWorker {
});
continue;
}
this.logger().warn('DecryptionWorker: error for: ' + item.id + ' (' + ItemClass.tableName() + ')');
throw error;
}
}

View File

@@ -66,6 +66,7 @@ class Synchronizer {
static reportToLines(report) {
let lines = [];
if (report.createLocal) lines.push(_('Created local items: %d.', report.createLocal));
if (report.fetchingTotal && report.fetchingProcessed) lines.push(_('Fetched items: %d/%d.', report.fetchingProcessed, report.fetchingTotal));
if (report.updateLocal) lines.push(_('Updated local items: %d.', report.updateLocal));
if (report.createRemote) lines.push(_('Created remote items: %d.', report.createRemote));
if (report.updateRemote) lines.push(_('Updated remote items: %d.', report.updateRemote));
@@ -78,7 +79,7 @@ class Synchronizer {
return lines;
}
logSyncOperation(action, local = null, remote = null, message = null) {
logSyncOperation(action, local = null, remote = null, message = null, actionCount = 1) {
let line = ['Sync'];
line.push(action);
if (message) line.push(message);
@@ -105,7 +106,7 @@ class Synchronizer {
this.logger().debug(line.join(': '));
if (!this.progressReport_[action]) this.progressReport_[action] = 0;
this.progressReport_[action]++;
this.progressReport_[action] += actionCount;
this.progressReport_.state = this.state();
this.onProgress_(this.progressReport_);
@@ -320,9 +321,23 @@ class Synchronizer {
}
}
// Note: Currently, we set sync_time to update_time, which should work fine given that the resolution is the millisecond.
// In theory though, this could happen:
//
// 1. t0: Editor: Note is modified
// 2. t0: Sync: Found that note was modified so start uploading it
// 3. t0: Editor: Note is modified again
// 4. t1: Sync: Note has finished uploading, set sync_time to t0
//
// Later any attempt to sync will not detect that note was modified in (3) (within the same millisecond as it was being uploaded)
// because sync_time will be t0 too.
//
// The solution would be to use something like an etag (a simple counter incremented on every change) to make sure each
// change is uniquely identified. Leaving it like this for now.
if (canSync) {
await this.api().setTimestamp(path, local.updated_time);
await ItemClass.saveSyncTime(syncTargetId, local, time.unixMs());
await ItemClass.saveSyncTime(syncTargetId, local, local.updated_time);
}
} else if (action == 'itemConflict') {
@@ -432,12 +447,17 @@ class Synchronizer {
});
let remotes = listResult.items;
this.logSyncOperation('fetchingTotal', null, null, 'Fetching delta items from sync target', remotes.length);
for (let i = 0; i < remotes.length; i++) {
if (this.cancelling() || this.debugFlags_.indexOf('cancelDeltaLoop2') >= 0) {
hasCancelled = true;
break;
}
this.logSyncOperation('fetchingProcessed', null, null, 'Processing fetched item');
let remote = remotes[i];
if (!BaseItem.isSystemPath(remote.path)) continue; // The delta API might return things like the .sync, .resource or the root folder

View File

@@ -97,6 +97,10 @@ const generalMiddleware = store => next => async (action) => {
type: 'MASTERKEY_REMOVE_NOT_LOADED',
ids: loadedMasterKeyIds,
});
// Schedule a sync operation so that items that need to be encrypted
// are sent to sync target.
reg.scheduleSync();
}
if (action.type == 'NAV_GO' && action.routeName == 'Notes') {