You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-08-30 20:39:46 +02:00
Compare commits
1 Commits
android-v0
...
android-v0
Author | SHA1 | Date | |
---|---|---|---|
|
10b4907cee |
21
BUILD.md
21
BUILD.md
@@ -1,21 +1,20 @@
|
||||
# General information
|
||||
|
||||
- All the applications share the same library, which, for historical reasons, is in ReactNativeClient/lib. This library is copied to the relevant directories when builing each app.
|
||||
- The translations are built by running CliClient/build-translation.sh. You normally don't need to run this if you haven't updated the translation since the compiled files are on the repository.
|
||||
- The translations are built by running CliClient/build-translation.sh. For this reasons, it's generally better to get the CLI app to build first so that everything is setup correctly.
|
||||
- Note: building translations is no longer required to run the apps, so you can ignore all the below requirements about gettext.
|
||||
|
||||
## macOS dependencies
|
||||
|
||||
brew install yarn node
|
||||
echo 'export PATH="/usr/local/opt/gettext/bin:$PATH"' >> ~/.bash_profile
|
||||
source ~/.bash_profile
|
||||
|
||||
If you get a node-gyp related error you might need to manually install it: `npm install -g node-gyp`
|
||||
brew install yarn node xgettext
|
||||
npm install -g node-gyp
|
||||
echo 'export PATH="/usr/local/opt/gettext/bin:$PATH"' >> ~/.bash_profile
|
||||
source ~/.bash_profile
|
||||
|
||||
## Linux and Windows (WSL) dependencies
|
||||
## Linux and Windows dependencies
|
||||
|
||||
- Install yarn - https://yarnpkg.com/lang/en/docs/install/
|
||||
- Install node v8.x (check with `node --version`) - https://nodejs.org/en/
|
||||
- If you get a node-gyp related error you might need to manually install it: `npm install -g node-gyp`
|
||||
|
||||
# Building the Electron application
|
||||
|
||||
@@ -38,8 +37,4 @@ From `/ReactNativeClient`, run `npm install`, then `react-native run-ios` or `re
|
||||
|
||||
# Building the Terminal application
|
||||
|
||||
From `/CliClient`:
|
||||
- Run `npm install`
|
||||
- Then `build.sh`
|
||||
- Copy the translations to the build directory: `rsync -aP ../ReactNativeClient/locales/ build/locales/`
|
||||
- Run `run.sh` to start the application for testing.
|
||||
From `/CliClient`, run `npm install` then run `run.sh`. If you get an error about `xgettext`, comment out the command `node build-translation.js --silent` in build.sh
|
||||
|
@@ -177,33 +177,22 @@ class Application extends BaseApplication {
|
||||
await doExit();
|
||||
}
|
||||
|
||||
commands(uiType = null) {
|
||||
if (!this.allCommandsLoaded_) {
|
||||
fs.readdirSync(__dirname).forEach((path) => {
|
||||
if (path.indexOf('command-') !== 0) return;
|
||||
const ext = fileExtension(path)
|
||||
if (ext != 'js') return;
|
||||
commands() {
|
||||
if (this.allCommandsLoaded_) return this.commands_;
|
||||
|
||||
let CommandClass = require('./' + path);
|
||||
let cmd = new CommandClass();
|
||||
if (!cmd.enabled()) return;
|
||||
cmd = this.setupCommand(cmd);
|
||||
this.commands_[cmd.name()] = cmd;
|
||||
});
|
||||
fs.readdirSync(__dirname).forEach((path) => {
|
||||
if (path.indexOf('command-') !== 0) return;
|
||||
const ext = fileExtension(path)
|
||||
if (ext != 'js') return;
|
||||
|
||||
this.allCommandsLoaded_ = true;
|
||||
}
|
||||
let CommandClass = require('./' + path);
|
||||
let cmd = new CommandClass();
|
||||
if (!cmd.enabled()) return;
|
||||
cmd = this.setupCommand(cmd);
|
||||
this.commands_[cmd.name()] = cmd;
|
||||
});
|
||||
|
||||
if (uiType !== null) {
|
||||
let temp = [];
|
||||
for (let n in this.commands_) {
|
||||
if (!this.commands_.hasOwnProperty(n)) continue;
|
||||
const c = this.commands_[n];
|
||||
if (!c.supportsUi(uiType)) continue;
|
||||
temp[n] = c;
|
||||
}
|
||||
return temp;
|
||||
}
|
||||
this.allCommandsLoaded_ = true;
|
||||
|
||||
return this.commands_;
|
||||
}
|
||||
@@ -257,13 +246,9 @@ class Application extends BaseApplication {
|
||||
try {
|
||||
CommandClass = require(__dirname + '/command-' + name + '.js');
|
||||
} catch (error) {
|
||||
if (error.message && error.message.indexOf('Cannot find module') >= 0) {
|
||||
let e = new Error(_('No such command: %s', name));
|
||||
e.type = 'notFound';
|
||||
throw e;
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
let e = new Error('No such command: ' + name);
|
||||
e.type = 'notFound';
|
||||
throw e;
|
||||
}
|
||||
|
||||
let cmd = new CommandClass();
|
||||
@@ -321,8 +306,6 @@ class Application extends BaseApplication {
|
||||
if (argv.length) {
|
||||
this.gui_ = this.dummyGui();
|
||||
|
||||
this.currentFolder_ = await Folder.load(Setting.value('activeFolderId'));
|
||||
|
||||
try {
|
||||
await this.execCommand(argv);
|
||||
} catch (error) {
|
||||
|
@@ -12,10 +12,6 @@ class Command extends BaseCommand {
|
||||
return _('Exits the application.');
|
||||
}
|
||||
|
||||
compatibleUis() {
|
||||
return ['gui'];
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
await app().exit();
|
||||
}
|
||||
|
@@ -18,7 +18,7 @@ class Command extends BaseCommand {
|
||||
}
|
||||
|
||||
allCommands() {
|
||||
const commands = app().commands(app().uiType());
|
||||
const commands = app().commands();
|
||||
let output = [];
|
||||
for (let n in commands) {
|
||||
if (!commands.hasOwnProperty(n)) continue;
|
||||
@@ -69,8 +69,6 @@ class Command extends BaseCommand {
|
||||
this.stdout('');
|
||||
this.stdout(_('The possible commands are:'));
|
||||
this.stdout('');
|
||||
this.stdout(_('Type `help all` for the complete help of all the commands.'));
|
||||
this.stdout('');
|
||||
this.stdout(commandNames.join(', '));
|
||||
this.stdout('');
|
||||
this.stdout(_('In any command, a note or notebook can be refered to by title or ID, or using the shortcuts `$n` or `$b` for, respectively, the currently selected note or notebook. `$c` can be used to refer to the currently selected item.'));
|
||||
|
@@ -2,7 +2,6 @@ const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { BaseModel } = require('lib/base-model.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const { Folder } = require('lib/models/folder.js');
|
||||
const { Note } = require('lib/models/note.js');
|
||||
const { BaseItem } = require('lib/models/base-item.js');
|
||||
@@ -13,16 +12,16 @@ class Command extends BaseCommand {
|
||||
return 'set <note> <name> [value]';
|
||||
}
|
||||
|
||||
description() {
|
||||
const fields = Note.fields();
|
||||
const s = [];
|
||||
for (let i = 0; i < fields.length; i++) {
|
||||
const f = fields[i];
|
||||
if (f.name === 'id') continue;
|
||||
s.push(f.name + ' (' + Database.enumName('fieldType', f.type) + ')');
|
||||
}
|
||||
enabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
return _('Sets the property <name> of the given <note> to the given [value]. Possible properties are:\n\n%s', s.join(', '));
|
||||
description() {
|
||||
return _('Sets the property <name> of the given <note> to the given [value].');
|
||||
}
|
||||
|
||||
hidden() {
|
||||
return true;
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
|
@@ -32,6 +32,7 @@ class Command extends BaseCommand {
|
||||
options() {
|
||||
return [
|
||||
['--target <target>', _('Sync to provided target (defaults to sync.target config value)')],
|
||||
['--random-failures', 'For debugging purposes. Do not use.'],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -139,6 +140,7 @@ class Command extends BaseCommand {
|
||||
cliUtils.redrawDone();
|
||||
this.stdout(msg);
|
||||
},
|
||||
randomFailures: args.options['random-failures'] === true,
|
||||
};
|
||||
|
||||
this.stdout(_('Synchronisation target: %s (%s)', Setting.enumOptionLabel('sync.target', this.syncTargetId_), this.syncTargetId_));
|
||||
|
@@ -18,8 +18,8 @@ class Command extends BaseCommand {
|
||||
return { data: autocompleteFolders };
|
||||
}
|
||||
|
||||
compatibleUis() {
|
||||
return ['cli'];
|
||||
enabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -100,10 +100,6 @@ msgstr ""
|
||||
msgid "Cancelling background synchronisation... Please wait."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "No such command: %s"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "The command \"%s\" is only available in GUI mode"
|
||||
msgstr ""
|
||||
@@ -512,9 +508,6 @@ msgstr ""
|
||||
msgid "Tools"
|
||||
msgstr ""
|
||||
|
||||
msgid "Synchronisation status"
|
||||
msgstr ""
|
||||
|
||||
msgid "Options"
|
||||
msgstr ""
|
||||
|
||||
@@ -575,12 +568,6 @@ msgstr ""
|
||||
msgid "Layout"
|
||||
msgstr ""
|
||||
|
||||
msgid "Some items cannot be synchronised."
|
||||
msgstr ""
|
||||
|
||||
msgid "View them now"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add or remove tags"
|
||||
msgstr ""
|
||||
|
||||
@@ -618,9 +605,6 @@ msgstr ""
|
||||
msgid "Import"
|
||||
msgstr ""
|
||||
|
||||
msgid "Synchronisation Status"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete notebook?"
|
||||
msgstr ""
|
||||
|
||||
@@ -828,13 +812,6 @@ msgstr ""
|
||||
msgid "Invalid option value: \"%s\". Possible values are: %s."
|
||||
msgstr ""
|
||||
|
||||
msgid "Items that cannot be synchronised"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "\"%s\": \"%s\""
|
||||
msgstr ""
|
||||
|
||||
msgid "Sync status (synced items / total items)"
|
||||
msgstr ""
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -106,10 +106,6 @@ msgstr ""
|
||||
msgid "Cancelling background synchronisation... Please wait."
|
||||
msgstr "Cancelando sincronización en segundo plano... Por favor espere."
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "No such command: %s"
|
||||
msgstr "Comando inválido: \"%s\""
|
||||
|
||||
#, javascript-format
|
||||
msgid "The command \"%s\" is only available in GUI mode"
|
||||
msgstr "El comando \"%s\" solo está disponible en el modo gráfico (GUI)"
|
||||
@@ -557,10 +553,6 @@ msgstr "Buscar en todas la notas"
|
||||
msgid "Tools"
|
||||
msgstr "Herramientas"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Synchronisation status"
|
||||
msgstr "Objetivo de sincronización"
|
||||
|
||||
msgid "Options"
|
||||
msgstr "Opciones"
|
||||
|
||||
@@ -624,13 +616,6 @@ msgstr "Establecer alarma"
|
||||
msgid "Layout"
|
||||
msgstr "Plantilla"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Some items cannot be synchronised."
|
||||
msgstr "No se puede iniciar la sincronización."
|
||||
|
||||
msgid "View them now"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add or remove tags"
|
||||
msgstr "Añadir o eliminar etiquetas"
|
||||
|
||||
@@ -669,10 +654,6 @@ msgstr "Inicio de sesión de OneDrive"
|
||||
msgid "Import"
|
||||
msgstr "Importar"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Synchronisation Status"
|
||||
msgstr "Objetivo de sincronización"
|
||||
|
||||
msgid "Delete notebook?"
|
||||
msgstr "¿Eliminar cuaderno?"
|
||||
|
||||
@@ -888,13 +869,6 @@ msgstr ""
|
||||
msgid "Invalid option value: \"%s\". Possible values are: %s."
|
||||
msgstr "Valor inválido: \"%s\". Posibles valores: %s."
|
||||
|
||||
msgid "Items that cannot be synchronised"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "\"%s\": \"%s\""
|
||||
msgstr ""
|
||||
|
||||
msgid "Sync status (synced items / total items)"
|
||||
msgstr ""
|
||||
"Estado de sincronización (elementos sincronizados / total de elementos)"
|
||||
|
@@ -100,10 +100,6 @@ msgstr "o"
|
||||
msgid "Cancelling background synchronisation... Please wait."
|
||||
msgstr "Annulation de la synchronisation... Veuillez patienter."
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "No such command: %s"
|
||||
msgstr "Commande invalide : \"%s\""
|
||||
|
||||
#, javascript-format
|
||||
msgid "The command \"%s\" is only available in GUI mode"
|
||||
msgstr ""
|
||||
@@ -562,10 +558,6 @@ msgstr "Chercher dans toutes les notes"
|
||||
msgid "Tools"
|
||||
msgstr "Outils"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Synchronisation status"
|
||||
msgstr "Cible de la synchronisation"
|
||||
|
||||
msgid "Options"
|
||||
msgstr "Options"
|
||||
|
||||
@@ -629,13 +621,6 @@ msgstr "Définir ou modifier alarme"
|
||||
msgid "Layout"
|
||||
msgstr "Disposition"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Some items cannot be synchronised."
|
||||
msgstr "Impossible d'initialiser la synchronisation."
|
||||
|
||||
msgid "View them now"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add or remove tags"
|
||||
msgstr "Gérer les étiquettes"
|
||||
|
||||
@@ -675,10 +660,6 @@ msgstr "Connexion OneDrive"
|
||||
msgid "Import"
|
||||
msgstr "Importer"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Synchronisation Status"
|
||||
msgstr "Cible de la synchronisation"
|
||||
|
||||
msgid "Delete notebook?"
|
||||
msgstr "Supprimer le carnet ?"
|
||||
|
||||
@@ -894,13 +875,6 @@ msgstr ""
|
||||
msgid "Invalid option value: \"%s\". Possible values are: %s."
|
||||
msgstr "Option invalide: \"%s\". Les valeurs possibles sont : %s."
|
||||
|
||||
msgid "Items that cannot be synchronised"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "\"%s\": \"%s\""
|
||||
msgstr ""
|
||||
|
||||
msgid "Sync status (synced items / total items)"
|
||||
msgstr "Status de la synchronisation (objets synchro. / total)"
|
||||
|
||||
@@ -1104,6 +1078,10 @@ msgstr "Bienvenue"
|
||||
#~ msgid "Show/Hide the console"
|
||||
#~ msgstr "Quitter le logiciel."
|
||||
|
||||
#, fuzzy
|
||||
#~ msgid "Last command: %s"
|
||||
#~ msgstr "Commande invalide : \"%s\""
|
||||
|
||||
#~ msgid "Done editing."
|
||||
#~ msgstr "Edition terminée."
|
||||
|
||||
|
@@ -100,10 +100,6 @@ msgstr ""
|
||||
msgid "Cancelling background synchronisation... Please wait."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "No such command: %s"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "The command \"%s\" is only available in GUI mode"
|
||||
msgstr ""
|
||||
@@ -512,9 +508,6 @@ msgstr ""
|
||||
msgid "Tools"
|
||||
msgstr ""
|
||||
|
||||
msgid "Synchronisation status"
|
||||
msgstr ""
|
||||
|
||||
msgid "Options"
|
||||
msgstr ""
|
||||
|
||||
@@ -575,12 +568,6 @@ msgstr ""
|
||||
msgid "Layout"
|
||||
msgstr ""
|
||||
|
||||
msgid "Some items cannot be synchronised."
|
||||
msgstr ""
|
||||
|
||||
msgid "View them now"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add or remove tags"
|
||||
msgstr ""
|
||||
|
||||
@@ -618,9 +605,6 @@ msgstr ""
|
||||
msgid "Import"
|
||||
msgstr ""
|
||||
|
||||
msgid "Synchronisation Status"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete notebook?"
|
||||
msgstr ""
|
||||
|
||||
@@ -828,13 +812,6 @@ msgstr ""
|
||||
msgid "Invalid option value: \"%s\". Possible values are: %s."
|
||||
msgstr ""
|
||||
|
||||
msgid "Items that cannot be synchronised"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "\"%s\": \"%s\""
|
||||
msgstr ""
|
||||
|
||||
msgid "Sync status (synced items / total items)"
|
||||
msgstr ""
|
||||
|
||||
|
2
CliClient/package-lock.json
generated
2
CliClient/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "joplin",
|
||||
"version": "0.10.79",
|
||||
"version": "0.10.77",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@@ -18,7 +18,7 @@
|
||||
],
|
||||
"owner": "Laurent Cozic"
|
||||
},
|
||||
"version": "0.10.79",
|
||||
"version": "0.10.77",
|
||||
"bin": {
|
||||
"joplin": "./main.js"
|
||||
},
|
||||
|
@@ -629,32 +629,4 @@ describe('Synchronizer', function() {
|
||||
done();
|
||||
});
|
||||
|
||||
it('items should skip items that cannot be synced', async (done) => {
|
||||
let folder1 = await Folder.save({ title: "folder1" });
|
||||
let note1 = await Note.save({ title: "un", is_todo: 1, parent_id: folder1.id });
|
||||
const noteId = note1.id;
|
||||
await synchronizer().start();
|
||||
let disabledItems = await BaseItem.syncDisabledItems();
|
||||
expect(disabledItems.length).toBe(0);
|
||||
await Note.save({ id: noteId, title: "un mod", });
|
||||
synchronizer().debugFlags_ = ['cannotSync'];
|
||||
await synchronizer().start();
|
||||
synchronizer().debugFlags_ = [];
|
||||
await synchronizer().start(); // Another sync to check that this item is now excluded from sync
|
||||
|
||||
await switchClient(2);
|
||||
|
||||
await synchronizer().start();
|
||||
let notes = await Note.all();
|
||||
expect(notes.length).toBe(1);
|
||||
expect(notes[0].title).toBe('un');
|
||||
|
||||
await switchClient(1);
|
||||
|
||||
disabledItems = await BaseItem.syncDisabledItems();
|
||||
expect(disabledItems.length).toBe(1);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
@@ -43,9 +43,8 @@ const syncDir = __dirname + '/../tests/sync';
|
||||
const sleepTime = syncTargetId_ == SyncTargetRegistry.nameToId('filesystem') ? 1001 : 400;
|
||||
|
||||
const logger = new Logger();
|
||||
logger.addTarget('console');
|
||||
logger.addTarget('file', { path: logDir + '/log.txt' });
|
||||
logger.setLevel(Logger.LEVEL_WARN);
|
||||
logger.setLevel(Logger.LEVEL_DEBUG);
|
||||
|
||||
BaseItem.loadClass('Note', Note);
|
||||
BaseItem.loadClass('Folder', Folder);
|
||||
|
@@ -259,14 +259,6 @@ class Application extends BaseApplication {
|
||||
}, {
|
||||
label: _('Tools'),
|
||||
submenu: [{
|
||||
label: _('Synchronisation status'),
|
||||
click: () => {
|
||||
this.dispatch({
|
||||
type: 'NAV_GO',
|
||||
routeName: 'Status',
|
||||
});
|
||||
}
|
||||
},{
|
||||
label: _('Options'),
|
||||
click: () => {
|
||||
this.dispatch({
|
||||
|
@@ -39,22 +39,10 @@ class Bridge {
|
||||
return this.window().setSize(width, height);
|
||||
}
|
||||
|
||||
showSaveDialog(options) {
|
||||
const {dialog} = require('electron');
|
||||
if (!options) options = {};
|
||||
if (!('defaultPath' in options) && this.lastSelectedPath_) options.defaultPath = this.lastSelectedPath_;
|
||||
const filePath = dialog.showSaveDialog(options);
|
||||
if (filePath) {
|
||||
this.lastSelectedPath_ = filePath;
|
||||
}
|
||||
return filePath;
|
||||
}
|
||||
|
||||
showOpenDialog(options) {
|
||||
const {dialog} = require('electron');
|
||||
if (!options) options = {};
|
||||
if (!('defaultPath' in options) && this.lastSelectedPath_) options.defaultPath = this.lastSelectedPath_;
|
||||
if (!('createDirectory' in options)) options.createDirectory = true;
|
||||
const filePaths = dialog.showOpenDialog(options);
|
||||
if (filePaths && filePaths.length) {
|
||||
this.lastSelectedPath_ = dirname(filePaths[0]);
|
||||
@@ -83,15 +71,6 @@ class Bridge {
|
||||
return result === 0;
|
||||
}
|
||||
|
||||
showInfoMessageBox(message) {
|
||||
const result = this.showMessageBox({
|
||||
type: 'info',
|
||||
message: message,
|
||||
buttons: [_('OK')],
|
||||
});
|
||||
return result === 0;
|
||||
}
|
||||
|
||||
get Menu() {
|
||||
return require('electron').Menu;
|
||||
}
|
||||
|
@@ -9,18 +9,6 @@ const { _ } = require('lib/locale.js');
|
||||
|
||||
class ConfigScreenComponent extends React.Component {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
settings: {},
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.setState({ settings: this.props.settings });
|
||||
}
|
||||
|
||||
settingToComponent(key, value) {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
|
||||
@@ -40,9 +28,7 @@ class ConfigScreenComponent extends React.Component {
|
||||
};
|
||||
|
||||
const updateSettingValue = (key, value) => {
|
||||
const settings = Object.assign({}, this.state.settings);
|
||||
settings[key] = value;
|
||||
this.setState({ settings: settings });
|
||||
Setting.setValue(key, value);
|
||||
}
|
||||
|
||||
// Component key needs to be key+value otherwise it doesn't update when the settings change.
|
||||
@@ -66,53 +52,22 @@ class ConfigScreenComponent extends React.Component {
|
||||
</div>
|
||||
);
|
||||
} else if (md.type === Setting.TYPE_BOOL) {
|
||||
const onCheckboxClick = (event) => {
|
||||
updateSettingValue(key, !value)
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={key+value} style={rowStyle}>
|
||||
<div style={controlStyle}>
|
||||
<input id={'setting_checkbox_' + key} type="checkbox" defaultChecked={!!value} onChange={(event) => { onCheckboxClick(event) }}/><label onClick={(event) => { onCheckboxClick(event) }} style={labelStyle} htmlFor={'setting_checkbox_' + key}>{md.label()}</label>
|
||||
<label><input type="checkbox" defaultChecked={!!value} onChange={(event) => { updateSettingValue(key, !!event.target.checked) }}/><span style={labelStyle}> {md.label()}</span></label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
} else if (md.type === Setting.TYPE_STRING) {
|
||||
const onTextChange = (event) => {
|
||||
const settings = Object.assign({}, this.state.settings);
|
||||
settings[key] = event.target.value;
|
||||
this.setState({ settings: settings });
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={key+value} style={rowStyle}>
|
||||
<div style={labelStyle}><label>{md.label()}</label></div>
|
||||
<input type="text" style={controlStyle} value={this.state.settings[key]} onChange={(event) => {onTextChange(event)}} />
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
console.warn('Type not implemented: ' + key);
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
onSaveClick() {
|
||||
for (let n in this.state.settings) {
|
||||
if (!this.state.settings.hasOwnProperty(n)) continue;
|
||||
Setting.setValue(n, this.state.settings[n]);
|
||||
}
|
||||
this.props.dispatch({ type: 'NAV_BACK' });
|
||||
}
|
||||
|
||||
onCancelClick() {
|
||||
this.props.dispatch({ type: 'NAV_BACK' });
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const style = this.props.style;
|
||||
const settings = this.state.settings;
|
||||
const settings = this.props.settings;
|
||||
|
||||
const headerStyle = {
|
||||
width: style.width,
|
||||
@@ -122,21 +77,15 @@ class ConfigScreenComponent extends React.Component {
|
||||
padding: 10,
|
||||
};
|
||||
|
||||
const buttonStyle = {
|
||||
display: this.state.settings === this.props.settings ? 'none' : 'inline-block',
|
||||
marginRight: 10,
|
||||
}
|
||||
|
||||
let settingComps = [];
|
||||
let keys = Setting.keys(true, 'desktop');
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i];
|
||||
if (key === 'sync.target') continue;
|
||||
if (!(key in settings)) {
|
||||
console.warn('Missing setting: ' + key);
|
||||
continue;
|
||||
}
|
||||
const md = Setting.settingMetadata(key);
|
||||
if (md.show && !md.show(settings)) continue;
|
||||
const comp = this.settingToComponent(key, settings[key]);
|
||||
if (!comp) continue;
|
||||
settingComps.push(comp);
|
||||
@@ -147,8 +96,6 @@ class ConfigScreenComponent extends React.Component {
|
||||
<Header style={headerStyle} />
|
||||
<div style={containerStyle}>
|
||||
{ settingComps }
|
||||
<button onClick={() => {this.onSaveClick()}} style={buttonStyle}>{_('Save')}</button>
|
||||
<button onClick={() => {this.onCancelClick()}} style={buttonStyle}>{_('Cancel')}</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@@ -41,24 +41,19 @@ class ImportScreenComponent extends React.Component {
|
||||
const messages = this.state.messages.slice();
|
||||
let found = false;
|
||||
|
||||
messages.push({ key: key, text: text });
|
||||
for (let i = 0; i < messages.length; i++) {
|
||||
if (messages[i].key === key) {
|
||||
messages[i].text = text;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) messages.push({ key: key, text: text });
|
||||
|
||||
this.setState({ messages: messages });
|
||||
}
|
||||
|
||||
uniqueMessages() {
|
||||
let output = [];
|
||||
const messages = this.state.messages.slice();
|
||||
let foundKeys = [];
|
||||
for (let i = messages.length - 1; i >= 0; i--) {
|
||||
const msg = messages[i];
|
||||
if (foundKeys.indexOf(msg.key) >= 0) continue;
|
||||
foundKeys.push(msg.key);
|
||||
output.unshift(msg);
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
async doImport() {
|
||||
const filePath = this.props.filePath;
|
||||
const folderTitle = await Folder.findUniqueFolderTitle(filename(filePath));
|
||||
@@ -82,9 +77,10 @@ class ImportScreenComponent extends React.Component {
|
||||
this.addMessage('progress', lastProgress);
|
||||
},
|
||||
onError: (error) => {
|
||||
// Don't display the error directly because most of the time it doesn't matter
|
||||
// (eg. for weird broken HTML, but the note is still imported)
|
||||
console.warn('When importing ENEX file', error);
|
||||
const messages = this.state.messages.slice();
|
||||
let s = error.trace ? error.trace : error.toString();
|
||||
messages.push({ key: 'error_' + (progressCount++), text: s });
|
||||
this.addMessage('error_' + (progressCount++), lastProgress);
|
||||
},
|
||||
}
|
||||
|
||||
@@ -99,7 +95,7 @@ class ImportScreenComponent extends React.Component {
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const style = this.props.style;
|
||||
const messages = this.uniqueMessages();
|
||||
const messages = this.state.messages;
|
||||
|
||||
const messagesStyle = {
|
||||
padding: 10,
|
||||
|
@@ -229,8 +229,8 @@ class MainScreenComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
styles(themeId, width, height, messageBoxVisible) {
|
||||
const styleKey = themeId + '_' + width + '_' + height + '_' + messageBoxVisible;
|
||||
styles(themeId, width, height) {
|
||||
const styleKey = themeId + '_' + width + '_' + height;
|
||||
if (styleKey === this.styleKey_) return this.styles_;
|
||||
|
||||
const theme = themeStyle(themeId);
|
||||
@@ -239,21 +239,12 @@ class MainScreenComponent extends React.Component {
|
||||
|
||||
this.styles_ = {};
|
||||
|
||||
const rowHeight = height - theme.headerHeight;
|
||||
|
||||
this.styles_.header = {
|
||||
width: width,
|
||||
};
|
||||
|
||||
this.styles_.messageBox = {
|
||||
width: width,
|
||||
height: 30,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
paddingLeft: 10,
|
||||
backgroundColor: theme.warningBackgroundColor,
|
||||
}
|
||||
|
||||
const rowHeight = height - theme.headerHeight - (messageBoxVisible ? this.styles_.messageBox.height : 0);
|
||||
|
||||
this.styles_.sideBar = {
|
||||
width: Math.floor(layoutUtils.size(width * .2, 150, 300)),
|
||||
height: rowHeight,
|
||||
@@ -288,10 +279,8 @@ class MainScreenComponent extends React.Component {
|
||||
const promptOptions = this.state.promptOptions;
|
||||
const folders = this.props.folders;
|
||||
const notes = this.props.notes;
|
||||
const messageBoxVisible = this.props.hasDisabledSyncItems;
|
||||
|
||||
const styles = this.styles(this.props.theme, style.width, style.height, messageBoxVisible);
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const styles = this.styles(this.props.theme, style.width, style.height);
|
||||
|
||||
const headerButtons = [];
|
||||
|
||||
@@ -336,21 +325,6 @@ class MainScreenComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const onViewDisabledItemsClick = () => {
|
||||
this.props.dispatch({
|
||||
type: 'NAV_GO',
|
||||
routeName: 'Status',
|
||||
});
|
||||
}
|
||||
|
||||
const messageComp = messageBoxVisible ? (
|
||||
<div style={styles.messageBox}>
|
||||
<span style={theme.textStyle}>
|
||||
{_('Some items cannot be synchronised.')} <a href="#" onClick={() => { onViewDisabledItemsClick() }}>{_('View them now')}</a>
|
||||
</span>
|
||||
</div>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<PromptDialog
|
||||
@@ -365,7 +339,6 @@ class MainScreenComponent extends React.Component {
|
||||
buttons={promptOptions && ('buttons' in promptOptions) ? promptOptions.buttons : null}
|
||||
inputType={promptOptions && ('inputType' in promptOptions) ? promptOptions.inputType : null} />
|
||||
<Header style={styles.header} showBackButton={false} buttons={headerButtons} />
|
||||
{messageComp}
|
||||
<SideBar style={styles.sideBar} />
|
||||
<NoteList style={styles.noteList} />
|
||||
<NoteText style={styles.noteText} visiblePanes={this.props.noteVisiblePanes} />
|
||||
@@ -382,7 +355,6 @@ const mapStateToProps = (state) => {
|
||||
noteVisiblePanes: state.noteVisiblePanes,
|
||||
folders: state.folders,
|
||||
notes: state.notes,
|
||||
hasDisabledSyncItems: state.hasDisabledSyncItems,
|
||||
};
|
||||
};
|
||||
|
||||
|
@@ -174,7 +174,7 @@ class NoteListComponent extends React.Component {
|
||||
}, style);
|
||||
emptyDivStyle.width = emptyDivStyle.width - padding * 2;
|
||||
emptyDivStyle.height = emptyDivStyle.height - padding * 2;
|
||||
return <div style={emptyDivStyle}>{ this.props.folders.length ? _('No notes in here. Create one by clicking on "New note".') : _('There is currently no notebook. Create one by clicking on "New notebook".')}</div>
|
||||
return <div style={emptyDivStyle}>{_('No notes in here. Create one by clicking on "New note".')}</div>
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -193,7 +193,6 @@ class NoteListComponent extends React.Component {
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
notes: state.notes,
|
||||
folders: state.folders,
|
||||
selectedNoteIds: state.selectedNoteIds,
|
||||
theme: state.settings.theme,
|
||||
// uncompletedTodosOnTop: state.settings.uncompletedTodosOnTop,
|
||||
|
@@ -8,7 +8,6 @@ const { Setting } = require('lib/models/setting.js');
|
||||
|
||||
const { MainScreen } = require('./MainScreen.min.js');
|
||||
const { OneDriveLoginScreen } = require('./OneDriveLoginScreen.min.js');
|
||||
const { StatusScreen } = require('./StatusScreen.min.js');
|
||||
const { ImportScreen } = require('./ImportScreen.min.js');
|
||||
const { ConfigScreen } = require('./ConfigScreen.min.js');
|
||||
const { Navigator } = require('./Navigator.min.js');
|
||||
@@ -76,7 +75,6 @@ class RootComponent extends React.Component {
|
||||
OneDriveLogin: { screen: OneDriveLoginScreen, title: () => _('OneDrive Login') },
|
||||
Import: { screen: ImportScreen, title: () => _('Import') },
|
||||
Config: { screen: ConfigScreen, title: () => _('Options') },
|
||||
Status: { screen: StatusScreen, title: () => _('Synchronisation Status') },
|
||||
};
|
||||
|
||||
return (
|
||||
|
@@ -1,135 +0,0 @@
|
||||
const React = require('react');
|
||||
const { connect } = require('react-redux');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { Setting } = require('lib/models/setting.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const { Header } = require('./Header.min.js');
|
||||
const { themeStyle } = require('../theme.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { ReportService } = require('lib/services/report.js');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
class StatusScreenComponent extends React.Component {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {
|
||||
report: [],
|
||||
};
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.resfreshScreen();
|
||||
}
|
||||
|
||||
async resfreshScreen() {
|
||||
const service = new ReportService();
|
||||
const report = await service.status(Setting.value('sync.target'));
|
||||
this.setState({ report: report });
|
||||
}
|
||||
|
||||
async exportDebugReportClick() {
|
||||
const filename = 'syncReport-' + (new Date()).getTime() + '.csv';
|
||||
|
||||
const filePath = bridge().showSaveDialog({
|
||||
title: _('Please select where the sync status should be exported to'),
|
||||
defaultPath: filename,
|
||||
});
|
||||
|
||||
if (!filePath) return;
|
||||
|
||||
const service = new ReportService();
|
||||
const csv = await service.basicItemList({ format: 'csv' });
|
||||
await fs.writeFileSync(filePath, csv);
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const style = this.props.style;
|
||||
|
||||
const headerStyle = {
|
||||
width: style.width,
|
||||
};
|
||||
|
||||
const containerPadding = 10;
|
||||
|
||||
const containerStyle = {
|
||||
padding: containerPadding,
|
||||
overflowY: 'auto',
|
||||
height: style.height - theme.headerHeight - containerPadding * 2,
|
||||
};
|
||||
|
||||
function renderSectionTitleHtml(key, title) {
|
||||
return <h2 key={'section_' + key} style={theme.h2Style}>{title}</h2>
|
||||
}
|
||||
|
||||
function renderSectionHtml(key, section) {
|
||||
let itemsHtml = [];
|
||||
|
||||
itemsHtml.push(renderSectionTitleHtml(section.title, section.title));
|
||||
|
||||
for (let n in section.body) {
|
||||
if (!section.body.hasOwnProperty(n)) continue;
|
||||
itemsHtml.push(<div style={theme.textStyle} key={'item_' + n}>{section.body[n]}</div>);
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={key}>
|
||||
{itemsHtml}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function renderBodyHtml(report) {
|
||||
let output = [];
|
||||
let baseStyle = {
|
||||
paddingLeft: 6,
|
||||
paddingRight: 6,
|
||||
paddingTop: 2,
|
||||
paddingBottom: 2,
|
||||
flex: 0,
|
||||
color: theme.color,
|
||||
fontSize: theme.fontSize,
|
||||
};
|
||||
|
||||
let sectionsHtml = [];
|
||||
|
||||
for (let i = 0; i < report.length; i++) {
|
||||
let section = report[i];
|
||||
if (!section.body.length) continue;
|
||||
sectionsHtml.push(renderSectionHtml(i, section));
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{sectionsHtml}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
let body = renderBodyHtml(this.state.report);
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<Header style={headerStyle} />
|
||||
<div style={containerStyle}>
|
||||
<a style={theme.textStyle} onClick={() => this.exportDebugReportClick()}href="#">Export debug report</a>
|
||||
{body}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
theme: state.settings.theme,
|
||||
settings: state.settings,
|
||||
locale: state.settings.locale,
|
||||
};
|
||||
};
|
||||
|
||||
const StatusScreen = connect(mapStateToProps)(StatusScreenComponent);
|
||||
|
||||
module.exports = { StatusScreen };
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,6 +1,5 @@
|
||||
var locales = {};
|
||||
locales['en_GB'] = require('./en_GB.json');
|
||||
locales['de_DE'] = require('./de_DE.json');
|
||||
locales['es_CR'] = require('./es_CR.json');
|
||||
locales['fr_FR'] = require('./fr_FR.json');
|
||||
module.exports = { locales: locales };
|
2
ElectronClient/app/package-lock.json
generated
2
ElectronClient/app/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Joplin",
|
||||
"version": "0.10.37",
|
||||
"version": "0.10.36",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Joplin",
|
||||
"version": "0.10.37",
|
||||
"version": "0.10.36",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
@@ -25,10 +25,6 @@
|
||||
"win": {
|
||||
"icon": "../../Assets/Joplin.ico"
|
||||
},
|
||||
"nsis": {
|
||||
"oneClick": false,
|
||||
"allowToChangeInstallationDirectory": true
|
||||
},
|
||||
"mac": {
|
||||
"icon": "../../Assets/macOs.icns",
|
||||
"asar": false
|
||||
|
@@ -9,19 +9,6 @@ body, textarea {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
table th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
table td, table th {
|
||||
padding: .5em;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
/* By default, the Ice Editor displays invalid characters, such as non-breaking spaces
|
||||
as red boxes, but since those are actually valid characters and common in imported
|
||||
Evernote data, we hide them here. */
|
||||
|
@@ -25,8 +25,6 @@ const globalStyle = {
|
||||
selectedColor2: "#5A4D70",
|
||||
colorError2: "#ff6c6c",
|
||||
|
||||
warningBackgroundColor: "#FFD08D",
|
||||
|
||||
headerHeight: 35,
|
||||
headerButtonHPadding: 6,
|
||||
|
||||
@@ -71,9 +69,6 @@ globalStyle.textStyle2 = Object.assign({}, globalStyle.textStyle, {
|
||||
color: globalStyle.color2,
|
||||
});
|
||||
|
||||
globalStyle.h2Style = Object.assign({}, globalStyle.textStyle);
|
||||
globalStyle.h2Style.fontSize *= 1.3;
|
||||
|
||||
let themeCache_ = {};
|
||||
|
||||
function themeStyle(theme) {
|
||||
|
@@ -18,9 +18,9 @@ Three types of applications are available: for the **desktop** (Windows, macOS a
|
||||
|
||||
Operating System | Download
|
||||
-----------------|--------
|
||||
Windows | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.37/Joplin-Setup-0.10.37.exe'><img alt='Get it on Windows' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeWindows.png'/></a>
|
||||
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.37/Joplin-0.10.37.dmg'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeMacOS.png'/></a>
|
||||
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.37/Joplin-0.10.37-x86_64.AppImage'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeLinux.png'/></a>
|
||||
Windows | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.36/Joplin-Setup-0.10.36.exe'><img alt='Get it on Windows' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeWindows.png'/></a>
|
||||
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.36/Joplin-0.10.36.dmg'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeMacOS.png'/></a>
|
||||
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.36/Joplin-0.10.36-x86_64.AppImage'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeLinux.png'/></a>
|
||||
|
||||
## Mobile applications
|
||||
|
||||
|
@@ -137,25 +137,6 @@ Since this is still an actual URL, the terminal will still make it clickable. An
|
||||
|
||||
In Markdown, links to resources are represented as a simple ID to the resource. In order to give access to these resources, they will be, like links, converted to local URLs. Clicking this link will then open a browser, which will handle the file - i.e. display the image, open the PDF file, etc.
|
||||
|
||||
# Shell mode
|
||||
|
||||
Commands can also be used directly from a shell. To view the list of available commands, type `joplin help all`. To reference a note, notebook or tag you can either use the ID (type `joplin ls -l` to view the ID) or by title.
|
||||
|
||||
For example, this will create a new note "My note" in the notebook "My notebook":
|
||||
|
||||
$ joplin mkbook "My notebook"
|
||||
$ joplin use "My notebook"
|
||||
$ joplin mknote "My note"
|
||||
|
||||
To view the newly created note:
|
||||
|
||||
$ joplin ls -l
|
||||
fe889 07/12/2017 17:57 My note
|
||||
|
||||
Give a new title to the note:
|
||||
|
||||
$ joplin set fe889 title "New title"
|
||||
|
||||
# Available shortcuts
|
||||
|
||||
There are two types of shortcuts: those that manipulate the user interface directly, such as `TAB` to move from one pane to another, and those that are simply shortcuts to actual commands. In a way similar to Vim, these shortcuts are generally a verb followed by an object. For example, typing `mn` ([m]ake [n]ote), is used to create a new note: it will switch the interface to command line mode and pre-fill it with `mknote ""` from where the title of the note can be entered. See below for the full list of shortcuts:
|
||||
|
@@ -17,11 +17,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.10.5</string>
|
||||
<string>0.10.3</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>5</string>
|
||||
<string>3</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
|
@@ -66,15 +66,10 @@ class BaseApplication {
|
||||
}
|
||||
|
||||
switchCurrentFolder(folder) {
|
||||
if (!this.hasGui()) {
|
||||
this.currentFolder_ = Object.assign({}, folder);
|
||||
Setting.setValue('activeFolderId', folder ? folder.id : '');
|
||||
} else {
|
||||
this.dispatch({
|
||||
type: 'FOLDER_SELECT',
|
||||
id: folder ? folder.id : '',
|
||||
});
|
||||
}
|
||||
this.dispatch({
|
||||
type: 'FOLDER_SELECT',
|
||||
id: folder ? folder.id : '',
|
||||
});
|
||||
}
|
||||
|
||||
// Handles the initial flags passed to main script and
|
||||
@@ -232,10 +227,6 @@ class BaseApplication {
|
||||
return false;
|
||||
}
|
||||
|
||||
uiType() {
|
||||
return this.hasGui() ? 'gui' : 'cli';
|
||||
}
|
||||
|
||||
generalMiddlewareFn() {
|
||||
const middleware = store => next => (action) => {
|
||||
return this.generalMiddleware(store, next, action);
|
||||
|
@@ -100,7 +100,7 @@ class MdToHtml {
|
||||
const href = this.getAttr_(attrs, 'src');
|
||||
|
||||
if (!Resource.isResourceUrl(href)) {
|
||||
return '<img title="' + htmlentities(title) + '" src="' + href + '"/>';
|
||||
return '<span>' + href + '</span><img title="' + htmlentities(title) + '" src="' + href + '"/>';
|
||||
}
|
||||
|
||||
const resourceId = Resource.urlToId(href);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
const React = require('react'); const Component = React.Component;
|
||||
const { connect } = require('react-redux');
|
||||
const { Platform, View, Text, Button, StyleSheet, TouchableOpacity, Image, ScrollView, Dimensions } = require('react-native');
|
||||
const { Platform, View, Text, Button, StyleSheet, TouchableOpacity, Image } = require('react-native');
|
||||
const Icon = require('react-native-vector-icons/Ionicons').default;
|
||||
const { Log } = require('lib/log.js');
|
||||
const { BackButtonService } = require('lib/services/back-button.js');
|
||||
@@ -218,7 +218,7 @@ class ScreenHeaderComponent extends Component {
|
||||
const itemListCsv = await service.basicItemList({ format: 'csv' });
|
||||
const filePath = RNFS.ExternalDirectoryPath + '/syncReport-' + (new Date()).getTime() + '.txt';
|
||||
|
||||
const finalText = [logItemCsv, itemListCsv].join("\n================================================================================\n");
|
||||
const finalText = [logItemCsv, itemListCsv].join("\n--------------------------------------------------------------------------------");
|
||||
|
||||
await RNFS.writeFile(filePath, finalText);
|
||||
alert('Debug report exported to ' + filePath);
|
||||
@@ -410,7 +410,6 @@ class ScreenHeaderComponent extends Component {
|
||||
const backButtonComp = backButton(this.styles(), () => this.backButton_press(), !this.props.historyCanGoBack);
|
||||
const searchButtonComp = this.props.noteSelectionEnabled ? null : searchButton(this.styles(), () => this.searchButton_press());
|
||||
const deleteButtonComp = this.props.noteSelectionEnabled ? deleteButton(this.styles(), () => this.deleteButton_press()) : null;
|
||||
const windowHeight = Dimensions.get('window').height - 50;
|
||||
|
||||
const menuComp = (
|
||||
<Menu onSelect={(value) => this.menu_select(value)} style={this.styles().contextMenu}>
|
||||
@@ -418,9 +417,7 @@ class ScreenHeaderComponent extends Component {
|
||||
<Text style={this.styles().contextMenuTrigger}> ⋮</Text>
|
||||
</MenuTrigger>
|
||||
<MenuOptions>
|
||||
<ScrollView style={{ maxHeight: windowHeight }}>
|
||||
{ menuOptionComponents }
|
||||
</ScrollView>
|
||||
{ menuOptionComponents }
|
||||
</MenuOptions>
|
||||
</Menu>
|
||||
);
|
||||
|
@@ -29,11 +29,9 @@ shared.saveNoteButton_press = async function(comp) {
|
||||
}
|
||||
|
||||
let isNew = !note.id;
|
||||
let titleWasAutoAssigned = false;
|
||||
|
||||
if (isNew && !note.title) {
|
||||
note.title = Note.defaultTitle(note);
|
||||
titleWasAutoAssigned = true;
|
||||
}
|
||||
|
||||
// Save only the properties that have changed
|
||||
@@ -56,11 +54,8 @@ shared.saveNoteButton_press = async function(comp) {
|
||||
// But we preserve the current title and body because
|
||||
// the user might have changed them between the time
|
||||
// saveNoteButton_press was called and the note was
|
||||
// saved (it's done asynchronously).
|
||||
//
|
||||
// If the title was auto-assigned above, we don't restore
|
||||
// it from the state because it will be empty there.
|
||||
if (!titleWasAutoAssigned) note.title = stateNote.title;
|
||||
// saved (it's done asynchronously)
|
||||
note.title = stateNote.title;
|
||||
note.body = stateNote.body;
|
||||
}
|
||||
|
||||
|
@@ -165,16 +165,6 @@ class Database {
|
||||
throw new Error('Unknown enum type or value: ' + type + ', ' + s);
|
||||
}
|
||||
|
||||
static enumName(type, id) {
|
||||
if (type === 'fieldType') {
|
||||
if (id === Database.TYPE_UNKNOWN) return 'unknown';
|
||||
if (id === Database.TYPE_INT) return 'int';
|
||||
if (id === Database.TYPE_TEXT) return 'text';
|
||||
if (id === Database.TYPE_NUMERIC) return 'numeric';
|
||||
throw new Error('Invalid type id: ' + id);
|
||||
}
|
||||
}
|
||||
|
||||
static formatValue(type, value) {
|
||||
if (value === null || value === undefined) return null;
|
||||
if (type == this.TYPE_INT) return Number(value);
|
||||
|
@@ -123,27 +123,15 @@ class FileApiDriverOneDrive {
|
||||
return this.makeItem_(item);
|
||||
}
|
||||
|
||||
async put(path, content, options = null) {
|
||||
put(path, content, options = null) {
|
||||
if (!options) options = {};
|
||||
|
||||
let response = null;
|
||||
|
||||
try {
|
||||
if (options.source == 'file') {
|
||||
response = await this.api_.exec('PUT', this.makePath_(path) + ':/content', null, null, options);
|
||||
} else {
|
||||
options.headers = { 'Content-Type': 'text/plain' };
|
||||
response = await this.api_.exec('PUT', this.makePath_(path) + ':/content', null, content, options);
|
||||
}
|
||||
} catch (error) {
|
||||
if (error && error.code === 'BadRequest' && error.message === 'Maximum request length exceeded.') {
|
||||
error.code = 'cannotSync';
|
||||
error.message = 'Resource exceeds OneDrive max file size (4MB)';
|
||||
}
|
||||
throw error;
|
||||
if (options.source == 'file') {
|
||||
return this.api_.exec('PUT', this.makePath_(path) + ':/content', null, null, options);
|
||||
} else {
|
||||
options.headers = { 'Content-Type': 'text/plain' };
|
||||
return this.api_.exec('PUT', this.makePath_(path) + ':/content', null, content, options);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
delete(path) {
|
||||
|
@@ -194,15 +194,11 @@ function addResourceTag(lines, resource, alt = "") {
|
||||
|
||||
|
||||
function isBlockTag(n) {
|
||||
return n=="div" || n=="p" || n=="dl" || n=="dd" || n == 'dt' || n=="center";
|
||||
return n=="div" || n=="p" || n=="dl" || n=="dd" || n=="center";
|
||||
}
|
||||
|
||||
function isStrongTag(n) {
|
||||
return n == "strong" || n == "b" || n == 'big';
|
||||
}
|
||||
|
||||
function isStrikeTag(n) {
|
||||
return n == "strike" || n == "s" || n == 'del';
|
||||
return n == "strong" || n == "b";
|
||||
}
|
||||
|
||||
function isEmTag(n) {
|
||||
@@ -214,7 +210,7 @@ function isAnchor(n) {
|
||||
}
|
||||
|
||||
function isIgnoredEndTag(n) {
|
||||
return n=="en-note" || n=="en-todo" || n=="span" || n=="body" || n=="html" || n=="font" || n=="br" || n=='hr' || n == 'tbody' || n == 'sup' || n == 'img' || n == 'abbr' || n == 'cite' || n == 'thead' || n == 'small' || n == 'tt' || n == 'sub';
|
||||
return n=="en-note" || n=="en-todo" || n=="span" || n=="body" || n=="html" || n=="font" || n=="br" || n=='hr' || n=='s' || n == 'tbody' || n == 'sup';
|
||||
}
|
||||
|
||||
function isListTag(n) {
|
||||
@@ -223,7 +219,7 @@ function isListTag(n) {
|
||||
|
||||
// Elements that don't require any special treatment beside adding a newline character
|
||||
function isNewLineOnlyEndTag(n) {
|
||||
return n=="div" || n=="p" || n=="li" || n=="h1" || n=="h2" || n=="h3" || n=="h4" || n=="h5" || n=='h6' || n=="dl" || n=="dd" || n == 'dt' || n=="center";
|
||||
return n=="div" || n=="p" || n=="li" || n=="h1" || n=="h2" || n=="h3" || n=="h4" || n=="h5" || n=="dl" || n=="dd" || n=="center";
|
||||
}
|
||||
|
||||
function isCodeTag(n) {
|
||||
@@ -257,27 +253,8 @@ function xmlNodeText(xmlNode) {
|
||||
return xmlNode[0];
|
||||
}
|
||||
|
||||
function attributeToLowerCase(node) {
|
||||
if (!node.attributes) return {};
|
||||
let output = {};
|
||||
for (let n in node.attributes) {
|
||||
if (!node.attributes.hasOwnProperty(n)) continue;
|
||||
output[n.toLowerCase()] = node.attributes[n];
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
function enexXmlToMdArray(stream, resources) {
|
||||
let remainingResources = resources.slice();
|
||||
|
||||
const removeRemainingResource = (id) => {
|
||||
for (let i = 0; i < remainingResources.length; i++) {
|
||||
const r = remainingResources[i];
|
||||
if (r.id === id) {
|
||||
remainingResources.splice(i, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
resources = resources.slice();
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
let state = {
|
||||
@@ -288,7 +265,7 @@ function enexXmlToMdArray(stream, resources) {
|
||||
};
|
||||
|
||||
let options = {};
|
||||
let strict = false;
|
||||
let strict = true;
|
||||
var saxStream = require('sax').createStream(strict, options)
|
||||
|
||||
let section = {
|
||||
@@ -298,18 +275,14 @@ function enexXmlToMdArray(stream, resources) {
|
||||
};
|
||||
|
||||
saxStream.on('error', function(e) {
|
||||
console.warn(e);
|
||||
//reject(e);
|
||||
reject(e);
|
||||
})
|
||||
|
||||
saxStream.on('text', function(text) {
|
||||
if (['table', 'tr', 'tbody'].indexOf(section.type) >= 0) return;
|
||||
section.lines = collapseWhiteSpaceAndAppend(section.lines, state, text);
|
||||
})
|
||||
|
||||
saxStream.on('opentag', function(node) {
|
||||
const nodeAttributes = attributeToLowerCase(node);
|
||||
|
||||
let n = node.name.toLowerCase();
|
||||
if (n == 'en-note') {
|
||||
// Start of note
|
||||
@@ -323,13 +296,10 @@ function enexXmlToMdArray(stream, resources) {
|
||||
};
|
||||
section.lines.push(newSection);
|
||||
section = newSection;
|
||||
} else if (n == 'tbody' || n == 'thead') {
|
||||
} else if (n == 'tbody') {
|
||||
// Ignore it
|
||||
} else if (n == 'tr') {
|
||||
if (section.type != 'table') {
|
||||
console.warn('Found a <tr> tag outside of a table');
|
||||
return;
|
||||
}
|
||||
if (section.type != 'table') throw new Error('Found a <tr> tag outside of a table');
|
||||
|
||||
let newSection = {
|
||||
type: 'tr',
|
||||
@@ -341,10 +311,7 @@ function enexXmlToMdArray(stream, resources) {
|
||||
section.lines.push(newSection);
|
||||
section = newSection;
|
||||
} else if (n == 'td' || n == 'th') {
|
||||
if (section.type != 'tr') {
|
||||
console.warn('Found a <td> tag outside of a <tr>');
|
||||
return;
|
||||
}
|
||||
if (section.type != 'tr') throw new Error('Found a <td> tag outside of a <tr>');
|
||||
|
||||
if (n == 'th') section.isHeader = true;
|
||||
|
||||
@@ -375,27 +342,17 @@ function enexXmlToMdArray(stream, resources) {
|
||||
}
|
||||
} else if (isStrongTag(n)) {
|
||||
section.lines.push("**");
|
||||
} else if (isStrikeTag(n)) {
|
||||
section.lines.push('(');
|
||||
} else if (n == 'samp') {
|
||||
section.lines.push('`');
|
||||
} else if (n == 's') {
|
||||
// Not supported
|
||||
} else if (n == 'q') {
|
||||
section.lines.push('"');
|
||||
} else if (n == 'img') {
|
||||
// TODO: TEST IMAGE
|
||||
if (nodeAttributes.src) { // Many (most?) img tags don't have no source associated, especially when they were imported from HTML
|
||||
let s = '';
|
||||
section.lines.push(s);
|
||||
}
|
||||
} else if (isAnchor(n)) {
|
||||
state.anchorAttributes.push(nodeAttributes);
|
||||
state.anchorAttributes.push(node.attributes);
|
||||
section.lines.push('[');
|
||||
} else if (isEmTag(n)) {
|
||||
section.lines.push("*");
|
||||
} else if (n == "en-todo") {
|
||||
let x = nodeAttributes && nodeAttributes.checked && nodeAttributes.checked.toLowerCase() == 'true' ? 'X' : ' ';
|
||||
let x = node.attributes && node.attributes.checked && node.attributes.checked.toLowerCase() == 'true' ? 'X' : ' ';
|
||||
section.lines.push('- [' + x + '] ');
|
||||
} else if (n == "hr") {
|
||||
// Needs to be surrounded by new lines so that it's properly rendered as a line when converting to HTML
|
||||
@@ -418,20 +375,20 @@ function enexXmlToMdArray(stream, resources) {
|
||||
} else if (n == 'blockquote') {
|
||||
section.lines.push(BLOCK_OPEN);
|
||||
state.inQuote = true;
|
||||
} else if (isCodeTag(n, nodeAttributes)) {
|
||||
} else if (isCodeTag(n, node.attributes)) {
|
||||
section.lines.push(BLOCK_OPEN);
|
||||
state.inCode = true;
|
||||
} else if (n == "br") {
|
||||
section.lines.push(NEWLINE);
|
||||
} else if (n == "en-media") {
|
||||
const hash = nodeAttributes.hash;
|
||||
const hash = node.attributes.hash;
|
||||
|
||||
let resource = null;
|
||||
for (let i = 0; i < resources.length; i++) {
|
||||
let r = resources[i];
|
||||
if (r.id == hash) {
|
||||
resource = r;
|
||||
removeRemainingResource(r.id);
|
||||
resources.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -473,11 +430,11 @@ function enexXmlToMdArray(stream, resources) {
|
||||
// </en-export>
|
||||
|
||||
let found = false;
|
||||
for (let i = 0; i < remainingResources.length; i++) {
|
||||
let r = remainingResources[i];
|
||||
for (let i = 0; i < resources.length; i++) {
|
||||
let r = resources[i];
|
||||
if (!r.id) {
|
||||
r.id = hash;
|
||||
remainingResources[i] = r;
|
||||
resources[i] = r;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
@@ -491,29 +448,27 @@ function enexXmlToMdArray(stream, resources) {
|
||||
// means it's an attachement. It will be appended along with the
|
||||
// other remaining resources at the bottom of the markdown text.
|
||||
if (!!resource.id) {
|
||||
section.lines = addResourceTag(section.lines, resource, nodeAttributes.alt);
|
||||
section.lines = addResourceTag(section.lines, resource, node.attributes.alt);
|
||||
}
|
||||
}
|
||||
} else if (n == "span" || n == "font" || n == 'sup' || n == 'cite' || n == 'abbr' || n == 'small' || n == 'tt' || n == 'sub') {
|
||||
// Inline tags that can be ignored in Markdown
|
||||
} else if (n == "span" || n == "font" || n == 'sup') {
|
||||
// Ignore
|
||||
} else {
|
||||
console.warn("Unsupported start tag: " + n);
|
||||
}
|
||||
})
|
||||
|
||||
saxStream.on('closetag', function(n) {
|
||||
n = n ? n.toLowerCase() : n;
|
||||
|
||||
if (n == 'en-note') {
|
||||
// End of note
|
||||
} else if (isNewLineOnlyEndTag(n)) {
|
||||
section.lines.push(BLOCK_CLOSE);
|
||||
} else if (n == 'td' || n == 'th') {
|
||||
if (section && section.parent) section = section.parent;
|
||||
section = section.parent;
|
||||
} else if (n == 'tr') {
|
||||
if (section && section.parent) section = section.parent;
|
||||
section = section.parent;
|
||||
} else if (n == 'table') {
|
||||
if (section && section.parent) section = section.parent;
|
||||
section = section.parent;
|
||||
} else if (isIgnoredEndTag(n)) {
|
||||
// Skip
|
||||
} else if (isListTag(n)) {
|
||||
@@ -521,10 +476,6 @@ function enexXmlToMdArray(stream, resources) {
|
||||
state.lists.pop();
|
||||
} else if (isStrongTag(n)) {
|
||||
section.lines.push("**");
|
||||
} else if (isStrikeTag(n)) {
|
||||
section.lines.push(')');
|
||||
} else if (n == 'samp') {
|
||||
section.lines.push('`');
|
||||
} else if (isEmTag(n)) {
|
||||
section.lines.push("*");
|
||||
} else if (n == 'q') {
|
||||
@@ -541,10 +492,6 @@ function enexXmlToMdArray(stream, resources) {
|
||||
|
||||
if (section.lines.length < 1) throw new Error('Invalid anchor tag closing'); // Sanity check, but normally not possible
|
||||
|
||||
const pushEmptyAnchor = (url) => {
|
||||
section.lines.push('[link](' + url + ')');
|
||||
}
|
||||
|
||||
// When closing the anchor tag, check if there's is any text content. If not
|
||||
// put the URL as is (don't wrap it in [](url)). The markdown parser, using
|
||||
// GitHub flavour, will turn this URL into a link. This is to generate slightly
|
||||
@@ -552,38 +499,13 @@ function enexXmlToMdArray(stream, resources) {
|
||||
let previous = section.lines[section.lines.length - 1];
|
||||
if (previous == '[') {
|
||||
section.lines.pop();
|
||||
pushEmptyAnchor(url);
|
||||
section.lines.push(url);
|
||||
} else if (!previous || previous == url) {
|
||||
section.lines.pop();
|
||||
section.lines.pop();
|
||||
pushEmptyAnchor(url);
|
||||
section.lines.push(url);
|
||||
} else {
|
||||
// Need to remove any new line character between the current ']' and the previous '['
|
||||
// otherwise it won't render properly.
|
||||
let allSpaces = true;
|
||||
for (let i = section.lines.length - 1; i >= 0; i--) {
|
||||
const c = section.lines[i];
|
||||
if (c === '[') {
|
||||
break;
|
||||
} else {
|
||||
if (c === BLOCK_CLOSE || c === BLOCK_OPEN || c === NEWLINE) {
|
||||
section.lines[i] = SPACE;
|
||||
} else {
|
||||
if (!isWhiteSpace(c)) allSpaces = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allSpaces) {
|
||||
for (let i = section.lines.length - 1; i >= 0; i--) {
|
||||
const c = section.lines.pop();
|
||||
if (c === '[') break;
|
||||
}
|
||||
//section.lines.push(url);
|
||||
pushEmptyAnchor(url);
|
||||
} else {
|
||||
section.lines.push('](' + url + ')');
|
||||
}
|
||||
section.lines.push('](' + url + ')');
|
||||
}
|
||||
} else if (isListTag(n)) {
|
||||
section.lines.push(BLOCK_CLOSE);
|
||||
@@ -605,7 +527,7 @@ function enexXmlToMdArray(stream, resources) {
|
||||
saxStream.on('end', function() {
|
||||
resolve({
|
||||
content: section,
|
||||
resources: remainingResources,
|
||||
resources: resources,
|
||||
});
|
||||
})
|
||||
|
||||
@@ -613,28 +535,50 @@ function enexXmlToMdArray(stream, resources) {
|
||||
});
|
||||
}
|
||||
|
||||
function removeTableCellNewLines(cellText) {
|
||||
return cellText.replace(/\n+/g, " ");
|
||||
function setTableCellContent(table) {
|
||||
if (!table.type == 'table') throw new Error('Only for tables');
|
||||
|
||||
for (let trIndex = 0; trIndex < table.lines.length; trIndex++) {
|
||||
const tr = table.lines[trIndex];
|
||||
for (let tdIndex = 0; tdIndex < tr.lines.length; tdIndex++) {
|
||||
let td = tr.lines[tdIndex];
|
||||
td.content = processMdArrayNewLines(td.lines);
|
||||
td.content = td.content.replace(/\n\n\n\n\n/g, ' ');
|
||||
td.content = td.content.replace(/\n\n\n\n/g, ' ');
|
||||
td.content = td.content.replace(/\n\n\n/g, ' ');
|
||||
td.content = td.content.replace(/\n\n/g, ' ');
|
||||
td.content = td.content.replace(/\n/g, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
function tableHasSubTables(table) {
|
||||
function cellWidth(cellText) {
|
||||
const lines = cellText.split("\n");
|
||||
let maxWidth = 0;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i];
|
||||
if (line.length > maxWidth) maxWidth = line.length;
|
||||
}
|
||||
return maxWidth;
|
||||
}
|
||||
|
||||
function colWidths(table) {
|
||||
let output = [];
|
||||
for (let trIndex = 0; trIndex < table.lines.length; trIndex++) {
|
||||
const tr = table.lines[trIndex];
|
||||
for (let tdIndex = 0; tdIndex < tr.lines.length; tdIndex++) {
|
||||
const td = tr.lines[tdIndex];
|
||||
for (let i = 0; i < td.lines.length; i++) {
|
||||
if (typeof td.lines[i] === 'object') return true;
|
||||
}
|
||||
const w = cellWidth(td.content);
|
||||
if (output.length <= tdIndex) output.push(0);
|
||||
if (w > output[tdIndex]) output[tdIndex] = w;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return output;
|
||||
}
|
||||
|
||||
// Markdown tables don't support tables within tables, which is common in notes that are complete web pages, for example when imported
|
||||
// via Web Clipper. So to handle this, we render all the outer tables as regular text (as if replacing all the <table>, <tr> and <td>
|
||||
// elements by <div>) and only the inner ones, those that don't contain any other tables, are rendered as actual tables. This is generally
|
||||
// the required behaviour since the outer tables are usually for layout and the inner ones are the content.
|
||||
function drawTable(table) {
|
||||
function drawTable(table, colWidths) {
|
||||
// | First Header | Second Header |
|
||||
// | ------------- | ------------- |
|
||||
// | Content Cell | Content Cell |
|
||||
@@ -642,11 +586,8 @@ function drawTable(table) {
|
||||
|
||||
// There must be at least 3 dashes separating each header cell.
|
||||
// https://gist.github.com/IanWang/28965e13cdafdef4e11dc91f578d160d#tables
|
||||
|
||||
const flatRender = tableHasSubTables(table); // Render the table has regular text
|
||||
const minColWidth = 3;
|
||||
let lines = [];
|
||||
lines.push(BLOCK_OPEN);
|
||||
let headerDone = false;
|
||||
for (let trIndex = 0; trIndex < table.lines.length; trIndex++) {
|
||||
const tr = table.lines[trIndex];
|
||||
@@ -654,79 +595,37 @@ function drawTable(table) {
|
||||
let line = [];
|
||||
let headerLine = [];
|
||||
let emptyHeader = null;
|
||||
for (let tdIndex = 0; tdIndex < tr.lines.length; tdIndex++) {
|
||||
const td = tr.lines[tdIndex];
|
||||
for (let tdIndex = 0; tdIndex < colWidths.length; tdIndex++) {
|
||||
const width = Math.max(minColWidth, colWidths[tdIndex]);
|
||||
const cell = tr.lines[tdIndex] ? tr.lines[tdIndex].content : '';
|
||||
line.push(stringPadding(cell, width, ' ', stringPadding.RIGHT));
|
||||
|
||||
if (flatRender) {
|
||||
line.push(BLOCK_OPEN);
|
||||
|
||||
let currentCells = [];
|
||||
|
||||
const renderCurrentCells = () => {
|
||||
if (!currentCells.length) return;
|
||||
const cellText = processMdArrayNewLines(currentCells);
|
||||
line.push(cellText);
|
||||
currentCells = [];
|
||||
if (!headerDone) {
|
||||
if (!isHeader) {
|
||||
if (!emptyHeader) emptyHeader = [];
|
||||
let h = stringPadding(' ', width, ' ', stringPadding.RIGHT);
|
||||
if (!width) h = '';
|
||||
emptyHeader.push(h);
|
||||
}
|
||||
|
||||
// In here, recursively render the tables
|
||||
for (let i = 0; i < td.lines.length; i++) {
|
||||
const c = td.lines[i];
|
||||
if (typeof c === 'object') { // This is a table
|
||||
renderCurrentCells();
|
||||
currentCells = currentCells.concat(drawTable(c));
|
||||
} else { // This is plain text
|
||||
currentCells.push(c);
|
||||
}
|
||||
}
|
||||
|
||||
renderCurrentCells();
|
||||
|
||||
line.push(BLOCK_CLOSE);
|
||||
} else { // Regular table rendering
|
||||
|
||||
// A cell in a Markdown table cannot have new lines so remove them
|
||||
const cellText = removeTableCellNewLines(processMdArrayNewLines(td.lines));
|
||||
|
||||
const width = Math.max(cellText.length, 3);
|
||||
line.push(stringPadding(cellText, width, ' ', stringPadding.RIGHT));
|
||||
|
||||
if (!headerDone) {
|
||||
if (!isHeader) {
|
||||
if (!emptyHeader) emptyHeader = [];
|
||||
let h = stringPadding(' ', width, ' ', stringPadding.RIGHT);
|
||||
emptyHeader.push(h);
|
||||
}
|
||||
headerLine.push('-'.repeat(width));
|
||||
}
|
||||
|
||||
headerLine.push('-'.repeat(width));
|
||||
}
|
||||
}
|
||||
|
||||
if (flatRender) {
|
||||
if (emptyHeader) {
|
||||
lines.push('| ' + emptyHeader.join(' | ') + ' |');
|
||||
lines.push('| ' + headerLine.join(' | ') + ' |');
|
||||
headerDone = true;
|
||||
lines.push(BLOCK_OPEN);
|
||||
lines = lines.concat(line);
|
||||
lines.push(BLOCK_CLOSE);
|
||||
} else {
|
||||
if (emptyHeader) {
|
||||
lines.push('| ' + emptyHeader.join(' | ') + ' |');
|
||||
lines.push('| ' + headerLine.join(' | ') + ' |');
|
||||
headerDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
lines.push('| ' + line.join(' | ') + ' |');
|
||||
lines.push('| ' + line.join(' | ') + ' |');
|
||||
|
||||
if (!headerDone) {
|
||||
lines.push('| ' + headerLine.join(' | ') + ' |');
|
||||
headerDone = true;
|
||||
}
|
||||
if (!headerDone) {
|
||||
lines.push('| ' + headerLine.join(' | ') + ' |');
|
||||
headerDone = true;
|
||||
}
|
||||
}
|
||||
|
||||
lines.push(BLOCK_CLOSE);
|
||||
|
||||
return flatRender ? lines : lines.join('<<<<:D>>>>' + NEWLINE + '<<<<:D>>>>').split('<<<<:D>>>>');
|
||||
return lines.join('<<<<:D>>>>' + NEWLINE + '<<<<:D>>>>').split('<<<<:D>>>>');
|
||||
}
|
||||
|
||||
async function enexXmlToMd(stream, resources) {
|
||||
@@ -737,9 +636,13 @@ async function enexXmlToMd(stream, resources) {
|
||||
for (let i = 0; i < result.content.lines.length; i++) {
|
||||
let line = result.content.lines[i];
|
||||
if (typeof line === 'object') { // A table
|
||||
const table = line;
|
||||
const tableLines = drawTable(table);
|
||||
let table = setTableCellContent(line);
|
||||
//console.log(require('util').inspect(table, false, null))
|
||||
const cw = colWidths(table);
|
||||
const tableLines = drawTable(table, cw);
|
||||
mdLines.push(BLOCK_OPEN);
|
||||
mdLines = mdLines.concat(tableLines);
|
||||
mdLines.push(BLOCK_CLOSE);
|
||||
} else { // an actual line
|
||||
mdLines.push(line);
|
||||
}
|
||||
|
@@ -212,92 +212,51 @@ function importEnex(parentFolderId, filePath, importOptions = null) {
|
||||
async function processNotes() {
|
||||
if (processingNotes) return false;
|
||||
|
||||
try {
|
||||
processingNotes = true;
|
||||
stream.pause();
|
||||
processingNotes = true;
|
||||
stream.pause();
|
||||
|
||||
while (notes.length) {
|
||||
let note = notes.shift();
|
||||
const contentStream = stringToStream(note.bodyXml);
|
||||
const body = await enexXmlToMd(contentStream, note.resources);
|
||||
delete note.bodyXml;
|
||||
let chain = [];
|
||||
while (notes.length) {
|
||||
let note = notes.shift();
|
||||
const contentStream = stringToStream(note.bodyXml);
|
||||
chain.push(() => {
|
||||
return enexXmlToMd(contentStream, note.resources).then((body) => {
|
||||
delete note.bodyXml;
|
||||
|
||||
// console.info('*************************************************************************');
|
||||
// console.info(body);
|
||||
// console.info('*************************************************************************');
|
||||
// console.info('-----------------------------------------------------------');
|
||||
// console.info(body);
|
||||
// console.info('-----------------------------------------------------------');
|
||||
|
||||
note.id = uuid.create();
|
||||
note.parent_id = parentFolderId;
|
||||
note.body = body;
|
||||
note.id = uuid.create();
|
||||
note.parent_id = parentFolderId;
|
||||
note.body = body;
|
||||
|
||||
// Notes in enex files always have a created timestamp but not always an
|
||||
// updated timestamp (it the note has never been modified). For sync
|
||||
// we require an updated_time property, so set it to create_time in that case
|
||||
if (!note.updated_time) note.updated_time = note.created_time;
|
||||
// Notes in enex files always have a created timestamp but not always an
|
||||
// updated timestamp (it the note has never been modified). For sync
|
||||
// we require an updated_time property, so set it to create_time in that case
|
||||
if (!note.updated_time) note.updated_time = note.created_time;
|
||||
|
||||
const result = await saveNoteToStorage(note, importOptions.fuzzyMatching);
|
||||
|
||||
if (result.noteUpdated) {
|
||||
progressState.updated++;
|
||||
} else if (result.noteCreated) {
|
||||
progressState.created++;
|
||||
} else if (result.noteSkipped) {
|
||||
progressState.skipped++;
|
||||
}
|
||||
progressState.resourcesCreated += result.resourcesCreated;
|
||||
progressState.notesTagged += result.notesTagged;
|
||||
importOptions.onProgress(progressState);
|
||||
}
|
||||
} catch(error) {
|
||||
console.error(error);
|
||||
return saveNoteToStorage(note, importOptions.fuzzyMatching);
|
||||
}).then((result) => {
|
||||
if (result.noteUpdated) {
|
||||
progressState.updated++;
|
||||
} else if (result.noteCreated) {
|
||||
progressState.created++;
|
||||
} else if (result.noteSkipped) {
|
||||
progressState.skipped++;
|
||||
}
|
||||
progressState.resourcesCreated += result.resourcesCreated;
|
||||
progressState.notesTagged += result.notesTagged;
|
||||
importOptions.onProgress(progressState);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
stream.resume();
|
||||
processingNotes = false;
|
||||
return true;
|
||||
|
||||
// let chain = [];
|
||||
// while (notes.length) {
|
||||
// let note = notes.shift();
|
||||
// const contentStream = stringToStream(note.bodyXml);
|
||||
// chain.push(() => {
|
||||
// return enexXmlToMd(contentStream, note.resources).then((body) => {
|
||||
// delete note.bodyXml;
|
||||
|
||||
// // console.info('-----------------------------------------------------------');
|
||||
// // console.info(body);
|
||||
// // console.info('-----------------------------------------------------------');
|
||||
|
||||
// note.id = uuid.create();
|
||||
// note.parent_id = parentFolderId;
|
||||
// note.body = body;
|
||||
|
||||
// // Notes in enex files always have a created timestamp but not always an
|
||||
// // updated timestamp (it the note has never been modified). For sync
|
||||
// // we require an updated_time property, so set it to create_time in that case
|
||||
// if (!note.updated_time) note.updated_time = note.created_time;
|
||||
|
||||
// return saveNoteToStorage(note, importOptions.fuzzyMatching);
|
||||
// }).then((result) => {
|
||||
// if (result.noteUpdated) {
|
||||
// progressState.updated++;
|
||||
// } else if (result.noteCreated) {
|
||||
// progressState.created++;
|
||||
// } else if (result.noteSkipped) {
|
||||
// progressState.skipped++;
|
||||
// }
|
||||
// progressState.resourcesCreated += result.resourcesCreated;
|
||||
// progressState.notesTagged += result.notesTagged;
|
||||
// importOptions.onProgress(progressState);
|
||||
// });
|
||||
// });
|
||||
// }
|
||||
|
||||
// return promiseChain(chain).then(() => {
|
||||
// stream.resume();
|
||||
// processingNotes = false;
|
||||
// return true;
|
||||
// });
|
||||
return promiseChain(chain).then(() => {
|
||||
stream.resume();
|
||||
processingNotes = false;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
saxStream.on('error', (error) => {
|
||||
@@ -364,11 +323,7 @@ function importEnex(parentFolderId, filePath, importOptions = null) {
|
||||
noteResourceRecognition.objID = extractRecognitionObjId(data);
|
||||
} else if (note) {
|
||||
if (n == 'content') {
|
||||
if ('bodyXml' in note) {
|
||||
note.bodyXml += data;
|
||||
} else {
|
||||
note.bodyXml = data;
|
||||
}
|
||||
note.bodyXml = data;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@@ -202,7 +202,7 @@ class JoplinDatabase extends Database {
|
||||
// default value and thus might cause problems. In that case, the default value
|
||||
// must be set in the synchronizer too.
|
||||
|
||||
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8];
|
||||
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7];
|
||||
|
||||
let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion);
|
||||
// currentVersionIndex < 0 if for the case where an old version of Joplin used with a newer
|
||||
@@ -265,11 +265,6 @@ class JoplinDatabase extends Database {
|
||||
queries.push('ALTER TABLE resources ADD COLUMN file_extension TEXT NOT NULL DEFAULT ""');
|
||||
}
|
||||
|
||||
if (targetVersion == 8) {
|
||||
queries.push('ALTER TABLE sync_items ADD COLUMN sync_disabled INT NOT NULL DEFAULT "0"');
|
||||
queries.push('ALTER TABLE sync_items ADD COLUMN sync_disabled_reason TEXT NOT NULL DEFAULT ""');
|
||||
}
|
||||
|
||||
queries.push({ sql: 'UPDATE version SET version = ?', params: [targetVersion] });
|
||||
await this.transactionExecBatch(queries);
|
||||
|
||||
|
@@ -339,7 +339,6 @@ class BaseItem extends BaseModel {
|
||||
JOIN sync_items s ON s.item_id = items.id
|
||||
WHERE sync_target = %d
|
||||
AND s.sync_time < items.updated_time
|
||||
AND s.sync_disabled = 0
|
||||
%s
|
||||
LIMIT %d
|
||||
`,
|
||||
@@ -383,21 +382,7 @@ class BaseItem extends BaseModel {
|
||||
throw new Error('Invalid type: ' + type);
|
||||
}
|
||||
|
||||
static async syncDisabledItems(syncTargetId) {
|
||||
const rows = await this.db().selectAll('SELECT * FROM sync_items WHERE sync_disabled = 1 AND sync_target = ?', [syncTargetId]);
|
||||
let output = [];
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const item = await this.loadItem(rows[i].item_type, rows[i].item_id);
|
||||
if (!item) continue; // The referenced item no longer exist
|
||||
output.push({
|
||||
syncInfo: rows[i],
|
||||
item: item,
|
||||
});
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
static updateSyncTimeQueries(syncTarget, item, syncTime, syncDisabled = false, syncDisabledReason = '') {
|
||||
static updateSyncTimeQueries(syncTarget, item, syncTime) {
|
||||
const itemType = item.type_;
|
||||
const itemId = item.id;
|
||||
if (!itemType || !itemId || syncTime === undefined) throw new Error('Invalid parameters in updateSyncTimeQueries()');
|
||||
@@ -408,8 +393,8 @@ class BaseItem extends BaseModel {
|
||||
params: [syncTarget, itemType, itemId],
|
||||
},
|
||||
{
|
||||
sql: 'INSERT INTO sync_items (sync_target, item_type, item_id, sync_time, sync_disabled, sync_disabled_reason) VALUES (?, ?, ?, ?, ?, ?)',
|
||||
params: [syncTarget, itemType, itemId, syncTime, syncDisabled ? 1 : 0, syncDisabledReason + ''],
|
||||
sql: 'INSERT INTO sync_items (sync_target, item_type, item_id, sync_time) VALUES (?, ?, ?, ?)',
|
||||
params: [syncTarget, itemType, itemId, syncTime],
|
||||
}
|
||||
];
|
||||
}
|
||||
@@ -419,12 +404,6 @@ class BaseItem extends BaseModel {
|
||||
return this.db().transactionExecBatch(queries);
|
||||
}
|
||||
|
||||
static async saveSyncDisabled(syncTargetId, item, syncDisabledReason) {
|
||||
const syncTime = 'sync_time' in item ? item.sync_time : 0;
|
||||
const queries = this.updateSyncTimeQueries(syncTargetId, item, syncTime, true, syncDisabledReason);
|
||||
return this.db().transactionExecBatch(queries);
|
||||
}
|
||||
|
||||
// When an item is deleted, its associated sync_items data is not immediately deleted for
|
||||
// performance reason. So this function is used to look for these remaining sync_items and
|
||||
// delete them.
|
||||
|
@@ -126,11 +126,7 @@ class Note extends BaseItem {
|
||||
let r = null;
|
||||
r = noteFieldComp(a.user_updated_time, b.user_updated_time); if (r) return r;
|
||||
r = noteFieldComp(a.user_created_time, b.user_created_time); if (r) return r;
|
||||
|
||||
const titleA = a.title ? a.title.toLowerCase() : '';
|
||||
const titleB = b.title ? b.title.toLowerCase() : '';
|
||||
r = noteFieldComp(titleA, titleB); if (r) return r;
|
||||
|
||||
r = noteFieldComp(a.title.toLowerCase(), b.title.toLowerCase()); if (r) return r;
|
||||
return noteFieldComp(a.id, b.id);
|
||||
}
|
||||
|
||||
|
@@ -22,6 +22,15 @@ class Setting extends BaseModel {
|
||||
this.metadata_ = {
|
||||
'activeFolderId': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'firstStart': { value: true, type: Setting.TYPE_BOOL, public: false },
|
||||
'sync.2.path': { value: '', type: Setting.TYPE_STRING, public: true, appTypes: ['cli'], label: () => _('File system synchronisation target directory'), description: () => _('The path to synchronise with when file system synchronisation is enabled. See `sync.target`.') },
|
||||
'sync.3.auth': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'sync.4.auth': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'sync.1.context': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'sync.2.context': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'sync.3.context': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'sync.4.context': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'sync.5.context': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'sync.6.context': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'editor': { value: '', type: Setting.TYPE_STRING, public: true, appTypes: ['cli'], label: () => _('Text editor'), description: () => _('The editor that will be used to open a note. If none is provided it will try to auto-detect the default editor.') },
|
||||
'locale': { value: defaultLocale(), type: Setting.TYPE_STRING, isEnum: true, public: true, label: () => _('Language'), options: () => {
|
||||
return supportedLocalesToLanguages();
|
||||
@@ -77,15 +86,6 @@ class Setting extends BaseModel {
|
||||
'sync.target': { value: SyncTargetRegistry.nameToId('onedrive'), type: Setting.TYPE_INT, isEnum: true, public: true, label: () => _('Synchronisation target'), description: () => _('The target to synchonise to. If synchronising with the file system, set `sync.2.path` to specify the target directory.'), options: () => {
|
||||
return SyncTargetRegistry.idAndLabelPlainObject();
|
||||
}},
|
||||
'sync.2.path': { value: '', type: Setting.TYPE_STRING, show: (settings) => { return settings['sync.target'] == SyncTargetRegistry.nameToId('filesystem') }, public: true, label: () => _('Directory to synchronise with (absolute path)'), description: () => _('The path to synchronise with when file system synchronisation is enabled. See `sync.target`.') },
|
||||
'sync.3.auth': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'sync.4.auth': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'sync.1.context': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'sync.2.context': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'sync.3.context': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'sync.4.context': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'sync.5.context': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
'sync.6.context': { value: '', type: Setting.TYPE_STRING, public: false },
|
||||
};
|
||||
|
||||
return this.metadata_;
|
||||
|
@@ -25,8 +25,7 @@ const defaultState = {
|
||||
searchQuery: '',
|
||||
settings: {},
|
||||
appState: 'starting',
|
||||
//windowContentSize: { width: 0, height: 0 },
|
||||
hasDisabledSyncItems: false,
|
||||
windowContentSize: { width: 0, height: 0 },
|
||||
};
|
||||
|
||||
// When deleting a note, tag or folder
|
||||
@@ -396,12 +395,6 @@ const reducer = (state = defaultState, action) => {
|
||||
newState.appState = action.state;
|
||||
break;
|
||||
|
||||
case 'SYNC_HAS_DISABLED_SYNC_ITEMS':
|
||||
|
||||
newState = Object.assign({}, state);
|
||||
newState.hasDisabledSyncItems = true;
|
||||
break;
|
||||
|
||||
}
|
||||
} catch (error) {
|
||||
error.message = 'In reducer: ' + error.message + ' Action: ' + JSON.stringify(action);
|
||||
|
@@ -109,21 +109,8 @@ class ReportService {
|
||||
async status(syncTarget) {
|
||||
let r = await this.syncStatus(syncTarget);
|
||||
let sections = [];
|
||||
let section = null;
|
||||
|
||||
const disabledItems = await BaseItem.syncDisabledItems(syncTarget);
|
||||
|
||||
if (disabledItems.length) {
|
||||
section = { title: _('Items that cannot be synchronised'), body: [] };
|
||||
|
||||
for (let i = 0; i < disabledItems.length; i++) {
|
||||
const row = disabledItems[i];
|
||||
section.body.push(_('"%s": "%s"', row.item.title, row.syncInfo.sync_disabled_reason));
|
||||
}
|
||||
sections.push(section);
|
||||
}
|
||||
|
||||
section = { title: _('Sync status (synced items / total items)'), body: [] };
|
||||
let section = { title: _('Sync status (synced items / total items)'), body: [] };
|
||||
|
||||
for (let n in r.items) {
|
||||
if (!r.items.hasOwnProperty(n)) continue;
|
||||
@@ -151,20 +138,17 @@ class ReportService {
|
||||
|
||||
sections.push(section);
|
||||
|
||||
section = { title: _('Coming alarms'), body: [] };
|
||||
|
||||
const alarms = await Alarm.allDue();
|
||||
|
||||
if (alarms.length) {
|
||||
section = { title: _('Coming alarms'), body: [] };
|
||||
|
||||
for (let i = 0; i < alarms.length; i++) {
|
||||
const alarm = alarms[i];
|
||||
const note = await Note.load(alarm.note_id);
|
||||
section.body.push(_('On %s: %s', time.formatMsToLocal(alarm.trigger_time), note.title));
|
||||
}
|
||||
|
||||
sections.push(section);
|
||||
for (let i = 0; i < alarms.length; i++) {
|
||||
const alarm = alarms[i];
|
||||
const note = await Note.load(alarm.note_id);
|
||||
section.body.push(_('On %s: %s', time.formatMsToLocal(alarm.trigger_time), note.title));
|
||||
}
|
||||
|
||||
sections.push(section);
|
||||
|
||||
return sections;
|
||||
}
|
||||
|
||||
|
@@ -253,36 +253,22 @@ class Synchronizer {
|
||||
|
||||
this.logSyncOperation(action, local, remote, reason);
|
||||
|
||||
const handleCannotSyncItem = async (syncTargetId, item, cannotSyncReason) => {
|
||||
await ItemClass.saveSyncDisabled(syncTargetId, item, cannotSyncReason);
|
||||
this.dispatch({ type: 'SYNC_HAS_DISABLED_SYNC_ITEMS' });
|
||||
}
|
||||
|
||||
if (local.type_ == BaseModel.TYPE_RESOURCE && (action == 'createRemote' || (action == 'itemConflict' && remote))) {
|
||||
let remoteContentPath = this.resourceDirName_ + '/' + local.id;
|
||||
try {
|
||||
// TODO: handle node and mobile in the same way
|
||||
if (shim.isNode()) {
|
||||
let resourceContent = '';
|
||||
try {
|
||||
resourceContent = await Resource.content(local);
|
||||
} catch (error) {
|
||||
error.message = 'Cannot read resource content: ' + local.id + ': ' + error.message;
|
||||
this.logger().error(error);
|
||||
this.progressReport_.errors.push(error);
|
||||
}
|
||||
await this.api().put(remoteContentPath, resourceContent);
|
||||
} else {
|
||||
const localResourceContentPath = Resource.fullPath(local);
|
||||
await this.api().put(remoteContentPath, null, { path: localResourceContentPath, source: 'file' });
|
||||
}
|
||||
} catch (error) {
|
||||
if (error && error.code === 'cannotSync') {
|
||||
await handleCannotSyncItem(syncTargetId, local, error.message);
|
||||
action = null;
|
||||
} else {
|
||||
throw error;
|
||||
// TODO: handle node and mobile in the same way
|
||||
if (shim.isNode()) {
|
||||
let resourceContent = '';
|
||||
try {
|
||||
resourceContent = await Resource.content(local);
|
||||
} catch (error) {
|
||||
error.message = 'Cannot read resource content: ' + local.id + ': ' + error.message;
|
||||
this.logger().error(error);
|
||||
this.progressReport_.errors.push(error);
|
||||
}
|
||||
await this.api().put(remoteContentPath, resourceContent);
|
||||
} else {
|
||||
const localResourceContentPath = Resource.fullPath(local);
|
||||
await this.api().put(remoteContentPath, null, { path: localResourceContentPath, source: 'file' });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,27 +285,9 @@ class Synchronizer {
|
||||
// await this.api().setTimestamp(tempPath, local.updated_time);
|
||||
// await this.api().move(tempPath, path);
|
||||
|
||||
let canSync = true;
|
||||
try {
|
||||
if (this.debugFlags_.indexOf('cannotSync') >= 0) {
|
||||
const error = new Error('Testing cannotSync');
|
||||
error.code = 'cannotSync';
|
||||
throw error;
|
||||
}
|
||||
await this.api().put(path, content);
|
||||
} catch (error) {
|
||||
if (error && error.code === 'cannotSync') {
|
||||
await handleCannotSyncItem(syncTargetId, local, error.message);
|
||||
canSync = false;
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
if (canSync) {
|
||||
await this.api().setTimestamp(path, local.updated_time);
|
||||
await ItemClass.saveSyncTime(syncTargetId, local, time.unixMs());
|
||||
}
|
||||
await this.api().put(path, content);
|
||||
await this.api().setTimestamp(path, local.updated_time);
|
||||
await ItemClass.saveSyncTime(syncTargetId, local, time.unixMs());
|
||||
|
||||
} else if (action == 'itemConflict') {
|
||||
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,6 +1,5 @@
|
||||
var locales = {};
|
||||
locales['en_GB'] = require('./en_GB.json');
|
||||
locales['de_DE'] = require('./de_DE.json');
|
||||
locales['es_CR'] = require('./es_CR.json');
|
||||
locales['fr_FR'] = require('./fr_FR.json');
|
||||
module.exports = { locales: locales };
|
886
ReactNativeClient/package-lock.json
generated
886
ReactNativeClient/package-lock.json
generated
@@ -549,15 +549,6 @@
|
||||
"babel-template": "6.26.0"
|
||||
}
|
||||
},
|
||||
"babel-plugin-transform-define": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-define/-/babel-plugin-transform-define-1.3.0.tgz",
|
||||
"integrity": "sha1-lMX5RZyBDHOMx8UMvUSjGCnW8xk=",
|
||||
"requires": {
|
||||
"lodash": "4.17.4",
|
||||
"traverse": "0.6.6"
|
||||
}
|
||||
},
|
||||
"babel-plugin-transform-es2015-arrow-functions": {
|
||||
"version": "6.22.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz",
|
||||
@@ -1201,11 +1192,6 @@
|
||||
"integrity": "sha512-vHDDF/bP9RYpTWtUhpJRhCFdvvp3iDWvEbuDbWgvjUrNGV1MXJrE0MPcwGtEled04m61iwdBLUIHZtDgzWS4ZQ==",
|
||||
"dev": true
|
||||
},
|
||||
"clamp": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz",
|
||||
"integrity": "sha1-ZqDmQBGBbjcZaCj9yMjBRzEshjQ="
|
||||
},
|
||||
"cli-cursor": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
|
||||
@@ -2101,6 +2087,795 @@
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"fsevents": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.3.tgz",
|
||||
"integrity": "sha512-WIr7iDkdmdbxu/Gh6eKEZJL6KPE74/5MEsf2whTOFNxbIoIixogroLdKYqB6FDav4Wavh/lZdzzd3b2KxIXC5Q==",
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"nan": "2.8.0",
|
||||
"node-pre-gyp": "0.6.39"
|
||||
},
|
||||
"dependencies": {
|
||||
"abbrev": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ajv": {
|
||||
"version": "4.11.8",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"co": "4.6.0",
|
||||
"json-stable-stringify": "1.0.1"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"bundled": true
|
||||
},
|
||||
"aproba": {
|
||||
"version": "1.1.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"are-we-there-yet": {
|
||||
"version": "1.1.4",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"delegates": "1.0.0",
|
||||
"readable-stream": "2.2.9"
|
||||
}
|
||||
},
|
||||
"asn1": {
|
||||
"version": "0.2.3",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"assert-plus": {
|
||||
"version": "0.2.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"aws-sign2": {
|
||||
"version": "0.6.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"aws4": {
|
||||
"version": "1.6.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "0.4.2",
|
||||
"bundled": true
|
||||
},
|
||||
"bcrypt-pbkdf": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"tweetnacl": "0.14.5"
|
||||
}
|
||||
},
|
||||
"block-stream": {
|
||||
"version": "0.0.9",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"inherits": "2.0.3"
|
||||
}
|
||||
},
|
||||
"boom": {
|
||||
"version": "2.10.1",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"hoek": "2.16.3"
|
||||
}
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.7",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"balanced-match": "0.4.2",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"buffer-shims": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true
|
||||
},
|
||||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"co": {
|
||||
"version": "4.6.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"code-point-at": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true
|
||||
},
|
||||
"combined-stream": {
|
||||
"version": "1.0.5",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"delayed-stream": "1.0.0"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"bundled": true
|
||||
},
|
||||
"console-control-strings": {
|
||||
"version": "1.1.0",
|
||||
"bundled": true
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true
|
||||
},
|
||||
"cryptiles": {
|
||||
"version": "2.0.5",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"boom": "2.10.1"
|
||||
}
|
||||
},
|
||||
"dashdash": {
|
||||
"version": "1.14.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.8",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"deep-extend": {
|
||||
"version": "0.4.2",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true
|
||||
},
|
||||
"delegates": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"detect-libc": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"ecc-jsbn": {
|
||||
"version": "0.1.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"jsbn": "0.1.1"
|
||||
}
|
||||
},
|
||||
"extend": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"extsprintf": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"form-data": {
|
||||
"version": "2.1.4",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"asynckit": "0.4.0",
|
||||
"combined-stream": "1.0.5",
|
||||
"mime-types": "2.1.15"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true
|
||||
},
|
||||
"fstream": {
|
||||
"version": "1.0.11",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"graceful-fs": "4.1.11",
|
||||
"inherits": "2.0.3",
|
||||
"mkdirp": "0.5.1",
|
||||
"rimraf": "2.6.1"
|
||||
}
|
||||
},
|
||||
"fstream-ignore": {
|
||||
"version": "1.0.5",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"fstream": "1.0.11",
|
||||
"inherits": "2.0.3",
|
||||
"minimatch": "3.0.4"
|
||||
}
|
||||
},
|
||||
"gauge": {
|
||||
"version": "2.7.4",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"aproba": "1.1.1",
|
||||
"console-control-strings": "1.1.0",
|
||||
"has-unicode": "2.0.1",
|
||||
"object-assign": "4.1.1",
|
||||
"signal-exit": "3.0.2",
|
||||
"string-width": "1.0.2",
|
||||
"strip-ansi": "3.0.1",
|
||||
"wide-align": "1.1.2"
|
||||
}
|
||||
},
|
||||
"getpass": {
|
||||
"version": "0.1.7",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "7.1.2",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"fs.realpath": "1.0.0",
|
||||
"inflight": "1.0.6",
|
||||
"inherits": "2.0.3",
|
||||
"minimatch": "3.0.4",
|
||||
"once": "1.4.0",
|
||||
"path-is-absolute": "1.0.1"
|
||||
}
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.1.11",
|
||||
"bundled": true
|
||||
},
|
||||
"har-schema": {
|
||||
"version": "1.0.5",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "4.2.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"ajv": "4.11.8",
|
||||
"har-schema": "1.0.5"
|
||||
}
|
||||
},
|
||||
"has-unicode": {
|
||||
"version": "2.0.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"hawk": {
|
||||
"version": "3.1.3",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"boom": "2.10.1",
|
||||
"cryptiles": "2.0.5",
|
||||
"hoek": "2.16.3",
|
||||
"sntp": "1.0.9"
|
||||
}
|
||||
},
|
||||
"hoek": {
|
||||
"version": "2.16.3",
|
||||
"bundled": true
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.1.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"assert-plus": "0.2.0",
|
||||
"jsprim": "1.4.0",
|
||||
"sshpk": "1.13.0"
|
||||
}
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"once": "1.4.0",
|
||||
"wrappy": "1.0.2"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"bundled": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.4",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"number-is-nan": "1.0.1"
|
||||
}
|
||||
},
|
||||
"is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"isarray": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true
|
||||
},
|
||||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"jodid25519": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"jsbn": "0.1.1"
|
||||
}
|
||||
},
|
||||
"jsbn": {
|
||||
"version": "0.1.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"json-schema": {
|
||||
"version": "0.2.3",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"json-stable-stringify": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"jsonify": "0.0.0"
|
||||
}
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"jsonify": {
|
||||
"version": "0.0.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"jsprim": {
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"extsprintf": "1.0.2",
|
||||
"json-schema": "0.2.3",
|
||||
"verror": "1.3.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.27.0",
|
||||
"bundled": true
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.15",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"mime-db": "1.27.0"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"brace-expansion": "1.1.7"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"bundled": true
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"node-pre-gyp": {
|
||||
"version": "0.6.39",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"detect-libc": "1.0.2",
|
||||
"hawk": "3.1.3",
|
||||
"mkdirp": "0.5.1",
|
||||
"nopt": "4.0.1",
|
||||
"npmlog": "4.1.0",
|
||||
"rc": "1.2.1",
|
||||
"request": "2.81.0",
|
||||
"rimraf": "2.6.1",
|
||||
"semver": "5.3.0",
|
||||
"tar": "2.2.1",
|
||||
"tar-pack": "3.4.0"
|
||||
}
|
||||
},
|
||||
"nopt": {
|
||||
"version": "4.0.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"abbrev": "1.1.0",
|
||||
"osenv": "0.1.4"
|
||||
}
|
||||
},
|
||||
"npmlog": {
|
||||
"version": "4.1.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"are-we-there-yet": "1.1.4",
|
||||
"console-control-strings": "1.1.0",
|
||||
"gauge": "2.7.4",
|
||||
"set-blocking": "2.0.0"
|
||||
}
|
||||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.8.2",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"wrappy": "1.0.2"
|
||||
}
|
||||
},
|
||||
"os-homedir": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"os-tmpdir": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"osenv": {
|
||||
"version": "0.1.4",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"os-homedir": "1.0.2",
|
||||
"os-tmpdir": "1.0.2"
|
||||
}
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true
|
||||
},
|
||||
"performance-now": {
|
||||
"version": "0.2.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "1.0.7",
|
||||
"bundled": true
|
||||
},
|
||||
"punycode": {
|
||||
"version": "1.4.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.4.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"rc": {
|
||||
"version": "1.2.1",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"deep-extend": "0.4.2",
|
||||
"ini": "1.3.4",
|
||||
"minimist": "1.2.0",
|
||||
"strip-json-comments": "2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": {
|
||||
"version": "1.2.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"readable-stream": {
|
||||
"version": "2.2.9",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"buffer-shims": "1.0.0",
|
||||
"core-util-is": "1.0.2",
|
||||
"inherits": "2.0.3",
|
||||
"isarray": "1.0.0",
|
||||
"process-nextick-args": "1.0.7",
|
||||
"string_decoder": "1.0.1",
|
||||
"util-deprecate": "1.0.2"
|
||||
}
|
||||
},
|
||||
"request": {
|
||||
"version": "2.81.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"aws-sign2": "0.6.0",
|
||||
"aws4": "1.6.0",
|
||||
"caseless": "0.12.0",
|
||||
"combined-stream": "1.0.5",
|
||||
"extend": "3.0.1",
|
||||
"forever-agent": "0.6.1",
|
||||
"form-data": "2.1.4",
|
||||
"har-validator": "4.2.1",
|
||||
"hawk": "3.1.3",
|
||||
"http-signature": "1.1.1",
|
||||
"is-typedarray": "1.0.0",
|
||||
"isstream": "0.1.2",
|
||||
"json-stringify-safe": "5.0.1",
|
||||
"mime-types": "2.1.15",
|
||||
"oauth-sign": "0.8.2",
|
||||
"performance-now": "0.2.0",
|
||||
"qs": "6.4.0",
|
||||
"safe-buffer": "5.0.1",
|
||||
"stringstream": "0.0.5",
|
||||
"tough-cookie": "2.3.2",
|
||||
"tunnel-agent": "0.6.0",
|
||||
"uuid": "3.0.1"
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.6.1",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"glob": "7.1.2"
|
||||
}
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.0.1",
|
||||
"bundled": true
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.3.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"set-blocking": {
|
||||
"version": "2.0.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"signal-exit": {
|
||||
"version": "3.0.2",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"sntp": {
|
||||
"version": "1.0.9",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"hoek": "2.16.3"
|
||||
}
|
||||
},
|
||||
"sshpk": {
|
||||
"version": "1.13.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"asn1": "0.2.3",
|
||||
"assert-plus": "1.0.0",
|
||||
"bcrypt-pbkdf": "1.0.1",
|
||||
"dashdash": "1.14.1",
|
||||
"ecc-jsbn": "0.1.1",
|
||||
"getpass": "0.1.7",
|
||||
"jodid25519": "1.0.2",
|
||||
"jsbn": "0.1.1",
|
||||
"tweetnacl": "0.14.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"code-point-at": "1.1.0",
|
||||
"is-fullwidth-code-point": "1.0.0",
|
||||
"strip-ansi": "3.0.1"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "1.0.1",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.0.1"
|
||||
}
|
||||
},
|
||||
"stringstream": {
|
||||
"version": "0.0.5",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
}
|
||||
},
|
||||
"strip-json-comments": {
|
||||
"version": "2.0.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"tar": {
|
||||
"version": "2.2.1",
|
||||
"bundled": true,
|
||||
"requires": {
|
||||
"block-stream": "0.0.9",
|
||||
"fstream": "1.0.11",
|
||||
"inherits": "2.0.3"
|
||||
}
|
||||
},
|
||||
"tar-pack": {
|
||||
"version": "3.4.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"debug": "2.6.8",
|
||||
"fstream": "1.0.11",
|
||||
"fstream-ignore": "1.0.5",
|
||||
"once": "1.4.0",
|
||||
"readable-stream": "2.2.9",
|
||||
"rimraf": "2.6.1",
|
||||
"tar": "2.2.1",
|
||||
"uid-number": "0.0.6"
|
||||
}
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.3.2",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"punycode": "1.4.1"
|
||||
}
|
||||
},
|
||||
"tunnel-agent": {
|
||||
"version": "0.6.0",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"safe-buffer": "5.0.1"
|
||||
}
|
||||
},
|
||||
"tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"uid-number": {
|
||||
"version": "0.0.6",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.0.1",
|
||||
"bundled": true,
|
||||
"optional": true
|
||||
},
|
||||
"verror": {
|
||||
"version": "1.3.6",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"extsprintf": "1.0.2"
|
||||
}
|
||||
},
|
||||
"wide-align": {
|
||||
"version": "1.1.2",
|
||||
"bundled": true,
|
||||
"optional": true,
|
||||
"requires": {
|
||||
"string-width": "1.0.2"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"bundled": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"gauge": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/gauge/-/gauge-1.2.7.tgz",
|
||||
@@ -3984,6 +4759,12 @@
|
||||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
|
||||
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s="
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz",
|
||||
"integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=",
|
||||
"optional": true
|
||||
},
|
||||
"natural-compare": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
|
||||
@@ -4264,21 +5045,6 @@
|
||||
"integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=",
|
||||
"dev": true
|
||||
},
|
||||
"path-to-regexp": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
|
||||
"integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
|
||||
"requires": {
|
||||
"isarray": "0.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"isarray": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
|
||||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
|
||||
}
|
||||
}
|
||||
},
|
||||
"path-type": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz",
|
||||
@@ -4636,32 +5402,11 @@
|
||||
"prop-types": "15.6.0"
|
||||
}
|
||||
},
|
||||
"react-native-dismiss-keyboard": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-dismiss-keyboard/-/react-native-dismiss-keyboard-1.0.0.tgz",
|
||||
"integrity": "sha1-MohiQrPyMX4SHzrrmwpYXiuHm0k="
|
||||
},
|
||||
"react-native-document-picker": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-document-picker/-/react-native-document-picker-2.1.0.tgz",
|
||||
"integrity": "sha512-BFCBXwz8xuLvHLVFVeQM+RhaY8yZ38PEWt9WSbq5VIoZ/VssP6uu51XxOfdwaMALOrAHIojK0SiYnd155upZAg=="
|
||||
},
|
||||
"react-native-drawer-layout": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-drawer-layout/-/react-native-drawer-layout-1.3.2.tgz",
|
||||
"integrity": "sha512-fjO0scqbJUfNu2wuEpvywL7DYLXuCXJ2W/zYhWz986rdLytidbys1QGVvkaszHrb4Y7OqO96mTkgpOcP8KWevw==",
|
||||
"requires": {
|
||||
"react-native-dismiss-keyboard": "1.0.0"
|
||||
}
|
||||
},
|
||||
"react-native-drawer-layout-polyfill": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-drawer-layout-polyfill/-/react-native-drawer-layout-polyfill-1.3.2.tgz",
|
||||
"integrity": "sha512-XzPhfLDJrYHru+e8+dFwhf0FtTeAp7JXPpFYezYV6P1nTeA1Tia/kDpFT+O2DWTrBKBEI8FGhZnThrroZmHIxg==",
|
||||
"requires": {
|
||||
"react-native-drawer-layout": "1.3.2"
|
||||
}
|
||||
},
|
||||
"react-native-dropdownalert": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-dropdownalert/-/react-native-dropdownalert-3.1.2.tgz",
|
||||
@@ -4770,9 +5515,9 @@
|
||||
}
|
||||
},
|
||||
"react-native-popup-menu": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-popup-menu/-/react-native-popup-menu-0.10.0.tgz",
|
||||
"integrity": "sha1-zhU2eo1WKIfVypB+IyMB1BLd5+c="
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-popup-menu/-/react-native-popup-menu-0.8.3.tgz",
|
||||
"integrity": "sha1-HbsLT4iclBC2myKKidV7Vq6lzC4="
|
||||
},
|
||||
"react-native-push-notification": {
|
||||
"version": "3.0.1",
|
||||
@@ -4845,14 +5590,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-native-tab-view": {
|
||||
"version": "0.0.70",
|
||||
"resolved": "https://registry.npmjs.org/react-native-tab-view/-/react-native-tab-view-0.0.70.tgz",
|
||||
"integrity": "sha512-VOTZs2UCamYrTyBtcxpn4Ko9RkWrq6rZGOY7AagHF/gcCUQBMSWADoplsBopSc5vXnHHeCSR458BkceEuSEaQA==",
|
||||
"requires": {
|
||||
"prop-types": "15.6.0"
|
||||
}
|
||||
},
|
||||
"react-native-vector-icons": {
|
||||
"version": "4.4.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-4.4.2.tgz",
|
||||
@@ -4994,27 +5731,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-navigation": {
|
||||
"version": "1.0.0-beta.21",
|
||||
"resolved": "https://registry.npmjs.org/react-navigation/-/react-navigation-1.0.0-beta.21.tgz",
|
||||
"integrity": "sha512-bNAiNBtxrumZQmIj6uH2l1jHIkH8mILoGse5BMHOeFWACADl8LnbE91mzAkJ2EbAzAQ5LNpkSjGSIGxbvfsRCw==",
|
||||
"requires": {
|
||||
"babel-plugin-transform-define": "1.3.0",
|
||||
"clamp": "1.0.1",
|
||||
"hoist-non-react-statics": "2.3.1",
|
||||
"path-to-regexp": "1.7.0",
|
||||
"prop-types": "15.6.0",
|
||||
"react-native-drawer-layout-polyfill": "1.3.2",
|
||||
"react-native-tab-view": "0.0.70"
|
||||
},
|
||||
"dependencies": {
|
||||
"hoist-non-react-statics": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.3.1.tgz",
|
||||
"integrity": "sha1-ND24TGAYxlB3iJgkATWhQg7iLOA="
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-proxy": {
|
||||
"version": "1.1.8",
|
||||
"resolved": "https://registry.npmjs.org/react-proxy/-/react-proxy-1.1.8.tgz",
|
||||
@@ -5330,6 +6046,7 @@
|
||||
"anymatch": "1.3.2",
|
||||
"exec-sh": "0.2.1",
|
||||
"fb-watchman": "2.0.0",
|
||||
"fsevents": "1.1.3",
|
||||
"minimatch": "3.0.4",
|
||||
"minimist": "1.2.0",
|
||||
"walker": "1.0.7",
|
||||
@@ -5832,11 +6549,6 @@
|
||||
"integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=",
|
||||
"dev": true
|
||||
},
|
||||
"traverse": {
|
||||
"version": "0.6.6",
|
||||
"resolved": "https://registry.npmjs.org/traverse/-/traverse-0.6.6.tgz",
|
||||
"integrity": "sha1-y99WD9e5r2MlAv7UD5GMFX6pcTc="
|
||||
},
|
||||
"trim-right": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
|
||||
|
@@ -29,12 +29,11 @@
|
||||
"react-native-image-resizer": "^1.0.0",
|
||||
"react-native-material-dropdown": "^0.5.2",
|
||||
"react-native-popup-dialog": "^0.9.35",
|
||||
"react-native-popup-menu": "^0.10.0",
|
||||
"react-native-popup-menu": "^0.8.3",
|
||||
"react-native-push-notification": "^3.0.1",
|
||||
"react-native-side-menu": "^1.1.3",
|
||||
"react-native-sqlite-storage": "3.3.*",
|
||||
"react-native-vector-icons": "^4.4.2",
|
||||
"react-navigation": "^1.0.0-beta.21",
|
||||
"react-redux": "4.4.8",
|
||||
"redux": "3.6.0",
|
||||
"uuid": "^3.0.1"
|
||||
|
@@ -1,6 +1,5 @@
|
||||
const React = require('react'); const Component = React.Component;
|
||||
const { Keyboard, NativeModules, BackHandler } = require('react-native');
|
||||
const { SafeAreaView } = require('react-navigation');
|
||||
const { connect, Provider } = require('react-redux');
|
||||
const { BackButtonService } = require('lib/services/back-button.js');
|
||||
const AlarmService = require('lib/services/AlarmService.js');
|
||||
@@ -462,7 +461,7 @@ class AppComponent extends React.Component {
|
||||
render() {
|
||||
if (this.props.appState != 'ready') return null;
|
||||
|
||||
const sideMenuContent = <SafeAreaView style={{flex:1}}><SideMenuContent/></SafeAreaView>;
|
||||
const sideMenuContent = <SideMenuContent/>;
|
||||
|
||||
const appNavInit = {
|
||||
Welcome: { screen: WelcomeScreen },
|
||||
@@ -488,9 +487,7 @@ class AppComponent extends React.Component {
|
||||
}}
|
||||
>
|
||||
<MenuContext style={{ flex: 1 }}>
|
||||
<SafeAreaView style={{flex:1}}>
|
||||
<AppNav screens={appNavInit} />
|
||||
</SafeAreaView>
|
||||
<AppNav screens={appNavInit} />
|
||||
<DropdownAlert ref={ref => this.dropdownAlert_ = ref} tapToCloseEnabled={true} />
|
||||
</MenuContext>
|
||||
</SideMenu>
|
||||
|
@@ -218,15 +218,15 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Windows</td>
|
||||
<td><a href='https://github.com/laurent22/joplin/releases/download/v0.10.37/Joplin-Setup-0.10.37.exe'><img alt='Get it on Windows' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeWindows.png'/></a></td>
|
||||
<td><a href='https://github.com/laurent22/joplin/releases/download/v0.10.36/Joplin-Setup-0.10.36.exe'><img alt='Get it on Windows' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeWindows.png'/></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>macOS</td>
|
||||
<td><a href='https://github.com/laurent22/joplin/releases/download/v0.10.37/Joplin-0.10.37.dmg'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeMacOS.png'/></a></td>
|
||||
<td><a href='https://github.com/laurent22/joplin/releases/download/v0.10.36/Joplin-0.10.36.dmg'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeMacOS.png'/></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Linux</td>
|
||||
<td><a href='https://github.com/laurent22/joplin/releases/download/v0.10.37/Joplin-0.10.37-x86_64.AppImage'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeLinux.png'/></a></td>
|
||||
<td><a href='https://github.com/laurent22/joplin/releases/download/v0.10.36/Joplin-0.10.36-x86_64.AppImage'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeLinux.png'/></a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@@ -309,18 +309,7 @@ sudo ln -s ~/.joplin-bin/bin/joplin /usr/bin/joplin
|
||||
<p>Since this is still an actual URL, the terminal will still make it clickable. And with shorter URLs, the text is more readable and the links unlikely to be cut. Both resources (files that are attached to notes) and external links are handled in this way.</p>
|
||||
<h1 id="attachments-resources">Attachments / Resources</h1>
|
||||
<p>In Markdown, links to resources are represented as a simple ID to the resource. In order to give access to these resources, they will be, like links, converted to local URLs. Clicking this link will then open a browser, which will handle the file - i.e. display the image, open the PDF file, etc.</p>
|
||||
<h1 id="shell-mode">Shell mode</h1>
|
||||
<p>Commands can also be used directly from a shell. To view the list of available commands, type <code>joplin help all</code>. To reference a note, notebook or tag you can either use the ID (type <code>joplin ls -l</code> to view the ID) or by title.</p>
|
||||
<p>For example, this will create a new note "My note" in the notebook "My notebook":</p>
|
||||
<pre><code>$ joplin mkbook "My notebook"
|
||||
$ joplin use "My notebook"
|
||||
$ joplin mknote "My note"
|
||||
</code></pre><p>To view the newly created note:</p>
|
||||
<pre><code>$ joplin ls -l
|
||||
fe889 07/12/2017 17:57 My note
|
||||
</code></pre><p>Give a new title to the note:</p>
|
||||
<pre><code>$ joplin set fe889 title "New title"
|
||||
</code></pre><h1 id="available-shortcuts">Available shortcuts</h1>
|
||||
<h1 id="available-shortcuts">Available shortcuts</h1>
|
||||
<p>There are two types of shortcuts: those that manipulate the user interface directly, such as <code>TAB</code> to move from one pane to another, and those that are simply shortcuts to actual commands. In a way similar to Vim, these shortcuts are generally a verb followed by an object. For example, typing <code>mn</code> ([m]ake [n]ote), is used to create a new note: it will switch the interface to command line mode and pre-fill it with <code>mknote ""</code> from where the title of the note can be entered. See below for the full list of shortcuts:</p>
|
||||
<pre><code>Tab Give focus to next pane
|
||||
Shift+Tab Give focus to previous pane
|
||||
|
Reference in New Issue
Block a user