Compare commits
530 Commits
android-v2
...
android-v2
Author | SHA1 | Date | |
---|---|---|---|
|
1f2ecd08eb | ||
|
d5eeb12ce7 | ||
|
e9e9986978 | ||
|
d44f212b95 | ||
|
47be6bcde3 | ||
|
ef256a82ab | ||
|
b4d56f42f7 | ||
|
c1598c1a59 | ||
|
3b6decfaf6 | ||
|
5494e8c3dc | ||
|
39059ae6bf | ||
|
5773f63664 | ||
|
5698c30d1a | ||
|
218d69cf38 | ||
|
00369fd613 | ||
|
ef86e01cd6 | ||
|
efd9f740ca | ||
|
36c121523a | ||
|
991c12025b | ||
|
f1d6945cd5 | ||
|
2ebebd1dfa | ||
|
d955ed464e | ||
|
b7695115da | ||
|
f274678ad7 | ||
|
5ff0309586 | ||
|
ffb6c25ce7 | ||
|
7237ae319a | ||
|
cdd5a911a9 | ||
|
66b53f013d | ||
|
4d9ffffe56 | ||
|
c08f7de6bb | ||
|
77789c0b17 | ||
|
be7056ff59 | ||
|
274a55a31f | ||
|
32977c4e3f | ||
|
075b1626bd | ||
|
fd6538fdc7 | ||
|
b897191cc8 | ||
|
31897581d3 | ||
|
6c1820edc0 | ||
|
cfe4c8c6f4 | ||
|
b83165f9e5 | ||
|
7706f9058b | ||
|
03222ba1b2 | ||
|
d4f49db342 | ||
|
40e1b0559e | ||
|
738f1decbb | ||
|
e5a364d052 | ||
|
357a3e2e7b | ||
|
af91fd99cc | ||
|
3855f60a0d | ||
|
079b379e7a | ||
|
e23e036677 | ||
|
b824ff5457 | ||
|
3669a1b5d6 | ||
|
b93f9aaf01 | ||
|
8679290206 | ||
|
4acec5c6c7 | ||
|
f1b03453a4 | ||
|
7972dd5556 | ||
|
4842500f0a | ||
|
84c7f28ec5 | ||
|
f3eea43d24 | ||
|
8babaddbcb | ||
|
13cdaabb17 | ||
|
a94aa21088 | ||
|
6116bed4e3 | ||
|
fabd0b4dda | ||
|
6b72f86e7b | ||
|
02cf546124 | ||
|
eecb012d64 | ||
|
04e9b40769 | ||
|
efdbaeb397 | ||
|
46425b920c | ||
|
f5be43c2ac | ||
|
080541a2fe | ||
|
7dc638edf4 | ||
|
3b686194d8 | ||
|
5c2640f88f | ||
|
eca0f92dff | ||
|
260fa6c038 | ||
|
8ec6bc9138 | ||
|
93fa92369b | ||
|
bc6c5ab7a7 | ||
|
1826625e4f | ||
|
20b8fb2719 | ||
|
f813e71b29 | ||
|
02422a6e31 | ||
|
69a34e87f3 | ||
|
cbeaa16b61 | ||
|
05917ac142 | ||
|
0c8de68b80 | ||
|
44d93d52d3 | ||
|
073bec9e8c | ||
|
e6a8c2bea5 | ||
|
81c316cd2c | ||
|
659c851960 | ||
|
572701d9a0 | ||
|
66ef37bd4e | ||
|
9ddf75604d | ||
|
3ed7e1d7e8 | ||
|
b2b412105a | ||
|
60a3c4f65e | ||
|
9645414c17 | ||
|
af0136ef39 | ||
|
b76586c4fd | ||
|
376e4ebde0 | ||
|
1439b8787f | ||
|
b8854a99be | ||
|
6cf02173dc | ||
|
4d8a53d8c9 | ||
|
7f43718e1d | ||
|
690ce637b1 | ||
|
4d023e679e | ||
|
6e220a978f | ||
|
39757cd90e | ||
|
5ccbbea757 | ||
|
309222c082 | ||
|
50f5fe2c91 | ||
|
eacae83182 | ||
|
403d770b1d | ||
|
a481bf1b53 | ||
|
0d32570c9e | ||
|
f017e99b02 | ||
|
a89d64d435 | ||
|
3a27086534 | ||
|
413c1e41b5 | ||
|
8b879464b8 | ||
|
97c9bbc1fe | ||
|
e5bebef7b2 | ||
|
73752c4b3f | ||
|
dcf7c9838d | ||
|
f325e7694b | ||
|
75d204c9ca | ||
|
cf4008951d | ||
|
d67818d096 | ||
|
6aaea8ad4f | ||
|
de41278096 | ||
|
f01ab70907 | ||
|
bbdb221a67 | ||
|
7d053f8c79 | ||
|
fcad0bf3ca | ||
|
58f929f6b5 | ||
|
943198c56e | ||
|
2112ad4004 | ||
|
5995dc81f3 | ||
|
104e752634 | ||
|
fc335cd15d | ||
|
45923ba0d3 | ||
|
8fefa99d81 | ||
|
85d652cd67 | ||
|
88e41e9c7d | ||
|
26750488d0 | ||
|
a0f582b2b9 | ||
|
917b53bec2 | ||
|
e44a93422a | ||
|
e115ef4259 | ||
|
bcec699124 | ||
|
d23c728a1a | ||
|
0a2d507dec | ||
|
0c08617606 | ||
|
29fba45c33 | ||
|
1071a455b6 | ||
|
57e4b36fd7 | ||
|
f08fa92294 | ||
|
3a8d87d292 | ||
|
53302c9e90 | ||
|
28a24d8c03 | ||
|
3e52411bc4 | ||
|
1548ea18e1 | ||
|
f8cd1ba8e5 | ||
|
d18a4be31f | ||
|
c56f270ed6 | ||
|
2bca3d1032 | ||
|
9f81d69c5e | ||
|
815419260d | ||
|
6729a3d51f | ||
|
6d8ce280dd | ||
|
9e5b455065 | ||
|
09cbac3019 | ||
|
5354ad3934 | ||
|
7754048b80 | ||
|
ffeeff260f | ||
|
71ea74d273 | ||
|
3a744c79ae | ||
|
d9ba27a1ec | ||
|
0a3540049c | ||
|
ab50ca9bbd | ||
|
0bee793ab8 | ||
|
89fc5e19d9 | ||
|
6a3bf51084 | ||
|
df1e298c84 | ||
|
b9c706324b | ||
|
ab7e2de1d9 | ||
|
7ef8753b94 | ||
|
d48a5efa03 | ||
|
0804b62ffb | ||
|
37995b9ec7 | ||
|
7a3e6fde7f | ||
|
bd4291462e | ||
|
489d6778db | ||
|
538a1413d9 | ||
|
3c9b755045 | ||
|
fd7b345efa | ||
|
c96468149a | ||
|
d6d4897e1c | ||
|
e07e248fea | ||
|
b561460307 | ||
|
f6d1a27f51 | ||
|
80a1500634 | ||
|
bcb578c933 | ||
|
75ad454971 | ||
|
e90b7f2d81 | ||
|
ffca11ca8a | ||
|
bd98951d32 | ||
|
9106fb82f3 | ||
|
5c6e17bc89 | ||
|
538e9e9b4e | ||
|
d871b3c7d6 | ||
|
99c6c9b411 | ||
|
3eca4ada5a | ||
|
19431abc73 | ||
|
3e299f1ab9 | ||
|
42873d3829 | ||
|
4983327f90 | ||
|
fddbe6cf6c | ||
|
57f00c612d | ||
|
235288e903 | ||
|
38be744c3e | ||
|
2384ec8792 | ||
|
85f7caa0eb | ||
|
ad0f0414c4 | ||
|
437320f90c | ||
|
c1db7182ac | ||
|
aa4af69afc | ||
|
21a39af97b | ||
|
0812cc5944 | ||
|
1fb5d2c6c5 | ||
|
1b9901d232 | ||
|
f15d2793cc | ||
|
ad4d71dbe1 | ||
|
4bee6ffc90 | ||
|
01f63b3d97 | ||
|
b19b590efc | ||
|
6b7577f94d | ||
|
4d09b14522 | ||
|
9f1e95324d | ||
|
f98314346d | ||
|
fa659b615a | ||
|
e4aafa7edb | ||
|
811c40b074 | ||
|
405c528ef0 | ||
|
a176216374 | ||
|
8d0b090f66 | ||
|
337c5ed61c | ||
|
b32d39860b | ||
|
a091608f72 | ||
|
9686ee7833 | ||
|
3b55dcac65 | ||
|
d524d11d8a | ||
|
9c080ec631 | ||
|
da11476fd7 | ||
|
d157b9cfc7 | ||
|
bc8c61748a | ||
|
3ad727889b | ||
|
8df128bb7a | ||
|
5a4568f4db | ||
|
170295919a | ||
|
2262cbfdfd | ||
|
43e40bcf5a | ||
|
e60595f0de | ||
|
527a7c115d | ||
|
c22f910500 | ||
|
667b7969ff | ||
|
1bbf065142 | ||
|
606cad3c3a | ||
|
c9612dc8b8 | ||
|
6ee30e22a6 | ||
|
a6536e1ef9 | ||
|
92cf5abd08 | ||
|
d1e545ac2c | ||
|
ec6567c68d | ||
|
3b236307f7 | ||
|
14ac54bf3d | ||
|
e53b796523 | ||
|
9d189b2b34 | ||
|
84545cf26d | ||
|
c427a6d0a5 | ||
|
42dc6e1ea6 | ||
|
431b95ff7b | ||
|
041fb731a6 | ||
|
5289f80394 | ||
|
6c12ce0e04 | ||
|
46982c7d64 | ||
|
359448eb69 | ||
|
32bb256cca | ||
|
219585bbcf | ||
|
1a9dbcbd1d | ||
|
6a9848ebe7 | ||
|
9e73d3590b | ||
|
08c9a25182 | ||
|
5b9a45dc1d | ||
|
9dd2fb9674 | ||
|
234b5c8363 | ||
|
1dc7ec3701 | ||
|
72773caf58 | ||
|
094249d074 | ||
|
c324f17453 | ||
|
25a31b0689 | ||
|
f2995dd196 | ||
|
ca575162f7 | ||
|
f0ade02435 | ||
|
b13c02017a | ||
|
716c8c1ce4 | ||
|
30a49b84ad | ||
|
b8e4150bfd | ||
|
ed0edcb36c | ||
|
e93046e8e2 | ||
|
18a0ca0881 | ||
|
21dbc800d5 | ||
|
5c1eda3392 | ||
|
1139317788 | ||
|
a24ccb8da9 | ||
|
0aaa396315 | ||
|
f9dc19e1c4 | ||
|
1839724d7c | ||
|
a5aeb3a2f8 | ||
|
d5d57aa360 | ||
|
9aa5df7790 | ||
|
a7697465a8 | ||
|
aeb7e5ce47 | ||
|
42cef1e918 | ||
|
b832930512 | ||
|
057ac550bd | ||
|
3a14b76a61 | ||
|
e1a8c76598 | ||
|
b62e6552cd | ||
|
ca6e50e80c | ||
|
ad8947a85d | ||
|
fb562210b2 | ||
|
34940d1c4f | ||
|
7fa1459dc3 | ||
|
625689dbb1 | ||
|
dc976047d2 | ||
|
a014e830e7 | ||
|
1e828bfdca | ||
|
60309ec6a3 | ||
|
aabba090b1 | ||
|
6ae903ef4d | ||
|
dd86940c6b | ||
|
7d7b7ed6f3 | ||
|
8de904cd3c | ||
|
c706b8dd2f | ||
|
d55d4d42e5 | ||
|
bbfeffec69 | ||
|
3c471dc120 | ||
|
be0fa69b3b | ||
|
bf1bdf0951 | ||
|
a1a10a6c55 | ||
|
02e8307093 | ||
|
285a3a211e | ||
|
80cb1471fc | ||
|
a7a194f835 | ||
|
41a0b3359a | ||
|
1f70357d37 | ||
|
107f2e128e | ||
|
e1cd8d9b85 | ||
|
293f621e46 | ||
|
c5b551bbcb | ||
|
ac776eabc1 | ||
|
05c17fbfac | ||
|
6b9de394ca | ||
|
bef9a29581 | ||
|
1546aad7e9 | ||
|
fe47fef261 | ||
|
288f4ab43b | ||
|
294cbaea4d | ||
|
f5fc1f2f22 | ||
|
c90865c4d2 | ||
|
322641ccd6 | ||
|
2656666ed8 | ||
|
2d673902a4 | ||
|
631c41a1ff | ||
|
7841c99c02 | ||
|
ac75d8f6ac | ||
|
200ff617dc | ||
|
bbdc18f371 | ||
|
0d35b64f9a | ||
|
765c4482d6 | ||
|
0a0e31a37c | ||
|
0c1f4031b4 | ||
|
9ed022458b | ||
|
ba5f0bc6e3 | ||
|
793e8f6c0f | ||
|
6182ce521d | ||
|
544c50663a | ||
|
53aa9e2b42 | ||
|
884260189c | ||
|
2f9464f21f | ||
|
c6e993b04a | ||
|
89eb012b25 | ||
|
1e2aa4e2b5 | ||
|
0019bb8d6b | ||
|
e629a4d325 | ||
|
049c769d37 | ||
|
47aed8742a | ||
|
8aad67ccfe | ||
|
af7cbcbca7 | ||
|
88a91314af | ||
|
9873c2d756 | ||
|
46b68cf461 | ||
|
6b96b1f355 | ||
|
dc819700bb | ||
|
1d1d5fea06 | ||
|
c3afc0ede7 | ||
|
2092110a6e | ||
|
fc940e9a7c | ||
|
8718310dd0 | ||
|
1b527f2bbe | ||
|
4da217bc2f | ||
|
d3abd4ebf2 | ||
|
38851edf86 | ||
|
05efb765d6 | ||
|
28dc4a6abd | ||
|
2f7b56f96f | ||
|
fdaa3735fb | ||
|
7dfaea12f7 | ||
|
18199b27d9 | ||
|
a7c52082bb | ||
|
3b5357e0c1 | ||
|
10dd4e45ed | ||
|
1963835309 | ||
|
46ec0c1381 | ||
|
bb84ae4d68 | ||
|
b3ff53c0da | ||
|
acd7bfd9f5 | ||
|
1dff50d080 | ||
|
af40970d09 | ||
|
07535a494e | ||
|
907422cefa | ||
|
f643baea25 | ||
|
55a4f33982 | ||
|
df6700959a | ||
|
fde8235f3e | ||
|
f6ba56d966 | ||
|
4a5312823b | ||
|
31a27b0e1c | ||
|
984ad868e8 | ||
|
9b657eeda2 | ||
|
6f3ad4b3b0 | ||
|
56f06fae3c | ||
|
c22d884357 | ||
|
35dc22197d | ||
|
70d56ca0be | ||
|
bca09b9476 | ||
|
b82bf16505 | ||
|
2f254d81cd | ||
|
b14ce03e5b | ||
|
90b04cbd37 | ||
|
5ae866ea85 | ||
|
b450ab9f5a | ||
|
138bc8144b | ||
|
c9831833c4 | ||
|
2813f93c18 | ||
|
27bec674a0 | ||
|
ff79ca8781 | ||
|
34a1342db6 | ||
|
e252986b98 | ||
|
3537c3e5f9 | ||
|
fdc86f94c4 | ||
|
dc5dc94ed5 | ||
|
f7682d3da3 | ||
|
8e2975d23d | ||
|
ce85489166 | ||
|
13f5738090 | ||
|
cce2ae7401 | ||
|
c9b49a50c8 | ||
|
c419c43622 | ||
|
cc5ecfba2b | ||
|
a98d5feff6 | ||
|
7aa4feffd4 | ||
|
0b46a744f1 | ||
|
4d5efff2af | ||
|
4b2aaa6836 | ||
|
4e27600f9c | ||
|
6d4394a88d | ||
|
aad9c803ba | ||
|
5418cc82ca | ||
|
79eb33a7f4 | ||
|
cdef433f38 | ||
|
b29b50df0b | ||
|
91c3986f88 | ||
|
29e4a335fb | ||
|
4e942afba6 | ||
|
3bee0a142b | ||
|
9a9fdef512 | ||
|
4888b4bd14 | ||
|
bba3af17b7 | ||
|
b695acf4ed | ||
|
3367b52b53 | ||
|
c31622f286 | ||
|
488e469e33 | ||
|
fa7d48a3bd | ||
|
0cd2fd660d | ||
|
6bb52d5ad6 | ||
|
c2d5fc13d7 | ||
|
b1e5896d4c | ||
|
cc42b7bc38 | ||
|
4bdb3d0b7e | ||
|
6601547b81 | ||
|
7e29804e68 | ||
|
257a24166e | ||
|
8b3c9e81a7 | ||
|
c65e051e1f | ||
|
a91c8ffbf3 | ||
|
0530c74924 | ||
|
2feed88c51 | ||
|
2017e9d7e9 | ||
|
afe6cb9454 | ||
|
690e22b39a | ||
|
8a0aa40707 | ||
|
1f40a14084 | ||
|
6b9a270225 | ||
|
0bc55bff7f | ||
|
d9215b044f | ||
|
2f9384f891 | ||
|
a172b1380e | ||
|
0d4c074e84 | ||
|
9eff7e6060 | ||
|
1edb345e85 |
1582
.eslintignore
32
.eslintrc.js
@@ -77,6 +77,7 @@ module.exports = {
|
||||
'no-array-constructor': ['error'],
|
||||
'radix': ['error'],
|
||||
'eqeqeq': ['error', 'always'],
|
||||
'no-console': ['error', { 'allow': ['warn', 'error'] }],
|
||||
|
||||
// Warn only for now because fixing everything would take too much
|
||||
// refactoring, but new code should try to stick to it.
|
||||
@@ -90,7 +91,12 @@ module.exports = {
|
||||
// Disable because of this: https://github.com/facebook/react/issues/16265
|
||||
// "react-hooks/exhaustive-deps": "warn",
|
||||
|
||||
'jest/require-top-level-describe': ['error', { 'maxNumberOfTopLevelDescribes': 1 }],
|
||||
'jest/no-identical-title': ['error'],
|
||||
'jest/prefer-lowercase-title': ['error', { 'ignoreTopLevelDescribe': true }],
|
||||
|
||||
'promise/prefer-await-to-then': 'error',
|
||||
'no-unneeded-ternary': 'error',
|
||||
|
||||
// -------------------------------
|
||||
// Formatting
|
||||
@@ -109,6 +115,7 @@ module.exports = {
|
||||
'exports': 'always-multiline',
|
||||
'functions': 'never',
|
||||
}],
|
||||
'comma-spacing': ['error', { 'before': false, 'after': true }],
|
||||
'no-trailing-spaces': 'error',
|
||||
'linebreak-style': ['error', 'unix'],
|
||||
'prefer-template': ['error'],
|
||||
@@ -134,6 +141,14 @@ module.exports = {
|
||||
'spaced-comment': ['error', 'always'],
|
||||
'keyword-spacing': ['error', { 'before': true, 'after': true }],
|
||||
'no-multi-spaces': ['error'],
|
||||
|
||||
// Regarding the keyword blacklist:
|
||||
// - err: We generally avoid using too many abbreviations, so it should
|
||||
// be "error", not "err"
|
||||
// - notebook: In code, it should always be "folder" (not "notebook").
|
||||
// In user-facing text, it should be "notebook".
|
||||
'id-denylist': ['error', 'err', 'notebook', 'notebooks'],
|
||||
'prefer-arrow-callback': ['error'],
|
||||
},
|
||||
'plugins': [
|
||||
'react',
|
||||
@@ -144,8 +159,19 @@ module.exports = {
|
||||
// 'react-hooks',
|
||||
'import',
|
||||
'promise',
|
||||
'jest',
|
||||
],
|
||||
'overrides': [
|
||||
{
|
||||
'files': [
|
||||
'packages/tools/**',
|
||||
'packages/app-mobile/tools/**',
|
||||
'packages/app-desktop/tools/**',
|
||||
],
|
||||
'rules': {
|
||||
'no-console': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
// enable the rule specifically for TypeScript files
|
||||
'files': ['*.ts', '*.tsx'],
|
||||
@@ -154,10 +180,9 @@ module.exports = {
|
||||
'project': './tsconfig.eslint.json',
|
||||
},
|
||||
'rules': {
|
||||
// Warn only because it would make it difficult to convert JS classes to TypeScript, unless we
|
||||
// make everything public which is not great. New code however should specify member accessibility.
|
||||
'@typescript-eslint/explicit-member-accessibility': ['warn'],
|
||||
'@typescript-eslint/explicit-member-accessibility': ['error'],
|
||||
'@typescript-eslint/type-annotation-spacing': ['error', { 'before': false, 'after': true }],
|
||||
'@typescript-eslint/no-inferrable-types': ['error', { 'ignoreParameters': true, 'ignoreProperties': true }],
|
||||
'@typescript-eslint/comma-dangle': ['error', {
|
||||
'arrays': 'always-multiline',
|
||||
'objects': 'always-multiline',
|
||||
@@ -168,6 +193,7 @@ module.exports = {
|
||||
'tuples': 'always-multiline',
|
||||
'functions': 'never',
|
||||
}],
|
||||
'@typescript-eslint/object-curly-spacing': ['error', 'always'],
|
||||
'@typescript-eslint/semi': ['error', 'always'],
|
||||
'@typescript-eslint/member-delimiter-style': ['error', {
|
||||
'multiline': {
|
||||
|
2
.github/workflows/build-android.yml
vendored
@@ -6,6 +6,7 @@ on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
pre_job:
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||
@@ -16,6 +17,7 @@ jobs:
|
||||
concurrent_skipping: 'same_content_newer'
|
||||
|
||||
BuildAndroidDebug:
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
needs: pre_job
|
||||
if: needs.pre_job.outputs.should_skip != 'true'
|
||||
runs-on: ubuntu-latest
|
||||
|
3
.github/workflows/cla.yml
vendored
@@ -7,12 +7,13 @@ on:
|
||||
|
||||
jobs:
|
||||
CLAAssistant:
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "CLA Assistant"
|
||||
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
|
||||
# Beta Release
|
||||
uses: contributor-assistant/github-action@v2.2.1
|
||||
uses: contributor-assistant/github-action@v2.3.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# the below token should have repo scope and must be manually added by you in the repository's secret
|
||||
|
1
.github/workflows/close-stale-issues.yml
vendored
@@ -6,6 +6,7 @@ permissions:
|
||||
issues: write
|
||||
jobs:
|
||||
ProcessStaleIssues:
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v4
|
||||
|
10
.github/workflows/github-actions-main.yml
vendored
@@ -2,6 +2,7 @@ name: Joplin Continuous Integration
|
||||
on: [push, pull_request]
|
||||
jobs:
|
||||
pre_job:
|
||||
if: github.repository == 'laurent22/joplin'
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
should_skip: ${{ steps.skip_check.outputs.should_skip }}
|
||||
@@ -14,7 +15,7 @@ jobs:
|
||||
Main:
|
||||
needs: pre_job
|
||||
# We always process server or desktop release tags, because they also publish the release
|
||||
if: needs.pre_job.outputs.should_skip != 'true' || startsWith(github.ref, 'refs/tags/server-v') || startsWith(github.ref, 'refs/tags/v')
|
||||
if: github.repository == 'laurent22/joplin' && (needs.pre_job.outputs.should_skip != 'true' || startsWith(github.ref, 'refs/tags/server-v') || startsWith(github.ref, 'refs/tags/v'))
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
@@ -70,7 +71,9 @@ jobs:
|
||||
- uses: olegtarasov/get-tag@v2.1
|
||||
- uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: '18'
|
||||
# We need to pin the version to 18.15, because 18.16+ fails with this error:
|
||||
# https://github.com/facebook/react-native/issues/36440
|
||||
node-version: '18.15.0'
|
||||
|
||||
- name: Install Yarn
|
||||
run: |
|
||||
@@ -92,6 +95,7 @@ jobs:
|
||||
APPLE_ASC_PROVIDER: ${{ secrets.APPLE_ASC_PROVIDER }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.APPLE_CSC_KEY_PASSWORD }}
|
||||
CSC_LINK: ${{ secrets.APPLE_CSC_LINK }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN }}
|
||||
@@ -129,7 +133,7 @@ jobs:
|
||||
|
||||
ServerDockerImage:
|
||||
needs: pre_job
|
||||
if: needs.pre_job.outputs.should_skip != 'true'
|
||||
if: github.repository == 'laurent22/joplin' && needs.pre_job.outputs.should_skip != 'true'
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
|
1584
.gitignore
vendored
@@ -13,7 +13,10 @@
|
||||
"@joplin/turndown",
|
||||
"@joplin/turndown-plugin-gfm",
|
||||
"@joplin/tools",
|
||||
"@joplin/react-native-saf-x"
|
||||
"@joplin/react-native-saf-x",
|
||||
"@joplin/react-native-alarm-notification",
|
||||
"@joplin/react-native-vosk",
|
||||
"@joplin/utils"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
20
.yarn/patches/react-native-camera-npm-4.2.1-24b2600a7e.patch
Normal file
@@ -0,0 +1,20 @@
|
||||
diff --git a/src/RNCamera.js b/src/RNCamera.js
|
||||
index b7a271ad64771c0f654dbd5fe3c0d9e0d2e2c4ef..1182a40ace081a32fbaefe2bc4a499b79c2e7dac 100644
|
||||
--- a/src/RNCamera.js
|
||||
+++ b/src/RNCamera.js
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
findNodeHandle,
|
||||
Platform,
|
||||
NativeModules,
|
||||
- ViewPropTypes,
|
||||
requireNativeComponent,
|
||||
View,
|
||||
ActivityIndicator,
|
||||
@@ -14,6 +13,7 @@ import {
|
||||
PermissionsAndroid,
|
||||
} from 'react-native';
|
||||
|
||||
+import ViewPropTypes from 'deprecated-react-native-prop-types';
|
||||
import type { FaceFeature } from './FaceDetector';
|
||||
|
||||
const Rationale = PropTypes.shape({
|
127
.yarn/patches/react-native-vosk-npm-0.1.12-76b1caaae8.patch
Normal file
@@ -0,0 +1,127 @@
|
||||
diff --git a/android/build.gradle b/android/build.gradle
|
||||
index 6afcbbf0cc8ca2d69dd78077d61e59a90b2136bb..9f8d72b4ec5b2b3d290975d6a255917c95300854 100644
|
||||
--- a/android/build.gradle
|
||||
+++ b/android/build.gradle
|
||||
@@ -67,19 +67,19 @@ repositories {
|
||||
}
|
||||
|
||||
// Generate UUIDs for each models contained in android/src/main/assets/
|
||||
-tasks.register('genUUID') {
|
||||
- doLast {
|
||||
- fileTree(dir: "$rootDir/app/src/main/assets", exclude: ['*/*']).visit { fileDetails ->
|
||||
- if (fileDetails.directory) {
|
||||
- def odir = file("$rootDir/app/src/main/assets/$fileDetails.relativePath")
|
||||
- def ofile = file("$odir/uuid")
|
||||
- mkdir odir
|
||||
- ofile.text = UUID.randomUUID().toString()
|
||||
- }
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-preBuild.dependsOn genUUID
|
||||
+// tasks.register('genUUID') {
|
||||
+// doLast {
|
||||
+// fileTree(dir: "$rootDir/app/src/main/assets", exclude: ['*/*']).visit { fileDetails ->
|
||||
+// if (fileDetails.directory) {
|
||||
+// def odir = file("$rootDir/app/src/main/assets/$fileDetails.relativePath")
|
||||
+// def ofile = file("$odir/uuid")
|
||||
+// mkdir odir
|
||||
+// ofile.text = UUID.randomUUID().toString()
|
||||
+// }
|
||||
+// }
|
||||
+// }
|
||||
+// }
|
||||
+// preBuild.dependsOn genUUID
|
||||
|
||||
def kotlin_version = getExtOrDefault('kotlinVersion')
|
||||
|
||||
diff --git a/android/src/main/java/com/reactnativevosk/VoskModule.kt b/android/src/main/java/com/reactnativevosk/VoskModule.kt
|
||||
index 0e2b6595b1b2cf1ee01c6c64239c4b0ea37fce19..f3da440bc2863a59db6d2d1691c54d8d4870cb3f 100644
|
||||
--- a/android/src/main/java/com/reactnativevosk/VoskModule.kt
|
||||
+++ b/android/src/main/java/com/reactnativevosk/VoskModule.kt
|
||||
@@ -19,13 +19,25 @@ class VoskModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
||||
return "Vosk"
|
||||
}
|
||||
|
||||
+ @ReactMethod
|
||||
+ fun addListener(type: String?) {
|
||||
+ // Keep: Required for RN built in Event Emitter Calls.
|
||||
+ }
|
||||
+
|
||||
+ @ReactMethod
|
||||
+ fun removeListeners(type: Int?) {
|
||||
+ // Keep: Required for RN built in Event Emitter Calls.
|
||||
+ }
|
||||
+
|
||||
override fun onResult(hypothesis: String) {
|
||||
// Get text data from string object
|
||||
val text = getHypothesisText(hypothesis)
|
||||
|
||||
// Stop recording if data found
|
||||
if (text != null && text.isNotEmpty()) {
|
||||
- cleanRecognizer();
|
||||
+ // Don't auto-stop the recogniser - we want to do that when the user
|
||||
+ // presses on "stop" only.
|
||||
+ // cleanRecognizer();
|
||||
sendEvent("onResult", text)
|
||||
}
|
||||
}
|
||||
@@ -153,6 +165,25 @@ class VoskModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaMo
|
||||
cleanRecognizer();
|
||||
}
|
||||
|
||||
+ @ReactMethod
|
||||
+ fun stopOnly() {
|
||||
+ if (speechService != null) {
|
||||
+ speechService!!.stop()
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ @ReactMethod
|
||||
+ fun cleanup() {
|
||||
+ if (speechService != null) {
|
||||
+ speechService!!.shutdown();
|
||||
+ speechService = null
|
||||
+ }
|
||||
+ if (recognizer != null) {
|
||||
+ recognizer!!.close();
|
||||
+ recognizer = null;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
@ReactMethod
|
||||
fun unload() {
|
||||
cleanRecognizer();
|
||||
diff --git a/lib/typescript/index.d.ts b/lib/typescript/index.d.ts
|
||||
index 441e41cc402cca3a60b34978ef4fea976076259c..a173acebb4b314402550442ad471e0f7c706e3c4 100644
|
||||
--- a/lib/typescript/index.d.ts
|
||||
+++ b/lib/typescript/index.d.ts
|
||||
@@ -10,6 +10,8 @@ export default class Vosk {
|
||||
currentRegisteredEvents: EmitterSubscription[];
|
||||
start: (grammar?: string[] | null) => Promise<String>;
|
||||
stop: () => void;
|
||||
+ stopOnly: () => void;
|
||||
+ cleanup: () => void;
|
||||
unload: () => void;
|
||||
onResult: (onResult: (e: VoskEvent) => void) => EventSubscription;
|
||||
onFinalResult: (onFinalResult: (e: VoskEvent) => void) => EventSubscription;
|
||||
diff --git a/src/index.tsx b/src/index.tsx
|
||||
index d9f90c921d89b1b4d85e145443ed3376546a368a..29e4068dbd7500828a73145bd25497a52c9bf638 100644
|
||||
--- a/src/index.tsx
|
||||
+++ b/src/index.tsx
|
||||
@@ -69,6 +69,15 @@ export default class Vosk {
|
||||
VoskModule.stop();
|
||||
};
|
||||
|
||||
+ stopOnly = () => {
|
||||
+ VoskModule.stopOnly();
|
||||
+ };
|
||||
+
|
||||
+ cleanup = () => {
|
||||
+ this.cleanListeners();
|
||||
+ VoskModule.cleanup();
|
||||
+ };
|
||||
+
|
||||
unload = () => {
|
||||
this.cleanListeners();
|
||||
VoskModule.unload();
|
30
.yarn/patches/rn-fetch-blob-npm-0.12.0-cf02e3c544.patch
Normal file
@@ -0,0 +1,30 @@
|
||||
diff --git a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java
|
||||
index a8abd71833879201e3438b2fa51d712a311c4551..ffe9c2c6dfa5c703ba76b65d94d5dd6784102c19 100644
|
||||
--- a/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java
|
||||
+++ b/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java
|
||||
@@ -591,7 +591,7 @@ public class RNFetchBlobReq extends BroadcastReceiver implements Runnable {
|
||||
// ignored.printStackTrace();
|
||||
}
|
||||
|
||||
- RNFetchBlobFileResp rnFetchBlobFileResp = (RNFetchBlobFileResp) responseBody;
|
||||
+ RNFetchBlobFileResp rnFetchBlobFileResp = new RNFetchBlobFileResp(responseBody);
|
||||
|
||||
if(rnFetchBlobFileResp != null && !rnFetchBlobFileResp.isDownloadComplete()){
|
||||
callback.invoke("Download interrupted.", null);
|
||||
diff --git a/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java b/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java
|
||||
index 2470eef612308c15a89dfea5a1f16937469be29f..965f8becc195965907699182c764ec9e51811450 100644
|
||||
--- a/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java
|
||||
+++ b/android/src/main/java/com/RNFetchBlob/Response/RNFetchBlobFileResp.java
|
||||
@@ -35,6 +35,12 @@ public class RNFetchBlobFileResp extends ResponseBody {
|
||||
FileOutputStream ofStream;
|
||||
boolean isEndMarkerReceived;
|
||||
|
||||
+ // ref: https://github.com/joltup/rn-fetch-blob/issues/490#issuecomment-990899440
|
||||
+ public RNFetchBlobFileResp(ResponseBody body) {
|
||||
+ super();
|
||||
+ this.originalBody = body;
|
||||
+ }
|
||||
+
|
||||
public RNFetchBlobFileResp(ReactApplicationContext ctx, String taskId, ResponseBody body, String path, boolean overwrite) throws IOException {
|
||||
super();
|
||||
this.rctContext = ctx;
|
136
Assets/ImageSources/DocSources/Application.drawio
Normal file
@@ -0,0 +1,136 @@
|
||||
<mxfile host="Electron" modified="2023-04-29T09:42:39.598Z" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.2.1 Chrome/112.0.5615.87 Electron/24.1.2 Safari/537.36" etag="apmX4QvXCQymGu7gtKJn" version="21.2.1" type="device">
|
||||
<diagram name="Page-1" id="5f0bae14-7c28-e335-631c-24af17079c00">
|
||||
<mxGraphModel dx="1244" dy="759" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" background="none" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-1" value="Front end" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="465" y="120" width="170" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-4" value="Service" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="320" y="280" width="120" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-5" value="Service" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="490" y="280" width="120" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-6" value="Service" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="670" y="280" width="120" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-10" value="Model" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="320" y="430" width="120" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-11" value="Model" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="490" y="430" width="120" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-12" value="Model" style="ellipse;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="670" y="430" width="120" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-14" value="SQLite database" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;" vertex="1" parent="1">
|
||||
<mxGeometry x="490" y="580" width="122.5" height="100" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-19" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="93vzSs2z7RmF_nCAYhdf-10" target="93vzSs2z7RmF_nCAYhdf-4">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="700" y="440" as="sourcePoint" />
|
||||
<mxPoint x="750" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-20" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="93vzSs2z7RmF_nCAYhdf-11" target="93vzSs2z7RmF_nCAYhdf-4">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="700" y="440" as="sourcePoint" />
|
||||
<mxPoint x="750" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-21" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="93vzSs2z7RmF_nCAYhdf-12" target="93vzSs2z7RmF_nCAYhdf-4">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="700" y="440" as="sourcePoint" />
|
||||
<mxPoint x="750" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-22" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="93vzSs2z7RmF_nCAYhdf-10" target="93vzSs2z7RmF_nCAYhdf-5">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="700" y="440" as="sourcePoint" />
|
||||
<mxPoint x="750" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-23" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="93vzSs2z7RmF_nCAYhdf-11" target="93vzSs2z7RmF_nCAYhdf-5">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="700" y="440" as="sourcePoint" />
|
||||
<mxPoint x="750" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-24" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="93vzSs2z7RmF_nCAYhdf-12" target="93vzSs2z7RmF_nCAYhdf-5">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="700" y="440" as="sourcePoint" />
|
||||
<mxPoint x="750" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-25" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="93vzSs2z7RmF_nCAYhdf-10" target="93vzSs2z7RmF_nCAYhdf-6">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="700" y="440" as="sourcePoint" />
|
||||
<mxPoint x="750" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-26" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="93vzSs2z7RmF_nCAYhdf-11" target="93vzSs2z7RmF_nCAYhdf-6">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="700" y="440" as="sourcePoint" />
|
||||
<mxPoint x="750" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-27" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="93vzSs2z7RmF_nCAYhdf-12" target="93vzSs2z7RmF_nCAYhdf-6">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="700" y="440" as="sourcePoint" />
|
||||
<mxPoint x="750" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-28" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="93vzSs2z7RmF_nCAYhdf-10" target="93vzSs2z7RmF_nCAYhdf-14">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="700" y="440" as="sourcePoint" />
|
||||
<mxPoint x="750" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-29" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="93vzSs2z7RmF_nCAYhdf-14" target="93vzSs2z7RmF_nCAYhdf-11">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="700" y="440" as="sourcePoint" />
|
||||
<mxPoint x="750" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-30" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="93vzSs2z7RmF_nCAYhdf-14" target="93vzSs2z7RmF_nCAYhdf-12">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="700" y="440" as="sourcePoint" />
|
||||
<mxPoint x="750" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-31" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="93vzSs2z7RmF_nCAYhdf-4" target="93vzSs2z7RmF_nCAYhdf-1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="620" y="440" as="sourcePoint" />
|
||||
<mxPoint x="670" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-32" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="93vzSs2z7RmF_nCAYhdf-5" target="93vzSs2z7RmF_nCAYhdf-1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="620" y="440" as="sourcePoint" />
|
||||
<mxPoint x="670" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-33" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="1" source="93vzSs2z7RmF_nCAYhdf-6" target="93vzSs2z7RmF_nCAYhdf-1">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="620" y="440" as="sourcePoint" />
|
||||
<mxPoint x="670" y="390" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-37" value="BACKEND" style="swimlane;whiteSpace=wrap;html=1;swimlaneFillColor=none;shadow=0;" vertex="1" parent="1">
|
||||
<mxGeometry x="280" y="230" width="560" height="480" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-38" value="JSON config file" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" vertex="1" parent="1">
|
||||
<mxGeometry x="910" y="420" width="80" height="100" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="93vzSs2z7RmF_nCAYhdf-39" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;" edge="1" parent="1" source="93vzSs2z7RmF_nCAYhdf-37" target="93vzSs2z7RmF_nCAYhdf-38">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="830" y="480" as="sourcePoint" />
|
||||
<mxPoint x="800" y="580" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
70
Assets/ImageSources/DocSources/JoplinServer.drawio
Normal file
@@ -0,0 +1,70 @@
|
||||
<mxfile host="Electron" modified="2023-04-29T10:24:42.580Z" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.2.1 Chrome/112.0.5615.87 Electron/24.1.2 Safari/537.36" etag="kcPEKHJGaBvNGFhEOF2g" version="21.2.1" type="device">
|
||||
<diagram name="Page-1" id="5f0bae14-7c28-e335-631c-24af17079c00">
|
||||
<mxGraphModel dx="1306" dy="797" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1100" pageHeight="850" background="none" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="t8PL5avYcYxuv0YEq-6K-7" value="Joplin Server" style="swimlane;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="320" y="40" width="630" height="465" as="geometry">
|
||||
<mxRectangle x="350" y="300" width="120" height="30" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="t8PL5avYcYxuv0YEq-6K-2" value="Server application" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="t8PL5avYcYxuv0YEq-6K-7">
|
||||
<mxGeometry x="270" y="92.5" width="170" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="t8PL5avYcYxuv0YEq-6K-3" value="PostgreSQL" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;" vertex="1" parent="t8PL5avYcYxuv0YEq-6K-7">
|
||||
<mxGeometry x="190" y="262.5" width="140" height="110" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="t8PL5avYcYxuv0YEq-6K-5" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="t8PL5avYcYxuv0YEq-6K-7" source="t8PL5avYcYxuv0YEq-6K-3" target="t8PL5avYcYxuv0YEq-6K-2">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="280" y="232.5" as="sourcePoint" />
|
||||
<mxPoint x="330" y="182.5" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="t8PL5avYcYxuv0YEq-6K-11" value="Note metadata,<br>user accounts, etc." style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="t8PL5avYcYxuv0YEq-6K-5">
|
||||
<mxGeometry x="0.0586" y="1" relative="1" as="geometry">
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="t8PL5avYcYxuv0YEq-6K-4" value="AWS S3" style="shape=cube;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;darkOpacity=0.05;darkOpacity2=0.1;" vertex="1" parent="t8PL5avYcYxuv0YEq-6K-7">
|
||||
<mxGeometry x="430" y="277.5" width="120" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="t8PL5avYcYxuv0YEq-6K-6" value="Note and attachment<br>content" style="endArrow=classic;startArrow=classic;html=1;rounded=0;exitX=0;exitY=0;exitDx=50;exitDy=0;exitPerimeter=0;" edge="1" parent="t8PL5avYcYxuv0YEq-6K-7" source="t8PL5avYcYxuv0YEq-6K-4" target="t8PL5avYcYxuv0YEq-6K-2">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="390" y="242.5" as="sourcePoint" />
|
||||
<mxPoint x="440" y="192.5" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="t8PL5avYcYxuv0YEq-6K-12" value="Reverse proxy" style="whiteSpace=wrap;html=1;aspect=fixed;" vertex="1" parent="t8PL5avYcYxuv0YEq-6K-7">
|
||||
<mxGeometry x="70" y="97.5" width="75" height="75" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="t8PL5avYcYxuv0YEq-6K-13" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;" edge="1" parent="t8PL5avYcYxuv0YEq-6K-7" source="t8PL5avYcYxuv0YEq-6K-12" target="t8PL5avYcYxuv0YEq-6K-2">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="180" y="160" as="sourcePoint" />
|
||||
<mxPoint x="230" y="110" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="t8PL5avYcYxuv0YEq-6K-18" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;" edge="1" parent="t8PL5avYcYxuv0YEq-6K-7" source="t8PL5avYcYxuv0YEq-6K-14" target="t8PL5avYcYxuv0YEq-6K-2">
|
||||
<mxGeometry relative="1" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="t8PL5avYcYxuv0YEq-6K-14" value="Env file (config)" style="shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;darkOpacity=0.05;" vertex="1" parent="t8PL5avYcYxuv0YEq-6K-7">
|
||||
<mxGeometry x="540" y="90" width="60" height="85" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="t8PL5avYcYxuv0YEq-6K-8" value="Joplin Application (mobile, desktop, ...)" style="whiteSpace=wrap;html=1;aspect=fixed;" vertex="1" parent="1">
|
||||
<mxGeometry x="40" y="110" width="130" height="130" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="t8PL5avYcYxuv0YEq-6K-9" value="" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;" edge="1" parent="1" source="t8PL5avYcYxuv0YEq-6K-8" target="t8PL5avYcYxuv0YEq-6K-12">
|
||||
<mxGeometry width="50" height="50" relative="1" as="geometry">
|
||||
<mxPoint x="130" y="270" as="sourcePoint" />
|
||||
<mxPoint x="310" y="230" as="targetPoint" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="t8PL5avYcYxuv0YEq-6K-10" value="HTTP REST requests" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" vertex="1" connectable="0" parent="t8PL5avYcYxuv0YEq-6K-9">
|
||||
<mxGeometry x="0.0435" relative="1" as="geometry">
|
||||
<mxPoint as="offset" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
@@ -657,6 +657,16 @@ footer .bottom-links-row p {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.language-switcher {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.language-switcher > button {
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
color: #0557ba;
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
WHAT'S NEW PAGE
|
||||
*****************************************************************/
|
||||
@@ -728,6 +738,16 @@ footer .bottom-links-row p {
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
LARGE VIEW
|
||||
*****************************************************************/
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
#nav-section a {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
MEDIUM VIEW
|
||||
- Make menu bar elements smaller and closer to each others
|
||||
@@ -770,6 +790,7 @@ footer .bottom-links-row p {
|
||||
|
||||
#menu-mobile .social-links .social-link-mastodon,
|
||||
#menu-mobile .social-links .social-link-reddit,
|
||||
#menu-mobile .social-links .social-link-linkedin,
|
||||
#menu-mobile .social-links .social-link-patreon {
|
||||
display: none;
|
||||
}
|
||||
@@ -937,6 +958,41 @@ footer .bottom-links-row p {
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
MORE NARROW VIEW
|
||||
eg for Galaxy S9
|
||||
*****************************************************************/
|
||||
|
||||
@media (max-width: 580px) {
|
||||
|
||||
#nav-section .plans-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
MORE NARROW VIEW
|
||||
eg for Galaxy S9
|
||||
*****************************************************************/
|
||||
|
||||
@media (max-width: 400px) {
|
||||
|
||||
#nav-section .navbar-mobile-content a.sponsor-button .sponsor-button-label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#nav-section .navbar-mobile-content a.sponsor-button {
|
||||
padding: 2px 6px;
|
||||
margin-right: 0.2em;
|
||||
}
|
||||
|
||||
#nav-section a {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
VERY NARROW VIEW
|
||||
eg for Galaxy Fold
|
||||
@@ -958,6 +1014,15 @@ footer .bottom-links-row p {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
div.navbar-mobile-content a.sponsor-button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#nav-section .button-link {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*****************************************************************
|
||||
|
BIN
Assets/WebsiteAssets/images/architecture/Application.png
Normal file
After Width: | Height: | Size: 138 KiB |
BIN
Assets/WebsiteAssets/images/architecture/JoplinServer.png
Normal file
After Width: | Height: | Size: 110 KiB |
After Width: | Height: | Size: 10 KiB |
BIN
Assets/WebsiteAssets/images/news/20230116-ga-raw-log-colored.png
Normal file
After Width: | Height: | Size: 963 KiB |
BIN
Assets/WebsiteAssets/images/news/20230116-ga-raw-log.png
Normal file
After Width: | Height: | Size: 295 KiB |
BIN
Assets/WebsiteAssets/images/news/20230202-jdll.jpg
Normal file
After Width: | Height: | Size: 238 KiB |
BIN
Assets/WebsiteAssets/images/plans/CarbonNeutral.png
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
Assets/WebsiteAssets/images/sponsors/Grundstueckspreise.png
Normal file
After Width: | Height: | Size: 9.9 KiB |
7
Assets/WebsiteAssets/js/bootstrap5.0.2.bundle.min.js
vendored
Normal file
5016
Assets/WebsiteAssets/js/bootstrap5.0.2.js
vendored
@@ -1,4 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Joplin]]></title><description><![CDATA[Joplin, the open source note-taking application]]></description><link>https://joplinapp.org</link><generator>RSS for Node</generator><lastBuildDate>Wed, 21 Dec 2022 00:00:00 GMT</lastBuildDate><atom:link href="https://joplinapp.org/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Wed, 21 Dec 2022 00:00:00 GMT</pubDate><item><title><![CDATA[Joplin is switching to the GNU Affero General Public License v3 (AGPL-3.0)]]></title><description><![CDATA[<p>As was <a href="https://discourse.joplinapp.org/t/rfc-switch-to-agpl-license-for-joplin-server/16529">discussed last year</a>, Joplin is switching to the GNU Affero General Public License v3 (AGPL-3.0) for the desktop, mobile and CLI applications, as well as the web clipper.</p>
|
||||
<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Joplin]]></title><description><![CDATA[Joplin, the open source note-taking application]]></description><link>https://joplinapp.org</link><generator>RSS for Node</generator><lastBuildDate>Thu, 02 Mar 2023 00:00:00 GMT</lastBuildDate><atom:link href="https://joplinapp.org/rss.xml" rel="self" type="application/rss+xml"/><pubDate>Thu, 02 Mar 2023 00:00:00 GMT</pubDate><item><title><![CDATA[Joplin will participate in JdLL 2023!]]></title><description><![CDATA[<p>On 1 and 2 April 2023, we will have a stand for Joplin at the <a href="https://www.jdll.org/">Journées du Logiciel Libre</a> in Lyon, France. The JdLL has been taking place in Lyon for 24 years and is a popular open source conference in France. We had a stand in 2020 and 2021 but that was cancelled due to Covid, so this year is a first for Joplin!</p>
|
||||
<p>Admission is free, so don't hesitate to come and meet us, exchange ideas and learn more about Joplin!</p>
|
||||
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20230202-jdll.jpg" alt="Joplin at JdLL 2023"></p>
|
||||
]]></description><link>https://joplinapp.org/news/20230302-jdll-2023/</link><guid isPermaLink="false">20230302-jdll-2023</guid><pubDate>Thu, 02 Mar 2023 00:00:00 GMT</pubDate><twitter-text></twitter-text></item><item><title><![CDATA[Introducing the "GitHub Actions Raw Log Viewer" extension for Chrome]]></title><description><![CDATA[<p>If you've ever used GitHub Actions, you will find that they provide by default a nice coloured output for the log. It looks good and it's even interactive! (You can click to collapse/expand blocks of text) But unfortunately it doesn't scale to large workflows, like we have for Joplin - the log can freeze and it will take forever to search for something. Indeed searching is done in "real time"... which mostly means it will freeze for a minute or two for each letter you type in the search box. Not great.</p>
|
||||
<p>Thankfully GitHub provides an alternative access: the raw logs. This is much better because they will open as plain text, without any styling or JS magic, which means you can use the browser native search and it will be fast.</p>
|
||||
<p>But now the problem is that raw logs look like this:</p>
|
||||
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20230116-ga-raw-log.png" alt="Raw log without extension"></p>
|
||||
<p>While it's not impossible to read, all colours that would display nicely in a terminal are gone and replaced by <a href="https://en.wikipedia.org/wiki/ANSI_escape_code">ANSI codes</a>. You can find what you need in there but it's not particularly easy.</p>
|
||||
<p>This is where the new <strong>GitHub Action Raw Log Viewer</strong> extension for Chrome can help. It will parse your raw log and convert the ANSI codes to proper colours. This results in a much more readable rendering:</p>
|
||||
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20230116-ga-raw-log-colored.png" alt="Raw log with extension"></p>
|
||||
<p>The extension is fast even for very large logs and it's of course easy to search for text since it simply works with your browser built-in search.</p>
|
||||
<p>The extension is open source, with the code available here: <a href="https://github.com/laurent22/github-actions-logs-extension">https://github.com/laurent22/github-actions-logs-extension</a></p>
|
||||
<p>And to install it, follow this link:</p>
|
||||
<p><a href="https://chrome.google.com/webstore/detail/github-action-raw-log-vie/lgejlnoopmcdglhfjblaeldbcfnmjddf"><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20230116-extension-get-it-now.png" alt="Download GitHub Action Raw Log Viewer extension"></a></p>
|
||||
]]></description><link>https://joplinapp.org/news/20230116-github-actions-log-viewer/</link><guid isPermaLink="false">20230116-github-actions-log-viewer</guid><pubDate>Mon, 16 Jan 2023 00:00:00 GMT</pubDate><twitter-text>Introducing the "GitHub Action Raw Log Viewer" extension for Chrome</twitter-text></item><item><title><![CDATA[Joplin is switching to the GNU Affero General Public License v3 (AGPL-3.0)]]></title><description><![CDATA[<p>As was <a href="https://discourse.joplinapp.org/t/rfc-switch-to-agpl-license-for-joplin-server/16529">discussed last year</a>, Joplin is switching to the GNU Affero General Public License v3 (AGPL-3.0) for the desktop, mobile and CLI applications, as well as the web clipper.</p>
|
||||
<p>Any open source or commercial fork of Joplin will have to license any changes they make under AGPL, and share these changes back with the community. This is the main reason we switch to this license. It allows us to continue releasing the project as open source while ensuring that those who benefit commercially (or not) from it share back their changes.</p>
|
||||
<h2>What is the GPL license?<a name="what-is-the-gpl-license" href="#what-is-the-gpl-license" class="heading-anchor">🔗</a></h2>
|
||||
<p>The AGPL license is based on the GPL license. This is what tldr;Legal has to say about the GPL license:</p>
|
||||
@@ -283,14 +297,4 @@
|
||||
<p>This release also includes about 30 various bug fixes and improvements.</p>
|
||||
<p>A notable one is a fix for GotoAnything, which recently wasn't working on first try.</p>
|
||||
<p>The plugin screen has also been improved so that search works even when GitHub is down or blocked, as it is in China in particular.</p>
|
||||
]]></description><link>https://joplinapp.org/news/20210929-144036/</link><guid isPermaLink="false">20210929-144036</guid><pubDate>Wed, 29 Sep 2021 14:40:36 GMT</pubDate><twitter-text></twitter-text></item><item><title><![CDATA[Introducing recommended plugins in the next Joplin version]]></title><description><![CDATA[<p>A common request from new users is how to know which plugin is safe to install or not. In fact probably all of them are safe but as a new user that's not necessarily easy to know. So to help with this, the next version of Joplin will support recommended plugins - those will be plugins that meet our standards of quality and performance, and they will be indicated by a small crown tag inside the plugin box. Recommended plugins will also appear on top when searching.</p>
|
||||
<p><img src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/news/20210901-113415_0.png" alt=""></p>
|
||||
<p>For now, since we don't have a review process, the recommended plugins are those developed by the Joplin team and frequent contributors, because we know those are safe to use.</p>
|
||||
<p>Later we might have a review process and add more recommended plugins. That being said, in the meantime even if a plugin is not marked as recommended, there's a good chance it is still safe and have good performance too. Often you can search for it on the forum and if it's active with many users commenting, you're most likely good to go.</p>
|
||||
<p>But if there's any doubt, the recommended tag is a good way to be sure.</p>
|
||||
]]></description><link>https://joplinapp.org/news/20210901-113415/</link><guid isPermaLink="false">20210901-113415</guid><pubDate>Wed, 01 Sep 2021 11:34:15 GMT</pubDate><twitter-text></twitter-text></item><item><title><![CDATA[Joplin Cloud is officially production ready!]]></title><description><![CDATA[<p><a href="https://joplinapp.org/plans/">Joplin Cloud</a> has been out of beta for a few weeks now and since then it has been quietly running without any troubles. There is no known bugs and the service is running smoothly so it's now safe to say that it is production ready!</p>
|
||||
<p>As a reminder, Joplin Cloud is meant to provide a more seamless Joplin experience - if you want to quickly get started, it's as easy as downloading the app and getting a Joplin Cloud account. Besides improved sync performance, that will give you the ability to collaborate on notebooks with others, as well as publishing and sharing notes.</p>
|
||||
<p>Of course Joplin still supports other sync options such as Nextcloud, Dropbox and OneDrive or AWS S3. You can also self host using Joplin Server. The advantage of Joplin Cloud being that you don't need to maintain a server yourself - for a small fee you'll get that taken care of.</p>
|
||||
<p>Additionally, subscribing to Joplin Cloud is a great way to support the project as a whole, including the open source applications. Such support is needed in the long term to provide bug and security fixes, add new features, and provide support.</p>
|
||||
<p>At some level it is also an experiment, to see if such a service is financially viable and can allow me to work full time on the project. This is certainly something I would like, and perhaps Joplin Cloud combined with your donations will allow that.</p>
|
||||
]]></description><link>https://joplinapp.org/news/20210831-154354/</link><guid isPermaLink="false">20210831-154354</guid><pubDate>Tue, 31 Aug 2021 15:43:54 GMT</pubDate><twitter-text></twitter-text></item></channel></rss>
|
||||
]]></description><link>https://joplinapp.org/news/20210929-144036/</link><guid isPermaLink="false">20210929-144036</guid><pubDate>Wed, 29 Sep 2021 14:40:36 GMT</pubDate><twitter-text></twitter-text></item></channel></rss>
|
@@ -421,7 +421,7 @@
|
||||
</div>
|
||||
|
||||
<script
|
||||
src="{{jsBaseUrl}}/bootstrap5.0.2.min.js"
|
||||
src="{{jsBaseUrl}}/bootstrap5.0.2.bundle.min.js"
|
||||
rel="preload"
|
||||
as="script"
|
||||
></script>
|
||||
|
@@ -85,6 +85,11 @@ https://github.com/laurent22/joplin/blob/dev/{{{sourceMarkdownFile}}}
|
||||
{{> footer}}
|
||||
</div>
|
||||
|
||||
<script
|
||||
src="{{jsBaseUrl}}/bootstrap5.0.2.bundle.min.js"
|
||||
rel="preload"
|
||||
as="script"
|
||||
></script>
|
||||
<script src="{{{assetUrls.js.script}}}"></script>
|
||||
|
||||
{{> analytics}}
|
||||
|
@@ -17,6 +17,21 @@
|
||||
<a href="{{baseUrl}}/help/" class="fw500">Help</a>
|
||||
<a href="{{forumUrl}}" class="fw500">Forum</a>
|
||||
<a href="{{baseUrl}}/cn/" class="fw500">中文</a>
|
||||
|
||||
<!--
|
||||
<div class="dropdown language-switcher">
|
||||
<button class="fw500" type="button" id="dropdownMenuButton1" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Language
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton1">
|
||||
<li><a class="dropdown-item" href="#">Action</a></li>
|
||||
<li><a class="dropdown-item" href="#">Another action</a></li>
|
||||
<li><a class="dropdown-item" href="#">Something else here</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
-->
|
||||
|
||||
|
||||
{{#showJoplinCloudLinks}}
|
||||
{{> joplinCloudButton}}
|
||||
{{/showJoplinCloudLinks}}
|
||||
@@ -24,7 +39,8 @@
|
||||
</div>
|
||||
<div class="col-9 text-right d-block d-md-none navbar-mobile-content">
|
||||
{{> twitterLink}}
|
||||
<a href="{{baseUrl}}/cn/" class="fw500">中文</a>
|
||||
<a href="{{baseUrl}}/cn/" class="fw500 chinese-page-link">中文</a>
|
||||
{{> joplinCloudButton}}
|
||||
{{> supportButton}}
|
||||
|
||||
<span class="pointer"
|
||||
|
@@ -4,6 +4,7 @@
|
||||
<a class="social-link-mastodon" href="https://mastodon.social/@joplinapp" title="Joplin Mastodon feed"><i class="fab fa-mastodon"></i></a>
|
||||
<a class="social-link-patreon" href="https://www.patreon.com/joplin" title="Joplin Patreon"><i class="fab fa-patreon"></i></a>
|
||||
<a class="social-link-discord" href="https://discord.gg/VSj7AFHvpq" title="Joplin Discord chat"><i class="fab fa-discord"></i></a>
|
||||
<a class="social-link-linkedin" href="https://www.linkedin.com/company/joplin" title="Joplin LinkedIn Feed"><i class="fab fa-linkedin"></i></a>
|
||||
<a class="social-link-reddit" href="https://www.reddit.com/r/joplinapp/" title="Joplin Subreddit"><i class="fab fa-reddit"></i></a>
|
||||
<a class="social-link-github" href="https://github.com/laurent22/joplin/" title="Joplin GitHub repository"><i class="fab fa-github"></i></a>
|
||||
</div>
|
||||
|
@@ -1 +1 @@
|
||||
<a href="https://twitter.com/joplinapp" title="Joplin Twitter feed" class="fw500"><i class="fab fa-twitter"></i></a>
|
||||
<a href="https://twitter.com/joplinapp" title="Joplin Twitter feed" class="fw500 twitter-link"><i class="fab fa-twitter"></i></a>
|
@@ -130,7 +130,11 @@
|
||||
});
|
||||
|
||||
setupBetaHandling(urlQuery);
|
||||
applyPeriod('yearly');
|
||||
if (urlQuery.get('period') === 'monthly') {
|
||||
// Nothing - this is the default
|
||||
} else {
|
||||
applyPeriod('yearly');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</div>
|
||||
|
@@ -30,6 +30,7 @@ COPY packages/fork-uslug ./packages/fork-uslug
|
||||
COPY packages/htmlpack ./packages/htmlpack
|
||||
COPY packages/renderer ./packages/renderer
|
||||
COPY packages/tools ./packages/tools
|
||||
COPY packages/utils ./packages/utils
|
||||
COPY packages/lib ./packages/lib
|
||||
COPY packages/server ./packages/server
|
||||
|
||||
|
96
README.md
@@ -1,5 +1,5 @@
|
||||
<!-- DONATELINKS -->
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=E8JMYD2LQ8MMA&lc=GB&item_name=Joplin+Development¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted) [](https://github.com/sponsors/laurent22/) [](https://www.patreon.com/joplin) [](https://joplinapp.org/donate/#donations)
|
||||
[](https://www.paypal.com/donate/?business=E8JMYD2LQ8MMA&no_recurring=0&item_name=I+rely+on+donations+to+maintain+and+improve+the+Joplin+open+source+project.+Thank+you+for+your+help+-+it+makes+a+difference%21¤cy_code=EUR) [](https://github.com/sponsors/laurent22/) [](https://www.patreon.com/joplin) [](https://joplinapp.org/donate/#donations)
|
||||
<!-- DONATELINKS -->
|
||||
|
||||
<img width="64" src="https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/LinuxIcons/256x256.png" align="left" /> **Joplin** is a free, open source note taking and to-do application, which can handle a large number of notes organised into notebooks. The notes are searchable, can be copied, tagged and modified either from the applications directly or from your own text editor. The notes are in [Markdown format](#markdown).
|
||||
@@ -36,7 +36,7 @@ Linux | <a href='https://github.com/laurent22/joplin/releases/download/v2.9.17/J
|
||||
|
||||
Operating System | Download | Alt. 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="40px" src='https://raw.githubusercontent.com/laurent22/joplin/dev/Assets/WebsiteAssets/images/BadgeAndroid.png'/></a> | or download the APK file: [64-bit](https://github.com/laurent22/joplin-android/releases/download/android-v2.8.1/joplin-v2.8.1.apk) [32-bit](https://github.com/laurent22/joplin-android/releases/download/android-v2.8.1/joplin-v2.8.1-32bit.apk)
|
||||
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/dev/Assets/WebsiteAssets/images/BadgeAndroid.png'/></a> | or download the APK file: [64-bit](https://github.com/laurent22/joplin-android/releases/download/android-v2.9.8/joplin-v2.9.8.apk) [32-bit](https://github.com/laurent22/joplin-android/releases/download/android-v2.9.8/joplin-v2.9.8-32bit.apk)
|
||||
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/dev/Assets/WebsiteAssets/images/BadgeIOS.png'/></a> | -
|
||||
|
||||
## Terminal application
|
||||
@@ -64,7 +64,7 @@ A community maintained list of these distributions can be found here: [Unofficia
|
||||
# Sponsors
|
||||
|
||||
<!-- SPONSORS-ORG -->
|
||||
<a href="https://seirei.ne.jp"><img title="Serei Network" width="256" src="https://joplinapp.org/images/sponsors/SeireiNetwork.png"/></a> <a href="https://usrigging.com/"><img title="U.S. Ringing Supply" width="256" src="https://joplinapp.org/images/sponsors/RingingSupply.svg"/></a> <a href="https://www.hosting.de/nextcloud/?mtm_campaign=managed-nextcloud&mtm_kwd=joplinapp&mtm_source=joplinapp-github&mtm_medium=banner"><img title="Hosting.de" width="256" src="https://joplinapp.org/images/sponsors/HostingDe.png"/></a> <a href="https://residence-greece.com/"><img title="Greece Golden Visa" width="256" src="https://joplinapp.org/images/sponsors/ResidenceGreece.jpg"/></a>
|
||||
<a href="https://seirei.ne.jp"><img title="Serei Network" width="256" src="https://joplinapp.org/images/sponsors/SeireiNetwork.png"/></a> <a href="https://www.hosting.de/nextcloud/?mtm_campaign=managed-nextcloud&mtm_kwd=joplinapp&mtm_source=joplinapp-webseite&mtm_medium=banner"><img title="Hosting.de" width="256" src="https://joplinapp.org/images/sponsors/HostingDe.png"/></a> <a href="https://residence-greece.com/"><img title="Greece Golden Visa" width="256" src="https://joplinapp.org/images/sponsors/ResidenceGreece.jpg"/></a> <a href="https://grundstueckspreise.info/"><img title="SP Software GmbH" width="256" src="https://joplinapp.org/images/sponsors/Grundstueckspreise.png"/></a>
|
||||
<!-- SPONSORS-ORG -->
|
||||
|
||||
* * *
|
||||
@@ -72,14 +72,11 @@ A community maintained list of these distributions can be found here: [Unofficia
|
||||
<!-- SPONSORS-GITHUB -->
|
||||
| | | | |
|
||||
| :---: | :---: | :---: | :---: |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/215668?s=96&v=4"/></br>[avanderberg](https://github.com/avanderberg) | <img width="50" src="https://avatars2.githubusercontent.com/u/3061769?s=96&v=4"/></br>[c-nagy](https://github.com/c-nagy) | <img width="50" src="https://avatars2.githubusercontent.com/u/70780798?s=96&v=4"/></br>[cabottech](https://github.com/cabottech) | <img width="50" src="https://avatars2.githubusercontent.com/u/67130?s=96&v=4"/></br>[chr15m](https://github.com/chr15m) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/4862947?s=96&v=4"/></br>[chrootlogin](https://github.com/chrootlogin) | <img width="50" src="https://avatars2.githubusercontent.com/u/82579431?s=96&v=4"/></br>[clmntsl](https://github.com/clmntsl) | <img width="50" src="https://avatars2.githubusercontent.com/u/808091?s=96&v=4"/></br>[cuongtransc](https://github.com/cuongtransc) | <img width="50" src="https://avatars2.githubusercontent.com/u/1307332?s=96&v=4"/></br>[dbrandonjohnson](https://github.com/dbrandonjohnson) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/1439535?s=96&v=4"/></br>[fbloise](https://github.com/fbloise) | <img width="50" src="https://avatars2.githubusercontent.com/u/49439044?s=96&v=4"/></br>[fourstepper](https://github.com/fourstepper) | <img width="50" src="https://avatars2.githubusercontent.com/u/38898566?s=96&v=4"/></br>[h4sh5](https://github.com/h4sh5) | <img width="50" src="https://avatars2.githubusercontent.com/u/3266447?s=96&v=4"/></br>[iamwillbar](https://github.com/iamwillbar) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/37297218?s=96&v=4"/></br>[Jesssullivan](https://github.com/Jesssullivan) | <img width="50" src="https://avatars2.githubusercontent.com/u/1310474?s=96&v=4"/></br>[jknowles](https://github.com/jknowles) | <img width="50" src="https://avatars2.githubusercontent.com/u/1248504?s=96&v=4"/></br>[joesfer](https://github.com/joesfer) | <img width="50" src="https://avatars2.githubusercontent.com/u/5588131?s=96&v=4"/></br>[kianenigma](https://github.com/kianenigma) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/24908652?s=96&v=4"/></br>[konishi-t](https://github.com/konishi-t) | <img width="50" src="https://avatars2.githubusercontent.com/u/42319182?s=96&v=4"/></br>[marcdw1289](https://github.com/marcdw1289) | <img width="50" src="https://avatars2.githubusercontent.com/u/1788010?s=96&v=4"/></br>[maxtruxa](https://github.com/maxtruxa) | <img width="50" src="https://avatars2.githubusercontent.com/u/29300939?s=96&v=4"/></br>[mcejp](https://github.com/mcejp) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/1168659?s=96&v=4"/></br>[nicholashead](https://github.com/nicholashead) | <img width="50" src="https://avatars2.githubusercontent.com/u/5782817?s=96&v=4"/></br>[piccobit](https://github.com/piccobit) | <img width="50" src="https://avatars2.githubusercontent.com/u/77214738?s=96&v=4"/></br>[Polymathic-Company](https://github.com/Polymathic-Company) | <img width="50" src="https://avatars2.githubusercontent.com/u/47742?s=96&v=4"/></br>[ravenscroftj](https://github.com/ravenscroftj) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/327998?s=96&v=4"/></br>[sif](https://github.com/sif) | <img width="50" src="https://avatars2.githubusercontent.com/u/54626606?s=96&v=4"/></br>[skyrunner15](https://github.com/skyrunner15) | <img width="50" src="https://avatars2.githubusercontent.com/u/765564?s=96&v=4"/></br>[taskcruncher](https://github.com/taskcruncher) | <img width="50" src="https://avatars2.githubusercontent.com/u/73081837?s=96&v=4"/></br>[thismarty](https://github.com/thismarty) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/15859362?s=96&v=4"/></br>[thomasbroussard](https://github.com/thomasbroussard) | | | |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/215668?s=96&v=4"/></br>[avanderberg](https://github.com/avanderberg) | <img width="50" src="https://avatars2.githubusercontent.com/u/67130?s=96&v=4"/></br>[chr15m](https://github.com/chr15m) | <img width="50" src="https://avatars2.githubusercontent.com/u/2793530?s=96&v=4"/></br>[CyberXZT](https://github.com/CyberXZT) | <img width="50" src="https://avatars2.githubusercontent.com/u/1307332?s=96&v=4"/></br>[dbrandonjohnson](https://github.com/dbrandonjohnson) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/49439044?s=96&v=4"/></br>[fourstepper](https://github.com/fourstepper) | <img width="50" src="https://avatars2.githubusercontent.com/u/64712218?s=96&v=4"/></br>[Hegghammer](https://github.com/Hegghammer) | <img width="50" src="https://avatars2.githubusercontent.com/u/3266447?s=96&v=4"/></br>[iamwillbar](https://github.com/iamwillbar) | <img width="50" src="https://avatars2.githubusercontent.com/u/1310474?s=96&v=4"/></br>[jknowles](https://github.com/jknowles) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/11947658?s=96&v=4"/></br>[KentBrockman](https://github.com/KentBrockman) | <img width="50" src="https://avatars2.githubusercontent.com/u/5588131?s=96&v=4"/></br>[kianenigma](https://github.com/kianenigma) | <img width="50" src="https://avatars2.githubusercontent.com/u/24908652?s=96&v=4"/></br>[konishi-t](https://github.com/konishi-t) | <img width="50" src="https://avatars2.githubusercontent.com/u/42319182?s=96&v=4"/></br>[marcdw1289](https://github.com/marcdw1289) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/126279083?s=96&v=4"/></br>[matmoly](https://github.com/matmoly) | <img width="50" src="https://avatars2.githubusercontent.com/u/1788010?s=96&v=4"/></br>[maxtruxa](https://github.com/maxtruxa) | <img width="50" src="https://avatars2.githubusercontent.com/u/29300939?s=96&v=4"/></br>[mcejp](https://github.com/mcejp) | <img width="50" src="https://avatars2.githubusercontent.com/u/31054972?s=96&v=4"/></br>[saarantras](https://github.com/saarantras) |
|
||||
| <img width="50" src="https://avatars2.githubusercontent.com/u/327998?s=96&v=4"/></br>[sif](https://github.com/sif) | <img width="50" src="https://avatars2.githubusercontent.com/u/765564?s=96&v=4"/></br>[taskcruncher](https://github.com/taskcruncher) | <img width="50" src="https://avatars2.githubusercontent.com/u/333944?s=96&v=4"/></br>[tateisu](https://github.com/tateisu) | |
|
||||
<!-- SPONSORS-GITHUB -->
|
||||
|
||||
<!-- TOC -->
|
||||
@@ -128,9 +125,11 @@ A community maintained list of these distributions can be found here: [Unofficia
|
||||
- [How to build the apps](https://github.com/laurent22/joplin/blob/dev/BUILD.md)
|
||||
- [Writing a technical spec](https://github.com/laurent22/joplin/blob/dev/readme/technical_spec.md)
|
||||
- [Desktop application styling](https://github.com/laurent22/joplin/blob/dev/readme/spec/desktop_styling.md)
|
||||
- [Note History spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/history.md)
|
||||
- [Note history spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/history.md)
|
||||
- [Synchronisation spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/sync.md)
|
||||
- [Sync Lock spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/sync_lock.md)
|
||||
- [Synchronous Scroll spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/sync_scroll.md)
|
||||
- [Overall Architecture spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/architecture.md)
|
||||
- [Plugin Architecture spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/plugins.md)
|
||||
- [Search Sorting spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/search_sorting.md)
|
||||
- [E2EE: Technical spec](https://github.com/laurent22/joplin/blob/dev/readme/spec/e2ee.md)
|
||||
@@ -505,6 +504,7 @@ Name | Description
|
||||
[Mastodon feed](https://mastodon.social/@joplinapp) | Follow us on Mastodon
|
||||
[Patreon page](https://www.patreon.com/joplin) |The latest news are often posted there
|
||||
[Discord server](https://discord.gg/VSj7AFHvpq) | Our chat server
|
||||
[LinkedIn](https://www.linkedin.com/company/joplin) | Our LinkedIn page
|
||||
[Sub-reddit](https://www.reddit.com/r/joplinapp/) | Also a good place to get help
|
||||
|
||||
# Contributing
|
||||
@@ -530,47 +530,47 @@ Current translations:
|
||||
<!-- LOCALE-TABLE-AUTO-GENERATED -->
|
||||
| Language | Po File | Last translator | Percent done
|
||||
---|---|---|---|---
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/arableague.png" width="16px"/> | Arabic | [ar](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ar.po) | [Whaell O](mailto:Whaell@protonmail.com) | 82%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/arableague.png" width="16px"/> | Arabic | [ar](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ar.po) | [Whaell O](mailto:Whaell@protonmail.com) | 80%
|
||||
<img src="https://joplinapp.org/images/flags/es/basque_country.png" width="16px"/> | Basque | [eu](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eu.po) | juan.abasolo@ehu.eus | 23%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ba.png" width="16px"/> | Bosnian (Bosna i Hercegovina) | [bs_BA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bs_BA.po) | [Derviš T.](mailto:dervis.t@pm.me) | 59%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/bg.png" width="16px"/> | Bulgarian (България) | [bg_BG](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bg_BG.po) | | 46%
|
||||
<img src="https://joplinapp.org/images/flags/es/catalonia.png" width="16px"/> | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ca.po) | [Xavi Ivars](mailto:xavi.ivars@gmail.com) | 92%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/hr.png" width="16px"/> | Croatian (Hrvatska) | [hr_HR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hr_HR.po) | [Milo Ivir](mailto:mail@milotype.de) | 92%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/cz.png" width="16px"/> | Czech (Česká republika) | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/cs_CZ.po) | [Michal Stanke](mailto:michal@stanke.cz) | 79%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ba.png" width="16px"/> | Bosnian (Bosna i Hercegovina) | [bs_BA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bs_BA.po) | [Derviš T.](mailto:dervis.t@pm.me) | 58%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/bg.png" width="16px"/> | Bulgarian (България) | [bg_BG](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/bg_BG.po) | | 45%
|
||||
<img src="https://joplinapp.org/images/flags/es/catalonia.png" width="16px"/> | Catalan | [ca](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ca.po) | [Xavi Ivars](mailto:xavi.ivars@gmail.com) | 89%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/hr.png" width="16px"/> | Croatian (Hrvatska) | [hr_HR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hr_HR.po) | [Milo Ivir](mailto:mail@milotype.de) | 89%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/cz.png" width="16px"/> | Czech (Česká republika) | [cs_CZ](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/cs_CZ.po) | [Michal Stanke](mailto:michal@stanke.cz) | 77%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/dk.png" width="16px"/> | Dansk (Danmark) | [da_DK](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/da_DK.po) | ERYpTION | 99%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/de.png" width="16px"/> | Deutsch (Deutschland) | [de_DE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/de_DE.po) | [MrKanister](mailto:pueblos_spatulas@aleeas.com) | 99%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ee.png" width="16px"/> | Eesti Keel (Eesti) | [et_EE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/et_EE.po) | | 45%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ee.png" width="16px"/> | Eesti Keel (Eesti) | [et_EE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/et_EE.po) | | 44%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/gb.png" width="16px"/> | English (United Kingdom) | [en_GB](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_GB.po) | | 100%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/us.png" width="16px"/> | English (United States of America) | [en_US](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/en_US.po) | | 100%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/es.png" width="16px"/> | Español (España) | [es_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/es_ES.po) | [Francisco Mora](mailto:francisco.m.collao@gmail.com) | 91%
|
||||
<img src="https://joplinapp.org/images/flags/esperanto.png" width="16px"/> | Esperanto | [eo](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eo.po) | Marton Paulo | 26%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/fi.png" width="16px"/> | Finnish (Suomi) | [fi_FI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fi_FI.po) | mrkaato0 | 98%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/fr.png" width="16px"/> | Français (France) | [fr_FR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fr_FR.po) | Laurent Cozic | 98%
|
||||
<img src="https://joplinapp.org/images/flags/es/galicia.png" width="16px"/> | Galician (España) | [gl_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/gl_ES.po) | [Marcos Lans](mailto:marcoslansgarza@gmail.com) | 30%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/id.png" width="16px"/> | Indonesian (Indonesia) | [id_ID](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/id_ID.po) | [Wisnu Adi Santoso](mailto:waditos@gmail.com) | 92%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/it.png" width="16px"/> | Italiano (Italia) | [it_IT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/it_IT.po) | [Albano Battistella](mailto:albano_battistella@hotmail.com) | 80%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/hu.png" width="16px"/> | Magyar (Magyarország) | [hu_HU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hu_HU.po) | [Magyari Balázs](mailto:balmag@gmail.com) | 80%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/be.png" width="16px"/> | Nederlands (België, Belgique, Belgien) | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_BE.po) | | 81%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/nl.png" width="16px"/> | Nederlands (Nederland) | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_NL.po) | [MHolkamp](mailto:mholkamp@users.noreply.github.com) | 91%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/no.png" width="16px"/> | Norwegian (Norge, Noreg) | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nb_NO.po) | [Mats Estensen](mailto:code@mxe.no) | 91%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ir.png" width="16px"/> | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 57%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/pl.png" width="16px"/> | Polski (Polska) | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pl_PL.po) | [X3NO](mailto:X3NO@disroot.org) | 92%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/br.png" width="16px"/> | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_BR.po) | [Renato Nunes Bastos](mailto:rnbastos@gmail.com) | 91%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/pt.png" width="16px"/> | Português (Portugal) | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_PT.po) | [Diogo Caveiro](mailto:dcaveiro@yahoo.com) | 75%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ro.png" width="16px"/> | Română | [ro](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ro.po) | [Cristi Duluta](mailto:cristi.duluta@gmail.com) | 52%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/si.png" width="16px"/> | Slovenian (Slovenija) | [sl_SI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sl_SI.po) | [Martin Korelič](mailto:martin.korelic@protonmail.com) | 83%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/se.png" width="16px"/> | Svenska | [sv](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sv.po) | [Jonatan Nyberg](mailto:jonatan@autistici.org) | 99%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/th.png" width="16px"/> | Thai (ประเทศไทย) | [th_TH](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/th_TH.po) | | 38%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/vn.png" width="16px"/> | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/vi.po) | | 80%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/tr.png" width="16px"/> | Türkçe (Türkiye) | [tr_TR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/tr_TR.po) | [Arda Kılıçdağı](mailto:arda@kilicdagi.com) | 92%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ua.png" width="16px"/> | Ukrainian (Україна) | [uk_UA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/uk_UA.po) | [Vyacheslav Andreykiv](mailto:vandreykiv@gmail.com) | 75%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/gr.png" width="16px"/> | Ελληνικά (Ελλάδα) | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 91%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ru.png" width="16px"/> | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Sergey Segeda](mailto:thesermanarm@gmail.com) | 83%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/rs.png" width="16px"/> | српски језик (Србија) | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sr_RS.po) | | 67%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/cn.png" width="16px"/> | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [KaneGreen](mailto:737445366KG@Gmail.com) | 97%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/tw.png" width="16px"/> | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [Kevin Hsu](mailto:kevin.hsu.hws@gmail.com) | 92%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/jp.png" width="16px"/> | 日本語 (日本) | [ja_JP](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ja_JP.po) | [genneko](mailto:genneko217@gmail.com) | 92%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/kr.png" width="16px"/> | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 92%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/es.png" width="16px"/> | Español (España) | [es_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/es_ES.po) | [Francisco Villaverde](mailto:teko.gr@gmail.com) | 99%
|
||||
<img src="https://joplinapp.org/images/flags/esperanto.png" width="16px"/> | Esperanto | [eo](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/eo.po) | Marton Paulo | 25%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/fi.png" width="16px"/> | Finnish (Suomi) | [fi_FI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fi_FI.po) | mrkaato0 | 95%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/fr.png" width="16px"/> | Français (France) | [fr_FR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fr_FR.po) | Laurent Cozic | 99%
|
||||
<img src="https://joplinapp.org/images/flags/es/galicia.png" width="16px"/> | Galician (España) | [gl_ES](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/gl_ES.po) | [Marcos Lans](mailto:marcoslansgarza@gmail.com) | 29%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/id.png" width="16px"/> | Indonesian (Indonesia) | [id_ID](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/id_ID.po) | [Wisnu Adi Santoso](mailto:waditos@gmail.com) | 89%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/it.png" width="16px"/> | Italiano (Italia) | [it_IT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/it_IT.po) | [Manuel Tassi](mailto:mannivuwiki@gmail.com) | 81%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/hu.png" width="16px"/> | Magyar (Magyarország) | [hu_HU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/hu_HU.po) | [Magyari Balázs](mailto:balmag@gmail.com) | 78%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/be.png" width="16px"/> | Nederlands (België, Belgique, Belgien) | [nl_BE](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_BE.po) | | 79%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/nl.png" width="16px"/> | Nederlands (Nederland) | [nl_NL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nl_NL.po) | [MHolkamp](mailto:mholkamp@users.noreply.github.com) | 88%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/no.png" width="16px"/> | Norwegian (Norge, Noreg) | [nb_NO](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/nb_NO.po) | [Mats Estensen](mailto:code@mxe.no) | 88%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ir.png" width="16px"/> | Persian | [fa](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/fa.po) | [Kourosh Firoozbakht](mailto:kourox@protonmail.com) | 55%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/pl.png" width="16px"/> | Polski (Polska) | [pl_PL](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pl_PL.po) | [X3NO](mailto:X3NO@disroot.org) | 91%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/br.png" width="16px"/> | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_BR.po) | [Renato Nunes Bastos](mailto:rnbastos@gmail.com) | 88%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/pt.png" width="16px"/> | Português (Portugal) | [pt_PT](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/pt_PT.po) | [Diogo Caveiro](mailto:dcaveiro@yahoo.com) | 73%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ro.png" width="16px"/> | Română | [ro](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ro.po) | [Cristi Duluta](mailto:cristi.duluta@gmail.com) | 51%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/si.png" width="16px"/> | Slovenian (Slovenija) | [sl_SI](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sl_SI.po) | [Martin Korelič](mailto:martin.korelic@protonmail.com) | 80%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/se.png" width="16px"/> | Svenska | [sv](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sv.po) | [Jonatan Nyberg](mailto:jonatan@autistici.org) | 96%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/th.png" width="16px"/> | Thai (ประเทศไทย) | [th_TH](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/th_TH.po) | | 36%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/vn.png" width="16px"/> | Tiếng Việt | [vi](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/vi.po) | | 78%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/tr.png" width="16px"/> | Türkçe (Türkiye) | [tr_TR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/tr_TR.po) | [Arda Kılıçdağı](mailto:arda@kilicdagi.com) | 89%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ua.png" width="16px"/> | Ukrainian (Україна) | [uk_UA](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/uk_UA.po) | [Vyacheslav Andreykiv](mailto:vandreykiv@gmail.com) | 72%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/gr.png" width="16px"/> | Ελληνικά (Ελλάδα) | [el_GR](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/el_GR.po) | [Harris Arvanitis](mailto:xaris@tuta.io) | 88%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/ru.png" width="16px"/> | Русский (Россия) | [ru_RU](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ru_RU.po) | [Dmitriy Q](mailto:krotesk@mail.ru) | 99%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/rs.png" width="16px"/> | српски језик (Србија) | [sr_RS](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/sr_RS.po) | | 65%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/cn.png" width="16px"/> | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_CN.po) | [wh201906](mailto:wh201906@yandex.com) | 97%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/tw.png" width="16px"/> | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/zh_TW.po) | [Kevin Hsu](mailto:kevin.hsu.hws@gmail.com) | 89%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/jp.png" width="16px"/> | 日本語 (日本) | [ja_JP](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ja_JP.po) | [genneko](mailto:genneko217@gmail.com) | 89%
|
||||
<img src="https://joplinapp.org/images/flags/country-4x3/kr.png" width="16px"/> | 한국어 | [ko](https://github.com/laurent22/joplin/blob/dev/packages/tools/locales/ko.po) | [Ji-Hyeon Gim](mailto:potatogim@potatogim.net) | 89%
|
||||
<!-- LOCALE-TABLE-AUTO-GENERATED -->
|
||||
|
||||
# Contributors
|
||||
|
7
bootstrap.bundle.min.js
vendored
Normal file
53
gulpfile.js
@@ -1,23 +1,52 @@
|
||||
const gulp = require('gulp');
|
||||
const utils = require('./packages/tools/gulp/utils');
|
||||
const execa = require('execa');
|
||||
const { stdout } = require('process');
|
||||
|
||||
const execCommand = async (executableName, args, options = null) => {
|
||||
options = {
|
||||
showInput: true,
|
||||
showStdout: true,
|
||||
showStderr: true,
|
||||
quiet: false,
|
||||
...options,
|
||||
};
|
||||
|
||||
if (options.quiet) {
|
||||
options.showInput = false;
|
||||
options.showStdout = false;
|
||||
options.showStderr = false;
|
||||
}
|
||||
|
||||
if (options.showInput) {
|
||||
stdout.write(`> ${executableName} ${args.join(' ')}\n`);
|
||||
}
|
||||
|
||||
const promise = execa(executableName, args);
|
||||
if (options.showStdout && promise.stdout) promise.stdout.pipe(process.stdout);
|
||||
if (options.showStderr && promise.stderr) promise.stderr.pipe(process.stderr);
|
||||
const result = await promise;
|
||||
return result.stdout.trim();
|
||||
};
|
||||
|
||||
|
||||
const tasks = {
|
||||
updateIgnoredTypeScriptBuild: require('./packages/tools/gulp/tasks/updateIgnoredTypeScriptBuild'),
|
||||
buildCommandIndex: require('./packages/tools/gulp/tasks/buildCommandIndex'),
|
||||
completePublishAll: {
|
||||
fn: async () => {
|
||||
|
||||
await utils.execCommandVerbose('git', ['add', '-A']);
|
||||
await utils.execCommandVerbose('git', ['commit', '-m', 'Releasing sub-packages']);
|
||||
await execCommand('git', ['add', '-A']);
|
||||
await execCommand('git', ['commit', '-m', 'Releasing sub-packages']);
|
||||
|
||||
// Lerna does some unnecessary auth check that doesn't work with
|
||||
// automation tokens, thus the --no-verify-access. Automation token
|
||||
// is still used for access when publishing even with this flag
|
||||
// (publishing would fail otherwise).
|
||||
// https://github.com/lerna/lerna/issues/2788
|
||||
await utils.execCommandVerbose('lerna', ['publish', 'from-package', '-y', '--no-verify-access']);
|
||||
await execCommand('lerna', ['publish', 'from-package', '-y', '--no-verify-access']);
|
||||
|
||||
await utils.execCommandVerbose('git', ['push']);
|
||||
await execCommand('yarn', ['install']);
|
||||
await execCommand('git', ['add', '-A']);
|
||||
await execCommand('git', ['commit', '-m', 'Lock file']);
|
||||
|
||||
await execCommand('git', ['push']);
|
||||
},
|
||||
},
|
||||
build: {
|
||||
@@ -30,12 +59,14 @@ const tasks = {
|
||||
// faster, especially when having to rebuild after adding a
|
||||
// dependency.
|
||||
if (process.env.BUILD_SEQUENCIAL === '1') {
|
||||
await utils.execCommandVerbose('yarn', ['run', 'buildSequential']);
|
||||
await execCommand('yarn', ['run', 'buildSequential']);
|
||||
} else {
|
||||
await utils.execCommandVerbose('yarn', ['run', 'buildParallel']);
|
||||
await execCommand('yarn', ['run', 'buildParallel']);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
utils.registerGulpTasks(gulp, tasks);
|
||||
for (const taskName in tasks) {
|
||||
gulp.task(taskName, tasks[taskName].fn);
|
||||
}
|
||||
|
@@ -317,6 +317,9 @@
|
||||
"packages/app-tools/github_oauth_token.txt": true,
|
||||
"packages/generator-joplin/generators/app/templates/api/": true,
|
||||
"packages/htmlpack/dist/": true,
|
||||
"packages/react-native-alarm-notification/android/build": true,
|
||||
"packages/react-native-saf-x/android/build": true,
|
||||
"packages/react-native-saf-x/android/wrapper": true,
|
||||
"packages/renderer/**/.vscode/": true,
|
||||
"packages/renderer/**/copyLib.bat": true,
|
||||
"packages/renderer/**/node_modules/": true,
|
||||
@@ -326,6 +329,7 @@
|
||||
"packages/renderer/MdToHtml/rules/sanitize_html.js": true,
|
||||
"packages/server/db-*.sqlite": true,
|
||||
"packages/server/dist/": true,
|
||||
"packages/utils/dist/": true,
|
||||
"packages/server/temp": true,
|
||||
"packages/server/test.pid": true,
|
||||
"phpunit.xml": true,
|
||||
|
32
package.json
@@ -15,7 +15,7 @@
|
||||
"buildParallel": "yarn workspaces foreach --verbose --interlaced --parallel --jobs 2 --topological run build && yarn run tsc",
|
||||
"buildSequential": "yarn workspaces foreach --verbose --interlaced --topological run build && yarn run tsc",
|
||||
"buildApiDoc": "yarn workspace joplin start apidoc ../../readme/api/references/rest_api.md",
|
||||
"buildCommandIndex": "gulp buildCommandIndex",
|
||||
"buildCommandIndex": "node packages/tools/gulp/tasks/buildCommandIndexRun.js",
|
||||
"buildPluginDoc": "typedoc --name 'Joplin Plugin API Documentation' --mode file -theme './Assets/PluginDocTheme/' --readme './Assets/PluginDocTheme/index.md' --excludeNotExported --excludeExternals --excludePrivate --excludeProtected --out ../joplin-website/docs/api/references/plugin_api packages/lib/services/plugins/api/",
|
||||
"updateMarkdownDoc": "node ./packages/tools/updateMarkdownDoc",
|
||||
"updateNews": "node ./packages/tools/website/updateNews",
|
||||
@@ -53,7 +53,7 @@
|
||||
"test-ci": "yarn workspaces foreach --parallel --verbose --interlaced --jobs 2 run test-ci",
|
||||
"test": "yarn workspaces foreach --parallel --verbose --interlaced --jobs 2 run test",
|
||||
"tsc": "yarn workspaces foreach --parallel --verbose --interlaced run tsc",
|
||||
"updateIgnored": "gulp updateIgnoredTypeScriptBuild",
|
||||
"updateIgnored": "node packages/tools/gulp/tasks/updateIgnoredTypeScriptBuildRun.js",
|
||||
"updatePluginTypes": "./packages/generator-joplin/updateTypes.sh",
|
||||
"watch": "yarn workspaces foreach --parallel --verbose --interlaced --jobs 999 run watch",
|
||||
"watchWebsite": "nodemon --verbose --watch Assets/WebsiteAssets --watch packages/tools/website --watch packages/tools/website/utils --ext md,ts,js,mustache,css,tsx,gif,png,svg --exec \"node packages/tools/website/build.js && http-server --port 8077 ../joplin-website/docs -a localhost\""
|
||||
@@ -64,22 +64,25 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@joplin/utils": "~2.11",
|
||||
"@seiyab/eslint-plugin-react-hooks": "4.5.1-beta.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.48.0",
|
||||
"@typescript-eslint/parser": "5.48.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.48.2",
|
||||
"@typescript-eslint/parser": "5.48.2",
|
||||
"cspell": "5.21.2",
|
||||
"eslint": "8.31.0",
|
||||
"eslint-interactive": "10.3.0",
|
||||
"eslint-plugin-import": "2.26.0",
|
||||
"eslint-plugin-import": "2.27.4",
|
||||
"eslint-plugin-jest": "27.2.1",
|
||||
"eslint-plugin-promise": "6.1.1",
|
||||
"eslint-plugin-react": "7.31.11",
|
||||
"fs-extra": "11.1.0",
|
||||
"glob": "8.0.3",
|
||||
"eslint-plugin-react": "7.32.0",
|
||||
"execa": "5.1.1",
|
||||
"fs-extra": "11.1.1",
|
||||
"glob": "8.1.0",
|
||||
"gulp": "4.0.2",
|
||||
"husky": "3.1.0",
|
||||
"lerna": "3.22.1",
|
||||
"lint-staged": "13.1.0",
|
||||
"madge": "5.0.1",
|
||||
"lint-staged": "13.2.1",
|
||||
"madge": "6.0.0",
|
||||
"npm-package-json-lint": "6.4.0",
|
||||
"typedoc": "0.17.8",
|
||||
"typescript": "4.9.4"
|
||||
@@ -88,7 +91,12 @@
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"http-server": "14.1.1",
|
||||
"node-gyp": "9.3.1",
|
||||
"nodemon": "2.0.20"
|
||||
"nodemon": "2.0.22"
|
||||
},
|
||||
"packageManager": "yarn@3.3.1"
|
||||
"packageManager": "yarn@3.3.1",
|
||||
"resolutions": {
|
||||
"react-native-camera@4.2.1": "patch:react-native-camera@npm%3A4.2.1#./.yarn/patches/react-native-camera-npm-4.2.1-24b2600a7e.patch",
|
||||
"rn-fetch-blob@0.12.0": "patch:rn-fetch-blob@npm%3A0.12.0#./.yarn/patches/rn-fetch-blob-npm-0.12.0-cf02e3c544.patch",
|
||||
"react-native-vosk@0.1.12": "patch:react-native-vosk@npm%3A0.1.12#./.yarn/patches/react-native-vosk-npm-0.1.12-76b1caaae8.patch"
|
||||
}
|
||||
}
|
||||
|
@@ -6,14 +6,14 @@ interface LinkStoreEntry {
|
||||
}
|
||||
|
||||
class LinkSelector {
|
||||
noteId_: string;
|
||||
scrollTop_: number;
|
||||
renderedText_: string;
|
||||
currentLinkIndex_: number;
|
||||
linkStore_: LinkStoreEntry[];
|
||||
linkRegex_: RegExp;
|
||||
private noteId_: string;
|
||||
private scrollTop_: number;
|
||||
private renderedText_: string;
|
||||
private currentLinkIndex_: number;
|
||||
private linkStore_: LinkStoreEntry[];
|
||||
private linkRegex_: RegExp;
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
this.noteId_ = null;
|
||||
this.scrollTop_ = null; // used so 'o' won't open unhighlighted link after scrolling
|
||||
this.renderedText_ = null;
|
||||
@@ -22,22 +22,22 @@ class LinkSelector {
|
||||
this.linkRegex_ = /http:\/\/[0-9.]+:[0-9]+\/[0-9]+/g;
|
||||
}
|
||||
|
||||
get link(): string | null {
|
||||
public get link(): string | null {
|
||||
if (this.currentLinkIndex_ === null) return null;
|
||||
return this.linkStore_[this.currentLinkIndex_].link;
|
||||
}
|
||||
|
||||
get noteX(): number | null {
|
||||
public get noteX(): number | null {
|
||||
if (this.currentLinkIndex_ === null) return null;
|
||||
return this.linkStore_[this.currentLinkIndex_].noteX;
|
||||
}
|
||||
|
||||
get noteY(): number | null {
|
||||
public get noteY(): number | null {
|
||||
if (this.currentLinkIndex_ === null) return null;
|
||||
return this.linkStore_[this.currentLinkIndex_].noteY;
|
||||
}
|
||||
|
||||
findLinks(renderedText: string): LinkStoreEntry[] {
|
||||
public findLinks(renderedText: string): LinkStoreEntry[] {
|
||||
const newLinkStore: LinkStoreEntry[] = [];
|
||||
const lines: string[] = renderedText.split('\n');
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
@@ -56,19 +56,19 @@ class LinkSelector {
|
||||
return newLinkStore;
|
||||
}
|
||||
|
||||
updateText(renderedText: string): void {
|
||||
public updateText(renderedText: string): void {
|
||||
this.currentLinkIndex_ = null;
|
||||
this.renderedText_ = renderedText;
|
||||
this.linkStore_ = this.findLinks(this.renderedText_);
|
||||
}
|
||||
|
||||
updateNote(textWidget: any): void {
|
||||
public updateNote(textWidget: any): void {
|
||||
this.noteId_ = textWidget.noteId;
|
||||
this.scrollTop_ = textWidget.scrollTop_;
|
||||
this.updateText(textWidget.renderedText_);
|
||||
}
|
||||
|
||||
scrollWidget(textWidget: any): void {
|
||||
public scrollWidget(textWidget: any): void {
|
||||
if (this.currentLinkIndex_ === null) return;
|
||||
|
||||
const noteY = this.linkStore_[this.currentLinkIndex_].noteY;
|
||||
@@ -93,7 +93,7 @@ class LinkSelector {
|
||||
return;
|
||||
}
|
||||
|
||||
changeLink(textWidget: any, offset: number): void | null {
|
||||
public changeLink(textWidget: any, offset: number): void | null {
|
||||
if (textWidget.noteId !== this.noteId_) {
|
||||
this.updateNote(textWidget);
|
||||
this.changeLink(textWidget, offset);
|
||||
@@ -123,7 +123,7 @@ class LinkSelector {
|
||||
return;
|
||||
}
|
||||
|
||||
openLink(textWidget: any): void {
|
||||
public openLink(textWidget: any): void {
|
||||
if (textWidget.noteId !== this.noteId_) return;
|
||||
if (textWidget.renderedText_ !== this.renderedText_) return;
|
||||
if (textWidget.scrollTop_ !== this.scrollTop_) return;
|
||||
|
@@ -8,7 +8,7 @@ const Resource = require('@joplin/lib/models/Resource').default;
|
||||
const Setting = require('@joplin/lib/models/Setting').default;
|
||||
const reducer = require('@joplin/lib/reducer').default;
|
||||
const { defaultState } = require('@joplin/lib/reducer');
|
||||
const { splitCommandString } = require('@joplin/lib/string-utils.js');
|
||||
const { splitCommandString } = require('@joplin/utils');
|
||||
const { reg } = require('@joplin/lib/registry.js');
|
||||
const { _ } = require('@joplin/lib/locale');
|
||||
const shim = require('@joplin/lib/shim').default;
|
||||
|
@@ -9,7 +9,8 @@ const Tag = require('@joplin/lib/models/Tag').default;
|
||||
const Setting = require('@joplin/lib/models/Setting').default;
|
||||
const { reg } = require('@joplin/lib/registry.js');
|
||||
const { fileExtension } = require('@joplin/lib/path-utils');
|
||||
const { splitCommandString, splitCommandBatch } = require('@joplin/lib/string-utils');
|
||||
const { splitCommandString } = require('@joplin/utils');
|
||||
const { splitCommandBatch } = require('@joplin/lib/string-utils');
|
||||
const { _ } = require('@joplin/lib/locale');
|
||||
const fs = require('fs-extra');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
@@ -246,6 +247,7 @@ class Application extends BaseApplication {
|
||||
showConsole: () => {},
|
||||
maximizeConsole: () => {},
|
||||
stdout: text => {
|
||||
// eslint-disable-next-line no-console
|
||||
console.info(text);
|
||||
},
|
||||
fullScreen: () => {},
|
||||
@@ -407,6 +409,7 @@ class Application extends BaseApplication {
|
||||
if (this.showStackTraces_) {
|
||||
console.error(error);
|
||||
} else {
|
||||
// eslint-disable-next-line no-console
|
||||
console.info(error.message);
|
||||
}
|
||||
process.exit(1);
|
||||
|
@@ -125,14 +125,14 @@ async function handleAutocompletionPromise(line) {
|
||||
}
|
||||
function handleAutocompletion(str, callback) {
|
||||
// eslint-disable-next-line promise/prefer-await-to-then -- Old code before rule was applied
|
||||
handleAutocompletionPromise(str).then(function(res) {
|
||||
handleAutocompletionPromise(str).then((res) => {
|
||||
callback(undefined, res);
|
||||
});
|
||||
}
|
||||
function toCommandLine(args) {
|
||||
if (Array.isArray(args)) {
|
||||
return args
|
||||
.map(function(a) {
|
||||
.map((a) => {
|
||||
if (a.indexOf('"') !== -1 || a.indexOf(' ') !== -1) {
|
||||
return `'${a}'`;
|
||||
} else if (a.indexOf('\'') !== -1) {
|
||||
|
@@ -1,97 +0,0 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const locale_1 = require("@joplin/lib/locale");
|
||||
const registry_js_1 = require("@joplin/lib/registry.js");
|
||||
class BaseCommand {
|
||||
constructor() {
|
||||
this.stdout_ = null;
|
||||
this.prompt_ = null;
|
||||
}
|
||||
usage() {
|
||||
throw new Error('Usage not defined');
|
||||
}
|
||||
encryptionCheck(item) {
|
||||
if (item && item.encryption_applied)
|
||||
throw new Error((0, locale_1._)('Cannot change encrypted item'));
|
||||
}
|
||||
description() {
|
||||
throw new Error('Description not defined');
|
||||
}
|
||||
action(_args) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
throw new Error('Action not defined');
|
||||
});
|
||||
}
|
||||
compatibleUis() {
|
||||
return ['cli', 'gui'];
|
||||
}
|
||||
supportsUi(ui) {
|
||||
return this.compatibleUis().indexOf(ui) >= 0;
|
||||
}
|
||||
options() {
|
||||
return [];
|
||||
}
|
||||
hidden() {
|
||||
return false;
|
||||
}
|
||||
enabled() {
|
||||
return true;
|
||||
}
|
||||
cancellable() {
|
||||
return false;
|
||||
}
|
||||
cancel() {
|
||||
return __awaiter(this, void 0, void 0, function* () { });
|
||||
}
|
||||
name() {
|
||||
const r = this.usage().split(' ');
|
||||
return r[0];
|
||||
}
|
||||
setDispatcher(fn) {
|
||||
this.dispatcher_ = fn;
|
||||
}
|
||||
dispatch(action) {
|
||||
if (!this.dispatcher_)
|
||||
throw new Error('Dispatcher not defined');
|
||||
return this.dispatcher_(action);
|
||||
}
|
||||
setStdout(fn) {
|
||||
this.stdout_ = fn;
|
||||
}
|
||||
stdout(text) {
|
||||
if (this.stdout_)
|
||||
this.stdout_(text);
|
||||
}
|
||||
setPrompt(fn) {
|
||||
this.prompt_ = fn;
|
||||
}
|
||||
prompt(message, options = null) {
|
||||
return __awaiter(this, void 0, void 0, function* () {
|
||||
if (!this.prompt_)
|
||||
throw new Error('Prompt is undefined');
|
||||
return yield this.prompt_(message, options);
|
||||
});
|
||||
}
|
||||
metadata() {
|
||||
return {
|
||||
name: this.name(),
|
||||
usage: this.usage(),
|
||||
options: this.options(),
|
||||
hidden: this.hidden(),
|
||||
};
|
||||
}
|
||||
logger() {
|
||||
return registry_js_1.reg.logger();
|
||||
}
|
||||
}
|
||||
exports.default = BaseCommand;
|
||||
//# sourceMappingURL=base-command.js.map
|
@@ -7,80 +7,80 @@ export default class BaseCommand {
|
||||
protected prompt_: any = null;
|
||||
protected dispatcher_: any;
|
||||
|
||||
usage(): string {
|
||||
public usage(): string {
|
||||
throw new Error('Usage not defined');
|
||||
}
|
||||
|
||||
encryptionCheck(item: any) {
|
||||
public encryptionCheck(item: any) {
|
||||
if (item && item.encryption_applied) throw new Error(_('Cannot change encrypted item'));
|
||||
}
|
||||
|
||||
description() {
|
||||
public description() {
|
||||
throw new Error('Description not defined');
|
||||
}
|
||||
|
||||
async action(_args: any) {
|
||||
public async action(_args: any) {
|
||||
throw new Error('Action not defined');
|
||||
}
|
||||
|
||||
compatibleUis() {
|
||||
public compatibleUis() {
|
||||
return ['cli', 'gui'];
|
||||
}
|
||||
|
||||
supportsUi(ui: string) {
|
||||
public supportsUi(ui: string) {
|
||||
return this.compatibleUis().indexOf(ui) >= 0;
|
||||
}
|
||||
|
||||
options(): any[] {
|
||||
public options(): any[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
hidden() {
|
||||
public hidden() {
|
||||
return false;
|
||||
}
|
||||
|
||||
enabled() {
|
||||
public enabled() {
|
||||
return true;
|
||||
}
|
||||
|
||||
cancellable() {
|
||||
public cancellable() {
|
||||
return false;
|
||||
}
|
||||
|
||||
async cancel() {}
|
||||
public async cancel() {}
|
||||
|
||||
name() {
|
||||
public name() {
|
||||
const r = this.usage().split(' ');
|
||||
return r[0];
|
||||
}
|
||||
|
||||
setDispatcher(fn: Function) {
|
||||
public setDispatcher(fn: Function) {
|
||||
this.dispatcher_ = fn;
|
||||
}
|
||||
|
||||
dispatch(action: any) {
|
||||
public dispatch(action: any) {
|
||||
if (!this.dispatcher_) throw new Error('Dispatcher not defined');
|
||||
return this.dispatcher_(action);
|
||||
}
|
||||
|
||||
setStdout(fn: Function) {
|
||||
public setStdout(fn: Function) {
|
||||
this.stdout_ = fn;
|
||||
}
|
||||
|
||||
stdout(text: string) {
|
||||
public stdout(text: string) {
|
||||
if (this.stdout_) this.stdout_(text);
|
||||
}
|
||||
|
||||
setPrompt(fn: Function) {
|
||||
public setPrompt(fn: Function) {
|
||||
this.prompt_ = fn;
|
||||
}
|
||||
|
||||
async prompt(message: string, options: any = null) {
|
||||
public async prompt(message: string, options: any = null) {
|
||||
if (!this.prompt_) throw new Error('Prompt is undefined');
|
||||
return await this.prompt_(message, options);
|
||||
}
|
||||
|
||||
metadata() {
|
||||
public metadata() {
|
||||
return {
|
||||
name: this.name(),
|
||||
usage: this.usage(),
|
||||
@@ -89,7 +89,7 @@ export default class BaseCommand {
|
||||
};
|
||||
}
|
||||
|
||||
logger() {
|
||||
public logger() {
|
||||
return reg.logger();
|
||||
}
|
||||
}
|
||||
|
@@ -131,6 +131,7 @@ async function main() {
|
||||
const commandsText = commandBlocks.join('\n\n');
|
||||
const footerText = getFooter();
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.info(`${headerText}\n\n` + 'USAGE' + `\n\n${commandsText}\n\n${footerText}`);
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const fs = require('fs-extra');
|
||||
const Logger = require('@joplin/lib/Logger').default;
|
||||
const { dirname } = require('@joplin/lib/path-utils');
|
||||
|
@@ -82,6 +82,7 @@ cliUtils.makeCommandArgs = function(cmd, argv) {
|
||||
const options = cmd.options();
|
||||
const booleanFlags = [];
|
||||
const aliases = {};
|
||||
const flagSpecs = [];
|
||||
for (let i = 0; i < options.length; i++) {
|
||||
if (options[i].length !== 2) throw new Error(`Invalid options: ${options[i]}`);
|
||||
let flags = options[i][0];
|
||||
@@ -96,6 +97,8 @@ cliUtils.makeCommandArgs = function(cmd, argv) {
|
||||
if (flags.short && flags.long) {
|
||||
aliases[flags.long] = [flags.short];
|
||||
}
|
||||
|
||||
flagSpecs.push(flags);
|
||||
}
|
||||
|
||||
const args = yargParser(argv, {
|
||||
@@ -121,6 +124,19 @@ cliUtils.makeCommandArgs = function(cmd, argv) {
|
||||
argOptions[key] = args[key];
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(argOptions)) {
|
||||
const flagSpec = flagSpecs.find(s => {
|
||||
return s.short === key || s.long === key;
|
||||
});
|
||||
if (flagSpec?.arg?.required) {
|
||||
// If a flag is required, and no value is provided for it, Yargs
|
||||
// sets the value to `true`.
|
||||
if (value === true) {
|
||||
throw new Error(_('Missing required flag value: %s', `-${flagSpec.short} <${flagSpec.arg.name}>`));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output.options = argOptions;
|
||||
|
||||
return output;
|
||||
|
@@ -39,9 +39,9 @@ class Command extends BaseCommand {
|
||||
let settingsObj;
|
||||
try {
|
||||
settingsObj = JSON.parse(json);
|
||||
} catch (err) {
|
||||
} catch (error) {
|
||||
isSettled = true;
|
||||
return reject(new Error(`Invalid JSON passed to config --import: \n${err.message}.`));
|
||||
return reject(new Error(`Invalid JSON passed to config --import: \n${error.message}.`));
|
||||
}
|
||||
if (settingsObj) {
|
||||
Object.entries(settingsObj)
|
||||
|
@@ -12,15 +12,15 @@ const imageType = require('image-type');
|
||||
const readChunk = require('read-chunk');
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
public usage() {
|
||||
return 'e2ee <command> [path]';
|
||||
}
|
||||
|
||||
description() {
|
||||
public description() {
|
||||
return _('Manages E2EE configuration. Commands are `enable`, `disable`, `decrypt`, `status`, `decrypt-file`, and `target-status`.'); // `generate-ppk`
|
||||
}
|
||||
|
||||
options() {
|
||||
public options() {
|
||||
return [
|
||||
// This is here mostly for testing - shouldn't be used
|
||||
['-p, --password <password>', 'Use this password as master password (For security reasons, it is not recommended to use this option).'],
|
||||
@@ -30,7 +30,7 @@ class Command extends BaseCommand {
|
||||
];
|
||||
}
|
||||
|
||||
async action(args: any) {
|
||||
public async action(args: any) {
|
||||
const options = args.options;
|
||||
|
||||
const askForMasterKey = async (error: any) => {
|
||||
|
@@ -1,6 +1,6 @@
|
||||
const fs = require('fs-extra');
|
||||
const BaseCommand = require('./base-command').default;
|
||||
const { splitCommandString } = require('@joplin/lib/string-utils.js');
|
||||
const { splitCommandString } = require('@joplin/utils');
|
||||
const uuid = require('@joplin/lib/uuid').default;
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('@joplin/lib/locale');
|
||||
|
@@ -1,21 +0,0 @@
|
||||
const BaseCommand = require('./base-command').default;
|
||||
const { app } = require('./app.js');
|
||||
const { _ } = require('@joplin/lib/locale');
|
||||
const Folder = require('@joplin/lib/models/Folder').default;
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
return 'mkbook <new-notebook>';
|
||||
}
|
||||
|
||||
description() {
|
||||
return _('Creates a new notebook.');
|
||||
}
|
||||
|
||||
async action(args) {
|
||||
const folder = await Folder.save({ title: args['new-notebook'] }, { userSideValidation: true });
|
||||
app().switchCurrentFolder(folder);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Command;
|
50
packages/app-cli/app/command-mkbook.test.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import { setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
|
||||
import { setupCommandForTesting, setupApplication } from './utils/testUtils';
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
const Command = require('./command-mkbook');
|
||||
|
||||
|
||||
describe('command-mkbook', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
await setupApplication();
|
||||
});
|
||||
|
||||
|
||||
it('should create a subfolder in first folder', async () => {
|
||||
const command = setupCommandForTesting(Command);
|
||||
await command.action({ 'new-notebook': 'folder1', options: {} });
|
||||
await command.action({ 'new-notebook': 'folder1_1', options: { parent: 'folder1' } });
|
||||
|
||||
const folder1 = await Folder.loadByTitle('folder1');
|
||||
const folder1_1 = await Folder.loadByTitle('folder1_1');
|
||||
|
||||
expect(folder1.title).toBe('folder1');
|
||||
expect(folder1_1.parent_id).toBe(folder1.id);
|
||||
});
|
||||
|
||||
it('should not be possible to create a subfolder without an argument.', async () => {
|
||||
const command = setupCommandForTesting(Command);
|
||||
await command.action({ 'new-notebook': 'folder2', options: {} });
|
||||
await expect(command.action({ 'new-notebook': 'folder2_1', options: { parent: true } })).rejects.toThrowError();
|
||||
});
|
||||
|
||||
it('should not be possible to create subfolder in ambiguous destination folder', async () => {
|
||||
const command = setupCommandForTesting(Command);
|
||||
await command.action({ 'new-notebook': 'folder3', options: {} });
|
||||
await command.action({ 'new-notebook': 'folder3', options: {} }); // ambiguous folder
|
||||
await expect(command.action({ 'new-notebook': 'folder3_1', options: { parent: 'folder3' } })).rejects.toThrowError();
|
||||
|
||||
// check if duplicate entries have been created.
|
||||
const folderAll = await Folder.all();
|
||||
const folders3 = folderAll.filter(x => x.title === 'folder3');
|
||||
expect(folders3.length).toBe(2);
|
||||
|
||||
// check if something has been created in one of the duplicate entries.
|
||||
expect(await Folder.childrenIds(folders3[0].id)).toEqual([]);
|
||||
expect(await Folder.childrenIds(folders3[1].id)).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
65
packages/app-cli/app/command-mkbook.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
const BaseCommand = require('./base-command').default;
|
||||
const { app } = require('./app.js');
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import BaseModel from '@joplin/lib/BaseModel';
|
||||
import Folder from '@joplin/lib/models/Folder';
|
||||
import { FolderEntity } from '@joplin/lib/services/database/types';
|
||||
|
||||
class Command extends BaseCommand {
|
||||
public usage() {
|
||||
return 'mkbook <new-notebook>';
|
||||
}
|
||||
|
||||
public description() {
|
||||
return _('Creates a new notebook.');
|
||||
}
|
||||
|
||||
public options() {
|
||||
return [
|
||||
['-p, --parent <parent-notebook>', _('Create a new notebook under a parent notebook.')],
|
||||
];
|
||||
}
|
||||
|
||||
// validDestinationFolder check for presents and ambiguous folders
|
||||
public async validDestinationFolder(targetFolder: string) {
|
||||
|
||||
const destinationFolder = await app().loadItem(BaseModel.TYPE_FOLDER, targetFolder);
|
||||
if (!destinationFolder) {
|
||||
throw new Error(_('Cannot find: "%s"', targetFolder));
|
||||
}
|
||||
|
||||
const destinationDups = await Folder.search({ titlePattern: targetFolder, limit: 2 });
|
||||
if (destinationDups.length > 1) {
|
||||
throw new Error(_('Ambiguous notebook "%s". Please use short notebook id instead - press "ti" to see the short notebook id', targetFolder));
|
||||
}
|
||||
|
||||
return destinationFolder;
|
||||
}
|
||||
|
||||
public async saveAndSwitchFolder(newFolder: FolderEntity) {
|
||||
|
||||
const folder = await Folder.save(newFolder, { userSideValidation: true });
|
||||
app().switchCurrentFolder(folder);
|
||||
|
||||
}
|
||||
|
||||
public async action(args: any) {
|
||||
const targetFolder = args.options.parent;
|
||||
|
||||
const newFolder: FolderEntity = {
|
||||
title: args['new-notebook'],
|
||||
};
|
||||
|
||||
if (targetFolder) {
|
||||
|
||||
const destinationFolder = await this.validDestinationFolder(targetFolder);
|
||||
newFolder.parent_id = destinationFolder.id;
|
||||
await this.saveAndSwitchFolder(newFolder);
|
||||
|
||||
} else {
|
||||
await this.saveAndSwitchFolder(newFolder);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Command;
|
@@ -26,7 +26,7 @@ class Command extends BaseCommand {
|
||||
|
||||
const destinationDuplicates = await Folder.search({ titlePattern: destination, limit: 2 });
|
||||
if (destinationDuplicates.length > 1) {
|
||||
throw new Error(_('Ambiguous notebook "%s". Please use short notebook id instead - press "ti" to see the short notebook id' , destination));
|
||||
throw new Error(_('Ambiguous notebook "%s". Please use short notebook id instead - press "ti" to see the short notebook id', destination));
|
||||
}
|
||||
|
||||
const itemFolder = await app().loadItem(BaseModel.TYPE_FOLDER, pattern);
|
||||
|
@@ -23,19 +23,19 @@ function settingTypeToSchemaType(type: SettingItemType): string {
|
||||
}
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
public usage() {
|
||||
return 'settingschema <file>';
|
||||
}
|
||||
|
||||
description() {
|
||||
public description() {
|
||||
return 'Build the setting schema file';
|
||||
}
|
||||
|
||||
enabled() {
|
||||
public enabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
async action(args: any) {
|
||||
public async action(args: any) {
|
||||
const schema: Record<string, any> = {
|
||||
title: 'JSON schema for Joplin setting files',
|
||||
'$id': Setting.schemaUrl,
|
||||
|
@@ -9,11 +9,11 @@ import { appTypeToLockType } from '@joplin/lib/services/synchronizer/LockHandler
|
||||
const BaseCommand = require('./base-command').default;
|
||||
const { app } = require('./app.js');
|
||||
const { OneDriveApiNodeUtils } = require('@joplin/lib/onedrive-api-node-utils.js');
|
||||
const { reg } = require('@joplin/lib/registry.js');
|
||||
import { reg } from '@joplin/lib/registry';
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const md5 = require('md5');
|
||||
const locker = require('proper-lockfile');
|
||||
const fs = require('fs-extra');
|
||||
import * as locker from 'proper-lockfile';
|
||||
import { pathExists, writeFile } from 'fs-extra';
|
||||
|
||||
class Command extends BaseCommand {
|
||||
|
||||
@@ -21,15 +21,15 @@ class Command extends BaseCommand {
|
||||
private releaseLockFn_: Function = null;
|
||||
private oneDriveApiUtils_: any = null;
|
||||
|
||||
usage() {
|
||||
public usage() {
|
||||
return 'sync';
|
||||
}
|
||||
|
||||
description() {
|
||||
public description() {
|
||||
return _('Synchronises with remote storage.');
|
||||
}
|
||||
|
||||
options() {
|
||||
public options() {
|
||||
return [
|
||||
['--target <target>', _('Sync to provided target (defaults to sync.target config value)')],
|
||||
['--upgrade', _('Upgrade the sync target to the latest version.')],
|
||||
@@ -37,24 +37,15 @@ class Command extends BaseCommand {
|
||||
];
|
||||
}
|
||||
|
||||
static async lockFile(filePath: string): Promise<Function> {
|
||||
private static async lockFile(filePath: string) {
|
||||
return locker.lock(filePath, { stale: 1000 * 60 * 5 });
|
||||
}
|
||||
|
||||
static isLocked(filePath: string) {
|
||||
return new Promise((resolve, reject) => {
|
||||
locker.check(filePath, (error: any, isLocked: boolean) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(isLocked);
|
||||
});
|
||||
});
|
||||
private static async isLocked(filePath: string) {
|
||||
return locker.check(filePath);
|
||||
}
|
||||
|
||||
async doAuth() {
|
||||
public async doAuth() {
|
||||
const syncTarget = reg.syncTarget(this.syncTargetId_);
|
||||
const syncTargetMd = SyncTargetRegistry.idToMetadata(this.syncTargetId_);
|
||||
|
||||
@@ -98,23 +89,23 @@ class Command extends BaseCommand {
|
||||
return false;
|
||||
}
|
||||
|
||||
cancelAuth() {
|
||||
public cancelAuth() {
|
||||
if (this.oneDriveApiUtils_) {
|
||||
this.oneDriveApiUtils_.cancelOAuthDance();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
doingAuth() {
|
||||
public doingAuth() {
|
||||
return !!this.oneDriveApiUtils_;
|
||||
}
|
||||
|
||||
async action(args: any) {
|
||||
public async action(args: any) {
|
||||
this.releaseLockFn_ = null;
|
||||
|
||||
// Lock is unique per profile/database
|
||||
const lockFilePath = `${require('os').tmpdir()}/synclock_${md5(escape(Setting.value('profileDir')))}`; // https://github.com/pvorb/node-md5/issues/41
|
||||
if (!(await fs.pathExists(lockFilePath))) await fs.writeFile(lockFilePath, 'synclock');
|
||||
if (!(await pathExists(lockFilePath))) await writeFile(lockFilePath, 'synclock');
|
||||
|
||||
const useLock = args.options.useLock !== 0;
|
||||
|
||||
@@ -247,7 +238,7 @@ class Command extends BaseCommand {
|
||||
cleanUp();
|
||||
}
|
||||
|
||||
async cancel() {
|
||||
public async cancel() {
|
||||
if (this.doingAuth()) {
|
||||
this.cancelAuth();
|
||||
return;
|
||||
@@ -272,7 +263,7 @@ class Command extends BaseCommand {
|
||||
this.syncTargetId_ = null;
|
||||
}
|
||||
|
||||
cancellable() {
|
||||
public cancellable() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@@ -18,19 +18,19 @@ function itemCount(args: any) {
|
||||
}
|
||||
|
||||
class Command extends BaseCommand {
|
||||
usage() {
|
||||
public usage() {
|
||||
return 'testing <command> [arg0]';
|
||||
}
|
||||
|
||||
description() {
|
||||
public description() {
|
||||
return 'testing';
|
||||
}
|
||||
|
||||
enabled() {
|
||||
public enabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
options(): any[] {
|
||||
public options(): any[] {
|
||||
return [
|
||||
['--folder-count <count>', 'Folders to create'],
|
||||
['--note-count <count>', 'Notes to create'],
|
||||
@@ -40,7 +40,7 @@ class Command extends BaseCommand {
|
||||
];
|
||||
}
|
||||
|
||||
async action(args: any) {
|
||||
public async action(args: any) {
|
||||
const { command, options } = args;
|
||||
|
||||
if (command === 'populate') {
|
||||
@@ -118,6 +118,7 @@ class Command extends BaseCommand {
|
||||
}
|
||||
await Promise.all(promises);
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.info(await api.exec('GET', 'api/items/root:/testing:'));
|
||||
}
|
||||
|
||||
|
@@ -12,7 +12,7 @@ class Command extends BaseCommand {
|
||||
}
|
||||
|
||||
async action() {
|
||||
this.stdout(versionInfo(require('./package.json')).message);
|
||||
this.stdout(versionInfo(require('./package.json'), {}).message);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -36,7 +36,7 @@ class FolderListWidget extends ListWidget {
|
||||
if (Setting.value('showNoteCounts')) {
|
||||
let noteCount = item.note_count;
|
||||
// Subtract children note_count from parent folder.
|
||||
if (this.folderHasChildren_(this.folders,item.id)) {
|
||||
if (this.folderHasChildren_(this.folders, item.id)) {
|
||||
for (let i = 0; i < this.folders.length; i++) {
|
||||
if (this.folders[i].parent_id === item.id) {
|
||||
noteCount -= this.folders[i].note_count;
|
||||
|
@@ -5,7 +5,7 @@ const stripAnsi = require('strip-ansi');
|
||||
const { handleAutocompletion } = require('../autocompletion.js');
|
||||
|
||||
export default class StatusBarWidget extends BaseWidget {
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
|
||||
this.promptState_ = null;
|
||||
@@ -14,20 +14,20 @@ export default class StatusBarWidget extends BaseWidget {
|
||||
this.items_ = [];
|
||||
}
|
||||
|
||||
get name() {
|
||||
public get name() {
|
||||
return 'statusBar';
|
||||
}
|
||||
|
||||
get canHaveFocus() {
|
||||
public get canHaveFocus() {
|
||||
return false;
|
||||
}
|
||||
|
||||
setItemAt(index: number, text: string) {
|
||||
public setItemAt(index: number, text: string) {
|
||||
this.items_[index] = stripAnsi(text).trim();
|
||||
this.invalidate();
|
||||
}
|
||||
|
||||
async prompt(initialText = '', promptString: any = null, options: any = null) {
|
||||
public async prompt(initialText = '', promptString: any = null, options: any = null) {
|
||||
if (this.promptState_) throw new Error('Another prompt already active');
|
||||
if (promptString === null) promptString = ':';
|
||||
if (options === null) options = {};
|
||||
@@ -53,15 +53,15 @@ export default class StatusBarWidget extends BaseWidget {
|
||||
return this.promptState_.promise;
|
||||
}
|
||||
|
||||
get promptActive() {
|
||||
public get promptActive() {
|
||||
return !!this.promptState_;
|
||||
}
|
||||
|
||||
get history() {
|
||||
public get history() {
|
||||
return this.history_;
|
||||
}
|
||||
|
||||
resetCursor() {
|
||||
public resetCursor() {
|
||||
if (!this.promptActive) return;
|
||||
if (!this.inputEventEmitter_) return;
|
||||
|
||||
@@ -70,7 +70,7 @@ export default class StatusBarWidget extends BaseWidget {
|
||||
this.term.moveTo(this.absoluteInnerX + termutils.textLength(this.promptState_.promptString) + this.inputEventEmitter_.getInput().length, this.absoluteInnerY);
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
super.render();
|
||||
|
||||
const doSaveCursor = !this.promptActive;
|
||||
|
@@ -75,14 +75,14 @@ if (process.platform === 'win32') {
|
||||
output: process.stdout,
|
||||
});
|
||||
|
||||
rl.on('SIGINT', function() {
|
||||
rl.on('SIGINT', () => {
|
||||
process.emit('SIGINT');
|
||||
});
|
||||
}
|
||||
|
||||
process.stdout.on('error', function(err) {
|
||||
process.stdout.on('error', (error) => {
|
||||
// https://stackoverflow.com/questions/12329816/error-write-epipe-when-piping-node-output-to-head#15884508
|
||||
if (err.code === 'EPIPE') {
|
||||
if (error.code === 'EPIPE') {
|
||||
process.exit(0);
|
||||
}
|
||||
});
|
||||
|
@@ -11,6 +11,7 @@ function createConsoleWrapper(pluginId: string) {
|
||||
const wrapper: any = {};
|
||||
|
||||
for (const n in console) {
|
||||
// eslint-disable-next-line no-console
|
||||
if (!console.hasOwnProperty(n)) continue;
|
||||
wrapper[n] = (...args: any[]) => {
|
||||
const newArgs = args.slice();
|
||||
@@ -34,7 +35,7 @@ export default class PluginRunner extends BasePluginRunner {
|
||||
private eventHandlers_: EventHandlers = {};
|
||||
private activeSandboxCalls_: any = {};
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
|
||||
this.eventHandler = this.eventHandler.bind(this);
|
||||
@@ -63,7 +64,7 @@ export default class PluginRunner extends BasePluginRunner {
|
||||
};
|
||||
}
|
||||
|
||||
async run(plugin: Plugin, sandbox: Global): Promise<void> {
|
||||
public async run(plugin: Plugin, sandbox: Global): Promise<void> {
|
||||
return new Promise((resolve: Function, reject: Function) => {
|
||||
const onStarted = () => {
|
||||
plugin.off('started', onStarted);
|
||||
|
@@ -30,34 +30,36 @@
|
||||
2019,
|
||||
2020,
|
||||
2021,
|
||||
2022
|
||||
2022,
|
||||
2023
|
||||
],
|
||||
"owner": "Laurent Cozic"
|
||||
},
|
||||
"version": "2.10.0",
|
||||
"version": "2.11.0",
|
||||
"bin": "./main.js",
|
||||
"engines": {
|
||||
"node": ">=10.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@joplin/lib": "~2.10",
|
||||
"@joplin/renderer": "~2.10",
|
||||
"aws-sdk": "2.1288.0",
|
||||
"@joplin/lib": "~2.11",
|
||||
"@joplin/renderer": "~2.11",
|
||||
"@joplin/utils": "~2.11",
|
||||
"aws-sdk": "2.1340.0",
|
||||
"chalk": "4.1.2",
|
||||
"compare-version": "0.1.2",
|
||||
"fs-extra": "11.1.0",
|
||||
"fs-extra": "11.1.1",
|
||||
"html-entities": "1.4.0",
|
||||
"image-type": "3.1.0",
|
||||
"keytar": "7.9.0",
|
||||
"md5": "2.3.0",
|
||||
"node-rsa": "1.1.1",
|
||||
"open": "8.4.0",
|
||||
"open": "8.4.2",
|
||||
"proper-lockfile": "4.1.2",
|
||||
"read-chunk": "2.1.0",
|
||||
"server-destroy": "1.0.1",
|
||||
"sharp": "0.31.3",
|
||||
"sharp": "0.32.0",
|
||||
"sprintf-js": "1.1.2",
|
||||
"sqlite3": "5.1.4",
|
||||
"sqlite3": "5.1.6",
|
||||
"string-padding": "1.0.2",
|
||||
"strip-ansi": "6.0.1",
|
||||
"tcp-port-used": "1.0.2",
|
||||
@@ -68,12 +70,13 @@
|
||||
"yargs-parser": "21.1.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@joplin/tools": "~2.10",
|
||||
"@joplin/tools": "~2.11",
|
||||
"@types/fs-extra": "9.0.13",
|
||||
"@types/jest": "29.2.5",
|
||||
"@types/jest": "29.2.6",
|
||||
"@types/node": "18.11.18",
|
||||
"@types/proper-lockfile": "^4.1.2",
|
||||
"gulp": "4.0.2",
|
||||
"jest": "29.3.1",
|
||||
"jest": "29.4.3",
|
||||
"temp": "0.9.4",
|
||||
"typescript": "4.9.4"
|
||||
}
|
||||
|
@@ -12,7 +12,7 @@ const shim = require('@joplin/lib/shim').default;
|
||||
const HtmlToHtml = require('@joplin/renderer/HtmlToHtml').default;
|
||||
const { enexXmlToMd } = require('@joplin/lib/import-enex-md-gen.js');
|
||||
|
||||
describe('HtmlToHtml', function() {
|
||||
describe('HtmlToHtml', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
@@ -49,6 +49,7 @@ describe('HtmlToHtml', function() {
|
||||
}
|
||||
|
||||
if (actualHtml !== expectedHtml) {
|
||||
/* eslint-disable no-console */
|
||||
console.info('');
|
||||
console.info(`Error converting file: ${htmlSourceFilename}`);
|
||||
console.info('--------------------------------- Got:');
|
||||
@@ -59,6 +60,7 @@ describe('HtmlToHtml', function() {
|
||||
console.info(expectedHtml.split('\n'));
|
||||
console.info('--------------------------------------------');
|
||||
console.info('');
|
||||
/* eslint-enable */
|
||||
|
||||
expect(false).toBe(true);
|
||||
// return;
|
||||
|
@@ -3,7 +3,7 @@ const os = require('os');
|
||||
const { filename } = require('@joplin/lib/path-utils');
|
||||
import HtmlToMd from '@joplin/lib/HtmlToMd';
|
||||
|
||||
describe('HtmlToMd', function() {
|
||||
describe('HtmlToMd', () => {
|
||||
|
||||
it('should convert from Html to Markdown', (async () => {
|
||||
const basePath = `${__dirname}/html_to_md`;
|
||||
@@ -57,6 +57,7 @@ describe('HtmlToMd', function() {
|
||||
result.push('--------------------------------------------');
|
||||
result.push('');
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.info(result.join('\n'));
|
||||
|
||||
// console.info('');
|
||||
|
@@ -1,7 +1,7 @@
|
||||
|
||||
const MarkupToHtml = require('@joplin/renderer/MarkupToHtml').default;
|
||||
|
||||
describe('MarkupToHtml', function() {
|
||||
describe('MarkupToHtml', () => {
|
||||
|
||||
it('should strip markup', (async () => {
|
||||
const service = new MarkupToHtml();
|
||||
|
@@ -16,7 +16,7 @@ function newTestMdToHtml(options: any = null) {
|
||||
return new MdToHtml(options);
|
||||
}
|
||||
|
||||
describe('MdToHtml', function() {
|
||||
describe('MdToHtml', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
@@ -72,6 +72,7 @@ describe('MdToHtml', function() {
|
||||
'',
|
||||
];
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.info(msg.join('\n'));
|
||||
|
||||
expect(false).toBe(true);
|
||||
|
@@ -22,7 +22,7 @@ const goToNote = (testApp, note) => {
|
||||
testApp.dispatch({ type: 'NOTE_SELECT', id: note.id });
|
||||
};
|
||||
|
||||
describe('feature_NoteHistory', function() {
|
||||
describe('feature_NoteHistory', () => {
|
||||
beforeEach(async () => {
|
||||
testApp = new TestApp();
|
||||
await testApp.start(['--no-welcome']);
|
||||
|
@@ -8,7 +8,7 @@ const time = require('@joplin/lib/time').default;
|
||||
|
||||
let testApp = null;
|
||||
|
||||
describe('integration_NoteList', function() {
|
||||
describe('integration_NoteList', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
testApp = new TestApp();
|
||||
|
@@ -22,7 +22,7 @@ const { ALL_NOTES_FILTER_ID } = require('@joplin/lib/reserved-ids.js');
|
||||
|
||||
let testApp = null;
|
||||
|
||||
describe('integration_ShowAllNotes', function() {
|
||||
describe('integration_ShowAllNotes', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
testApp = new TestApp();
|
||||
|
@@ -8,7 +8,7 @@ const time = require('@joplin/lib/time').default;
|
||||
|
||||
let testApp = null;
|
||||
|
||||
describe('integration_TagList', function() {
|
||||
describe('integration_TagList', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
testApp = new TestApp();
|
||||
|
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable jest/require-top-level-describe */
|
||||
|
||||
import KeychainService from '@joplin/lib/services/keychain/KeychainService';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
@@ -11,7 +13,7 @@ function describeIfCompatible(name: string, fn: any, elseFn: any) {
|
||||
}
|
||||
}
|
||||
|
||||
describeIfCompatible('services_KeychainService', function() {
|
||||
describeIfCompatible('services_KeychainService', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await setupDatabaseAndSynchronizer(1, { keychainEnabled: true });
|
||||
|
@@ -29,7 +29,7 @@ function newPluginService(appVersion: string = '1.4') {
|
||||
return service;
|
||||
}
|
||||
|
||||
describe('services_PluginService', function() {
|
||||
describe('services_PluginService', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
|
@@ -8,7 +8,7 @@ async function newRepoApi(): Promise<RepositoryApi> {
|
||||
return repo;
|
||||
}
|
||||
|
||||
describe('services_plugins_RepositoryApi', function() {
|
||||
describe('services_plugins_RepositoryApi', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
@@ -47,9 +47,11 @@ describe('services_plugins_RepositoryApi', function() {
|
||||
|
||||
it('should tell if a plugin can be updated', (async () => {
|
||||
const api = await newRepoApi();
|
||||
expect(await api.pluginCanBeUpdated('org.joplinapp.plugins.ToggleSidebars', '1.0.0')).toBe(true);
|
||||
expect(await api.pluginCanBeUpdated('org.joplinapp.plugins.ToggleSidebars', '1.0.2')).toBe(false);
|
||||
expect(await api.pluginCanBeUpdated('does.not.exist', '1.0.0')).toBe(false);
|
||||
|
||||
expect(await api.pluginCanBeUpdated('org.joplinapp.plugins.ToggleSidebars', '1.0.0', '3.0.0')).toBe(true);
|
||||
expect(await api.pluginCanBeUpdated('org.joplinapp.plugins.ToggleSidebars', '1.0.0', '1.0.0')).toBe(false);
|
||||
expect(await api.pluginCanBeUpdated('org.joplinapp.plugins.ToggleSidebars', '1.0.2', '3.0.0')).toBe(false);
|
||||
expect(await api.pluginCanBeUpdated('does.not.exist', '1.0.0', '3.0.0')).toBe(false);
|
||||
}));
|
||||
|
||||
});
|
||||
|
@@ -24,7 +24,7 @@ function newPluginService(appVersion: string = '1.4') {
|
||||
return service;
|
||||
}
|
||||
|
||||
describe('defaultPluginsUtils', function() {
|
||||
describe('defaultPluginsUtils', () => {
|
||||
|
||||
const pluginsId = ['joplin.plugin.ambrt.backlinksToNote', 'org.joplinapp.plugins.ToggleSidebars'];
|
||||
|
||||
|
@@ -1,7 +1,7 @@
|
||||
const sandboxProxy = require('@joplin/lib/services/plugins/sandboxProxy');
|
||||
import { setupDatabaseAndSynchronizer, switchClient } from '@joplin/lib/testing/test-utils';
|
||||
|
||||
describe('services_plugins_sandboxProxy', function() {
|
||||
describe('services_plugins_sandboxProxy', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
|
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
// This script can be used to simulate a running production environment, by
|
||||
// having multiple users in parallel changing notes and synchronising.
|
||||
//
|
||||
@@ -17,7 +19,7 @@
|
||||
|
||||
import * as fs from 'fs-extra';
|
||||
import { homedir } from 'os';
|
||||
import { execCommand2 } from '@joplin/tools/tool-utils';
|
||||
import { execCommand } from '@joplin/utils';
|
||||
import { chdir } from 'process';
|
||||
|
||||
const minUserNum = 1;
|
||||
@@ -64,7 +66,7 @@ const processUser = async (userNum: number) => {
|
||||
|
||||
await chdir(cliDir);
|
||||
|
||||
await execCommand2(['yarn', 'run', 'start-no-build', '--', '--profile', profileDir, 'batch', commandFile]);
|
||||
await execCommand(['yarn', 'run', 'start-no-build', '--', '--profile', profileDir, 'batch', commandFile]);
|
||||
} catch (error) {
|
||||
console.error(`Could not process user ${userNum}:`, error);
|
||||
} finally {
|
||||
@@ -88,7 +90,7 @@ const main = async () => {
|
||||
|
||||
// Build the app once before starting, because we'll use start-no-build to
|
||||
// run the scripts (faster)
|
||||
await execCommand2(['yarn', 'run', 'build']);
|
||||
await execCommand(['yarn', 'run', 'build']);
|
||||
|
||||
const focusUserNum = 0;
|
||||
|
||||
|
@@ -50,7 +50,7 @@ async function browserGetZoom(tabId) {
|
||||
});
|
||||
}
|
||||
|
||||
browser_.runtime.onInstalled.addListener(function() {
|
||||
browser_.runtime.onInstalled.addListener(() => {
|
||||
if (window.joplinEnv() === 'dev') {
|
||||
browser_.browserAction.setIcon({
|
||||
path: 'icons/32-dev.png',
|
||||
@@ -165,7 +165,7 @@ async function sendClipMessage(clipType) {
|
||||
}
|
||||
}
|
||||
|
||||
browser_.commands.onCommand.addListener(function(command) {
|
||||
browser_.commands.onCommand.addListener((command) => {
|
||||
// We could enumerate these twice, but since we're in here first,
|
||||
// why not save ourselves the trouble with this convention
|
||||
if (command.startsWith('clip')) {
|
||||
|
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
(function() {
|
||||
|
||||
if (window.jopext_hasRun) return;
|
||||
|
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "Joplin Web Clipper [DEV]",
|
||||
"version": "2.10.0",
|
||||
"version": "2.11.2",
|
||||
"description": "Capture and save web pages and screenshots from your browser to Joplin.",
|
||||
"homepage_url": "https://joplinapp.org",
|
||||
"content_security_policy": "script-src 'self'; object-src 'self'",
|
||||
|
@@ -126,4 +126,4 @@
|
||||
"react-app"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
/* eslint-disable no-console */
|
||||
|
||||
// Do this as the first thing so that any code reading it knows the right env.
|
||||
process.env.BABEL_ENV = 'development';
|
||||
process.env.NODE_ENV = 'development';
|
||||
@@ -104,9 +106,9 @@ checkBrowsers(paths.appPath, isInteractive)
|
||||
);
|
||||
const devServer = new WebpackDevServer(compiler, serverConfig);
|
||||
// Launch WebpackDevServer.
|
||||
devServer.listen(port, HOST, err => {
|
||||
if (err) {
|
||||
return console.log(err);
|
||||
devServer.listen(port, HOST, error => {
|
||||
if (error) {
|
||||
return console.log(error);
|
||||
}
|
||||
if (isInteractive) {
|
||||
clearConsole();
|
||||
@@ -128,16 +130,16 @@ checkBrowsers(paths.appPath, isInteractive)
|
||||
openBrowser(urls.localUrlForBrowser);
|
||||
});
|
||||
|
||||
['SIGINT', 'SIGTERM'].forEach(function(sig) {
|
||||
process.on(sig, function() {
|
||||
['SIGINT', 'SIGTERM'].forEach((sig) => {
|
||||
process.on(sig, () => {
|
||||
devServer.close();
|
||||
process.exit();
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
if (err && err.message) {
|
||||
console.log(err.message);
|
||||
.catch(error => {
|
||||
if (error && error.message) {
|
||||
console.log(error.message);
|
||||
}
|
||||
process.exit(1);
|
||||
});
|
||||
|
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
const { randomClipperPort } = require('./randomClipperPort');
|
||||
|
||||
function msleep(ms) {
|
||||
|
@@ -1,3 +1,5 @@
|
||||
/* eslint-disable no-console */
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import App from './App';
|
||||
@@ -114,7 +116,13 @@ async function main() {
|
||||
|
||||
console.info('Popup: Creating React app...');
|
||||
|
||||
ReactDOM.render(<Provider store={store}><App /></Provider>, document.getElementById('root'));
|
||||
ReactDOM.render(
|
||||
<div style = {{ maxHeight: screen.height * 0.65, overflowY: 'scroll' }}>
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
</div>,
|
||||
document.getElementById('root'));
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
|
@@ -3,7 +3,7 @@ import { PluginMessage } from './services/plugins/PluginRunner';
|
||||
import shim from '@joplin/lib/shim';
|
||||
import { isCallbackUrl } from '@joplin/lib/callbackUrlUtils';
|
||||
|
||||
const { BrowserWindow, Tray, screen } = require('electron');
|
||||
import { BrowserWindow, Tray, screen } from 'electron';
|
||||
const url = require('url');
|
||||
const path = require('path');
|
||||
const { dirname } = require('@joplin/lib/path-utils');
|
||||
@@ -25,7 +25,7 @@ export default class ElectronAppWrapper {
|
||||
private env_: string;
|
||||
private isDebugMode_: boolean;
|
||||
private profilePath_: string;
|
||||
private win_: any = null;
|
||||
private win_: BrowserWindow = null;
|
||||
private willQuitApp_: boolean = false;
|
||||
private tray_: any = null;
|
||||
private buildDir_: string = null;
|
||||
@@ -33,7 +33,7 @@ export default class ElectronAppWrapper {
|
||||
private pluginWindows_: PluginWindows = {};
|
||||
private initialCallbackUrl_: string = null;
|
||||
|
||||
constructor(electronApp: any, env: string, profilePath: string, isDebugMode: boolean, initialCallbackUrl: string) {
|
||||
public constructor(electronApp: any, env: string, profilePath: string, isDebugMode: boolean, initialCallbackUrl: string) {
|
||||
this.electronApp_ = electronApp;
|
||||
this.env_ = env;
|
||||
this.isDebugMode_ = isDebugMode;
|
||||
@@ -41,31 +41,31 @@ export default class ElectronAppWrapper {
|
||||
this.initialCallbackUrl_ = initialCallbackUrl;
|
||||
}
|
||||
|
||||
electronApp() {
|
||||
public electronApp() {
|
||||
return this.electronApp_;
|
||||
}
|
||||
|
||||
setLogger(v: Logger) {
|
||||
public setLogger(v: Logger) {
|
||||
this.logger_ = v;
|
||||
}
|
||||
|
||||
logger() {
|
||||
public logger() {
|
||||
return this.logger_;
|
||||
}
|
||||
|
||||
window() {
|
||||
public window() {
|
||||
return this.win_;
|
||||
}
|
||||
|
||||
env() {
|
||||
public env() {
|
||||
return this.env_;
|
||||
}
|
||||
|
||||
initialCallbackUrl() {
|
||||
public initialCallbackUrl() {
|
||||
return this.initialCallbackUrl_;
|
||||
}
|
||||
|
||||
createWindow() {
|
||||
public createWindow() {
|
||||
// Set to true to view errors if the application does not start
|
||||
const debugEarlyBugs = this.env_ === 'dev' || this.isDebugMode_;
|
||||
|
||||
@@ -117,7 +117,7 @@ export default class ElectronAppWrapper {
|
||||
this.win_.setPosition(primaryDisplayWidth / 2 - windowWidth, primaryDisplayHeight / 2 - windowHeight);
|
||||
}
|
||||
|
||||
this.win_.loadURL(url.format({
|
||||
void this.win_.loadURL(url.format({
|
||||
pathname: path.join(__dirname, 'index.html'),
|
||||
protocol: 'file:',
|
||||
slashes: true,
|
||||
@@ -236,11 +236,11 @@ export default class ElectronAppWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
registerPluginWindow(pluginId: string, window: any) {
|
||||
public registerPluginWindow(pluginId: string, window: any) {
|
||||
this.pluginWindows_[pluginId] = window;
|
||||
}
|
||||
|
||||
async waitForElectronAppReady() {
|
||||
public async waitForElectronAppReady() {
|
||||
if (this.electronApp().isReady()) return Promise.resolve();
|
||||
|
||||
return new Promise<void>((resolve) => {
|
||||
@@ -253,25 +253,25 @@ export default class ElectronAppWrapper {
|
||||
});
|
||||
}
|
||||
|
||||
quit() {
|
||||
public quit() {
|
||||
this.electronApp_.quit();
|
||||
}
|
||||
|
||||
exit(errorCode = 0) {
|
||||
public exit(errorCode = 0) {
|
||||
this.electronApp_.exit(errorCode);
|
||||
}
|
||||
|
||||
trayShown() {
|
||||
public trayShown() {
|
||||
return !!this.tray_;
|
||||
}
|
||||
|
||||
// This method is used in macOS only to hide the whole app (and not just the main window)
|
||||
// including the menu bar. This follows the macOS way of hiding an app.
|
||||
hide() {
|
||||
public hide() {
|
||||
this.electronApp_.hide();
|
||||
}
|
||||
|
||||
buildDir() {
|
||||
public buildDir() {
|
||||
if (this.buildDir_) return this.buildDir_;
|
||||
let dir = `${__dirname}/build`;
|
||||
if (!fs.pathExistsSync(dir)) {
|
||||
@@ -283,7 +283,7 @@ export default class ElectronAppWrapper {
|
||||
return dir;
|
||||
}
|
||||
|
||||
trayIconFilename_() {
|
||||
private trayIconFilename_() {
|
||||
let output = '';
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
@@ -298,7 +298,7 @@ export default class ElectronAppWrapper {
|
||||
}
|
||||
|
||||
// Note: this must be called only after the "ready" event of the app has been dispatched
|
||||
createTray(contextMenu: any) {
|
||||
public createTray(contextMenu: any) {
|
||||
try {
|
||||
this.tray_ = new Tray(`${this.buildDir()}/icons/${this.trayIconFilename_()}`);
|
||||
this.tray_.setToolTip(this.electronApp_.name);
|
||||
@@ -312,13 +312,13 @@ export default class ElectronAppWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
destroyTray() {
|
||||
public destroyTray() {
|
||||
if (!this.tray_) return;
|
||||
this.tray_.destroy();
|
||||
this.tray_ = null;
|
||||
}
|
||||
|
||||
ensureSingleInstance() {
|
||||
public ensureSingleInstance() {
|
||||
if (this.env_ === 'dev') return false;
|
||||
|
||||
const gotTheLock = this.electronApp_.requestSingleInstanceLock();
|
||||
@@ -347,7 +347,7 @@ export default class ElectronAppWrapper {
|
||||
return false;
|
||||
}
|
||||
|
||||
async start() {
|
||||
public async start() {
|
||||
// Since we are doing other async things before creating the window, we might miss
|
||||
// the "ready" event. So we use the function below to make sure that the app is ready.
|
||||
await this.waitForElectronAppReady();
|
||||
@@ -375,7 +375,7 @@ export default class ElectronAppWrapper {
|
||||
});
|
||||
}
|
||||
|
||||
async openCallbackUrl(url: string) {
|
||||
public async openCallbackUrl(url: string) {
|
||||
this.win_.webContents.send('asynchronous-message', 'openCallbackUrl', {
|
||||
url: url,
|
||||
});
|
||||
|
@@ -40,6 +40,7 @@ export default class InteropServiceHelper {
|
||||
const service = InteropService.instance();
|
||||
|
||||
const result = await service.export(fullExportOptions);
|
||||
// eslint-disable-next-line no-console
|
||||
console.info('Export HTML result: ', result);
|
||||
return tempFile;
|
||||
}
|
||||
@@ -190,6 +191,7 @@ export default class InteropServiceHelper {
|
||||
|
||||
try {
|
||||
const result = await service.export(exportOptions);
|
||||
// eslint-disable-next-line no-console
|
||||
console.info('Export result: ', result);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
@@ -1,9 +1,9 @@
|
||||
import { AppState } from './app.reducer';
|
||||
import appReducer, { createAppDefaultState } from './app.reducer';
|
||||
|
||||
describe('app.reducer', function() {
|
||||
describe('app.reducer', () => {
|
||||
|
||||
it('DIALOG_OPEN', async () => {
|
||||
it('should handle DIALOG_OPEN', async () => {
|
||||
const state: AppState = createAppDefaultState({}, {});
|
||||
|
||||
let newState = appReducer(state, {
|
||||
|
@@ -38,6 +38,7 @@ export interface AppState extends State {
|
||||
watchedResources: any;
|
||||
mainLayout: LayoutItem;
|
||||
dialogs: AppStateDialog[];
|
||||
isResettingLayout: boolean;
|
||||
}
|
||||
|
||||
export function createAppDefaultState(windowContentSize: any, resourceEditWatcherDefaultState: any): AppState {
|
||||
@@ -60,6 +61,7 @@ export function createAppDefaultState(windowContentSize: any, resourceEditWatche
|
||||
mainLayout: null,
|
||||
startupPluginsLoaded: false,
|
||||
dialogs: [],
|
||||
isResettingLayout: false,
|
||||
...resourceEditWatcherDefaultState,
|
||||
};
|
||||
}
|
||||
@@ -308,7 +310,15 @@ export default function(state: AppState, action: any) {
|
||||
};
|
||||
break;
|
||||
|
||||
|
||||
case 'RESET_LAYOUT':
|
||||
newState = {
|
||||
...state,
|
||||
isResettingLayout: action.value,
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
error.message = `In reducer: ${error.message} Action: ${JSON.stringify(action)}`;
|
||||
throw error;
|
||||
|
@@ -559,7 +559,12 @@ class Application extends BaseApplication {
|
||||
|
||||
await SpellCheckerService.instance().initialize(new SpellCheckerServiceDriverNative());
|
||||
|
||||
// await populateDatabase(reg.db());
|
||||
// await populateDatabase(reg.db(), {
|
||||
// clearDatabase: true,
|
||||
// folderCount: 1000,
|
||||
// rootFolderCount: 1,
|
||||
// subFolderDepth: 1,
|
||||
// });
|
||||
|
||||
// setTimeout(() => {
|
||||
// console.info(CommandService.instance().commandsToMarkdownTable(this.store().getState()));
|
||||
|
@@ -21,7 +21,7 @@ export class Bridge {
|
||||
private electronWrapper_: ElectronAppWrapper;
|
||||
private lastSelectedPaths_: LastSelectedPath;
|
||||
|
||||
constructor(electronWrapper: ElectronAppWrapper) {
|
||||
public constructor(electronWrapper: ElectronAppWrapper) {
|
||||
this.electronWrapper_ = electronWrapper;
|
||||
this.lastSelectedPaths_ = {
|
||||
file: null,
|
||||
@@ -29,11 +29,11 @@ export class Bridge {
|
||||
};
|
||||
}
|
||||
|
||||
electronApp() {
|
||||
public electronApp() {
|
||||
return this.electronWrapper_;
|
||||
}
|
||||
|
||||
electronIsDev() {
|
||||
public electronIsDev() {
|
||||
return !this.electronApp().electronApp().isPackaged;
|
||||
}
|
||||
|
||||
@@ -60,11 +60,11 @@ export class Bridge {
|
||||
return `${__dirname}/vendor`;
|
||||
}
|
||||
|
||||
env() {
|
||||
public env() {
|
||||
return this.electronWrapper_.env();
|
||||
}
|
||||
|
||||
processArgv() {
|
||||
public processArgv() {
|
||||
return process.argv;
|
||||
}
|
||||
|
||||
@@ -114,44 +114,44 @@ export class Bridge {
|
||||
});
|
||||
}
|
||||
|
||||
window() {
|
||||
public window() {
|
||||
return this.electronWrapper_.window();
|
||||
}
|
||||
|
||||
showItemInFolder(fullPath: string) {
|
||||
public showItemInFolder(fullPath: string) {
|
||||
return require('electron').shell.showItemInFolder(toSystemSlashes(fullPath));
|
||||
}
|
||||
|
||||
newBrowserWindow(options: any) {
|
||||
public newBrowserWindow(options: any) {
|
||||
return new BrowserWindow(options);
|
||||
}
|
||||
|
||||
windowContentSize() {
|
||||
public windowContentSize() {
|
||||
if (!this.window()) return { width: 0, height: 0 };
|
||||
const s = this.window().getContentSize();
|
||||
return { width: s[0], height: s[1] };
|
||||
}
|
||||
|
||||
windowSize() {
|
||||
public windowSize() {
|
||||
if (!this.window()) return { width: 0, height: 0 };
|
||||
const s = this.window().getSize();
|
||||
return { width: s[0], height: s[1] };
|
||||
}
|
||||
|
||||
windowSetSize(width: number, height: number) {
|
||||
public windowSetSize(width: number, height: number) {
|
||||
if (!this.window()) return;
|
||||
return this.window().setSize(width, height);
|
||||
}
|
||||
|
||||
openDevTools() {
|
||||
public openDevTools() {
|
||||
return this.window().webContents.openDevTools();
|
||||
}
|
||||
|
||||
closeDevTools() {
|
||||
public closeDevTools() {
|
||||
return this.window().webContents.closeDevTools();
|
||||
}
|
||||
|
||||
async showSaveDialog(options: any) {
|
||||
public async showSaveDialog(options: any) {
|
||||
const { dialog } = require('electron');
|
||||
if (!options) options = {};
|
||||
if (!('defaultPath' in options) && this.lastSelectedPaths_.file) options.defaultPath = this.lastSelectedPaths_.file;
|
||||
@@ -162,7 +162,7 @@ export class Bridge {
|
||||
return filePath;
|
||||
}
|
||||
|
||||
async showOpenDialog(options: OpenDialogOptions = null) {
|
||||
public async showOpenDialog(options: OpenDialogOptions = null) {
|
||||
const { dialog } = require('electron');
|
||||
if (!options) options = {};
|
||||
let fileType = 'file';
|
||||
@@ -177,13 +177,13 @@ export class Bridge {
|
||||
}
|
||||
|
||||
// Don't use this directly - call one of the showXxxxxxxMessageBox() instead
|
||||
showMessageBox_(window: any, options: any): number {
|
||||
private showMessageBox_(window: any, options: any): number {
|
||||
const { dialog } = require('electron');
|
||||
if (!window) window = this.window();
|
||||
return dialog.showMessageBoxSync(window, options);
|
||||
}
|
||||
|
||||
showErrorMessageBox(message: string) {
|
||||
public showErrorMessageBox(message: string) {
|
||||
return this.showMessageBox_(this.window(), {
|
||||
type: 'error',
|
||||
message: message,
|
||||
@@ -191,7 +191,7 @@ export class Bridge {
|
||||
});
|
||||
}
|
||||
|
||||
showConfirmMessageBox(message: string, options: any = null) {
|
||||
public showConfirmMessageBox(message: string, options: any = null) {
|
||||
options = {
|
||||
buttons: [_('OK'), _('Cancel')],
|
||||
...options,
|
||||
@@ -208,7 +208,7 @@ export class Bridge {
|
||||
}
|
||||
|
||||
/* returns the index of the clicked button */
|
||||
showMessageBox(message: string, options: any = null) {
|
||||
public showMessageBox(message: string, options: any = null) {
|
||||
if (options === null) options = {};
|
||||
|
||||
const result = this.showMessageBox_(this.window(), Object.assign({}, {
|
||||
@@ -220,7 +220,7 @@ export class Bridge {
|
||||
return result;
|
||||
}
|
||||
|
||||
showInfoMessageBox(message: string, options: any = {}) {
|
||||
public showInfoMessageBox(message: string, options: any = {}) {
|
||||
const result = this.showMessageBox_(this.window(), Object.assign({}, {
|
||||
type: 'info',
|
||||
message: message,
|
||||
@@ -229,35 +229,35 @@ export class Bridge {
|
||||
return result === 0;
|
||||
}
|
||||
|
||||
setLocale(locale: string) {
|
||||
public setLocale(locale: string) {
|
||||
setLocale(locale);
|
||||
}
|
||||
|
||||
get Menu() {
|
||||
public get Menu() {
|
||||
return require('electron').Menu;
|
||||
}
|
||||
|
||||
get MenuItem() {
|
||||
public get MenuItem() {
|
||||
return require('electron').MenuItem;
|
||||
}
|
||||
|
||||
openExternal(url: string) {
|
||||
public openExternal(url: string) {
|
||||
return require('electron').shell.openExternal(url);
|
||||
}
|
||||
|
||||
async openItem(fullPath: string) {
|
||||
public async openItem(fullPath: string) {
|
||||
return require('electron').shell.openPath(toSystemSlashes(fullPath));
|
||||
}
|
||||
|
||||
screen() {
|
||||
public screen() {
|
||||
return require('electron').screen;
|
||||
}
|
||||
|
||||
shouldUseDarkColors() {
|
||||
public shouldUseDarkColors() {
|
||||
return nativeTheme.shouldUseDarkColors;
|
||||
}
|
||||
|
||||
addEventListener(name: string, fn: Function) {
|
||||
public addEventListener(name: string, fn: Function) {
|
||||
if (name === 'nativeThemeUpdated') {
|
||||
nativeTheme.on('updated', fn);
|
||||
} else {
|
||||
@@ -265,7 +265,7 @@ export class Bridge {
|
||||
}
|
||||
}
|
||||
|
||||
restart(linuxSafeRestart = true) {
|
||||
public restart(linuxSafeRestart = true) {
|
||||
// Note that in this case we are not sending the "appClose" event
|
||||
// to notify services and component that the app is about to close
|
||||
// but for the current use-case it's not really needed.
|
||||
|
@@ -40,7 +40,7 @@ async function fetchLatestRelease(options: CheckForUpdateOptions) {
|
||||
|
||||
if (!response.ok) {
|
||||
const responseText = await response.text();
|
||||
throw new Error(`Cannot get latest release info: ${responseText.substr(0,500)}`);
|
||||
throw new Error(`Cannot get latest release info: ${responseText.substr(0, 500)}`);
|
||||
}
|
||||
|
||||
const releases = await response.json();
|
||||
|
@@ -1,8 +1,7 @@
|
||||
const React = require('react');
|
||||
const { connect } = require('react-redux');
|
||||
const { clipboard } = require('electron');
|
||||
const ExtensionBadge = require('./ExtensionBadge.min');
|
||||
import bridge from '../services/bridge';
|
||||
import ExtensionBadge from './ExtensionBadge';
|
||||
import { themeStyle } from '@joplin/lib/theme';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import ClipperServer from '@joplin/lib/ClipperServer';
|
||||
@@ -11,37 +10,29 @@ import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
|
||||
import { AppState } from '../app.reducer';
|
||||
|
||||
class ClipperConfigScreenComponent extends React.Component {
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
|
||||
this.copyToken_click = this.copyToken_click.bind(this);
|
||||
}
|
||||
|
||||
disableClipperServer_click() {
|
||||
private disableClipperServer_click() {
|
||||
Setting.setValue('clipperServer.autoStart', false);
|
||||
void ClipperServer.instance().stop();
|
||||
}
|
||||
|
||||
enableClipperServer_click() {
|
||||
private enableClipperServer_click() {
|
||||
Setting.setValue('clipperServer.autoStart', true);
|
||||
void ClipperServer.instance().start();
|
||||
}
|
||||
|
||||
chromeButton_click() {
|
||||
void bridge().openExternal('https://chrome.google.com/webstore/detail/joplin-web-clipper/alofnhikmmkdbbbgpnglcpdollgjjfek');
|
||||
}
|
||||
|
||||
firefoxButton_click() {
|
||||
void bridge().openExternal('https://addons.mozilla.org/en-US/firefox/addon/joplin-web-clipper/');
|
||||
}
|
||||
|
||||
copyToken_click() {
|
||||
private copyToken_click() {
|
||||
clipboard.writeText(this.props.apiToken);
|
||||
|
||||
alert(_('Token has been copied to the clipboard!'));
|
||||
}
|
||||
|
||||
renewToken_click() {
|
||||
private renewToken_click() {
|
||||
if (confirm(_('Are you sure you want to renew the authorisation token?'))) {
|
||||
void EncryptionService.instance()
|
||||
.generateApiToken()
|
||||
@@ -52,7 +43,7 @@ class ClipperConfigScreenComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const containerStyle = Object.assign({}, theme.containerStyle, {
|
||||
|
@@ -26,9 +26,9 @@ const settingKeyToControl: any = {
|
||||
|
||||
class ConfigScreenComponent extends React.Component<any, any> {
|
||||
|
||||
rowStyle_: any = null;
|
||||
private rowStyle_: any = null;
|
||||
|
||||
constructor(props: any) {
|
||||
public constructor(props: any) {
|
||||
super(props);
|
||||
|
||||
shared.init(this, reg);
|
||||
@@ -55,15 +55,15 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
this.handleSettingButton = this.handleSettingButton.bind(this);
|
||||
}
|
||||
|
||||
async checkSyncConfig_() {
|
||||
private async checkSyncConfig_() {
|
||||
await shared.checkSyncConfig(this, this.state.settings);
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
public UNSAFE_componentWillMount() {
|
||||
this.setState({ settings: this.props.settings });
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
public componentDidMount() {
|
||||
if (this.props.defaultSection) {
|
||||
this.setState({ selectedSectionName: this.props.defaultSection }, () => {
|
||||
this.switchSection(this.props.defaultSection);
|
||||
@@ -93,7 +93,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
}
|
||||
}
|
||||
|
||||
sectionByName(name: string) {
|
||||
public sectionByName(name: string) {
|
||||
const sections = shared.settingsSections({ device: 'desktop', settings: this.state.settings });
|
||||
for (const section of sections) {
|
||||
if (section.name === name) return section;
|
||||
@@ -102,7 +102,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
throw new Error(`Invalid section name: ${name}`);
|
||||
}
|
||||
|
||||
screenFromName(screenName: string) {
|
||||
public screenFromName(screenName: string) {
|
||||
if (screenName === 'encryption') return <EncryptionConfigScreen/>;
|
||||
if (screenName === 'server') return <ClipperConfigScreen themeId={this.props.themeId}/>;
|
||||
if (screenName === 'keymap') return <KeymapConfigScreen themeId={this.props.themeId}/>;
|
||||
@@ -110,7 +110,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
throw new Error(`Invalid screen name: ${screenName}`);
|
||||
}
|
||||
|
||||
switchSection(name: string) {
|
||||
public switchSection(name: string) {
|
||||
const section = this.sectionByName(name);
|
||||
let screenName = '';
|
||||
if (section.isScreen) {
|
||||
@@ -125,11 +125,11 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
this.setState({ selectedSectionName: section.name, screenName: screenName });
|
||||
}
|
||||
|
||||
sidebar_selectionChange(event: any) {
|
||||
private sidebar_selectionChange(event: any) {
|
||||
this.switchSection(event.section.name);
|
||||
}
|
||||
|
||||
renderSectionDescription(section: any) {
|
||||
public renderSectionDescription(section: any) {
|
||||
const description = Setting.sectionDescription(section.name);
|
||||
if (!description) return null;
|
||||
|
||||
@@ -141,7 +141,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
);
|
||||
}
|
||||
|
||||
sectionToComponent(key: string, section: any, settings: any, selected: boolean) {
|
||||
public sectionToComponent(key: string, section: any, settings: any, selected: boolean) {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const createSettingComponents = (advanced: boolean) => {
|
||||
@@ -284,7 +284,7 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
return description ? <div style={this.descriptionStyle(themeId)}>{description}</div> : null;
|
||||
}
|
||||
|
||||
settingToComponent(key: string, value: any) {
|
||||
public settingToComponent(key: string, value: any) {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const output: any = null;
|
||||
@@ -453,6 +453,12 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
inputStyle.marginBottom = subLabel.marginBottom;
|
||||
|
||||
const splitCmd = (cmdString: string) => {
|
||||
// Normally not necessary but certain plugins found a way to
|
||||
// set the set the value to "undefined", leading to a crash.
|
||||
// This is now fixed at the model level but to be sure we
|
||||
// check here too, to handle any already existing data.
|
||||
// https://github.com/laurent22/joplin/issues/7621
|
||||
if (!cmdString) cmdString = '';
|
||||
const path = pathUtils.extractExecutablePath(cmdString);
|
||||
const args = cmdString.substr(path.length + 1);
|
||||
return [pathUtils.unquotePath(path), args];
|
||||
@@ -651,26 +657,26 @@ class ConfigScreenComponent extends React.Component<any, any> {
|
||||
}
|
||||
}
|
||||
|
||||
async onApplyClick() {
|
||||
public async onApplyClick() {
|
||||
shared.saveSettings(this);
|
||||
await this.checkNeedRestart();
|
||||
}
|
||||
|
||||
async onSaveClick() {
|
||||
public async onSaveClick() {
|
||||
shared.saveSettings(this);
|
||||
await this.checkNeedRestart();
|
||||
this.props.dispatch({ type: 'NAV_BACK' });
|
||||
}
|
||||
|
||||
onCancelClick() {
|
||||
public onCancelClick() {
|
||||
this.props.dispatch({ type: 'NAV_BACK' });
|
||||
}
|
||||
|
||||
hasChanges() {
|
||||
public hasChanges() {
|
||||
return !!this.state.changedSettingKeys.length;
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
const style = Object.assign({},
|
||||
|
@@ -55,7 +55,7 @@ export interface PluginItem {
|
||||
hasBeenUpdated: boolean;
|
||||
}
|
||||
|
||||
const CellRoot = styled.div<{isCompatible: boolean}>`
|
||||
const CellRoot = styled.div<{ isCompatible: boolean }>`
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
background-color: ${props => props.theme.backgroundColor};
|
||||
@@ -104,7 +104,7 @@ const DevModeLabel = styled.div`
|
||||
color: ${props => props.theme.color};
|
||||
`;
|
||||
|
||||
const StyledNameAndVersion = styled.div<{mb: any}>`
|
||||
const StyledNameAndVersion = styled.div<{ mb: any }>`
|
||||
font-family: ${props => props.theme.fontFamily};
|
||||
color: ${props => props.theme.color};
|
||||
font-size: ${props => props.theme.fontSize}px;
|
||||
|
@@ -20,7 +20,7 @@ const { space } = require('styled-system');
|
||||
|
||||
const logger = Logger.create('PluginState');
|
||||
|
||||
const maxWidth: number = 320;
|
||||
const maxWidth = 320;
|
||||
|
||||
const Root = styled.div`
|
||||
display: flex;
|
||||
@@ -143,7 +143,7 @@ export default function(props: Props) {
|
||||
let cancelled = false;
|
||||
|
||||
async function fetchPluginIds() {
|
||||
const pluginIds = await repoApi().canBeUpdatedPlugins(pluginItems.map(p => p.manifest));
|
||||
const pluginIds = await repoApi().canBeUpdatedPlugins(pluginItems.map(p => p.manifest), pluginService.appVersion);
|
||||
if (cancelled) return;
|
||||
const conv: Record<string, boolean> = {};
|
||||
pluginIds.forEach(id => conv[id] = true);
|
||||
@@ -155,7 +155,7 @@ export default function(props: Props) {
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [manifestsLoaded, pluginItems]);
|
||||
}, [manifestsLoaded, pluginItems, pluginService.appVersion]);
|
||||
|
||||
const onDelete = useCallback(async (event: ItemEvent) => {
|
||||
const item = event.item;
|
||||
@@ -225,7 +225,7 @@ export default function(props: Props) {
|
||||
];
|
||||
|
||||
const menu = bridge().Menu.buildFromTemplate(template);
|
||||
menu.popup(bridge().window());
|
||||
menu.popup({ window: bridge().window() });
|
||||
}, [onInstall, onBrowsePlugins]);
|
||||
|
||||
const onSearchQueryChange = useCallback((event: OnChangeEvent) => {
|
||||
|
@@ -13,19 +13,19 @@ interface Props {
|
||||
|
||||
class DropboxLoginScreenComponent extends React.Component<any, any> {
|
||||
|
||||
shared_: any;
|
||||
private shared_: any;
|
||||
|
||||
constructor(props: Props) {
|
||||
public constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.shared_ = new Shared(this, (msg: string) => bridge().showInfoMessageBox(msg), (msg: string) => bridge().showErrorMessageBox(msg));
|
||||
}
|
||||
|
||||
UNSAFE_componentWillMount() {
|
||||
public UNSAFE_componentWillMount() {
|
||||
this.shared_.refreshUrl();
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const style = this.props.style;
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import versionInfo from '@joplin/lib/versionInfo';
|
||||
import PluginService from '@joplin/lib/services/plugins/PluginService';
|
||||
import PluginService, { Plugins } from '@joplin/lib/services/plugins/PluginService';
|
||||
import Setting from '@joplin/lib/models/Setting';
|
||||
import restart from '../services/restart';
|
||||
const packageInfo = require('../packageInfo.js');
|
||||
@@ -21,6 +21,7 @@ interface State {
|
||||
error: Error;
|
||||
errorInfo: ErrorInfo;
|
||||
pluginInfos: PluginInfo[];
|
||||
plugins: Plugins;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
@@ -29,14 +30,16 @@ interface Props {
|
||||
|
||||
export default class ErrorBoundary extends React.Component<Props, State> {
|
||||
|
||||
public state: State = { error: null, errorInfo: null, pluginInfos: [] };
|
||||
public state: State = { error: null, errorInfo: null, pluginInfos: [], plugins: {} };
|
||||
|
||||
componentDidCatch(error: any, errorInfo: ErrorInfo) {
|
||||
public componentDidCatch(error: any, errorInfo: ErrorInfo) {
|
||||
if (typeof error === 'string') error = { message: error };
|
||||
|
||||
const pluginInfos: PluginInfo[] = [];
|
||||
let plugins: Plugins = {};
|
||||
try {
|
||||
const service = PluginService.instance();
|
||||
plugins = service.plugins;
|
||||
const pluginSettings = service.unserializePluginSettings(Setting.value('plugins.states'));
|
||||
for (const pluginId in pluginSettings) {
|
||||
const plugin = PluginService.instance().pluginById(pluginId);
|
||||
@@ -52,10 +55,10 @@ export default class ErrorBoundary extends React.Component<Props, State> {
|
||||
console.error('Could not get plugin info:', error);
|
||||
}
|
||||
|
||||
this.setState({ error, errorInfo, pluginInfos });
|
||||
this.setState({ error, errorInfo, pluginInfos, plugins });
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
public componentDidMount() {
|
||||
const onAppClose = () => {
|
||||
ipcRenderer.send('asynchronous-message', 'appCloseReply', {
|
||||
canClose: true,
|
||||
@@ -65,12 +68,12 @@ export default class ErrorBoundary extends React.Component<Props, State> {
|
||||
ipcRenderer.on('appClose', onAppClose);
|
||||
}
|
||||
|
||||
renderMessage() {
|
||||
public renderMessage() {
|
||||
const message = this.props.message || 'Joplin encountered a fatal error and could not continue.';
|
||||
return <p>{message}</p>;
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
if (this.state.error) {
|
||||
const safeMode_click = async () => {
|
||||
Setting.setValue('isSafeMode', true);
|
||||
@@ -91,7 +94,7 @@ export default class ErrorBoundary extends React.Component<Props, State> {
|
||||
output.push(
|
||||
<section key="versionInfo">
|
||||
<h2>Version info</h2>
|
||||
<pre>{versionInfo(packageInfo).message}</pre>
|
||||
<pre>{versionInfo(packageInfo, this.state.plugins).message}</pre>
|
||||
</section>
|
||||
);
|
||||
|
||||
|
@@ -1,45 +0,0 @@
|
||||
const React = require('react');
|
||||
const bridge = require('@electron/remote').require('./bridge').default;
|
||||
const styleSelector = require('./style/ExtensionBadge');
|
||||
const { _ } = require('@joplin/lib/locale');
|
||||
|
||||
function platformAssets(type) {
|
||||
if (type === 'firefox') {
|
||||
return {
|
||||
logoImage: `${bridge().buildDir()}/images/firefox-logo.svg`,
|
||||
locationLabel: _('Firefox Extension'),
|
||||
};
|
||||
}
|
||||
|
||||
if (type === 'chrome') {
|
||||
return {
|
||||
logoImage: `${bridge().buildDir()}/images/chrome-logo.svg`,
|
||||
locationLabel: _('Chrome Web Store'),
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`Invalid type:${type}`);
|
||||
}
|
||||
|
||||
function ExtensionBadge(props) {
|
||||
const style = styleSelector(null, props);
|
||||
const assets = platformAssets(props.type);
|
||||
|
||||
const onClick = () => {
|
||||
bridge().openExternal(props.url);
|
||||
};
|
||||
|
||||
const rootStyle = props.style ? Object.assign({}, style.root, props.style) : style.root;
|
||||
|
||||
return (
|
||||
<a style={rootStyle} onClick={onClick} href="#">
|
||||
<img style={style.logo} src={assets.logoImage}/>
|
||||
<div style={style.labelGroup} >
|
||||
<div>{_('Get it now:')}</div>
|
||||
<div style={style.locationLabel}>{assets.locationLabel}</div>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = ExtensionBadge;
|
98
packages/app-desktop/gui/ExtensionBadge.tsx
Normal file
@@ -0,0 +1,98 @@
|
||||
import * as React from 'react';
|
||||
import bridge from '../services/bridge';
|
||||
import { _ } from '@joplin/lib/locale';
|
||||
import { themeStyle } from '@joplin/lib/theme';
|
||||
const { createSelector } = require('reselect');
|
||||
|
||||
interface Props {
|
||||
themeId: number;
|
||||
type: string;
|
||||
url: string;
|
||||
style?: any;
|
||||
}
|
||||
|
||||
const themeSelector = (_state: any, props: any) => themeStyle(props.themeId);
|
||||
|
||||
const styleSelector = createSelector(
|
||||
themeSelector,
|
||||
(theme: any) => {
|
||||
const output = {
|
||||
root: {
|
||||
width: 220,
|
||||
height: 60,
|
||||
borderRadius: 4,
|
||||
border: '1px solid',
|
||||
borderColor: theme.dividerColor,
|
||||
backgroundColor: theme.backgroundColor,
|
||||
paddingLeft: 14,
|
||||
paddingRight: 14,
|
||||
paddingTop: 8,
|
||||
paddingBottom: 8,
|
||||
boxSizing: 'border-box',
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
boxShadow: '0px 1px 1px rgba(0,0,0,0.3)',
|
||||
},
|
||||
logo: {
|
||||
width: 42,
|
||||
height: 42,
|
||||
},
|
||||
labelGroup: {
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
marginLeft: 14,
|
||||
fontFamily: theme.fontFamily,
|
||||
color: theme.color,
|
||||
fontSize: theme.fontSize,
|
||||
},
|
||||
locationLabel: {
|
||||
fontSize: theme.fontSize * 1.2,
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
};
|
||||
|
||||
return output;
|
||||
}
|
||||
);
|
||||
|
||||
function platformAssets(type: string) {
|
||||
if (type === 'firefox') {
|
||||
return {
|
||||
logoImage: `${bridge().buildDir()}/images/firefox-logo.svg`,
|
||||
locationLabel: _('Firefox Extension'),
|
||||
};
|
||||
}
|
||||
|
||||
if (type === 'chrome') {
|
||||
return {
|
||||
logoImage: `${bridge().buildDir()}/images/chrome-logo.svg`,
|
||||
locationLabel: _('Chrome Web Store'),
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`Invalid type:${type}`);
|
||||
}
|
||||
|
||||
function ExtensionBadge(props: Props) {
|
||||
const style = styleSelector(null, props);
|
||||
const assets = platformAssets(props.type);
|
||||
|
||||
const onClick = () => {
|
||||
void bridge().openExternal(props.url);
|
||||
};
|
||||
|
||||
const rootStyle = props.style ? Object.assign({}, style.root, props.style) : style.root;
|
||||
|
||||
return (
|
||||
<a style={rootStyle} onClick={onClick} href="#">
|
||||
<img style={style.logo} src={assets.logoImage}/>
|
||||
<div style={style.labelGroup} >
|
||||
<div>{_('Get it now:')}</div>
|
||||
<div style={style.locationLabel}>{assets.locationLabel}</div>
|
||||
</div>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
export default ExtensionBadge;
|
@@ -1,23 +1,31 @@
|
||||
const React = require('react');
|
||||
import * as React from 'react';
|
||||
const { connect } = require('react-redux');
|
||||
const { themeStyle } = require('@joplin/lib/theme');
|
||||
import { themeStyle } from '@joplin/lib/theme';
|
||||
import { AppState } from '../app.reducer';
|
||||
|
||||
class HelpButtonComponent extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
interface Props {
|
||||
tip: string;
|
||||
onClick: Function;
|
||||
themeId: number;
|
||||
style: any;
|
||||
}
|
||||
|
||||
class HelpButtonComponent extends React.Component<Props> {
|
||||
public constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.onClick = this.onClick.bind(this);
|
||||
}
|
||||
|
||||
onClick() {
|
||||
public onClick() {
|
||||
if (this.props.onClick) this.props.onClick();
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const theme = themeStyle(this.props.themeId);
|
||||
const style = Object.assign({}, this.props.style, { color: theme.color, textDecoration: 'none' });
|
||||
const helpIconStyle = { flex: 0, width: 16, height: 16, marginLeft: 10 };
|
||||
const extraProps = {};
|
||||
const extraProps: any = {};
|
||||
if (this.props.tip) extraProps['data-tip'] = this.props.tip;
|
||||
return (
|
||||
<a href="#" style={style} onClick={this.onClick} {...extraProps}>
|
||||
@@ -27,7 +35,7 @@ class HelpButtonComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => {
|
||||
const mapStateToProps = (state: AppState) => {
|
||||
return {
|
||||
themeId: state.settings.theme,
|
||||
};
|
||||
@@ -35,4 +43,4 @@ const mapStateToProps = state => {
|
||||
|
||||
const HelpButton = connect(mapStateToProps)(HelpButtonComponent);
|
||||
|
||||
module.exports = HelpButton;
|
||||
export default HelpButton;
|