From a983a9f1084e0a95ce8d279a5e35d2478704db99 Mon Sep 17 00:00:00 2001 From: Laurent Cozic Date: Mon, 24 Jul 2017 18:58:11 +0000 Subject: [PATCH] Tidy settings and sync creation --- CliClient/app/command-config.js | 16 ++- CliClient/app/command-status.js | 2 +- CliClient/app/fuzzing.js | 2 +- CliClient/locales/en_GB.po | 20 +++- CliClient/locales/fr_FR.po | 21 +++- CliClient/locales/joplin.pot | 20 +++- CliClient/tests/synchronizer.js | 11 +- CliClient/tests/test-utils.js | 24 ++-- .../lib/components/screens/status.js | 2 +- ReactNativeClient/lib/database.js | 14 --- .../lib/file-api-driver-local.js | 21 ++-- .../lib/file-api-driver-memory.js | 8 -- .../lib/file-api-driver-onedrive.js | 8 -- ReactNativeClient/lib/file-api.js | 10 ++ ReactNativeClient/lib/models/base-item.js | 66 +---------- ReactNativeClient/lib/models/setting.js | 108 ++++++++++++++---- ReactNativeClient/lib/registry.js | 15 +-- ReactNativeClient/lib/synchronizer.js | 4 +- 18 files changed, 204 insertions(+), 168 deletions(-) diff --git a/CliClient/app/command-config.js b/CliClient/app/command-config.js index 2bbf77e10..bcc40d1f1 100644 --- a/CliClient/app/command-config.js +++ b/CliClient/app/command-config.js @@ -10,20 +10,30 @@ class Command extends BaseCommand { } description() { - return _('Gets or sets a config value. If [value] is not provided, it will show the value of [name]. If neither [name] nor [value] is provided, it will list the current configuration.'); + return _("Gets or sets a config value. If [value] is not provided, it will show the value of [name]. If neither [name] nor [value] is provided, it will list the current configuration."); } async action(args) { + + const renderKeyValue = (name) => { + const value = Setting.value(name); + if (Setting.isEnum(name)) { + return _('%s = %s (%s)', name, value, Setting.enumOptionLabel(name, value)); + } else { + return _('%s = %s', name, value); + } + } + if (!args.name && !args.value) { let keys = Setting.publicKeys(); for (let i = 0; i < keys.length; i++) { - this.log(keys[i] + ' = ' + Setting.value(keys[i])); + this.log(renderKeyValue(keys[i])); } return; } if (args.name && !args.value) { - this.log(args.name + ' = ' + Setting.value(args.name)); + this.log(renderKeyValue(args.name)); return; } diff --git a/CliClient/app/command-status.js b/CliClient/app/command-status.js index 1628d721d..66d59cb8c 100644 --- a/CliClient/app/command-status.js +++ b/CliClient/app/command-status.js @@ -16,7 +16,7 @@ class Command extends BaseCommand { async action(args) { let service = new ReportService(); - let report = await service.status(Database.enumId('syncTarget', Setting.value('sync.target'))); + let report = await service.status(Setting.value('sync.target')); for (let i = 0; i < report.length; i++) { let section = report[i]; diff --git a/CliClient/app/fuzzing.js b/CliClient/app/fuzzing.js index a0255b49b..1d247959e 100644 --- a/CliClient/app/fuzzing.js +++ b/CliClient/app/fuzzing.js @@ -42,7 +42,7 @@ async function createClients() { for (let clientId = 0; clientId < 2; clientId++) { let client = createClient(clientId); promises.push(fs.remove(client.profileDir)); - promises.push(execCommand(client, 'config sync.target filesystem').then(() => { return execCommand(client, 'config sync.filesystem.path ' + syncDir); })); + promises.push(execCommand(client, 'config sync.target 2').then(() => { return execCommand(client, 'config sync.2.path ' + syncDir); })); output.push(client); } diff --git a/CliClient/locales/en_GB.po b/CliClient/locales/en_GB.po index 3ccf95d0c..8537c6daf 100644 --- a/CliClient/locales/en_GB.po +++ b/CliClient/locales/en_GB.po @@ -70,6 +70,14 @@ msgid "" "current configuration." msgstr "" +#, javascript-format +msgid "%s = %s (%s)" +msgstr "" + +#, javascript-format +msgid "%s = %s" +msgstr "" + msgid "" "Duplicates the notes matching to [notebook]. If no notebook is " "specified the note is duplicated in the current notebook." @@ -276,8 +284,8 @@ msgid "Please open this URL in your browser to authenticate the application:" msgstr "" msgid "" -"Please set the \"sync.filesystem.path\" config value to the desired " -"synchronisation destination." +"Please set the \"sync.2.path\" config value to the desired synchronisation " +"destination." msgstr "" #, javascript-format @@ -338,6 +346,14 @@ msgstr "" msgid "Cannot move note to \"%s\" notebook" msgstr "" +#, javascript-format +msgid "Invalid option value: \"%s\". Possible values are: %s." +msgstr "" + +#, javascript-format +msgid "%s (%s)" +msgstr "" + msgid "Synchronisation target" msgstr "" diff --git a/CliClient/locales/fr_FR.po b/CliClient/locales/fr_FR.po index 179d2dc24..216d9a4fa 100644 --- a/CliClient/locales/fr_FR.po +++ b/CliClient/locales/fr_FR.po @@ -66,6 +66,7 @@ msgstr "Affiche tous les détails de la note." msgid "Cannot find \"%s\"." msgstr "Impossible de trouver \"%s\"." +#, fuzzy msgid "" "Gets or sets a config value. If [value] is not provided, it will show the " "value of [name]. If neither [name] nor [value] is provided, it will list the " @@ -75,6 +76,14 @@ msgstr "" "fournie, la valeur de [nom] est affichée. Si ni le [nom] ni la [valeur] ne " "sont fournies, la configuration complète est affichée." +#, fuzzy, javascript-format +msgid "%s = %s (%s)" +msgstr "%s %s (%s)" + +#, javascript-format +msgid "%s = %s" +msgstr "" + msgid "" "Duplicates the notes matching to [notebook]. If no notebook is " "specified the note is duplicated in the current notebook." @@ -311,8 +320,8 @@ msgstr "" "logiciel :" msgid "" -"Please set the \"sync.filesystem.path\" config value to the desired " -"synchronisation destination." +"Please set the \"sync.2.path\" config value to the desired synchronisation " +"destination." msgstr "" #, javascript-format @@ -373,6 +382,14 @@ msgstr "Impossible de copier la note dans le carnet \"%s\"" msgid "Cannot move note to \"%s\" notebook" msgstr "Impossible de déplacer la note vers le carnet \"%s\"" +#, javascript-format +msgid "Invalid option value: \"%s\". Possible values are: %s." +msgstr "" + +#, fuzzy, javascript-format +msgid "%s (%s)" +msgstr "%s %s (%s)" + #, fuzzy msgid "Synchronisation target" msgstr "Cible de la synchronisation : %s" diff --git a/CliClient/locales/joplin.pot b/CliClient/locales/joplin.pot index 3ccf95d0c..8537c6daf 100644 --- a/CliClient/locales/joplin.pot +++ b/CliClient/locales/joplin.pot @@ -70,6 +70,14 @@ msgid "" "current configuration." msgstr "" +#, javascript-format +msgid "%s = %s (%s)" +msgstr "" + +#, javascript-format +msgid "%s = %s" +msgstr "" + msgid "" "Duplicates the notes matching to [notebook]. If no notebook is " "specified the note is duplicated in the current notebook." @@ -276,8 +284,8 @@ msgid "Please open this URL in your browser to authenticate the application:" msgstr "" msgid "" -"Please set the \"sync.filesystem.path\" config value to the desired " -"synchronisation destination." +"Please set the \"sync.2.path\" config value to the desired synchronisation " +"destination." msgstr "" #, javascript-format @@ -338,6 +346,14 @@ msgstr "" msgid "Cannot move note to \"%s\" notebook" msgstr "" +#, javascript-format +msgid "Invalid option value: \"%s\". Possible values are: %s." +msgstr "" + +#, javascript-format +msgid "%s (%s)" +msgstr "" + msgid "Synchronisation target" msgstr "" diff --git a/CliClient/tests/synchronizer.js b/CliClient/tests/synchronizer.js index 4c347deb2..bab33069e 100644 --- a/CliClient/tests/synchronizer.js +++ b/CliClient/tests/synchronizer.js @@ -38,7 +38,7 @@ async function localItemsSameAsRemote(locals, expect) { expect(!!remote).toBe(true); if (!remote) continue; - if (syncTargetId() == Database.enumId('syncTarget', 'filesystem')) { + if (syncTargetId() == Setting.SYNC_TARGET_FILESYSTEM) { expect(remote.updated_time).toBe(Math.floor(dbItem.updated_time / 1000) * 1000); } else { expect(remote.updated_time).toBe(dbItem.updated_time); @@ -142,29 +142,20 @@ describe('Synchronizer', function() { await switchClient(2); await synchronizer().start(); - - await sleep(0.1); - let note2 = await Note.load(note1.id); note2.title = "Updated on client 2"; await Note.save(note2); note2 = await Note.load(note2.id); - await synchronizer().start(); await switchClient(1); - await sleep(0.1); - let note2conf = await Note.load(note1.id); note2conf.title = "Updated on client 1"; await Note.save(note2conf); note2conf = await Note.load(note1.id); - await synchronizer().start(); - let conflictedNotes = await Note.conflictedNotes(); - expect(conflictedNotes.length).toBe(1); // Other than the id (since the conflicted note is a duplicate), and the is_conflict property diff --git a/CliClient/tests/test-utils.js b/CliClient/tests/test-utils.js index 592ed222b..bb6485297 100644 --- a/CliClient/tests/test-utils.js +++ b/CliClient/tests/test-utils.js @@ -29,10 +29,12 @@ Resource.fsDriver_ = fsDriver; const logDir = __dirname + '/../tests/logs'; fs.mkdirpSync(logDir, 0o755); -//const syncTarget = 'filesystem'; -const syncTarget = 'memory'; +//const syncTargetId_ = Setting.SYNC_TARGET_MEMORY; +const syncTargetId_ = Setting.SYNC_TARGET_FILESYSTEM; const syncDir = __dirname + '/../tests/sync'; +const sleepTime = syncTargetId_ == Setting.SYNC_TARGET_FILESYSTEM ? 1001 : 200; + const logger = new Logger(); logger.addTarget('file', { path: logDir + '/log.txt' }); logger.setLevel(Logger.LEVEL_DEBUG); @@ -47,7 +49,7 @@ Setting.setConstant('appId', 'net.cozic.joplin-cli'); Setting.setConstant('appType', 'cli'); function syncTargetId() { - return JoplinDatabase.enumId('syncTarget', syncTarget); + return syncTargetId_; } function sleep(n) { @@ -59,7 +61,7 @@ function sleep(n) { } async function switchClient(id) { - await time.msleep(200); // Always leave a little time so that updated_time properties don't overlap + await time.msleep(sleepTime); // Always leave a little time so that updated_time properties don't overlap await Setting.saveAll(); currentClient_ = id; @@ -120,7 +122,7 @@ async function setupDatabaseAndSynchronizer(id = null) { synchronizers_[id].setLogger(logger); } - if (syncTarget == 'filesystem') { + if (syncTargetId_ == Setting.SYNC_TARGET_FILESYSTEM) { fs.removeSync(syncDir) fs.mkdirpSync(syncDir, 0o755); } else { @@ -141,17 +143,19 @@ function synchronizer(id = null) { function fileApi() { if (fileApi_) return fileApi_; - if (syncTarget == 'filesystem') { + if (syncTargetId_ == Setting.SYNC_TARGET_FILESYSTEM) { fs.removeSync(syncDir) fs.mkdirpSync(syncDir, 0o755); fileApi_ = new FileApi(syncDir, new FileApiDriverLocal()); - fileApi_.setLogger(logger); - return fileApi_; } else { fileApi_ = new FileApi('/root', new FileApiDriverMemory()); fileApi_.setLogger(logger); - return fileApi_; - } + + } + + fileApi_.setLogger(logger); + fileApi_.setSyncTargetId(syncTargetId_); + return fileApi_; } export { setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId }; \ No newline at end of file diff --git a/ReactNativeClient/lib/components/screens/status.js b/ReactNativeClient/lib/components/screens/status.js index 3486ea674..d518f3f77 100644 --- a/ReactNativeClient/lib/components/screens/status.js +++ b/ReactNativeClient/lib/components/screens/status.js @@ -41,7 +41,7 @@ class StatusScreenComponent extends BaseScreenComponent { async resfreshScreen() { let service = new ReportService(); - let report = await service.status(Database.enumId('syncTarget', Setting.value('sync.target'))); + let report = await service.status(Setting.value('sync.target')); this.setState({ report: report }); } diff --git a/ReactNativeClient/lib/database.js b/ReactNativeClient/lib/database.js index e1d6a4997..5699dec0e 100644 --- a/ReactNativeClient/lib/database.js +++ b/ReactNativeClient/lib/database.js @@ -157,20 +157,6 @@ class Database { throw new Error('Unknown enum type or value: ' + type + ', ' + s); } - static enumName(type, id) { - if (type == 'syncTarget') { - if (id === 1) return 'memory'; - if (id === 2) return 'filesystem'; - if (id === 3) return 'onedrive'; - } - throw new Error('Unknown enum type or id: ' + type + ', ' + id); - } - - static enumIds(type) { - if (type == 'syncTarget') return [1,2,3]; - throw new Error('Unknown enum type: ' + type); - } - static formatValue(type, value) { if (value === null || value === undefined) return null; if (type == this.TYPE_INT) return Number(value); diff --git a/ReactNativeClient/lib/file-api-driver-local.js b/ReactNativeClient/lib/file-api-driver-local.js index f48b9155c..02cba5da7 100644 --- a/ReactNativeClient/lib/file-api-driver-local.js +++ b/ReactNativeClient/lib/file-api-driver-local.js @@ -4,16 +4,21 @@ import moment from 'moment'; import { BaseItem } from 'lib/models/base-item.js'; import { time } from 'lib/time-utils.js'; +// NOTE: when synchronising with the file system the time resolution is the second (unlike milliseconds for OneDrive for instance). +// What it means is that if, for example, client 1 changes a note at time t, and client 2 changes the same note within the same second, +// both clients will not know about each others updates during the next sync. They will simply both sync their note and whoever +// comes last will overwrite (on the remote storage) the note of the other client. Both client will then have a different note at +// that point and that will only be resolved if one of them changes the note and sync (if they don't change it, it will never get resolved). +// +// This is compound with the fact that we can't have a reliable delta API on the file system so we need to check all the timestamps +// every time and rely on this exclusively to know about changes. +// +// This explains occasional failures of the fuzzing program (it finds that the clients end up with two different notes after sync). To +// check that it is indeed the problem, check log-database.txt of both clients, search for the note ID, and most likely both notes +// will have been modified at the same exact second at some point. If not, it's another bug that needs to be investigated. + class FileApiDriverLocal { - syncTargetId() { - return 2; - } - - syncTargetName() { - return 'filesystem'; - } - fsErrorToJsError_(error) { let msg = error.toString(); let output = new Error(msg); diff --git a/ReactNativeClient/lib/file-api-driver-memory.js b/ReactNativeClient/lib/file-api-driver-memory.js index 0ba938dc0..eb2fe59e2 100644 --- a/ReactNativeClient/lib/file-api-driver-memory.js +++ b/ReactNativeClient/lib/file-api-driver-memory.js @@ -2,14 +2,6 @@ import { time } from 'lib/time-utils.js'; class FileApiDriverMemory { - syncTargetId() { - return 1; - } - - syncTargetName() { - return 'memory'; - } - constructor() { this.items_ = []; this.deletedItems_ = []; diff --git a/ReactNativeClient/lib/file-api-driver-onedrive.js b/ReactNativeClient/lib/file-api-driver-onedrive.js index ae588d8b8..0662f767b 100644 --- a/ReactNativeClient/lib/file-api-driver-onedrive.js +++ b/ReactNativeClient/lib/file-api-driver-onedrive.js @@ -9,14 +9,6 @@ class FileApiDriverOneDrive { this.api_ = api; } - syncTargetId() { - return 3; - } - - syncTargetName() { - return 'onedrive'; - } - api() { return this.api_; } diff --git a/ReactNativeClient/lib/file-api.js b/ReactNativeClient/lib/file-api.js index 93792b392..9d11dcbef 100644 --- a/ReactNativeClient/lib/file-api.js +++ b/ReactNativeClient/lib/file-api.js @@ -7,12 +7,22 @@ class FileApi { this.baseDir_ = baseDir; this.driver_ = driver; this.logger_ = new Logger(); + this.syncTargetId_ = null; } driver() { return this.driver_; } + setSyncTargetId(v) { + this.syncTargetId_ = v; + } + + syncTargetId() { + if (this.syncTargetId_ === null) throw new Error('syncTargetId has not been set!!'); + return this.syncTargetId_; + } + supportsDelta() { return this.driver_.supportsDelta(); } diff --git a/ReactNativeClient/lib/models/base-item.js b/ReactNativeClient/lib/models/base-item.js index cd59c300e..f6cd0b262 100644 --- a/ReactNativeClient/lib/models/base-item.js +++ b/ReactNativeClient/lib/models/base-item.js @@ -1,5 +1,6 @@ import { BaseModel } from 'lib/base-model.js'; import { Database } from 'lib/database.js'; +import { Setting } from 'lib/models/setting.js'; import { time } from 'lib/time-utils.js'; import { sprintf } from 'sprintf-js'; import moment from 'moment'; @@ -136,7 +137,7 @@ class BaseItem extends BaseModel { await super.batchDelete(ids, options); if (trackDeleted) { - const syncTargetIds = Database.enumIds('syncTarget'); + const syncTargetIds = Setting.enumOptionValues('sync.target'); let queries = []; let now = time.unixMs(); for (let i = 0; i < ids.length; i++) { @@ -313,10 +314,6 @@ class BaseItem extends BaseModel { limit); let neverSyncedItem = await ItemClass.modelSelectAll(sql); - //for (let i = 0; i < neverSyncedItem.length; i++) neverSyncedItem[i].sync_time = 0; - - // console.info(sql); - // console.info('NEVER', neverSyncedItem); // Secondly get the items that have been synced under this sync target but that have been changed since then @@ -344,8 +341,6 @@ class BaseItem extends BaseModel { changedItems = await ItemClass.modelSelectAll(sql); } - // console.info('CHANGED', changedItems); - const items = neverSyncedItem.concat(changedItems); if (i >= classNames.length - 1) { @@ -353,63 +348,6 @@ class BaseItem extends BaseModel { } else { if (items.length) return { hasMore: true, items: items }; } - - - - - //let extraWhere = className == 'Note' ? 'AND is_conflict = 0' : ''; - - // First get all the items that have never been synced under this sync target - - // let sql = sprintf(` - // SELECT %s FROM %s items - // LEFT JOIN sync_items t ON t.item_id = items.id - // WHERE (t.id IS NULL OR t.sync_target != %d) %s - // LIMIT %d - // `, - // this.db().escapeFields(fieldNames), - // this.db().escapeField(ItemClass.tableName()), - // Number(syncTarget), - // extraWhere, - // limit); - - // let neverSyncedItem = await ItemClass.modelSelectAll(sql); - // for (let i = 0; i < neverSyncedItem.length; i++) neverSyncedItem[i].sync_time = 0; - - // console.info(sql); - // console.info('NEVER', neverSyncedItem); - - // // Secondly get the items that have been synced under this sync target but that have been changed since then - - // const newLimit = limit - neverSyncedItem.length; - - // let changedItems = []; - - // if (newLimit > 0) { - // let sql = sprintf(` - // SELECT %s FROM %s items - // LEFT JOIN sync_items t ON t.item_id = items.id - // WHERE (t.sync_time < items.updated_time AND t.sync_target = %d) %s - // LIMIT %d - // `, - // this.db().escapeFields(fieldNames), - // this.db().escapeField(ItemClass.tableName()), - // Number(syncTarget), - // extraWhere, - // newLimit); - - // changedItems = await ItemClass.modelSelectAll(sql); - // } - - // console.info('CHANGED', changedItems); - - // const items = neverSyncedItem.concat(changedItems); - - // if (i >= classNames.length - 1) { - // return { hasMore: items.length >= limit, items: items }; - // } else { - // if (items.length) return { hasMore: true, items: items }; - // } } throw new Error('Unreachable'); diff --git a/ReactNativeClient/lib/models/setting.js b/ReactNativeClient/lib/models/setting.js index 9de805669..a0649ced9 100644 --- a/ReactNativeClient/lib/models/setting.js +++ b/ReactNativeClient/lib/models/setting.js @@ -12,9 +12,9 @@ class Setting extends BaseModel { return BaseModel.TYPE_SETTING; } - static defaultSetting(key) { - if (!(key in this.defaults_)) throw new Error('Unknown key: ' + key); - let output = Object.assign({}, this.defaults_[key]); + static settingMetadata(key) { + if (!(key in this.metadata_)) throw new Error('Unknown key: ' + key); + let output = Object.assign({}, this.metadata_[key]); output.key = key; return output; } @@ -22,8 +22,8 @@ class Setting extends BaseModel { static keys() { if (this.keys_) return this.keys_; this.keys_ = []; - for (let n in this.defaults_) { - if (!this.defaults_.hasOwnProperty(n)) continue; + for (let n in this.metadata_) { + if (!this.metadata_.hasOwnProperty(n)) continue; this.keys_.push(n); } return this.keys_; @@ -31,9 +31,9 @@ class Setting extends BaseModel { static publicKeys() { let output = []; - for (let n in this.defaults_) { - if (!this.defaults_.hasOwnProperty(n)) continue; - if (this.defaults_[n].public) output.push(n); + for (let n in this.metadata_) { + if (!this.metadata_.hasOwnProperty(n)) continue; + if (this.metadata_[n].public) output.push(n); } return output; } @@ -55,15 +55,24 @@ class Setting extends BaseModel { if (!this.cache_) throw new Error('Settings have not been initialized!'); for (let i = 0; i < this.cache_.length; i++) { - if (this.cache_[i].key == key) { - if (this.cache_[i].value === value) return; - this.cache_[i].value = value; + let c = this.cache_[i]; + if (c.key == key) { + const md = this.settingMetadata(key); + + if (md.type == 'enum') { + if (!this.isAllowedEnumOption(key, value)) { + throw new Error(_('Invalid option value: "%s". Possible values are: %s.', value, this.enumOptionsDoc(key))); + } + } + + if (c.value === value) return; + c.value = value; this.scheduleUpdate(); return; } } - let s = this.defaultSetting(key); + let s = this.settingMetadata(key); s.value = value; this.cache_.push(s); this.scheduleUpdate(); @@ -84,10 +93,54 @@ class Setting extends BaseModel { } } - let s = this.defaultSetting(key); + let s = this.settingMetadata(key); return s.value; } + static isEnum(key) { + const md = this.settingMetadata(key); + return md.type == 'enum'; + } + + static enumOptionValues(key) { + const options = this.enumOptions(key); + let output = []; + for (let n in options) { + if (!options.hasOwnProperty(n)) continue; + output.push(n); + } + return output; + } + + static enumOptionLabel(key, value) { + const options = this.enumOptions(key); + for (let n in options) { + if (n == value) return options[n]; + } + return ''; + } + + static enumOptions(key) { + if (!this.metadata_[key]) throw new Error('Unknown key: ' + key); + if (!this.metadata_[key].options) throw new Error('No options for: ' + key); + return this.metadata_[key].options(); + } + + static enumOptionsDoc(key) { + const options = this.enumOptions(key); + let output = []; + for (let n in options) { + if (!options.hasOwnProperty(n)) continue; + output.push(_('%s (%s)', n, options[n])); + } + return output.join(', '); + } + + static isAllowedEnumOption(key, value) { + const options = this.enumOptions(key); + return !!options[value]; + } + // Currently only supports objects with properties one level deep static object(key) { let output = {}; @@ -149,9 +202,9 @@ class Setting extends BaseModel { if (!appType) throw new Error('appType is required'); let output = {}; - for (let key in Setting.defaults_) { - if (!Setting.defaults_.hasOwnProperty(key)) continue; - let s = Object.assign({}, Setting.defaults_[key]); + for (let key in Setting.metadata_) { + if (!Setting.metadata_.hasOwnProperty(key)) continue; + let s = Object.assign({}, Setting.metadata_[key]); if (!s.public) continue; if (s.appTypes && s.appTypes.indexOf(appType) < 0) continue; s.value = this.value(key); @@ -162,19 +215,24 @@ class Setting extends BaseModel { } -Setting.defaults_ = { +Setting.SYNC_TARGET_MEMORY = 1; +Setting.SYNC_TARGET_FILESYSTEM = 2; +Setting.SYNC_TARGET_ONEDRIVE = 3; + +Setting.metadata_ = { 'activeFolderId': { value: '', type: 'string', public: false }, - 'sync.onedrive.auth': { value: '', type: 'string', public: false }, - 'sync.filesystem.path': { value: '', type: 'string', public: true, appTypes: ['cli'] }, - 'sync.target': { value: 'onedrive', type: 'enum', public: true, label: () => _('Synchronisation target'), options: () => ({ - 1: 'Memory', - 2: _('File system'), - 3: _('OneDrive'), - })}, + 'sync.2.path': { value: '', type: 'string', public: true, appTypes: ['cli'] }, + 'sync.3.auth': { value: '', type: 'string', public: false }, + 'sync.target': { value: 'onedrive', type: 'enum', public: true, label: () => _('Synchronisation target'), options: () => { + let output = {}; + output[Setting.SYNC_TARGET_MEMORY] = 'Memory'; + output[Setting.SYNC_TARGET_FILESYSTEM] = _('File system'); + output[Setting.SYNC_TARGET_ONEDRIVE] = _('OneDrive'); + return output; + }}, 'sync.context': { value: '', type: 'string', public: false }, 'editor': { value: '', type: 'string', public: true, appTypes: ['cli'] }, 'locale': { value: 'en_GB', type: 'string', public: true }, - //'aliases': { value: '', type: 'string', public: true }, 'todoFilter': { value: 'all', type: 'enum', public: true, appTypes: ['mobile'], label: () => _('Todo filter'), options: () => ({ all: _('Show all'), recent: _('Non-completed and recently completed ones'), diff --git a/ReactNativeClient/lib/registry.js b/ReactNativeClient/lib/registry.js index ed7fcc144..6f1ca6797 100644 --- a/ReactNativeClient/lib/registry.js +++ b/ReactNativeClient/lib/registry.js @@ -34,10 +34,10 @@ reg.oneDriveApi = () => { reg.oneDriveApi_.on('authRefreshed', (a) => { reg.logger().info('Saving updated OneDrive auth.'); - Setting.setValue('sync.onedrive.auth', a ? JSON.stringify(a) : null); + Setting.setValue('sync.3.auth', a ? JSON.stringify(a) : null); }); - let auth = Setting.value('sync.onedrive.auth'); + let auth = Setting.value('sync.3.auth'); if (auth) { try { auth = JSON.parse(auth); @@ -61,20 +61,20 @@ reg.synchronizer = async (syncTargetId) => { let fileApi = null; - if (syncTargetId == 'onedrive') { + if (syncTargetId == Setting.SYNC_TARGET_ONEDRIVE) { if (!reg.oneDriveApi().auth()) throw new Error('User is not authentified'); let appDir = await reg.oneDriveApi().appDirectory(); fileApi = new FileApi(appDir, new FileApiDriverOneDrive(reg.oneDriveApi())); - } else if (syncTargetId == 'memory') { + } else if (syncTargetId == Setting.SYNC_TARGET_MEMORY) { fileApi = new FileApi('joplin', new FileApiDriverMemory()); - } else if (syncTargetId == 'filesystem') { + } else if (syncTargetId == Setting.SYNC_TARGET_FILESYSTEM) { - let syncDir = Setting.value('sync.filesystem.path'); - if (!syncDir) throw new Error(_('Please set the "sync.filesystem.path" config value to the desired synchronisation destination.')); + let syncDir = Setting.value('sync.2.path'); + if (!syncDir) throw new Error(_('Please set the "sync.2.path" config value to the desired synchronisation destination.')); await shim.fs.mkdirp(syncDir, 0o755); fileApi = new FileApi(syncDir, new shim.FileApiDriverLocal()); @@ -84,6 +84,7 @@ reg.synchronizer = async (syncTargetId) => { } + fileApi.setSyncTargetId(syncTargetId); fileApi.setLogger(reg.logger()); let sync = new Synchronizer(reg.db(), fileApi, Setting.value('appType')); diff --git a/ReactNativeClient/lib/synchronizer.js b/ReactNativeClient/lib/synchronizer.js index f3aa6883d..91ba87f9d 100644 --- a/ReactNativeClient/lib/synchronizer.js +++ b/ReactNativeClient/lib/synchronizer.js @@ -153,7 +153,7 @@ class Synchronizer { const lastContext = options.context ? options.context : {}; - const syncTargetId = this.api().driver().syncTargetId(); + const syncTargetId = this.api().syncTargetId(); if (this.state() != 'idle') { this.logger().info('Synchronization is already in progress. State: ' + this.state()); @@ -176,7 +176,7 @@ class Synchronizer { this.dispatch({ type: 'SYNC_STARTED' }); - this.logSyncOperation('starting', null, null, 'Starting synchronization to ' + this.api().driver().syncTargetName() + ' (' + syncTargetId + ')... [' + synchronizationId + ']'); + this.logSyncOperation('starting', null, null, 'Starting synchronization to target ' + syncTargetId + '... [' + synchronizationId + ']'); try { await this.api().mkdir(this.syncDirName_);