Compare commits
182 Commits
v0.10.16
...
android-v0
Author | SHA1 | Date | |
---|---|---|---|
|
7851b6b429 | ||
|
8eb5f8b74e | ||
|
1830ee9fd2 | ||
|
dd615d6a8f | ||
|
08a518db70 | ||
|
581372de0b | ||
|
fe2c1c197e | ||
|
9854fddeb2 | ||
|
6c3918ebd2 | ||
|
4af496632f | ||
|
cd1e7a1083 | ||
|
4dce9e9e47 | ||
|
dbeff4fd7d | ||
|
aef2e4845d | ||
|
739c6be476 | ||
|
ff502670bf | ||
|
3894dd1191 | ||
|
818537933a | ||
|
ec50808ba1 | ||
|
33fd8325be | ||
|
c72e0a14c0 | ||
|
58bc708014 | ||
|
ede1ed8b22 | ||
|
22e39b4434 | ||
|
9d984596cc | ||
|
fe909f659d | ||
|
ea120eae91 | ||
|
fa819d25b0 | ||
|
65739a5077 | ||
|
228d06e27f | ||
|
d386b83c53 | ||
|
e1b1f31cf1 | ||
|
f80b403dfe | ||
|
5af55d7da3 | ||
|
5e6a389f97 | ||
|
6f26910243 | ||
|
b2056f9e4d | ||
|
dca4bb204b | ||
|
ade39837de | ||
|
2ef2296566 | ||
|
04ed914894 | ||
|
332dd0d859 | ||
|
d9a1f7855d | ||
|
3ec59a835c | ||
|
620225bb2d | ||
|
9d7d469908 | ||
|
16bf0cf646 | ||
|
8079106c3e | ||
|
28143db968 | ||
|
7f1a14fa22 | ||
|
4de1edda05 | ||
|
52f09d2638 | ||
|
97b8cad755 | ||
|
52cb10dd4e | ||
|
135a8a9273 | ||
|
a346116d5f | ||
|
934c3c8001 | ||
|
565c17df37 | ||
|
5ee9a35f7d | ||
|
bd73107853 | ||
|
2e8fe88f53 | ||
|
444c96d5e7 | ||
|
f5ff68b236 | ||
|
d1a83d065a | ||
|
ddb73c8642 | ||
|
4df73cd82c | ||
|
c446e4471d | ||
|
fa22d5bae3 | ||
|
1a610054d3 | ||
|
11517fa037 | ||
|
67a457b9c5 | ||
|
18dc6c826a | ||
|
033d356b56 | ||
|
f9f5974267 | ||
|
6e23fead59 | ||
|
7df6541902 | ||
|
9a40851c77 | ||
|
748acdf03f | ||
|
13d27357a0 | ||
|
c72caad764 | ||
|
2933d09366 | ||
|
f51ad26db7 | ||
|
50ca686727 | ||
|
3612529e52 | ||
|
c053146885 | ||
|
8ccf2ec521 | ||
|
0428917ea2 | ||
|
914a2554ab | ||
|
26bb7dc33b | ||
|
8e5b0eadd9 | ||
|
a96b91cfef | ||
|
03251d4c40 | ||
|
112609c5f1 | ||
|
cc7cbc2ecf | ||
|
946ad7c71a | ||
|
60d2b0c763 | ||
|
d7f3cfd778 | ||
|
a2ae2c766a | ||
|
f52ff730b1 | ||
|
9327a61a36 | ||
|
05997908e5 | ||
|
2a93dea378 | ||
|
fbf7b2cc43 | ||
|
d8b19f7d08 | ||
|
acc4eb5d28 | ||
|
bcd5cd9110 | ||
|
45a4034816 | ||
|
978a08fb06 | ||
|
72dc5a6c99 | ||
|
5340fb8af9 | ||
|
3ce1172c36 | ||
|
e4d48f43d6 | ||
|
3e1ea0eb0a | ||
|
d10d6ba7de | ||
|
cf832354a2 | ||
|
b27519b85a | ||
|
27af0e69ef | ||
|
6283bf6190 | ||
|
48b648e656 | ||
|
367a18db93 | ||
|
f5feb595f6 | ||
|
c6ec0279fc | ||
|
b0b5488c2e | ||
|
3722012da5 | ||
|
c5214b6c44 | ||
|
585ccc2b8b | ||
|
ab8115b89d | ||
|
8d8395c226 | ||
|
8dbec0e8f4 | ||
|
1c1228bb88 | ||
|
7d9eec262a | ||
|
a057fbf3bd | ||
|
518feadc3e | ||
|
b1e351ce77 | ||
|
c6cb2800d7 | ||
|
c31c7a8a67 | ||
|
3d6fe4c2cd | ||
|
da7034ae08 | ||
|
37de5fd4b3 | ||
|
e2cbef1538 | ||
|
4de044dc90 | ||
|
d5dc27d788 | ||
|
e80dd59da2 | ||
|
cbd2075156 | ||
|
4b5c1491d0 | ||
|
ea077852a1 | ||
|
ca20a2a1c2 | ||
|
37c0b6d24a | ||
|
0c14a42b28 | ||
|
f126e0a944 | ||
|
9519bb1218 | ||
|
716b1315a9 | ||
|
6f88c025f5 | ||
|
2f3458e207 | ||
|
bd265e5a9d | ||
|
9d5d197747 | ||
|
ba1a005fcd | ||
|
f09cec794b | ||
|
9619d451f9 | ||
|
240fbf1720 | ||
|
286722fda7 | ||
|
86b5f3582f | ||
|
69ae32a9af | ||
|
504c3d4c0d | ||
|
9e77f01088 | ||
|
409f2ca98d | ||
|
2bfaa0e02c | ||
|
209f2cae55 | ||
|
ce9da90846 | ||
|
43bc7b5619 | ||
|
056741e2c2 | ||
|
01ba0c3078 | ||
|
7d58763fe9 | ||
|
4bb9be8f78 | ||
|
092c09065f | ||
|
fcc2eb6354 | ||
|
94a800aa3c | ||
|
add69aa210 | ||
|
5098c03264 | ||
|
2724167028 | ||
|
eacd0e5cfd | ||
|
7ac6f39658 |
5
.gitignore
vendored
@@ -32,4 +32,7 @@ INFO.md
|
||||
sync_staging.sh
|
||||
*.swp
|
||||
_vieux/
|
||||
_mydocs
|
||||
_mydocs
|
||||
.DS_Store
|
||||
Assets/DownloadBadges*.psd
|
||||
node_modules
|
@@ -28,13 +28,17 @@ matrix:
|
||||
before_install:
|
||||
# HOMEBREW_NO_AUTO_UPDATE needed so that Homebrew doesn't upgrade to the next
|
||||
# version, which requires Ruby 2.3, which is not available on the Travis VM.
|
||||
|
||||
# Silence apt-get update errors (for example when a module doesn't exist) since
|
||||
# otherwise it will make the whole build fails, even though all we need is yarn.
|
||||
- |
|
||||
if [ "$TRAVIS_OS_NAME" == "osx" ]; then
|
||||
HOMEBREW_NO_AUTO_UPDATE=1 brew install yarn
|
||||
else
|
||||
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
||||
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
|
||||
sudo apt-get update && sudo apt-get install yarn
|
||||
sudo apt-get update || true
|
||||
sudo apt-get install -y yarn
|
||||
fi
|
||||
|
||||
script:
|
||||
@@ -42,6 +46,4 @@ script:
|
||||
cd ElectronClient/app
|
||||
rsync -aP ../../ReactNativeClient/lib/ lib/
|
||||
npm install
|
||||
ls -la .
|
||||
ls -la lib
|
||||
yarn dist
|
||||
|
BIN
Assets/All.psd
Normal file
BIN
Assets/DemoDesktop.PNG
Normal file
After Width: | Height: | Size: 92 KiB |
BIN
Assets/DemoDesktop.psd
Normal file
BIN
Assets/Icon-Android-1024.png
Normal file
After Width: | Height: | Size: 92 KiB |
BIN
Assets/Icon-Android-512.png
Normal file
After Width: | Height: | Size: 37 KiB |
BIN
Assets/Icon-Android.psd
Normal file
BIN
Assets/Icon-ios-512.png
Normal file
After Width: | Height: | Size: 30 KiB |
BIN
Assets/Icon-ios.psd
Normal file
BIN
Assets/Laptop-Terminal.psd
Normal file
BIN
Assets/LinuxIcons/1024x1024.png
Normal file
After Width: | Height: | Size: 79 KiB |
BIN
Assets/LinuxIcons/128x128.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
Assets/LinuxIcons/144x144.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
Assets/LinuxIcons/16x16.png
Normal file
After Width: | Height: | Size: 679 B |
BIN
Assets/LinuxIcons/24x24.png
Normal file
After Width: | Height: | Size: 968 B |
BIN
Assets/LinuxIcons/256x256.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
Assets/LinuxIcons/32x32.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
Assets/LinuxIcons/48x48.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
Assets/LinuxIcons/512x512.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
Assets/LinuxIcons/72x72.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
Assets/LinuxIcons/96x96.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 151 KiB After Width: | Height: | Size: 151 KiB |
Before Width: | Height: | Size: 170 KiB After Width: | Height: | Size: 170 KiB |
BIN
Assets/Screenshots/Screenshot_1511192771.png
Normal file
After Width: | Height: | Size: 205 KiB |
BIN
Assets/Screenshots/Screenshot_1511192805.png
Normal file
After Width: | Height: | Size: 300 KiB |
BIN
Assets/Screenshots/Screenshot_1511193130.png
Normal file
After Width: | Height: | Size: 117 KiB |
BIN
Assets/Screenshots/Screenshot_1511193142.png
Normal file
After Width: | Height: | Size: 277 KiB |
BIN
Assets/Screenshots/Screenshot_1511193169.png
Normal file
After Width: | Height: | Size: 133 KiB |
BIN
Assets/Screenshots/Screenshot_1511193181.png
Normal file
After Width: | Height: | Size: 214 KiB |
BIN
Assets/Screenshots/Screenshot_1511193188.png
Normal file
After Width: | Height: | Size: 181 KiB |
BIN
Assets/Screenshots/Screenshot_1511193192.png
Normal file
After Width: | Height: | Size: 133 KiB |
BIN
Assets/Screenshots/iOS/Screenshot_iPad_Paysage.jpg
Normal file
After Width: | Height: | Size: 288 KiB |
BIN
Assets/Screenshots/iOS/Screenshot_iPad_Paysage.png
Normal file
After Width: | Height: | Size: 335 KiB |
BIN
Assets/Screenshots/iOS/Screenshot_iPad_Paysage.psd
Normal file
BIN
Assets/Screenshots/iOS/Screenshot_iPhone_Portrait.jpg
Normal file
After Width: | Height: | Size: 226 KiB |
BIN
Assets/Screenshots/iOS/Screenshot_iPhone_Portrait.png
Normal file
After Width: | Height: | Size: 254 KiB |
BIN
Assets/Screenshots/iOS/Screenshot_iPhone_Portrait.psd
Normal file
BIN
Assets/Tablet.psd
Normal file
2
Assets/check-square-o.svg
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1472 930v318q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q63 0 117 25 15 7 18 23 3 17-9 29l-49 49q-10 10-23 10-3 0-9-2-23-6-45-6h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-254q0-13 9-22l64-64q10-10 23-10 6 0 12 3 20 8 20 29zm231-489l-814 814q-24 24-57 24t-57-24l-430-430q-24-24-24-57t24-57l110-110q24-24 57-24t57 24l263 263 647-647q24-24 57-24t57 24l110 110q24 24 24 57t-24 57z"/></svg>
|
After Width: | Height: | Size: 612 B |
2
Assets/check-square.svg
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<svg width="1792" height="1792" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M813 1299l614-614q19-19 19-45t-19-45l-102-102q-19-19-45-19t-45 19l-467 467-211-211q-19-19-45-19t-45 19l-102 102q-19 19-19 45t19 45l358 358q19 19 45 19t45-19zm851-883v960q0 119-84.5 203.5t-203.5 84.5h-960q-119 0-203.5-84.5t-84.5-203.5v-960q0-119 84.5-203.5t203.5-84.5h960q119 0 203.5 84.5t84.5 203.5z"/></svg>
|
After Width: | Height: | Size: 447 B |
BIN
Assets/iOSIcons/29.png
Normal file
After Width: | Height: | Size: 1.1 KiB |
BIN
Assets/iOSIcons/29x2.png
Normal file
After Width: | Height: | Size: 2.2 KiB |
BIN
Assets/iOSIcons/29x3.png
Normal file
After Width: | Height: | Size: 3.4 KiB |
BIN
Assets/iOSIcons/40.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
Assets/iOSIcons/40x2.png
Normal file
After Width: | Height: | Size: 3.1 KiB |
BIN
Assets/iOSIcons/40x3.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
Assets/iOSIcons/57.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
Assets/iOSIcons/57x2.png
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
Assets/iOSIcons/60x2.png
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
Assets/iOSIcons/60x3.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
Assets/iOSIcons/AppStore.png
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
Assets/macOs.iconset/icon_1024x1024.png
Normal file
After Width: | Height: | Size: 79 KiB |
2
Assets/square-o.svg
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<svg viewBox='0 0 1792 1792' xmlns='http://www.w3.org/2000/svg'><path d='M1312 256h-832q-66 0-113 47t-47 113v832q0 66 47 113t113 47h832q66 0 113-47t47-113v-832q0-66-47-113t-113-47zm288 160v832q0 119-84.5 203.5t-203.5 84.5h-832q-119 0-203.5-84.5t-84.5-203.5v-832q0-119 84.5-203.5t203.5-84.5h832q119 0 203.5 84.5t84.5 203.5z'/></svg>
|
After Width: | Height: | Size: 370 B |
38
BUILD.md
Normal file
@@ -0,0 +1,38 @@
|
||||
# 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. For this reasons, it's generally better to get the CLI app to build first so that everything is setup correctly.
|
||||
|
||||
## macOS dependencies
|
||||
|
||||
brew install yarn node xgettext
|
||||
echo 'export PATH="/usr/local/opt/gettext/bin:$PATH"' >> ~/.bash_profile
|
||||
source ~/.bash_profile
|
||||
|
||||
## 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/
|
||||
|
||||
# Building the Electron application
|
||||
|
||||
```
|
||||
cd ElectronClient/app
|
||||
rsync -a ../../ReactNativeClient/lib/ lib/
|
||||
npm install
|
||||
yarn dist
|
||||
```
|
||||
|
||||
If there's an error `while loading shared libraries: libgconf-2.so.4: cannot open shared object file: No such file or directory`, run `sudo apt-get install libgconf-2-4`
|
||||
|
||||
That will create the executable file in the `dist` directory.
|
||||
|
||||
From `/ElectronClient` you can also run `run.sh` to run the app for testing.
|
||||
|
||||
# Building the Mobile application
|
||||
|
||||
From `/ReactNativeClient`, run `npm install`, then `react-native run-ios` or `react-native run-android`.
|
||||
|
||||
# Building the Terminal application
|
||||
|
||||
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
|
@@ -6,6 +6,7 @@ const { Note } = require('lib/models/note.js');
|
||||
const { Resource } = require('lib/models/resource.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const { reducer, defaultState } = require('lib/reducer.js');
|
||||
const { splitCommandString } = require('lib/string-utils.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
|
||||
@@ -166,7 +167,7 @@ class AppGui {
|
||||
});
|
||||
this.rootWidget_.connect(noteList, (state) => {
|
||||
return {
|
||||
selectedNoteId: state.selectedNoteId,
|
||||
selectedNoteId: state.selectedNoteIds.length ? state.selectedNoteIds[0] : null,
|
||||
items: state.notes,
|
||||
};
|
||||
});
|
||||
@@ -180,7 +181,7 @@ class AppGui {
|
||||
};
|
||||
this.rootWidget_.connect(noteText, (state) => {
|
||||
return {
|
||||
noteId: state.selectedNoteId,
|
||||
noteId: state.selectedNoteIds.length ? state.selectedNoteIds[0] : null,
|
||||
notes: state.notes,
|
||||
};
|
||||
});
|
||||
@@ -194,7 +195,7 @@ class AppGui {
|
||||
borderRightWidth: 1,
|
||||
};
|
||||
this.rootWidget_.connect(noteMetadata, (state) => {
|
||||
return { noteId: state.selectedNoteId };
|
||||
return { noteId: state.selectedNoteIds.length ? state.selectedNoteIds[0] : null };
|
||||
});
|
||||
noteMetadata.hide();
|
||||
|
||||
@@ -320,13 +321,16 @@ class AppGui {
|
||||
action: async () => {
|
||||
if (this.widget('folderList').hasFocus) {
|
||||
const item = this.widget('folderList').selectedJoplinItem;
|
||||
|
||||
if (!item) return;
|
||||
|
||||
if (item.type_ === BaseModel.TYPE_FOLDER) {
|
||||
await this.processCommand('rmbook ' + item.id);
|
||||
} else if (item.type_ === BaseModel.TYPE_TAG) {
|
||||
this.stdout(_('To delete a tag, untag the associated notes.'));
|
||||
} else if (item.type_ === BaseModel.TYPE_SEARCH) {
|
||||
this.store().dispatch({
|
||||
type: 'SEARCH_REMOVE',
|
||||
type: 'SEARCH_DELETE',
|
||||
id: item.id,
|
||||
});
|
||||
}
|
||||
@@ -338,6 +342,10 @@ class AppGui {
|
||||
}
|
||||
};
|
||||
|
||||
shortcuts['BACKSPACE'] = {
|
||||
alias: 'DELETE',
|
||||
};
|
||||
|
||||
shortcuts[' '] = {
|
||||
friendlyName: 'SPACE',
|
||||
description: () => _('Set a to-do as completed / not completed'),
|
||||
@@ -518,22 +526,22 @@ class AppGui {
|
||||
return;
|
||||
}
|
||||
|
||||
let note = this.widget('noteList').currentItem;
|
||||
let folder = this.widget('folderList').currentItem;
|
||||
let args = cliUtils.splitCommandString(cmd);
|
||||
try {
|
||||
let note = this.widget('noteList').currentItem;
|
||||
let folder = this.widget('folderList').currentItem;
|
||||
let args = splitCommandString(cmd);
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] == '$n') {
|
||||
args[i] = note ? note.id : '';
|
||||
} else if (args[i] == '$b') {
|
||||
args[i] = folder ? folder.id : '';
|
||||
} else if (args[i] == '$c') {
|
||||
const item = this.activeListItem();
|
||||
args[i] = item ? item.id : '';
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] == '$n') {
|
||||
args[i] = note ? note.id : '';
|
||||
} else if (args[i] == '$b') {
|
||||
args[i] = folder ? folder.id : '';
|
||||
} else if (args[i] == '$c') {
|
||||
const item = this.activeListItem();
|
||||
args[i] = item ? item.id : '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.app().execCommand(args);
|
||||
} catch (error) {
|
||||
this.stdout(error.message);
|
||||
@@ -764,7 +772,10 @@ class AppGui {
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
const shortcutKey = this.currentShortcutKeys_.join('');
|
||||
const cmd = shortcutKey in this.shortcuts_ ? this.shortcuts_[shortcutKey] : null;
|
||||
let cmd = shortcutKey in this.shortcuts_ ? this.shortcuts_[shortcutKey] : null;
|
||||
|
||||
// If this command is an alias to another command, resolve to the actual command
|
||||
if (cmd && cmd.alias) cmd = this.shortcuts_[cmd.alias];
|
||||
|
||||
let processShortcutKeys = !this.app().currentCommand() && cmd;
|
||||
if (cmd && cmd.canRunAlongOtherCommands) processShortcutKeys = true;
|
||||
|
@@ -168,9 +168,9 @@ class Application extends BaseApplication {
|
||||
await doExit();
|
||||
}, 5000);
|
||||
|
||||
if (await reg.syncStarted()) {
|
||||
if (await reg.syncTarget().syncStarted()) {
|
||||
this.stdout(_('Cancelling background synchronisation... Please wait.'));
|
||||
const sync = await reg.synchronizer(Setting.value('sync.target'));
|
||||
const sync = await reg.syncTarget().synchronizer();
|
||||
await sync.cancel();
|
||||
}
|
||||
|
||||
|
@@ -6,7 +6,7 @@ const processArgs = process.argv.splice(2, process.argv.length);
|
||||
|
||||
const silentLog = processArgs.indexOf('--silent') >= 0;
|
||||
|
||||
const { basename, dirname } = require('lib/path-utils.js');
|
||||
const { basename, dirname, filename, fileExtension } = require('lib/path-utils.js');
|
||||
const fs = require('fs-extra');
|
||||
const gettextParser = require('gettext-parser');
|
||||
|
||||
@@ -68,6 +68,9 @@ function buildLocale(inputFile, outputFile) {
|
||||
}
|
||||
|
||||
async function removePoHeaderDate(filePath) {
|
||||
// Note: on macOS this will fail because it needs to be 'sed -i ""'
|
||||
// Solution would be to install gsed, detect it here, and use it in place of sed in macOS
|
||||
// https://stackoverflow.com/questions/30003570/how-to-use-gnu-sed-on-mac-os-x#34815955
|
||||
await execCommand('sed -i -e\'/POT-Creation-Date:/d\' "' + filePath + '"');
|
||||
await execCommand('sed -i -e\'/PO-Revision-Date:/d\' "' + filePath + '"');
|
||||
}
|
||||
@@ -110,6 +113,17 @@ function buildIndex(locales) {
|
||||
return output.join("\n");
|
||||
}
|
||||
|
||||
function availableLocales(defaultLocale) {
|
||||
const output = [defaultLocale];
|
||||
fs.readdirSync(cliLocalesDir).forEach((path) => {
|
||||
if (fileExtension(path) !== 'po') return;
|
||||
const locale = filename(path);
|
||||
if (locale === defaultLocale) return;
|
||||
output.push(locale);
|
||||
});
|
||||
return output;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let potFilePath = cliLocalesDir + '/joplin.pot';
|
||||
let jsonLocalesDir = cliDir + '/build/locales';
|
||||
@@ -131,7 +145,7 @@ async function main() {
|
||||
|
||||
fs.mkdirpSync(jsonLocalesDir, 0o755);
|
||||
|
||||
let locales = [defaultLocale, 'fr_FR'];
|
||||
let locales = availableLocales(defaultLocale);
|
||||
for (let i = 0; i < locales.length; i++) {
|
||||
const locale = locales[i];
|
||||
const poFilePäth = cliLocalesDir + '/' + locale + '.po';
|
||||
|
@@ -1,18 +1,20 @@
|
||||
const fs = require('fs-extra');
|
||||
const { fileExtension, basename, dirname } = require('lib/path-utils.js');
|
||||
const { _, setLocale, languageCode } = require('lib/locale.js');
|
||||
const marked = require('lib/marked.js');
|
||||
const marked = require('marked');
|
||||
const Mustache = require('mustache');
|
||||
|
||||
const headerHtml = `
|
||||
<!doctype html>
|
||||
const headerHtml = `<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Joplin - a free, open source, te taking and to-do application with synchronisation capabilities</title>
|
||||
<title>Joplin - an open source note taking and to-do application with synchronisation capabilities</title>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
|
||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
|
||||
<link rel="stylesheet" href="https://opensource.keycdn.com/fontawesome/4.7.0/font-awesome.min.css" integrity="sha384-dNpIIXE8U05kAbPhy3G1cz+yZmTzA6CY8Vg/u2L9xRnHjJiAK76m2BIEaSEV+/aU" crossorigin="anonymous">
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha256-k2WSCIexGzOj3Euiig+TlR8gA0EmPjuc79OEeY5L45g=" crossorigin="anonymous"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #F1F1F1;
|
||||
@@ -22,7 +24,8 @@ const headerHtml = `
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
td, th {
|
||||
padding: .5em 1em .5em 0;
|
||||
padding: .8em;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
h1, h2 {
|
||||
border-bottom: 1px solid #eaecef;
|
||||
@@ -88,6 +91,10 @@ const headerHtml = `
|
||||
.cli-screenshot .prompt {
|
||||
color: #48C2F0;
|
||||
}
|
||||
.top-screenshot {
|
||||
margin-top: 2em;
|
||||
text-align: center;
|
||||
}
|
||||
.header {
|
||||
position: relative;
|
||||
padding-left: 2em;
|
||||
@@ -108,7 +115,68 @@ const headerHtml = `
|
||||
right: 0;
|
||||
top:0;
|
||||
}
|
||||
</style>
|
||||
.nav-wrapper {
|
||||
position: relative;
|
||||
width: inherit;
|
||||
}
|
||||
.nav {
|
||||
background-color: black;
|
||||
display: table;
|
||||
width: inherit;
|
||||
}
|
||||
.nav.sticky {
|
||||
position:fixed;
|
||||
top: 0;
|
||||
width: inherit;
|
||||
box-shadow: 0 0 10px #000000;
|
||||
}
|
||||
.nav a {
|
||||
color: white;
|
||||
display: inline-block;
|
||||
padding: .6em .9em .6em .9em;
|
||||
}
|
||||
.nav ul {
|
||||
padding-left: 2em;
|
||||
margin-bottom: 0;
|
||||
display: table-cell;
|
||||
min-width: 165px;
|
||||
}
|
||||
.nav ul li {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
}
|
||||
.nav li.selected {
|
||||
background-color: #222;
|
||||
font-weight: bold;
|
||||
}
|
||||
.nav-right {
|
||||
display: table-cell;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
vertical-align: middle;
|
||||
line-height: 0;
|
||||
}
|
||||
.nav-right .share-btn {
|
||||
display: none;
|
||||
}
|
||||
.share-btn-github {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav-right .small-share-btn {
|
||||
display: none;
|
||||
}
|
||||
.nav-right .share-btn-github {
|
||||
display: inline-block;
|
||||
}
|
||||
@media all and (min-width: 400px) {
|
||||
.nav-right .share-btn {
|
||||
display: inline-block;
|
||||
}
|
||||
.nav-right .small-share-btn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -116,9 +184,24 @@ const headerHtml = `
|
||||
<div class="container">
|
||||
|
||||
<div class="header">
|
||||
<a class="forkme" href="https://github.com/laurent22/joplin"><img src="docs/images/ForkMe.png"/></a>
|
||||
<h1 id="joplin"><img class="title-icon" src="docs/images/Icon512.png">oplin</h1>
|
||||
<p class="sub-title">A free, open source, note taking and to-do application with synchronisation capabilities.</p>
|
||||
<a class="forkme" href="https://github.com/laurent22/joplin"><img src="{{{imageBaseUrl}}}/ForkMe.png"/></a>
|
||||
<h1 id="joplin"><img class="title-icon" src="{{{imageBaseUrl}}}/Icon512.png">oplin</h1>
|
||||
<p class="sub-title">An open source note taking and to-do application with synchronisation capabilities.</p>
|
||||
</div>
|
||||
|
||||
<div class="nav-wrapper">
|
||||
<div class="nav">
|
||||
<ul>
|
||||
<li class="{{selectedHome}}"><a href="{{baseUrl}}/" title="Home"><i class="fa fa-home"></i></a></li>
|
||||
<li class="{{selectedTerminal}}"><a href="{{baseUrl}}/terminal" title="Terminal"><i class="fa fa-terminal"></i></a></li>
|
||||
<li class="{{selectedDesktop}}"><a href="{{baseUrl}}/desktop" title="Desktop"><i class="fa fa-desktop"></i></a></li>
|
||||
</ul>
|
||||
<div class="nav-right">
|
||||
<iframe class="share-btn" src="https://www.facebook.com/plugins/share_button.php?href=http%3A%2F%2Fjoplin.cozic.net&layout=button&size=small&mobile_iframe=true&width=60&height=20&appId" width="60" height="20" style="border:none;overflow:hidden" scrolling="no" frameborder="0" allowTransparency="true"></iframe>
|
||||
<iframe class="share-btn" src="https://platform.twitter.com/widgets/tweet_button.html?url=http%3A%2F%2Fjoplin.cozic.net" width="62" height="20" title="Tweet" style="border: 0; overflow: hidden;"></iframe>
|
||||
<iframe class="share-btn share-btn-github" src="https://ghbtns.com/github-btn.html?user=laurent22&repo=joplin&type=star&count=true" frameborder="0" scrolling="0" width="80px" height="20px"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content">
|
||||
@@ -157,26 +240,40 @@ const footerHtml = `
|
||||
// Grocery shopping list 📝
|
||||
// Work itinerary
|
||||
// Tuesday random note
|
||||
// Vacation plans ☀️
|
||||
// Vacation plans ☀
|
||||
// </pre>
|
||||
// </td>
|
||||
// </tr>
|
||||
// </table>
|
||||
// `;
|
||||
|
||||
const gaHtml = `
|
||||
const scriptHtml = `
|
||||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
function stickyHeader() {
|
||||
if ($(window).scrollTop() > 179) {
|
||||
$('.nav').addClass('sticky');
|
||||
} else {
|
||||
$('.nav').removeClass('sticky');
|
||||
}
|
||||
}
|
||||
|
||||
ga('create', 'UA-103586105-1', 'auto');
|
||||
ga('send', 'pageview');
|
||||
$(window).scroll(function() {
|
||||
stickyHeader();
|
||||
});
|
||||
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
||||
ga('create', 'UA-103586105-1', 'auto');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
||||
`;
|
||||
|
||||
// <a href="#" class="small-share-btn" style="background-color: #365899;"><img src="images/ShareFacebook.svg" style=" width: 1.2em; height: 1.2em"/></a>
|
||||
// <a href="#" class="small-share-btn" style="background-color: #1b95e0;"><img src="images/ShareTwitter.svg" style=" width: 1.2em; height: 1.2em"/></a>
|
||||
// <a href="#" class="small-share-btn" style="background-color: #eee;"><img src="images/ShareGithub.svg" style=" width: 1.2em; height: 1.2em"/></a>
|
||||
|
||||
const rootDir = dirname(dirname(__dirname));
|
||||
|
||||
function markdownToHtml(md) {
|
||||
@@ -193,14 +290,29 @@ function markdownToHtml(md) {
|
||||
|
||||
//output = output.replace(/<!-- \[SCREENSHOTS\] -->/, screenshotHtml);
|
||||
|
||||
return headerHtml + output + gaHtml + footerHtml;
|
||||
return headerHtml + output + scriptHtml + footerHtml;
|
||||
}
|
||||
|
||||
function renderFileToHtml(sourcePath, targetPath, params) {
|
||||
const md = fs.readFileSync(sourcePath, 'utf8');
|
||||
params.baseUrl = 'http://joplin.cozic.net';
|
||||
params.imageBaseUrl = params.baseUrl + '/images';
|
||||
const html = Mustache.render(markdownToHtml(md), params);
|
||||
fs.writeFileSync(targetPath, html);
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const md = fs.readFileSync(rootDir + '/README.md', 'utf8');
|
||||
const html = markdownToHtml(md);
|
||||
renderFileToHtml(rootDir + '/README.md', rootDir + '/docs/index.html', {
|
||||
selectedHome: 'selected',
|
||||
});
|
||||
|
||||
fs.writeFileSync(rootDir + '/index.html', html);
|
||||
renderFileToHtml(rootDir + '/README_terminal.md', rootDir + '/docs/terminal/index.html', {
|
||||
selectedTerminal: 'selected',
|
||||
});
|
||||
|
||||
renderFileToHtml(rootDir + '/README_desktop.md', rootDir + '/docs/desktop/index.html', {
|
||||
selectedDesktop: 'selected',
|
||||
});
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
|
@@ -5,75 +5,6 @@ const stringPadding = require('string-padding');
|
||||
|
||||
const cliUtils = {};
|
||||
|
||||
cliUtils.splitCommandString = function(command) {
|
||||
let args = [];
|
||||
let state = "start"
|
||||
let current = ""
|
||||
let quote = "\""
|
||||
let escapeNext = false;
|
||||
for (let i = 0; i < command.length; i++) {
|
||||
let c = command[i]
|
||||
|
||||
if (state == "quotes") {
|
||||
if (c != quote) {
|
||||
current += c
|
||||
} else {
|
||||
args.push(current)
|
||||
current = ""
|
||||
state = "start"
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (escapeNext) {
|
||||
current += c;
|
||||
escapeNext = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == "\\") {
|
||||
escapeNext = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (c == '"' || c == '\'') {
|
||||
state = "quotes"
|
||||
quote = c
|
||||
continue
|
||||
}
|
||||
|
||||
if (state == "arg") {
|
||||
if (c == ' ' || c == '\t') {
|
||||
args.push(current)
|
||||
current = ""
|
||||
state = "start"
|
||||
} else {
|
||||
current += c
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (c != ' ' && c != "\t") {
|
||||
state = "arg"
|
||||
current += c
|
||||
}
|
||||
}
|
||||
|
||||
if (state == "quotes") {
|
||||
throw new Error("Unclosed quote in command line: " + command)
|
||||
}
|
||||
|
||||
if (current != "") {
|
||||
args.push(current)
|
||||
}
|
||||
|
||||
if (args.length <= 0) {
|
||||
throw new Error("Empty command line")
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
cliUtils.printArray = function(logFunction, rows, headers = null) {
|
||||
if (!rows.length) return '';
|
||||
|
||||
|
36
CliClient/app/command-export-sync-status.js
Normal file
@@ -0,0 +1,36 @@
|
||||
const { BaseCommand } = require('./base-command.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const { app } = require('./app.js');
|
||||
const { Setting } = require('lib/models/setting.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { ReportService } = require('lib/services/report.js');
|
||||
const fs = require('fs-extra');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
usage() {
|
||||
return 'export-sync-status';
|
||||
}
|
||||
|
||||
description() {
|
||||
return 'Export sync status';
|
||||
}
|
||||
|
||||
hidden() {
|
||||
return true;
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
const service = new ReportService();
|
||||
const csv = await service.basicItemList({ format: 'csv' });
|
||||
const filePath = Setting.value('profileDir') + '/syncReport-' + (new Date()).getTime() + '.csv';
|
||||
await fs.writeFileSync(filePath, csv);
|
||||
this.stdout('Sync status exported to ' + filePath);
|
||||
|
||||
app().gui().showConsole();
|
||||
app().gui().maximizeConsole();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Command;
|
@@ -2,7 +2,7 @@ const { BaseCommand } = require('./base-command.js');
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { Folder } = require('lib/models/folder.js');
|
||||
const { importEnex } = require('import-enex');
|
||||
const { importEnex } = require('lib/import-enex');
|
||||
const { filename, basename } = require('lib/path-utils.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
|
||||
|
@@ -16,7 +16,7 @@ class Command extends BaseCommand {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.syncTarget_ = null;
|
||||
this.syncTargetId_ = null;
|
||||
this.releaseLockFn_ = null;
|
||||
this.oneDriveApiUtils_ = null;
|
||||
}
|
||||
@@ -62,6 +62,27 @@ class Command extends BaseCommand {
|
||||
});
|
||||
}
|
||||
|
||||
async doAuth(syncTargetId) {
|
||||
const syncTarget = reg.syncTarget(this.syncTargetId_);
|
||||
this.oneDriveApiUtils_ = new OneDriveApiNodeUtils(syncTarget.api());
|
||||
const auth = await this.oneDriveApiUtils_.oauthDance({
|
||||
log: (...s) => { return this.stdout(...s); }
|
||||
});
|
||||
this.oneDriveApiUtils_ = null;
|
||||
return auth;
|
||||
}
|
||||
|
||||
cancelAuth() {
|
||||
if (this.oneDriveApiUtils_) {
|
||||
this.oneDriveApiUtils_.cancelOAuthDance();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
doingAuth() {
|
||||
return !!this.oneDriveApiUtils_;
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
this.releaseLockFn_ = null;
|
||||
|
||||
@@ -91,25 +112,24 @@ class Command extends BaseCommand {
|
||||
};
|
||||
|
||||
try {
|
||||
this.syncTarget_ = Setting.value('sync.target');
|
||||
if (args.options.target) this.syncTarget_ = args.options.target;
|
||||
this.syncTargetId_ = Setting.value('sync.target');
|
||||
if (args.options.target) this.syncTargetId_ = args.options.target;
|
||||
|
||||
if (this.syncTarget_ == Setting.SYNC_TARGET_ONEDRIVE && !reg.syncHasAuth(this.syncTarget_)) {
|
||||
const syncTarget = reg.syncTarget(this.syncTargetId_);
|
||||
|
||||
if (!syncTarget.isAuthenticated()) {
|
||||
app().gui().showConsole();
|
||||
app().gui().maximizeConsole();
|
||||
this.oneDriveApiUtils_ = new OneDriveApiNodeUtils(reg.oneDriveApi());
|
||||
const auth = await this.oneDriveApiUtils_.oauthDance({
|
||||
log: (...s) => { return this.stdout(...s); }
|
||||
});
|
||||
this.oneDriveApiUtils_ = null;
|
||||
Setting.setValue('sync.3.auth', auth ? JSON.stringify(auth) : null);
|
||||
|
||||
const auth = await this.doAuth(this.syncTargetId_);
|
||||
Setting.setValue('sync.' + this.syncTargetId_ + '.auth', auth ? JSON.stringify(auth) : null);
|
||||
if (!auth) {
|
||||
this.stdout(_('Authentication was not completed (did not receive an authentication token).'));
|
||||
return cleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
let sync = await reg.synchronizer(this.syncTarget_);
|
||||
const sync = await syncTarget.synchronizer();
|
||||
|
||||
let options = {
|
||||
onProgress: (report) => {
|
||||
@@ -123,13 +143,13 @@ class Command extends BaseCommand {
|
||||
randomFailures: args.options['random-failures'] === true,
|
||||
};
|
||||
|
||||
this.stdout(_('Synchronisation target: %s (%s)', Setting.enumOptionLabel('sync.target', this.syncTarget_), this.syncTarget_));
|
||||
this.stdout(_('Synchronisation target: %s (%s)', Setting.enumOptionLabel('sync.target', this.syncTargetId_), this.syncTargetId_));
|
||||
|
||||
if (!sync) throw new Error(_('Cannot initialize synchroniser.'));
|
||||
|
||||
this.stdout(_('Starting synchronisation...'));
|
||||
|
||||
const contextKey = 'sync.' + this.syncTarget_ + '.context';
|
||||
const contextKey = 'sync.' + this.syncTargetId_ + '.context';
|
||||
let context = Setting.value(contextKey);
|
||||
|
||||
context = context ? JSON.parse(context) : {};
|
||||
@@ -156,26 +176,28 @@ class Command extends BaseCommand {
|
||||
}
|
||||
|
||||
async cancel() {
|
||||
if (this.oneDriveApiUtils_) {
|
||||
this.oneDriveApiUtils_.cancelOAuthDance();
|
||||
if (this.doingAuth()) {
|
||||
this.cancelAuth();
|
||||
return;
|
||||
}
|
||||
|
||||
const target = this.syncTarget_ ? this.syncTarget_ : Setting.value('sync.target');
|
||||
const syncTargetId = this.syncTargetId_ ? this.syncTargetId_ : Setting.value('sync.target');
|
||||
|
||||
cliUtils.redrawDone();
|
||||
|
||||
this.stdout(_('Cancelling... Please wait.'));
|
||||
|
||||
if (reg.syncHasAuth(target)) {
|
||||
let sync = await reg.synchronizer(target);
|
||||
const syncTarget = reg.syncTarget(syncTargetId);
|
||||
|
||||
if (syncTarget.isAuthenticated()) {
|
||||
const sync = await syncTarget.synchronizer();
|
||||
if (sync) await sync.cancel();
|
||||
} else {
|
||||
if (this.releaseLockFn_) this.releaseLockFn_();
|
||||
this.releaseLockFn_ = null;
|
||||
}
|
||||
|
||||
this.syncTarget_ = null;
|
||||
this.syncTargetId_ = null;
|
||||
}
|
||||
|
||||
cancellable() {
|
||||
|
@@ -1,3 +1,4 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
./build.sh && NODE_PATH=build node build/build-website.js
|
||||
ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
"$ROOT_DIR/build.sh" && NODE_PATH="$ROOT_DIR/build" node "$ROOT_DIR/build/build-website.js"
|
@@ -8,53 +8,5 @@ rsync -a "$ROOT_DIR/../ReactNativeClient/lib/" "$BUILD_DIR/lib/"
|
||||
cp "$ROOT_DIR/package.json" "$BUILD_DIR"
|
||||
chmod 755 "$BUILD_DIR/main.js"
|
||||
|
||||
cd "$BUILD_DIR"
|
||||
node build-translation.js --silent
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||
|
||||
# # require('cache-require-paths');
|
||||
|
||||
# mkdir -p "$ROOT_DIR/build"
|
||||
# rm -f "$ROOT_DIR/app/lib"
|
||||
# ln -s "$ROOT_DIR/../ReactNativeClient/lib" "$ROOT_DIR/app"
|
||||
|
||||
# npm run build || exit 1
|
||||
|
||||
# # Files under app/gui are in ES6 already but I cannot get Babel
|
||||
# # to ignore them, so copy them back to the build directory.
|
||||
# rsync -a "$ROOT_DIR/app/gui/" "$ROOT_DIR/build/gui/"
|
||||
|
||||
# cp "$ROOT_DIR/package.json" "$ROOT_DIR/build"
|
||||
|
||||
# chmod 755 "$ROOT_DIR/build/main.js"
|
||||
|
||||
# # if [[ ! -f "$ROOT_DIR/package.json.md5" ]]; then
|
||||
# # "$ROOT_DIR/update-package-md5.sh"
|
||||
# # fi
|
||||
|
||||
# # Add modules on top of main.js:
|
||||
# # - cache-require-paths to cache require() calls
|
||||
# # - app-module-path so that lib/something paths can be resolved.
|
||||
|
||||
# #PACKAGE_MD5=$(cat "$ROOT_DIR/package.json.md5")
|
||||
# MAIN_PATH="$ROOT_DIR/build/main.js"
|
||||
# #LINE_TO_ADD="var osTmpdir = require('os-tmpdir'); process.env.CACHE_REQUIRE_PATHS_FILE = osTmpdir() + '/joplin-module-path-cache-$PACKAGE_MD5'; require('cache-require-paths'); require('app-module-path').addPath(__dirname);"
|
||||
# LINE_TO_ADD="require('app-module-path').addPath(__dirname);"
|
||||
# RESULT="$(grep "$LINE_TO_ADD" "$MAIN_PATH")"
|
||||
# if [[ -z "$RESULT" ]]; then
|
||||
# echo "Adding extra modules..."
|
||||
# sed -i "2i $LINE_TO_ADD" "$MAIN_PATH"
|
||||
# else
|
||||
# echo "Extra modules already added."
|
||||
# fi
|
||||
|
||||
# NODE_PATH="$ROOT_DIR/build" node "$ROOT_DIR/build/build-translation.js" --silent
|
||||
# cd "$BUILD_DIR"
|
||||
# node build-translation.js --silent
|
@@ -490,6 +490,21 @@ msgstr ""
|
||||
msgid "Quit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cut"
|
||||
msgstr ""
|
||||
|
||||
msgid "Paste"
|
||||
msgstr ""
|
||||
|
||||
msgid "Search in all the notes"
|
||||
msgstr ""
|
||||
|
||||
msgid "Tools"
|
||||
msgstr ""
|
||||
|
||||
@@ -544,18 +559,27 @@ msgstr ""
|
||||
msgid "Separate each tag by a comma."
|
||||
msgstr ""
|
||||
|
||||
msgid "Rename notebook:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Layout"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add or remove tags"
|
||||
msgstr ""
|
||||
|
||||
msgid "Switch between note and to-do"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete notes?"
|
||||
msgstr ""
|
||||
|
||||
msgid "No notes in here. Create one by clicking on \"New note\"."
|
||||
msgstr ""
|
||||
|
||||
@@ -566,31 +590,43 @@ msgstr ""
|
||||
msgid "Attach file"
|
||||
msgstr ""
|
||||
|
||||
msgid "Set alarm"
|
||||
msgstr ""
|
||||
|
||||
msgid "Refresh"
|
||||
msgstr ""
|
||||
|
||||
msgid "Clear"
|
||||
msgstr ""
|
||||
|
||||
msgid "OneDrive Login"
|
||||
msgstr ""
|
||||
|
||||
msgid "Import"
|
||||
msgstr ""
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete notebook?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remove this tag from all the notes?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remove this search from the sidebar?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Rename"
|
||||
msgstr ""
|
||||
|
||||
msgid "Synchronise"
|
||||
msgstr ""
|
||||
|
||||
msgid "Notebooks"
|
||||
msgstr ""
|
||||
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
msgid "Synchronise"
|
||||
msgid "Searches"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
@@ -601,6 +637,15 @@ msgstr ""
|
||||
msgid "Unknown flag: %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "File system"
|
||||
msgstr ""
|
||||
|
||||
msgid "OneDrive"
|
||||
msgstr ""
|
||||
|
||||
msgid "OneDrive Dev (For testing only)"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unknown log level: %s"
|
||||
msgstr ""
|
||||
@@ -615,8 +660,12 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Please set the \"sync.2.path\" config value to the desired synchronisation "
|
||||
"destination."
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
"This error often happens when using OneDrive for Business, which "
|
||||
"unfortunately cannot be supported.\n"
|
||||
"\n"
|
||||
"Please consider using a regular OneDrive account."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
@@ -687,10 +736,6 @@ msgstr ""
|
||||
msgid "Cannot move note to \"%s\" notebook"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Invalid option value: \"%s\". Possible values are: %s."
|
||||
msgstr ""
|
||||
|
||||
msgid "File system synchronisation target directory"
|
||||
msgstr ""
|
||||
|
||||
@@ -699,20 +744,6 @@ msgid ""
|
||||
"See `sync.target`."
|
||||
msgstr ""
|
||||
|
||||
msgid "Synchronisation target"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The target to synchonise to. If synchronising with the file system, set "
|
||||
"`sync.2.path` to specify the target directory."
|
||||
msgstr ""
|
||||
|
||||
msgid "File system"
|
||||
msgstr ""
|
||||
|
||||
msgid "OneDrive"
|
||||
msgstr ""
|
||||
|
||||
msgid "Text editor"
|
||||
msgstr ""
|
||||
|
||||
@@ -724,6 +755,12 @@ msgstr ""
|
||||
msgid "Language"
|
||||
msgstr ""
|
||||
|
||||
msgid "Date format"
|
||||
msgstr ""
|
||||
|
||||
msgid "Time format"
|
||||
msgstr ""
|
||||
|
||||
msgid "Theme"
|
||||
msgstr ""
|
||||
|
||||
@@ -757,9 +794,24 @@ msgstr ""
|
||||
msgid "%d hours"
|
||||
msgstr ""
|
||||
|
||||
msgid "Automatically update the application"
|
||||
msgstr ""
|
||||
|
||||
msgid "Show advanced options"
|
||||
msgstr ""
|
||||
|
||||
msgid "Synchronisation target"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The target to synchonise to. If synchronising with the file system, set "
|
||||
"`sync.2.path` to specify the target directory."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Invalid option value: \"%s\". Possible values are: %s."
|
||||
msgstr ""
|
||||
|
||||
msgid "Sync status (synced items / total items)"
|
||||
msgstr ""
|
||||
|
||||
@@ -786,15 +838,44 @@ msgstr ""
|
||||
msgid "%s: %d notes"
|
||||
msgstr ""
|
||||
|
||||
msgid "Coming alarms"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "On %s: %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "There are currently no notes. Create one by clicking on the (+) button."
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete these notes?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Log"
|
||||
msgstr ""
|
||||
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
msgid "Export Debug Report"
|
||||
msgstr ""
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "Move to notebook..."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Move %d notes to notebook \"%s\"?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select date"
|
||||
msgstr ""
|
||||
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cancel synchronisation"
|
||||
msgstr ""
|
||||
|
||||
@@ -814,10 +895,17 @@ msgstr ""
|
||||
msgid "Discard changes"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete note"
|
||||
#, javascript-format
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to regular note"
|
||||
msgid "Attach photo"
|
||||
msgstr ""
|
||||
|
||||
msgid "Attach any file"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to todo"
|
||||
@@ -829,10 +917,7 @@ msgstr ""
|
||||
msgid "Show metadata"
|
||||
msgstr ""
|
||||
|
||||
msgid "View location on map"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit"
|
||||
msgid "View on map"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete notebook"
|
||||
|
1002
CliClient/locales/es_CR.po
Normal file
@@ -28,7 +28,7 @@ msgid "Exit command line mode"
|
||||
msgstr "Sortir du mode de ligne de commande"
|
||||
|
||||
msgid "Edit the selected note"
|
||||
msgstr "Editer la note sélectionnée"
|
||||
msgstr "Éditer la note sélectionnée"
|
||||
|
||||
msgid "Cancel the current command."
|
||||
msgstr "Annuler la commande en cours."
|
||||
@@ -114,7 +114,7 @@ msgid "%s: %s"
|
||||
msgstr "%s : %s"
|
||||
|
||||
msgid "Your choice: "
|
||||
msgstr "Votre choix :"
|
||||
msgstr "Votre choix : "
|
||||
|
||||
#, javascript-format
|
||||
msgid "Invalid answer: %s"
|
||||
@@ -157,7 +157,7 @@ msgid ""
|
||||
"Duplicates the notes matching <note> to [notebook]. If no notebook is "
|
||||
"specified the note is duplicated in the current notebook."
|
||||
msgstr ""
|
||||
"Copie les notes correspondant à <nom> vers [carnet]. Si aucun carnet n'est "
|
||||
"Copier les notes correspondant à <nom> vers [carnet]. Si aucun carnet n'est "
|
||||
"spécifié, la note est dupliquée sur place."
|
||||
|
||||
msgid "Marks a to-do as done."
|
||||
@@ -168,7 +168,7 @@ msgid "Note is not a to-do: \"%s\""
|
||||
msgstr "La note n'est pas une tâche : \"%s\""
|
||||
|
||||
msgid "Edit note."
|
||||
msgstr "Editer la note."
|
||||
msgstr "Éditer la note."
|
||||
|
||||
msgid ""
|
||||
"No text editor is defined. Please set it using `config editor <editor-path>`"
|
||||
@@ -185,7 +185,7 @@ msgstr "Cette note n'existe pas : \"%s\". La créer ?"
|
||||
|
||||
msgid "Starting to edit note. Close the editor to get back to the prompt."
|
||||
msgstr ""
|
||||
"Edition de la note en cours. Fermez l'éditeur de texte pour retourner à "
|
||||
"Édition de la note en cours. Fermez l'éditeur de texte pour retourner à "
|
||||
"l'invite de commande."
|
||||
|
||||
msgid "Note has been saved."
|
||||
@@ -228,8 +228,8 @@ msgid ""
|
||||
"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."
|
||||
msgstr ""
|
||||
"Dans n'importe quelle commande, une note ou carnet peut être référé par "
|
||||
"titre ou identifiant, ou en utilisant les raccourcis `$n` et `$b` pour, "
|
||||
"Dans une commande, une note ou carnet peut être référé par titre ou "
|
||||
"identifiant, ou en utilisant les raccourcis `$n` et `$b` pour, "
|
||||
"respectivement, la note sélectionnée et le carnet sélectionné. `$c` peut "
|
||||
"être utilisé pour faire référence à l'objet sélectionné en cours."
|
||||
|
||||
@@ -286,7 +286,7 @@ msgstr "Créés : %d."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Updated: %d."
|
||||
msgstr "Mise à jour : %d."
|
||||
msgstr "Mis à jour : %d."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Skipped: %d."
|
||||
@@ -298,7 +298,7 @@ msgstr "Ressources : %d."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Tagged: %d."
|
||||
msgstr "Etiquettes : %d."
|
||||
msgstr "Étiquettes : %d."
|
||||
|
||||
msgid "Importing notes..."
|
||||
msgstr "Importation des notes..."
|
||||
@@ -510,18 +510,17 @@ msgid ""
|
||||
"any files outside this directory nor to any other personal data. No data "
|
||||
"will be shared with any third party."
|
||||
msgstr ""
|
||||
"Veuillez ouvrir le lien ci-dessous dans votre browser pour authentifier le "
|
||||
"logiciel. Joplin va créer un répertoire \"Apps/Joplin\" et lire/écrira des "
|
||||
"fichiers uniquement dans ce répertoire. Le logiciel n'aura pas d'accès à "
|
||||
"Veuillez ouvrir le lien ci-dessous dans votre navigateur pour authentifier "
|
||||
"le logiciel. Joplin va créer un répertoire \"Apps/Joplin\" et lire/écrira "
|
||||
"des fichiers uniquement dans ce répertoire. Le logiciel n'aura pas d'accès à "
|
||||
"aucun fichier en dehors de ce répertoire, ni à d'autres données "
|
||||
"personnelles. Aucune donnée ne sera partagé avec aucun tier."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search:"
|
||||
msgstr "Chercher"
|
||||
msgstr "Recherche :"
|
||||
|
||||
msgid "File"
|
||||
msgstr ""
|
||||
msgstr "Fichier"
|
||||
|
||||
msgid "New note"
|
||||
msgstr "Nouvelle note"
|
||||
@@ -533,7 +532,7 @@ msgid "New notebook"
|
||||
msgstr "Nouveau carnet"
|
||||
|
||||
msgid "Import Evernote notes"
|
||||
msgstr "Importer notes d'Evernotes"
|
||||
msgstr "Importer notes d'Evernote"
|
||||
|
||||
msgid "Evernote Export Files"
|
||||
msgstr "Fichiers d'export Evernote"
|
||||
@@ -541,6 +540,21 @@ msgstr "Fichiers d'export Evernote"
|
||||
msgid "Quit"
|
||||
msgstr "Quitter"
|
||||
|
||||
msgid "Edit"
|
||||
msgstr "Édition"
|
||||
|
||||
msgid "Copy"
|
||||
msgstr "Copier"
|
||||
|
||||
msgid "Cut"
|
||||
msgstr "Couper"
|
||||
|
||||
msgid "Paste"
|
||||
msgstr "Coller"
|
||||
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Chercher dans toutes les notes"
|
||||
|
||||
msgid "Tools"
|
||||
msgstr "Outils"
|
||||
|
||||
@@ -550,16 +564,15 @@ msgstr "Options"
|
||||
msgid "Help"
|
||||
msgstr "Aide"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Website and documentation"
|
||||
msgstr "Documentation"
|
||||
msgstr "Documentation en ligne"
|
||||
|
||||
msgid "About Joplin"
|
||||
msgstr "A props de Joplin"
|
||||
msgstr "A propos de Joplin"
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "%s %s (%s, %s)"
|
||||
msgstr "%s %s (%s)"
|
||||
msgstr "%s %s (%s, %s)"
|
||||
|
||||
msgid "OK"
|
||||
msgstr "OK"
|
||||
@@ -570,95 +583,107 @@ msgstr "Annulation"
|
||||
msgid "Back"
|
||||
msgstr "Retour"
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"New notebook \"%s\" will be created and file \"%s\" will be imported into it"
|
||||
msgstr ""
|
||||
"Un nouveau carnet \"%s\" va être créé et le fichier \"%s\" va être importé "
|
||||
"dedans. Continuer ?"
|
||||
"dedans"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Please create a notebook first."
|
||||
msgstr "Veuillez d'abord sélectionner un carnet."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Note title:"
|
||||
msgstr "cat <titre>"
|
||||
msgstr "Titre de la note :"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Please create a notebook first"
|
||||
msgstr "Veuillez d'abord sélectionner un carnet."
|
||||
msgstr "Veuillez d'abord créer un carnet d'abord"
|
||||
|
||||
#, fuzzy
|
||||
msgid "To-do title:"
|
||||
msgstr "cat <titre>"
|
||||
msgstr "Titre de la tâche :"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Notebook title:"
|
||||
msgstr "cat <titre>"
|
||||
msgstr "Titre du carnet :"
|
||||
|
||||
msgid "Add or remove tags:"
|
||||
msgstr "Modifier les étiquettes"
|
||||
msgstr "Modifier les étiquettes :"
|
||||
|
||||
msgid "Separate each tag by a comma."
|
||||
msgstr ""
|
||||
msgstr "Séparez chaque étiquette par une virgule."
|
||||
|
||||
msgid "Rename notebook:"
|
||||
msgstr "Renommer le carnet :"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Set alarm:"
|
||||
msgstr "Définir ou modifier alarme"
|
||||
|
||||
msgid "Layout"
|
||||
msgstr ""
|
||||
msgstr "Disposition"
|
||||
|
||||
msgid "Add or remove tags"
|
||||
msgstr ""
|
||||
msgstr "Gérer les étiquettes"
|
||||
|
||||
msgid "Switch between note and to-do"
|
||||
msgstr ""
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Alterner entre note et tâche"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Delete"
|
||||
msgstr "Supprimer la note"
|
||||
msgstr "Supprimer"
|
||||
|
||||
msgid "Delete notes?"
|
||||
msgstr "Supprimer les notes ?"
|
||||
|
||||
#, fuzzy
|
||||
msgid "No notes in here. Create one by clicking on \"New note\"."
|
||||
msgstr ""
|
||||
"Vous n'avez pour l'instant pas de carnets. Créez-en un en pressant le bouton "
|
||||
"(+)."
|
||||
"Pas de notes ici. Créez-en une en pressant le bouton \"Nouvelle note\"."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
msgstr ""
|
||||
msgstr "Lien ou message non géré : %s"
|
||||
|
||||
msgid "Attach file"
|
||||
msgstr "Attacher un fichier"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Set alarm"
|
||||
msgstr "Définir ou modifier alarme"
|
||||
|
||||
msgid "Refresh"
|
||||
msgstr "Rafraîchir"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear"
|
||||
msgstr "Supprimer"
|
||||
|
||||
msgid "OneDrive Login"
|
||||
msgstr "OneDrive"
|
||||
msgstr "Connexion OneDrive"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Import"
|
||||
msgstr "Importé - %s"
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr "Configuration"
|
||||
msgstr "Importer"
|
||||
|
||||
msgid "Delete notebook?"
|
||||
msgstr "Supprimer le carnet ?"
|
||||
|
||||
msgid "Remove this tag from all the notes?"
|
||||
msgstr ""
|
||||
msgstr "Enlever cette étiquette de toutes les notes ?"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Notebooks"
|
||||
msgstr "Nouveau carnet"
|
||||
msgid "Remove this search from the sidebar?"
|
||||
msgstr "Enlever cette recherche de la barre latérale ?"
|
||||
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
msgid "Rename"
|
||||
msgstr "Renommer"
|
||||
|
||||
msgid "Synchronise"
|
||||
msgstr "Synchroniser"
|
||||
|
||||
msgid "Notebooks"
|
||||
msgstr "Carnets"
|
||||
|
||||
msgid "Tags"
|
||||
msgstr "Étiquettes"
|
||||
|
||||
msgid "Searches"
|
||||
msgstr "Recherches"
|
||||
|
||||
#, javascript-format
|
||||
msgid "Usage: %s"
|
||||
msgstr "Utilisation : %s"
|
||||
@@ -667,6 +692,15 @@ msgstr "Utilisation : %s"
|
||||
msgid "Unknown flag: %s"
|
||||
msgstr "Paramètre inconnu : %s"
|
||||
|
||||
msgid "File system"
|
||||
msgstr "Système de fichier"
|
||||
|
||||
msgid "OneDrive"
|
||||
msgstr "OneDrive"
|
||||
|
||||
msgid "OneDrive Dev (For testing only)"
|
||||
msgstr "OneDrive Dév (Pour tester uniquement)"
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unknown log level: %s"
|
||||
msgstr "Paramètre inconnu : %s"
|
||||
@@ -679,15 +713,17 @@ msgid ""
|
||||
"Cannot refresh token: authentication data is missing. Starting the "
|
||||
"synchronisation again may fix the problem."
|
||||
msgstr ""
|
||||
"Impossible de rafraîchir la connection à OneDrive. Démarrez la "
|
||||
"Impossible de rafraîchir la connexion à OneDrive. Démarrez la "
|
||||
"synchronisation à nouveau pour corriger le problème."
|
||||
|
||||
msgid ""
|
||||
"Please set the \"sync.2.path\" config value to the desired synchronisation "
|
||||
"destination."
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
"This error often happens when using OneDrive for Business, which "
|
||||
"unfortunately cannot be supported.\n"
|
||||
"\n"
|
||||
"Please consider using a regular OneDrive account."
|
||||
msgstr ""
|
||||
"Veuillez attribuer une valeur au paramètre de configuration \"sync.2.path\" "
|
||||
"pour indiquer le dossier où devra se faire la synchronisation."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Cannot access %s"
|
||||
@@ -719,7 +755,7 @@ msgstr "Objets distants supprimés : %d."
|
||||
|
||||
#, javascript-format
|
||||
msgid "State: \"%s\"."
|
||||
msgstr "Etat : \"%s\"."
|
||||
msgstr "État : \"%s\"."
|
||||
|
||||
msgid "Cancelling..."
|
||||
msgstr "Annulation..."
|
||||
@@ -730,7 +766,7 @@ msgstr "Terminé : %s"
|
||||
|
||||
#, javascript-format
|
||||
msgid "Synchronisation is already in progress. State: %s"
|
||||
msgstr "La synchronisation est déjà en cours. Etat : %s"
|
||||
msgstr "La synchronisation est déjà en cours. État : %s"
|
||||
|
||||
msgid "Conflicts"
|
||||
msgstr "Conflits"
|
||||
@@ -757,10 +793,6 @@ msgstr "Impossible de copier la note vers le carnet \"%s\""
|
||||
msgid "Cannot move note to \"%s\" notebook"
|
||||
msgstr "Impossible de déplacer la note vers le carnet \"%s\""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Invalid option value: \"%s\". Possible values are: %s."
|
||||
msgstr "Option invalide: \"%s\". Les valeurs possibles sont : %s."
|
||||
|
||||
msgid "File system synchronisation target directory"
|
||||
msgstr "Cible de la synchronisation sur le disque dur"
|
||||
|
||||
@@ -771,24 +803,8 @@ msgstr ""
|
||||
"Le chemin du répertoire avec lequel synchroniser lorsque la synchronisation "
|
||||
"par système de fichier est activée. Voir `sync.target`."
|
||||
|
||||
msgid "Synchronisation target"
|
||||
msgstr "Cible de la synchronisation"
|
||||
|
||||
msgid ""
|
||||
"The target to synchonise to. If synchronising with the file system, set "
|
||||
"`sync.2.path` to specify the target directory."
|
||||
msgstr ""
|
||||
"La cible avec laquelle synchroniser. Pour synchroniser avec le système de "
|
||||
"fichier, veuillez spécifier le répertoire avec `sync.2.path`."
|
||||
|
||||
msgid "File system"
|
||||
msgstr "Système de fichier"
|
||||
|
||||
msgid "OneDrive"
|
||||
msgstr "OneDrive"
|
||||
|
||||
msgid "Text editor"
|
||||
msgstr "Editeur de texte"
|
||||
msgstr "Éditeur de texte"
|
||||
|
||||
msgid ""
|
||||
"The editor that will be used to open a note. If none is provided it will try "
|
||||
@@ -800,6 +816,12 @@ msgstr ""
|
||||
msgid "Language"
|
||||
msgstr "Langue"
|
||||
|
||||
msgid "Date format"
|
||||
msgstr "Format de la date"
|
||||
|
||||
msgid "Time format"
|
||||
msgstr "Format de l'heure"
|
||||
|
||||
msgid "Theme"
|
||||
msgstr "Apparence"
|
||||
|
||||
@@ -816,7 +838,7 @@ msgid "Save geo-location with notes"
|
||||
msgstr "Enregistrer l'emplacement avec les notes"
|
||||
|
||||
msgid "Synchronisation interval"
|
||||
msgstr "Interval de synchronisation"
|
||||
msgstr "Intervalle de synchronisation"
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr "Désactivé"
|
||||
@@ -833,9 +855,26 @@ msgstr "%d heure"
|
||||
msgid "%d hours"
|
||||
msgstr "%d heures"
|
||||
|
||||
msgid "Automatically update the application"
|
||||
msgstr "Mettre à jour le logiciel automatiquement"
|
||||
|
||||
msgid "Show advanced options"
|
||||
msgstr "Montrer les options avancées"
|
||||
|
||||
msgid "Synchronisation target"
|
||||
msgstr "Cible de la synchronisation"
|
||||
|
||||
msgid ""
|
||||
"The target to synchonise to. If synchronising with the file system, set "
|
||||
"`sync.2.path` to specify the target directory."
|
||||
msgstr ""
|
||||
"La cible avec laquelle synchroniser. Pour synchroniser avec le système de "
|
||||
"fichier, veuillez spécifier le répertoire avec `sync.2.path`."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Invalid option value: \"%s\". Possible values are: %s."
|
||||
msgstr "Option invalide: \"%s\". Les valeurs possibles sont : %s."
|
||||
|
||||
msgid "Sync status (synced items / total items)"
|
||||
msgstr "Status de la synchronisation (objets synchro. / total)"
|
||||
|
||||
@@ -862,16 +901,45 @@ msgstr "Carnets"
|
||||
msgid "%s: %d notes"
|
||||
msgstr "%s : %d notes"
|
||||
|
||||
msgid "Coming alarms"
|
||||
msgstr "Alarmes à venir"
|
||||
|
||||
#, javascript-format
|
||||
msgid "On %s: %s"
|
||||
msgstr "Le %s : %s"
|
||||
|
||||
msgid "There are currently no notes. Create one by clicking on the (+) button."
|
||||
msgstr ""
|
||||
"Ce carnet ne contient aucune note. Créez-en une en appuyant sur le bouton "
|
||||
"(+)."
|
||||
|
||||
msgid "Delete these notes?"
|
||||
msgstr "Supprimer ces notes ?"
|
||||
|
||||
msgid "Log"
|
||||
msgstr "Journal"
|
||||
|
||||
msgid "Status"
|
||||
msgstr "Etat"
|
||||
msgstr "État"
|
||||
|
||||
msgid "Export Debug Report"
|
||||
msgstr "Exporter rapport de débogage"
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr "Configuration"
|
||||
|
||||
msgid "Move to notebook..."
|
||||
msgstr "Déplacer la note vers carnet..."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Move %d notes to notebook \"%s\"?"
|
||||
msgstr "Déplacer %d notes vers carnet \"%s\" ?"
|
||||
|
||||
msgid "Select date"
|
||||
msgstr "Sélectionner date"
|
||||
|
||||
msgid "Confirm"
|
||||
msgstr "Confirmer"
|
||||
|
||||
msgid "Cancel synchronisation"
|
||||
msgstr "Annuler synchronisation"
|
||||
@@ -881,7 +949,7 @@ msgid "The notebook could not be saved: %s"
|
||||
msgstr "Ce carnet n'a pas pu être sauvegardé : %s"
|
||||
|
||||
msgid "Edit notebook"
|
||||
msgstr "Editer le carnet"
|
||||
msgstr "Éditer le carnet"
|
||||
|
||||
msgid "This note has been modified:"
|
||||
msgstr "Cette note a été modifiée :"
|
||||
@@ -892,10 +960,17 @@ msgstr "Enregistrer les changements"
|
||||
msgid "Discard changes"
|
||||
msgstr "Ignorer les changements"
|
||||
|
||||
msgid "Delete note"
|
||||
msgstr "Supprimer la note"
|
||||
#, javascript-format
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Type d'image non géré : %s"
|
||||
|
||||
msgid "Convert to regular note"
|
||||
msgid "Attach photo"
|
||||
msgstr "Attacher une photo"
|
||||
|
||||
msgid "Attach any file"
|
||||
msgstr "Attacher un fichier"
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr "Convertir en note"
|
||||
|
||||
msgid "Convert to todo"
|
||||
@@ -907,17 +982,14 @@ msgstr "Cacher les métadonnées"
|
||||
msgid "Show metadata"
|
||||
msgstr "Afficher les métadonnées"
|
||||
|
||||
msgid "View location on map"
|
||||
msgstr "Voir l'emplacement sur la carte"
|
||||
|
||||
msgid "Edit"
|
||||
msgstr "Editer"
|
||||
msgid "View on map"
|
||||
msgstr "Voir emplacement sur carte"
|
||||
|
||||
msgid "Delete notebook"
|
||||
msgstr "Supprimer le carnet"
|
||||
|
||||
msgid "Login with OneDrive"
|
||||
msgstr "Se connecter avec OneDrive"
|
||||
msgstr "Se connecter à OneDrive"
|
||||
|
||||
msgid ""
|
||||
"Click on the (+) button to create a new note or notebook. Click on the side "
|
||||
@@ -934,6 +1006,37 @@ msgstr ""
|
||||
msgid "Welcome"
|
||||
msgstr "Bienvenue"
|
||||
|
||||
#~ msgid "Set or clear alarm:"
|
||||
#~ msgstr "Définir ou modifier alarme :"
|
||||
|
||||
#~ msgid "Set or clear alarm"
|
||||
#~ msgstr "Définir ou modifier alarme"
|
||||
|
||||
#~ msgid "Attach image"
|
||||
#~ msgstr "Joindre une image ou photo"
|
||||
|
||||
#~ msgid "Attach any other file"
|
||||
#~ msgstr "Joindre un fichier"
|
||||
|
||||
#~ msgid "Delete note"
|
||||
#~ msgstr "Supprimer la note"
|
||||
|
||||
#~ msgid "Convert to regular note"
|
||||
#~ msgstr "Convertir en note"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "Please set the \"sync.2.path\" config value to the desired "
|
||||
#~ "synchronisation destination."
|
||||
#~ msgstr ""
|
||||
#~ "Veuillez attribuer une valeur au paramètre de configuration \"sync.2.path"
|
||||
#~ "\" pour indiquer le dossier où devra se faire la synchronisation."
|
||||
|
||||
#~ msgid "Seach:"
|
||||
#~ msgstr "Chercher :"
|
||||
|
||||
#~ msgid "Seach"
|
||||
#~ msgstr "Chercher"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "Command line argument \"%s\" contains both quotes and double-quotes - "
|
||||
#~ "aborting."
|
||||
@@ -982,10 +1085,6 @@ msgstr "Bienvenue"
|
||||
#~ msgid "Done editing."
|
||||
#~ msgstr "Edition terminée."
|
||||
|
||||
#, fuzzy
|
||||
#~ msgid "Confirm"
|
||||
#~ msgstr "Conflits"
|
||||
|
||||
#~ msgid "Last error: %s (stacktrace in log)."
|
||||
#~ msgstr "Dernière erreur : %s (Plus d'information dans le journal d'erreurs)"
|
||||
|
||||
|
@@ -490,6 +490,21 @@ msgstr ""
|
||||
msgid "Quit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cut"
|
||||
msgstr ""
|
||||
|
||||
msgid "Paste"
|
||||
msgstr ""
|
||||
|
||||
msgid "Search in all the notes"
|
||||
msgstr ""
|
||||
|
||||
msgid "Tools"
|
||||
msgstr ""
|
||||
|
||||
@@ -544,18 +559,27 @@ msgstr ""
|
||||
msgid "Separate each tag by a comma."
|
||||
msgstr ""
|
||||
|
||||
msgid "Rename notebook:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Set alarm:"
|
||||
msgstr ""
|
||||
|
||||
msgid "Layout"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add or remove tags"
|
||||
msgstr ""
|
||||
|
||||
msgid "Switch between note and to-do"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete notes?"
|
||||
msgstr ""
|
||||
|
||||
msgid "No notes in here. Create one by clicking on \"New note\"."
|
||||
msgstr ""
|
||||
|
||||
@@ -566,31 +590,43 @@ msgstr ""
|
||||
msgid "Attach file"
|
||||
msgstr ""
|
||||
|
||||
msgid "Set alarm"
|
||||
msgstr ""
|
||||
|
||||
msgid "Refresh"
|
||||
msgstr ""
|
||||
|
||||
msgid "Clear"
|
||||
msgstr ""
|
||||
|
||||
msgid "OneDrive Login"
|
||||
msgstr ""
|
||||
|
||||
msgid "Import"
|
||||
msgstr ""
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete notebook?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remove this tag from all the notes?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Remove this search from the sidebar?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Rename"
|
||||
msgstr ""
|
||||
|
||||
msgid "Synchronise"
|
||||
msgstr ""
|
||||
|
||||
msgid "Notebooks"
|
||||
msgstr ""
|
||||
|
||||
msgid "Tags"
|
||||
msgstr ""
|
||||
|
||||
msgid "Synchronise"
|
||||
msgid "Searches"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
@@ -601,6 +637,15 @@ msgstr ""
|
||||
msgid "Unknown flag: %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "File system"
|
||||
msgstr ""
|
||||
|
||||
msgid "OneDrive"
|
||||
msgstr ""
|
||||
|
||||
msgid "OneDrive Dev (For testing only)"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unknown log level: %s"
|
||||
msgstr ""
|
||||
@@ -615,8 +660,12 @@ msgid ""
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Please set the \"sync.2.path\" config value to the desired synchronisation "
|
||||
"destination."
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
"This error often happens when using OneDrive for Business, which "
|
||||
"unfortunately cannot be supported.\n"
|
||||
"\n"
|
||||
"Please consider using a regular OneDrive account."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
@@ -687,10 +736,6 @@ msgstr ""
|
||||
msgid "Cannot move note to \"%s\" notebook"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Invalid option value: \"%s\". Possible values are: %s."
|
||||
msgstr ""
|
||||
|
||||
msgid "File system synchronisation target directory"
|
||||
msgstr ""
|
||||
|
||||
@@ -699,20 +744,6 @@ msgid ""
|
||||
"See `sync.target`."
|
||||
msgstr ""
|
||||
|
||||
msgid "Synchronisation target"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The target to synchonise to. If synchronising with the file system, set "
|
||||
"`sync.2.path` to specify the target directory."
|
||||
msgstr ""
|
||||
|
||||
msgid "File system"
|
||||
msgstr ""
|
||||
|
||||
msgid "OneDrive"
|
||||
msgstr ""
|
||||
|
||||
msgid "Text editor"
|
||||
msgstr ""
|
||||
|
||||
@@ -724,6 +755,12 @@ msgstr ""
|
||||
msgid "Language"
|
||||
msgstr ""
|
||||
|
||||
msgid "Date format"
|
||||
msgstr ""
|
||||
|
||||
msgid "Time format"
|
||||
msgstr ""
|
||||
|
||||
msgid "Theme"
|
||||
msgstr ""
|
||||
|
||||
@@ -757,9 +794,24 @@ msgstr ""
|
||||
msgid "%d hours"
|
||||
msgstr ""
|
||||
|
||||
msgid "Automatically update the application"
|
||||
msgstr ""
|
||||
|
||||
msgid "Show advanced options"
|
||||
msgstr ""
|
||||
|
||||
msgid "Synchronisation target"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"The target to synchonise to. If synchronising with the file system, set "
|
||||
"`sync.2.path` to specify the target directory."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Invalid option value: \"%s\". Possible values are: %s."
|
||||
msgstr ""
|
||||
|
||||
msgid "Sync status (synced items / total items)"
|
||||
msgstr ""
|
||||
|
||||
@@ -786,15 +838,44 @@ msgstr ""
|
||||
msgid "%s: %d notes"
|
||||
msgstr ""
|
||||
|
||||
msgid "Coming alarms"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "On %s: %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "There are currently no notes. Create one by clicking on the (+) button."
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete these notes?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Log"
|
||||
msgstr ""
|
||||
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
msgid "Export Debug Report"
|
||||
msgstr ""
|
||||
|
||||
msgid "Configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "Move to notebook..."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Move %d notes to notebook \"%s\"?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select date"
|
||||
msgstr ""
|
||||
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
|
||||
msgid "Cancel synchronisation"
|
||||
msgstr ""
|
||||
|
||||
@@ -814,10 +895,17 @@ msgstr ""
|
||||
msgid "Discard changes"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete note"
|
||||
#, javascript-format
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to regular note"
|
||||
msgid "Attach photo"
|
||||
msgstr ""
|
||||
|
||||
msgid "Attach any file"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to note"
|
||||
msgstr ""
|
||||
|
||||
msgid "Convert to todo"
|
||||
@@ -829,10 +917,7 @@ msgstr ""
|
||||
msgid "Show metadata"
|
||||
msgstr ""
|
||||
|
||||
msgid "View location on map"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit"
|
||||
msgid "View on map"
|
||||
msgstr ""
|
||||
|
||||
msgid "Delete notebook"
|
||||
|
22
CliClient/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "joplin",
|
||||
"version": "0.10.68",
|
||||
"version": "0.10.77",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -797,6 +797,12 @@
|
||||
"highlight.js": "9.12.0"
|
||||
}
|
||||
},
|
||||
"marked": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz",
|
||||
"integrity": "sha1-ssbGGPzOzk74bE/Gy4p8v1rtqNc=",
|
||||
"dev": true
|
||||
},
|
||||
"md5": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
|
||||
@@ -878,6 +884,12 @@
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"mustache": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/mustache/-/mustache-2.3.0.tgz",
|
||||
"integrity": "sha1-QCj3d4sXcIpImTCm5SrDvKDaQdA=",
|
||||
"dev": true
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz",
|
||||
@@ -911,6 +923,14 @@
|
||||
"resolved": "https://registry.npmjs.org/node-bitmap/-/node-bitmap-0.0.1.tgz",
|
||||
"integrity": "sha1-GA6scAPgxwdhjvMTaPYvhLKmkJE="
|
||||
},
|
||||
"node-emoji": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.8.1.tgz",
|
||||
"integrity": "sha512-+ktMAh1Jwas+TnGodfCfjUbJKoANqPaJFN0z0iqh41eqD8dvguNzcitVSBSVK1pidz0AqGbLKcoVuVLRVZ/aVg==",
|
||||
"requires": {
|
||||
"lodash.toarray": "4.4.0"
|
||||
}
|
||||
},
|
||||
"node-fetch": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz",
|
||||
|
@@ -18,7 +18,7 @@
|
||||
],
|
||||
"owner": "Laurent Cozic"
|
||||
},
|
||||
"version": "0.10.68",
|
||||
"version": "0.10.77",
|
||||
"bin": {
|
||||
"joplin": "./main.js"
|
||||
},
|
||||
@@ -37,6 +37,7 @@
|
||||
"md5": "^2.2.1",
|
||||
"mime": "^2.0.3",
|
||||
"moment": "^2.18.1",
|
||||
"node-emoji": "^1.8.1",
|
||||
"node-fetch": "^1.7.1",
|
||||
"node-persist": "^2.1.0",
|
||||
"os-tmpdir": "^1.0.2",
|
||||
@@ -60,7 +61,9 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"gettext-parser": "^1.2.2",
|
||||
"jasmine": "^2.6.0"
|
||||
"jasmine": "^2.6.0",
|
||||
"marked": "^0.3.6",
|
||||
"mustache": "^2.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "jasmine"
|
||||
|
@@ -1,40 +0,0 @@
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient } = require('test-utils.js');
|
||||
const { Folder } = require('lib/models/folder.js');
|
||||
const { Note } = require('lib/models/note.js');
|
||||
const { Setting } = require('lib/models/setting.js');
|
||||
const { BaseItem } = require('lib/models/base-item.js');
|
||||
const { BaseModel } = require('lib/base-model.js');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.error('Unhandled promise rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
describe('BaseItem', function() {
|
||||
|
||||
beforeEach( async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
switchClient(1);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should create a deleted_items record', async (done) => {
|
||||
let folder1 = await Folder.save({ title: 'folder1' });
|
||||
let folder2 = await Folder.save({ title: 'folder2' });
|
||||
|
||||
await Folder.delete(folder1.id);
|
||||
|
||||
let items = await BaseItem.deletedItems();
|
||||
|
||||
expect(items.length).toBe(1);
|
||||
expect(items[0].item_id).toBe(folder1.id);
|
||||
expect(items[0].item_type).toBe(folder1.type_);
|
||||
|
||||
let folders = await Folder.all();
|
||||
|
||||
expect(folders.length).toBe(1);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
@@ -9,6 +9,7 @@ const { Database } = require('lib/database.js');
|
||||
const { Setting } = require('lib/models/setting.js');
|
||||
const { BaseItem } = require('lib/models/base-item.js');
|
||||
const { BaseModel } = require('lib/base-model.js');
|
||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
@@ -37,7 +38,7 @@ async function localItemsSameAsRemote(locals, expect) {
|
||||
expect(!!remote).toBe(true);
|
||||
if (!remote) continue;
|
||||
|
||||
if (syncTargetId() == Setting.SYNC_TARGET_FILESYSTEM) {
|
||||
if (syncTargetId() == SyncTargetRegistry.nameToId('filesystem')) {
|
||||
expect(remote.updated_time).toBe(Math.floor(dbItem.updated_time / 1000) * 1000);
|
||||
} else {
|
||||
expect(remote.updated_time).toBe(dbItem.updated_time);
|
||||
@@ -606,5 +607,25 @@ describe('Synchronizer', function() {
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('items should be downloaded again when user cancels in the middle of delta operation', async (done) => {
|
||||
let folder1 = await Folder.save({ title: "folder1" });
|
||||
let note1 = await Note.save({ title: "un", is_todo: 1, parent_id: folder1.id });
|
||||
await synchronizer().start();
|
||||
|
||||
await switchClient(2);
|
||||
|
||||
synchronizer().debugFlags_ = ['cancelDeltaLoop2'];
|
||||
let context = await synchronizer().start();
|
||||
let notes = await Note.all();
|
||||
expect(notes.length).toBe(0);
|
||||
|
||||
synchronizer().debugFlags_ = [];
|
||||
await synchronizer().start({ context: context });
|
||||
notes = await Note.all();
|
||||
expect(notes.length).toBe(1);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
@@ -16,6 +16,10 @@ const { FileApiDriverMemory } = require('lib/file-api-driver-memory.js');
|
||||
const { FileApiDriverLocal } = require('lib/file-api-driver-local.js');
|
||||
const { FsDriverNode } = require('lib/fs-driver-node.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const SyncTargetRegistry = require('lib/SyncTargetRegistry.js');
|
||||
const SyncTargetMemory = require('lib/SyncTargetMemory.js');
|
||||
const SyncTargetFilesystem = require('lib/SyncTargetFilesystem.js');
|
||||
const SyncTargetOneDrive = require('lib/SyncTargetOneDrive.js');
|
||||
|
||||
let databases_ = [];
|
||||
let synchronizers_ = [];
|
||||
@@ -29,12 +33,14 @@ Resource.fsDriver_ = fsDriver;
|
||||
const logDir = __dirname + '/../tests/logs';
|
||||
fs.mkdirpSync(logDir, 0o755);
|
||||
|
||||
const syncTargetId_ = Setting.SYNC_TARGET_MEMORY;
|
||||
//const syncTargetId_ = Setting.SYNC_TARGET_FILESYSTEM;
|
||||
//const syncTargetId_ = Setting.SYNC_TARGET_ONEDRIVE;
|
||||
SyncTargetRegistry.addClass(SyncTargetMemory);
|
||||
SyncTargetRegistry.addClass(SyncTargetFilesystem);
|
||||
SyncTargetRegistry.addClass(SyncTargetOneDrive);
|
||||
|
||||
const syncTargetId_ = SyncTargetRegistry.nameToId('memory');
|
||||
const syncDir = __dirname + '/../tests/sync';
|
||||
|
||||
const sleepTime = syncTargetId_ == Setting.SYNC_TARGET_FILESYSTEM ? 1001 : 400;
|
||||
const sleepTime = syncTargetId_ == SyncTargetRegistry.nameToId('filesystem') ? 1001 : 400;
|
||||
|
||||
const logger = new Logger();
|
||||
logger.addTarget('file', { path: logDir + '/log.txt' });
|
||||
@@ -121,11 +127,14 @@ async function setupDatabaseAndSynchronizer(id = null) {
|
||||
await setupDatabase(id);
|
||||
|
||||
if (!synchronizers_[id]) {
|
||||
synchronizers_[id] = new Synchronizer(db(id), fileApi(), Setting.value('appType'));
|
||||
synchronizers_[id].setLogger(logger);
|
||||
const SyncTargetClass = SyncTargetRegistry.classById(syncTargetId_);
|
||||
const syncTarget = new SyncTargetClass(db(id));
|
||||
syncTarget.setFileApi(fileApi());
|
||||
syncTarget.setLogger(logger);
|
||||
synchronizers_[id] = await syncTarget.synchronizer();
|
||||
}
|
||||
|
||||
if (syncTargetId_ == Setting.SYNC_TARGET_FILESYSTEM) {
|
||||
if (syncTargetId_ == SyncTargetRegistry.nameToId('filesystem')) {
|
||||
fs.removeSync(syncDir)
|
||||
fs.mkdirpSync(syncDir, 0o755);
|
||||
} else {
|
||||
@@ -146,13 +155,12 @@ function synchronizer(id = null) {
|
||||
function fileApi() {
|
||||
if (fileApi_) return fileApi_;
|
||||
|
||||
if (syncTargetId_ == Setting.SYNC_TARGET_FILESYSTEM) {
|
||||
if (syncTargetId_ == SyncTargetRegistry.nameToId('filesystem')) {
|
||||
fs.removeSync(syncDir)
|
||||
fs.mkdirpSync(syncDir, 0o755);
|
||||
fileApi_ = new FileApi(syncDir, new FileApiDriverLocal());
|
||||
} else if (syncTargetId_ == Setting.SYNC_TARGET_MEMORY) {
|
||||
} else if (syncTargetId_ == SyncTargetRegistry.nameToId('memory')) {
|
||||
fileApi_ = new FileApi('/root', new FileApiDriverMemory());
|
||||
fileApi_.setLogger(logger);
|
||||
}
|
||||
// } else if (syncTargetId == Setting.SYNC_TARGET_ONEDRIVE) {
|
||||
// let auth = require('./onedrive-auth.json');
|
||||
|
201
CliClientDemo/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "demo-joplin",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.8",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -25,7 +25,7 @@
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.0.tgz",
|
||||
"integrity": "sha512-NnSOmMEYtVR2JVMIGTzynRkkaxtiq1xnFBcdQD/DnNCYPoEPsVJhM98BDyaoNOQIi7p4okdi3E27eN7GQbsUug==",
|
||||
"requires": {
|
||||
"color-convert": "1.9.0"
|
||||
"color-convert": "1.9.1"
|
||||
}
|
||||
},
|
||||
"app-module-path": {
|
||||
@@ -79,23 +79,6 @@
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz",
|
||||
"integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4="
|
||||
},
|
||||
"babel-plugin-transform-runtime": {
|
||||
"version": "6.23.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz",
|
||||
"integrity": "sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4=",
|
||||
"requires": {
|
||||
"babel-runtime": "6.26.0"
|
||||
}
|
||||
},
|
||||
"babel-runtime": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
||||
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
|
||||
"requires": {
|
||||
"core-js": "2.5.1",
|
||||
"regenerator-runtime": "0.11.0"
|
||||
}
|
||||
},
|
||||
"bcrypt-pbkdf": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
|
||||
@@ -113,11 +96,6 @@
|
||||
"hoek": "4.2.0"
|
||||
}
|
||||
},
|
||||
"cache-require-paths": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/cache-require-paths/-/cache-require-paths-0.3.0.tgz",
|
||||
"integrity": "sha1-EqYHWj5JiNpMIvIY4pSFZj5MSmM="
|
||||
},
|
||||
"camelcase": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz",
|
||||
@@ -165,18 +143,18 @@
|
||||
"integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ="
|
||||
},
|
||||
"color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-2.0.0.tgz",
|
||||
"integrity": "sha1-4MmXLR6WmFcASxAeqlXOq1lh1n0=",
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color/-/color-2.0.1.tgz",
|
||||
"integrity": "sha512-ubUCVVKfT7r2w2D3qtHakj8mbmKms+tThR8gI8zEYCbUBl8/voqFGt3kgBqGwXAopgXybnkuOq+qMYCRrp4cXw==",
|
||||
"requires": {
|
||||
"color-convert": "1.9.0",
|
||||
"color-convert": "1.9.1",
|
||||
"color-string": "1.5.2"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.0.tgz",
|
||||
"integrity": "sha1-Gsz5fdc5uYO/mU1W/sj5WFNkG3o=",
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.1.tgz",
|
||||
"integrity": "sha512-mjGanIiwQJskCC18rPR6OmrZ6fm2Lc7PeGFYwCmy5J34wC6F1PzdGL6xeMfmgicfYcNLGuVFA3WzXtIDCQSZxQ==",
|
||||
"requires": {
|
||||
"color-name": "1.1.3"
|
||||
}
|
||||
@@ -212,11 +190,6 @@
|
||||
"proto-list": "1.2.4"
|
||||
}
|
||||
},
|
||||
"core-js": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.1.tgz",
|
||||
"integrity": "sha1-rmh03GaTd4m4B1T/VCjfZoGcpQs="
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||
@@ -306,6 +279,53 @@
|
||||
"jsbn": "0.1.1"
|
||||
}
|
||||
},
|
||||
"emphasize": {
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/emphasize/-/emphasize-1.5.0.tgz",
|
||||
"integrity": "sha1-48WvLdzLSYKCKjNJtHFhPMfOzJI=",
|
||||
"requires": {
|
||||
"chalk": "1.1.3",
|
||||
"highlight.js": "9.12.0",
|
||||
"lowlight": "1.9.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
|
||||
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4="
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"requires": {
|
||||
"ansi-styles": "2.2.1",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"has-ansi": "2.0.0",
|
||||
"strip-ansi": "3.0.1",
|
||||
"supports-color": "2.0.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc="
|
||||
}
|
||||
}
|
||||
},
|
||||
"encoding": {
|
||||
"version": "0.1.12",
|
||||
"resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz",
|
||||
@@ -395,7 +415,7 @@
|
||||
"resolved": "https://registry.npmjs.org/get-proxy/-/get-proxy-2.1.0.tgz",
|
||||
"integrity": "sha512-zmZIaQTWnNQb4R4fJUEp/FC51eZsc6EkErspy3xtIYStaq8EB/hDIWipxsal+E8rz0qD7f2sL/NA9Xee4RInJw==",
|
||||
"requires": {
|
||||
"npm-conf": "1.1.2"
|
||||
"npm-conf": "1.1.3"
|
||||
}
|
||||
},
|
||||
"getpass": {
|
||||
@@ -425,6 +445,21 @@
|
||||
"har-schema": "2.0.0"
|
||||
}
|
||||
},
|
||||
"has-ansi": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
|
||||
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||
}
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
|
||||
@@ -454,11 +489,21 @@
|
||||
"sntp": "2.1.0"
|
||||
}
|
||||
},
|
||||
"highlight.js": {
|
||||
"version": "9.12.0",
|
||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz",
|
||||
"integrity": "sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4="
|
||||
},
|
||||
"hoek": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz",
|
||||
"integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ=="
|
||||
},
|
||||
"html-entities": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/html-entities/-/html-entities-1.2.1.tgz",
|
||||
"integrity": "sha1-DfKTUfByEWNRXfueVUPl9u7VFi8="
|
||||
},
|
||||
"http-signature": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
@@ -577,22 +622,21 @@
|
||||
}
|
||||
},
|
||||
"joplin": {
|
||||
"version": "0.10.64",
|
||||
"resolved": "https://registry.npmjs.org/joplin/-/joplin-0.10.64.tgz",
|
||||
"integrity": "sha512-CdhUZCnbpGmvOJqpeJp4ri9kGk6nP+VxulbaJLx8MTWho8n3tBRHcvAGP/lOLovFvUy760lOyzqHiYfcimonJQ==",
|
||||
"version": "0.10.69",
|
||||
"resolved": "https://registry.npmjs.org/joplin/-/joplin-0.10.69.tgz",
|
||||
"integrity": "sha512-P5b7pRiUqM2O/vG4etiAquGd7LM6j/eh93OmQgJmfcGu5F6D9DJERIjBP8Po/BnzXQVxsGnURHr3SmLlaUxQSg==",
|
||||
"requires": {
|
||||
"app-module-path": "2.2.0",
|
||||
"babel-plugin-transform-runtime": "6.23.0",
|
||||
"cache-require-paths": "0.3.0",
|
||||
"follow-redirects": "1.2.5",
|
||||
"form-data": "2.3.1",
|
||||
"fs-extra": "3.0.1",
|
||||
"html-entities": "1.2.1",
|
||||
"jssha": "2.3.1",
|
||||
"levenshtein": "1.0.5",
|
||||
"lodash": "4.17.4",
|
||||
"md5": "2.2.1",
|
||||
"mime": "2.0.3",
|
||||
"moment": "2.19.1",
|
||||
"moment": "2.19.2",
|
||||
"node-fetch": "1.7.3",
|
||||
"node-persist": "2.1.0",
|
||||
"os-tmpdir": "1.0.2",
|
||||
@@ -603,14 +647,13 @@
|
||||
"sax": "1.2.4",
|
||||
"server-destroy": "1.0.1",
|
||||
"sharp": "0.18.4",
|
||||
"source-map-support": "0.4.18",
|
||||
"sprintf-js": "1.1.1",
|
||||
"sqlite3": "3.1.13",
|
||||
"string-padding": "1.0.2",
|
||||
"string-to-stream": "1.1.0",
|
||||
"strip-ansi": "4.0.0",
|
||||
"tcp-port-used": "0.1.2",
|
||||
"tkwidgets": "0.5.19",
|
||||
"tkwidgets": "0.5.20",
|
||||
"uuid": "3.1.0",
|
||||
"word-wrap": "1.2.3",
|
||||
"yargs-parser": "7.0.0"
|
||||
@@ -719,6 +762,14 @@
|
||||
"js-tokens": "3.0.2"
|
||||
}
|
||||
},
|
||||
"lowlight": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-1.9.1.tgz",
|
||||
"integrity": "sha512-CpDhyVhI+xHjruiGvH2F/Fr5q5aTn5A6Oyh7MI+4oI8G0A1E7p9a3Zqv9Hzx9WByK8gAiNifEueAXz+cA2xdEA==",
|
||||
"requires": {
|
||||
"highlight.js": "9.12.0"
|
||||
}
|
||||
},
|
||||
"md5": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/md5/-/md5-2.2.1.tgz",
|
||||
@@ -782,9 +833,9 @@
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.19.1",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.19.1.tgz",
|
||||
"integrity": "sha1-VtoaLRy/AdOLfhr8McELz6GSkWc="
|
||||
"version": "2.19.2",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.19.2.tgz",
|
||||
"integrity": "sha512-Rf6jiHPEfxp9+dlzxPTmRHbvoFXsh2L/U8hOupUMpnuecHQmI6cF6lUbJl3QqKPko1u6ujO+FxtcajLVfLpAtA=="
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
@@ -792,9 +843,9 @@
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz",
|
||||
"integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY="
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz",
|
||||
"integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo="
|
||||
},
|
||||
"ndarray": {
|
||||
"version": "1.0.18",
|
||||
@@ -850,9 +901,9 @@
|
||||
}
|
||||
},
|
||||
"npm-conf": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.2.tgz",
|
||||
"integrity": "sha512-dotwbpwVzfNB/2EF3A2wjK5tEMLggKfuA/8TG6WvBB1Zrv+JsvF7E8ei9B/HGq211st/GwXFbREcNJvJ1eySUQ==",
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz",
|
||||
"integrity": "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw==",
|
||||
"requires": {
|
||||
"config-chain": "1.1.11",
|
||||
"pify": "3.0.0"
|
||||
@@ -985,11 +1036,6 @@
|
||||
"symbol-observable": "1.0.4"
|
||||
}
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.0.tgz",
|
||||
"integrity": "sha512-/aA0kLeRb5N9K0d4fw7ooEbI+xDe+DKD499EQqygGqeS8N3xto15p09uY2xj7ixP81sNPXvRLnAQIqdVStgb1A=="
|
||||
},
|
||||
"request": {
|
||||
"version": "2.83.0",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz",
|
||||
@@ -1050,9 +1096,9 @@
|
||||
"integrity": "sha1-/jKcDwaJbCiqJDdt8f/wKuV/LTQ=",
|
||||
"requires": {
|
||||
"caw": "2.0.1",
|
||||
"color": "2.0.0",
|
||||
"color": "2.0.1",
|
||||
"detect-libc": "0.2.0",
|
||||
"nan": "2.7.0",
|
||||
"nan": "2.8.0",
|
||||
"semver": "5.4.1",
|
||||
"simple-get": "2.7.0",
|
||||
"tar": "3.2.1"
|
||||
@@ -1097,19 +1143,6 @@
|
||||
"hoek": "4.2.0"
|
||||
}
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.4.18",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
|
||||
"integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
|
||||
"requires": {
|
||||
"source-map": "0.5.7"
|
||||
}
|
||||
},
|
||||
"sprintf-js": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.1.tgz",
|
||||
@@ -1518,6 +1551,11 @@
|
||||
"version": "2.0.0",
|
||||
"bundled": true
|
||||
},
|
||||
"nan": {
|
||||
"version": "2.7.0",
|
||||
"resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz",
|
||||
"integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY="
|
||||
},
|
||||
"node-pre-gyp": {
|
||||
"version": "0.6.38",
|
||||
"bundled": true,
|
||||
@@ -1947,9 +1985,9 @@
|
||||
}
|
||||
},
|
||||
"terminal-kit": {
|
||||
"version": "1.14.0",
|
||||
"resolved": "https://registry.npmjs.org/terminal-kit/-/terminal-kit-1.14.0.tgz",
|
||||
"integrity": "sha512-ir0I2QtcBDSg2w0UvohlqdDpGlS3S2UYBG4NnYKnK/4VywgnbfxgdpXN3el0uCH3OeH6fG38luW7RmDM96FqUw==",
|
||||
"version": "1.14.3",
|
||||
"resolved": "https://registry.npmjs.org/terminal-kit/-/terminal-kit-1.14.3.tgz",
|
||||
"integrity": "sha512-ZHtuElnBhK0IXOYNvQ7eYgaArwEoOv7saQc4Q0Z9p02JeC7iajC20/odV77BKB3jw/Qthvf9mpASf8gNDYv7xQ==",
|
||||
"requires": {
|
||||
"async-kit": "2.2.3",
|
||||
"get-pixels": "3.3.0",
|
||||
@@ -1965,15 +2003,16 @@
|
||||
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU="
|
||||
},
|
||||
"tkwidgets": {
|
||||
"version": "0.5.19",
|
||||
"resolved": "https://registry.npmjs.org/tkwidgets/-/tkwidgets-0.5.19.tgz",
|
||||
"integrity": "sha512-Etskz52RInmmB1aGD9cIodIZ33Gwf0yPP3+lh6ceANgS4N8LpquQajDRyahjZnvwDhfYdo1N87eEKzXBJbLQDg==",
|
||||
"version": "0.5.20",
|
||||
"resolved": "https://registry.npmjs.org/tkwidgets/-/tkwidgets-0.5.20.tgz",
|
||||
"integrity": "sha512-9wGsMrrFJvE/6TKUc0dEFFhwxvZLeNsYOxnpy1JCwyk/hYCEF70nuvk7VvJeG4TPaQBaGKPj6c7pCgdREvz4Jw==",
|
||||
"requires": {
|
||||
"chalk": "2.3.0",
|
||||
"emphasize": "1.5.0",
|
||||
"node-emoji": "git+https://github.com/laurent22/node-emoji.git#9fa01eac463e94dde1316ef8c53089eeef4973b5",
|
||||
"slice-ansi": "1.0.0",
|
||||
"string-width": "2.1.1",
|
||||
"terminal-kit": "1.14.0",
|
||||
"terminal-kit": "1.14.3",
|
||||
"wrap-ansi": "3.0.1"
|
||||
}
|
||||
},
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "demo-joplin",
|
||||
"version": "1.0.6",
|
||||
"version": "1.0.8",
|
||||
"description": "Demo for Joplin CLI",
|
||||
"bin": {
|
||||
"demo-joplin": "./index.js"
|
||||
@@ -25,7 +25,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"fs-extra": "^4.0.2",
|
||||
"joplin": "^0.10.64"
|
||||
"joplin": "^0.10.69"
|
||||
},
|
||||
"author": "Laurent Cozic",
|
||||
"license": "MIT"
|
||||
|
2
ElectronClient/.gitignore
vendored
@@ -1,5 +1,5 @@
|
||||
build/
|
||||
app/node_modules/
|
||||
app/packageInfo.js
|
||||
dist/
|
||||
app/lib/
|
||||
app/gui/*.min.js
|
||||
|
@@ -10,6 +10,7 @@ class ElectronAppWrapper {
|
||||
this.electronApp_ = electronApp;
|
||||
this.env_ = env;
|
||||
this.win_ = null;
|
||||
this.willQuitApp_ = false;
|
||||
}
|
||||
|
||||
electronApp() {
|
||||
@@ -50,10 +51,16 @@ class ElectronAppWrapper {
|
||||
slashes: true
|
||||
}))
|
||||
|
||||
// Uncomment this to view errors if the application does not start
|
||||
if (this.env_ === 'dev') this.win_.webContents.openDevTools();
|
||||
|
||||
this.win_.on('closed', () => {
|
||||
this.win_ = null
|
||||
this.win_.on('close', (event) => {
|
||||
if (this.willQuitApp_ || process.platform !== 'darwin') {
|
||||
this.win_ = null;
|
||||
} else {
|
||||
event.preventDefault();
|
||||
this.win_.hide();
|
||||
}
|
||||
})
|
||||
|
||||
// Let us register listeners on the window, so we can update the state
|
||||
@@ -86,20 +93,16 @@ class ElectronAppWrapper {
|
||||
|
||||
this.createWindow();
|
||||
|
||||
this.electronApp_.on('before-quit', () => {
|
||||
this.willQuitApp_ = true;
|
||||
})
|
||||
|
||||
this.electronApp_.on('window-all-closed', () => {
|
||||
// On macOS it is common for applications and their menu bar
|
||||
// to stay active until the user quits explicitly with Cmd + Q
|
||||
if (process.platform !== 'darwin') {
|
||||
this.electronApp_.quit()
|
||||
}
|
||||
this.electronApp_.quit();
|
||||
})
|
||||
|
||||
this.electronApp_.on('activate', () => {
|
||||
// On macOS it's common to re-create a window in the app when the
|
||||
// dock icon is clicked and there are no other windows open.
|
||||
if (this.win_ === null) {
|
||||
createWindow()
|
||||
}
|
||||
this.win_.show();
|
||||
})
|
||||
}
|
||||
|
||||
|
@@ -8,7 +8,6 @@ const { BaseModel } = require('lib/base-model.js');
|
||||
const { _, setLocale } = require('lib/locale.js');
|
||||
const os = require('os');
|
||||
const fs = require('fs-extra');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { Tag } = require('lib/models/tag.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
@@ -16,6 +15,9 @@ const { JoplinDatabase } = require('lib/joplin-database.js');
|
||||
const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
||||
const { ElectronAppWrapper } = require('./ElectronAppWrapper');
|
||||
const { defaultState } = require('lib/reducer.js');
|
||||
const packageInfo = require('./packageInfo.js');
|
||||
const AlarmService = require('lib/services/AlarmService.js');
|
||||
const AlarmServiceDriverNode = require('lib/services/AlarmServiceDriverNode');
|
||||
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const Menu = bridge().Menu;
|
||||
@@ -133,7 +135,11 @@ class Application extends BaseApplication {
|
||||
}
|
||||
|
||||
if (['NOTE_UPDATE_ONE', 'NOTE_DELETE', 'FOLDER_UPDATE_ONE', 'FOLDER_DELETE'].indexOf(action.type) >= 0) {
|
||||
if (!await reg.syncStarted()) reg.scheduleSync();
|
||||
if (!await reg.syncTarget().syncStarted()) reg.scheduleSync();
|
||||
}
|
||||
|
||||
if (['EVENT_NOTE_ALARM_FIELD_CHANGE', 'NOTE_DELETE'].indexOf(action.type) >= 0) {
|
||||
await AlarmService.updateNoteNotification(action.id, action.type === 'NOTE_DELETE');
|
||||
}
|
||||
|
||||
const result = await super.generalMiddleware(store, next, action);
|
||||
@@ -219,6 +225,37 @@ class Application extends BaseApplication {
|
||||
accelerator: 'CommandOrControl+Q',
|
||||
click: () => { bridge().electronApp().exit() }
|
||||
}]
|
||||
}, {
|
||||
label: _('Edit'),
|
||||
submenu: [{
|
||||
label: _('Copy'),
|
||||
screens: ['Main', 'OneDriveLogin'],
|
||||
role: 'copy',
|
||||
accelerator: 'CommandOrControl+C',
|
||||
}, {
|
||||
label: _('Cut'),
|
||||
screens: ['Main', 'OneDriveLogin'],
|
||||
role: 'cut',
|
||||
accelerator: 'CommandOrControl+X',
|
||||
}, {
|
||||
label: _('Paste'),
|
||||
screens: ['Main', 'OneDriveLogin'],
|
||||
role: 'paste',
|
||||
accelerator: 'CommandOrControl+V',
|
||||
}, {
|
||||
type: 'separator',
|
||||
screens: ['Main'],
|
||||
}, {
|
||||
label: _('Search in all the notes'),
|
||||
screens: ['Main'],
|
||||
accelerator: 'F6',
|
||||
click: () => {
|
||||
this.dispatch({
|
||||
type: 'WINDOW_COMMAND',
|
||||
name: 'search',
|
||||
});
|
||||
},
|
||||
}]
|
||||
}, {
|
||||
label: _('Tools'),
|
||||
submenu: [{
|
||||
@@ -239,7 +276,7 @@ class Application extends BaseApplication {
|
||||
}, {
|
||||
label: _('About Joplin'),
|
||||
click: () => {
|
||||
const p = require('./package.json');
|
||||
const p = packageInfo;
|
||||
let message = [
|
||||
p.description,
|
||||
'',
|
||||
@@ -254,12 +291,21 @@ class Application extends BaseApplication {
|
||||
},
|
||||
];
|
||||
|
||||
function isEmptyMenu(template) {
|
||||
for (let i = 0; i < template.length; i++) {
|
||||
const t = template[i];
|
||||
if (t.type !== 'separator') return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function removeUnwantedItems(template, screen) {
|
||||
let output = [];
|
||||
for (let i = 0; i < template.length; i++) {
|
||||
const t = Object.assign({}, template[i]);
|
||||
if (t.screens && t.screens.indexOf(screen) < 0) continue;
|
||||
if (t.submenu) t.submenu = removeUnwantedItems(t.submenu, screen);
|
||||
if (('submenu' in t) && isEmptyMenu(t.submenu)) continue;
|
||||
output.push(t);
|
||||
}
|
||||
return output;
|
||||
@@ -276,20 +322,19 @@ class Application extends BaseApplication {
|
||||
async start(argv) {
|
||||
argv = await super.start(argv);
|
||||
|
||||
AlarmService.setDriver(new AlarmServiceDriverNode({ appName: packageInfo.build.appId }));
|
||||
AlarmService.setLogger(reg.logger());
|
||||
|
||||
reg.setShowErrorMessageBoxHandler((message) => { bridge().showErrorMessageBox(message) });
|
||||
|
||||
if (Setting.value('openDevTools')) {
|
||||
bridge().window().webContents.openDevTools();
|
||||
}
|
||||
|
||||
this.updateMenu('Main');
|
||||
|
||||
this.initRedux();
|
||||
|
||||
// const windowSize = Setting.value('windowSize');
|
||||
// const width = windowSize && windowSize.width ? windowSize.width : 800;
|
||||
// const height = windowSize && windowSize.height ? windowSize.height : 800;
|
||||
// bridge().windowSetSize(width, height);
|
||||
|
||||
// this.store().dispatch({
|
||||
// type: 'WINDOW_CONTENT_SIZE_SET',
|
||||
// size: bridge().windowContentSize(),
|
||||
// });
|
||||
|
||||
// Since the settings need to be loaded before the store is created, it will never
|
||||
// receive the SETTING_UPDATE_ALL even, which mean state.settings will not be
|
||||
// initialised. So we manually call dispatchUpdateAll() to force an update.
|
||||
@@ -313,13 +358,29 @@ class Application extends BaseApplication {
|
||||
// but then doesn't install it on exit.
|
||||
if (shim.isWindows() || shim.isMac()) {
|
||||
const runAutoUpdateCheck = function() {
|
||||
bridge().checkForUpdatesAndNotify(Setting.value('profileDir') + '/log-autoupdater.txt');
|
||||
if (Setting.value('autoUpdateEnabled')) {
|
||||
bridge().checkForUpdatesAndNotify(Setting.value('profileDir') + '/log-autoupdater.txt');
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(() => { runAutoUpdateCheck() }, 5000);
|
||||
// For those who leave the app always open
|
||||
setInterval(() => { runAutoUpdateCheck() }, 2 * 60 * 60 * 1000);
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
AlarmService.garbageCollect();
|
||||
}, 1000 * 60 * 60);
|
||||
|
||||
if (Setting.value('env') === 'dev') {
|
||||
AlarmService.updateAllNotifications();
|
||||
} else {
|
||||
reg.scheduleSync().then(() => {
|
||||
// Wait for the first sync before updating the notifications, since synchronisation
|
||||
// might change the notifications.
|
||||
AlarmService.updateAllNotifications();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,10 +1,13 @@
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { dirname } = require('lib/path-utils.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
|
||||
class Bridge {
|
||||
|
||||
constructor(electronWrapper) {
|
||||
this.electronWrapper_ = electronWrapper;
|
||||
this.autoUpdateLogger_ = null;
|
||||
this.lastSelectedPath_ = null;
|
||||
}
|
||||
|
||||
electronApp() {
|
||||
@@ -38,7 +41,13 @@ class Bridge {
|
||||
|
||||
showOpenDialog(options) {
|
||||
const {dialog} = require('electron');
|
||||
return dialog.showOpenDialog(options);
|
||||
if (!options) options = {};
|
||||
if (!('defaultPath' in options) && this.lastSelectedPath_) options.defaultPath = this.lastSelectedPath_;
|
||||
const filePaths = dialog.showOpenDialog(options);
|
||||
if (filePaths && filePaths.length) {
|
||||
this.lastSelectedPath_ = dirname(filePaths[0]);
|
||||
}
|
||||
return filePaths;
|
||||
}
|
||||
|
||||
showMessageBox(options) {
|
||||
@@ -78,17 +87,21 @@ class Bridge {
|
||||
return require('electron').shell.openItem(fullPath)
|
||||
}
|
||||
|
||||
checkForUpdatesAndNotify(logFilePath) {
|
||||
async checkForUpdatesAndNotify(logFilePath) {
|
||||
if (!this.autoUpdater_) {
|
||||
const logger = new Logger();
|
||||
logger.addTarget('file', { path: logFilePath });
|
||||
logger.setLevel(Logger.LEVEL_DEBUG);
|
||||
logger.info('checkForUpdatesAndNotify: Intializing...');
|
||||
this.autoUpdateLogger_ = new Logger();
|
||||
this.autoUpdateLogger_.addTarget('file', { path: logFilePath });
|
||||
this.autoUpdateLogger_.setLevel(Logger.LEVEL_DEBUG);
|
||||
this.autoUpdateLogger_.info('checkForUpdatesAndNotify: Initializing...');
|
||||
this.autoUpdater_ = require("electron-updater").autoUpdater;
|
||||
this.autoUpdater_.logger = logger;
|
||||
this.autoUpdater_.logger = this.autoUpdateLogger_;
|
||||
}
|
||||
|
||||
return this.autoUpdater_.checkForUpdatesAndNotify();
|
||||
try {
|
||||
await this.autoUpdater_.checkForUpdatesAndNotify();
|
||||
} catch (error) {
|
||||
this.autoUpdateLogger_.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
BIN
ElectronClient/app/build/icons/1024x1024.png
Normal file
After Width: | Height: | Size: 79 KiB |
BIN
ElectronClient/app/build/icons/128x128.png
Normal file
After Width: | Height: | Size: 4.7 KiB |
BIN
ElectronClient/app/build/icons/144x144.png
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
ElectronClient/app/build/icons/16x16.png
Normal file
After Width: | Height: | Size: 679 B |
BIN
ElectronClient/app/build/icons/24x24.png
Normal file
After Width: | Height: | Size: 968 B |
BIN
ElectronClient/app/build/icons/256x256.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
ElectronClient/app/build/icons/32x32.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
ElectronClient/app/build/icons/48x48.png
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
ElectronClient/app/build/icons/512x512.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
ElectronClient/app/build/icons/72x72.png
Normal file
After Width: | Height: | Size: 2.3 KiB |
BIN
ElectronClient/app/build/icons/96x96.png
Normal file
After Width: | Height: | Size: 3.3 KiB |
23
ElectronClient/app/compile-package-info.js
Normal file
@@ -0,0 +1,23 @@
|
||||
const fs = require('fs-extra');
|
||||
|
||||
// Electron Builder strip off certain important keys from package.json, which we need, in particular build.appId
|
||||
// so this script is used to preserve the keys that we need.
|
||||
|
||||
const packageInfo = require(__dirname + '/package.json');
|
||||
|
||||
let removeKeys = ['scripts', 'devDependencies', 'optionalDependencies', 'dependencies'];
|
||||
|
||||
for (let i = 0; i < removeKeys.length; i++) {
|
||||
delete packageInfo[removeKeys[i]];
|
||||
}
|
||||
|
||||
const appId = packageInfo.build.appId;
|
||||
|
||||
delete packageInfo.build;
|
||||
packageInfo.build = { appId: appId };
|
||||
|
||||
let fileContent = "// Auto-generated by compile-package-info.js\n// Do not change directly\nconst packageInfo = " + JSON.stringify(packageInfo, null, 4) + ';';
|
||||
fileContent += "\n";
|
||||
fileContent += "module.exports = packageInfo;";
|
||||
|
||||
fs.writeFileSync(__dirname + '/packageInfo.js', fileContent);
|
25
ElectronClient/app/eventManager.js
Normal file
@@ -0,0 +1,25 @@
|
||||
const events = require('events');
|
||||
|
||||
class EventManager {
|
||||
|
||||
constructor() {
|
||||
this.emitter_ = new events.EventEmitter();
|
||||
}
|
||||
|
||||
on(eventName, callback) {
|
||||
return this.emitter_.on(eventName, callback);
|
||||
}
|
||||
|
||||
emit(eventName, object = null) {
|
||||
return this.emitter_.emit(eventName, object);
|
||||
}
|
||||
|
||||
removeListener(eventName, callback) {
|
||||
return this.emitter_.removeListener(eventName, callback);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const eventManager = new EventManager();
|
||||
|
||||
module.exports = eventManager;
|
@@ -31,6 +31,8 @@ class ConfigScreenComponent extends React.Component {
|
||||
Setting.setValue(key, value);
|
||||
}
|
||||
|
||||
// Component key needs to be key+value otherwise it doesn't update when the settings change.
|
||||
|
||||
const md = Setting.settingMetadata(key);
|
||||
|
||||
if (md.isEnum) {
|
||||
@@ -42,7 +44,7 @@ class ConfigScreenComponent extends React.Component {
|
||||
}
|
||||
|
||||
return (
|
||||
<div key={key} style={rowStyle}>
|
||||
<div key={key+value} style={rowStyle}>
|
||||
<div style={labelStyle}><label>{md.label()}</label></div>
|
||||
<select value={value} style={controlStyle} onChange={(event) => { updateSettingValue(key, event.target.value) }}>
|
||||
{items}
|
||||
@@ -51,7 +53,7 @@ class ConfigScreenComponent extends React.Component {
|
||||
);
|
||||
} else if (md.type === Setting.TYPE_BOOL) {
|
||||
return (
|
||||
<div key={key} style={rowStyle}>
|
||||
<div key={key+value} style={rowStyle}>
|
||||
<div style={controlStyle}>
|
||||
<label><input type="checkbox" defaultChecked={!!value} onChange={(event) => { updateSettingValue(key, !!event.target.checked) }}/><span style={labelStyle}> {md.label()}</span></label>
|
||||
</div>
|
||||
|
@@ -41,7 +41,7 @@ class HeaderComponent extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const style = this.props.style;
|
||||
const style = Object.assign({}, this.props.style);
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const showBackButton = this.props.showBackButton === undefined || this.props.showBackButton === true;
|
||||
style.height = theme.headerHeight;
|
||||
|
@@ -6,13 +6,16 @@ const { NoteList } = require('./NoteList.min.js');
|
||||
const { NoteText } = require('./NoteText.min.js');
|
||||
const { PromptDialog } = require('./PromptDialog.min.js');
|
||||
const { Setting } = require('lib/models/setting.js');
|
||||
const { BaseModel } = require('lib/base-model.js');
|
||||
const { Tag } = require('lib/models/tag.js');
|
||||
const { Note } = require('lib/models/note.js');
|
||||
const { uuid } = require('lib/uuid.js');
|
||||
const { Folder } = require('lib/models/folder.js');
|
||||
const { themeStyle } = require('../theme.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const layoutUtils = require('lib/layout-utils.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const eventManager = require('../eventManager');
|
||||
|
||||
class MainScreenComponent extends React.Component {
|
||||
|
||||
@@ -97,13 +100,14 @@ class MainScreenComponent extends React.Component {
|
||||
folder = await Folder.save({ title: answer }, { userSideValidation: true });
|
||||
} catch (error) {
|
||||
bridge().showErrorMessageBox(error.message);
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.dispatch({
|
||||
type: 'FOLDER_SELECT',
|
||||
id: folder.id,
|
||||
});
|
||||
if (folder) {
|
||||
this.props.dispatch({
|
||||
type: 'FOLDER_SELECT',
|
||||
id: folder.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ promptOptions: null });
|
||||
@@ -128,6 +132,91 @@ class MainScreenComponent extends React.Component {
|
||||
}
|
||||
},
|
||||
});
|
||||
} else if (command.name === 'renameNotebook') {
|
||||
const folder = await Folder.load(command.id);
|
||||
if (!folder) return;
|
||||
|
||||
this.setState({
|
||||
promptOptions: {
|
||||
label: _('Rename notebook:'),
|
||||
value: folder.title,
|
||||
onClose: async (answer) => {
|
||||
if (answer !== null) {
|
||||
try {
|
||||
await Folder.save({ id: folder.id, title: answer }, { userSideValidation: true });
|
||||
} catch (error) {
|
||||
bridge().showErrorMessageBox(error.message);
|
||||
}
|
||||
}
|
||||
this.setState({ promptOptions: null });
|
||||
}
|
||||
},
|
||||
});
|
||||
} else if (command.name === 'search') {
|
||||
this.setState({
|
||||
promptOptions: {
|
||||
label: _('Search:'),
|
||||
onClose: async (answer) => {
|
||||
if (answer !== null) {
|
||||
const searchId = uuid.create();
|
||||
|
||||
this.props.dispatch({
|
||||
type: 'SEARCH_ADD',
|
||||
search: {
|
||||
id: searchId,
|
||||
title: answer,
|
||||
query_pattern: answer,
|
||||
query_folder_id: null,
|
||||
type_: BaseModel.TYPE_SEARCH,
|
||||
},
|
||||
});
|
||||
|
||||
this.props.dispatch({
|
||||
type: 'SEARCH_SELECT',
|
||||
id: searchId,
|
||||
});
|
||||
}
|
||||
this.setState({ promptOptions: null });
|
||||
}
|
||||
},
|
||||
});
|
||||
} else if (command.name === 'editAlarm') {
|
||||
const note = await Note.load(command.noteId);
|
||||
|
||||
let defaultDate = new Date(Date.now() + 2 * 3600 * 1000);
|
||||
defaultDate.setMinutes(0);
|
||||
defaultDate.setSeconds(0);
|
||||
|
||||
this.setState({
|
||||
promptOptions: {
|
||||
label: _('Set alarm:'),
|
||||
inputType: 'datetime',
|
||||
buttons: ['ok', 'cancel', 'clear'],
|
||||
value: note.todo_due ? new Date(note.todo_due) : defaultDate,
|
||||
onClose: async (answer, buttonType) => {
|
||||
let newNote = null;
|
||||
|
||||
if (buttonType === 'clear') {
|
||||
newNote = {
|
||||
id: note.id,
|
||||
todo_due: 0,
|
||||
};
|
||||
} else if (answer !== null) {
|
||||
newNote = {
|
||||
id: note.id,
|
||||
todo_due: answer.getTime(),
|
||||
};
|
||||
}
|
||||
|
||||
if (newNote) {
|
||||
await Note.save(newNote);
|
||||
eventManager.emit('alarmChange', { noteId: note.id });
|
||||
}
|
||||
|
||||
this.setState({ promptOptions: null });
|
||||
}
|
||||
},
|
||||
});
|
||||
} else {
|
||||
commandProcessed = false;
|
||||
}
|
||||
@@ -140,44 +229,58 @@ class MainScreenComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
styles(themeId, width, height) {
|
||||
const styleKey = themeId + '_' + width + '_' + height;
|
||||
if (styleKey === this.styleKey_) return this.styles_;
|
||||
|
||||
const theme = themeStyle(themeId);
|
||||
|
||||
this.styleKey_ = styleKey;
|
||||
|
||||
this.styles_ = {};
|
||||
|
||||
const rowHeight = height - theme.headerHeight;
|
||||
|
||||
this.styles_.header = {
|
||||
width: width,
|
||||
};
|
||||
|
||||
this.styles_.sideBar = {
|
||||
width: Math.floor(layoutUtils.size(width * .2, 150, 300)),
|
||||
height: rowHeight,
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'top',
|
||||
};
|
||||
|
||||
this.styles_.noteList = {
|
||||
width: Math.floor(layoutUtils.size(width * .2, 150, 300)),
|
||||
height: rowHeight,
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'top',
|
||||
};
|
||||
|
||||
this.styles_.noteText = {
|
||||
width: Math.floor(layoutUtils.size(width - this.styles_.sideBar.width - this.styles_.noteList.width, 0)),
|
||||
height: rowHeight,
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'top',
|
||||
};
|
||||
|
||||
this.styles_.prompt = {
|
||||
width: width,
|
||||
height: height,
|
||||
};
|
||||
|
||||
return this.styles_;
|
||||
}
|
||||
|
||||
render() {
|
||||
const style = this.props.style;
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const promptOptions = this.state.promptOptions;
|
||||
const folders = this.props.folders;
|
||||
const notes = this.props.notes;
|
||||
|
||||
const headerStyle = {
|
||||
width: style.width,
|
||||
};
|
||||
|
||||
const rowHeight = style.height - theme.headerHeight;
|
||||
|
||||
const sideBarStyle = {
|
||||
width: Math.floor(layoutUtils.size(style.width * .2, 150, 300)),
|
||||
height: rowHeight,
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'top',
|
||||
};
|
||||
|
||||
const noteListStyle = {
|
||||
width: Math.floor(layoutUtils.size(style.width * .2, 150, 300)),
|
||||
height: rowHeight,
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'top',
|
||||
};
|
||||
|
||||
const noteTextStyle = {
|
||||
width: Math.floor(layoutUtils.size(style.width - sideBarStyle.width - noteListStyle.width, 0)),
|
||||
height: rowHeight,
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'top',
|
||||
};
|
||||
|
||||
const promptStyle = {
|
||||
width: style.width,
|
||||
height: style.height,
|
||||
};
|
||||
const styles = this.styles(this.props.theme, style.width, style.height);
|
||||
|
||||
const headerButtons = [];
|
||||
|
||||
@@ -201,6 +304,12 @@ class MainScreenComponent extends React.Component {
|
||||
onClick: () => { this.doCommand({ name: 'newNotebook' }) },
|
||||
});
|
||||
|
||||
headerButtons.push({
|
||||
title: _('Search'),
|
||||
iconName: 'fa-search',
|
||||
onClick: () => { this.doCommand({ name: 'search' }) },
|
||||
});
|
||||
|
||||
headerButtons.push({
|
||||
title: _('Layout'),
|
||||
iconName: 'fa-columns',
|
||||
@@ -210,20 +319,29 @@ class MainScreenComponent extends React.Component {
|
||||
},
|
||||
});
|
||||
|
||||
if (!this.promptOnClose_) {
|
||||
this.promptOnClose_ = (answer, buttonType) => {
|
||||
return this.state.promptOptions.onClose(answer, buttonType);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={style}>
|
||||
<PromptDialog
|
||||
value={promptOptions && promptOptions.value ? promptOptions.value : ''}
|
||||
autocomplete={promptOptions && ('autocomplete' in promptOptions) ? promptOptions.autocomplete : null}
|
||||
defaultValue={promptOptions && promptOptions.value ? promptOptions.value : ''}
|
||||
theme={this.props.theme}
|
||||
style={promptStyle}
|
||||
onClose={(answer) => promptOptions.onClose(answer)}
|
||||
style={styles.prompt}
|
||||
onClose={this.promptOnClose_}
|
||||
label={promptOptions ? promptOptions.label : ''}
|
||||
description={promptOptions ? promptOptions.description : null}
|
||||
visible={!!this.state.promptOptions} />
|
||||
<Header style={headerStyle} showBackButton={false} buttons={headerButtons} />
|
||||
<SideBar style={sideBarStyle} />
|
||||
<NoteList style={noteListStyle} />
|
||||
<NoteText style={noteTextStyle} visiblePanes={this.props.noteVisiblePanes} />
|
||||
visible={!!this.state.promptOptions}
|
||||
buttons={promptOptions && ('buttons' in promptOptions) ? promptOptions.buttons : null}
|
||||
inputType={promptOptions && ('inputType' in promptOptions) ? promptOptions.inputType : null} />
|
||||
<Header style={styles.header} showBackButton={false} buttons={headerButtons} />
|
||||
<SideBar style={styles.sideBar} />
|
||||
<NoteList style={styles.noteList} />
|
||||
<NoteText style={styles.noteText} visiblePanes={this.props.noteVisiblePanes} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -33,8 +33,6 @@ class NavigatorComponent extends Component {
|
||||
height: this.props.style.height,
|
||||
};
|
||||
|
||||
|
||||
|
||||
return (
|
||||
<div style={this.props.style}>
|
||||
<Screen style={screenStyle} {...screenProps}/>
|
||||
|
@@ -7,6 +7,7 @@ const { _ } = require('lib/locale.js');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const Menu = bridge().Menu;
|
||||
const MenuItem = bridge().MenuItem;
|
||||
const eventManager = require('../eventManager');
|
||||
|
||||
class NoteListComponent extends React.Component {
|
||||
|
||||
@@ -52,28 +53,31 @@ class NoteListComponent extends React.Component {
|
||||
}
|
||||
|
||||
itemContextMenu(event) {
|
||||
const noteId = event.target.getAttribute('data-id');
|
||||
if (!noteId) throw new Error('No data-id on element');
|
||||
const noteIds = this.props.selectedNoteIds;
|
||||
if (!noteIds.length) return;
|
||||
|
||||
const menu = new Menu()
|
||||
|
||||
menu.append(new MenuItem({label: _('Add or remove tags'), click: async () => {
|
||||
menu.append(new MenuItem({label: _('Add or remove tags'), enabled: noteIds.length === 1, click: async () => {
|
||||
this.props.dispatch({
|
||||
type: 'WINDOW_COMMAND',
|
||||
name: 'setTags',
|
||||
noteId: noteId,
|
||||
noteId: noteIds[0],
|
||||
});
|
||||
}}));
|
||||
|
||||
menu.append(new MenuItem({label: _('Switch between note and to-do'), click: async () => {
|
||||
const note = await Note.load(noteId);
|
||||
await Note.save(Note.toggleIsTodo(note));
|
||||
menu.append(new MenuItem({label: _('Switch between note and to-do type'), click: async () => {
|
||||
for (let i = 0; i < noteIds.length; i++) {
|
||||
const note = await Note.load(noteIds[i]);
|
||||
await Note.save(Note.toggleIsTodo(note));
|
||||
eventManager.emit('noteTypeToggle', { noteId: note.id });
|
||||
}
|
||||
}}));
|
||||
|
||||
menu.append(new MenuItem({label: _('Delete'), click: async () => {
|
||||
const ok = bridge().showConfirmMessageBox(_('Delete note?'));
|
||||
const ok = bridge().showConfirmMessageBox(noteIds.length > 1 ? _('Delete notes?') : _('Delete note?'));
|
||||
if (!ok) return;
|
||||
await Note.delete(noteId);
|
||||
await Note.batchDelete(noteIds);
|
||||
}}));
|
||||
|
||||
menu.popup(bridge().window());
|
||||
@@ -81,10 +85,33 @@ class NoteListComponent extends React.Component {
|
||||
|
||||
itemRenderer(item, theme, width) {
|
||||
const onTitleClick = async (event, item) => {
|
||||
this.props.dispatch({
|
||||
type: 'NOTE_SELECT',
|
||||
id: item.id,
|
||||
});
|
||||
if (event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
this.props.dispatch({
|
||||
type: 'NOTE_SELECT_TOGGLE',
|
||||
id: item.id,
|
||||
});
|
||||
} else if (event.shiftKey) {
|
||||
event.preventDefault();
|
||||
this.props.dispatch({
|
||||
type: 'NOTE_SELECT_EXTEND',
|
||||
id: item.id,
|
||||
});
|
||||
} else {
|
||||
this.props.dispatch({
|
||||
type: 'NOTE_SELECT',
|
||||
id: item.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const onDragStart = (event) => {
|
||||
const noteIds = this.props.selectedNoteIds;
|
||||
if (!noteIds.length) return;
|
||||
|
||||
event.dataTransfer.setDragImage(new Image(), 1, 1);
|
||||
event.dataTransfer.clearData();
|
||||
event.dataTransfer.setData('text/x-jop-note-ids', JSON.stringify(noteIds));
|
||||
}
|
||||
|
||||
const onCheckboxClick = async (event) => {
|
||||
@@ -94,12 +121,13 @@ class NoteListComponent extends React.Component {
|
||||
todo_completed: checked ? time.unixMs() : 0,
|
||||
}
|
||||
await Note.save(newNote);
|
||||
eventManager.emit('todoToggle', { noteId: item.id });
|
||||
}
|
||||
|
||||
const hPadding = 10;
|
||||
|
||||
let style = Object.assign({ width: width }, this.style().listItem);
|
||||
if (this.props.selectedNoteId === item.id) style = Object.assign(style, this.style().listItemSelected);
|
||||
if (this.props.selectedNoteIds.indexOf(item.id) >= 0) style = Object.assign(style, this.style().listItemSelected);
|
||||
|
||||
// Setting marginBottom = 1 because it makes the checkbox looks more centered, at least on Windows
|
||||
// but don't know how it will look in other OSes.
|
||||
@@ -118,12 +146,13 @@ class NoteListComponent extends React.Component {
|
||||
return <div key={item.id + '_' + item.todo_completed} style={style}>
|
||||
{checkbox}
|
||||
<a
|
||||
data-id={item.id}
|
||||
className="list-item"
|
||||
onContextMenu={(event) => this.itemContextMenu(event)}
|
||||
href="#"
|
||||
draggable={true}
|
||||
style={listItemTitleStyle}
|
||||
onClick={(event) => { onTitleClick(event, item) }}
|
||||
onDragStart={(event) => onDragStart(event) }
|
||||
>
|
||||
{item.title}
|
||||
</a>
|
||||
@@ -164,7 +193,7 @@ class NoteListComponent extends React.Component {
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
notes: state.notes,
|
||||
selectedNoteId: state.selectedNoteId,
|
||||
selectedNoteIds: state.selectedNoteIds,
|
||||
theme: state.settings.theme,
|
||||
// uncompletedTodosOnTop: state.settings.uncompletedTodosOnTop,
|
||||
};
|
||||
|
@@ -1,7 +1,9 @@
|
||||
const React = require('react');
|
||||
const { Note } = require('lib/models/note.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { Setting } = require('lib/models/setting.js');
|
||||
const { IconButton } = require('./IconButton.min.js');
|
||||
const Toolbar = require('./Toolbar.min.js');
|
||||
const { connect } = require('react-redux');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
@@ -13,6 +15,7 @@ const AceEditor = require('react-ace').default;
|
||||
const Menu = bridge().Menu;
|
||||
const MenuItem = bridge().MenuItem;
|
||||
const { shim } = require('lib/shim.js');
|
||||
const eventManager = require('../eventManager');
|
||||
|
||||
require('brace/mode/markdown');
|
||||
// https://ace.c9.io/build/kitchen-sink.html
|
||||
@@ -55,11 +58,18 @@ class NoteTextComponent extends React.Component {
|
||||
this.restoreScrollTop_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.onAlarmChange_ = (event) => { if (event.noteId === this.props.noteId) this.reloadNote(this.props); }
|
||||
this.onNoteTypeToggle_ = (event) => { if (event.noteId === this.props.noteId) this.reloadNote(this.props); }
|
||||
this.onTodoToggle_ = (event) => { if (event.noteId === this.props.noteId) this.reloadNote(this.props); }
|
||||
}
|
||||
|
||||
mdToHtml() {
|
||||
if (this.mdToHtml_) return this.mdToHtml_;
|
||||
this.mdToHtml_ = new MdToHtml({ supportsResourceLinks: true });
|
||||
this.mdToHtml_ = new MdToHtml({
|
||||
supportsResourceLinks: true,
|
||||
resourceBaseUrl: 'file://' + Setting.value('resourceDir') + '/',
|
||||
});
|
||||
return this.mdToHtml_;
|
||||
}
|
||||
|
||||
@@ -79,6 +89,10 @@ class NoteTextComponent extends React.Component {
|
||||
});
|
||||
|
||||
this.lastLoadedNoteId_ = note ? note.id : null;
|
||||
|
||||
eventManager.on('alarmChange', this.onAlarmChange_);
|
||||
eventManager.on('noteTypeToggle', this.onNoteTypeToggle_);
|
||||
eventManager.on('todoToggle', this.onTodoToggle_);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@@ -86,6 +100,10 @@ class NoteTextComponent extends React.Component {
|
||||
|
||||
this.mdToHtml_ = null;
|
||||
this.destroyWebview();
|
||||
|
||||
eventManager.removeListener('alarmChange', this.onAlarmChange_);
|
||||
eventManager.removeListener('noteTypeToggle', this.onNoteTypeToggle_);
|
||||
eventManager.removeListener('todoToggle', this.onTodoToggle_);
|
||||
}
|
||||
|
||||
async saveIfNeeded() {
|
||||
@@ -106,13 +124,24 @@ class NoteTextComponent extends React.Component {
|
||||
}, 500);
|
||||
}
|
||||
|
||||
async reloadNote(props) {
|
||||
this.mdToHtml_ = null;
|
||||
async reloadNote(props, options = null) {
|
||||
if (!options) options = {};
|
||||
if (!('noReloadIfLocalChanges' in options)) options.noReloadIfLocalChanges = false;
|
||||
|
||||
const noteId = props.noteId;
|
||||
this.lastLoadedNoteId_ = noteId;
|
||||
const note = noteId ? await Note.load(noteId) : null;
|
||||
if (noteId !== this.lastLoadedNoteId_) return; // Race condition - current note was changed while this one was loading
|
||||
if (!options.noReloadIfLocalChanges && this.isModified()) return;
|
||||
|
||||
// If the note hasn't been changed, exit now
|
||||
if (this.state.note && note) {
|
||||
let diff = Note.diffObjects(this.state.note, note);
|
||||
delete diff.type_;
|
||||
if (!Object.getOwnPropertyNames(diff).length) return;
|
||||
}
|
||||
|
||||
this.mdToHtml_ = null;
|
||||
|
||||
// If we are loading nothing (noteId == null), make sure to
|
||||
// set webviewReady to false too because the webview component
|
||||
@@ -141,7 +170,7 @@ class NoteTextComponent extends React.Component {
|
||||
}
|
||||
|
||||
if ('syncStarted' in nextProps && !nextProps.syncStarted && !this.isModified()) {
|
||||
await this.reloadNote(nextProps);
|
||||
await this.reloadNote(nextProps, { noReloadIfLocalChanges: true });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,6 +334,45 @@ class NoteTextComponent extends React.Component {
|
||||
this.scheduleSave();
|
||||
}
|
||||
|
||||
async commandAttachFile() {
|
||||
const noteId = this.props.noteId;
|
||||
if (!noteId) return;
|
||||
|
||||
const filePaths = bridge().showOpenDialog({
|
||||
properties: ['openFile', 'createDirectory', 'multiSelections'],
|
||||
});
|
||||
if (!filePaths || !filePaths.length) return;
|
||||
|
||||
await this.saveIfNeeded();
|
||||
let note = await Note.load(noteId);
|
||||
|
||||
for (let i = 0; i < filePaths.length; i++) {
|
||||
const filePath = filePaths[i];
|
||||
try {
|
||||
reg.logger().info('Attaching ' + filePath);
|
||||
note = await shim.attachFileToNote(note, filePath);
|
||||
reg.logger().info('File was attached.');
|
||||
this.setState({
|
||||
note: Object.assign({}, note),
|
||||
lastSavedNote: Object.assign({}, note),
|
||||
});
|
||||
} catch (error) {
|
||||
reg.logger().error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commandSetAlarm() {
|
||||
const noteId = this.props.noteId;
|
||||
if (!noteId) return;
|
||||
|
||||
this.props.dispatch({
|
||||
type: 'WINDOW_COMMAND',
|
||||
name: 'editAlarm',
|
||||
noteId: noteId,
|
||||
});
|
||||
}
|
||||
|
||||
itemContextMenu(event) {
|
||||
const noteId = this.props.noteId;
|
||||
if (!noteId) return;
|
||||
@@ -312,24 +380,26 @@ class NoteTextComponent extends React.Component {
|
||||
const menu = new Menu()
|
||||
|
||||
menu.append(new MenuItem({label: _('Attach file'), click: async () => {
|
||||
const filePaths = bridge().showOpenDialog({
|
||||
properties: ['openFile', 'createDirectory'],
|
||||
});
|
||||
if (!filePaths || !filePaths.length) return;
|
||||
return this.commandAttachFile();
|
||||
}}));
|
||||
|
||||
await this.saveIfNeeded();
|
||||
const note = await Note.load(noteId);
|
||||
const newNote = await shim.attachFileToNote(note, filePaths[0]);
|
||||
|
||||
this.setState({
|
||||
note: newNote,
|
||||
lastSavedNote: Object.assign({}, newNote),
|
||||
});
|
||||
menu.append(new MenuItem({label: _('Set alarm'), click: async () => {
|
||||
return this.commandSetAlarm();
|
||||
}}));
|
||||
|
||||
menu.popup(bridge().window());
|
||||
}
|
||||
|
||||
// shouldComponentUpdate(nextProps, nextState) {
|
||||
// //console.info('NEXT PROPS', JSON.stringify(nextProps));
|
||||
// console.info('NEXT STATE ====================');
|
||||
// for (var n in nextProps) {
|
||||
// if (!nextProps.hasOwnProperty(n)) continue;
|
||||
// console.info(n + ' = ' + (nextProps[n] === this.props[n]));
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
render() {
|
||||
const style = this.props.style;
|
||||
const note = this.state.note;
|
||||
@@ -361,7 +431,7 @@ class NoteTextComponent extends React.Component {
|
||||
height: 30,
|
||||
boxSizing: 'border-box',
|
||||
marginTop: 10,
|
||||
marginBottom: 10,
|
||||
marginBottom: 0,
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
};
|
||||
@@ -377,7 +447,11 @@ class NoteTextComponent extends React.Component {
|
||||
marginRight: rootStyle.paddingLeft,
|
||||
};
|
||||
|
||||
const bottomRowHeight = rootStyle.height - titleBarStyle.height - titleBarStyle.marginBottom - titleBarStyle.marginTop;
|
||||
const toolbarStyle = {
|
||||
marginBottom: 10,
|
||||
};
|
||||
|
||||
const bottomRowHeight = rootStyle.height - titleBarStyle.height - titleBarStyle.marginBottom - titleBarStyle.marginTop - theme.toolbarHeight - toolbarStyle.marginBottom;
|
||||
|
||||
const viewerStyle = {
|
||||
width: Math.floor(innerWidth / 2),
|
||||
@@ -431,6 +505,28 @@ class NoteTextComponent extends React.Component {
|
||||
this.webview_.send('setHtml', html);
|
||||
}
|
||||
|
||||
const toolbarItems = [];
|
||||
|
||||
toolbarItems.push({
|
||||
title: _('Attach file'),
|
||||
iconName: 'fa-paperclip',
|
||||
onClick: () => { return this.commandAttachFile(); },
|
||||
});
|
||||
|
||||
if (note.is_todo) {
|
||||
toolbarItems.push({
|
||||
title: Note.needAlarm(note) ? time.formatMsToLocal(note.todo_due) : _('Set alarm'),
|
||||
iconName: 'fa-clock-o',
|
||||
enabled: !note.todo_completed,
|
||||
onClick: () => { return this.commandSetAlarm(); },
|
||||
});
|
||||
}
|
||||
|
||||
const toolbar = <Toolbar
|
||||
style={toolbarStyle}
|
||||
items={toolbarItems}
|
||||
/>
|
||||
|
||||
const titleEditor = <input
|
||||
type="text"
|
||||
style={titleEditorStyle}
|
||||
@@ -485,6 +581,7 @@ class NoteTextComponent extends React.Component {
|
||||
{ titleEditor }
|
||||
{ titleBarMenuButton }
|
||||
</div>
|
||||
{ toolbar }
|
||||
{ editor }
|
||||
{ viewer }
|
||||
</div>
|
||||
@@ -495,7 +592,7 @@ class NoteTextComponent extends React.Component {
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
noteId: state.selectedNoteId,
|
||||
noteId: state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null,
|
||||
folderId: state.selectedFolderId,
|
||||
itemType: state.selectedItemType,
|
||||
folders: state.folders,
|
||||
|