1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-12-14 23:26:58 +02:00

Compare commits

...

65 Commits

Author SHA1 Message Date
Laurent Cozic
f51ad26db7 Release v0.10.59-android 2017-11-26 19:27:42 +00:00
Laurent Cozic
0428917ea2 iOS: Updated version 2017-11-24 17:28:43 +00:00
Laurent Cozic
914a2554ab All: Updated translations 2017-11-24 19:59:21 +00:00
Laurent Cozic
26bb7dc33b All: Added dev sync target and option to select it in mobile 2017-11-24 19:47:24 +00:00
Laurent Cozic
8e5b0eadd9 All: Clean up 2017-11-24 19:26:49 +00:00
Laurent Cozic
a96b91cfef All: Removed sync target info out of setting class 2017-11-24 19:21:30 +00:00
Laurent Cozic
03251d4c40 All: renamed sync targets 2017-11-24 19:09:15 +00:00
Laurent Cozic
112609c5f1 All: Moving sync target logic to SyncTarget classes 2017-11-24 18:59:16 +00:00
Laurent Cozic
cc7cbc2ecf All: Started moving sync target logic under SyncTarget classes 2017-11-24 18:37:40 +00:00
Laurent Cozic
946ad7c71a All: Started moving sync target logic under SyncTarget classes 2017-11-24 18:06:30 +00:00
Laurent Cozic
d7f3cfd778 All: Started moving sync glue logic to SyncTarget classes 2017-11-23 23:10:55 +00:00
Laurent Cozic
a2ae2c766a Merge branch 'master' of github.com:laurent22/joplin 2017-11-23 19:29:12 +00:00
Laurent Cozic
f52ff730b1 Disable cache 2017-11-23 19:29:03 +00:00
Laurent Cozic
9327a61a36 iOS: maybe bad change 2017-11-23 19:15:44 +00:00
Laurent Cozic
05997908e5 Electron release v0.10.25 2017-11-23 19:05:37 +00:00
Laurent Cozic
2a93dea378 Electron release v0.10.24 2017-11-23 19:04:56 +00:00
Laurent Cozic
fbf7b2cc43 Mobile: Hide irrelevant settings 2017-11-23 18:54:26 +00:00
Laurent Cozic
d8b19f7d08 Mobile: Also do multi-selection from search page 2017-11-23 18:41:35 +00:00
Laurent Cozic
acc4eb5d28 Mobile: Allow selecting, deleting and moving multiple notes 2017-11-23 18:47:51 +00:00
Laurent Cozic
bcd5cd9110 Electron: Hide invalid characters 2017-11-23 18:16:17 +00:00
Laurent Cozic
45a4034816 Android: Automate building for prod 2017-11-23 18:14:38 +00:00
Laurent Cozic
978a08fb06 Trying BuddyBuilder 72 2017-11-22 22:24:20 +00:00
Laurent Cozic
72dc5a6c99 Trying BuddyBuilder 2017-11-22 22:21:27 +00:00
Laurent Cozic
5340fb8af9 Electron: Disable Control+Click 2017-11-22 19:29:49 +00:00
Laurent Cozic
3ce1172c36 Allow changing notebook by drag and dropping notes 2017-11-22 19:20:19 +00:00
Laurent Cozic
e4d48f43d6 Allow multiple selection 2017-11-22 18:35:31 +00:00
Laurent Cozic
3e1ea0eb0a Android icons 2017-11-22 17:37:57 +00:00
Laurent Cozic
d10d6ba7de Merge branch 'master' of github.com:laurent22/joplin 2017-11-21 19:44:10 +00:00
Laurent Cozic
cf832354a2 Updated website 2017-11-21 19:43:09 +00:00
Laurent Cozic
b27519b85a Update appveyor.yml 2017-11-21 19:31:43 +00:00
Laurent Cozic
27af0e69ef Electron release v0.10.23 2017-11-21 18:26:17 +00:00
Laurent Cozic
6283bf6190 Minor fixing on note rendering 2017-11-21 19:47:29 +00:00
Laurent Cozic
48b648e656 Electron: allow disabling auto-updates 2017-11-21 19:37:34 +00:00
Laurent Cozic
367a18db93 Electron: fixed loading of resources, and disable drag and dropping of links in app 2017-11-21 19:31:21 +00:00
Laurent Cozic
f5feb595f6 Updated translation 2017-11-21 19:14:30 +00:00
Laurent Cozic
c6ec0279fc Mobile: Made checkboxes smaller 2017-11-21 19:05:59 +00:00
Laurent Cozic
b0b5488c2e Electron: Better error handling for auto-update 2017-11-21 18:59:32 +00:00
Laurent Cozic
3722012da5 Added command to export debug information from mobile and CLI 2017-11-21 18:48:50 +00:00
Laurent Cozic
c5214b6c44 Core: Fixed potential out-of-sync issue if user cancels while in the middle of delta step 2017-11-21 18:17:50 +00:00
Laurent Cozic
585ccc2b8b Update website 2017-11-20 22:31:29 +00:00
Laurent Cozic
ab8115b89d Added download badges 2017-11-20 22:28:22 +00:00
Laurent Cozic
8d8395c226 Added App Store icon 2017-11-20 20:06:32 +00:00
Laurent Cozic
8dbec0e8f4 Put locales under versioning 2017-11-20 19:14:02 +00:00
Laurent Cozic
1c1228bb88 Electron release v0.10.22 2017-11-20 18:59:13 +00:00
Laurent Cozic
7d9eec262a Updated translation 2017-11-20 18:58:51 +00:00
Laurent Cozic
a057fbf3bd Electron: added sync icon 2017-11-20 18:44:56 +00:00
Laurent Cozic
518feadc3e All: Better checkbox alignment and added padding on notebook title 2017-11-20 18:40:36 +00:00
Laurent Cozic
b1e351ce77 Merge branch 'master' of github.com:laurent22/joplin 2017-11-20 18:31:10 +00:00
Laurent Cozic
c6cb2800d7 Android: improved image type detection 2017-11-20 19:18:49 +00:00
Laurent Cozic
c31c7a8a67 Added iOS icon 512x512 2017-11-20 19:09:22 +00:00
Laurent Cozic
3d6fe4c2cd Android: Fixed image picker issues 2017-11-20 19:01:19 +00:00
Laurent Cozic
da7034ae08 iOS: Improved icon and fixed file attachement issue 2017-11-20 18:29:39 +00:00
Laurent Cozic
37de5fd4b3 Mobile: Improved loading of images. Added privacy policy url. 2017-11-20 18:25:23 +00:00
Laurent Cozic
e2cbef1538 Added privacy policy 2017-11-20 18:09:29 +00:00
Laurent Cozic
4de044dc90 Fix Android/iOS inconsitency with webview page scaling 2017-11-20 00:21:40 +00:00
Laurent Cozic
d5dc27d788 Use SVG icons for checkboxes 2017-11-19 23:34:41 +00:00
Laurent Cozic
e80dd59da2 iOS: Fixed attaching images 2017-11-19 22:08:58 +00:00
Laurent Cozic
cbd2075156 Merge branch 'ios-fixes-2' of github.com:laurent22/joplin into ios-fixes-2 2017-11-19 15:21:41 +00:00
Laurent Cozic
4b5c1491d0 iOS: various fixes 2017-11-19 15:19:36 +00:00
Laurent Cozic
ea077852a1 Mobile: allow attaching image or any other file 2017-11-19 15:18:07 +00:00
Laurent Cozic
ca20a2a1c2 Dropdown fioxes 2017-11-19 00:23:18 +00:00
Laurent Cozic
37c0b6d24a iOs tweaks 2017-11-19 00:03:42 +00:00
Laurent Cozic
0c14a42b28 RN: Replaced broken Picker by custom dropdown 2017-11-18 23:59:07 +00:00
Laurent Cozic
f126e0a944 Update website 2017-11-18 17:35:49 +00:00
Laurent Cozic
9519bb1218 Update website 2017-11-18 16:13:27 +00:00
170 changed files with 2896 additions and 2598 deletions

