You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2026-01-05 00:12:33 +02:00
Compare commits
193 Commits
v1.3.2
...
plugin_sys
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4693c924c3 | ||
|
|
6e7015e8f9 | ||
|
|
1f1cd9f11b | ||
|
|
ac959b2882 | ||
|
|
4609634259 | ||
|
|
f06b64f3f9 | ||
|
|
80503ad0c1 | ||
|
|
05dfaf7347 | ||
|
|
59be83d958 | ||
|
|
e7676a148f | ||
|
|
cb28021a27 | ||
|
|
d8ab03f3bc | ||
|
|
48b6b83950 | ||
|
|
3e3bad0947 | ||
|
|
e527f590c5 | ||
|
|
88f968aac5 | ||
|
|
bbd01c3a38 | ||
|
|
e8d22a4177 | ||
|
|
6caec4df39 | ||
|
|
eea56fcf94 | ||
|
|
a5a278eced | ||
|
|
a978ae4cfa | ||
|
|
dd1c1dded4 | ||
|
|
5407481976 | ||
|
|
5a1f6b13cf | ||
|
|
a904431782 | ||
|
|
b23ec936fd | ||
|
|
73a4517902 | ||
|
|
a966b32583 | ||
|
|
91a3428124 | ||
|
|
93a5ba9490 | ||
|
|
e570f7a226 | ||
|
|
9b28a8995f | ||
|
|
197f509f5d | ||
|
|
4c83d7aa75 | ||
|
|
17e053b358 | ||
|
|
a29c93634d | ||
|
|
a5f5bfcfec | ||
|
|
b509329383 | ||
|
|
91c7abed42 | ||
|
|
54fda6ac97 | ||
|
|
341eefebaf | ||
|
|
af714cbdac | ||
|
|
6a0bf15c28 | ||
|
|
5a2bdad348 | ||
|
|
fd08bfdd86 | ||
|
|
1a5cead659 | ||
|
|
85df133386 | ||
|
|
869d11e113 | ||
|
|
d7a01d3965 | ||
|
|
989036241d | ||
|
|
433cef1827 | ||
|
|
433d34f62b | ||
|
|
e7ec33bbf9 | ||
|
|
a29f42d78d | ||
|
|
74ed234ab9 | ||
|
|
86b76ccb77 | ||
|
|
29aedf8480 | ||
|
|
9486f5e0d2 | ||
|
|
9a458431b8 | ||
|
|
2c9db9d18c | ||
|
|
ae9c535842 | ||
|
|
a518da587e | ||
|
|
92e6e00d9e | ||
|
|
b64c9cf18a | ||
|
|
275d6e230d | ||
|
|
ab8ce15c33 | ||
|
|
3fb6179587 | ||
|
|
28cc38064c | ||
|
|
1fa5ab2b96 | ||
|
|
15f4e635d9 | ||
|
|
7a79d7ef7e | ||
|
|
776e7dfe3f | ||
|
|
790b622cbc | ||
|
|
89117b4ee9 | ||
|
|
c245517e0f | ||
|
|
31db95d0c4 | ||
|
|
2cd50de065 | ||
|
|
6dcb7e2605 | ||
|
|
9c9bd4ba51 | ||
|
|
71e6964945 | ||
|
|
97dc2fa6bd | ||
|
|
161e112e7b | ||
|
|
c93a6f86cd | ||
|
|
9bb178b9d9 | ||
|
|
8087bc5280 | ||
|
|
100aaab58d | ||
|
|
126c7b5a75 | ||
|
|
d448ea3a02 | ||
|
|
555727e5fe | ||
|
|
77eceea426 | ||
|
|
a09c6f152c | ||
|
|
3830195752 | ||
|
|
c9594065c3 | ||
|
|
360d3bbff9 | ||
|
|
6fde8dfe52 | ||
|
|
de2ccfbfe0 | ||
|
|
31f53b12fd | ||
|
|
d45b280ada | ||
|
|
b5a137545e | ||
|
|
4e80dbb7a7 | ||
|
|
90b0c99806 | ||
|
|
c8c0c3af46 | ||
|
|
110de50bf9 | ||
|
|
b7e110d888 | ||
|
|
f1c557cb43 | ||
|
|
43e9fc92c9 | ||
|
|
10fa331731 | ||
|
|
cc8e122de3 | ||
|
|
bca5fd8db9 | ||
|
|
13101f7d9b | ||
|
|
4f2884a0f7 | ||
|
|
4872f1419c | ||
|
|
fdcaab3caf | ||
|
|
9f2c4b938f | ||
|
|
8be65acf7d | ||
|
|
e05aa433e4 | ||
|
|
ccfb0b43d6 | ||
|
|
4211e962c6 | ||
|
|
6b54c4244b | ||
|
|
1d5a4bd381 | ||
|
|
86a1103a64 | ||
|
|
45edaaa96f | ||
|
|
b7b158aa94 | ||
|
|
e6bc55b6f8 | ||
|
|
111092791c | ||
|
|
4e91e01ddc | ||
|
|
44dce6c6c3 | ||
|
|
b25ad50a1b | ||
|
|
e2e3be66ee | ||
|
|
4b3119ebba | ||
|
|
1712a67e2e | ||
|
|
cd563b050c | ||
|
|
4426f2dbec | ||
|
|
0fbbf2eee0 | ||
|
|
2fdad2fe80 | ||
|
|
0249ff48ca | ||
|
|
142fc02f9c | ||
|
|
842b123285 | ||
|
|
a4aca68b85 | ||
|
|
1397af0692 | ||
|
|
5da760dd0c | ||
|
|
511d58d3af | ||
|
|
a1c9e675ad | ||
|
|
0349ad0b80 | ||
|
|
9d8411a25e | ||
|
|
754ba2267a | ||
|
|
4cdac308d1 | ||
|
|
c0d5c7c282 | ||
|
|
f9296992bf | ||
|
|
5b06ebde1e | ||
|
|
d309c75974 | ||
|
|
807c89a42f | ||
|
|
0b8350e9ea | ||
|
|
49e7be8924 | ||
|
|
810c63e3b2 | ||
|
|
d917ca5551 | ||
|
|
ec642300ab | ||
|
|
9034d646bf | ||
|
|
96f6bbb035 | ||
|
|
e14ed655d2 | ||
|
|
54e5323f54 | ||
|
|
ef58affed1 | ||
|
|
76fa87d878 | ||
|
|
2db2225788 | ||
|
|
425c1763f4 | ||
|
|
044f2eac6a | ||
|
|
281f135125 | ||
|
|
6c88f8b8ae | ||
|
|
832abcddf0 | ||
|
|
e6c481f45c | ||
|
|
11fecfb83a | ||
|
|
2facc9868e | ||
|
|
7b9ad9d1c3 | ||
|
|
3b96a99f3e | ||
|
|
575ce5128b | ||
|
|
dece28da35 | ||
|
|
2ffc429971 | ||
|
|
8baa125842 | ||
|
|
6b6092ffc7 | ||
|
|
714c912aff | ||
|
|
ebf8b744e1 | ||
|
|
1039e98396 | ||
|
|
8f00538cb1 | ||
|
|
2b530a3ff9 | ||
|
|
d089511aa6 | ||
|
|
450c8dbf22 | ||
|
|
94d87285dc | ||
|
|
ffeb7d0ecd | ||
|
|
aba92857cb | ||
|
|
5918c81dbb | ||
|
|
819d9fd977 | ||
|
|
ad80e074de |
@@ -68,7 +68,6 @@ CliClient/tests/models_Setting.js
|
||||
CliClient/tests/services_CommandService.js
|
||||
CliClient/tests/services_InteropService.js
|
||||
CliClient/tests/services_PluginService.js
|
||||
CliClient/tests/services_rest_Api.js
|
||||
CliClient/tests/services/plugins/sandboxProxy.js
|
||||
CliClient/tests/synchronizer_LockHandler.js
|
||||
CliClient/tests/synchronizer_MigrationHandler.js
|
||||
@@ -78,7 +77,6 @@ ElectronClient/commands/copyDevCommand.js
|
||||
ElectronClient/commands/focusElement.js
|
||||
ElectronClient/commands/startExternalEditing.js
|
||||
ElectronClient/commands/stopExternalEditing.js
|
||||
ElectronClient/commands/toggleExternalEditing.js
|
||||
ElectronClient/ElectronAppWrapper.js
|
||||
ElectronClient/global.d.js
|
||||
ElectronClient/gui/Button/Button.js
|
||||
|
||||
2
.github/PULL_REQUEST_TEMPLATE
vendored
2
.github/PULL_REQUEST_TEMPLATE
vendored
@@ -20,6 +20,6 @@ If it's not related to any platform (such as a translation, change to the docume
|
||||
|
||||
Then please append the issue that you've addressed or fixed. Use "Resolves #123" for new features or improvements and "Fixes #123" for bug fixes.
|
||||
|
||||
AND PLEASE READ THE GUIDE: https://github.com/laurent22/joplin/blob/dev/CONTRIBUTING.md
|
||||
AND PLEASE READ THE GUIDE: https://github.com/laurent22/joplin/blob/master/CONTRIBUTING.md
|
||||
|
||||
-->
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -62,7 +62,6 @@ CliClient/tests/models_Setting.js
|
||||
CliClient/tests/services_CommandService.js
|
||||
CliClient/tests/services_InteropService.js
|
||||
CliClient/tests/services_PluginService.js
|
||||
CliClient/tests/services_rest_Api.js
|
||||
CliClient/tests/services/plugins/sandboxProxy.js
|
||||
CliClient/tests/synchronizer_LockHandler.js
|
||||
CliClient/tests/synchronizer_MigrationHandler.js
|
||||
@@ -72,7 +71,6 @@ ElectronClient/commands/copyDevCommand.js
|
||||
ElectronClient/commands/focusElement.js
|
||||
ElectronClient/commands/startExternalEditing.js
|
||||
ElectronClient/commands/stopExternalEditing.js
|
||||
ElectronClient/commands/toggleExternalEditing.js
|
||||
ElectronClient/ElectronAppWrapper.js
|
||||
ElectronClient/global.d.js
|
||||
ElectronClient/gui/Button/Button.js
|
||||
|
||||
@@ -38,7 +38,7 @@ If you want to start contributing to the project's code, please follow these gui
|
||||
- All the applications share the same backend (database, synchronisation, settings, models, business logic, etc.) so if you change something in the backend in one app, makes sure it still work in the other apps. Usually it does, but keep this in mind.
|
||||
- Pull requests that make many changes using an automated tool, like for spell fixing, styling, etc. will not be accepted. An exception would be if the changes have been discussed in the forum and someone has agreed to review **and test** the pull request.
|
||||
|
||||
Building the apps is relatively easy - please [see the build instructions](https://github.com/laurent22/joplin/blob/dev/BUILD.md) for more details.
|
||||
Building the apps is relatively easy - please [see the build instructions](https://github.com/laurent22/joplin/blob/master/BUILD.md) for more details.
|
||||
|
||||
## Coding style
|
||||
|
||||
|
||||
@@ -124,6 +124,7 @@ class Command extends BaseCommand {
|
||||
|
||||
if (args.name == 'locale') {
|
||||
setLocale(Setting.value('locale'));
|
||||
app().onLocaleChanged();
|
||||
}
|
||||
|
||||
await Setting.saveAll();
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -42,38 +42,38 @@ stats['ar'] = {"percentDone":80};
|
||||
stats['eu'] = {"percentDone":34};
|
||||
stats['bs_BA'] = {"percentDone":83};
|
||||
stats['bg_BG'] = {"percentDone":66};
|
||||
stats['ca'] = {"percentDone":96};
|
||||
stats['hr_HR'] = {"percentDone":27};
|
||||
stats['ca'] = {"percentDone":53};
|
||||
stats['hr_HR'] = {"percentDone":28};
|
||||
stats['cs_CZ'] = {"percentDone":82};
|
||||
stats['da_DK'] = {"percentDone":74};
|
||||
stats['de_DE'] = {"percentDone":98};
|
||||
stats['de_DE'] = {"percentDone":95};
|
||||
stats['et_EE'] = {"percentDone":66};
|
||||
stats['en_GB'] = {"percentDone":100};
|
||||
stats['en_US'] = {"percentDone":100};
|
||||
stats['es_ES'] = {"percentDone":95};
|
||||
stats['eo'] = {"percentDone":38};
|
||||
stats['fr_FR'] = {"percentDone":99};
|
||||
stats['fr_FR'] = {"percentDone":94};
|
||||
stats['gl_ES'] = {"percentDone":43};
|
||||
stats['id_ID'] = {"percentDone":93};
|
||||
stats['it_IT'] = {"percentDone":90};
|
||||
stats['it_IT'] = {"percentDone":91};
|
||||
stats['nl_NL'] = {"percentDone":96};
|
||||
stats['nl_BE'] = {"percentDone":34};
|
||||
stats['nl_NL'] = {"percentDone":95};
|
||||
stats['nb_NO'] = {"percentDone":88};
|
||||
stats['fa'] = {"percentDone":83};
|
||||
stats['pl_PL'] = {"percentDone":98};
|
||||
stats['pt_PT'] = {"percentDone":88};
|
||||
stats['fa'] = {"percentDone":80};
|
||||
stats['pl_PL'] = {"percentDone":96};
|
||||
stats['pt_PT'] = {"percentDone":89};
|
||||
stats['pt_BR'] = {"percentDone":96};
|
||||
stats['ro'] = {"percentDone":77};
|
||||
stats['ro'] = {"percentDone":78};
|
||||
stats['sl_SI'] = {"percentDone":42};
|
||||
stats['sv'] = {"percentDone":70};
|
||||
stats['sv'] = {"percentDone":71};
|
||||
stats['th_TH'] = {"percentDone":52};
|
||||
stats['vi'] = {"percentDone":85};
|
||||
stats['tr_TR'] = {"percentDone":98};
|
||||
stats['tr_TR'] = {"percentDone":96};
|
||||
stats['el_GR'] = {"percentDone":96};
|
||||
stats['ru_RU'] = {"percentDone":95};
|
||||
stats['sr_RS'] = {"percentDone":71};
|
||||
stats['sr_RS'] = {"percentDone":72};
|
||||
stats['zh_CN'] = {"percentDone":96};
|
||||
stats['zh_TW'] = {"percentDone":95};
|
||||
stats['ja_JP'] = {"percentDone":98};
|
||||
stats['ko'] = {"percentDone":98};
|
||||
stats['ja_JP'] = {"percentDone":96};
|
||||
stats['ko'] = {"percentDone":86};
|
||||
module.exports = { locales: locales, stats: stats };
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -4,7 +4,7 @@
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, asyncTest, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync, mockDate, restoreDate } = require('test-utils.js');
|
||||
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, asyncTest, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const SearchEngine = require('lib/services/searchengine/SearchEngine');
|
||||
const Note = require('lib/models/Note');
|
||||
const ItemChange = require('lib/models/ItemChange');
|
||||
@@ -33,32 +33,16 @@ const calculateScore = (searchString, notes) => {
|
||||
const numTokens = notes.map(note => note.title.split(' ').length);
|
||||
const avgTokens = Math.round(numTokens.reduce((a, b) => a + b, 0) / notes.length);
|
||||
|
||||
const msSinceEpoch = Math.round(new Date().getTime());
|
||||
const msPerDay = 86400000;
|
||||
const weightForDaysSinceLastUpdate = (row) => {
|
||||
// BM25 weights typically range 0-10, and last updated date should weight similarly, though prioritizing recency logarithmically.
|
||||
// An alpha of 200 ensures matches in the last week will show up front (11.59) and often so for matches within 2 weeks (5.99),
|
||||
// but is much less of a factor at 30 days (2.84) or very little after 90 days (0.95), focusing mostly on content at that point.
|
||||
if (!row.user_updated_time) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const alpha = 200;
|
||||
const daysSinceLastUpdate = (msSinceEpoch - row.user_updated_time) / msPerDay;
|
||||
return alpha * Math.log(1 + 1 / Math.max(daysSinceLastUpdate, 0.5));
|
||||
};
|
||||
|
||||
let titleBM25WeightedByLastUpdate = new Array(notes.length).fill(-1);
|
||||
let titleBM25 = new Array(notes.length).fill(-1);
|
||||
if (avgTokens != 0) {
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
titleBM25WeightedByLastUpdate[i] = IDF(notes.length, notesWithWord) * ((freqTitle[i] * (K1 + 1)) / (freqTitle[i] + K1 * (1 - B + B * (numTokens[i] / avgTokens))));
|
||||
titleBM25WeightedByLastUpdate[i] += weightForDaysSinceLastUpdate(notes[i]);
|
||||
titleBM25[i] = IDF(notes.length, notesWithWord) * ((freqTitle[i] * (K1 + 1)) / (freqTitle[i] + K1 * (1 - B + B * (numTokens[i] / avgTokens))));
|
||||
}
|
||||
}
|
||||
|
||||
const scores = [];
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
if (freqTitle[i]) scores.push(titleBM25WeightedByLastUpdate[i]);
|
||||
if (freqTitle[i]) scores.push(titleBM25[i]);
|
||||
}
|
||||
|
||||
scores.sort().reverse();
|
||||
@@ -158,54 +142,33 @@ describe('services_SearchEngine', function() {
|
||||
expect(rows[1].id).toBe(n2.id);
|
||||
}));
|
||||
|
||||
it('should correctly weigh notes using BM25 and user_updated_time', asyncTest(async () => {
|
||||
await mockDate(2020, 9, 30, 50);
|
||||
it('should correctly weigh notes using BM25', asyncTest(async () => {
|
||||
|
||||
const noteData = [
|
||||
{
|
||||
title: 'abc test2 test2',
|
||||
updated_time: 1601425064756,
|
||||
user_updated_time: 1601425064756,
|
||||
created_time: 1601425064756,
|
||||
user_created_time: 1601425064756,
|
||||
},
|
||||
{
|
||||
title: 'foo foo',
|
||||
updated_time: 1601425064758,
|
||||
user_updated_time: 1601425064758,
|
||||
created_time: 1601425064758,
|
||||
user_created_time: 1601425064758,
|
||||
},
|
||||
{
|
||||
title: 'dead beef',
|
||||
updated_time: 1601425064760,
|
||||
user_updated_time: 1601425064760,
|
||||
created_time: 1601425064760,
|
||||
user_created_time: 1601425064760,
|
||||
},
|
||||
{
|
||||
title: 'test2 bar',
|
||||
updated_time: 1601425064761,
|
||||
user_updated_time: 1601425064761,
|
||||
created_time: 1601425064761,
|
||||
user_created_time: 1601425064761,
|
||||
},
|
||||
{
|
||||
title: 'blah blah abc',
|
||||
updated_time: 1601425064763,
|
||||
user_updated_time: 1601425064763,
|
||||
created_time: 1601425064763,
|
||||
user_created_time: 1601425064763,
|
||||
},
|
||||
];
|
||||
|
||||
const n0 = await Note.save(noteData[0], { autoTimestamp: false });
|
||||
const n1 = await Note.save(noteData[1], { autoTimestamp: false });
|
||||
const n2 = await Note.save(noteData[2], { autoTimestamp: false });
|
||||
const n3 = await Note.save(noteData[3], { autoTimestamp: false });
|
||||
const n4 = await Note.save(noteData[4], { autoTimestamp: false });
|
||||
restoreDate();
|
||||
const n0 = await Note.save(noteData[0]);
|
||||
const n1 = await Note.save(noteData[1]);
|
||||
const n2 = await Note.save(noteData[2]);
|
||||
const n3 = await Note.save(noteData[3]);
|
||||
const n4 = await Note.save(noteData[4]);
|
||||
|
||||
await engine.syncTables();
|
||||
await mockDate(2020, 9, 30, 50);
|
||||
|
||||
let searchString = 'abc';
|
||||
let scores = calculateScore(searchString, noteData);
|
||||
@@ -235,7 +198,6 @@ describe('services_SearchEngine', function() {
|
||||
// console.log(scores);
|
||||
|
||||
expect(rows[0].weight).toEqual(scores[0]);
|
||||
await restoreDate();
|
||||
}));
|
||||
|
||||
it('should tell where the results are found', asyncTest(async () => {
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import Api from 'lib/services/rest/Api';
|
||||
import shim from 'lib/shim';
|
||||
/* eslint-disable no-unused-vars */
|
||||
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { asyncTest, setupDatabaseAndSynchronizer, switchClient, checkThrowAsync } = require('test-utils.js');
|
||||
const Api = require('lib/services/rest/Api').default;
|
||||
const Folder = require('lib/models/Folder');
|
||||
const Resource = require('lib/models/Resource');
|
||||
const Note = require('lib/models/Note');
|
||||
const Tag = require('lib/models/Tag');
|
||||
const NoteTag = require('lib/models/NoteTag');
|
||||
const shim = require('lib/shim').default;
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
let api:Api = null;
|
||||
let api = null;
|
||||
|
||||
describe('services_rest_Api', function() {
|
||||
|
||||
@@ -25,23 +28,21 @@ describe('services_rest_Api', function() {
|
||||
|
||||
it('should ping', asyncTest(async () => {
|
||||
const response = await api.route('GET', 'ping');
|
||||
expect(response).toBe('JoplinClipperServer');
|
||||
}));
|
||||
|
||||
it('should handle Not Found errors', asyncTest(async () => {
|
||||
const hasThrown = await checkThrowAsync(async () => await api.route('GET', 'pong'));
|
||||
expect(hasThrown).toBe(true);
|
||||
}));
|
||||
|
||||
it('should get folders', asyncTest(async () => {
|
||||
await Folder.save({ title: 'mon carnet' });
|
||||
const f1 = await Folder.save({ title: 'mon carnet' });
|
||||
const response = await api.route('GET', 'folders');
|
||||
expect(response.length).toBe(1);
|
||||
}));
|
||||
|
||||
it('should update folders', asyncTest(async () => {
|
||||
const f1 = await Folder.save({ title: 'mon carnet' });
|
||||
await api.route('PUT', `folders/${f1.id}`, null, JSON.stringify({
|
||||
const response = await api.route('PUT', `folders/${f1.id}`, null, JSON.stringify({
|
||||
title: 'modifié',
|
||||
}));
|
||||
|
||||
@@ -83,8 +84,8 @@ describe('services_rest_Api', function() {
|
||||
const response2 = await api.route('GET', `folders/${f1.id}/notes`);
|
||||
expect(response2.length).toBe(0);
|
||||
|
||||
await Note.save({ title: 'un', parent_id: f1.id });
|
||||
await Note.save({ title: 'deux', parent_id: f1.id });
|
||||
const n1 = await Note.save({ title: 'un', parent_id: f1.id });
|
||||
const n2 = await Note.save({ title: 'deux', parent_id: f1.id });
|
||||
const response = await api.route('GET', `folders/${f1.id}/notes`);
|
||||
expect(response.length).toBe(2);
|
||||
}));
|
||||
@@ -99,7 +100,7 @@ describe('services_rest_Api', function() {
|
||||
const f1 = await Folder.save({ title: 'mon carnet' });
|
||||
const f2 = await Folder.save({ title: 'mon deuxième carnet' });
|
||||
const n1 = await Note.save({ title: 'un', parent_id: f1.id });
|
||||
await Note.save({ title: 'deux', parent_id: f1.id });
|
||||
const n2 = await Note.save({ title: 'deux', parent_id: f1.id });
|
||||
const n3 = await Note.save({ title: 'trois', parent_id: f2.id });
|
||||
|
||||
response = await api.route('GET', 'notes');
|
||||
@@ -133,41 +134,6 @@ describe('services_rest_Api', function() {
|
||||
expect(!!response.id).toBe(true);
|
||||
}));
|
||||
|
||||
it('should allow setting note properties', asyncTest(async () => {
|
||||
let response:any = null;
|
||||
const f = await Folder.save({ title: 'mon carnet' });
|
||||
|
||||
response = await api.route('POST', 'notes', null, JSON.stringify({
|
||||
title: 'testing',
|
||||
parent_id: f.id,
|
||||
latitude: '48.732071',
|
||||
longitude: '-3.458700',
|
||||
altitude: '21',
|
||||
}));
|
||||
|
||||
const noteId = response.id;
|
||||
|
||||
{
|
||||
const note = await Note.load(noteId);
|
||||
expect(note.latitude).toBe('48.73207100');
|
||||
expect(note.longitude).toBe('-3.45870000');
|
||||
expect(note.altitude).toBe('21.0000');
|
||||
}
|
||||
|
||||
await api.route('PUT', 'notes/' + noteId, null, JSON.stringify({
|
||||
latitude: '49',
|
||||
longitude: '-3',
|
||||
altitude: '22',
|
||||
}));
|
||||
|
||||
{
|
||||
const note = await Note.load(noteId);
|
||||
expect(note.latitude).toBe('49.00000000');
|
||||
expect(note.longitude).toBe('-3.00000000');
|
||||
expect(note.altitude).toBe('22.0000');
|
||||
}
|
||||
}));
|
||||
|
||||
it('should preserve user timestamps when creating notes', asyncTest(async () => {
|
||||
let response = null;
|
||||
const f = await Folder.save({ title: 'mon carnet' });
|
||||
@@ -255,9 +221,10 @@ describe('services_rest_Api', function() {
|
||||
}));
|
||||
|
||||
it('should delete resources', asyncTest(async () => {
|
||||
let response = null;
|
||||
const f = await Folder.save({ title: 'mon carnet' });
|
||||
|
||||
await api.route('POST', 'notes', null, JSON.stringify({
|
||||
response = await api.route('POST', 'notes', null, JSON.stringify({
|
||||
title: 'testing image',
|
||||
parent_id: f.id,
|
||||
image_data_url: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAIAAABLbSncAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAANZJREFUeNoAyAA3/wFwtO3K6gUB/vz2+Prw9fj/+/r+/wBZKAAExOgF4/MC9ff+MRH6Ui4E+/0Bqc/zutj6AgT+/Pz7+vv7++nu82c4DlMqCvLs8goA/gL8/fz09fb59vXa6vzZ6vjT5fbn6voD/fwC8vX4UiT9Zi//APHyAP8ACgUBAPv5APz7BPj2+DIaC2o3E+3o6ywaC5fT6gD6/QD9/QEVf9kD+/dcLQgJA/7v8vqfwOf18wA1IAIEVycAyt//v9XvAPv7APz8LhoIAPz9Ri4OAgwARgx4W/6fVeEAAAAASUVORK5CYII=',
|
||||
@@ -319,7 +286,7 @@ describe('services_rest_Api', function() {
|
||||
const tag = await Tag.save({ title: 'mon étiquette' });
|
||||
const note = await Note.save({ title: 'ma note' });
|
||||
|
||||
await api.route('POST', `tags/${tag.id}/notes`, null, JSON.stringify({
|
||||
const response = await api.route('POST', `tags/${tag.id}/notes`, null, JSON.stringify({
|
||||
id: note.id,
|
||||
}));
|
||||
|
||||
@@ -332,7 +299,7 @@ describe('services_rest_Api', function() {
|
||||
const note = await Note.save({ title: 'ma note' });
|
||||
await Tag.addNote(tag.id, note.id);
|
||||
|
||||
await api.route('DELETE', `tags/${tag.id}/notes/${note.id}`);
|
||||
const response = await api.route('DELETE', `tags/${tag.id}/notes/${note.id}`);
|
||||
|
||||
const noteIds = await Tag.noteIds(tag.id);
|
||||
expect(noteIds.length).toBe(0);
|
||||
@@ -634,16 +634,6 @@ function tempFilePath(ext) {
|
||||
return `${Setting.value('tempDir')}/${md5(Date.now() + Math.random())}.${ext}`;
|
||||
}
|
||||
|
||||
function mockDate(year, month, day, tick) {
|
||||
const fixedDate = new Date(2020, 0, 1);
|
||||
jasmine.clock().install();
|
||||
jasmine.clock().mockDate(fixedDate);
|
||||
}
|
||||
|
||||
function restoreDate() {
|
||||
jasmine.clock().uninstall();
|
||||
}
|
||||
|
||||
// Application for feature integration testing
|
||||
class TestApp extends BaseApplication {
|
||||
constructor(hasGui = true) {
|
||||
@@ -712,4 +702,4 @@ class TestApp extends BaseApplication {
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { synchronizerStart, syncTargetName, setSyncTargetName, syncDir, isNetworkSyncTarget, kvStore, expectThrow, logger, expectNotThrow, resourceService, resourceFetcher, tempFilePath, allSyncTargetItemsEncrypted, msleep, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync, checkThrow, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, asyncTest, currentClientId, id, ids, sortedIds, at, createNTestNotes, createNTestFolders, createNTestTags, mockDate, restoreDate, TestApp };
|
||||
module.exports = { synchronizerStart, syncTargetName, setSyncTargetName, syncDir, isNetworkSyncTarget, kvStore, expectThrow, logger, expectNotThrow, resourceService, resourceFetcher, tempFilePath, allSyncTargetItemsEncrypted, msleep, setupDatabase, revisionService, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync, checkThrow, encryptionService, loadEncryptionMasterKey, fileContentEqual, decryptionWorker, asyncTest, currentClientId, id, ids, sortedIds, at, createNTestNotes, createNTestFolders, createNTestTags, TestApp };
|
||||
|
||||
@@ -75,7 +75,6 @@ const globalCommands = [
|
||||
require('./commands/focusElement'),
|
||||
require('./commands/startExternalEditing'),
|
||||
require('./commands/stopExternalEditing'),
|
||||
require('./commands/toggleExternalEditing'),
|
||||
require('./commands/copyDevCommand'),
|
||||
require('lib/commands/synchronize'),
|
||||
require('lib/commands/historyBackward'),
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
import { CommandRuntime, CommandDeclaration } from '../lib/services/CommandService';
|
||||
import { _ } from 'lib/locale';
|
||||
import { AppState } from '../app';
|
||||
import CommandService from 'lib/services/CommandService';
|
||||
|
||||
interface Props {
|
||||
noteId: string
|
||||
noteIsBeingWatched: boolean
|
||||
}
|
||||
|
||||
export const declaration:CommandDeclaration = {
|
||||
name: 'toggleExternalEditing',
|
||||
label: () => _('Toggle external editing'),
|
||||
iconName: 'icon-share',
|
||||
};
|
||||
|
||||
export const runtime = ():CommandRuntime => {
|
||||
return {
|
||||
execute: async (props:Props) => {
|
||||
if (!props.noteId) return;
|
||||
|
||||
if (props.noteIsBeingWatched) {
|
||||
CommandService.instance().execute('stopExternalEditing', { noteId: props.noteId });
|
||||
} else {
|
||||
CommandService.instance().execute('startExternalEditing', { noteId: props.noteId });
|
||||
}
|
||||
},
|
||||
isEnabled: (props:Props) => {
|
||||
return !!props.noteId;
|
||||
},
|
||||
mapStateToProps: (state:AppState):Props => {
|
||||
const noteId = state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null;
|
||||
return {
|
||||
noteId: noteId,
|
||||
noteIsBeingWatched: noteId ? state.watchedNoteFiles.includes(noteId) : false,
|
||||
};
|
||||
},
|
||||
title: (props:Props) => {
|
||||
return props.noteIsBeingWatched ? _('Stop') : '';
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -65,7 +65,7 @@ const commandNames:string[] = [
|
||||
'toggleSidebar',
|
||||
'toggleNoteList',
|
||||
'toggleVisiblePanes',
|
||||
'toggleExternalEditing',
|
||||
'startExternalEditing',
|
||||
'setTags',
|
||||
'showNoteContentProperties',
|
||||
'copyDevCommand',
|
||||
@@ -564,7 +564,7 @@ function useMenu(props:Props) {
|
||||
note: {
|
||||
label: _('&Note'),
|
||||
submenu: [
|
||||
menuItemDic.toggleExternalEditing,
|
||||
menuItemDic.startExternalEditing,
|
||||
menuItemDic.setTags,
|
||||
separator(),
|
||||
menuItemDic.showNoteContentProperties,
|
||||
|
||||
@@ -34,7 +34,7 @@ const mapStateToProps = (state: AppState) => {
|
||||
const commandNames = [
|
||||
'historyBackward',
|
||||
'historyForward',
|
||||
'toggleExternalEditing',
|
||||
'startExternalEditing',
|
||||
'-',
|
||||
'textBold',
|
||||
'textItalic',
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'codemirror/addon/mode/multiplex';
|
||||
import 'codemirror/mode/stex/stex';
|
||||
import Setting from 'lib/models/Setting';
|
||||
|
||||
// Joplin markdown is a the same as markdown mode, but it has configured defaults
|
||||
// and support for katex math blocks
|
||||
@@ -11,7 +10,7 @@ export default function useJoplinMode(CodeMirror: any) {
|
||||
name: 'markdown',
|
||||
taskLists: true,
|
||||
strikethrough: true,
|
||||
emoji: Setting.value('markdown.plugin.emoji'),
|
||||
emoji: true,
|
||||
tokenTypeOverrides: {
|
||||
linkText: 'link-text',
|
||||
},
|
||||
@@ -34,7 +33,7 @@ export default function useJoplinMode(CodeMirror: any) {
|
||||
}
|
||||
|
||||
return {
|
||||
startState: function(): { outer: any, openCharacter: string, inner: any } {
|
||||
startState: function(): {outer: any, openCharacter: string, inner: any} {
|
||||
return {
|
||||
outer: CodeMirror.startState(markdownMode),
|
||||
openCharacter: '',
|
||||
|
||||
@@ -1112,7 +1112,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
|
||||
/>;
|
||||
}
|
||||
|
||||
const leftButtonCommandNames = ['historyBackward', 'historyForward', 'toggleExternalEditing'];
|
||||
const leftButtonCommandNames = ['historyBackward', 'historyForward', 'startExternalEditing'];
|
||||
|
||||
function renderLeftExtraToolbarButtons() {
|
||||
const buttons = [];
|
||||
|
||||
@@ -591,7 +591,7 @@ const mapStateToProps = (state: AppState) => {
|
||||
'historyBackward',
|
||||
'historyForward',
|
||||
'toggleEditors',
|
||||
'toggleExternalEditing',
|
||||
'startExternalEditing',
|
||||
]),
|
||||
setTagsToolbarButtonInfo: toolbarButtonUtils.commandsToToolbarButtons(state, [
|
||||
'setTags',
|
||||
|
||||
@@ -60,7 +60,7 @@ class ToolbarBaseComponent extends React.Component<Props, any> {
|
||||
toolbarButtonInfo={o}
|
||||
/>);
|
||||
} else if (itemType === 'button') {
|
||||
const target = ['historyForward', 'historyBackward', 'toggleExternalEditing'].includes(o.name) ? leftItemComps : centerItemComps;
|
||||
const target = ['historyForward', 'historyBackward', 'startExternalEditing'].includes(o.name) ? leftItemComps : centerItemComps;
|
||||
target.push(<ToolbarButton {...props} />);
|
||||
} else if (itemType === 'separator') {
|
||||
centerItemComps.push(<ToolbarSpace {...props} />);
|
||||
|
||||
@@ -29,8 +29,6 @@ export default class NoteListUtils {
|
||||
|
||||
const notes = noteIds.map(id => BaseModel.byId(props.notes, id));
|
||||
|
||||
const singleNoteId = noteIds.length === 1 ? noteIds[0] : null;
|
||||
|
||||
let hasEncrypted = false;
|
||||
for (let i = 0; i < notes.length; i++) {
|
||||
if (notes[i].encryption_applied) hasEncrypted = true;
|
||||
@@ -61,9 +59,14 @@ export default class NoteListUtils {
|
||||
})
|
||||
);
|
||||
|
||||
if (singleNoteId) {
|
||||
const cmd = props.watchedNoteFiles.includes(singleNoteId) ? 'stopExternalEditing' : 'startExternalEditing';
|
||||
menu.append(new MenuItem(menuUtils.commandToStatefulMenuItem(cmd, { noteId: singleNoteId })));
|
||||
if (props.watchedNoteFiles.indexOf(noteIds[0]) < 0) {
|
||||
menu.append(
|
||||
new MenuItem(menuUtils.commandToStatefulMenuItem('startExternalEditing', { noteId: noteIds[0] }))
|
||||
);
|
||||
} else {
|
||||
menu.append(
|
||||
new MenuItem(menuUtils.commandToStatefulMenuItem('stopExternalEditing', { noteId: noteIds[0] }))
|
||||
);
|
||||
}
|
||||
|
||||
if (noteIds.length <= 1) {
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user