5
.gitignore vendored
View File

@@ -32,4 +32,7 @@ INFO.md
sync_staging.sh
*.swp
_vieux/
_mydocs
_mydocs
.DS_Store
Assets/DownloadBadges*.psd
node_modules

BIN
Assets/DemoDesktop.PNG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
Assets/DemoDesktop.psd Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
Assets/Icon-Android-512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
Assets/Icon-Android.psd Normal file

Binary file not shown.

BIN
Assets/Icon-ios-512.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

BIN
Assets/Icon-ios.psd Normal file

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 151 KiB

View File

Before

Width:  |  Height:  |  Size: 170 KiB

After

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
Assets/iOSIcons/29x2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
Assets/iOSIcons/29x3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
Assets/iOSIcons/40.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
Assets/iOSIcons/40x2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

BIN
Assets/iOSIcons/40x3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
Assets/iOSIcons/57.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
Assets/iOSIcons/57x2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

BIN
Assets/iOSIcons/60x2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
Assets/iOSIcons/60x3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

2
Assets/square-o.svg Normal file
View 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

View File

@@ -167,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,
};
});
@@ -181,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,
};
});
@@ -195,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();

View File

@@ -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();
}

View File

@@ -13,6 +13,7 @@ const headerHtml = `<!doctype html>
<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 {
@@ -90,6 +91,10 @@ const headerHtml = `<!doctype html>
.cli-screenshot .prompt {
color: #48C2F0;
}
.top-screenshot {
margin-top: 2em;
text-align: center;
}
.header {
position: relative;
padding-left: 2em;
@@ -128,16 +133,13 @@ const headerHtml = `<!doctype html>
.nav a {
color: white;
display: inline-block;
padding: .7em 1.4em .7em 1.4em;
}
.nav.sticky a {
padding: .4em 1.4em .4em 1.4em;
padding: .6em .9em .6em .9em;
}
.nav ul {
padding-left: 2em;
margin-bottom: 0;
display: table-cell;
min-width: 400px;
min-width: 165px;
}
.nav ul li {
display: inline-block;
@@ -152,15 +154,29 @@ const headerHtml = `<!doctype html>
width: 100%;
text-align: right;
vertical-align: middle;
padding-top: 3px;
line-height: 0;
}
.twitter-share-button {
.nav-right .share-btn {
display: none;
}
.github-share-button {
.share-btn-github {
display: inline-block;
}
</style>
.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>
@@ -176,13 +192,14 @@ const headerHtml = `<!doctype html>
<div class="nav-wrapper">
<div class="nav">
<ul>
<li class="{{selectedHome}}"><a href="{{baseUrl}}/">Home</a></li>
<li class="{{selectedTerminal}}"><a href="{{baseUrl}}/terminal">Terminal App Manual</a></li>
<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 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>
<a class="twitter-share-button" href="https://twitter.com/intent/tweet?url=http%3A%2F%2Fjoplin.cozic.net">Tweet</a>
<iframe class="github-share-button" src="https://ghbtns.com/github-btn.html?user=laurent22&repo=joplin&type=star&count=true" frameborder="0" scrolling="0" width="110px" height="20px"></iframe>
<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>
@@ -231,21 +248,6 @@ const footerHtml = `
// `;
const scriptHtml = `
<script>window.twttr = (function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0],
t = window.twttr || {};
if (d.getElementById(id)) return t;
js = d.createElement(s);
js.id = id;
js.src = "https://platform.twitter.com/widgets.js";
fjs.parentNode.insertBefore(js, fjs);
t._e = [];
t.ready = function(f) {
t._e.push(f);
};
return t;
}(document, "script", "twitter-wjs"));</script>
<script>
function stickyHeader() {
if ($(window).scrollTop() > 179) {
@@ -268,6 +270,10 @@ const scriptHtml = `
</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) {
@@ -297,14 +303,16 @@ function renderFileToHtml(sourcePath, targetPath, params) {
async function main() {
renderFileToHtml(rootDir + '/README.md', rootDir + '/docs/index.html', {
baseUrl: '',
selectedHome: 'selected',
});
renderFileToHtml(rootDir + '/README_terminal.md', rootDir + '/docs/terminal/index.html', {
baseUrl: '..',
selectedTerminal: 'selected',
});
renderFileToHtml(rootDir + '/README_desktop.md', rootDir + '/docs/desktop/index.html', {
selectedDesktop: 'selected',
});
}
main().catch((error) => {

View 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;

View File

@@ -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() {

View File

@@ -562,24 +562,21 @@ msgstr ""
msgid "Rename notebook:"
msgstr ""
msgid "Seach:"
msgstr ""
msgid "Seach"
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 ""
@@ -614,6 +611,9 @@ msgstr ""
msgid "Rename"
msgstr ""
msgid "Synchronise"
msgstr ""
msgid "Notebooks"
msgstr ""
@@ -623,9 +623,6 @@ msgstr ""
msgid "Searches"
msgstr ""
msgid "Synchronise"
msgstr ""
#, javascript-format
msgid "Usage: %s"
msgstr ""
@@ -634,6 +631,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 ""
@@ -647,11 +653,6 @@ msgid ""
"synchronisation again may fix the problem."
msgstr ""
msgid ""
"Please set the \"sync.2.path\" config value to the desired synchronisation "
"destination."
msgstr ""
#, javascript-format
msgid "Cannot access %s"
msgstr ""
@@ -720,10 +721,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 ""
@@ -732,20 +729,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 ""
@@ -790,9 +773,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 ""
@@ -822,12 +820,25 @@ 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 "Move to notebook..."
msgstr ""
#, javascript-format
msgid "Move %d notes to notebook \"%s\"?"
msgstr ""
msgid "Cancel synchronisation"
msgstr ""
@@ -847,6 +858,16 @@ msgstr ""
msgid "Discard changes"
msgstr ""
#, javascript-format
msgid "Unsupported image type: %s"
msgstr ""
msgid "Attach image"
msgstr ""
msgid "Attach any other file"
msgstr ""
msgid "Delete note"
msgstr ""

View 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."
@@ -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,9 +510,9 @@ 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."
@@ -532,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,7 +541,7 @@ msgid "Quit"
msgstr "Quitter"
msgid "Edit"
msgstr "Edition"
msgstr "Édition"
msgid "Copy"
msgstr "Copier"
@@ -568,7 +568,7 @@ msgid "Website and documentation"
msgstr "Documentation en ligne"
msgid "About Joplin"
msgstr "A props de Joplin"
msgstr "A propos de Joplin"
#, javascript-format
msgid "%s %s (%s, %s)"
@@ -614,24 +614,21 @@ msgstr "Séparez chaque étiquette par une virgule."
msgid "Rename notebook:"
msgstr "Renommer le carnet :"
msgid "Seach:"
msgstr "Chercher :"
msgid "Seach"
msgstr "Chercher"
msgid "Layout"
msgstr "Disposition"
msgid "Add or remove tags"
msgstr "Gérer les étiquettes"
msgid "Switch between note and to-do"
msgid "Switch between note and to-do type"
msgstr "Alterner entre note et tâche"
msgid "Delete"
msgstr "Supprimer"
msgid "Delete notes?"
msgstr "Supprimer les notes ?"
msgid "No notes in here. Create one by clicking on \"New note\"."
msgstr ""
"Pas de notes ici. Créez-en une en pressant le bouton \"Nouvelle note\"."
@@ -647,7 +644,7 @@ msgid "Refresh"
msgstr "Rafraîchir"
msgid "OneDrive Login"
msgstr "Connection OneDrive"
msgstr "Connexion OneDrive"
msgid "Import"
msgstr "Importer"
@@ -667,18 +664,18 @@ msgstr "Enlever cette recherche de la barre latérale ?"
msgid "Rename"
msgstr "Renommer"
msgid "Synchronise"
msgstr "Synchroniser"
msgid "Notebooks"
msgstr "Carnets"
msgid "Tags"
msgstr "Etiquettes"
msgstr "Étiquettes"
msgid "Searches"
msgstr "Recherches"
msgid "Synchronise"
msgstr "Synchroniser"
#, javascript-format
msgid "Usage: %s"
msgstr "Utilisation : %s"
@@ -687,6 +684,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"
@@ -699,16 +705,9 @@ 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."
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"
msgstr "Impossible d'accéder à %s"
@@ -739,7 +738,7 @@ msgstr "Objets distants supprimés : %d."
#, javascript-format
msgid "State: \"%s\"."
msgstr "Etat : \"%s\"."
msgstr "État : \"%s\"."
msgid "Cancelling..."
msgstr "Annulation..."
@@ -750,7 +749,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"
@@ -777,10 +776,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"
@@ -791,24 +786,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 "
@@ -836,7 +815,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é"
@@ -853,9 +832,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)"
@@ -887,11 +883,24 @@ 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 "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 "Cancel synchronisation"
msgstr "Annuler synchronisation"
@@ -901,7 +910,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 :"
@@ -912,6 +921,16 @@ msgstr "Enregistrer les changements"
msgid "Discard changes"
msgstr "Ignorer les changements"
#, javascript-format
msgid "Unsupported image type: %s"
msgstr "Type d'image non géré : %s"
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"
@@ -934,7 +953,7 @@ 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 "
@@ -951,6 +970,19 @@ msgstr ""
msgid "Welcome"
msgstr "Bienvenue"
#~ 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."

View File

@@ -562,24 +562,21 @@ msgstr ""
msgid "Rename notebook:"
msgstr ""
msgid "Seach:"
msgstr ""
msgid "Seach"
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 ""
@@ -614,6 +611,9 @@ msgstr ""
msgid "Rename"
msgstr ""
msgid "Synchronise"
msgstr ""
msgid "Notebooks"
msgstr ""
@@ -623,9 +623,6 @@ msgstr ""
msgid "Searches"
msgstr ""
msgid "Synchronise"
msgstr ""
#, javascript-format
msgid "Usage: %s"
msgstr ""
@@ -634,6 +631,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 ""
@@ -647,11 +653,6 @@ msgid ""
"synchronisation again may fix the problem."
msgstr ""
msgid ""
"Please set the \"sync.2.path\" config value to the desired synchronisation "
"destination."
msgstr ""
#, javascript-format
msgid "Cannot access %s"
msgstr ""
@@ -720,10 +721,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 ""
@@ -732,20 +729,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 ""
@@ -790,9 +773,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 ""
@@ -822,12 +820,25 @@ 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 "Move to notebook..."
msgstr ""
#, javascript-format
msgid "Move %d notes to notebook \"%s\"?"
msgstr ""
msgid "Cancel synchronisation"
msgstr ""
@@ -847,6 +858,16 @@ msgstr ""
msgid "Discard changes"
msgstr ""
#, javascript-format
msgid "Unsupported image type: %s"
msgstr ""
msgid "Attach image"
msgstr ""
msgid "Attach any other file"
msgstr ""
msgid "Delete note"
msgstr ""

View File

@@ -1,6 +1,6 @@
{
"name": "joplin",
"version": "0.10.70",
"version": "0.10.74",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -917,6 +917,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",

View File

@@ -18,7 +18,7 @@
],
"owner": "Laurent Cozic"
},
"version": "0.10.70",
"version": "0.10.74",
"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",

View File

@@ -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();
});
});

View File

@@ -16,6 +16,9 @@ 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');
let databases_ = [];
let synchronizers_ = [];
@@ -29,12 +32,13 @@ 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);
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 +125,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 +153,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');

View File

@@ -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');
@@ -133,7 +132,7 @@ 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();
}
const result = await super.generalMiddleware(store, next, action);
@@ -337,13 +336,17 @@ 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);
}
reg.scheduleSync();
}
}

View File

@@ -5,6 +5,7 @@ class Bridge {
constructor(electronWrapper) {
this.electronWrapper_ = electronWrapper;
this.autoUpdateLogger_ = null;
}
electronApp() {
@@ -78,17 +79,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: Intializing...');
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);
}
}
}

View File

@@ -270,6 +270,7 @@ class MainScreenComponent extends React.Component {
return (
<div style={style}>
<PromptDialog
autocomplete={promptOptions && ('autocomplete' in promptOptions) ? promptOptions.autocomplete : null}
value={promptOptions && promptOptions.value ? promptOptions.value : ''}
theme={this.props.theme}
style={promptStyle}

View File

@@ -52,28 +52,30 @@ 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));
}
}}));
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 +83,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) => {
@@ -99,7 +124,7 @@ class NoteListComponent extends React.Component {
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 +143,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 +190,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,
};

View File

@@ -59,7 +59,10 @@ class NoteTextComponent extends React.Component {
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_;
}
@@ -501,7 +504,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,

View File

@@ -47,7 +47,7 @@ class OneDriveLoginScreenComponent extends React.Component {
this.authCode_ = code[1];
try {
await reg.oneDriveApi().execTokenRequest(this.authCode_, this.redirectUrl(), true);
await reg.syncTarget().api().execTokenRequest(this.authCode_, this.redirectUrl(), true);
this.props.dispatch({ type: 'NAV_BACK' });
reg.scheduleSync(0);
} catch (error) {
@@ -62,11 +62,11 @@ class OneDriveLoginScreenComponent extends React.Component {
}
startUrl() {
return reg.oneDriveApi().authCodeUrl(this.redirectUrl());
return reg.syncTarget().api().authCodeUrl(this.redirectUrl());
}
redirectUrl() {
return reg.oneDriveApi().nativeClientRedirectUrl();
return reg.syncTarget().api().nativeClientRedirectUrl();
}
render() {

View File

@@ -4,6 +4,7 @@ const shared = require('lib/components/shared/side-menu-shared.js');
const { Synchronizer } = require('lib/synchronizer.js');
const { BaseModel } = require('lib/base-model.js');
const { Folder } = require('lib/models/folder.js');
const { Note } = require('lib/models/note.js');
const { Tag } = require('lib/models/tag.js');
const { _ } = require('lib/locale.js');
const { themeStyle } = require('../theme.js');
@@ -164,7 +165,32 @@ class SideBarComponent extends React.Component {
let style = Object.assign({}, this.style().listItem);
if (selected) style = Object.assign(style, this.style().listItemSelected);
if (folder.id === Folder.conflictFolderId()) style = Object.assign(style, this.style().conflictFolder);
return <a className="list-item" href="#" data-id={folder.id} data-type={BaseModel.TYPE_FOLDER} onContextMenu={(event) => this.itemContextMenu(event)} key={folder.id} style={style} onClick={() => {this.folderItem_click(folder)}}>{folder.title}</a>
const onDragOver = (event, folder) => {
if (event.dataTransfer.types.indexOf('text/x-jop-note-ids') >= 0) event.preventDefault();
}
const onDrop = async (event, folder) => {
if (event.dataTransfer.types.indexOf('text/x-jop-note-ids') < 0) return;
event.preventDefault();
const noteIds = JSON.parse(event.dataTransfer.getData('text/x-jop-note-ids'));
for (let i = 0; i < noteIds.length; i++) {
await Note.moveToFolder(noteIds[i], folder.id);
}
}
return <a
className="list-item"
onDragOver={(event) => { onDragOver(event, folder) } }
onDrop={(event) => { onDrop(event, folder) } }
href="#"
data-id={folder.id}
data-type={BaseModel.TYPE_FOLDER}
onContextMenu={(event) => this.itemContextMenu(event)}
key={folder.id}
style={style} onClick={() => {this.folderItem_click(folder)}}>{folder.title}
</a>
}
tagItem(tag, selected) {
@@ -189,9 +215,12 @@ class SideBarComponent extends React.Component {
return <div style={style} key={key}>{icon}{label}</div>
}
synchronizeButton(label) {
synchronizeButton(type) {
const style = this.style().button;
return <a className="synchronize-button" style={style} href="#" key="sync_button" onClick={() => {this.sync_click()}}>{label}</a>
const iconName = type === 'sync' ? 'fa-refresh' : 'fa-times';
const label = type === 'sync' ? _('Synchronise') : _('Cancel');
const icon = <i style={{fontSize: style.fontSize, marginRight: 5}} className={"fa " + iconName}></i>
return <a className="synchronize-button" style={style} href="#" key="sync_button" onClick={() => {this.sync_click()}}>{icon}{label}</a>
}
render() {
@@ -232,7 +261,7 @@ class SideBarComponent extends React.Component {
syncReportText.push(<div key={i}>{lines[i]}</div>);
}
items.push(this.synchronizeButton(this.props.syncStarted ? _('Cancel') : _('Synchronise')));
items.push(this.synchronizeButton(this.props.syncStarted ? 'cancel' : 'sync'));
items.push(<div style={this.style().syncReport} key='sync_report'>{syncReportText}</div>);

View File

@@ -12,7 +12,7 @@ body {
</style>
<div id="hlScriptContainer"></div>
<div id="content"></div>
<div id="content" ondragstart="return false;" ondrop="return false;"></div>
<script>
const { ipcRenderer } = require('electron');
@@ -71,8 +71,6 @@ function applyHljs(codeElements) {
// / Handle dynamically loading HLJS when a code element is present
// ----------------------------------------------------------------------
ipcRenderer.on('setHtml', (event, html) => {
contentElement.innerHTML = html;

View File

@@ -9,5 +9,12 @@
<body>
<div id="react-root"></div>
<script src="main-html.js"></script>
<style>
/* Disable dragging of links (which are often buttons) */
a:not([draggable=true]), img:not([draggable=true]) {
-webkit-user-drag: none;
user-drag: none;
}
</style>
</body>
</html>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -31,6 +31,19 @@ BaseItem.loadClass('NoteTag', NoteTag);
Setting.setConstant('appId', 'net.cozic.joplin-desktop');
Setting.setConstant('appType', 'desktop');
// Disable drag and drop of links inside application (which would
// open it as if the whole app was a browser)
document.addEventListener('dragover', event => event.preventDefault());
document.addEventListener('drop', event => event.preventDefault());
// Disable middle-click (which would open a new browser window, but we don't want this)
document.addEventListener('auxclick', event => event.preventDefault());
// Each link (rendered as a button or list item) has its own custom click event
// so disable the default. In particular this will disable Ctrl+Clicking a link
// which would open a new browser window.
document.addEventListener('click', (event) => event.preventDefault());
shimInit();
app().start(bridge().processArgv()).then(() => {

View File

@@ -1,6 +1,6 @@
{
"name": "Joplin",
"version": "0.10.21",
"version": "0.10.25",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -10,13 +10,13 @@
"integrity": "sha512-+rr4OgeTNrLuJAf09o3USdttEYiXvZshWMkhD6wR9v1ieXH0JM1Q2yT41/cJuJcqiPpSXlM/g3aR+Y5MWQdr0Q==",
"dev": true,
"requires": {
"7zip-bin-mac": "1.0.1"
"7zip-bin-win": "2.1.1"
}
},
"7zip-bin-mac": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/7zip-bin-mac/-/7zip-bin-mac-1.0.1.tgz",
"integrity": "sha1-Pmh3i78JJq3GgVlCcHRQXUdVXAI=",
"7zip-bin-win": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/7zip-bin-win/-/7zip-bin-win-2.1.1.tgz",
"integrity": "sha512-6VGEW7PXGroTsoI2QW3b0ea95HJmbVBHvfANKLLMzSzFA1zKqVX5ybNuhmeGpf6vA0x8FJTt6twpprDANsY5WQ==",
"optional": true
},
"@types/node": {

View File

@@ -1,6 +1,6 @@
{
"name": "Joplin",
"version": "0.10.21",
"version": "0.10.25",
"description": "Joplin for Desktop",
"main": "main.js",
"scripts": {

View File

@@ -9,6 +9,14 @@ body, textarea {
overflow: hidden;
}
/* By default, the Ice Editor displays invalid characters, such as non-breaking spaces
as red boxes, but since those are actually valid characters and common in imported
Evernote data, we hide them here. */
.ace-chrome .ace_invisible_space {
background-color: transparent !important;
opacity: 0;
}
.note-list .list-item:hover {
background-color: rgba(0,160,255,0.1) !important;
}

View File

@@ -7,6 +7,14 @@ const request = require('request');
const url = 'https://api.github.com/repos/laurent22/joplin/releases/latest';
const readmePath = __dirname + '/../../README.md';
async function msleep(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, ms);
});
}
async function gitHubLatestRelease() {
return new Promise((resolve, reject) => {
request.get({
@@ -17,6 +25,7 @@ async function gitHubLatestRelease() {
if (error) {
reject(error);
} else if (response.statusCode !== 200) {
console.warn(data);
reject(new Error('Error HTTP ' + response.statusCode));
} else {
resolve(data);
@@ -48,8 +57,23 @@ function setReadmeContent(content) {
return fs.writeFileSync(readmePath, content);
}
async function main() {
const release = await gitHubLatestRelease();
async function main(argv) {
const waitForVersion = argv.length === 3 ? argv[2] : null;
if (waitForVersion) console.info('Waiting for version ' + waitForVersion + ' to be released before updating readme...');
let release = null;
while (true) {
release = await gitHubLatestRelease();
if (!waitForVersion) break;
if (release.tag_name !== waitForVersion) {
await msleep(60000 * 5);
} else {
console.info('Got version ' + waitForVersion);
break;
}
}
const winUrl = downloadUrl(release, 'windows');
const macOsUrl = downloadUrl(release, 'macos');
@@ -57,13 +81,13 @@ async function main() {
let content = readmeContent();
if (winUrl) content = content.replace(/(\(https:\/\/github.com\/laurent22\/joplin\/releases\/download\/.*?\.exe\))/, '(' + winUrl + ')');
if (macOsUrl) content = content.replace(/(\(https:\/\/github.com\/laurent22\/joplin\/releases\/download\/.*?\.dmg\))/, '(' + macOsUrl + ')');
if (linuxUrl) content = content.replace(/(\(https:\/\/github.com\/laurent22\/joplin\/releases\/download\/.*?\.AppImage\))/, '(' + linuxUrl + ')');
if (winUrl) content = content.replace(/(https:\/\/github.com\/laurent22\/joplin\/releases\/download\/.*?\.exe)/, winUrl);
if (macOsUrl) content = content.replace(/(https:\/\/github.com\/laurent22\/joplin\/releases\/download\/.*?\.dmg)/, macOsUrl);
if (linuxUrl) content = content.replace(/(https:\/\/github.com\/laurent22\/joplin\/releases\/download\/.*?\.AppImage)/, linuxUrl);
setReadmeContent(content);
}
main().catch((error) => {
main(process.argv).catch((error) => {
console.error('Fatal error', error);
});

View File

@@ -9,4 +9,6 @@ git commit -m "Electron release $VERSION"
git tag $VERSION
git push && git push --tags
echo "Create a draft release at: https://github.com/laurent22/joplin/releases/tag/$VERSION"
echo "Create a draft release at: https://github.com/laurent22/joplin/releases/tag/$VERSION"
node "$APP_DIR/update-readme-download.js" $VERSION

View File

@@ -1,47 +0,0 @@
'use strict';
const fs = require('fs-extra');
const https = require('https');
const request = require('request');
const url = 'https://api.github.com/repos/laurent22/joplin/releases/latest';
async function gitHubLatestRelease() {
return new Promise((resolve, reject) => {
request.get({
url: url,
json: true,
headers: {'User-Agent': 'Joplin Readme Updater'}
}, (error, response, data) => {
if (err) {
reject(error);
} else if (response.statusCode !== 200) {
reject(new Error('Error HTTP ' + response.statusCode));
} else {
resolve(data);
}
});
});
}
function downloadUrl(release, os) {
if (!release || !release.assets || !release.assets.length) return null;
for (let i = 0; i < release.assets.length; i++) {
const asset = release.assets[i];
const name = asset.name;
if (name.indexOf('.dmg') > 0 && os === 'macos') return asset.browser_download_url;
}
}
async function main() {
const release = await gitHubLatestRelease();
console.info(downloadUrl(release, 'windows'));
console.info(downloadUrl(release, 'macos'));
console.info(downloadUrl(release, 'linux'));
}
main().catch((error) => {
console.error('Fatal error', error);
});

View File

@@ -8,26 +8,26 @@ The notes can be [synchronised](#synchronisation) with various targets including
Joplin is still under development but is out of Beta and should be suitable for every day use. The UI of the terminal client is built on top of the great [terminal-kit](https://github.com/cronvel/terminal-kit) library, the desktop client using [Electron](https://electronjs.org/), and the Android client front end is done using [React Native](https://facebook.github.io/react-native/).
<img src="https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/AllClients.jpg" style="max-width: 100%">
<div class="top-screenshot"><img src="https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/AllClients.jpg" style="max-width: 100%; max-height: 35em;"></div>
# Installation
Three types of applications are available: **desktop** (Windows, macOS and Linux), **mobile** (Android) or for **terminal** emulator (Windows, macOS and Linux). All applications have similar user interfaces and can synchronise with each others.
Three types of applications are available: **desktop** (Windows, macOS and Linux), **mobile** (Android and iOS) and for **terminal** (Windows, macOS and Linux). All applications have similar user interfaces and can synchronise with each others.
## Desktop applications
Operating system | Link
Operating System | Download
-----------------|--------
Windows | [Download Joplin for Windows](https://github.com/laurent22/joplin/releases/download/v0.10.20/Joplin-Setup-0.10.20.exe)
macOS | [Download Joplin for macOS](https://github.com/laurent22/joplin/releases/download/v0.10.20/Joplin-0.10.20.dmg)
Linux | [Download Joplin for Linux](https://github.com/laurent22/joplin/releases/download/v0.10.20/Joplin-0.10.20-x86_64.AppImage)
Windows | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.25/Joplin-Setup-0.10.25.exe'><img alt='Get it on Windows' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeWindows.png'/></a>
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.25/Joplin-0.10.25.dmg'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeMacOS.png'/></a>
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v0.10.25/Joplin-0.10.25-x86_64.AppImage'><img alt='Get it on macOS' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeLinux.png'/></a>
## Mobile applications
Operating system | Link
Operating System | Download
-----------------|--------
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="60px" src='https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png'/></a>
iOS | Coming soon!
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeAndroid.png'/></a>
iOS | <a href='https://itunes.apple.com/us/app/joplin/id1315599797'><img alt='Get it on the App Store' height="40px" src='https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/BadgeIOS.png'/></a>
## Terminal application
@@ -108,15 +108,16 @@ This translation will apply to both the terminal and the Android application.
# Coming features
The two main features that remain to be implemented are:
- iOS application (80% done)
- End to end encryption (to-do)
- All: End to end encryption
- Windows: Tray icon
- Desktop apps: Tag auto-complete
- Desktop apps: Dark theme
- Linux: Enable auto-update for desktop app
# Known bugs
- Non-alphabetical characters such as Chinese or Arabic might create glitches in the terminal on Windows. This is a limitation of the current Windows console.
- In the React Native app, changing the notebook of a note sometimes has no effect, due to [this bug](https://github.com/facebook/react-native/issues/15556). Due to [this other bug](https://github.com/facebook/react-native/issues/13351), changing one config value sometimes also set a different one. This is likely to be resolved soon in a future version of React Native.
- Auto-update is not working in the Linux desktop application.
# License

1
README_desktop.md Normal file
View File

@@ -0,0 +1 @@
<img src="https://raw.githubusercontent.com/laurent22/joplin/master/docs/images/DemoDesktop.png" style="max-width: 100%">

View File

@@ -53,6 +53,5 @@ fastlane/Preview.html
fastlane/screenshots
# This is generated:
locales
android/build*
android/app/build*

View File

@@ -90,8 +90,8 @@ android {
applicationId "net.cozic.joplin"
minSdkVersion 16
targetSdkVersion 22
versionCode 57
versionName "0.10.44"
versionCode 74
versionName "0.10.59"
ndk {
abiFilters "armeabi-v7a", "x86"
}
@@ -137,6 +137,8 @@ android {
}
dependencies {
compile project(':react-native-fs')
compile project(':react-native-image-picker')
compile project(':react-native-vector-icons')
compile project(':react-native-fs')
compile fileTree(dir: "libs", include: ["*.jar"])

View File

@@ -1,38 +1,37 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="net.cozic.joplin"
android:versionCode="2"
android:versionName="0.8.0">
xmlns:tools="http://schemas.android.com/tools"
package="net.cozic.joplin"
android:versionCode="2"
android:versionName="0.8.0">
<uses-permission android:name="android.permission.INTERNET" />
<!-- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> -->
<!-- <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove"/>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove"/>
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="22" />
<uses-sdk
android:minSdkVersion="16"
android:targetSdkVersion="22" />
<application
android:name=".MainApplication"
android:allowBackup="true"
<application
android:name=".MainApplication"
android:allowBackup="true"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize"
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize"
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" />
</application>
</manifest>

View File

@@ -3,6 +3,7 @@ package net.cozic.joplin;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.imagepicker.ImagePickerPackage;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
@@ -30,6 +31,7 @@ public class MainApplication extends Application implements ReactApplication {
return Arrays.<ReactPackage>asList(
new ImageResizerPackage(),
new MainReactPackage(),
new ImagePickerPackage(),
new ReactNativeDocumentPicker(),
new RNFetchBlobPackage(),
new RNFSPackage(),

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

@@ -1,4 +1,8 @@
rootProject.name = 'Joplin'
include ':react-native-fs'
project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android')
include ':react-native-image-picker'
project(':react-native-image-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-image-picker/android')
include ':react-native-vector-icons'
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/android')
include ':react-native-image-resizer'

View File

@@ -1,2 +1,2 @@
cd android
gradlew.bat installDebug
gradlew.bat installDebug --console plain

View File

@@ -0,0 +1,9 @@
@echo off
rem Clear build dir if permission issue:
rem rmdir /S/Q android\app\build
setlocal
node ..\Tools\prepare-android-prod-build.js
cd android
gradlew.bat assembleRelease -PbuildDir=build --console plain

File diff suppressed because it is too large Load Diff

View File

@@ -1,129 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0820"
version = "1.3">
<BuildAction
parallelizeBuildables = "NO"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D2A28121D9B038B00D4039D"
BuildableName = "libReact.a"
BlueprintName = "React-tvOS"
ReferencedContainer = "container:../node_modules/react-native/React/React.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "Joplin-tvOS.app"
BlueprintName = "Joplin-tvOS"
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E48F1E0B4A5D006451C7"
BuildableName = "Joplin-tvOSTests.xctest"
BlueprintName = "Joplin-tvOSTests"
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E48F1E0B4A5D006451C7"
BuildableName = "Joplin-tvOSTests.xctest"
BlueprintName = "Joplin-tvOSTests"
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "Joplin-tvOS.app"
BlueprintName = "Joplin-tvOS"
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "Joplin-tvOS.app"
BlueprintName = "Joplin-tvOS"
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2D02E47A1E0B4A5D006451C7"
BuildableName = "Joplin-tvOS.app"
BlueprintName = "Joplin-tvOS"
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@@ -35,11 +35,11 @@
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForTesting = "NO"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
@@ -57,16 +57,6 @@
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "JoplinTests.xctest"
BlueprintName = "JoplinTests"
ReferencedContainer = "container:Joplin.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

@@ -1,34 +1,154 @@
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "icon-20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "icon-20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"scale" : "2x"
"idiom" : "iphone",
"filename" : "icon-29@1x.png",
"scale" : "1x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"filename" : "icon-29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"size" : "40x40",
"filename" : "icon-29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"size" : "60x60",
"filename" : "icon-40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"size" : "60x60",
"filename" : "icon-40@3x.png",
"scale" : "3x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "icon-57@1x.png",
"scale" : "1x"
},
{
"size" : "57x57",
"idiom" : "iphone",
"filename" : "icon-57@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "icon-60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "icon-60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "icon-20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "icon-20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "icon-29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "icon-29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "icon-40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "icon-40@2x.png",
"scale" : "2x"
},
{
"size" : "50x50",
"idiom" : "ipad",
"filename" : "icon-50@1x.png",
"scale" : "1x"
},
{
"size" : "50x50",
"idiom" : "ipad",
"filename" : "icon-50@2x.png",
"scale" : "2x"
},
{
"size" : "72x72",
"idiom" : "ipad",
"filename" : "icon-72@1x.png",
"scale" : "1x"
},
{
"size" : "72x72",
"idiom" : "ipad",
"filename" : "icon-72@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "icon-76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "icon-76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "icon-83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "AppStore.png",
"scale" : "1x"
}
],
"info" : {

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

Some files were not shown because too many files have changed in this diff Show More