You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-09-05 20:56:22 +02:00
Compare commits
94 Commits
cli-v2.13.
...
cli-v2.13.
Author | SHA1 | Date | |
---|---|---|---|
|
9bad668cc5 | ||
|
c18c31ab7f | ||
|
7c24a2f4be | ||
|
56438ea644 | ||
|
7f9bc1e15c | ||
|
b1c8cb5632 | ||
|
f0a1b41794 | ||
|
02982464a6 | ||
|
62e317db05 | ||
|
e0795748a9 | ||
|
67070ed3d5 | ||
|
fec8c6131c | ||
|
24ed5bda63 | ||
|
dbb354ad10 | ||
|
92dccbe98d | ||
|
9b775d77f6 | ||
|
4fd6937d05 | ||
|
7230f0e698 | ||
|
ada82538ee | ||
|
e7dd981db6 | ||
|
767bf9f002 | ||
|
f698068587 | ||
|
d0955b4ca2 | ||
|
18e86a7ba3 | ||
|
f9a1ab4d40 | ||
|
062d0898a0 | ||
|
3b51b4fd72 | ||
|
1a78ff4398 | ||
|
544af8d118 | ||
|
2616c377a9 | ||
|
4a63331306 | ||
|
48621443ec | ||
|
79fd66b94c | ||
|
6a6c8c1d83 | ||
|
cf19dacbaf | ||
|
50925abc40 | ||
|
c80cbaa32f | ||
|
f7cb1aef4b | ||
|
96d5d1dfab | ||
|
98d608fec5 | ||
|
1af46b0246 | ||
|
1e530b74d4 | ||
|
e61c4acce5 | ||
|
184499711d | ||
|
2c0181d097 | ||
|
06ea12adb3 | ||
|
9923e5c821 | ||
|
9a06e59cfe | ||
|
80a2cd91f4 | ||
|
df9ed3e487 | ||
|
368d0130f6 | ||
|
824e1b44dd | ||
|
ccf1c8ee31 | ||
|
d5f6d83f6d | ||
|
5d422f85c8 | ||
|
78aeb46d56 | ||
|
091bf45149 | ||
|
8d9d24740b | ||
|
21e5f88cb2 | ||
|
5d4259d064 | ||
|
9ac03ec33a | ||
|
e760276341 | ||
|
206f35ffe5 | ||
|
ddf716479d | ||
|
ec7f94df25 | ||
|
bcbba0973f | ||
|
bd1ddb8522 | ||
|
fb47398554 | ||
|
10356f4009 | ||
|
ba83fca47a | ||
|
b01295f0fd | ||
|
b928e614cc | ||
|
973b9c354c | ||
|
c12444d6e8 | ||
|
1401d28f82 | ||
|
335269f92d | ||
|
6211606a22 | ||
|
5f7d438ac1 | ||
|
ee2df96cfb | ||
|
692e925997 | ||
|
39803f53a0 | ||
|
b3591808b7 | ||
|
2427677fd5 | ||
|
9a051effcd | ||
|
e6e9f92e01 | ||
|
ca6762c891 | ||
|
76d07beb27 | ||
|
f3daa7f0e4 | ||
|
041ad22443 | ||
|
6cd0938ee4 | ||
|
c3dc30ee5d | ||
|
37c925dcf2 | ||
|
05bd51f85c | ||
|
cfbc37df8d |
@@ -383,6 +383,8 @@ packages/app-desktop/integration-tests/models/MainScreen.js
|
|||||||
packages/app-desktop/integration-tests/models/NoteEditorScreen.js
|
packages/app-desktop/integration-tests/models/NoteEditorScreen.js
|
||||||
packages/app-desktop/integration-tests/models/SettingsScreen.js
|
packages/app-desktop/integration-tests/models/SettingsScreen.js
|
||||||
packages/app-desktop/integration-tests/util/activateMainMenuItem.js
|
packages/app-desktop/integration-tests/util/activateMainMenuItem.js
|
||||||
|
packages/app-desktop/integration-tests/util/createStartupArgs.js
|
||||||
|
packages/app-desktop/integration-tests/util/firstNonDevToolsWindow.js
|
||||||
packages/app-desktop/integration-tests/util/test.js
|
packages/app-desktop/integration-tests/util/test.js
|
||||||
packages/app-desktop/playwright.config.js
|
packages/app-desktop/playwright.config.js
|
||||||
packages/app-desktop/plugins/GotoAnything.js
|
packages/app-desktop/plugins/GotoAnything.js
|
||||||
@@ -508,7 +510,10 @@ packages/app-mobile/services/profiles/index.js
|
|||||||
packages/app-mobile/services/voiceTyping/vosk.android.js
|
packages/app-mobile/services/voiceTyping/vosk.android.js
|
||||||
packages/app-mobile/services/voiceTyping/vosk.ios.js
|
packages/app-mobile/services/voiceTyping/vosk.ios.js
|
||||||
packages/app-mobile/setupQuickActions.js
|
packages/app-mobile/setupQuickActions.js
|
||||||
packages/app-mobile/tools/buildInjectedJs.js
|
packages/app-mobile/tools/buildInjectedJs/BundledFile.js
|
||||||
|
packages/app-mobile/tools/buildInjectedJs/constants.js
|
||||||
|
packages/app-mobile/tools/buildInjectedJs/copyJs.js
|
||||||
|
packages/app-mobile/tools/buildInjectedJs/gulpTasks.js
|
||||||
packages/app-mobile/utils/ShareExtension.js
|
packages/app-mobile/utils/ShareExtension.js
|
||||||
packages/app-mobile/utils/ShareUtils.test.js
|
packages/app-mobile/utils/ShareUtils.test.js
|
||||||
packages/app-mobile/utils/ShareUtils.js
|
packages/app-mobile/utils/ShareUtils.js
|
||||||
|
@@ -157,6 +157,8 @@ module.exports = {
|
|||||||
// In user-facing text, it should be "notebook".
|
// In user-facing text, it should be "notebook".
|
||||||
'id-denylist': ['error', 'err', 'notebook', 'notebooks'],
|
'id-denylist': ['error', 'err', 'notebook', 'notebooks'],
|
||||||
'prefer-arrow-callback': ['error'],
|
'prefer-arrow-callback': ['error'],
|
||||||
|
|
||||||
|
'no-constant-binary-expression': ['error'],
|
||||||
},
|
},
|
||||||
'plugins': [
|
'plugins': [
|
||||||
'react',
|
'react',
|
||||||
|
62
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
Normal file
62
.github/ISSUE_TEMPLATE/BUG_REPORT.yml
vendored
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
name: Bug Report
|
||||||
|
description: Report a reproducible bug or regression in Joplin.
|
||||||
|
labels: ['bug']
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Thanks for taking the time to fill out this bug report!
|
||||||
|
|
||||||
|
- type: dropdown
|
||||||
|
id: os
|
||||||
|
attributes:
|
||||||
|
label: "Operating system"
|
||||||
|
multiple: false
|
||||||
|
options:
|
||||||
|
- "Windows"
|
||||||
|
- "macOS"
|
||||||
|
- "Linux"
|
||||||
|
- "Android"
|
||||||
|
- "iOS"
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: input
|
||||||
|
id: version
|
||||||
|
attributes:
|
||||||
|
label: "Joplin version"
|
||||||
|
placeholder: "For example 2.3.6"
|
||||||
|
description:
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: desktop-about-content
|
||||||
|
attributes:
|
||||||
|
label: "Desktop version info"
|
||||||
|
description: "If this issue is about the **desktop app**, please open the \"About\" dialog under the \"Help\" or \"Joplin\" menu and copy its content here."
|
||||||
|
placeholder: "Joplin 2.13.5 (dev, darwin)\n\nClient ID: ..."
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: current
|
||||||
|
attributes:
|
||||||
|
label: Current behaviour
|
||||||
|
description: What did Joplin do? Include screenshots and video recordings for UI problems if needed. If you are reporting a clipper bug, please include an example URL that shows the issue.
|
||||||
|
placeholder: |
|
||||||
|
1. This
|
||||||
|
2. Then that
|
||||||
|
3. Then this
|
||||||
|
4. Etc.
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: expected
|
||||||
|
attributes:
|
||||||
|
label: Expected behaviour
|
||||||
|
description: What did you expect Joplin to do?
|
||||||
|
|
||||||
|
- type: textarea
|
||||||
|
id: logs
|
||||||
|
attributes:
|
||||||
|
label: Logs
|
||||||
|
description: "If relevant, please provide a log file as described here: https://joplinapp.org/help/apps/debugging"
|
52
.github/ISSUE_TEMPLATE/bug_report.md
vendored
52
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@@ -1,52 +0,0 @@
|
|||||||
---
|
|
||||||
name: "\U0001F41B Bug Report"
|
|
||||||
about: Report a reproducible bug or regression in Joplin.
|
|
||||||
title: ''
|
|
||||||
labels: bug
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Please provide a clear and concise description of what the bug is. (In the section Steps To Reproduce.)
|
|
||||||
Include screenshots for UI problems if needed.
|
|
||||||
DO NOT create screenshots of text !!! Copy and paste the text into a code block.
|
|
||||||
Please test using the latest Joplin release to make sure your issue has not already been fixed.
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
IMPORTANT: If you are reporting a clipper bug, please include an example URL that shows the issue.
|
|
||||||
Without the URL the issue is likely to be closed.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Environment
|
|
||||||
|
|
||||||
Joplin version:
|
|
||||||
Platform:
|
|
||||||
OS specifics:
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Platform can be one of: macOS, Linux, Windows, Android, iOS, terminal (or a combination)
|
|
||||||
OS specifics: e.g. OS version, Linux distribution, Android/iOS version...
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Steps to reproduce
|
|
||||||
|
|
||||||
1.
|
|
||||||
2.
|
|
||||||
3.
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Issues without reproduction steps are likely to stall.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## Describe what you expected to happen
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Logfile
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Please attach a debug log. Issues without a debug log are likely to stall.
|
|
||||||
For information on how to collect a log file: https://joplinapp.org/help/apps/debugging/
|
|
||||||
-->
|
|
9
.github/ISSUE_TEMPLATE/config.yml
vendored
9
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +1,8 @@
|
|||||||
blank_issues_enabled: false
|
blank_issues_enabled: false
|
||||||
contact_links:
|
contact_links:
|
||||||
- name: "\U0001F914 Feature requests and support"
|
- name: Feature Requests
|
||||||
url: https://discourse.joplinapp.org/
|
url: https://discourse.joplinapp.org/c/features/
|
||||||
about: I have a question or feature request …
|
about: Discuss ideas for new features or changes
|
||||||
|
- name: Support
|
||||||
|
url: https://discourse.joplinapp.org/c/support/
|
||||||
|
about: Please ask for help here
|
4
.github/scripts/run_ci.sh
vendored
4
.github/scripts/run_ci.sh
vendored
@@ -75,6 +75,10 @@ if [ "$IS_PULL_REQUEST" == "1" ] || [ "$IS_DEV_BRANCH" = "1" ]; then
|
|||||||
if [ "$IS_LINUX" == "1" ]; then
|
if [ "$IS_LINUX" == "1" ]; then
|
||||||
echo "Running Joplin Server tests using PostgreSQL..."
|
echo "Running Joplin Server tests using PostgreSQL..."
|
||||||
sudo docker-compose --file docker-compose.db-dev.yml up -d
|
sudo docker-compose --file docker-compose.db-dev.yml up -d
|
||||||
|
cmdResult=$?
|
||||||
|
if [ $cmdResult -ne 0 ]; then
|
||||||
|
exit $cmdResult
|
||||||
|
fi
|
||||||
export JOPLIN_TESTS_SERVER_DB=pg
|
export JOPLIN_TESTS_SERVER_DB=pg
|
||||||
else
|
else
|
||||||
echo "Running Joplin Server tests using SQLite..."
|
echo "Running Joplin Server tests using SQLite..."
|
||||||
|
7
.gitignore
vendored
7
.gitignore
vendored
@@ -365,6 +365,8 @@ packages/app-desktop/integration-tests/models/MainScreen.js
|
|||||||
packages/app-desktop/integration-tests/models/NoteEditorScreen.js
|
packages/app-desktop/integration-tests/models/NoteEditorScreen.js
|
||||||
packages/app-desktop/integration-tests/models/SettingsScreen.js
|
packages/app-desktop/integration-tests/models/SettingsScreen.js
|
||||||
packages/app-desktop/integration-tests/util/activateMainMenuItem.js
|
packages/app-desktop/integration-tests/util/activateMainMenuItem.js
|
||||||
|
packages/app-desktop/integration-tests/util/createStartupArgs.js
|
||||||
|
packages/app-desktop/integration-tests/util/firstNonDevToolsWindow.js
|
||||||
packages/app-desktop/integration-tests/util/test.js
|
packages/app-desktop/integration-tests/util/test.js
|
||||||
packages/app-desktop/playwright.config.js
|
packages/app-desktop/playwright.config.js
|
||||||
packages/app-desktop/plugins/GotoAnything.js
|
packages/app-desktop/plugins/GotoAnything.js
|
||||||
@@ -490,7 +492,10 @@ packages/app-mobile/services/profiles/index.js
|
|||||||
packages/app-mobile/services/voiceTyping/vosk.android.js
|
packages/app-mobile/services/voiceTyping/vosk.android.js
|
||||||
packages/app-mobile/services/voiceTyping/vosk.ios.js
|
packages/app-mobile/services/voiceTyping/vosk.ios.js
|
||||||
packages/app-mobile/setupQuickActions.js
|
packages/app-mobile/setupQuickActions.js
|
||||||
packages/app-mobile/tools/buildInjectedJs.js
|
packages/app-mobile/tools/buildInjectedJs/BundledFile.js
|
||||||
|
packages/app-mobile/tools/buildInjectedJs/constants.js
|
||||||
|
packages/app-mobile/tools/buildInjectedJs/copyJs.js
|
||||||
|
packages/app-mobile/tools/buildInjectedJs/gulpTasks.js
|
||||||
packages/app-mobile/utils/ShareExtension.js
|
packages/app-mobile/utils/ShareExtension.js
|
||||||
packages/app-mobile/utils/ShareUtils.test.js
|
packages/app-mobile/utils/ShareUtils.test.js
|
||||||
packages/app-mobile/utils/ShareUtils.js
|
packages/app-mobile/utils/ShareUtils.js
|
||||||
|
@@ -205,9 +205,16 @@ if command -v lsb_release &> /dev/null; then
|
|||||||
# Check for "The SUID sandbox helper binary was found, but is not configured correctly" problem.
|
# Check for "The SUID sandbox helper binary was found, but is not configured correctly" problem.
|
||||||
# It is present in Debian 1X. A (temporary) patch will be applied at .desktop file
|
# It is present in Debian 1X. A (temporary) patch will be applied at .desktop file
|
||||||
# Linux Mint 4 Debbie is based on Debian 10 and requires the same param handling.
|
# Linux Mint 4 Debbie is based on Debian 10 and requires the same param handling.
|
||||||
if [[ $DISTVER =~ Debian1. ]] || [ "$DISTVER" = "Linuxmint4" ] && [ "$DISTCODENAME" = "debbie" ] || [ "$DISTVER" = "CentOS" ] && [[ "$DISTMAJOR" =~ 6|7 ]]
|
#
|
||||||
|
# This also works around Ubuntu 23.10+'s restrictions on unprivileged user namespaces. Electron
|
||||||
|
# uses these to sandbox processes. Unfortunately, it doesn't look like we can get around this
|
||||||
|
# without writing the AppImage to a non-user-writable location (without invalidating other security
|
||||||
|
# controls). See https://discourse.joplinapp.org/t/possible-future-requirement-for-no-sandbox-flag-for-ubuntu-23-10/.
|
||||||
|
if [[ $DISTVER = "Ubuntu23.10" || $DISTVER =~ Debian1. || ( "$DISTVER" = "Linuxmint4" && "$DISTCODENAME" = "debbie" ) || ( "$DISTVER" = "CentOS" && "$DISTMAJOR" =~ 6|7 ) ]]
|
||||||
then
|
then
|
||||||
SANDBOXPARAM="--no-sandbox"
|
SANDBOXPARAM="--no-sandbox"
|
||||||
|
print "${COLOR_YELLOW}WARNING${COLOR_RESET} Electron sandboxing disabled."
|
||||||
|
print " See https://discourse.joplinapp.org/t/32160/5 for details."
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@@ -42,8 +42,8 @@ Please see the [donation page](https://github.com/laurent22/joplin/blob/dev/read
|
|||||||
| <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/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/14873877?s=96&v=4"/></br>[dchecks](https://github.com/dchecks) | <img width="50" src="https://avatars2.githubusercontent.com/u/56287?s=96&v=4"/></br>[fats](https://github.com/fats) | <img width="50" src="https://avatars2.githubusercontent.com/u/8030470?s=96&v=4"/></br>[Galliver7](https://github.com/Galliver7) | <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/14873877?s=96&v=4"/></br>[dchecks](https://github.com/dchecks) | <img width="50" src="https://avatars2.githubusercontent.com/u/56287?s=96&v=4"/></br>[fats](https://github.com/fats) | <img width="50" src="https://avatars2.githubusercontent.com/u/8030470?s=96&v=4"/></br>[Galliver7](https://github.com/Galliver7) | <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/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/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/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/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/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/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/4560672?s=96&v=4"/></br>[mu88](https://github.com/mu88) | <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/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) | | |
|
| <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 -->
|
<!-- SPONSORS-GITHUB -->
|
||||||
|
|
||||||
# Community
|
# Community
|
||||||
|
@@ -6,7 +6,7 @@ version: '3'
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
image: postgres:15
|
image: postgres:16
|
||||||
command: postgres -c work_mem=100000
|
command: postgres -c work_mem=100000
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
|
@@ -18,7 +18,7 @@ services:
|
|||||||
- POSTGRES_PORT=5432
|
- POSTGRES_PORT=5432
|
||||||
- POSTGRES_HOST=localhost
|
- POSTGRES_HOST=localhost
|
||||||
db:
|
db:
|
||||||
image: postgres:15
|
image: postgres:16
|
||||||
ports:
|
ports:
|
||||||
- "5432:5432"
|
- "5432:5432"
|
||||||
environment:
|
environment:
|
||||||
|
@@ -19,7 +19,7 @@ version: '3'
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
image: postgres:15
|
image: postgres:16
|
||||||
volumes:
|
volumes:
|
||||||
- ./data/postgres:/var/lib/postgresql/data
|
- ./data/postgres:/var/lib/postgresql/data
|
||||||
ports:
|
ports:
|
||||||
|
@@ -367,6 +367,12 @@
|
|||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "cd ${workspaceFolder}/packages/server && yarn tsc",
|
"command": "cd ${workspaceFolder}/packages/server && yarn tsc",
|
||||||
"group": "build",
|
"group": "build",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "transpile-lib",
|
||||||
|
"type": "shell",
|
||||||
|
"command": "cd ${workspaceFolder}/packages/lib && yarn tsc",
|
||||||
|
"group": "build",
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -395,6 +401,19 @@
|
|||||||
"APP_BASE_URL": "http://joplincloud.local:22300",
|
"APP_BASE_URL": "http://joplincloud.local:22300",
|
||||||
"API_BASE_URL": "http://api.joplincloud.local:22300",
|
"API_BASE_URL": "http://api.joplincloud.local:22300",
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "node",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "lib: debug test file",
|
||||||
|
"preLaunchTask": "transpile-lib",
|
||||||
|
"program": "${workspaceFolder}/packages/lib/node_modules/.bin/jest",
|
||||||
|
"args": [
|
||||||
|
"${fileBasenameNoExtension}",
|
||||||
|
"--config",
|
||||||
|
"packages/lib/jest.config.js",
|
||||||
|
],
|
||||||
|
"console": "integratedTerminal",
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@@ -35,7 +35,7 @@
|
|||||||
],
|
],
|
||||||
"owner": "Laurent Cozic"
|
"owner": "Laurent Cozic"
|
||||||
},
|
},
|
||||||
"version": "2.13.1",
|
"version": "2.13.2",
|
||||||
"bin": "./main.js",
|
"bin": "./main.js",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10.0.0"
|
"node": ">=10.0.0"
|
||||||
@@ -73,7 +73,7 @@
|
|||||||
"@joplin/tools": "~2.13",
|
"@joplin/tools": "~2.13",
|
||||||
"@types/fs-extra": "11.0.3",
|
"@types/fs-extra": "11.0.3",
|
||||||
"@types/jest": "29.5.5",
|
"@types/jest": "29.5.5",
|
||||||
"@types/node": "18.18.6",
|
"@types/node": "18.18.7",
|
||||||
"@types/proper-lockfile": "^4.1.2",
|
"@types/proper-lockfile": "^4.1.2",
|
||||||
"gulp": "4.0.2",
|
"gulp": "4.0.2",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
|
@@ -36,6 +36,10 @@ describe('HtmlToMd', () => {
|
|||||||
htmlToMdOptions.preserveImageTagsWithSize = true;
|
htmlToMdOptions.preserveImageTagsWithSize = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (htmlFilename.indexOf('preserve_nested_tables') === 0) {
|
||||||
|
htmlToMdOptions.preserveNestedTables = true;
|
||||||
|
}
|
||||||
|
|
||||||
const html = await readFile(htmlPath, 'utf8');
|
const html = await readFile(htmlPath, 'utf8');
|
||||||
let expectedMd = await readFile(mdPath, 'utf8');
|
let expectedMd = await readFile(mdPath, 'utf8');
|
||||||
|
|
||||||
|
@@ -0,0 +1,19 @@
|
|||||||
|
<body>
|
||||||
|
<table border="5px" bordercolor="#8707B0">
|
||||||
|
<tr>
|
||||||
|
<td>Left side of the main table</td>
|
||||||
|
<td>
|
||||||
|
<table border="5px" bordercolor="#F35557">
|
||||||
|
<h4 align="center">Nested Table</h4>
|
||||||
|
<tr>
|
||||||
|
<td>nested table C1</td>
|
||||||
|
<td>nested table C2</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>nested table</td>
|
||||||
|
<td>nested table</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
@@ -0,0 +1 @@
|
|||||||
|
<table border="5px" bordercolor="#8707B0"><tbody><tr><td>Left side of the main table</td><td><h4 align="center">Nested Table</h4><table border="5px" bordercolor="#F35557"><tbody><tr><td>nested table C1</td><td>nested table C2</td></tr><tr><td>nested table</td><td>nested table</td></tr></tbody></table></td></tr></tbody></table>
|
@@ -26,7 +26,7 @@ const { FoldersScreenUtils } = require('@joplin/lib/folders-screen-utils.js');
|
|||||||
import Folder from '@joplin/lib/models/Folder';
|
import Folder from '@joplin/lib/models/Folder';
|
||||||
import Tag from '@joplin/lib/models/Tag';
|
import Tag from '@joplin/lib/models/Tag';
|
||||||
import { reg } from '@joplin/lib/registry';
|
import { reg } from '@joplin/lib/registry';
|
||||||
const packageInfo = require('./packageInfo.js');
|
const packageInfo: PackageInfo = require('./packageInfo.js');
|
||||||
import DecryptionWorker from '@joplin/lib/services/DecryptionWorker';
|
import DecryptionWorker from '@joplin/lib/services/DecryptionWorker';
|
||||||
import ClipperServer from '@joplin/lib/ClipperServer';
|
import ClipperServer from '@joplin/lib/ClipperServer';
|
||||||
const { webFrame } = require('electron');
|
const { webFrame } = require('electron');
|
||||||
@@ -68,6 +68,7 @@ import path = require('path');
|
|||||||
import { checkPreInstalledDefaultPlugins, installDefaultPlugins, setSettingsForDefaultPlugins } from '@joplin/lib/services/plugins/defaultPlugins/defaultPluginsUtils';
|
import { checkPreInstalledDefaultPlugins, installDefaultPlugins, setSettingsForDefaultPlugins } from '@joplin/lib/services/plugins/defaultPlugins/defaultPluginsUtils';
|
||||||
import userFetcher, { initializeUserFetcher } from '@joplin/lib/utils/userFetcher';
|
import userFetcher, { initializeUserFetcher } from '@joplin/lib/utils/userFetcher';
|
||||||
import { parseNotesParent } from '@joplin/lib/reducer';
|
import { parseNotesParent } from '@joplin/lib/reducer';
|
||||||
|
import { PackageInfo } from '@joplin/lib/versionInfo';
|
||||||
|
|
||||||
const pluginClasses = [
|
const pluginClasses = [
|
||||||
require('./plugins/GotoAnything').default,
|
require('./plugins/GotoAnything').default,
|
||||||
|
@@ -5,8 +5,9 @@ import bridge from './services/bridge';
|
|||||||
import KvStore from '@joplin/lib/services/KvStore';
|
import KvStore from '@joplin/lib/services/KvStore';
|
||||||
import * as ArrayUtils from '@joplin/lib/ArrayUtils';
|
import * as ArrayUtils from '@joplin/lib/ArrayUtils';
|
||||||
import { CheckForUpdateOptions, extractVersionInfo, GitHubRelease } from './utils/checkForUpdatesUtils';
|
import { CheckForUpdateOptions, extractVersionInfo, GitHubRelease } from './utils/checkForUpdatesUtils';
|
||||||
const packageInfo = require('./packageInfo.js');
|
import { PackageInfo } from '@joplin/lib/versionInfo';
|
||||||
import { compareVersions } from 'compare-versions';
|
import { compareVersions } from 'compare-versions';
|
||||||
|
const packageInfo: PackageInfo = require('./packageInfo.js');
|
||||||
|
|
||||||
const logger = Logger.create('checkForUpdates');
|
const logger = Logger.create('checkForUpdates');
|
||||||
|
|
||||||
|
@@ -92,7 +92,15 @@ export default function Sidebar(props: Props) {
|
|||||||
function renderButton(section: any) {
|
function renderButton(section: any) {
|
||||||
const selected = props.selection === section.name;
|
const selected = props.selection === section.name;
|
||||||
return (
|
return (
|
||||||
<StyledListItem key={section.name} isSubSection={Setting.isSubSection(section.name)} selected={selected} onClick={() => { props.onSelectionChange({ section: section }); }}>
|
<StyledListItem
|
||||||
|
key={section.name}
|
||||||
|
href='#'
|
||||||
|
role='tab'
|
||||||
|
aria-selected={selected}
|
||||||
|
isSubSection={Setting.isSubSection(section.name)}
|
||||||
|
selected={selected}
|
||||||
|
onClick={() => { props.onSelectionChange({ section: section }); }}
|
||||||
|
>
|
||||||
<StyledListItemIcon
|
<StyledListItemIcon
|
||||||
className={Setting.sectionNameToIcon(section.name, AppType.Desktop)}
|
className={Setting.sectionNameToIcon(section.name, AppType.Desktop)}
|
||||||
/>
|
/>
|
||||||
@@ -123,7 +131,7 @@ export default function Sidebar(props: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledRoot>
|
<StyledRoot role='tablist'>
|
||||||
{buttons}
|
{buttons}
|
||||||
</StyledRoot>
|
</StyledRoot>
|
||||||
);
|
);
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import versionInfo from '@joplin/lib/versionInfo';
|
import versionInfo, { PackageInfo } from '@joplin/lib/versionInfo';
|
||||||
import PluginService, { Plugins } from '@joplin/lib/services/plugins/PluginService';
|
import PluginService, { Plugins } from '@joplin/lib/services/plugins/PluginService';
|
||||||
import Setting from '@joplin/lib/models/Setting';
|
import Setting from '@joplin/lib/models/Setting';
|
||||||
import restart from '../services/restart';
|
import restart from '../services/restart';
|
||||||
const packageInfo = require('../packageInfo.js');
|
const packageInfo: PackageInfo = require('../packageInfo.js');
|
||||||
const ipcRenderer = require('electron').ipcRenderer;
|
const ipcRenderer = require('electron').ipcRenderer;
|
||||||
|
|
||||||
interface ErrorInfo {
|
interface ErrorInfo {
|
||||||
|
@@ -8,7 +8,7 @@ import KeymapService from '@joplin/lib/services/KeymapService';
|
|||||||
import { PluginStates, utils as pluginUtils } from '@joplin/lib/services/plugins/reducer';
|
import { PluginStates, utils as pluginUtils } from '@joplin/lib/services/plugins/reducer';
|
||||||
import shim from '@joplin/lib/shim';
|
import shim from '@joplin/lib/shim';
|
||||||
import Setting from '@joplin/lib/models/Setting';
|
import Setting from '@joplin/lib/models/Setting';
|
||||||
import versionInfo from '@joplin/lib/versionInfo';
|
import versionInfo, { PackageInfo } from '@joplin/lib/versionInfo';
|
||||||
import makeDiscourseDebugUrl from '@joplin/lib/makeDiscourseDebugUrl';
|
import makeDiscourseDebugUrl from '@joplin/lib/makeDiscourseDebugUrl';
|
||||||
import { ImportModule } from '@joplin/lib/services/interop/Module';
|
import { ImportModule } from '@joplin/lib/services/interop/Module';
|
||||||
import InteropServiceHelper from '../InteropServiceHelper';
|
import InteropServiceHelper from '../InteropServiceHelper';
|
||||||
@@ -25,7 +25,7 @@ import { ProfileConfig } from '@joplin/lib/services/profileConfig/types';
|
|||||||
import PluginService, { PluginSettings } from '@joplin/lib/services/plugins/PluginService';
|
import PluginService, { PluginSettings } from '@joplin/lib/services/plugins/PluginService';
|
||||||
import { getListRendererById, getListRendererIds } from '@joplin/lib/services/noteList/renderers';
|
import { getListRendererById, getListRendererIds } from '@joplin/lib/services/noteList/renderers';
|
||||||
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
|
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
|
||||||
const packageInfo = require('../packageInfo.js');
|
const packageInfo: PackageInfo = require('../packageInfo.js');
|
||||||
const { clipboard } = require('electron');
|
const { clipboard } = require('electron');
|
||||||
const Menu = bridge().Menu;
|
const Menu = bridge().Menu;
|
||||||
|
|
||||||
|
@@ -559,11 +559,21 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
|
|||||||
|
|
||||||
const toolbarPluginButtons = pluginCommandNames.length ? ` | ${pluginCommandNames.join(' ')}` : '';
|
const toolbarPluginButtons = pluginCommandNames.length ? ` | ${pluginCommandNames.join(' ')}` : '';
|
||||||
|
|
||||||
|
// The toolbar is going to wrap based on groups of buttons
|
||||||
|
// (delimited by |). It means that if we leave large groups of
|
||||||
|
// buttons towards the end of the toolbar it's going to needlessly
|
||||||
|
// hide many buttons even when there is space. So this is why below,
|
||||||
|
// we create small groups of just one button towards the end.
|
||||||
|
|
||||||
const toolbar = [
|
const toolbar = [
|
||||||
'bold', 'italic', 'joplinHighlight', 'joplinStrikethrough', 'formattingExtras', '|',
|
'bold', 'italic', 'joplinHighlight', 'joplinStrikethrough', 'formattingExtras', '|',
|
||||||
'link', 'joplinInlineCode', 'joplinCodeBlock', 'joplinAttach', '|',
|
'link', 'joplinInlineCode', 'joplinCodeBlock', 'joplinAttach', '|',
|
||||||
'bullist', 'numlist', 'joplinChecklist', '|',
|
'bullist', 'numlist', 'joplinChecklist', '|',
|
||||||
'h1', 'h2', 'h3', 'hr', 'blockquote', 'table', `joplinInsertDateTime${toolbarPluginButtons}`,
|
'h1', 'h2', 'h3', '|',
|
||||||
|
'hr', '|',
|
||||||
|
'blockquote', '|',
|
||||||
|
'table', '|',
|
||||||
|
`joplinInsertDateTime${toolbarPluginButtons}`,
|
||||||
];
|
];
|
||||||
|
|
||||||
const editors = await (window as any).tinymce.init({
|
const editors = await (window as any).tinymce.init({
|
||||||
|
@@ -9,7 +9,10 @@ export async function htmlToMarkdown(markupLanguage: number, html: string, origi
|
|||||||
|
|
||||||
if (markupLanguage === MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN) {
|
if (markupLanguage === MarkupToHtml.MARKUP_LANGUAGE_MARKDOWN) {
|
||||||
const htmlToMd = new HtmlToMd();
|
const htmlToMd = new HtmlToMd();
|
||||||
newBody = htmlToMd.parse(html, { preserveImageTagsWithSize: true });
|
newBody = htmlToMd.parse(html, {
|
||||||
|
preserveImageTagsWithSize: true,
|
||||||
|
preserveNestedTables: true,
|
||||||
|
});
|
||||||
newBody = await Note.replaceResourceExternalToInternalLinks(newBody, { useAbsolutePaths: true });
|
newBody = await Note.replaceResourceExternalToInternalLinks(newBody, { useAbsolutePaths: true });
|
||||||
} else {
|
} else {
|
||||||
newBody = await Note.replaceResourceExternalToInternalLinks(html, { useAbsolutePaths: true });
|
newBody = await Note.replaceResourceExternalToInternalLinks(html, { useAbsolutePaths: true });
|
||||||
|
@@ -5,6 +5,8 @@ import SettingsScreen from './models/SettingsScreen';
|
|||||||
import { _electron as electron } from '@playwright/test';
|
import { _electron as electron } from '@playwright/test';
|
||||||
import { writeFile } from 'fs-extra';
|
import { writeFile } from 'fs-extra';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
import createStartupArgs from './util/createStartupArgs';
|
||||||
|
import firstNonDevToolsWindow from './util/firstNonDevToolsWindow';
|
||||||
|
|
||||||
|
|
||||||
test.describe('main', () => {
|
test.describe('main', () => {
|
||||||
@@ -130,11 +132,9 @@ test.describe('main', () => {
|
|||||||
|
|
||||||
// We need to write to the force-safe-mode file before opening the Electron app.
|
// We need to write to the force-safe-mode file before opening the Electron app.
|
||||||
// Open the app ourselves:
|
// Open the app ourselves:
|
||||||
const startupArgs = [
|
const startupArgs = createStartupArgs(profileDirectory);
|
||||||
'main.js', '--env', 'dev', '--profile', profileDirectory,
|
|
||||||
];
|
|
||||||
const electronApp = await electron.launch({ args: startupArgs });
|
const electronApp = await electron.launch({ args: startupArgs });
|
||||||
const mainWindow = await electronApp.firstWindow();
|
const mainWindow = await firstNonDevToolsWindow(electronApp);
|
||||||
|
|
||||||
const safeModeDisableLink = mainWindow.getByText('Disable safe mode and restart');
|
const safeModeDisableLink = mainWindow.getByText('Disable safe mode and restart');
|
||||||
await safeModeDisableLink.waitFor();
|
await safeModeDisableLink.waitFor();
|
||||||
|
@@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
const createStartupArgs = (profileDirectory: string) => {
|
||||||
|
// We need to run with --env dev to disable the single instance check.
|
||||||
|
return [
|
||||||
|
'main.js', '--env', 'dev', '--profile', profileDirectory,
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
export default createStartupArgs;
|
@@ -0,0 +1,43 @@
|
|||||||
|
import { ElectronApplication, Page } from '@playwright/test';
|
||||||
|
|
||||||
|
const isDevTools = async (page: Page) => {
|
||||||
|
// It seems that the developer tools window can have titles in different
|
||||||
|
// formats (e.g. DevTools, Developer Tools).
|
||||||
|
return (await page.title()).match(/Dev(eloper)?\s*Tools/i);
|
||||||
|
};
|
||||||
|
|
||||||
|
const firstNonDevToolsWindow = async (electronApp: ElectronApplication) => {
|
||||||
|
// Wait for the window event as soon as possible -- it's possible that
|
||||||
|
// the window we want will be shown while doing other async checks.
|
||||||
|
const nextNonDevToolsPage = electronApp.waitForEvent('window', {
|
||||||
|
predicate: async page => {
|
||||||
|
return !(await isDevTools(page));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// First use firstWindow -- it's possible that the first window
|
||||||
|
// has already been shown.
|
||||||
|
let mainWindow = await electronApp.firstWindow();
|
||||||
|
|
||||||
|
if (await isDevTools(mainWindow)) {
|
||||||
|
for (const window of electronApp.windows()) {
|
||||||
|
if (!(await isDevTools(window))) {
|
||||||
|
mainWindow = window;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await isDevTools(mainWindow)) {
|
||||||
|
mainWindow = await nextNonDevToolsPage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// waitForEvent will throw if no additional windows are created.
|
||||||
|
// Ignore.
|
||||||
|
// eslint-disable-next-line promise/prefer-await-to-then
|
||||||
|
nextNonDevToolsPage.catch(_error => {});
|
||||||
|
|
||||||
|
return mainWindow;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default firstNonDevToolsWindow;
|
@@ -2,6 +2,8 @@ import { resolve, join, dirname } from 'path';
|
|||||||
import { remove, mkdirp } from 'fs-extra';
|
import { remove, mkdirp } from 'fs-extra';
|
||||||
import { _electron as electron, Page, ElectronApplication, test as base } from '@playwright/test';
|
import { _electron as electron, Page, ElectronApplication, test as base } from '@playwright/test';
|
||||||
import uuid from '@joplin/lib/uuid';
|
import uuid from '@joplin/lib/uuid';
|
||||||
|
import createStartupArgs from './createStartupArgs';
|
||||||
|
import firstNonDevToolsWindow from './firstNonDevToolsWindow';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -32,9 +34,7 @@ export const test = base.extend<JoplinFixtures>({
|
|||||||
},
|
},
|
||||||
|
|
||||||
electronApp: async ({ profileDirectory }, use) => {
|
electronApp: async ({ profileDirectory }, use) => {
|
||||||
const startupArgs = [
|
const startupArgs = createStartupArgs(profileDirectory);
|
||||||
'main.js', '--env', 'dev', '--profile', profileDirectory,
|
|
||||||
];
|
|
||||||
const electronApp = await electron.launch({ args: startupArgs });
|
const electronApp = await electron.launch({ args: startupArgs });
|
||||||
|
|
||||||
await use(electronApp);
|
await use(electronApp);
|
||||||
@@ -44,8 +44,8 @@ export const test = base.extend<JoplinFixtures>({
|
|||||||
},
|
},
|
||||||
|
|
||||||
mainWindow: async ({ electronApp }, use) => {
|
mainWindow: async ({ electronApp }, use) => {
|
||||||
const window = await electronApp.firstWindow();
|
const mainWindow = await firstNonDevToolsWindow(electronApp);
|
||||||
await use(window);
|
await use(mainWindow);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@joplin/app-desktop",
|
"name": "@joplin/app-desktop",
|
||||||
"version": "2.13.5",
|
"version": "2.13.7",
|
||||||
"description": "Joplin for Desktop",
|
"description": "Joplin for Desktop",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
@@ -120,8 +120,8 @@
|
|||||||
"@playwright/test": "1.38.1",
|
"@playwright/test": "1.38.1",
|
||||||
"@testing-library/react-hooks": "8.0.1",
|
"@testing-library/react-hooks": "8.0.1",
|
||||||
"@types/jest": "29.5.5",
|
"@types/jest": "29.5.5",
|
||||||
"@types/node": "18.18.6",
|
"@types/node": "18.18.7",
|
||||||
"@types/react": "18.2.31",
|
"@types/react": "18.2.33",
|
||||||
"@types/react-redux": "7.1.28",
|
"@types/react-redux": "7.1.28",
|
||||||
"@types/styled-components": "5.1.29",
|
"@types/styled-components": "5.1.29",
|
||||||
"electron": "26.5.0",
|
"electron": "26.5.0",
|
||||||
|
@@ -110,8 +110,8 @@ android {
|
|||||||
applicationId "net.cozic.joplin"
|
applicationId "net.cozic.joplin"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 2097726
|
versionCode 2097729
|
||||||
versionName "2.13.6"
|
versionName "2.13.9"
|
||||||
ndk {
|
ndk {
|
||||||
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,12 @@
|
|||||||
const React = require('react');
|
const React = require('react');
|
||||||
import { useState, useCallback, useMemo } from 'react';
|
import { useState, useCallback, useMemo } from 'react';
|
||||||
|
|
||||||
const Icon = require('react-native-vector-icons/Ionicons').default;
|
|
||||||
import { FAB, Portal } from 'react-native-paper';
|
import { FAB, Portal } from 'react-native-paper';
|
||||||
import { _ } from '@joplin/lib/locale';
|
import { _ } from '@joplin/lib/locale';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
|
const Icon = require('react-native-vector-icons/Ionicons').default;
|
||||||
|
|
||||||
|
// eslint-disable-next-line no-undef -- Don't know why it says React is undefined when it's defined above
|
||||||
|
type FABGroupProps = React.ComponentProps<typeof FAB.Group>;
|
||||||
|
|
||||||
type OnButtonPress = ()=> void;
|
type OnButtonPress = ()=> void;
|
||||||
interface ButtonSpec {
|
interface ButtonSpec {
|
||||||
@@ -19,6 +21,7 @@ interface ActionButtonProps {
|
|||||||
|
|
||||||
// If not given, an "add" button will be used.
|
// If not given, an "add" button will be used.
|
||||||
mainButton?: ButtonSpec;
|
mainButton?: ButtonSpec;
|
||||||
|
dispatch: Dispatch;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultOnPress = () => {};
|
const defaultOnPress = () => {};
|
||||||
@@ -36,10 +39,12 @@ const useIcon = (iconName: string) => {
|
|||||||
|
|
||||||
const ActionButton = (props: ActionButtonProps) => {
|
const ActionButton = (props: ActionButtonProps) => {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const onMenuToggled = useCallback(
|
const onMenuToggled: FABGroupProps['onStateChange'] = useCallback(state => {
|
||||||
(state: { open: boolean }) => setOpen(state.open)
|
props.dispatch({
|
||||||
, [setOpen]);
|
type: 'SIDE_MENU_CLOSE',
|
||||||
|
});
|
||||||
|
setOpen(state.open);
|
||||||
|
}, [setOpen, props.dispatch]);
|
||||||
|
|
||||||
const actions = useMemo(() => (props.buttons ?? []).map(button => {
|
const actions = useMemo(() => (props.buttons ?? []).map(button => {
|
||||||
return {
|
return {
|
||||||
|
@@ -178,6 +178,7 @@ class Dropdown extends Component<DropdownProps, DropdownState> {
|
|||||||
onRequestClose={() => {
|
onRequestClose={() => {
|
||||||
closeList();
|
closeList();
|
||||||
}}
|
}}
|
||||||
|
supportedOrientations={['landscape', 'portrait']}
|
||||||
>
|
>
|
||||||
<TouchableWithoutFeedback
|
<TouchableWithoutFeedback
|
||||||
accessibilityElementsHidden={true}
|
accessibilityElementsHidden={true}
|
||||||
|
@@ -48,6 +48,8 @@ interface Props {
|
|||||||
// See react-native-webview's prop with the same name.
|
// See react-native-webview's prop with the same name.
|
||||||
mixedContentMode?: 'never' | 'always';
|
mixedContentMode?: 'never' | 'always';
|
||||||
|
|
||||||
|
allowFileAccessFromJs?: boolean;
|
||||||
|
|
||||||
// Initial javascript. Must evaluate to true.
|
// Initial javascript. Must evaluate to true.
|
||||||
injectedJavaScript: string;
|
injectedJavaScript: string;
|
||||||
|
|
||||||
@@ -143,6 +145,7 @@ const ExtendedWebView = (props: Props, ref: Ref<WebViewControl>) => {
|
|||||||
originWhitelist={['file://*', './*', 'http://*', 'https://*']}
|
originWhitelist={['file://*', './*', 'http://*', 'https://*']}
|
||||||
mixedContentMode={props.mixedContentMode}
|
mixedContentMode={props.mixedContentMode}
|
||||||
allowFileAccess={true}
|
allowFileAccess={true}
|
||||||
|
allowFileAccessFromFileURLs={props.allowFileAccessFromJs}
|
||||||
injectedJavaScript={props.injectedJavaScript}
|
injectedJavaScript={props.injectedJavaScript}
|
||||||
onMessage={props.onMessage}
|
onMessage={props.onMessage}
|
||||||
onError={props.onError}
|
onError={props.onError}
|
||||||
|
@@ -1,14 +1,14 @@
|
|||||||
import { useRef, useCallback } from 'react';
|
import { useRef, useCallback } from 'react';
|
||||||
|
|
||||||
import useSource from './hooks/useSource';
|
import useSource from './hooks/useSource';
|
||||||
import useOnMessage, { HandleMessageCallback, OnMarkForDownloadCallback } from './hooks/useOnMessage';
|
import useOnMessage, { HandleMessageCallback, HandleScrollCallback, OnMarkForDownloadCallback } from './hooks/useOnMessage';
|
||||||
import useOnResourceLongPress from './hooks/useOnResourceLongPress';
|
import useOnResourceLongPress from './hooks/useOnResourceLongPress';
|
||||||
|
|
||||||
const React = require('react');
|
const React = require('react');
|
||||||
import { View } from 'react-native';
|
import { View } from 'react-native';
|
||||||
import BackButtonDialogBox from '../BackButtonDialogBox';
|
import BackButtonDialogBox from '../BackButtonDialogBox';
|
||||||
import { reg } from '@joplin/lib/registry';
|
import { reg } from '@joplin/lib/registry';
|
||||||
import ExtendedWebView from '../ExtendedWebView';
|
import ExtendedWebView, { WebViewControl } from '../ExtendedWebView';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
themeId: number;
|
themeId: number;
|
||||||
@@ -18,11 +18,13 @@ interface Props {
|
|||||||
highlightedKeywords: string[];
|
highlightedKeywords: string[];
|
||||||
noteResources: any;
|
noteResources: any;
|
||||||
paddingBottom: number;
|
paddingBottom: number;
|
||||||
|
initialScroll: number|null;
|
||||||
noteHash: string;
|
noteHash: string;
|
||||||
onJoplinLinkClick: HandleMessageCallback;
|
onJoplinLinkClick: HandleMessageCallback;
|
||||||
onCheckboxChange?: HandleMessageCallback;
|
onCheckboxChange?: HandleMessageCallback;
|
||||||
onRequestEditResource?: HandleMessageCallback;
|
onRequestEditResource?: HandleMessageCallback;
|
||||||
onMarkForDownload?: OnMarkForDownloadCallback;
|
onMarkForDownload?: OnMarkForDownloadCallback;
|
||||||
|
onScroll: HandleScrollCallback;
|
||||||
onLoadEnd?: ()=> void;
|
onLoadEnd?: ()=> void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,6 +34,7 @@ const webViewStyle = {
|
|||||||
|
|
||||||
export default function NoteBodyViewer(props: Props) {
|
export default function NoteBodyViewer(props: Props) {
|
||||||
const dialogBoxRef = useRef(null);
|
const dialogBoxRef = useRef(null);
|
||||||
|
const webviewRef = useRef<WebViewControl>(null);
|
||||||
|
|
||||||
const { html, injectedJs } = useSource(
|
const { html, injectedJs } = useSource(
|
||||||
props.noteBody,
|
props.noteBody,
|
||||||
@@ -41,6 +44,7 @@ export default function NoteBodyViewer(props: Props) {
|
|||||||
props.noteResources,
|
props.noteResources,
|
||||||
props.paddingBottom,
|
props.paddingBottom,
|
||||||
props.noteHash,
|
props.noteHash,
|
||||||
|
props.initialScroll,
|
||||||
);
|
);
|
||||||
|
|
||||||
const onResourceLongPress = useOnResourceLongPress(
|
const onResourceLongPress = useOnResourceLongPress(
|
||||||
@@ -59,6 +63,7 @@ export default function NoteBodyViewer(props: Props) {
|
|||||||
onJoplinLinkClick: props.onJoplinLinkClick,
|
onJoplinLinkClick: props.onJoplinLinkClick,
|
||||||
onRequestEditResource: props.onRequestEditResource,
|
onRequestEditResource: props.onRequestEditResource,
|
||||||
onResourceLongPress,
|
onResourceLongPress,
|
||||||
|
onMainContainerScroll: props.onScroll,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -96,6 +101,7 @@ export default function NoteBodyViewer(props: Props) {
|
|||||||
return (
|
return (
|
||||||
<View style={props.style}>
|
<View style={props.style}>
|
||||||
<ExtendedWebView
|
<ExtendedWebView
|
||||||
|
ref={webviewRef}
|
||||||
webviewInstanceId='NoteBodyViewer'
|
webviewInstanceId='NoteBodyViewer'
|
||||||
themeId={props.themeId}
|
themeId={props.themeId}
|
||||||
style={webViewStyle}
|
style={webViewStyle}
|
||||||
|
@@ -3,6 +3,7 @@ import shared from '@joplin/lib/components/shared/note-screen-shared';
|
|||||||
|
|
||||||
export type HandleMessageCallback = (message: string)=> void;
|
export type HandleMessageCallback = (message: string)=> void;
|
||||||
export type OnMarkForDownloadCallback = (resource: { resourceId: string })=> void;
|
export type OnMarkForDownloadCallback = (resource: { resourceId: string })=> void;
|
||||||
|
export type HandleScrollCallback = (scrollTop: number)=> void;
|
||||||
|
|
||||||
interface MessageCallbacks {
|
interface MessageCallbacks {
|
||||||
onMarkForDownload?: OnMarkForDownloadCallback;
|
onMarkForDownload?: OnMarkForDownloadCallback;
|
||||||
@@ -10,6 +11,7 @@ interface MessageCallbacks {
|
|||||||
onResourceLongPress: HandleMessageCallback;
|
onResourceLongPress: HandleMessageCallback;
|
||||||
onRequestEditResource?: HandleMessageCallback;
|
onRequestEditResource?: HandleMessageCallback;
|
||||||
onCheckboxChange: HandleMessageCallback;
|
onCheckboxChange: HandleMessageCallback;
|
||||||
|
onMainContainerScroll: HandleScrollCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function useOnMessage(
|
export default function useOnMessage(
|
||||||
@@ -24,6 +26,7 @@ export default function useOnMessage(
|
|||||||
// Thus, useCallback should depend on each callback individually.
|
// Thus, useCallback should depend on each callback individually.
|
||||||
const {
|
const {
|
||||||
onMarkForDownload, onResourceLongPress, onCheckboxChange, onRequestEditResource, onJoplinLinkClick,
|
onMarkForDownload, onResourceLongPress, onCheckboxChange, onRequestEditResource, onJoplinLinkClick,
|
||||||
|
onMainContainerScroll,
|
||||||
} = callbacks;
|
} = callbacks;
|
||||||
|
|
||||||
return useCallback((event: any) => {
|
return useCallback((event: any) => {
|
||||||
@@ -35,10 +38,23 @@ export default function useOnMessage(
|
|||||||
// https://github.com/laurent22/joplin/issues/4494
|
// https://github.com/laurent22/joplin/issues/4494
|
||||||
const msg = event.nativeEvent.data;
|
const msg = event.nativeEvent.data;
|
||||||
|
|
||||||
// eslint-disable-next-line no-console
|
const isScrollMessage = msg.startsWith('onscroll:');
|
||||||
console.info('Got IPC message: ', msg);
|
|
||||||
|
|
||||||
if (msg.indexOf('checkboxclick:') === 0) {
|
// Scroll messages are very frequent so we avoid logging them.
|
||||||
|
if (!isScrollMessage) {
|
||||||
|
// eslint-disable-next-line no-console
|
||||||
|
console.info('Got IPC message: ', msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isScrollMessage) {
|
||||||
|
const eventData = JSON.parse(msg.substring(msg.indexOf(':') + 1));
|
||||||
|
|
||||||
|
if (typeof eventData.scrollTop !== 'number') {
|
||||||
|
throw new Error(`Invalid scroll message, ${msg}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMainContainerScroll?.(eventData.scrollTop);
|
||||||
|
} else if (msg.indexOf('checkboxclick:') === 0) {
|
||||||
const newBody = shared.toggleCheckbox(msg, noteBody);
|
const newBody = shared.toggleCheckbox(msg, noteBody);
|
||||||
onCheckboxChange?.(newBody);
|
onCheckboxChange?.(newBody);
|
||||||
} else if (msg.indexOf('markForDownload:') === 0) {
|
} else if (msg.indexOf('markForDownload:') === 0) {
|
||||||
@@ -63,5 +79,6 @@ export default function useOnMessage(
|
|||||||
onJoplinLinkClick,
|
onJoplinLinkClick,
|
||||||
onResourceLongPress,
|
onResourceLongPress,
|
||||||
onRequestEditResource,
|
onRequestEditResource,
|
||||||
|
onMainContainerScroll,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@@ -40,7 +40,16 @@ const onlyCheckboxHasChangedHack = (previousBody: string, newBody: string) => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function useSource(noteBody: string, noteMarkupLanguage: number, themeId: number, highlightedKeywords: string[], noteResources: any, paddingBottom: number, noteHash: string): UseSourceResult {
|
export default function useSource(
|
||||||
|
noteBody: string,
|
||||||
|
noteMarkupLanguage: number,
|
||||||
|
themeId: number,
|
||||||
|
highlightedKeywords: string[],
|
||||||
|
noteResources: any,
|
||||||
|
paddingBottom: number,
|
||||||
|
noteHash: string,
|
||||||
|
initialScroll: number|null,
|
||||||
|
): UseSourceResult {
|
||||||
const [html, setHtml] = useState<string>('');
|
const [html, setHtml] = useState<string>('');
|
||||||
const [injectedJs, setInjectedJs] = useState<string[]>([]);
|
const [injectedJs, setInjectedJs] = useState<string[]>([]);
|
||||||
const [resourceLoadedTime, setResourceLoadedTime] = useState(0);
|
const [resourceLoadedTime, setResourceLoadedTime] = useState(0);
|
||||||
@@ -142,6 +151,12 @@ export default function useSource(noteBody: string, noteMarkupLanguage: number,
|
|||||||
|
|
||||||
const resourceDownloadMode = Setting.value('sync.resourceDownloadMode');
|
const resourceDownloadMode = Setting.value('sync.resourceDownloadMode');
|
||||||
|
|
||||||
|
// On iOS, the root container has slow inertial scroll, which feels very different from
|
||||||
|
// the native scroll in other apps. This is not the case, however, when a child (e.g. a div)
|
||||||
|
// scrolls the content instead.
|
||||||
|
// Use a div to scroll on iOS instead of the main container:
|
||||||
|
const scrollRenderedMdContainer = shim.mobilePlatform() === 'ios';
|
||||||
|
|
||||||
const js = [];
|
const js = [];
|
||||||
js.push('try {');
|
js.push('try {');
|
||||||
js.push(shim.injectedJs('webviewLib'));
|
js.push(shim.injectedJs('webviewLib'));
|
||||||
@@ -149,15 +164,46 @@ export default function useSource(noteBody: string, noteMarkupLanguage: number,
|
|||||||
// the ReactNativeWebView actually supports only one, so the second arg is ignored (and currently not needed for the mobile app).
|
// the ReactNativeWebView actually supports only one, so the second arg is ignored (and currently not needed for the mobile app).
|
||||||
js.push('window.joplinPostMessage_ = (msg, args) => { return window.ReactNativeWebView.postMessage(msg); };');
|
js.push('window.joplinPostMessage_ = (msg, args) => { return window.ReactNativeWebView.postMessage(msg); };');
|
||||||
js.push('webviewLib.initialize({ postMessage: msg => { return window.ReactNativeWebView.postMessage(msg); } });');
|
js.push('webviewLib.initialize({ postMessage: msg => { return window.ReactNativeWebView.postMessage(msg); } });');
|
||||||
|
js.push(`
|
||||||
|
const scrollingElement =
|
||||||
|
${scrollRenderedMdContainer ? 'document.querySelector("#rendered-md")' : 'document.scrollingElement'};
|
||||||
|
let lastScrollTop;
|
||||||
|
const onMainContentScroll = () => {
|
||||||
|
const newScrollTop = scrollingElement.scrollTop;
|
||||||
|
if (lastScrollTop !== newScrollTop) {
|
||||||
|
const eventData = { scrollTop: newScrollTop };
|
||||||
|
window.ReactNativeWebView.postMessage('onscroll:' + JSON.stringify(eventData));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Listen for events on both scrollingElement and window
|
||||||
|
// - On Android, scrollingElement.addEventListener('scroll', callback) doesn't call callback on
|
||||||
|
// scroll. However, window.addEventListener('scroll', callback) does.
|
||||||
|
// - iOS needs a listener to be added to scrollingElement -- events aren't received when
|
||||||
|
// the listener is added to window with window.addEventListener('scroll', ...).
|
||||||
|
scrollingElement.addEventListener('scroll', onMainContentScroll);
|
||||||
|
window.addEventListener('scroll', onMainContentScroll);
|
||||||
|
|
||||||
|
const scrollContentToPosition = (position) => {
|
||||||
|
scrollingElement.scrollTop = position;
|
||||||
|
};
|
||||||
|
`);
|
||||||
js.push(`
|
js.push(`
|
||||||
const readyStateCheckInterval = setInterval(function() {
|
const readyStateCheckInterval = setInterval(function() {
|
||||||
if (document.readyState === "complete") {
|
if (document.readyState === "complete") {
|
||||||
clearInterval(readyStateCheckInterval);
|
clearInterval(readyStateCheckInterval);
|
||||||
if ("${resourceDownloadMode}" === "manual") webviewLib.setupResourceManualDownload();
|
if ("${resourceDownloadMode}" === "manual") webviewLib.setupResourceManualDownload();
|
||||||
|
|
||||||
const hash = "${noteHash}";
|
const hash = "${noteHash}";
|
||||||
// Gives it a bit of time before scrolling to the anchor
|
const initialScroll = ${JSON.stringify(initialScroll)};
|
||||||
// so that images are loaded.
|
|
||||||
if (hash) {
|
// Don't scroll to a hash if we're given initial scroll (initial scroll
|
||||||
|
// overrides scrolling to a hash).
|
||||||
|
if ((initialScroll ?? null) !== null) {
|
||||||
|
scrollContentToPosition(initialScroll);
|
||||||
|
} else if (hash) {
|
||||||
|
// Gives it a bit of time before scrolling to the anchor
|
||||||
|
// so that images are loaded.
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const e = document.getElementById(hash);
|
const e = document.getElementById(hash);
|
||||||
if (!e) {
|
if (!e) {
|
||||||
@@ -171,6 +217,7 @@ export default function useSource(noteBody: string, noteMarkupLanguage: number,
|
|||||||
}, 10);
|
}, 10);
|
||||||
`);
|
`);
|
||||||
js.push('} catch (e) {');
|
js.push('} catch (e) {');
|
||||||
|
js.push(' console.error(e);');
|
||||||
js.push(' window.ReactNativeWebView.postMessage("error:" + e.message + ": " + JSON.stringify(e))');
|
js.push(' window.ReactNativeWebView.postMessage("error:" + e.message + ": " + JSON.stringify(e))');
|
||||||
js.push(' true;');
|
js.push(' true;');
|
||||||
js.push('}');
|
js.push('}');
|
||||||
@@ -186,10 +233,11 @@ export default function useSource(noteBody: string, noteMarkupLanguage: number,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
:root > body {
|
||||||
iOS seems to increase inertial scrolling friction when the WebView body/root elements
|
padding: 0;
|
||||||
scroll. Scroll the main container instead.
|
}
|
||||||
*/
|
`;
|
||||||
|
const scrollRenderedMdContainerCss = `
|
||||||
body > #rendered-md {
|
body > #rendered-md {
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
@@ -197,10 +245,6 @@ export default function useSource(noteBody: string, noteMarkupLanguage: number,
|
|||||||
padding-bottom: ${paddingBottom}px;
|
padding-bottom: ${paddingBottom}px;
|
||||||
padding-top: ${paddingTop};
|
padding-top: ${paddingTop};
|
||||||
}
|
}
|
||||||
|
|
||||||
:root > body {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
const defaultCss = `
|
const defaultCss = `
|
||||||
code {
|
code {
|
||||||
@@ -219,6 +263,7 @@ export default function useSource(noteBody: string, noteMarkupLanguage: number,
|
|||||||
<style>
|
<style>
|
||||||
${defaultCss}
|
${defaultCss}
|
||||||
${shim.mobilePlatform() === 'ios' ? iOSSpecificCss : ''}
|
${shim.mobilePlatform() === 'ios' ? iOSSpecificCss : ''}
|
||||||
|
${scrollRenderedMdContainer ? scrollRenderedMdContainerCss : ''}
|
||||||
${editPopupCss}
|
${editPopupCss}
|
||||||
</style>
|
</style>
|
||||||
${assetsToHeaders(result.pluginAssets, { asHtml: true })}
|
${assetsToHeaders(result.pluginAssets, { asHtml: true })}
|
||||||
|
@@ -11,18 +11,17 @@ import { WebViewMessageEvent } from 'react-native-webview';
|
|||||||
import ExtendedWebView, { WebViewControl } from '../../ExtendedWebView';
|
import ExtendedWebView, { WebViewControl } from '../../ExtendedWebView';
|
||||||
import { clearAutosave, writeAutosave } from './autosave';
|
import { clearAutosave, writeAutosave } from './autosave';
|
||||||
import { LocalizedStrings } from './js-draw/types';
|
import { LocalizedStrings } from './js-draw/types';
|
||||||
|
import VersionInfo from 'react-native-version-info';
|
||||||
|
|
||||||
|
|
||||||
const logger = Logger.create('ImageEditor');
|
const logger = Logger.create('ImageEditor');
|
||||||
|
|
||||||
type OnSaveCallback = (svgData: string)=> void;
|
type OnSaveCallback = (svgData: string)=> void;
|
||||||
type OnCancelCallback = ()=> void;
|
type OnCancelCallback = ()=> void;
|
||||||
|
|
||||||
// Returns the empty string to load from a template.
|
|
||||||
type LoadInitialSVGCallback = ()=> Promise<string>;
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
themeId: number;
|
themeId: number;
|
||||||
loadInitialSVGData: LoadInitialSVGCallback;
|
resourceFilename: string|null;
|
||||||
onSave: OnSaveCallback;
|
onSave: OnSaveCallback;
|
||||||
onExit: OnCancelCallback;
|
onExit: OnCancelCallback;
|
||||||
}
|
}
|
||||||
@@ -166,10 +165,23 @@ const ImageEditor = (props: Props) => {
|
|||||||
redo: _('Redo'),
|
redo: _('Redo'),
|
||||||
}), []);
|
}), []);
|
||||||
|
|
||||||
|
const appInfo = useMemo(() => {
|
||||||
|
return {
|
||||||
|
name: 'Joplin',
|
||||||
|
description: `v${VersionInfo.appVersion}`,
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const injectedJavaScript = useMemo(() => `
|
const injectedJavaScript = useMemo(() => `
|
||||||
window.onerror = (message, source, lineno) => {
|
window.onerror = (message, source, lineno) => {
|
||||||
window.ReactNativeWebView.postMessage(
|
window.ReactNativeWebView.postMessage(
|
||||||
"error: " + message + " in file://" + source + ", line " + lineno
|
"error: " + message + " in file://" + source + ", line " + lineno,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
window.onunhandledrejection = (error) => {
|
||||||
|
window.ReactNativeWebView.postMessage(
|
||||||
|
"error: " + error.reason,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -229,6 +241,7 @@ const ImageEditor = (props: Props) => {
|
|||||||
${JSON.stringify(Setting.value('imageeditor.jsdrawToolbar'))},
|
${JSON.stringify(Setting.value('imageeditor.jsdrawToolbar'))},
|
||||||
${JSON.stringify(Setting.value('locale'))},
|
${JSON.stringify(Setting.value('locale'))},
|
||||||
${JSON.stringify(localizedStrings)},
|
${JSON.stringify(localizedStrings)},
|
||||||
|
${JSON.stringify({ appInfo })},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Start loading the SVG file (if present) after loading the editor.
|
// Start loading the SVG file (if present) after loading the editor.
|
||||||
@@ -242,7 +255,7 @@ const ImageEditor = (props: Props) => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
true;
|
true;
|
||||||
`, [localizedStrings]);
|
`, [localizedStrings, appInfo]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
webviewRef.current?.injectJS(`
|
webviewRef.current?.injectJS(`
|
||||||
@@ -255,19 +268,17 @@ const ImageEditor = (props: Props) => {
|
|||||||
}, [css]);
|
}, [css]);
|
||||||
|
|
||||||
const onReadyToLoadData = useCallback(async () => {
|
const onReadyToLoadData = useCallback(async () => {
|
||||||
const initialSVGData = await props.loadInitialSVGData?.() ?? '';
|
|
||||||
|
|
||||||
// It can take some time for initialSVGData to be transferred to the WebView.
|
// It can take some time for initialSVGData to be transferred to the WebView.
|
||||||
// Thus, do so after the main content has been loaded.
|
// Thus, do so after the main content has been loaded.
|
||||||
webviewRef.current.injectJS(`(async () => {
|
webviewRef.current.injectJS(`(async () => {
|
||||||
if (window.editorControl) {
|
if (window.editorControl) {
|
||||||
const initialSVGData = ${JSON.stringify(initialSVGData)};
|
const initialSVGPath = ${JSON.stringify(props.resourceFilename)};
|
||||||
const initialTemplateData = ${JSON.stringify(Setting.value('imageeditor.imageTemplate'))};
|
const initialTemplateData = ${JSON.stringify(Setting.value('imageeditor.imageTemplate'))};
|
||||||
|
|
||||||
editorControl.loadImageOrTemplate(initialSVGData, initialTemplateData);
|
editorControl.loadImageOrTemplate(initialSVGPath, initialTemplateData);
|
||||||
}
|
}
|
||||||
})();`);
|
})();`);
|
||||||
}, [webviewRef, props.loadInitialSVGData]);
|
}, [webviewRef, props.resourceFilename]);
|
||||||
|
|
||||||
const onMessage = useCallback(async (event: WebViewMessageEvent) => {
|
const onMessage = useCallback(async (event: WebViewMessageEvent) => {
|
||||||
const data = event.nativeEvent.data;
|
const data = event.nativeEvent.data;
|
||||||
@@ -306,6 +317,7 @@ const ImageEditor = (props: Props) => {
|
|||||||
themeId={props.themeId}
|
themeId={props.themeId}
|
||||||
html={html}
|
html={html}
|
||||||
injectedJavaScript={injectedJavaScript}
|
injectedJavaScript={injectedJavaScript}
|
||||||
|
allowFileAccessFromJs={true}
|
||||||
onMessage={onMessage}
|
onMessage={onMessage}
|
||||||
onError={onError}
|
onError={onError}
|
||||||
ref={webviewRef}
|
ref={webviewRef}
|
||||||
|
@@ -57,7 +57,7 @@ describe('createJsDrawEditor', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Load no image and an empty template so that autosave can start
|
// Load no image and an empty template so that autosave can start
|
||||||
await editorControl.loadImageOrTemplate(undefined, '{}');
|
await editorControl.loadImageOrTemplate('', '{}');
|
||||||
|
|
||||||
expect(calledAutosaveCount).toBe(0);
|
expect(calledAutosaveCount).toBe(0);
|
||||||
|
|
||||||
|
@@ -120,20 +120,44 @@ export const createJsDrawEditor = (
|
|||||||
editor.showLoadingWarning(0);
|
editor.showLoadingWarning(0);
|
||||||
editor.setReadOnly(true);
|
editor.setReadOnly(true);
|
||||||
|
|
||||||
|
const fetchInitialSvgData = (resourceUrl: string) => {
|
||||||
|
return new Promise<string>((resolve, reject) => {
|
||||||
|
if (!resourceUrl) {
|
||||||
|
resolve('');
|
||||||
|
}
|
||||||
|
|
||||||
|
// fetch seems to be unable to request file:// URLs.
|
||||||
|
// https://github.com/react-native-webview/react-native-webview/issues/1560#issuecomment-1783611805
|
||||||
|
const request = new XMLHttpRequest();
|
||||||
|
|
||||||
|
const onError = () => {
|
||||||
|
reject(`Failed to load initial SVG data: ${request.status}, ${request.statusText}, ${request.responseText}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
request.addEventListener('load', _ => {
|
||||||
|
resolve(request.responseText);
|
||||||
|
});
|
||||||
|
request.addEventListener('error', onError);
|
||||||
|
request.addEventListener('abort', onError);
|
||||||
|
|
||||||
|
request.open('GET', resourceUrl);
|
||||||
|
request.send();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const editorControl = {
|
const editorControl = {
|
||||||
editor,
|
editor,
|
||||||
loadImageOrTemplate: async (svgData: string|undefined, templateData: string) => {
|
loadImageOrTemplate: async (resourceUrl: string, templateData: string) => {
|
||||||
// loadFromSVG shows its own loading message. Hide the original.
|
// loadFromSVG shows its own loading message. Hide the original.
|
||||||
editor.hideLoadingWarning();
|
editor.hideLoadingWarning();
|
||||||
|
|
||||||
if (svgData && svgData.length > 0) {
|
const svgData = await fetchInitialSvgData(resourceUrl);
|
||||||
await editor.loadFromSVG(svgData);
|
|
||||||
} else {
|
|
||||||
await applyTemplateToEditor(editor, templateData);
|
|
||||||
|
|
||||||
// The editor expects to be saved initially (without
|
// Load from a template if no initial data
|
||||||
// unsaved changes). Save now.
|
if (svgData === '') {
|
||||||
saveNow();
|
await applyTemplateToEditor(editor, templateData);
|
||||||
|
} else {
|
||||||
|
await editor.loadFromSVG(svgData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We can now edit and save safely (without data loss).
|
// We can now edit and save safely (without data loss).
|
||||||
|
@@ -40,9 +40,7 @@ interface ActionButtonProps {
|
|||||||
onPress: Callback;
|
onPress: Callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ActionButton = (
|
const ActionButton = (props: ActionButtonProps) => {
|
||||||
props: ActionButtonProps,
|
|
||||||
) => {
|
|
||||||
return (
|
return (
|
||||||
<CustomButton
|
<CustomButton
|
||||||
themeId={props.themeId}
|
themeId={props.themeId}
|
||||||
|
@@ -70,6 +70,7 @@ interface ScreenHeaderProps {
|
|||||||
onRedoButtonPress: OnPressCallback;
|
onRedoButtonPress: OnPressCallback;
|
||||||
onSaveButtonPress: OnPressCallback;
|
onSaveButtonPress: OnPressCallback;
|
||||||
sortButton_press?: OnPressCallback;
|
sortButton_press?: OnPressCallback;
|
||||||
|
onSearchButtonPress?: OnPressCallback;
|
||||||
|
|
||||||
showSideMenuButton?: boolean;
|
showSideMenuButton?: boolean;
|
||||||
showSearchButton?: boolean;
|
showSearchButton?: boolean;
|
||||||
@@ -242,7 +243,11 @@ class ScreenHeaderComponent extends PureComponent<ScreenHeaderProps, ScreenHeade
|
|||||||
}
|
}
|
||||||
|
|
||||||
private searchButton_press() {
|
private searchButton_press() {
|
||||||
void NavService.go('Search');
|
if (this.props.onSearchButtonPress) {
|
||||||
|
this.props.onSearchButtonPress();
|
||||||
|
} else {
|
||||||
|
void NavService.go('Search');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async duplicateButton_press() {
|
private async duplicateButton_press() {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Platform, Linking, View, Switch, ScrollView, Text, TouchableOpacity, Alert, PermissionsAndroid, Dimensions, AccessibilityInfo } from 'react-native';
|
import { Platform, Linking, View, Switch, ScrollView, Text, TouchableOpacity, Alert, PermissionsAndroid, Dimensions, AccessibilityInfo } from 'react-native';
|
||||||
import Setting, { AppType } from '@joplin/lib/models/Setting';
|
import Setting, { AppType, SettingMetadataSection } from '@joplin/lib/models/Setting';
|
||||||
import NavService from '@joplin/lib/services/NavService';
|
import NavService from '@joplin/lib/services/NavService';
|
||||||
import SearchEngine from '@joplin/lib/services/searchengine/SearchEngine';
|
import SearchEngine from '@joplin/lib/services/searchengine/SearchEngine';
|
||||||
import checkPermissions from '../../../utils/checkPermissions';
|
import checkPermissions from '../../../utils/checkPermissions';
|
||||||
@@ -18,21 +18,25 @@ import * as shared from '@joplin/lib/components/shared/config/config-shared';
|
|||||||
import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry';
|
import SyncTargetRegistry from '@joplin/lib/SyncTargetRegistry';
|
||||||
import biometricAuthenticate from '../../biometrics/biometricAuthenticate';
|
import biometricAuthenticate from '../../biometrics/biometricAuthenticate';
|
||||||
import configScreenStyles, { ConfigScreenStyles } from './configScreenStyles';
|
import configScreenStyles, { ConfigScreenStyles } from './configScreenStyles';
|
||||||
import NoteExportButton from './NoteExportSection/NoteExportButton';
|
import NoteExportButton, { exportButtonDescription, exportButtonTitle } from './NoteExportSection/NoteExportButton';
|
||||||
import SettingsButton from './SettingsButton';
|
import SettingsButton from './SettingsButton';
|
||||||
import Clipboard from '@react-native-community/clipboard';
|
import Clipboard from '@react-native-community/clipboard';
|
||||||
import { ReactNode } from 'react';
|
import { ReactElement, ReactNode } from 'react';
|
||||||
import { Dispatch } from 'redux';
|
import { Dispatch } from 'redux';
|
||||||
import SectionHeader from './SectionHeader';
|
import SectionHeader from './SectionHeader';
|
||||||
import ExportProfileButton from './NoteExportSection/ExportProfileButton';
|
import ExportProfileButton, { exportProfileButtonTitle } from './NoteExportSection/ExportProfileButton';
|
||||||
import SettingComponent from './SettingComponent';
|
import SettingComponent from './SettingComponent';
|
||||||
import ExportDebugReportButton from './NoteExportSection/ExportDebugReportButton';
|
import ExportDebugReportButton, { exportDebugReportTitle } from './NoteExportSection/ExportDebugReportButton';
|
||||||
import SectionSelector from './SectionSelector';
|
import SectionSelector from './SectionSelector';
|
||||||
|
import { TextInput } from 'react-native-paper';
|
||||||
|
|
||||||
interface ConfigScreenState {
|
interface ConfigScreenState {
|
||||||
settings: any;
|
settings: any;
|
||||||
changedSettingKeys: string[];
|
changedSettingKeys: string[];
|
||||||
|
|
||||||
|
searchQuery: string;
|
||||||
|
searching: boolean;
|
||||||
|
|
||||||
fixingSearchIndex: boolean;
|
fixingSearchIndex: boolean;
|
||||||
checkSyncConfigResult: { ok: boolean; errorMessage: string }|'checking'|null;
|
checkSyncConfigResult: { ok: boolean; errorMessage: string }|'checking'|null;
|
||||||
showAdvancedSettings: boolean;
|
showAdvancedSettings: boolean;
|
||||||
@@ -66,6 +70,8 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
|||||||
selectedSectionName: null,
|
selectedSectionName: null,
|
||||||
fixingSearchIndex: false,
|
fixingSearchIndex: false,
|
||||||
sidebarWidth: 100,
|
sidebarWidth: 100,
|
||||||
|
searchQuery: '',
|
||||||
|
searching: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.scrollViewRef_ = React.createRef<ScrollView>();
|
this.scrollViewRef_ = React.createRef<ScrollView>();
|
||||||
@@ -115,10 +121,7 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
|||||||
};
|
};
|
||||||
|
|
||||||
private manageProfilesButtonPress_ = () => {
|
private manageProfilesButtonPress_ = () => {
|
||||||
this.props.dispatch({
|
void NavService.go('ProfileSwitcher');
|
||||||
type: 'NAV_GO',
|
|
||||||
routeName: 'ProfileSwitcher',
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private fixSearchEngineIndexButtonPress_ = async () => {
|
private fixSearchEngineIndexButtonPress_ = async () => {
|
||||||
@@ -131,6 +134,21 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
|||||||
void NavService.go('Log');
|
void NavService.go('Log');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private setShowSearch_(searching: boolean) {
|
||||||
|
if (searching !== this.state.searching) {
|
||||||
|
this.setState({ searching });
|
||||||
|
AccessibilityInfo.announceForAccessibility(searching ? _('Search shown') : _('Search hidden'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private onSearchButtonPress_ = () => {
|
||||||
|
this.setShowSearch_(!this.state.searching);
|
||||||
|
};
|
||||||
|
|
||||||
|
private onSearchUpdate_ = (newQuery: string) => {
|
||||||
|
this.setState({ searchQuery: newQuery });
|
||||||
|
};
|
||||||
|
|
||||||
private updateSidebarWidth = () => {
|
private updateSidebarWidth = () => {
|
||||||
const windowWidth = Dimensions.get('window').width;
|
const windowWidth = Dimensions.get('window').width;
|
||||||
|
|
||||||
@@ -153,10 +171,13 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
|||||||
return this.state.sidebarWidth > windowWidth / 2;
|
return this.state.sidebarWidth > windowWidth / 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private switchSectionPress_ = (section: string) => {
|
private onJumpToSection_ = (section: string) => {
|
||||||
const label = Setting.sectionNameToLabel(section);
|
const label = Setting.sectionNameToLabel(section);
|
||||||
AccessibilityInfo.announceForAccessibility(_('Opening section %s', label));
|
AccessibilityInfo.announceForAccessibility(_('Opening section %s', label));
|
||||||
this.setState({ selectedSectionName: section });
|
this.setState({
|
||||||
|
selectedSectionName: section,
|
||||||
|
searching: false,
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private showSectionNavigation_ = () => {
|
private showSectionNavigation_ = () => {
|
||||||
@@ -206,36 +227,65 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private hasUnsavedChanges() {
|
||||||
|
return this.state.changedSettingKeys.length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private promptSaveChanges(): Promise<void> {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
if (this.hasUnsavedChanges()) {
|
||||||
|
const dialogTitle: string|null = null;
|
||||||
|
Alert.alert(
|
||||||
|
dialogTitle,
|
||||||
|
_('There are unsaved changes.'),
|
||||||
|
[{
|
||||||
|
text: _('Save changes'),
|
||||||
|
onPress: async () => {
|
||||||
|
await this.saveButton_press();
|
||||||
|
resolve();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: _('Discard changes'),
|
||||||
|
onPress: () => resolve(),
|
||||||
|
}],
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private handleNavigateToNewScren = async (): Promise<boolean> => {
|
||||||
|
await this.promptSaveChanges();
|
||||||
|
|
||||||
|
// Continue navigation
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
private handleBackButtonPress = (): boolean => {
|
private handleBackButtonPress = (): boolean => {
|
||||||
const goBack = async () => {
|
const goBack = async () => {
|
||||||
BackButtonService.removeHandler(this.handleBackButtonPress);
|
BackButtonService.removeHandler(this.handleBackButtonPress);
|
||||||
await BackButtonService.back();
|
await BackButtonService.back();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Cancel search on back
|
||||||
|
if (this.state.searching) {
|
||||||
|
this.setShowSearch_(false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Show navigation when pressing "back" (unless always visible).
|
// Show navigation when pressing "back" (unless always visible).
|
||||||
if (this.state.selectedSectionName && this.navigationFillsScreen()) {
|
if (this.state.selectedSectionName && this.navigationFillsScreen()) {
|
||||||
this.showSectionNavigation_();
|
this.showSectionNavigation_();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.changedSettingKeys.length > 0) {
|
if (this.hasUnsavedChanges()) {
|
||||||
const dialogTitle: string|null = null;
|
void (async () => {
|
||||||
Alert.alert(
|
await this.promptSaveChanges();
|
||||||
dialogTitle,
|
await goBack();
|
||||||
_('There are unsaved changes.'),
|
})();
|
||||||
[{
|
|
||||||
text: _('Save changes'),
|
|
||||||
onPress: async () => {
|
|
||||||
await this.saveButton_press();
|
|
||||||
await goBack();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: _('Discard changes'),
|
|
||||||
onPress: goBack,
|
|
||||||
}],
|
|
||||||
);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -255,12 +305,14 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
|||||||
}
|
}
|
||||||
|
|
||||||
BackButtonService.addHandler(this.handleBackButtonPress);
|
BackButtonService.addHandler(this.handleBackButtonPress);
|
||||||
|
NavService.addHandler(this.handleNavigateToNewScren);
|
||||||
Dimensions.addEventListener('change', this.updateSidebarWidth);
|
Dimensions.addEventListener('change', this.updateSidebarWidth);
|
||||||
this.updateSidebarWidth();
|
this.updateSidebarWidth();
|
||||||
}
|
}
|
||||||
|
|
||||||
public componentWillUnmount() {
|
public componentWillUnmount() {
|
||||||
BackButtonService.removeHandler(this.handleBackButtonPress);
|
BackButtonService.removeHandler(this.handleBackButtonPress);
|
||||||
|
NavService.removeHandler(this.handleNavigateToNewScren);
|
||||||
}
|
}
|
||||||
|
|
||||||
private renderButton(key: string, title: string, clickHandler: ()=> void, options: any = null) {
|
private renderButton(key: string, title: string, clickHandler: ()=> void, options: any = null) {
|
||||||
@@ -276,10 +328,73 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public sectionToComponent(key: string, section: any, settings: any, isSelected: boolean) {
|
public sectionToComponent(key: string, section: SettingMetadataSection, settings: any, isSelected: boolean) {
|
||||||
const settingComps = [];
|
const settingComps: ReactElement[] = [];
|
||||||
|
|
||||||
|
const headerTitle = Setting.sectionNameToLabel(section.name);
|
||||||
|
|
||||||
|
const matchesSearchQuery = (relatedText: string|string[]) => {
|
||||||
|
let searchThrough;
|
||||||
|
if (Array.isArray(relatedText)) {
|
||||||
|
searchThrough = relatedText.join('\n');
|
||||||
|
} else {
|
||||||
|
searchThrough = relatedText;
|
||||||
|
}
|
||||||
|
searchThrough = searchThrough.toLocaleLowerCase();
|
||||||
|
|
||||||
|
const searchQuery = this.state.searchQuery.toLocaleLowerCase().trim();
|
||||||
|
|
||||||
|
const hasSearchMatches =
|
||||||
|
headerTitle.toLocaleLowerCase() === searchQuery
|
||||||
|
|| searchThrough.includes(searchQuery);
|
||||||
|
|
||||||
|
// Don't show results when the search input is empty
|
||||||
|
return this.state.searchQuery.length > 0 && hasSearchMatches;
|
||||||
|
};
|
||||||
|
|
||||||
|
const addSettingComponent = (component: ReactElement, relatedText: string|string[]) => {
|
||||||
|
const hiddenBySearch = this.state.searching && !matchesSearchQuery(relatedText);
|
||||||
|
if (component && !hiddenBySearch) {
|
||||||
|
settingComps.push(component);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addSettingButton = (key: string, title: string, clickHandler: ()=> void, options: any = null) => {
|
||||||
|
const relatedText = [title];
|
||||||
|
if (typeof options === 'object' && options?.description) {
|
||||||
|
relatedText.push(options.description);
|
||||||
|
}
|
||||||
|
addSettingComponent(this.renderButton(key, title, clickHandler, options), relatedText);
|
||||||
|
};
|
||||||
|
|
||||||
const styleSheet = this.styles().styleSheet;
|
const styleSheet = this.styles().styleSheet;
|
||||||
|
const addSettingLink = (key: string, title: string, target: string) => {
|
||||||
|
const component = (
|
||||||
|
<View key={key} style={styleSheet.settingContainer}>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => {
|
||||||
|
void Linking.openURL(target);
|
||||||
|
}}
|
||||||
|
accessibilityRole='link'
|
||||||
|
>
|
||||||
|
<Text key="label" style={styleSheet.linkText}>
|
||||||
|
{title}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
addSettingComponent(component, title);
|
||||||
|
};
|
||||||
|
|
||||||
|
const addSettingText = (key: string, text: string) => {
|
||||||
|
addSettingComponent(
|
||||||
|
<View key={key} style={styleSheet.settingContainer}>
|
||||||
|
<Text style={styleSheet.settingText}>{text}</Text>
|
||||||
|
</View>,
|
||||||
|
text,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
for (let i = 0; i < section.metadatas.length; i++) {
|
for (let i = 0; i < section.metadatas.length; i++) {
|
||||||
const md = section.metadatas[i];
|
const md = section.metadatas[i];
|
||||||
@@ -300,24 +415,29 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
|||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
settingComps.push(this.renderButton('check_sync_config_button', _('Check synchronisation configuration'), this.checkSyncConfig_, { statusComp: statusComp }));
|
addSettingButton('check_sync_config_button', _('Check synchronisation configuration'), this.checkSyncConfig_, { statusComp: statusComp });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const settingComp = this.settingToComponent(md.key, settings[md.key]);
|
const settingComp = this.settingToComponent(md.key, settings[md.key]);
|
||||||
settingComps.push(settingComp);
|
const relatedText = [md.label?.() ?? '', md.description?.() ?? ''];
|
||||||
|
addSettingComponent(
|
||||||
|
settingComp,
|
||||||
|
relatedText,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (section.name === 'sync') {
|
if (section.name === 'sync') {
|
||||||
settingComps.push(this.renderButton('e2ee_config_button', _('Encryption Config'), this.e2eeConfig_));
|
addSettingButton('e2ee_config_button', _('Encryption Config'), this.e2eeConfig_);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (section.name === 'joplinCloud') {
|
if (section.name === 'joplinCloud') {
|
||||||
|
const label = _('Email to note');
|
||||||
const description = _('Any email sent to this address will be converted into a note and added to your collection. The note will be saved into the Inbox notebook');
|
const description = _('Any email sent to this address will be converted into a note and added to your collection. The note will be saved into the Inbox notebook');
|
||||||
settingComps.push(
|
addSettingComponent(
|
||||||
<View key="joplinCloud">
|
<View key="joplinCloud">
|
||||||
<View style={this.styles().styleSheet.settingContainerNoBottomBorder}>
|
<View style={this.styles().styleSheet.settingContainerNoBottomBorder}>
|
||||||
<Text style={this.styles().styleSheet.settingText}>{_('Email to note')}</Text>
|
<Text style={this.styles().styleSheet.settingText}>{label}</Text>
|
||||||
<Text style={{ fontWeight: 'bold' }}>{this.props.settings['sync.10.inboxEmail']}</Text>
|
<Text style={{ fontWeight: 'bold' }}>{this.props.settings['sync.10.inboxEmail']}</Text>
|
||||||
</View>
|
</View>
|
||||||
{
|
{
|
||||||
@@ -329,20 +449,30 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
</View>,
|
</View>,
|
||||||
|
[label, description],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (section.name === 'tools') {
|
if (section.name === 'tools') {
|
||||||
settingComps.push(this.renderButton('profiles_buttons', _('Manage profiles'), this.manageProfilesButtonPress_));
|
addSettingButton('profiles_buttons', _('Manage profiles'), this.manageProfilesButtonPress_);
|
||||||
settingComps.push(this.renderButton('status_button', _('Sync Status'), this.syncStatusButtonPress_));
|
addSettingButton('status_button', _('Sync Status'), this.syncStatusButtonPress_);
|
||||||
settingComps.push(this.renderButton('log_button', _('Log'), this.logButtonPress_));
|
addSettingButton('log_button', _('Log'), this.logButtonPress_);
|
||||||
settingComps.push(this.renderButton('fix_search_engine_index', this.state.fixingSearchIndex ? _('Fixing search index...') : _('Fix search index'), this.fixSearchEngineIndexButtonPress_, { disabled: this.state.fixingSearchIndex, description: _('Use this to rebuild the search index if there is a problem with search. It may take a long time depending on the number of notes.') }));
|
addSettingButton('fix_search_engine_index', this.state.fixingSearchIndex ? _('Fixing search index...') : _('Fix search index'), this.fixSearchEngineIndexButtonPress_, { disabled: this.state.fixingSearchIndex, description: _('Use this to rebuild the search index if there is a problem with search. It may take a long time depending on the number of notes.') });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (section.name === 'export') {
|
if (section.name === 'export') {
|
||||||
settingComps.push(<NoteExportButton key='export_as_jex_button' styles={this.styles()} />);
|
addSettingComponent(
|
||||||
settingComps.push(<ExportDebugReportButton key='export_report_button' styles={this.styles()}/>);
|
<NoteExportButton key='export_as_jex_button' styles={this.styles()} />,
|
||||||
settingComps.push(<ExportProfileButton key='export_data' styles={this.styles()}/>);
|
[exportButtonTitle(), exportButtonDescription()],
|
||||||
|
);
|
||||||
|
addSettingComponent(
|
||||||
|
<ExportDebugReportButton key='export_report_button' styles={this.styles()}/>,
|
||||||
|
exportDebugReportTitle(),
|
||||||
|
);
|
||||||
|
addSettingComponent(
|
||||||
|
<ExportProfileButton key='export_data' styles={this.styles()}/>,
|
||||||
|
exportProfileButtonTitle(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (section.name === 'moreInfo') {
|
if (section.name === 'moreInfo') {
|
||||||
@@ -350,7 +480,7 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
|||||||
// Note: `PermissionsAndroid` doesn't work so we have to ask the user to manually
|
// Note: `PermissionsAndroid` doesn't work so we have to ask the user to manually
|
||||||
// set these permissions. https://stackoverflow.com/questions/49771084/permission-always-returns-never-ask-again
|
// set these permissions. https://stackoverflow.com/questions/49771084/permission-always-returns-never-ask-again
|
||||||
|
|
||||||
settingComps.push(
|
addSettingComponent(
|
||||||
<View key="permission_info" style={styleSheet.settingContainer}>
|
<View key="permission_info" style={styleSheet.settingContainer}>
|
||||||
<View key="permission_info_wrapper">
|
<View key="permission_info_wrapper">
|
||||||
<Text key="perm1a" style={styleSheet.settingText}>
|
<Text key="perm1a" style={styleSheet.settingText}>
|
||||||
@@ -367,95 +497,60 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</View>,
|
</View>,
|
||||||
|
'',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
settingComps.push(
|
addSettingLink('donate_link', _('Make a donation'), 'https://joplinapp.org/donate/');
|
||||||
<View key="donate_link" style={styleSheet.settingContainer}>
|
addSettingLink('website_link', _('Joplin website'), 'https://joplinapp.org/');
|
||||||
<TouchableOpacity
|
addSettingLink('privacy_link', _('Privacy Policy'), 'https://joplinapp.org/privacy/');
|
||||||
onPress={() => {
|
|
||||||
void Linking.openURL('https://joplinapp.org/donate/');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text key="label" style={styleSheet.linkText}>
|
|
||||||
{_('Make a donation')}
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>,
|
|
||||||
);
|
|
||||||
|
|
||||||
settingComps.push(
|
addSettingText('version_info_app', `Joplin ${VersionInfo.appVersion}`);
|
||||||
<View key="website_link" style={styleSheet.settingContainer}>
|
addSettingText('version_info_db', _('Database v%s', reg.db().version()));
|
||||||
<TouchableOpacity
|
addSettingText('version_info_fts', _('FTS enabled: %d', this.props.settings['db.ftsEnabled']));
|
||||||
onPress={() => {
|
addSettingText('version_info_hermes', _('Hermes enabled: %d', (global as any).HermesInternal ? 1 : 0));
|
||||||
void Linking.openURL('https://joplinapp.org/');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text key="label" style={styleSheet.linkText}>
|
|
||||||
{_('Joplin website')}
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>,
|
|
||||||
);
|
|
||||||
|
|
||||||
settingComps.push(
|
|
||||||
<View key="privacy_link" style={styleSheet.settingContainer}>
|
|
||||||
<TouchableOpacity
|
|
||||||
onPress={() => {
|
|
||||||
void Linking.openURL('https://joplinapp.org/privacy/');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text key="label" style={styleSheet.linkText}>
|
|
||||||
{_('Privacy Policy')}
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>,
|
|
||||||
);
|
|
||||||
|
|
||||||
settingComps.push(
|
|
||||||
<View key="version_info_app" style={styleSheet.settingContainer}>
|
|
||||||
<Text style={styleSheet.settingText}>{`Joplin ${VersionInfo.appVersion}`}</Text>
|
|
||||||
</View>,
|
|
||||||
);
|
|
||||||
|
|
||||||
settingComps.push(
|
|
||||||
<View key="version_info_db" style={styleSheet.settingContainer}>
|
|
||||||
<Text style={styleSheet.settingText}>{_('Database v%s', reg.db().version())}</Text>
|
|
||||||
</View>,
|
|
||||||
);
|
|
||||||
|
|
||||||
settingComps.push(
|
|
||||||
<View key="version_info_fts" style={styleSheet.settingContainer}>
|
|
||||||
<Text style={styleSheet.settingText}>{_('FTS enabled: %d', this.props.settings['db.ftsEnabled'])}</Text>
|
|
||||||
</View>,
|
|
||||||
);
|
|
||||||
|
|
||||||
settingComps.push(
|
|
||||||
<View key="version_info_hermes" style={styleSheet.settingContainer}>
|
|
||||||
<Text style={styleSheet.settingText}>{_('Hermes enabled: %d', (global as any).HermesInternal ? 1 : 0)}</Text>
|
|
||||||
</View>,
|
|
||||||
);
|
|
||||||
|
|
||||||
const featureFlagKeys = Setting.featureFlagKeys(AppType.Mobile);
|
const featureFlagKeys = Setting.featureFlagKeys(AppType.Mobile);
|
||||||
if (featureFlagKeys.length) {
|
if (featureFlagKeys.length) {
|
||||||
const headerKey = 'featureFlags';
|
const headerKey = 'featureFlags';
|
||||||
settingComps.push(<SectionHeader
|
const featureFlagsTitle = _('Feature flags');
|
||||||
key={headerKey}
|
addSettingComponent(
|
||||||
styles={this.styles().styleSheet}
|
<SectionHeader
|
||||||
title={_('Feature flags')}
|
key={headerKey}
|
||||||
onLayout={event => this.onHeaderLayout(headerKey, event)}
|
styles={this.styles().styleSheet}
|
||||||
/>);
|
title={featureFlagsTitle}
|
||||||
|
onLayout={event => this.onHeaderLayout(headerKey, event)}
|
||||||
|
/>,
|
||||||
|
_('Feature flags'),
|
||||||
|
);
|
||||||
|
|
||||||
settingComps.push(<View key="featureFlagsContainer">{this.renderFeatureFlags(settings, featureFlagKeys)}</View>);
|
addSettingComponent(
|
||||||
|
<View key="featureFlagsContainer">{this.renderFeatureFlags(settings, featureFlagKeys)}</View>,
|
||||||
|
featureFlagsTitle,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!settingComps.length) return null;
|
if (!settingComps.length) return null;
|
||||||
if (!isSelected) return null;
|
if (!isSelected && !this.state.searching) return null;
|
||||||
|
|
||||||
|
const headerComponent = (
|
||||||
|
<TouchableOpacity onPress={() => {
|
||||||
|
this.onJumpToSection_(section.name);
|
||||||
|
}}>
|
||||||
|
<SectionHeader
|
||||||
|
styles={styleSheet}
|
||||||
|
title={headerTitle}
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View key={key} onLayout={(event: any) => this.onSectionLayout(key, event)}>
|
<View key={key} onLayout={(event: any) => this.onSectionLayout(key, event)}>
|
||||||
<View>{settingComps}</View>
|
<View>
|
||||||
|
{this.state.searching ? headerComponent : null}
|
||||||
|
{settingComps}
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -541,18 +636,22 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
|||||||
currentSectionName = 'general';
|
currentSectionName = 'general';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.state.searching) {
|
||||||
|
currentSectionName = null;
|
||||||
|
}
|
||||||
|
|
||||||
const sectionSelector = (
|
const sectionSelector = (
|
||||||
<SectionSelector
|
<SectionSelector
|
||||||
selectedSectionName={currentSectionName}
|
selectedSectionName={currentSectionName}
|
||||||
styles={this.styles()}
|
styles={this.styles()}
|
||||||
settings={settings}
|
settings={settings}
|
||||||
openSection={this.switchSectionPress_}
|
openSection={this.onJumpToSection_}
|
||||||
width={this.state.sidebarWidth}
|
width={this.state.sidebarWidth}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
let currentSection: ReactNode;
|
let currentSection: ReactNode;
|
||||||
if (currentSectionName) {
|
if (currentSectionName || this.state.searching) {
|
||||||
const settingComps = shared.settingsToComponents2(
|
const settingComps = shared.settingsToComponents2(
|
||||||
this, AppType.Mobile, settings, currentSectionName,
|
this, AppType.Mobile, settings, currentSectionName,
|
||||||
|
|
||||||
@@ -560,11 +659,20 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
|||||||
// of React in lib/ and app-mobile/
|
// of React in lib/ and app-mobile/
|
||||||
) as ReactNode[];
|
) as ReactNode[];
|
||||||
|
|
||||||
|
const searchInput = <TextInput
|
||||||
|
value={this.state.searchQuery}
|
||||||
|
label={_('Search')}
|
||||||
|
placeholder={_('Search...')}
|
||||||
|
onChangeText={this.onSearchUpdate_}
|
||||||
|
autoFocus={true}
|
||||||
|
/>;
|
||||||
|
|
||||||
currentSection = (
|
currentSection = (
|
||||||
<ScrollView
|
<ScrollView
|
||||||
ref={this.scrollViewRef_}
|
ref={this.scrollViewRef_}
|
||||||
style={{ flexGrow: 1 }}
|
style={{ flexGrow: 1 }}
|
||||||
>
|
>
|
||||||
|
{this.state.searching ? searchInput : null}
|
||||||
{settingComps}
|
{settingComps}
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
);
|
);
|
||||||
@@ -598,10 +706,11 @@ class ConfigScreenComponent extends BaseScreenComponent<ConfigScreenProps, Confi
|
|||||||
<ScreenHeader
|
<ScreenHeader
|
||||||
title={screenHeadingText}
|
title={screenHeadingText}
|
||||||
showSaveButton={true}
|
showSaveButton={true}
|
||||||
showSearchButton={false}
|
showSearchButton={true}
|
||||||
showSideMenuButton={false}
|
showSideMenuButton={false}
|
||||||
saveButtonDisabled={!this.state.changedSettingKeys.length}
|
saveButtonDisabled={!this.hasUnsavedChanges()}
|
||||||
onSaveButtonPress={this.saveButton_press}
|
onSaveButtonPress={this.saveButton_press}
|
||||||
|
onSearchButtonPress={this.onSearchButtonPress_}
|
||||||
/>
|
/>
|
||||||
{mainComponent}
|
{mainComponent}
|
||||||
</View>
|
</View>
|
||||||
|
@@ -11,6 +11,8 @@ interface Props {
|
|||||||
styles: ConfigScreenStyles;
|
styles: ConfigScreenStyles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const exportDebugReportTitle = () => _('Export Debug Report');
|
||||||
|
|
||||||
const ExportDebugReportButton = (props: Props) => {
|
const ExportDebugReportButton = (props: Props) => {
|
||||||
const [creatingReport, setCreatingReport] = useState(false);
|
const [creatingReport, setCreatingReport] = useState(false);
|
||||||
|
|
||||||
@@ -24,7 +26,7 @@ const ExportDebugReportButton = (props: Props) => {
|
|||||||
|
|
||||||
const exportDebugReportButton = (
|
const exportDebugReportButton = (
|
||||||
<SettingsButton
|
<SettingsButton
|
||||||
title={creatingReport ? _('Creating report...') : _('Export Debug Report')}
|
title={creatingReport ? _('Creating report...') : exportDebugReportTitle()}
|
||||||
clickHandler={exportDebugButtonPress}
|
clickHandler={exportDebugButtonPress}
|
||||||
styles={props.styles}
|
styles={props.styles}
|
||||||
disabled={creatingReport}
|
disabled={creatingReport}
|
||||||
|
@@ -13,6 +13,8 @@ interface Props {
|
|||||||
styles: ConfigScreenStyles;
|
styles: ConfigScreenStyles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const exportProfileButtonTitle = () => _('Export profile');
|
||||||
|
|
||||||
const ExportProfileButton = (props: Props) => {
|
const ExportProfileButton = (props: Props) => {
|
||||||
const [profileExportStatus, setProfileExportStatus] = useState<'idle'|'prompt'|'exporting'>('idle');
|
const [profileExportStatus, setProfileExportStatus] = useState<'idle'|'prompt'|'exporting'>('idle');
|
||||||
const [profileExportPath, setProfileExportPath] = useState<string>('');
|
const [profileExportPath, setProfileExportPath] = useState<string>('');
|
||||||
@@ -31,7 +33,7 @@ const ExportProfileButton = (props: Props) => {
|
|||||||
const exportProfileButton = (
|
const exportProfileButton = (
|
||||||
<SettingsButton
|
<SettingsButton
|
||||||
styles={props.styles}
|
styles={props.styles}
|
||||||
title={profileExportStatus === 'exporting' ? _('Exporting profile...') : _('Export profile')}
|
title={profileExportStatus === 'exporting' ? _('Exporting profile...') : exportProfileButtonTitle()}
|
||||||
clickHandler={exportProfileButtonPress}
|
clickHandler={exportProfileButtonPress}
|
||||||
description={_('For debugging purpose only: export your profile to an external SD card.')}
|
description={_('For debugging purpose only: export your profile to an external SD card.')}
|
||||||
disabled={profileExportStatus === 'exporting'}
|
disabled={profileExportStatus === 'exporting'}
|
||||||
|
@@ -24,6 +24,9 @@ enum ExportStatus {
|
|||||||
Exported,
|
Exported,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const exportButtonTitle = () => _('Export all notes as JEX');
|
||||||
|
export const exportButtonDescription = () => _('Share a copy of all notes in a file format that can be imported by Joplin on a computer.');
|
||||||
|
|
||||||
const NoteExportButton: FunctionComponent<Props> = props => {
|
const NoteExportButton: FunctionComponent<Props> = props => {
|
||||||
const [exportStatus, setExportStatus] = useState<ExportStatus>(ExportStatus.NotStarted);
|
const [exportStatus, setExportStatus] = useState<ExportStatus>(ExportStatus.NotStarted);
|
||||||
const [exportProgress, setExportProgress] = useState<number|undefined>(0);
|
const [exportProgress, setExportProgress] = useState<number|undefined>(0);
|
||||||
@@ -80,13 +83,12 @@ const NoteExportButton: FunctionComponent<Props> = props => {
|
|||||||
indeterminate={exportProgress === undefined}
|
indeterminate={exportProgress === undefined}
|
||||||
progress={exportProgress}/>
|
progress={exportProgress}/>
|
||||||
);
|
);
|
||||||
const descriptionText = _('Share a copy of all notes in a file format that can be imported by Joplin on a computer.');
|
|
||||||
|
|
||||||
const startOrCancelExportButton = (
|
const startOrCancelExportButton = (
|
||||||
<SettingsButton
|
<SettingsButton
|
||||||
title={exportStatus === ExportStatus.Exporting ? _('Exporting...') : _('Export all notes as JEX')}
|
title={exportStatus === ExportStatus.Exporting ? _('Exporting...') : exportButtonTitle()}
|
||||||
disabled={exportStatus === ExportStatus.Exporting}
|
disabled={exportStatus === ExportStatus.Exporting}
|
||||||
description={exportStatus === ExportStatus.NotStarted ? descriptionText : null}
|
description={exportStatus === ExportStatus.NotStarted ? exportButtonDescription() : null}
|
||||||
statusComponent={progressComponent}
|
statusComponent={progressComponent}
|
||||||
clickHandler={startExport}
|
clickHandler={startExport}
|
||||||
styles={props.styles}
|
styles={props.styles}
|
||||||
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|||||||
import Setting, { AppType, SettingMetadataSection } from '@joplin/lib/models/Setting';
|
import Setting, { AppType, SettingMetadataSection } from '@joplin/lib/models/Setting';
|
||||||
import { FunctionComponent, useEffect, useMemo, useState } from 'react';
|
import { FunctionComponent, useEffect, useMemo, useState } from 'react';
|
||||||
import { ConfigScreenStyles } from './configScreenStyles';
|
import { ConfigScreenStyles } from './configScreenStyles';
|
||||||
import { FlatList, Text, Pressable, View } from 'react-native';
|
import { FlatList, Text, Pressable, View, ViewStyle } from 'react-native';
|
||||||
import { settingsSections } from '@joplin/lib/components/shared/config/config-shared';
|
import { settingsSections } from '@joplin/lib/components/shared/config/config-shared';
|
||||||
import Icon from '../../Icon';
|
import Icon from '../../Icon';
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ const SectionSelector: FunctionComponent<Props> = props => {
|
|||||||
</Text>
|
</Text>
|
||||||
<Text
|
<Text
|
||||||
style={styles.sidebarButtonDescriptionText}
|
style={styles.sidebarButtonDescriptionText}
|
||||||
numberOfLines={2}
|
numberOfLines={1}
|
||||||
ellipsizeMode='tail'
|
ellipsizeMode='tail'
|
||||||
>
|
>
|
||||||
{shortDescription ?? ''}
|
{shortDescription ?? ''}
|
||||||
@@ -82,8 +82,15 @@ const SectionSelector: FunctionComponent<Props> = props => {
|
|||||||
}
|
}
|
||||||
}, [props.selectedSectionName, flatListRef, sections]);
|
}, [props.selectedSectionName, flatListRef, sections]);
|
||||||
|
|
||||||
|
const containerStyle: ViewStyle = useMemo(() => ({
|
||||||
|
width: props.width,
|
||||||
|
maxWidth: props.width,
|
||||||
|
minWidth: props.width,
|
||||||
|
flex: 1,
|
||||||
|
}), [props.width]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={{ width: props.width, flexDirection: 'column' }}>
|
<View style={containerStyle}>
|
||||||
<FlatList
|
<FlatList
|
||||||
role='tablist'
|
role='tablist'
|
||||||
ref={setFlatListRef}
|
ref={setFlatListRef}
|
||||||
|
@@ -86,11 +86,13 @@ const configScreenStyles = (themeId: number): ConfigScreenStyles => {
|
|||||||
fontSize: theme.fontSize,
|
fontSize: theme.fontSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const fadedOpacity = 0.75;
|
||||||
const sidebarButtonDescriptionText: TextStyle = {
|
const sidebarButtonDescriptionText: TextStyle = {
|
||||||
...sidebarButtonMainText,
|
...sidebarButtonMainText,
|
||||||
fontSize: theme.fontSizeSmaller,
|
fontSize: theme.fontSizeSmaller,
|
||||||
color: theme.color,
|
color: theme.color,
|
||||||
opacity: 0.75,
|
opacity: fadedOpacity,
|
||||||
|
paddingTop: 3,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@@ -185,6 +187,7 @@ const configScreenStyles = (themeId: number): ConfigScreenStyles => {
|
|||||||
textAlign: 'center',
|
textAlign: 'center',
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
width: sidebarButtonHeight * 0.8,
|
width: sidebarButtonHeight * 0.8,
|
||||||
|
opacity: fadedOpacity,
|
||||||
},
|
},
|
||||||
sidebarSelectedButtonText: {
|
sidebarSelectedButtonText: {
|
||||||
...sidebarButtonMainText,
|
...sidebarButtonMainText,
|
||||||
|
@@ -61,6 +61,9 @@ const emptyArray: any[] = [];
|
|||||||
const logger = Logger.create('screens/Note');
|
const logger = Logger.create('screens/Note');
|
||||||
|
|
||||||
class NoteScreenComponent extends BaseScreenComponent {
|
class NoteScreenComponent extends BaseScreenComponent {
|
||||||
|
// This isn't in this.state because we don't want changing scroll to trigger
|
||||||
|
// a re-render.
|
||||||
|
private lastBodyScroll: number|undefined = undefined;
|
||||||
|
|
||||||
public static navigationOptions(): any {
|
public static navigationOptions(): any {
|
||||||
return { header: null };
|
return { header: null };
|
||||||
@@ -81,7 +84,6 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
fromShare: false,
|
fromShare: false,
|
||||||
showCamera: false,
|
showCamera: false,
|
||||||
showImageEditor: false,
|
showImageEditor: false,
|
||||||
loadImageEditorData: null,
|
|
||||||
imageEditorResource: null,
|
imageEditorResource: null,
|
||||||
noteResources: {},
|
noteResources: {},
|
||||||
|
|
||||||
@@ -747,7 +749,11 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
if (this.useEditorBeta()) {
|
if (this.useEditorBeta()) {
|
||||||
// The beta editor needs to be explicitly informed of changes
|
// The beta editor needs to be explicitly informed of changes
|
||||||
// to the note's body
|
// to the note's body
|
||||||
this.editorRef.current.insertText(newText);
|
if (this.editorRef.current) {
|
||||||
|
this.editorRef.current.insertText(newText);
|
||||||
|
} else {
|
||||||
|
logger.error(`Tried to attach resource ${resource.id} to the note when the editor is not visible!`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newNote.body += `\n${resourceTag}`;
|
newNote.body += `\n${resourceTag}`;
|
||||||
@@ -812,31 +818,34 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
}, 'image');
|
}, 'image');
|
||||||
}
|
}
|
||||||
|
|
||||||
private drawPicture_onPress = async () => {
|
|
||||||
// Create a new empty drawing and attach it now.
|
|
||||||
const resource = await this.attachNewDrawing('');
|
|
||||||
await this.editDrawing(resource);
|
|
||||||
};
|
|
||||||
|
|
||||||
private async updateDrawing(svgData: string) {
|
private async updateDrawing(svgData: string) {
|
||||||
let resource: ResourceEntity|null = this.state.imageEditorResource;
|
let resource: ResourceEntity|null = this.state.imageEditorResource;
|
||||||
|
|
||||||
if (!resource) {
|
if (!resource) {
|
||||||
throw new Error('No resource is loaded in the editor');
|
resource = await this.attachNewDrawing(svgData);
|
||||||
|
|
||||||
|
// Set resouce and file path to allow
|
||||||
|
// 1. subsequent saves to update the resource
|
||||||
|
// 2. the editor to load from the resource's filepath (can happen
|
||||||
|
// if the webview is reloaded).
|
||||||
|
this.setState({
|
||||||
|
imageEditorResourceFilepath: Resource.fullPath(resource),
|
||||||
|
imageEditorResource: resource,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.info('Saving drawing to resource', resource.id);
|
||||||
|
|
||||||
|
const tempFilePath = join(Setting.value('tempDir'), uuid.createNano());
|
||||||
|
await shim.fsDriver().writeFile(tempFilePath, svgData, 'utf8');
|
||||||
|
|
||||||
|
resource = await Resource.updateResourceBlobContent(
|
||||||
|
resource.id,
|
||||||
|
tempFilePath,
|
||||||
|
);
|
||||||
|
await shim.fsDriver().remove(tempFilePath);
|
||||||
|
|
||||||
|
await this.refreshResource(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info('Saving drawing to resource', resource.id);
|
|
||||||
|
|
||||||
const tempFilePath = join(Setting.value('tempDir'), uuid.createNano());
|
|
||||||
await shim.fsDriver().writeFile(tempFilePath, svgData, 'utf8');
|
|
||||||
|
|
||||||
resource = await Resource.updateResourceBlobContent(
|
|
||||||
resource.id,
|
|
||||||
tempFilePath,
|
|
||||||
);
|
|
||||||
await shim.fsDriver().remove(tempFilePath);
|
|
||||||
|
|
||||||
await this.refreshResource(resource);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private onSaveDrawing = async (svgData: string) => {
|
private onSaveDrawing = async (svgData: string) => {
|
||||||
@@ -847,13 +856,28 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
this.setState({ showImageEditor: false });
|
this.setState({ showImageEditor: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private drawPicture_onPress = async () => {
|
||||||
|
if (this.state.mode === 'edit') {
|
||||||
|
// Create a new empty drawing and attach it now, before the image editor is opened.
|
||||||
|
// With the present structure of Note.tsx, the we can't use this.editorRef while
|
||||||
|
// the image editor is open, and thus can't attach drawings at the cursor locaiton.
|
||||||
|
const resource = await this.attachNewDrawing('');
|
||||||
|
await this.editDrawing(resource);
|
||||||
|
} else {
|
||||||
|
logger.info('Showing image editor...');
|
||||||
|
this.setState({
|
||||||
|
showImageEditor: true,
|
||||||
|
imageEditorResourceFilepath: null,
|
||||||
|
imageEditorResource: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private async editDrawing(item: BaseItem) {
|
private async editDrawing(item: BaseItem) {
|
||||||
const filePath = Resource.fullPath(item);
|
const filePath = Resource.fullPath(item);
|
||||||
this.setState({
|
this.setState({
|
||||||
showImageEditor: true,
|
showImageEditor: true,
|
||||||
loadImageEditorData: async () => {
|
imageEditorResourceFilepath: filePath,
|
||||||
return await shim.fsDriver().readFile(filePath);
|
|
||||||
},
|
|
||||||
imageEditorResource: item,
|
imageEditorResource: item,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -1260,6 +1284,10 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
}, 5);
|
}, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private onBodyViewerScroll = (scrollTop: number) => {
|
||||||
|
this.lastBodyScroll = scrollTop;
|
||||||
|
};
|
||||||
|
|
||||||
public onBodyViewerCheckboxChange(newBody: string) {
|
public onBodyViewerCheckboxChange(newBody: string) {
|
||||||
void this.saveOneProperty('body', newBody);
|
void this.saveOneProperty('body', newBody);
|
||||||
}
|
}
|
||||||
@@ -1302,7 +1330,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
return <CameraView themeId={this.props.themeId} style={{ flex: 1 }} onPhoto={this.cameraView_onPhoto} onCancel={this.cameraView_onCancel} />;
|
return <CameraView themeId={this.props.themeId} style={{ flex: 1 }} onPhoto={this.cameraView_onPhoto} onCancel={this.cameraView_onCancel} />;
|
||||||
} else if (this.state.showImageEditor) {
|
} else if (this.state.showImageEditor) {
|
||||||
return <ImageEditor
|
return <ImageEditor
|
||||||
loadInitialSVGData={this.state.loadImageEditorData}
|
resourceFilename={this.state.imageEditorResourceFilepath}
|
||||||
themeId={this.props.themeId}
|
themeId={this.props.themeId}
|
||||||
onSave={this.onSaveDrawing}
|
onSave={this.onSaveDrawing}
|
||||||
onExit={this.onCloseDrawing}
|
onExit={this.onCloseDrawing}
|
||||||
@@ -1334,6 +1362,8 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
onMarkForDownload={this.onMarkForDownload}
|
onMarkForDownload={this.onMarkForDownload}
|
||||||
onRequestEditResource={this.onEditResource}
|
onRequestEditResource={this.onEditResource}
|
||||||
onLoadEnd={this.onBodyViewerLoadEnd}
|
onLoadEnd={this.onBodyViewerLoadEnd}
|
||||||
|
onScroll={this.onBodyViewerScroll}
|
||||||
|
initialScroll={this.lastBodyScroll}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
@@ -1416,7 +1446,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
|||||||
|
|
||||||
if (this.state.mode === 'edit') return null;
|
if (this.state.mode === 'edit') return null;
|
||||||
|
|
||||||
return <ActionButton mainButton={editButton} />;
|
return <ActionButton mainButton={editButton} dispatch={this.props.dispatch} />;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Save button is not really needed anymore with the improved save logic
|
// Save button is not really needed anymore with the improved save logic
|
||||||
|
@@ -16,6 +16,7 @@ const DialogBox = require('react-native-dialogbox').default;
|
|||||||
const { BaseScreenComponent } = require('../base-screen');
|
const { BaseScreenComponent } = require('../base-screen');
|
||||||
const { BackButtonService } = require('../../services/back-button.js');
|
const { BackButtonService } = require('../../services/back-button.js');
|
||||||
import { AppState } from '../../utils/types';
|
import { AppState } from '../../utils/types';
|
||||||
|
const { ALL_NOTES_FILTER_ID } = require('@joplin/lib/reserved-ids.js');
|
||||||
|
|
||||||
class NotesScreenComponent extends BaseScreenComponent<any> {
|
class NotesScreenComponent extends BaseScreenComponent<any> {
|
||||||
|
|
||||||
@@ -108,7 +109,7 @@ class NotesScreenComponent extends BaseScreenComponent<any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async componentDidUpdate(prevProps: any) {
|
public async componentDidUpdate(prevProps: any) {
|
||||||
if (prevProps.notesOrder !== this.props.notesOrder || prevProps.selectedFolderId !== this.props.selectedFolderId || prevProps.selectedTagId !== this.props.selectedTagId || prevProps.selectedSmartFilterId !== this.props.selectedSmartFilterId || prevProps.notesParentType !== this.props.notesParentType) {
|
if (prevProps.notesOrder !== this.props.notesOrder || prevProps.selectedFolderId !== this.props.selectedFolderId || prevProps.selectedTagId !== this.props.selectedTagId || prevProps.selectedSmartFilterId !== this.props.selectedSmartFilterId || prevProps.notesParentType !== this.props.notesParentType || prevProps.uncompletedTodosOnTop !== this.props.uncompletedTodosOnTop || prevProps.showCompletedTodos !== this.props.showCompletedTodos) {
|
||||||
await this.refreshNotes(this.props);
|
await this.refreshNotes(this.props);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,17 +224,32 @@ class NotesScreenComponent extends BaseScreenComponent<any> {
|
|||||||
let buttonFolderId = this.props.selectedFolderId !== Folder.conflictFolderId() ? this.props.selectedFolderId : null;
|
let buttonFolderId = this.props.selectedFolderId !== Folder.conflictFolderId() ? this.props.selectedFolderId : null;
|
||||||
if (!buttonFolderId) buttonFolderId = this.props.activeFolderId;
|
if (!buttonFolderId) buttonFolderId = this.props.activeFolderId;
|
||||||
|
|
||||||
const addFolderNoteButtons = !!buttonFolderId;
|
const isAllNotes =
|
||||||
|
this.props.notesParentType === 'SmartFilter'
|
||||||
|
&& this.props.selectedSmartFilterId === ALL_NOTES_FILTER_ID;
|
||||||
|
|
||||||
|
// Usually, when showing all notes, activeFolderId/selectedFolderId is set to the last
|
||||||
|
// active folder.
|
||||||
|
// If the app starts showing all notes, activeFolderId/selectedFolderId are
|
||||||
|
// empty or null. As such, we need a special case to show the buttons:
|
||||||
|
const addFolderNoteButtons = !!buttonFolderId || isAllNotes;
|
||||||
const thisComp = this;
|
const thisComp = this;
|
||||||
|
|
||||||
const makeActionButtonComp = () => {
|
const makeActionButtonComp = () => {
|
||||||
|
const getTargetFolderId = async () => {
|
||||||
|
if (!buttonFolderId && isAllNotes) {
|
||||||
|
return (await Folder.defaultFolder()).id;
|
||||||
|
}
|
||||||
|
return buttonFolderId;
|
||||||
|
};
|
||||||
if (addFolderNoteButtons && this.props.folders.length > 0) {
|
if (addFolderNoteButtons && this.props.folders.length > 0) {
|
||||||
const buttons = [];
|
const buttons = [];
|
||||||
buttons.push({
|
buttons.push({
|
||||||
label: _('New to-do'),
|
label: _('New to-do'),
|
||||||
onPress: () => {
|
onPress: async () => {
|
||||||
|
const folderId = await getTargetFolderId();
|
||||||
const isTodo = true;
|
const isTodo = true;
|
||||||
void this.newNoteNavigate(buttonFolderId, isTodo);
|
void this.newNoteNavigate(folderId, isTodo);
|
||||||
},
|
},
|
||||||
color: '#9b59b6',
|
color: '#9b59b6',
|
||||||
icon: 'checkbox-outline',
|
icon: 'checkbox-outline',
|
||||||
@@ -241,14 +257,15 @@ class NotesScreenComponent extends BaseScreenComponent<any> {
|
|||||||
|
|
||||||
buttons.push({
|
buttons.push({
|
||||||
label: _('New note'),
|
label: _('New note'),
|
||||||
onPress: () => {
|
onPress: async () => {
|
||||||
|
const folderId = await getTargetFolderId();
|
||||||
const isTodo = false;
|
const isTodo = false;
|
||||||
void this.newNoteNavigate(buttonFolderId, isTodo);
|
void this.newNoteNavigate(folderId, isTodo);
|
||||||
},
|
},
|
||||||
color: '#9b59b6',
|
color: '#9b59b6',
|
||||||
icon: 'document',
|
icon: 'document',
|
||||||
});
|
});
|
||||||
return <ActionButton buttons={buttons}/>;
|
return <ActionButton buttons={buttons} dispatch={this.props.dispatch}/>;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
@@ -1,17 +1,13 @@
|
|||||||
const gulp = require('gulp');
|
const gulp = require('gulp');
|
||||||
const utils = require('@joplin/tools/gulp/utils');
|
const utils = require('@joplin/tools/gulp/utils');
|
||||||
import { buildInjectedJS, watchInjectedJS } from './tools/buildInjectedJs';
|
|
||||||
|
import gulpTasks from './tools/buildInjectedJs/gulpTasks';
|
||||||
|
|
||||||
const tasks = {
|
const tasks = {
|
||||||
encodeAssets: {
|
encodeAssets: {
|
||||||
fn: require('./tools/encodeAssets'),
|
fn: require('./tools/encodeAssets'),
|
||||||
},
|
},
|
||||||
buildInjectedJs: {
|
...gulpTasks,
|
||||||
fn: buildInjectedJS,
|
|
||||||
},
|
|
||||||
watchInjectedJs: {
|
|
||||||
fn: watchInjectedJS,
|
|
||||||
},
|
|
||||||
podInstall: {
|
podInstall: {
|
||||||
fn: require('./tools/podInstall'),
|
fn: require('./tools/podInstall'),
|
||||||
},
|
},
|
||||||
@@ -19,6 +15,22 @@ const tasks = {
|
|||||||
|
|
||||||
utils.registerGulpTasks(gulp, tasks);
|
utils.registerGulpTasks(gulp, tasks);
|
||||||
|
|
||||||
|
gulp.task('buildInjectedJs', gulp.series(
|
||||||
|
'beforeBundle',
|
||||||
|
'buildCodeMirrorEditor',
|
||||||
|
'buildJsDrawEditor',
|
||||||
|
'copyWebviewLib',
|
||||||
|
));
|
||||||
|
|
||||||
|
gulp.task('watchInjectedJs', gulp.series(
|
||||||
|
'beforeBundle',
|
||||||
|
'copyWebviewLib',
|
||||||
|
gulp.parallel(
|
||||||
|
'watchCodeMirrorEditor',
|
||||||
|
'watchJsDrawEditor',
|
||||||
|
),
|
||||||
|
));
|
||||||
|
|
||||||
gulp.task('build', gulp.series(
|
gulp.task('build', gulp.series(
|
||||||
'buildInjectedJs',
|
'buildInjectedJs',
|
||||||
'encodeAssets',
|
'encodeAssets',
|
||||||
|
@@ -523,13 +523,13 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 101;
|
CURRENT_PROJECT_VERSION = 106;
|
||||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
INFOPLIST_FILE = Joplin/Info.plist;
|
INFOPLIST_FILE = Joplin/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
MARKETING_VERSION = 12.13.4;
|
MARKETING_VERSION = 12.13.9;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"-ObjC",
|
"-ObjC",
|
||||||
@@ -552,12 +552,12 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Joplin/Joplin.entitlements;
|
||||||
CURRENT_PROJECT_VERSION = 101;
|
CURRENT_PROJECT_VERSION = 106;
|
||||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||||
INFOPLIST_FILE = Joplin/Info.plist;
|
INFOPLIST_FILE = Joplin/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
MARKETING_VERSION = 12.13.4;
|
MARKETING_VERSION = 12.13.9;
|
||||||
OTHER_LDFLAGS = (
|
OTHER_LDFLAGS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"-ObjC",
|
"-ObjC",
|
||||||
@@ -704,14 +704,14 @@
|
|||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 101;
|
CURRENT_PROJECT_VERSION = 106;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
MARKETING_VERSION = 12.13.4;
|
MARKETING_VERSION = 12.13.9;
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
|
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
|
||||||
@@ -735,14 +735,14 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
CODE_SIGN_ENTITLEMENTS = ShareExtension/ShareExtension.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
CURRENT_PROJECT_VERSION = 101;
|
CURRENT_PROJECT_VERSION = 106;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
DEVELOPMENT_TEAM = A9BXAFS6CT;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
INFOPLIST_FILE = ShareExtension/Info.plist;
|
INFOPLIST_FILE = ShareExtension/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 11.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
|
||||||
MARKETING_VERSION = 12.13.4;
|
MARKETING_VERSION = 12.13.9;
|
||||||
MTL_FAST_MATH = YES;
|
MTL_FAST_MATH = YES;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
|
PRODUCT_BUNDLE_IDENTIFIER = net.cozic.joplin.ShareExtension;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
@@ -359,9 +359,9 @@ PODS:
|
|||||||
- React-Core
|
- React-Core
|
||||||
- react-native-rsa-native (2.0.5):
|
- react-native-rsa-native (2.0.5):
|
||||||
- React
|
- React
|
||||||
- react-native-saf-x (2.13.0):
|
- react-native-saf-x (2.13.3):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-safe-area-context (4.7.2):
|
- react-native-safe-area-context (4.7.4):
|
||||||
- React-Core
|
- React-Core
|
||||||
- react-native-slider (4.4.3):
|
- react-native-slider (4.4.3):
|
||||||
- React-Core
|
- React-Core
|
||||||
@@ -479,7 +479,8 @@ PODS:
|
|||||||
- React
|
- React
|
||||||
- RNShare (9.4.1):
|
- RNShare (9.4.1):
|
||||||
- React-Core
|
- React-Core
|
||||||
- RNVectorIcons (10.0.0):
|
- RNVectorIcons (10.0.1):
|
||||||
|
- RCT-Folly (= 2021.07.22.00)
|
||||||
- React-Core
|
- React-Core
|
||||||
- RNZipArchive (6.1.0):
|
- RNZipArchive (6.1.0):
|
||||||
- React-Core
|
- React-Core
|
||||||
@@ -786,8 +787,8 @@ SPEC CHECKSUMS:
|
|||||||
react-native-image-resizer: 681f7607418b97c084ba2d0999b153b103040d8a
|
react-native-image-resizer: 681f7607418b97c084ba2d0999b153b103040d8a
|
||||||
react-native-netinfo: fefd4e98d75cbdd6e85fc530f7111a8afdf2b0c5
|
react-native-netinfo: fefd4e98d75cbdd6e85fc530f7111a8afdf2b0c5
|
||||||
react-native-rsa-native: 12132eb627797529fdb1f0d22fd0f8f9678df64a
|
react-native-rsa-native: 12132eb627797529fdb1f0d22fd0f8f9678df64a
|
||||||
react-native-saf-x: b758d1b38a96a96b8179a16e96ed966a507ca5c2
|
react-native-saf-x: 0f7531c9f8bdbb62bbd55ceb7433de7bb756cd73
|
||||||
react-native-safe-area-context: 7aa8e6d9d0f3100a820efb1a98af68aa747f9284
|
react-native-safe-area-context: 2cd91d532de12acdb0a9cbc8d43ac72a8e4c897c
|
||||||
react-native-slider: 1cdd6ba29675df21f30544253bf7351d3c2d68c4
|
react-native-slider: 1cdd6ba29675df21f30544253bf7351d3c2d68c4
|
||||||
react-native-sqlite-storage: f6d515e1c446d1e6d026aa5352908a25d4de3261
|
react-native-sqlite-storage: f6d515e1c446d1e6d026aa5352908a25d4de3261
|
||||||
react-native-version-info: a106f23009ac0db4ee00de39574eb546682579b9
|
react-native-version-info: a106f23009ac0db4ee00de39574eb546682579b9
|
||||||
@@ -817,7 +818,7 @@ SPEC CHECKSUMS:
|
|||||||
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93
|
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93
|
||||||
RNSecureRandom: 07efbdf2cd99efe13497433668e54acd7df49fef
|
RNSecureRandom: 07efbdf2cd99efe13497433668e54acd7df49fef
|
||||||
RNShare: 32e97adc8d8c97d4a26bcdd3c45516882184f8b6
|
RNShare: 32e97adc8d8c97d4a26bcdd3c45516882184f8b6
|
||||||
RNVectorIcons: 8b5bb0fa61d54cd2020af4f24a51841ce365c7e9
|
RNVectorIcons: ace237de89f1574ef3c963ae9d5da3bd6fbeb02a
|
||||||
RNZipArchive: ef9451b849c45a29509bf44e65b788829ab07801
|
RNZipArchive: ef9451b849c45a29509bf44e65b788829ab07801
|
||||||
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
|
SocketRocket: fccef3f9c5cedea1353a9ef6ada904fde10d6608
|
||||||
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
|
SSZipArchive: fe6a26b2a54d5a0890f2567b5cc6de5caa600aef
|
||||||
@@ -826,4 +827,4 @@ SPEC CHECKSUMS:
|
|||||||
|
|
||||||
PODFILE CHECKSUM: 3b2cace838120977b5b54871752c9dddf5a11cea
|
PODFILE CHECKSUM: 3b2cace838120977b5b54871752c9dddf5a11cea
|
||||||
|
|
||||||
COCOAPODS: 1.14.2
|
COCOAPODS: 1.12.1
|
||||||
|
@@ -62,13 +62,13 @@
|
|||||||
"react-native-popup-menu": "0.16.1",
|
"react-native-popup-menu": "0.16.1",
|
||||||
"react-native-quick-actions": "0.3.13",
|
"react-native-quick-actions": "0.3.13",
|
||||||
"react-native-rsa-native": "2.0.5",
|
"react-native-rsa-native": "2.0.5",
|
||||||
"react-native-safe-area-context": "4.7.2",
|
"react-native-safe-area-context": "4.7.4",
|
||||||
"react-native-securerandom": "1.0.1",
|
"react-native-securerandom": "1.0.1",
|
||||||
"react-native-share": "9.4.1",
|
"react-native-share": "9.4.1",
|
||||||
"react-native-side-menu-updated": "1.3.2",
|
"react-native-side-menu-updated": "1.3.2",
|
||||||
"react-native-sqlite-storage": "6.0.1",
|
"react-native-sqlite-storage": "6.0.1",
|
||||||
"react-native-url-polyfill": "2.0.0",
|
"react-native-url-polyfill": "2.0.0",
|
||||||
"react-native-vector-icons": "10.0.0",
|
"react-native-vector-icons": "10.0.1",
|
||||||
"react-native-version-info": "1.1.1",
|
"react-native-version-info": "1.1.1",
|
||||||
"react-native-vosk": "0.1.12",
|
"react-native-vosk": "0.1.12",
|
||||||
"react-native-webview": "13.6.2",
|
"react-native-webview": "13.6.2",
|
||||||
@@ -88,14 +88,14 @@
|
|||||||
"@babel/preset-env": "7.20.2",
|
"@babel/preset-env": "7.20.2",
|
||||||
"@babel/runtime": "7.20.0",
|
"@babel/runtime": "7.20.0",
|
||||||
"@joplin/tools": "~2.13",
|
"@joplin/tools": "~2.13",
|
||||||
"@js-draw/material-icons": "1.5.0",
|
"@js-draw/material-icons": "1.13.2",
|
||||||
"@lezer/highlight": "1.1.4",
|
"@lezer/highlight": "1.1.4",
|
||||||
"@testing-library/jest-native": "5.4.3",
|
"@testing-library/jest-native": "5.4.3",
|
||||||
"@testing-library/react-native": "12.3.0",
|
"@testing-library/react-native": "12.3.1",
|
||||||
"@tsconfig/react-native": "2.0.2",
|
"@tsconfig/react-native": "2.0.2",
|
||||||
"@types/fs-extra": "11.0.3",
|
"@types/fs-extra": "11.0.3",
|
||||||
"@types/jest": "29.5.5",
|
"@types/jest": "29.5.5",
|
||||||
"@types/react": "18.2.31",
|
"@types/react": "18.2.33",
|
||||||
"@types/react-native": "0.70.6",
|
"@types/react-native": "0.70.6",
|
||||||
"@types/react-redux": "7.1.28",
|
"@types/react-redux": "7.1.28",
|
||||||
"@types/tar-stream": "2.2.3",
|
"@types/tar-stream": "2.2.3",
|
||||||
@@ -106,7 +106,7 @@
|
|||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
"jest-environment-jsdom": "29.7.0",
|
"jest-environment-jsdom": "29.7.0",
|
||||||
"jetifier": "2.0.0",
|
"jetifier": "2.0.0",
|
||||||
"js-draw": "1.5.0",
|
"js-draw": "1.13.2",
|
||||||
"jsdom": "22.1.0",
|
"jsdom": "22.1.0",
|
||||||
"metro-react-native-babel-preset": "0.73.9",
|
"metro-react-native-babel-preset": "0.73.9",
|
||||||
"nodemon": "3.0.1",
|
"nodemon": "3.0.1",
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
hash:"7d3976ee03fc0f6880dd54c78d1a325b", files: {
|
hash:"0f14c5392fd01275e9aec0841c384482", files: {
|
||||||
'highlight.js/atom-one-dark-reasonable.css': { data: require('./highlight.js/atom-one-dark-reasonable.css.base64.js'), mime: 'text/css', encoding: 'base64' },
|
'highlight.js/atom-one-dark-reasonable.css': { data: require('./highlight.js/atom-one-dark-reasonable.css.base64.js'), mime: 'text/css', encoding: 'base64' },
|
||||||
'highlight.js/atom-one-light.css': { data: require('./highlight.js/atom-one-light.css.base64.js'), mime: 'text/css', encoding: 'base64' },
|
'highlight.js/atom-one-light.css': { data: require('./highlight.js/atom-one-light.css.base64.js'), mime: 'text/css', encoding: 'base64' },
|
||||||
'katex/fonts/KaTeX_AMS-Regular.woff2': { data: require('./katex/fonts/KaTeX_AMS-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
|
'katex/fonts/KaTeX_AMS-Regular.woff2': { data: require('./katex/fonts/KaTeX_AMS-Regular.woff2.base64.js'), mime: 'application/octet-stream', encoding: 'base64' },
|
||||||
|
File diff suppressed because one or more lines are too long
@@ -317,6 +317,7 @@ const appReducer = (state = appDefaultState, action: any) => {
|
|||||||
|
|
||||||
if ('smartFilterId' in action) {
|
if ('smartFilterId' in action) {
|
||||||
newState.smartFilterId = action.smartFilterId;
|
newState.smartFilterId = action.smartFilterId;
|
||||||
|
newState.selectedSmartFilterId = action.smartFilterId;
|
||||||
newState.notesParentType = 'SmartFilter';
|
newState.notesParentType = 'SmartFilter';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1032,10 +1033,13 @@ class AppComponent extends React.Component {
|
|||||||
|
|
||||||
let sideMenuContent: ReactNode = null;
|
let sideMenuContent: ReactNode = null;
|
||||||
let menuPosition: SideMenuPosition = 'left';
|
let menuPosition: SideMenuPosition = 'left';
|
||||||
|
let disableSideMenuGestures = this.props.disableSideMenuGestures;
|
||||||
|
|
||||||
if (this.props.routeName === 'Note') {
|
if (this.props.routeName === 'Note') {
|
||||||
sideMenuContent = <SafeAreaView style={{ flex: 1, backgroundColor: theme.backgroundColor }}><SideMenuContentNote options={this.props.noteSideMenuOptions}/></SafeAreaView>;
|
sideMenuContent = <SafeAreaView style={{ flex: 1, backgroundColor: theme.backgroundColor }}><SideMenuContentNote options={this.props.noteSideMenuOptions}/></SafeAreaView>;
|
||||||
menuPosition = 'right';
|
menuPosition = 'right';
|
||||||
|
} else if (this.props.routeName === 'Config') {
|
||||||
|
disableSideMenuGestures = true;
|
||||||
} else {
|
} else {
|
||||||
sideMenuContent = <SafeAreaView style={{ flex: 1, backgroundColor: theme.backgroundColor }}><SideMenuContent/></SafeAreaView>;
|
sideMenuContent = <SafeAreaView style={{ flex: 1, backgroundColor: theme.backgroundColor }}><SideMenuContent/></SafeAreaView>;
|
||||||
}
|
}
|
||||||
@@ -1076,7 +1080,7 @@ class AppComponent extends React.Component {
|
|||||||
openMenuOffset={this.state.sideMenuWidth}
|
openMenuOffset={this.state.sideMenuWidth}
|
||||||
menuPosition={menuPosition}
|
menuPosition={menuPosition}
|
||||||
onChange={(isOpen: boolean) => this.sideMenu_change(isOpen)}
|
onChange={(isOpen: boolean) => this.sideMenu_change(isOpen)}
|
||||||
disableGestures={this.props.disableSideMenuGestures}
|
disableGestures={disableSideMenuGestures}
|
||||||
onSliding={(percent: number) => {
|
onSliding={(percent: number) => {
|
||||||
this.props.dispatch({
|
this.props.dispatch({
|
||||||
type: 'SIDE_MENU_OPEN_PERCENT',
|
type: 'SIDE_MENU_OPEN_PERCENT',
|
||||||
|
@@ -3,30 +3,14 @@
|
|||||||
// files: First here we convert the JS file to a plain string, and that string
|
// files: First here we convert the JS file to a plain string, and that string
|
||||||
// is then loaded by eg. the Mermaid plugin, and finally injected in the WebView.
|
// is then loaded by eg. the Mermaid plugin, and finally injected in the WebView.
|
||||||
|
|
||||||
import { mkdirp, readFile, writeFile } from 'fs-extra';
|
|
||||||
import { dirname, extname, basename } from 'path';
|
import { dirname, extname, basename } from 'path';
|
||||||
|
|
||||||
// We need this to be transpiled to `const webpack = require('webpack')`.
|
// We need this to be transpiled to `const webpack = require('webpack')`.
|
||||||
// As such, do a namespace import. See https://www.typescriptlang.org/tsconfig#esModuleInterop
|
// As such, do a namespace import. See https://www.typescriptlang.org/tsconfig#esModuleInterop
|
||||||
import * as webpack from 'webpack';
|
import * as webpack from 'webpack';
|
||||||
|
import copyJs from './copyJs';
|
||||||
|
|
||||||
const rootDir = dirname(dirname(dirname(__dirname)));
|
export default class BundledFile {
|
||||||
const mobileDir = `${rootDir}/packages/app-mobile`;
|
|
||||||
const outputDir = `${mobileDir}/lib/rnInjectedJs`;
|
|
||||||
|
|
||||||
// Stores the contents of the file at [filePath] as an importable string.
|
|
||||||
// [name] should be the name (excluding the .js extension) of the output file that will contain
|
|
||||||
// the JSON-ified file content.
|
|
||||||
async function copyJs(name: string, filePath: string) {
|
|
||||||
const outputPath = `${outputDir}/${name}.js`;
|
|
||||||
console.info(`Creating: ${outputPath}`);
|
|
||||||
const js = await readFile(filePath, 'utf-8');
|
|
||||||
const json = `module.exports = ${JSON.stringify(js)};`;
|
|
||||||
await writeFile(outputPath, json);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class BundledFile {
|
|
||||||
private readonly bundleOutputPath: string;
|
private readonly bundleOutputPath: string;
|
||||||
private readonly bundleBaseName: string;
|
private readonly bundleBaseName: string;
|
||||||
private readonly rootFileDirectory: string;
|
private readonly rootFileDirectory: string;
|
||||||
@@ -82,17 +66,23 @@ class BundledFile {
|
|||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Creates a file that can be imported by React native. This file contains the
|
||||||
|
// bundled JS as a string.
|
||||||
|
private async copyToImportableFile() {
|
||||||
|
await copyJs(`${this.bundleBaseName}.bundle`, this.bundleOutputPath);
|
||||||
|
}
|
||||||
|
|
||||||
private handleErrors(error: Error | undefined | null, stats: webpack.Stats | undefined): boolean {
|
private handleErrors(error: Error | undefined | null, stats: webpack.Stats | undefined): boolean {
|
||||||
let failed = false;
|
let failed = false;
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error(`Error: ${error.name}`, error.message, error.stack);
|
console.error(`Error (${this.bundleName}): ${error.name}`, error.message, error.stack);
|
||||||
failed = true;
|
failed = true;
|
||||||
} else if (stats?.hasErrors() || stats?.hasWarnings()) {
|
} else if (stats?.hasErrors() || stats?.hasWarnings()) {
|
||||||
const data = stats.toJson();
|
const data = stats.toJson();
|
||||||
|
|
||||||
if (data.warnings && data.warningsCount) {
|
if (data.warnings && data.warningsCount) {
|
||||||
console.warn('Warnings: ', data.warningsCount);
|
console.warn(`Warnings (${this.bundleName}): `, data.warningsCount);
|
||||||
for (const warning of data.warnings) {
|
for (const warning of data.warnings) {
|
||||||
// Stack contains the message
|
// Stack contains the message
|
||||||
if (warning.stack) {
|
if (warning.stack) {
|
||||||
@@ -103,7 +93,7 @@ class BundledFile {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (data.errors && data.errorsCount) {
|
if (data.errors && data.errorsCount) {
|
||||||
console.error('Errors: ', data.errorsCount);
|
console.error(`Errors (${this.bundleName}): `, data.errorsCount);
|
||||||
for (const error of data.errors) {
|
for (const error of data.errors) {
|
||||||
if (error.stack) {
|
if (error.stack) {
|
||||||
console.error(error.stack);
|
console.error(error.stack);
|
||||||
@@ -127,19 +117,34 @@ class BundledFile {
|
|||||||
return new Promise<void>((resolve, reject) => {
|
return new Promise<void>((resolve, reject) => {
|
||||||
console.info(`Building bundle: ${this.bundleName}...`);
|
console.info(`Building bundle: ${this.bundleName}...`);
|
||||||
|
|
||||||
compiler.run((error, stats) => {
|
compiler.run((buildError, stats) => {
|
||||||
let failed = this.handleErrors(error, stats);
|
// Always output stats, even on success
|
||||||
|
console.log(`Bundle ${this.bundleName} built: `, stats?.toString());
|
||||||
|
|
||||||
|
let failed = this.handleErrors(buildError, stats);
|
||||||
|
|
||||||
// Clean up.
|
// Clean up.
|
||||||
compiler.close(async (error) => {
|
compiler.close(async (closeError) => {
|
||||||
if (error) {
|
if (closeError) {
|
||||||
console.error('Error cleaning up:', error);
|
console.error('Error cleaning up:', closeError);
|
||||||
failed = true;
|
failed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let copyError;
|
||||||
|
if (!failed) {
|
||||||
|
try {
|
||||||
|
await this.copyToImportableFile();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error copying', error);
|
||||||
|
failed = true;
|
||||||
|
copyError = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!failed) {
|
if (!failed) {
|
||||||
resolve();
|
resolve();
|
||||||
} else {
|
} else {
|
||||||
reject();
|
reject(closeError ?? buildError ?? copyError);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -160,44 +165,4 @@ class BundledFile {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a file that can be imported by React native. This file contains the
|
|
||||||
// bundled JS as a string.
|
|
||||||
public async copyToImportableFile() {
|
|
||||||
await copyJs(`${this.bundleBaseName}.bundle`, this.bundleOutputPath);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const bundledFiles: BundledFile[] = [
|
|
||||||
new BundledFile(
|
|
||||||
'codeMirrorBundle',
|
|
||||||
`${mobileDir}/components/NoteEditor/CodeMirror/CodeMirror.ts`,
|
|
||||||
),
|
|
||||||
new BundledFile(
|
|
||||||
'svgEditorBundle',
|
|
||||||
`${mobileDir}/components/NoteEditor/ImageEditor/js-draw/createJsDrawEditor.ts`,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
export async function buildInjectedJS() {
|
|
||||||
await mkdirp(outputDir);
|
|
||||||
|
|
||||||
|
|
||||||
// Build all in parallel
|
|
||||||
await Promise.all(bundledFiles.map(async file => {
|
|
||||||
await file.build();
|
|
||||||
await file.copyToImportableFile();
|
|
||||||
}));
|
|
||||||
|
|
||||||
await copyJs('webviewLib', `${mobileDir}/../lib/renderers/webviewLib.js`);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function watchInjectedJS() {
|
|
||||||
// Watch for changes
|
|
||||||
for (const file of bundledFiles) {
|
|
||||||
file.startWatching();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
6
packages/app-mobile/tools/buildInjectedJs/constants.ts
Normal file
6
packages/app-mobile/tools/buildInjectedJs/constants.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
import { dirname } from 'path';
|
||||||
|
|
||||||
|
export const mobileDir = dirname(dirname(__dirname));
|
||||||
|
export const outputDir = `${mobileDir}/lib/rnInjectedJs`;
|
||||||
|
export const rootDir = dirname(dirname(mobileDir));
|
16
packages/app-mobile/tools/buildInjectedJs/copyJs.ts
Normal file
16
packages/app-mobile/tools/buildInjectedJs/copyJs.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
|
||||||
|
|
||||||
|
import { readFile, writeFile } from 'fs-extra';
|
||||||
|
import { outputDir } from './constants';
|
||||||
|
|
||||||
|
// Stores the contents of the file at [filePath] as an importable string.
|
||||||
|
// [name] should be the name (excluding the .js extension) of the output file that will contain
|
||||||
|
// the JSON-ified file content.
|
||||||
|
const copyJs = async (name: string, filePath: string) => {
|
||||||
|
const outputPath = `${outputDir}/${name}.js`;
|
||||||
|
console.info(`Creating: ${outputPath}`);
|
||||||
|
const js = await readFile(filePath, 'utf-8');
|
||||||
|
const json = `module.exports = ${JSON.stringify(js)};`;
|
||||||
|
await writeFile(outputPath, json);
|
||||||
|
};
|
||||||
|
export default copyJs;
|
38
packages/app-mobile/tools/buildInjectedJs/gulpTasks.ts
Normal file
38
packages/app-mobile/tools/buildInjectedJs/gulpTasks.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import BundledFile from './BundledFile';
|
||||||
|
import { mkdirp } from 'fs-extra';
|
||||||
|
import { mobileDir, outputDir } from './constants';
|
||||||
|
import copyJs from './copyJs';
|
||||||
|
|
||||||
|
|
||||||
|
const codeMirrorBundle = new BundledFile(
|
||||||
|
'codeMirrorBundle',
|
||||||
|
`${mobileDir}/components/NoteEditor/CodeMirror/CodeMirror.ts`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const jsDrawBundle = new BundledFile(
|
||||||
|
'svgEditorBundle',
|
||||||
|
`${mobileDir}/components/NoteEditor/ImageEditor/js-draw/createJsDrawEditor.ts`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const gulpTasks = {
|
||||||
|
beforeBundle: {
|
||||||
|
fn: () => mkdirp(outputDir),
|
||||||
|
},
|
||||||
|
buildCodeMirrorEditor: {
|
||||||
|
fn: () => codeMirrorBundle.build(),
|
||||||
|
},
|
||||||
|
buildJsDrawEditor: {
|
||||||
|
fn: () => jsDrawBundle.build(),
|
||||||
|
},
|
||||||
|
watchCodeMirrorEditor: {
|
||||||
|
fn: () => codeMirrorBundle.startWatching(),
|
||||||
|
},
|
||||||
|
watchJsDrawEditor: {
|
||||||
|
fn: () => jsDrawBundle.startWatching(),
|
||||||
|
},
|
||||||
|
copyWebviewLib: {
|
||||||
|
fn: () => copyJs('webviewLib', `${mobileDir}/../lib/renderers/webviewLib.js`),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default gulpTasks;
|
@@ -9,6 +9,7 @@ import * as tar from 'tar-stream';
|
|||||||
import { resolve } from 'path';
|
import { resolve } from 'path';
|
||||||
import { Buffer } from 'buffer';
|
import { Buffer } from 'buffer';
|
||||||
import Logger from '@joplin/utils/Logger';
|
import Logger from '@joplin/utils/Logger';
|
||||||
|
import JoplinError from '@joplin/lib/JoplinError';
|
||||||
|
|
||||||
const logger = Logger.create('fs-driver-rn');
|
const logger = Logger.create('fs-driver-rn');
|
||||||
|
|
||||||
@@ -285,6 +286,10 @@ export default class FsDriverRN extends FsDriverBase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async readFileChunk(handle: any, length: number, rawEncoding = 'base64') {
|
public async readFileChunk(handle: any, length: number, rawEncoding = 'base64') {
|
||||||
|
if (!handle?.stat) {
|
||||||
|
throw new JoplinError('File does not exist (reading file chunk).', 'ENOENT');
|
||||||
|
}
|
||||||
|
|
||||||
const encoding = normalizeEncoding(rawEncoding);
|
const encoding = normalizeEncoding(rawEncoding);
|
||||||
|
|
||||||
if (handle.offset + length > handle.stat.size) {
|
if (handle.offset + length > handle.stat.size) {
|
||||||
|
@@ -167,6 +167,18 @@ const testReadFileChunkUtf8 = async (tempDir: string) => {
|
|||||||
|
|
||||||
await fsDriver.close(filePath);
|
await fsDriver.close(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Should throw when the file doesn't exist
|
||||||
|
let readData = undefined;
|
||||||
|
try {
|
||||||
|
const handle = await fsDriver.open(`${filePath}.noexist`, 'r');
|
||||||
|
readData = await fsDriver.readFileChunk(handle, 1, 'utf8');
|
||||||
|
} catch (error) {
|
||||||
|
await expectToBe(error.code, 'ENOENT');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should not have read any data
|
||||||
|
await expectToBe(readData, undefined);
|
||||||
};
|
};
|
||||||
|
|
||||||
const testTarCreate = async (tempDir: string) => {
|
const testTarCreate = async (tempDir: string) => {
|
||||||
|
@@ -17,7 +17,7 @@
|
|||||||
"@joplin/lib": "~2.13",
|
"@joplin/lib": "~2.13",
|
||||||
"@testing-library/react-hooks": "8.0.1",
|
"@testing-library/react-hooks": "8.0.1",
|
||||||
"@types/jest": "29.5.5",
|
"@types/jest": "29.5.5",
|
||||||
"@types/react": "18.2.31",
|
"@types/react": "18.2.33",
|
||||||
"@types/react-redux": "7.1.28",
|
"@types/react-redux": "7.1.28",
|
||||||
"@types/styled-components": "5.1.29",
|
"@types/styled-components": "5.1.29",
|
||||||
"jest": "29.7.0",
|
"jest": "29.7.0",
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@joplin/fork-htmlparser2",
|
"name": "@joplin/fork-htmlparser2",
|
||||||
"description": "Fast & forgiving HTML/XML/RSS parser",
|
"description": "Fast & forgiving HTML/XML/RSS parser",
|
||||||
"version": "4.1.48",
|
"version": "4.1.50",
|
||||||
"author": "Felix Boehm <me@feedic.com>",
|
"author": "Felix Boehm <me@feedic.com>",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
@@ -46,7 +46,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "29.5.5",
|
"@types/jest": "29.5.5",
|
||||||
"@types/node": "18.18.6",
|
"@types/node": "18.18.7",
|
||||||
"@typescript-eslint/eslint-plugin": "6.7.2",
|
"@typescript-eslint/eslint-plugin": "6.7.2",
|
||||||
"@typescript-eslint/parser": "6.7.2",
|
"@typescript-eslint/parser": "6.7.2",
|
||||||
"coveralls": "3.1.1",
|
"coveralls": "3.1.1",
|
||||||
|
@@ -2,7 +2,7 @@
|
|||||||
"name": "@joplin/fork-sax",
|
"name": "@joplin/fork-sax",
|
||||||
"description": "An evented streaming XML parser in JavaScript",
|
"description": "An evented streaming XML parser in JavaScript",
|
||||||
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
|
"author": "Isaac Z. Schlueter <i@izs.me> (http://blog.izs.me/)",
|
||||||
"version": "1.2.52",
|
"version": "1.2.54",
|
||||||
"main": "lib/sax.js",
|
"main": "lib/sax.js",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@joplin/fork-uslug",
|
"name": "@joplin/fork-uslug",
|
||||||
"version": "1.0.13",
|
"version": "1.0.15",
|
||||||
"description": "A permissive slug generator that works with unicode.",
|
"description": "A permissive slug generator that works with unicode.",
|
||||||
"author": "Jeremy Selier <jerem.selier@gmail.com>",
|
"author": "Jeremy Selier <jerem.selier@gmail.com>",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
|
@@ -12,17 +12,6 @@ module.exports = class extends Generator {
|
|||||||
|
|
||||||
this.option('silent');
|
this.option('silent');
|
||||||
this.option('update');
|
this.option('update');
|
||||||
|
|
||||||
// This appears to be deprecated and undocumented
|
|
||||||
// Maybe need this instead?
|
|
||||||
// https://github.com/yeoman/generator/issues/1294#issuecomment-841668595
|
|
||||||
|
|
||||||
// this.option('nodePackageManager', 'npm');
|
|
||||||
|
|
||||||
if (this.options.update) {
|
|
||||||
// When updating, overwrite files without prompting
|
|
||||||
this.conflicter.force = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async prompting() {
|
async prompting() {
|
||||||
@@ -165,6 +154,15 @@ module.exports = class extends Generator {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
} else if (this.options.update) {
|
||||||
|
this.fs.copy(
|
||||||
|
this.templatePath(file),
|
||||||
|
destFilePath, {
|
||||||
|
process: (sourceBuffer) => {
|
||||||
|
return sourceBuffer.toString();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this.fs.copyTpl(
|
this.fs.copyTpl(
|
||||||
this.templatePath(file),
|
this.templatePath(file),
|
||||||
|
@@ -7,7 +7,7 @@ This documentation describes how to create a plugin, and how to work with the pl
|
|||||||
First, install [Yeoman](http://yeoman.io) and generator-joplin using [npm](https://www.npmjs.com/) (we assume you have pre-installed [node.js](https://nodejs.org/)).
|
First, install [Yeoman](http://yeoman.io) and generator-joplin using [npm](https://www.npmjs.com/) (we assume you have pre-installed [node.js](https://nodejs.org/)).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install -g yo
|
npm install -g yo@4.3.1
|
||||||
npm install -g generator-joplin
|
npm install -g generator-joplin
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@@ -2,7 +2,7 @@ import { ModelType } from '../../../BaseModel';
|
|||||||
import Plugin from '../Plugin';
|
import Plugin from '../Plugin';
|
||||||
import { Path } from './types';
|
import { Path } from './types';
|
||||||
/**
|
/**
|
||||||
* This module provides access to the Joplin data API: https://joplinapp.org/api/references/rest_api/
|
* This module provides access to the Joplin data API: https://joplinapp.org/help/api/references/rest_api
|
||||||
* This is the main way to retrieve data, such as notes, notebooks, tags, etc.
|
* This is the main way to retrieve data, such as notes, notebooks, tags, etc.
|
||||||
* or to update them or delete them.
|
* or to update them or delete them.
|
||||||
*
|
*
|
||||||
@@ -18,7 +18,7 @@ import { Path } from './types';
|
|||||||
* * `data`: (Optional) Applies to PUT and POST calls only. The request body contains the data you want to create or modify, for example the content of a note or folder.
|
* * `data`: (Optional) Applies to PUT and POST calls only. The request body contains the data you want to create or modify, for example the content of a note or folder.
|
||||||
* * `files`: (Optional) Used to create new resources and associate them with files.
|
* * `files`: (Optional) Used to create new resources and associate them with files.
|
||||||
*
|
*
|
||||||
* Please refer to the [Joplin API documentation](https://joplinapp.org/api/references/rest_api/) for complete details about each call. As the plugin runs within the Joplin application **you do not need an authorisation token** to use this API.
|
* Please refer to the [Joplin API documentation](https://joplinapp.org/help/api/references/rest_api) for complete details about each call. As the plugin runs within the Joplin application **you do not need an authorisation token** to use this API.
|
||||||
*
|
*
|
||||||
* For example:
|
* For example:
|
||||||
*
|
*
|
||||||
|
@@ -9,7 +9,7 @@ import { ExportModule, ImportModule } from './types';
|
|||||||
*
|
*
|
||||||
* See the documentation of the [[ExportModule]] and [[ImportModule]] for more information.
|
* See the documentation of the [[ExportModule]] and [[ImportModule]] for more information.
|
||||||
*
|
*
|
||||||
* You may also want to refer to the Joplin API documentation to see the list of properties for each item (note, notebook, etc.) - https://joplinapp.org/api/references/rest_api/
|
* You may also want to refer to the Joplin API documentation to see the list of properties for each item (note, notebook, etc.) - https://joplinapp.org/help/api/references/rest_api
|
||||||
*/
|
*/
|
||||||
export default class JoplinInterop {
|
export default class JoplinInterop {
|
||||||
registerExportModule(module: ExportModule): Promise<void>;
|
registerExportModule(module: ExportModule): Promise<void>;
|
||||||
|
@@ -524,13 +524,13 @@ export interface ContentScriptModuleLoadedEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ContentScriptModule {
|
export interface ContentScriptModule {
|
||||||
onLoaded?: (event:ContentScriptModuleLoadedEvent) => void;
|
onLoaded?: (event: ContentScriptModuleLoadedEvent)=> void;
|
||||||
plugin: () => any;
|
plugin: ()=> any;
|
||||||
assets?: () => void;
|
assets?: ()=> void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MarkdownItContentScriptModule extends Omit<ContentScriptModule, 'plugin'> {
|
export interface MarkdownItContentScriptModule extends Omit<ContentScriptModule, 'plugin'> {
|
||||||
plugin: (markdownIt:any, options:any) => any;
|
plugin: (markdownIt: any, options: any)=> any;
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ContentScriptType {
|
export enum ContentScriptType {
|
||||||
@@ -552,7 +552,7 @@ export enum ContentScriptType {
|
|||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
* See [the
|
* See [the
|
||||||
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script)
|
* demo](https://github.com/laurent22/joplin/tree/dev/packages/app-cli/tests/support/plugins/content_script)
|
||||||
* for a simple Markdown-it plugin example.
|
* for a simple Markdown-it plugin example.
|
||||||
|
@@ -5,7 +5,7 @@
|
|||||||
"dist": "webpack --env joplin-plugin-config=buildMain && webpack --env joplin-plugin-config=buildExtraScripts && webpack --env joplin-plugin-config=createArchive",
|
"dist": "webpack --env joplin-plugin-config=buildMain && webpack --env joplin-plugin-config=buildExtraScripts && webpack --env joplin-plugin-config=createArchive",
|
||||||
"prepare": "npm run dist",
|
"prepare": "npm run dist",
|
||||||
"updateVersion": "webpack --env joplin-plugin-config=updateVersion",
|
"updateVersion": "webpack --env joplin-plugin-config=updateVersion",
|
||||||
"update": "npm install -g generator-joplin && yo joplin --node-package-manager npm --update"
|
"update": "npm install -g generator-joplin && yo joplin --node-package-manager npm --update --force"
|
||||||
},
|
},
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
@@ -93,19 +93,24 @@ function validateCategories(categories) {
|
|||||||
|
|
||||||
function validateScreenshots(screenshots) {
|
function validateScreenshots(screenshots) {
|
||||||
if (!screenshots) return null;
|
if (!screenshots) return null;
|
||||||
// eslint-disable-next-line github/array-foreach -- Old code before rule was applied
|
for (const screenshot of screenshots) {
|
||||||
screenshots.forEach(screenshot => {
|
|
||||||
if (!screenshot.src) throw new Error('You must specify a src for each screenshot');
|
if (!screenshot.src) throw new Error('You must specify a src for each screenshot');
|
||||||
|
|
||||||
|
// Avoid attempting to download and verify URL screenshots.
|
||||||
|
if (screenshot.src.startsWith('https://') || screenshot.src.startsWith('http://')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const screenshotType = screenshot.src.split('.').pop();
|
const screenshotType = screenshot.src.split('.').pop();
|
||||||
if (!allPossibleScreenshotsType.includes(screenshotType)) throw new Error(`${screenshotType} is not a valid screenshot type. Valid types are: \n${allPossibleScreenshotsType}\n`);
|
if (!allPossibleScreenshotsType.includes(screenshotType)) throw new Error(`${screenshotType} is not a valid screenshot type. Valid types are: \n${allPossibleScreenshotsType}\n`);
|
||||||
|
|
||||||
const screenshotPath = path.resolve(srcDir, screenshot.src);
|
const screenshotPath = path.resolve(rootDir, screenshot.src);
|
||||||
|
|
||||||
// Max file size is 1MB
|
// Max file size is 1MB
|
||||||
const fileMaxSize = 1024;
|
const fileMaxSize = 1024;
|
||||||
const fileSize = fs.statSync(screenshotPath).size / 1024;
|
const fileSize = fs.statSync(screenshotPath).size / 1024;
|
||||||
if (fileSize > fileMaxSize) throw new Error(`Max screenshot file size is ${fileMaxSize}KB. ${screenshotPath} is ${fileSize}KB`);
|
if (fileSize > fileMaxSize) throw new Error(`Max screenshot file size is ${fileMaxSize}KB. ${screenshotPath} is ${fileSize}KB`);
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function readManifest(manifestPath) {
|
function readManifest(manifestPath) {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "generator-joplin",
|
"name": "generator-joplin",
|
||||||
"version": "2.13.1",
|
"version": "2.13.2",
|
||||||
"description": "Scaffolds out a new Joplin plugin",
|
"description": "Scaffolds out a new Joplin plugin",
|
||||||
"homepage": "https://github.com/laurent22/joplin/tree/dev/packages/generator-joplin",
|
"homepage": "https://github.com/laurent22/joplin/tree/dev/packages/generator-joplin",
|
||||||
"author": {
|
"author": {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@joplin/htmlpack",
|
"name": "@joplin/htmlpack",
|
||||||
"version": "2.13.2",
|
"version": "2.13.4",
|
||||||
"description": "Pack an HTML file and all its linked resources into a single HTML file",
|
"description": "Pack an HTML file and all its linked resources into a single HTML file",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"types": "src/index.ts",
|
"types": "src/index.ts",
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
"author": "Laurent Cozic",
|
"author": "Laurent Cozic",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@joplin/fork-htmlparser2": "^4.1.48",
|
"@joplin/fork-htmlparser2": "^4.1.50",
|
||||||
"css": "3.0.0",
|
"css": "3.0.0",
|
||||||
"datauri": "4.1.0",
|
"datauri": "4.1.0",
|
||||||
"fs-extra": "11.1.1",
|
"fs-extra": "11.1.1",
|
||||||
|
@@ -26,6 +26,7 @@ import time from './time';
|
|||||||
import BaseSyncTarget from './BaseSyncTarget';
|
import BaseSyncTarget from './BaseSyncTarget';
|
||||||
const reduxSharedMiddleware = require('./components/shared/reduxSharedMiddleware');
|
const reduxSharedMiddleware = require('./components/shared/reduxSharedMiddleware');
|
||||||
const os = require('os');
|
const os = require('os');
|
||||||
|
import dns = require('dns');
|
||||||
import fs = require('fs-extra');
|
import fs = require('fs-extra');
|
||||||
const EventEmitter = require('events');
|
const EventEmitter = require('events');
|
||||||
const syswidecas = require('./vendor/syswide-cas');
|
const syswidecas = require('./vendor/syswide-cas');
|
||||||
@@ -171,6 +172,13 @@ export default class BaseApplication {
|
|||||||
this.showStackTraces_ = true;
|
this.showStackTraces_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Work around issues with ipv6 resolution -- default to ipv4first.
|
||||||
|
// (possibly incorrect URL serialization see https://github.com/mswjs/msw/issues/1388#issuecomment-1241180921).
|
||||||
|
// See also https://github.com/node-fetch/node-fetch/issues/1624#issuecomment-1407717012
|
||||||
|
if (flags.matched.allowOverridingDnsResultOrder) {
|
||||||
|
dns.setDefaultResultOrder('ipv4first');
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
matched: flags.matched,
|
matched: flags.matched,
|
||||||
argv: flags.argv,
|
argv: flags.argv,
|
||||||
|
@@ -7,6 +7,7 @@ const pdfUrlRegex = /[\s\S]*?\.pdf$/i;
|
|||||||
export interface ParseOptions {
|
export interface ParseOptions {
|
||||||
anchorNames?: string[];
|
anchorNames?: string[];
|
||||||
preserveImageTagsWithSize?: boolean;
|
preserveImageTagsWithSize?: boolean;
|
||||||
|
preserveNestedTables?: boolean;
|
||||||
baseUrl?: string;
|
baseUrl?: string;
|
||||||
disableEscapeContent?: boolean;
|
disableEscapeContent?: boolean;
|
||||||
convertEmbeddedPdfsToLinks?: boolean;
|
convertEmbeddedPdfsToLinks?: boolean;
|
||||||
@@ -20,6 +21,7 @@ export default class HtmlToMd {
|
|||||||
anchorNames: options.anchorNames ? options.anchorNames.map(n => n.trim().toLowerCase()) : [],
|
anchorNames: options.anchorNames ? options.anchorNames.map(n => n.trim().toLowerCase()) : [],
|
||||||
codeBlockStyle: 'fenced',
|
codeBlockStyle: 'fenced',
|
||||||
preserveImageTagsWithSize: !!options.preserveImageTagsWithSize,
|
preserveImageTagsWithSize: !!options.preserveImageTagsWithSize,
|
||||||
|
preserveNestedTables: !!options.preserveNestedTables,
|
||||||
bulletListMarker: '-',
|
bulletListMarker: '-',
|
||||||
emDelimiter: '*',
|
emDelimiter: '*',
|
||||||
strongDelimiter: '**',
|
strongDelimiter: '**',
|
||||||
|
@@ -224,6 +224,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
"يتوفر تحديث ، هل تريد تنزيله الآن؟"
|
"يتوفر تحديث ، هل تريد تنزيله الآن؟"
|
||||||
],
|
],
|
||||||
@@ -1254,6 +1257,9 @@
|
|||||||
"جارٍ إنشاء الروابط...",
|
"جارٍ إنشاء الروابط...",
|
||||||
"جارٍ إنشاء الروابط..."
|
"جارٍ إنشاء الروابط..."
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
"احصل عليها الآن:"
|
"احصل عليها الآن:"
|
||||||
],
|
],
|
||||||
@@ -1589,7 +1595,7 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
"تسجيل الخروج"
|
"تسجيل الخروج"
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
@@ -1643,6 +1649,9 @@
|
|||||||
"Max Item Size": [
|
"Max Item Size": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing Master Keys": [
|
"Missing Master Keys": [
|
||||||
"المفاتيح الرئيسة مفقودة"
|
"المفاتيح الرئيسة مفقودة"
|
||||||
],
|
],
|
||||||
@@ -1895,6 +1904,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"فتح..."
|
"فتح..."
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"ألغيت العملية"
|
"ألغيت العملية"
|
||||||
],
|
],
|
||||||
@@ -2972,6 +2984,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
"عند إنشاء قائمة جديدة للمهام:"
|
"عند إنشاء قائمة جديدة للمهام:"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
"كلمات"
|
"كلمات"
|
||||||
],
|
],
|
||||||
|
@@ -188,6 +188,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
"Има обновление, искате ли да го свалите сега?"
|
"Има обновление, искате ли да го свалите сега?"
|
||||||
],
|
],
|
||||||
@@ -929,6 +932,9 @@
|
|||||||
"General": [
|
"General": [
|
||||||
"Общи"
|
"Общи"
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -1196,7 +1202,7 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
@@ -1232,6 +1238,9 @@
|
|||||||
"Max Item Size": [
|
"Max Item Size": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing Master Keys": [
|
"Missing Master Keys": [
|
||||||
"Липсващи главни ключове"
|
"Липсващи главни ключове"
|
||||||
],
|
],
|
||||||
@@ -1430,6 +1439,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"Отвори..."
|
"Отвори..."
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"Действието бе отменено"
|
"Действието бе отменено"
|
||||||
],
|
],
|
||||||
@@ -2393,6 +2405,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
"Когато се създава нова задача:"
|
"Когато се създава нова задача:"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
@@ -191,6 +191,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
"Nova je verzija dostupna. Želite li je preuzeti?"
|
"Nova je verzija dostupna. Želite li je preuzeti?"
|
||||||
],
|
],
|
||||||
@@ -938,6 +941,9 @@
|
|||||||
"General": [
|
"General": [
|
||||||
"Opće"
|
"Opće"
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
"Preuzmite sada:"
|
"Preuzmite sada:"
|
||||||
],
|
],
|
||||||
@@ -1202,7 +1208,7 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
@@ -1238,6 +1244,9 @@
|
|||||||
"Max Item Size": [
|
"Max Item Size": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing Master Keys": [
|
"Missing Master Keys": [
|
||||||
"Nedostaju glavni ključevi"
|
"Nedostaju glavni ključevi"
|
||||||
],
|
],
|
||||||
@@ -1424,6 +1433,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"Otvori..."
|
"Otvori..."
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"Radnja je prekinuta"
|
"Radnja je prekinuta"
|
||||||
],
|
],
|
||||||
@@ -2387,6 +2399,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
"Prilikom kreiranja novog zadatka:"
|
"Prilikom kreiranja novog zadatka:"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
"Riječi"
|
"Riječi"
|
||||||
],
|
],
|
||||||
|
@@ -227,6 +227,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
"Hi ha disponible una actualització. Voleu descarregar-la ara?"
|
"Hi ha disponible una actualització. Voleu descarregar-la ara?"
|
||||||
],
|
],
|
||||||
@@ -1267,6 +1270,9 @@
|
|||||||
"Generant enllaç...",
|
"Generant enllaç...",
|
||||||
"Generant enllaços..."
|
"Generant enllaços..."
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
"Obteniu-lo ara:"
|
"Obteniu-lo ara:"
|
||||||
],
|
],
|
||||||
@@ -1622,8 +1628,8 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
"Desconnecta"
|
"Desconnecta"
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
"Registres"
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
"Donatius"
|
"Donatius"
|
||||||
@@ -1685,6 +1691,9 @@
|
|||||||
"Max Total Size": [
|
"Max Total Size": [
|
||||||
"Mida màxima total"
|
"Mida màxima total"
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing keys": [
|
"Missing keys": [
|
||||||
"Claus que falten"
|
"Claus que falten"
|
||||||
],
|
],
|
||||||
@@ -1949,6 +1958,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"Obre..."
|
"Obre..."
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"L'operació s'ha cancel·lat"
|
"L'operació s'ha cancel·lat"
|
||||||
],
|
],
|
||||||
@@ -3167,6 +3179,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
"En crear un llistat de tasques pendents:"
|
"En crear un llistat de tasques pendents:"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
"Paraules"
|
"Paraules"
|
||||||
],
|
],
|
||||||
|
@@ -227,6 +227,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
"Je k dispozici aktualizace, chcete ji stáhnout?"
|
"Je k dispozici aktualizace, chcete ji stáhnout?"
|
||||||
],
|
],
|
||||||
@@ -1236,6 +1239,9 @@
|
|||||||
"Vytváření odkazů...",
|
"Vytváření odkazů...",
|
||||||
"Vytváření odkazů..."
|
"Vytváření odkazů..."
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
"Získat hned:"
|
"Získat hned:"
|
||||||
],
|
],
|
||||||
@@ -1586,8 +1592,8 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
"Odhlásit se"
|
"Odhlásit se"
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
"Logy"
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
"Přispět"
|
"Přispět"
|
||||||
@@ -1649,6 +1655,9 @@
|
|||||||
"Max Total Size": [
|
"Max Total Size": [
|
||||||
"Maximální celková velikost"
|
"Maximální celková velikost"
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing keys": [
|
"Missing keys": [
|
||||||
"Chybějící klíče"
|
"Chybějící klíče"
|
||||||
],
|
],
|
||||||
@@ -1913,6 +1922,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"Otevřít..."
|
"Otevřít..."
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"Operace zrušena"
|
"Operace zrušena"
|
||||||
],
|
],
|
||||||
@@ -3110,6 +3122,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
"Při vytváření nového úkolu:"
|
"Při vytváření nového úkolu:"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
"Slova"
|
"Slova"
|
||||||
],
|
],
|
||||||
|
@@ -74,6 +74,9 @@
|
|||||||
"%s: %s": [
|
"%s: %s": [
|
||||||
"%s: %s"
|
"%s: %s"
|
||||||
],
|
],
|
||||||
|
"%s: Missing password.": [
|
||||||
|
"%s: Manglende adgangskode."
|
||||||
|
],
|
||||||
"&Edit": [
|
"&Edit": [
|
||||||
"&Redigér"
|
"&Redigér"
|
||||||
],
|
],
|
||||||
@@ -108,7 +111,7 @@
|
|||||||
"(wysiwyg: %s)"
|
"(wysiwyg: %s)"
|
||||||
],
|
],
|
||||||
"(You may disable this prompt in the options)": [
|
"(You may disable this prompt in the options)": [
|
||||||
""
|
"(Du kan deaktivere denne prompt i indstillingerne)"
|
||||||
],
|
],
|
||||||
"- Camera: to allow taking a picture and attaching it to a note.": [
|
"- Camera: to allow taking a picture and attaching it to a note.": [
|
||||||
"- Kamera: Tilladelse til fotografering og vedhæftning af et billede til en note."
|
"- Kamera: Tilladelse til fotografering og vedhæftning af et billede til en note."
|
||||||
@@ -218,6 +221,12 @@
|
|||||||
"Always": [
|
"Always": [
|
||||||
"Altid"
|
"Altid"
|
||||||
],
|
],
|
||||||
|
"Always ask": [
|
||||||
|
"Spørg altid"
|
||||||
|
],
|
||||||
|
"Always resize": [
|
||||||
|
"Ændr altid størrelsen"
|
||||||
|
],
|
||||||
"Ambiguous notebook \"%s\". Please use notebook id instead - press \"ti\" to see the short notebook id or use $b for current selected notebook": [
|
"Ambiguous notebook \"%s\". Please use notebook id instead - press \"ti\" to see the short notebook id or use $b for current selected notebook": [
|
||||||
"Tvetydig notesbog \"%s\". Brug venligst notesbog-id i stedet - tryk på \"ti\" for at se det korte notebogs-id eller brug $b for den aktuelle valgte notesbog"
|
"Tvetydig notesbog \"%s\". Brug venligst notesbog-id i stedet - tryk på \"ti\" for at se det korte notebogs-id eller brug $b for den aktuelle valgte notesbog"
|
||||||
],
|
],
|
||||||
@@ -225,13 +234,16 @@
|
|||||||
"Tvetydig notesbog \"%s\". Brug venligst kort notesbog-id i stedet - tryk på \"ti\" for at se det korte notebogs-id"
|
"Tvetydig notesbog \"%s\". Brug venligst kort notesbog-id i stedet - tryk på \"ti\" for at se det korte notebogs-id"
|
||||||
],
|
],
|
||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
|
"Der blev fundet en automatisk gemt tegning. Vedhæfter en kopi af den til noten?"
|
||||||
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
"Opdatering er tilgængelig, vil du hente den nu?"
|
"Opdatering er tilgængelig, vil du hente den nu?"
|
||||||
],
|
],
|
||||||
"Any email sent to this address will be converted into a note and added to your collection. The note will be saved into the Inbox notebook": [
|
"Any email sent to this address will be converted into a note and added to your collection. The note will be saved into the Inbox notebook": [
|
||||||
"Enhver e-mail, der sendes til denne adresse, bliver konverteret til en note og føjet til din samling. Noten gemmes i indbakkens notesbog."
|
"Enhver e-mail, der sendes til denne adresse, bliver konverteret til en note og føjet til din samling. Noten gemmes i indbakkens notesbog"
|
||||||
],
|
],
|
||||||
"Appearance": [
|
"Appearance": [
|
||||||
"Udseende"
|
"Udseende"
|
||||||
@@ -464,6 +476,9 @@
|
|||||||
"Code View": [
|
"Code View": [
|
||||||
"Kodevisning"
|
"Kodevisning"
|
||||||
],
|
],
|
||||||
|
"Collaborate on a notebook with others": [
|
||||||
|
"Samarbejd med andre om en notesbog"
|
||||||
|
],
|
||||||
"Collaborate on notebooks with others": [
|
"Collaborate on notebooks with others": [
|
||||||
"Samarbejd med andre om notesbøger"
|
"Samarbejd med andre om notesbøger"
|
||||||
],
|
],
|
||||||
@@ -488,6 +503,9 @@
|
|||||||
"Command palette...": [
|
"Command palette...": [
|
||||||
"Kommando-udvalg..."
|
"Kommando-udvalg..."
|
||||||
],
|
],
|
||||||
|
"Compact": [
|
||||||
|
"Kompakt"
|
||||||
|
],
|
||||||
"Completed": [
|
"Completed": [
|
||||||
"Fuldført"
|
"Fuldført"
|
||||||
],
|
],
|
||||||
@@ -651,6 +669,12 @@
|
|||||||
"Creates a new to-do.": [
|
"Creates a new to-do.": [
|
||||||
"Opretter en ny opgave."
|
"Opretter en ny opgave."
|
||||||
],
|
],
|
||||||
|
"Creating new note...": [
|
||||||
|
"Opretter ny note..."
|
||||||
|
],
|
||||||
|
"Creating new to-do...": [
|
||||||
|
"Opretter nye gøremål..."
|
||||||
|
],
|
||||||
"Creating report...": [
|
"Creating report...": [
|
||||||
"Opretter rapport..."
|
"Opretter rapport..."
|
||||||
],
|
],
|
||||||
@@ -673,7 +697,7 @@
|
|||||||
"Brugerdefinerede TLS certifikater"
|
"Brugerdefinerede TLS certifikater"
|
||||||
],
|
],
|
||||||
"Customise the note publishing banner": [
|
"Customise the note publishing banner": [
|
||||||
""
|
"Tilpas banneret til udgivelse af noter"
|
||||||
],
|
],
|
||||||
"Cut": [
|
"Cut": [
|
||||||
"Klip"
|
"Klip"
|
||||||
@@ -793,7 +817,7 @@
|
|||||||
"Destinationsformat: %s"
|
"Destinationsformat: %s"
|
||||||
],
|
],
|
||||||
"Detailed": [
|
"Detailed": [
|
||||||
""
|
"Detailed"
|
||||||
],
|
],
|
||||||
"Directory": [
|
"Directory": [
|
||||||
"Indeks"
|
"Indeks"
|
||||||
@@ -819,6 +843,9 @@
|
|||||||
"Disabling encryption means *all* your notes and attachments are going to be re-synchronised and sent unencrypted to the sync target. Do you wish to continue?": [
|
"Disabling encryption means *all* your notes and attachments are going to be re-synchronised and sent unencrypted to the sync target. Do you wish to continue?": [
|
||||||
"Slås kryptering fra vil *alle* dine noter og vedhæftninger blive re-synkroniseret og sendt ukrypteret til synk-modtageren. Vil du fortsætte?"
|
"Slås kryptering fra vil *alle* dine noter og vedhæftninger blive re-synkroniseret og sendt ukrypteret til synk-modtageren. Vil du fortsætte?"
|
||||||
],
|
],
|
||||||
|
"Discard": [
|
||||||
|
"Kassér"
|
||||||
|
],
|
||||||
"Discard changes": [
|
"Discard changes": [
|
||||||
"Kassér ændringer"
|
"Kassér ændringer"
|
||||||
],
|
],
|
||||||
@@ -892,10 +919,10 @@
|
|||||||
"Dracula"
|
"Dracula"
|
||||||
],
|
],
|
||||||
"Draw picture": [
|
"Draw picture": [
|
||||||
""
|
"Tegn billede"
|
||||||
],
|
],
|
||||||
"Drawing": [
|
"Drawing": [
|
||||||
""
|
"Tegner"
|
||||||
],
|
],
|
||||||
"Drop notes or files here": [
|
"Drop notes or files here": [
|
||||||
"Slip noter eller filer her"
|
"Slip noter eller filer her"
|
||||||
@@ -1098,6 +1125,9 @@
|
|||||||
"Enter notebook title": [
|
"Enter notebook title": [
|
||||||
"Indtast notesbogstitel"
|
"Indtast notesbogstitel"
|
||||||
],
|
],
|
||||||
|
"Enter password": [
|
||||||
|
"Indtast adgangskode"
|
||||||
|
],
|
||||||
"Enum": [
|
"Enum": [
|
||||||
"Enum"
|
"Enum"
|
||||||
],
|
],
|
||||||
@@ -1267,6 +1297,9 @@
|
|||||||
"Opretter link...",
|
"Opretter link...",
|
||||||
"Opretter links..."
|
"Opretter links..."
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
"Få den nu:"
|
"Få den nu:"
|
||||||
],
|
],
|
||||||
@@ -1334,7 +1367,7 @@
|
|||||||
"HTML Indeks"
|
"HTML Indeks"
|
||||||
],
|
],
|
||||||
"HTML document": [
|
"HTML document": [
|
||||||
""
|
"HTML-dokument"
|
||||||
],
|
],
|
||||||
"HTML File": [
|
"HTML File": [
|
||||||
"HTML fil"
|
"HTML fil"
|
||||||
@@ -1553,7 +1586,7 @@
|
|||||||
"Indstil knap-rækkefølge"
|
"Indstil knap-rækkefølge"
|
||||||
],
|
],
|
||||||
"Leave it blank to download the language files from the default website": [
|
"Leave it blank to download the language files from the default website": [
|
||||||
"Lad den være tom for at downloade sprogfilerne fra standardwebstedet."
|
"Lad den være tom for at downloade sprogfilerne fra standardwebstedet"
|
||||||
],
|
],
|
||||||
"Leave notebook...": [
|
"Leave notebook...": [
|
||||||
"Forlad notesbog..."
|
"Forlad notesbog..."
|
||||||
@@ -1622,8 +1655,8 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
"Log ud"
|
"Log ud"
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
"Logfiler"
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
"Giv en donation"
|
"Giv en donation"
|
||||||
@@ -1685,6 +1718,9 @@
|
|||||||
"Max Total Size": [
|
"Max Total Size": [
|
||||||
"Maks samlet størrelse"
|
"Maks samlet størrelse"
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing keys": [
|
"Missing keys": [
|
||||||
"Manglende nøgler"
|
"Manglende nøgler"
|
||||||
],
|
],
|
||||||
@@ -1731,7 +1767,7 @@
|
|||||||
"N"
|
"N"
|
||||||
],
|
],
|
||||||
"Never resize": [
|
"Never resize": [
|
||||||
""
|
"Ændr aldrig størrelse"
|
||||||
],
|
],
|
||||||
"New note": [
|
"New note": [
|
||||||
"Ny note"
|
"Ny note"
|
||||||
@@ -1862,6 +1898,9 @@
|
|||||||
"Note list growth factor": [
|
"Note list growth factor": [
|
||||||
"Noteliste-vækstfaktor"
|
"Noteliste-vækstfaktor"
|
||||||
],
|
],
|
||||||
|
"Note list style": [
|
||||||
|
"Notatliste-stil"
|
||||||
|
],
|
||||||
"Note properties": [
|
"Note properties": [
|
||||||
"Noteegenskaber"
|
"Noteegenskaber"
|
||||||
],
|
],
|
||||||
@@ -1949,6 +1988,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"Åbn..."
|
"Åbn..."
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"Udførelse annulleret"
|
"Udførelse annulleret"
|
||||||
],
|
],
|
||||||
@@ -2238,7 +2280,7 @@
|
|||||||
"Nulstil hovedadgangskode"
|
"Nulstil hovedadgangskode"
|
||||||
],
|
],
|
||||||
"Resize large images:": [
|
"Resize large images:": [
|
||||||
""
|
"Ændr størrelsen på store billeder:"
|
||||||
],
|
],
|
||||||
"Resources: %d.": [
|
"Resources: %d.": [
|
||||||
"Ressourcer: %d."
|
"Ressourcer: %d."
|
||||||
@@ -2312,6 +2354,9 @@
|
|||||||
"Save changes": [
|
"Save changes": [
|
||||||
"Gem ændringer"
|
"Gem ændringer"
|
||||||
],
|
],
|
||||||
|
"Save changes?": [
|
||||||
|
"Gem ændringer?"
|
||||||
|
],
|
||||||
"Save geo-location with notes": [
|
"Save geo-location with notes": [
|
||||||
"Gem geo-lokation i noter"
|
"Gem geo-lokation i noter"
|
||||||
],
|
],
|
||||||
@@ -2387,6 +2432,9 @@
|
|||||||
"Share a copy of all notes in a file format that can be imported by Joplin on a computer.": [
|
"Share a copy of all notes in a file format that can be imported by Joplin on a computer.": [
|
||||||
"Del en kopi af alle noter i et filformat, der kan importeres af Joplin på en computer."
|
"Del en kopi af alle noter i et filformat, der kan importeres af Joplin på en computer."
|
||||||
],
|
],
|
||||||
|
"Share a notebook with others": [
|
||||||
|
"Del en notesbog med andre"
|
||||||
|
],
|
||||||
"Share Notebook": [
|
"Share Notebook": [
|
||||||
"Del notesbog"
|
"Del notesbog"
|
||||||
],
|
],
|
||||||
@@ -2394,7 +2442,7 @@
|
|||||||
"Del notesbog..."
|
"Del notesbog..."
|
||||||
],
|
],
|
||||||
"Share permissions": [
|
"Share permissions": [
|
||||||
""
|
"Deletilladelser"
|
||||||
],
|
],
|
||||||
"Sharing notebook...": [
|
"Sharing notebook...": [
|
||||||
"Deler notesbog..."
|
"Deler notesbog..."
|
||||||
@@ -2436,10 +2484,13 @@
|
|||||||
"Vis/skjul sidebjælken"
|
"Vis/skjul sidebjælken"
|
||||||
],
|
],
|
||||||
"Shrink large images before adding them to notes.": [
|
"Shrink large images before adding them to notes.": [
|
||||||
""
|
"Formindsk store billeder, før du tilføjer dem til noter."
|
||||||
|
],
|
||||||
|
"Side menu closed": [
|
||||||
|
"Sidemenu lukket"
|
||||||
],
|
],
|
||||||
"Side menu opened": [
|
"Side menu opened": [
|
||||||
""
|
"Sidemenu åbnet"
|
||||||
],
|
],
|
||||||
"Sidebar": [
|
"Sidebar": [
|
||||||
"Sidebjælke"
|
"Sidebjælke"
|
||||||
@@ -2672,6 +2723,9 @@
|
|||||||
"Teams": [
|
"Teams": [
|
||||||
"Teams"
|
"Teams"
|
||||||
],
|
],
|
||||||
|
"Text document": [
|
||||||
|
"Tekstdokument"
|
||||||
|
],
|
||||||
"Text editor command": [
|
"Text editor command": [
|
||||||
"Tekstredigeringskomando"
|
"Tekstredigeringskomando"
|
||||||
],
|
],
|
||||||
@@ -2765,6 +2819,9 @@
|
|||||||
"The sync target needs to be upgraded. Press this banner to proceed.": [
|
"The sync target needs to be upgraded. Press this banner to proceed.": [
|
||||||
"Synk-mål skal opgraderes! Tryk på denne banner for at fortsætte."
|
"Synk-mål skal opgraderes! Tryk på denne banner for at fortsætte."
|
||||||
],
|
],
|
||||||
|
"The synchronisation password is missing.": [
|
||||||
|
"Adgangskoden til synkronisering mangler."
|
||||||
|
],
|
||||||
"The tag \"%s\" already exists. Please choose a different name.": [
|
"The tag \"%s\" already exists. Please choose a different name.": [
|
||||||
"Etiketten \"%s\" eksisterer allerede. Vælg venligst et andet navn."
|
"Etiketten \"%s\" eksisterer allerede. Vælg venligst et andet navn."
|
||||||
],
|
],
|
||||||
@@ -2814,7 +2871,7 @@
|
|||||||
"Disse udvidelser forbedrer Markdown-fortolkeren med yderligere funktioner. Vær opmærksom på, at selvom disse funktioner kan være nyttige, er de ikke standard Markdown og derfor vil de fleste kun virke i Joplin. Derudover er nogle af dem ikke kompatible med WYSIWYG editoren. Hvis du åbner en note, som bruger en af disse udvidelser i den editor, vil du miste udvidelsesformatteringen. Det angives nedenfor, hvilke udvidelser der er kompatible eller ikke med WYSIWYG editoren."
|
"Disse udvidelser forbedrer Markdown-fortolkeren med yderligere funktioner. Vær opmærksom på, at selvom disse funktioner kan være nyttige, er de ikke standard Markdown og derfor vil de fleste kun virke i Joplin. Derudover er nogle af dem ikke kompatible med WYSIWYG editoren. Hvis du åbner en note, som bruger en af disse udvidelser i den editor, vil du miste udvidelsesformatteringen. Det angives nedenfor, hvilke udvidelser der er kompatible eller ikke med WYSIWYG editoren."
|
||||||
],
|
],
|
||||||
"This allows another user to share a notebook with you, and you can then both collaborate on it. It does not however allow you to share a notebook with someone else, unless you have the feature \"%s\".": [
|
"This allows another user to share a notebook with you, and you can then both collaborate on it. It does not however allow you to share a notebook with someone else, unless you have the feature \"%s\".": [
|
||||||
""
|
"Dette giver en anden bruger mulighed for at dele en notesbog med dig, og I kan så begge samarbejde om den. Det giver dig dog ikke mulighed for at dele en notesbog med en anden, medmindre du har funktionen \"%s\"."
|
||||||
],
|
],
|
||||||
"This attachment is not downloaded or not decrypted yet": [
|
"This attachment is not downloaded or not decrypted yet": [
|
||||||
"Denne vedhæftning er ikke downloadet eller ikke dekrypteret endnu"
|
"Denne vedhæftning er ikke downloadet eller ikke dekrypteret endnu"
|
||||||
@@ -2825,6 +2882,9 @@
|
|||||||
"This authorisation token is only needed to allow third-party applications to access Joplin.": [
|
"This authorisation token is only needed to allow third-party applications to access Joplin.": [
|
||||||
"Denne autoriseringstoken er kun nødvendig for at tillade tredjepartsprogrammer at tilgå Joplin."
|
"Denne autoriseringstoken er kun nødvendig for at tillade tredjepartsprogrammer at tilgå Joplin."
|
||||||
],
|
],
|
||||||
|
"This drawing may have unsaved changes.": [
|
||||||
|
"Denne tegning kan have ændringer, der ikke er gemt."
|
||||||
|
],
|
||||||
"This is an advanced tool to show the attachments that are linked to your notes. Please be careful when deleting one of them as they cannot be restored afterwards.": [
|
"This is an advanced tool to show the attachments that are linked to your notes. Please be careful when deleting one of them as they cannot be restored afterwards.": [
|
||||||
"Dette er et avanceret værktøj til at vise de vedhæftninger, som er knyttet til dine noter. Vær forsigtig med at slette dem, da de ikke kan gendannes bagefter."
|
"Dette er et avanceret værktøj til at vise de vedhæftninger, som er knyttet til dine noter. Vær forsigtig med at slette dem, da de ikke kan gendannes bagefter."
|
||||||
],
|
],
|
||||||
@@ -2981,9 +3041,15 @@
|
|||||||
"Type: %s.": [
|
"Type: %s.": [
|
||||||
"Tast: %s."
|
"Tast: %s."
|
||||||
],
|
],
|
||||||
|
"Unable to edit resource of type %s": [
|
||||||
|
"Kan ikke redigere ressource af typen %s"
|
||||||
|
],
|
||||||
"Unable to export or share data. Reason: %s": [
|
"Unable to export or share data. Reason: %s": [
|
||||||
"Kan ikke eksportere eller dele data. Årsag: %s"
|
"Kan ikke eksportere eller dele data. Årsag: %s"
|
||||||
],
|
],
|
||||||
|
"Unable to share log data. Reason: %s": [
|
||||||
|
"Kan ikke dele logdata. Årsag: %s"
|
||||||
|
],
|
||||||
"Uncompleted to-dos on top": [
|
"Uncompleted to-dos on top": [
|
||||||
"Ufærdige opgaver øverst"
|
"Ufærdige opgaver øverst"
|
||||||
],
|
],
|
||||||
@@ -3161,6 +3227,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
"Ved oprettelse af ny opgave:"
|
"Ved oprettelse af ny opgave:"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
"Ord"
|
"Ord"
|
||||||
],
|
],
|
||||||
|
@@ -227,6 +227,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
"Es ist eine Aktualisierung verfügbar. Soll sie jetzt heruntergeladen werden?"
|
"Es ist eine Aktualisierung verfügbar. Soll sie jetzt heruntergeladen werden?"
|
||||||
],
|
],
|
||||||
@@ -1267,6 +1270,9 @@
|
|||||||
"Link wird erzeugt ...",
|
"Link wird erzeugt ...",
|
||||||
"Links werden erzeugt ..."
|
"Links werden erzeugt ..."
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
"Hole es jetzt:"
|
"Hole es jetzt:"
|
||||||
],
|
],
|
||||||
@@ -1622,8 +1628,8 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
"Abmelden"
|
"Abmelden"
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
"Protokolle"
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
"Spenden"
|
"Spenden"
|
||||||
@@ -1685,6 +1691,9 @@
|
|||||||
"Max Total Size": [
|
"Max Total Size": [
|
||||||
"Maximale Gesamtgröße"
|
"Maximale Gesamtgröße"
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing keys": [
|
"Missing keys": [
|
||||||
"Fehlende Schlüssel"
|
"Fehlende Schlüssel"
|
||||||
],
|
],
|
||||||
@@ -1949,6 +1958,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"Öffnen ..."
|
"Öffnen ..."
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"Vorgang abgebrochen"
|
"Vorgang abgebrochen"
|
||||||
],
|
],
|
||||||
@@ -3158,6 +3170,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
"Wenn eine neue Aufgabe erstellt wird:"
|
"Wenn eine neue Aufgabe erstellt wird:"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
"Wörter"
|
"Wörter"
|
||||||
],
|
],
|
||||||
|
@@ -227,6 +227,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
"Υπάρχει διαθέσιμη μια ενημέρωση, θέλετε να την κατεβάσετε τώρα;"
|
"Υπάρχει διαθέσιμη μια ενημέρωση, θέλετε να την κατεβάσετε τώρα;"
|
||||||
],
|
],
|
||||||
@@ -1243,6 +1246,9 @@
|
|||||||
"Δημιουργία συνδέσμου...",
|
"Δημιουργία συνδέσμου...",
|
||||||
"Δημιουργία νεου %s..."
|
"Δημιουργία νεου %s..."
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
"Λήψη τώρα:"
|
"Λήψη τώρα:"
|
||||||
],
|
],
|
||||||
@@ -1598,8 +1604,8 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
"Αποσύνδεση"
|
"Αποσύνδεση"
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
"Αρχεία Καταγραφής"
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
"Κάντε μια δωρεά"
|
"Κάντε μια δωρεά"
|
||||||
@@ -1661,6 +1667,9 @@
|
|||||||
"Max Total Size": [
|
"Max Total Size": [
|
||||||
"Μέγιστο συνολικό μέγεθος"
|
"Μέγιστο συνολικό μέγεθος"
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing keys": [
|
"Missing keys": [
|
||||||
"Απουσία κλειδιών"
|
"Απουσία κλειδιών"
|
||||||
],
|
],
|
||||||
@@ -1925,6 +1934,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"Άνοιγμα..."
|
"Άνοιγμα..."
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"Η λειτουργία ακυρώθηκε"
|
"Η λειτουργία ακυρώθηκε"
|
||||||
],
|
],
|
||||||
@@ -3128,6 +3140,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
"Κατά τη δημιουργία ενός νέου to-do:"
|
"Κατά τη δημιουργία ενός νέου to-do:"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
"Λέξεις"
|
"Λέξεις"
|
||||||
],
|
],
|
||||||
|
@@ -236,6 +236,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -885,6 +888,9 @@
|
|||||||
"Do not lose the password as, for security purposes, this will be the *only* way to decrypt the data! To enable encryption, please enter your password below.": [
|
"Do not lose the password as, for security purposes, this will be the *only* way to decrypt the data! To enable encryption, please enter your password below.": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Donate, website": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Done": [
|
"Done": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -999,6 +1005,9 @@
|
|||||||
"Email to Note": [
|
"Email to Note": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Email To Note, login information": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Emails": [
|
"Emails": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -1173,6 +1182,9 @@
|
|||||||
"Export profile": [
|
"Export profile": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Export your data": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Exported successfully!": [
|
"Exported successfully!": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -1294,6 +1306,9 @@
|
|||||||
"",
|
"",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -1567,6 +1582,9 @@
|
|||||||
"Language": [
|
"Language": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Language, date format": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Last error: %s": [
|
"Last error: %s": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -1649,7 +1667,7 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
@@ -1712,6 +1730,9 @@
|
|||||||
"Max Total Size": [
|
"Max Total Size": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing keys": [
|
"Missing keys": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -1979,6 +2000,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -2276,6 +2300,9 @@
|
|||||||
"Restart and upgrade": [
|
"Restart and upgrade": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Restart in safe mode": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Restart now": [
|
"Restart now": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -2390,6 +2417,9 @@
|
|||||||
"Select parent notebook": [
|
"Select parent notebook": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Send bug report": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Server is already running on port %d": [
|
"Server is already running on port %d": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -2654,6 +2684,9 @@
|
|||||||
"Sync your notes": [
|
"Sync your notes": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Sync, encryption, proxy": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Synchronisation": [
|
"Synchronisation": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -2831,6 +2864,9 @@
|
|||||||
"Theme": [
|
"Theme": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Themes, editor font": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"There are currently no notes. Create one by clicking on the (+) button.": [
|
"There are currently no notes. Create one by clicking on the (+) button.": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -2981,6 +3017,9 @@
|
|||||||
"Toggle external editing": [
|
"Toggle external editing": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Toggle note history, keep notes for": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Toggle note list": [
|
"Toggle note list": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -3215,6 +3254,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
@@ -236,6 +236,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -885,6 +888,9 @@
|
|||||||
"Do not lose the password as, for security purposes, this will be the *only* way to decrypt the data! To enable encryption, please enter your password below.": [
|
"Do not lose the password as, for security purposes, this will be the *only* way to decrypt the data! To enable encryption, please enter your password below.": [
|
||||||
"Do not lose the password as, for security purposes, this will be the *only* way to decrypt the data! To enable encryption, please enter your password below."
|
"Do not lose the password as, for security purposes, this will be the *only* way to decrypt the data! To enable encryption, please enter your password below."
|
||||||
],
|
],
|
||||||
|
"Donate, website": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Done": [
|
"Done": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -999,6 +1005,9 @@
|
|||||||
"Email to Note": [
|
"Email to Note": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Email To Note, login information": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Emails": [
|
"Emails": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -1173,6 +1182,9 @@
|
|||||||
"Export profile": [
|
"Export profile": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Export your data": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Exported successfully!": [
|
"Exported successfully!": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -1294,6 +1306,9 @@
|
|||||||
"",
|
"",
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -1567,6 +1582,9 @@
|
|||||||
"Language": [
|
"Language": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Language, date format": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Last error: %s": [
|
"Last error: %s": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -1649,7 +1667,7 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
@@ -1712,6 +1730,9 @@
|
|||||||
"Max Total Size": [
|
"Max Total Size": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing keys": [
|
"Missing keys": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -1979,6 +2000,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -2276,6 +2300,9 @@
|
|||||||
"Restart and upgrade": [
|
"Restart and upgrade": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Restart in safe mode": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Restart now": [
|
"Restart now": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -2390,6 +2417,9 @@
|
|||||||
"Select parent notebook": [
|
"Select parent notebook": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Send bug report": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Server is already running on port %d": [
|
"Server is already running on port %d": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -2654,6 +2684,9 @@
|
|||||||
"Sync your notes": [
|
"Sync your notes": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Sync, encryption, proxy": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Synchronisation": [
|
"Synchronisation": [
|
||||||
"Synchronization"
|
"Synchronization"
|
||||||
],
|
],
|
||||||
@@ -2828,6 +2861,9 @@
|
|||||||
"Theme": [
|
"Theme": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Themes, editor font": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"There are currently no notes. Create one by clicking on the (+) button.": [
|
"There are currently no notes. Create one by clicking on the (+) button.": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -2978,6 +3014,9 @@
|
|||||||
"Toggle external editing": [
|
"Toggle external editing": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Toggle note history, keep notes for": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Toggle note list": [
|
"Toggle note list": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -3212,6 +3251,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
@@ -194,6 +194,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -770,6 +773,9 @@
|
|||||||
"Email to Note": [
|
"Email to Note": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Email To Note, login information": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Emails": [
|
"Emails": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -1010,6 +1016,9 @@
|
|||||||
"General": [
|
"General": [
|
||||||
"Ĝenerala"
|
"Ĝenerala"
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
"Akiri ĝin nun:"
|
"Akiri ĝin nun:"
|
||||||
],
|
],
|
||||||
@@ -1296,7 +1305,7 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
@@ -1335,6 +1344,9 @@
|
|||||||
"Max Item Size": [
|
"Max Item Size": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing keys": [
|
"Missing keys": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -1539,6 +1551,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"Malfermi..."
|
"Malfermi..."
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"Operacio nuligita"
|
"Operacio nuligita"
|
||||||
],
|
],
|
||||||
@@ -1857,6 +1872,9 @@
|
|||||||
"Select all": [
|
"Select all": [
|
||||||
"Elekti ĉiujn"
|
"Elekti ĉiujn"
|
||||||
],
|
],
|
||||||
|
"Send bug report": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Server is already running on port %d": [
|
"Server is already running on port %d": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -2073,6 +2091,9 @@
|
|||||||
"Sync to provided target (defaults to sync.target config value)": [
|
"Sync to provided target (defaults to sync.target config value)": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Sync, encryption, proxy": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Synchronisation interval": [
|
"Synchronisation interval": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -2357,6 +2378,9 @@
|
|||||||
"to-do": [
|
"to-do": [
|
||||||
"tasko"
|
"tasko"
|
||||||
],
|
],
|
||||||
|
"Toggle note history, keep notes for": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Toggle sidebar": [
|
"Toggle sidebar": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -2537,6 +2561,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
@@ -224,6 +224,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
"Está disponible una actualización. ¿Quiere descargarla ahora?"
|
"Está disponible una actualización. ¿Quiere descargarla ahora?"
|
||||||
],
|
],
|
||||||
@@ -1225,6 +1228,9 @@
|
|||||||
"Creando enlace...",
|
"Creando enlace...",
|
||||||
"Creando enlaces..."
|
"Creando enlaces..."
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
"Obtenla ahora en:"
|
"Obtenla ahora en:"
|
||||||
],
|
],
|
||||||
@@ -1574,8 +1580,8 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
"Cerrar sesión"
|
"Cerrar sesión"
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
"Registros"
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
"Hacer una donación"
|
"Hacer una donación"
|
||||||
@@ -1637,6 +1643,9 @@
|
|||||||
"Max Total Size": [
|
"Max Total Size": [
|
||||||
"Tamaño original"
|
"Tamaño original"
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing keys": [
|
"Missing keys": [
|
||||||
"Claves faltantes"
|
"Claves faltantes"
|
||||||
],
|
],
|
||||||
@@ -1898,6 +1907,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"Abrir..."
|
"Abrir..."
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"Operación cancelada"
|
"Operación cancelada"
|
||||||
],
|
],
|
||||||
@@ -3092,6 +3104,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
"Al crear una tarea nueva:"
|
"Al crear una tarea nueva:"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
"Palabras"
|
"Palabras"
|
||||||
],
|
],
|
||||||
|
@@ -191,6 +191,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
"Saadaval on värskendus, kas soovite selle kohe alla laadida?"
|
"Saadaval on värskendus, kas soovite selle kohe alla laadida?"
|
||||||
],
|
],
|
||||||
@@ -868,6 +871,9 @@
|
|||||||
"Lingi loomine...",
|
"Lingi loomine...",
|
||||||
"Linkide loomine..."
|
"Linkide loomine..."
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
"Võta nüüd:"
|
"Võta nüüd:"
|
||||||
],
|
],
|
||||||
@@ -1124,7 +1130,7 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
@@ -1154,6 +1160,9 @@
|
|||||||
"Max Item Size": [
|
"Max Item Size": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing Master Keys": [
|
"Missing Master Keys": [
|
||||||
"Puuduvad juhtklahvid"
|
"Puuduvad juhtklahvid"
|
||||||
],
|
],
|
||||||
@@ -1337,6 +1346,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"Avatud..."
|
"Avatud..."
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"Toiming tühistati"
|
"Toiming tühistati"
|
||||||
],
|
],
|
||||||
@@ -2219,6 +2231,9 @@
|
|||||||
"When creating a new note:": [
|
"When creating a new note:": [
|
||||||
"Uue märkme loomisel:"
|
"Uue märkme loomisel:"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
@@ -176,6 +176,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -627,6 +630,9 @@
|
|||||||
"Do not ask for confirmation.": [
|
"Do not ask for confirmation.": [
|
||||||
"Ez galdetu berresteko."
|
"Ez galdetu berresteko."
|
||||||
],
|
],
|
||||||
|
"Donate, website": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Done": [
|
"Done": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -909,6 +915,9 @@
|
|||||||
"Full name": [
|
"Full name": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -1174,7 +1183,7 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Manage your plugins": [
|
"Manage your plugins": [
|
||||||
@@ -1207,6 +1216,9 @@
|
|||||||
"Max Item Size": [
|
"Max Item Size": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing required argument: %s": [
|
"Missing required argument: %s": [
|
||||||
"Beharrezko argumentua faltan: %s"
|
"Beharrezko argumentua faltan: %s"
|
||||||
],
|
],
|
||||||
@@ -1366,6 +1378,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
" Eragiketa utzita"
|
" Eragiketa utzita"
|
||||||
],
|
],
|
||||||
@@ -2092,6 +2107,9 @@
|
|||||||
"To work correctly, the app needs the following permissions. Please enable them in your phone settings, in Apps > Joplin > Permissions": [
|
"To work correctly, the app needs the following permissions. Please enable them in your phone settings, in Apps > Joplin > Permissions": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Toggle note history, keep notes for": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Toggle sidebar": [
|
"Toggle sidebar": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -2254,6 +2272,9 @@
|
|||||||
"Welcome to Joplin!\n\nType `:help shortcuts` for the list of keyboard shortcuts, or just `:help` for usage information.\n\nFor example, to create a notebook press `mb`; to create a note press `mn`.": [
|
"Welcome to Joplin!\n\nType `:help shortcuts` for the list of keyboard shortcuts, or just `:help` for usage information.\n\nFor example, to create a notebook press `mb`; to create a note press `mn`.": [
|
||||||
"Ongi etorri Joplin-era!\n\nIdatz `:help shortcuts` lasterbideak ikusteko, edo soilik `:help`erabilerako informaziorako.\n\nEsate baterako, koadernoa sortzeko sakatu `mb`: oharra sortzeko sakatu `mn`"
|
"Ongi etorri Joplin-era!\n\nIdatz `:help shortcuts` lasterbideak ikusteko, edo soilik `:help`erabilerako informaziorako.\n\nEsate baterako, koadernoa sortzeko sakatu `mb`: oharra sortzeko sakatu `mn`"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -227,6 +227,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
"Päivitys on saatavilla. Haluatko ladata sen nyt?"
|
"Päivitys on saatavilla. Haluatko ladata sen nyt?"
|
||||||
],
|
],
|
||||||
@@ -1234,6 +1237,9 @@
|
|||||||
"Luodaan linkki...",
|
"Luodaan linkki...",
|
||||||
"Luodaan linkkejä..."
|
"Luodaan linkkejä..."
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
"Hae se nyt:"
|
"Hae se nyt:"
|
||||||
],
|
],
|
||||||
@@ -1583,8 +1589,8 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
"Uloskirjaus"
|
"Uloskirjaus"
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
"Lokit"
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
"Tee lahjoitus"
|
"Tee lahjoitus"
|
||||||
@@ -1646,6 +1652,9 @@
|
|||||||
"Max Total Size": [
|
"Max Total Size": [
|
||||||
"Suurin kokonaiskoko"
|
"Suurin kokonaiskoko"
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing keys": [
|
"Missing keys": [
|
||||||
"Puuttuvat avaimet"
|
"Puuttuvat avaimet"
|
||||||
],
|
],
|
||||||
@@ -1910,6 +1919,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"Avaa..."
|
"Avaa..."
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"Toiminto peruutettu"
|
"Toiminto peruutettu"
|
||||||
],
|
],
|
||||||
@@ -3107,6 +3119,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
"Kun luot uuden tehtävän:"
|
"Kun luot uuden tehtävän:"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
"Sanat"
|
"Sanat"
|
||||||
],
|
],
|
||||||
|
@@ -236,6 +236,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
"Un dessin automatiquement sauvegardé a été trouvé. Le joindre à la note ?"
|
"Un dessin automatiquement sauvegardé a été trouvé. Le joindre à la note ?"
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
"Erreur : %s"
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
"Une mise à jour est disponible, souhaitez vous la télécharger maintenant ?"
|
"Une mise à jour est disponible, souhaitez vous la télécharger maintenant ?"
|
||||||
],
|
],
|
||||||
@@ -885,6 +888,9 @@
|
|||||||
"Do not lose the password as, for security purposes, this will be the *only* way to decrypt the data! To enable encryption, please enter your password below.": [
|
"Do not lose the password as, for security purposes, this will be the *only* way to decrypt the data! To enable encryption, please enter your password below.": [
|
||||||
"Ne perdez pas le mot de passe car, par sécurité, ce sera la seule façon de déchiffrer les données. Pour activer le chiffrement, veuillez entrer votre mot de passe ci-dessous."
|
"Ne perdez pas le mot de passe car, par sécurité, ce sera la seule façon de déchiffrer les données. Pour activer le chiffrement, veuillez entrer votre mot de passe ci-dessous."
|
||||||
],
|
],
|
||||||
|
"Donate, website": [
|
||||||
|
"Donations, site web"
|
||||||
|
],
|
||||||
"Done": [
|
"Done": [
|
||||||
"Terminé"
|
"Terminé"
|
||||||
],
|
],
|
||||||
@@ -999,6 +1005,9 @@
|
|||||||
"Email to Note": [
|
"Email to Note": [
|
||||||
"Conversion email en note"
|
"Conversion email en note"
|
||||||
],
|
],
|
||||||
|
"Email To Note, login information": [
|
||||||
|
"\"Email to note\", info de connexion"
|
||||||
|
],
|
||||||
"Emails": [
|
"Emails": [
|
||||||
"Emails"
|
"Emails"
|
||||||
],
|
],
|
||||||
@@ -1173,6 +1182,9 @@
|
|||||||
"Export profile": [
|
"Export profile": [
|
||||||
"Exporter le profil"
|
"Exporter le profil"
|
||||||
],
|
],
|
||||||
|
"Export your data": [
|
||||||
|
"Exporter vos données"
|
||||||
|
],
|
||||||
"Exported successfully!": [
|
"Exported successfully!": [
|
||||||
"Exporté avec succès !"
|
"Exporté avec succès !"
|
||||||
],
|
],
|
||||||
@@ -1294,6 +1306,9 @@
|
|||||||
"Génération du lien…",
|
"Génération du lien…",
|
||||||
"Génération des liens…"
|
"Génération des liens…"
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
"Géolocalisation, vérification orthographique, barre d'outils, taille des images"
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
"L'obtenir maintenant :"
|
"L'obtenir maintenant :"
|
||||||
],
|
],
|
||||||
@@ -1567,6 +1582,9 @@
|
|||||||
"Language": [
|
"Language": [
|
||||||
"Langue"
|
"Langue"
|
||||||
],
|
],
|
||||||
|
"Language, date format": [
|
||||||
|
"Langue, format de la date"
|
||||||
|
],
|
||||||
"Last error: %s": [
|
"Last error: %s": [
|
||||||
"Dernière erreur : %s"
|
"Dernière erreur : %s"
|
||||||
],
|
],
|
||||||
@@ -1649,8 +1667,8 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
"Se déconnecter"
|
"Se déconnecter"
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
"Journal"
|
"Logs, profils, info synchronisation"
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
"Faire un don"
|
"Faire un don"
|
||||||
@@ -1712,6 +1730,9 @@
|
|||||||
"Max Total Size": [
|
"Max Total Size": [
|
||||||
"Taille max totale"
|
"Taille max totale"
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
"Lecteur média, math, diagrammes, sommaire"
|
||||||
|
],
|
||||||
"Missing keys": [
|
"Missing keys": [
|
||||||
"Clefs manquantes"
|
"Clefs manquantes"
|
||||||
],
|
],
|
||||||
@@ -1979,6 +2000,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"Ouvrir…"
|
"Ouvrir…"
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
"Ouverture section %s"
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"Opération annulée"
|
"Opération annulée"
|
||||||
],
|
],
|
||||||
@@ -2276,6 +2300,9 @@
|
|||||||
"Restart and upgrade": [
|
"Restart and upgrade": [
|
||||||
"Redémarrer et mettre à jour"
|
"Redémarrer et mettre à jour"
|
||||||
],
|
],
|
||||||
|
"Restart in safe mode": [
|
||||||
|
"Redémarrer en mode sans échec"
|
||||||
|
],
|
||||||
"Restart now": [
|
"Restart now": [
|
||||||
"Redémarrer maintenant"
|
"Redémarrer maintenant"
|
||||||
],
|
],
|
||||||
@@ -2390,6 +2417,9 @@
|
|||||||
"Select parent notebook": [
|
"Select parent notebook": [
|
||||||
"Sélectionner le carnet parent"
|
"Sélectionner le carnet parent"
|
||||||
],
|
],
|
||||||
|
"Send bug report": [
|
||||||
|
"Envoyer rapport de débogage"
|
||||||
|
],
|
||||||
"Server is already running on port %d": [
|
"Server is already running on port %d": [
|
||||||
"Le serveur tourne déjà sur le port %d"
|
"Le serveur tourne déjà sur le port %d"
|
||||||
],
|
],
|
||||||
@@ -2654,6 +2684,9 @@
|
|||||||
"Sync your notes": [
|
"Sync your notes": [
|
||||||
"Synchroniser vos notes"
|
"Synchroniser vos notes"
|
||||||
],
|
],
|
||||||
|
"Sync, encryption, proxy": [
|
||||||
|
"Synchronisation, chiffrement, proxy"
|
||||||
|
],
|
||||||
"Synchronisation": [
|
"Synchronisation": [
|
||||||
"Synchronisation"
|
"Synchronisation"
|
||||||
],
|
],
|
||||||
@@ -2831,6 +2864,9 @@
|
|||||||
"Theme": [
|
"Theme": [
|
||||||
"Apparence"
|
"Apparence"
|
||||||
],
|
],
|
||||||
|
"Themes, editor font": [
|
||||||
|
"Thèmes, police de l'éditeur"
|
||||||
|
],
|
||||||
"There are currently no notes. Create one by clicking on the (+) button.": [
|
"There are currently no notes. Create one by clicking on the (+) button.": [
|
||||||
"Ce carnet ne contient aucune note. Créez‑en une en appuyant sur le bouton (+)."
|
"Ce carnet ne contient aucune note. Créez‑en une en appuyant sur le bouton (+)."
|
||||||
],
|
],
|
||||||
@@ -2981,6 +3017,9 @@
|
|||||||
"Toggle external editing": [
|
"Toggle external editing": [
|
||||||
"Basculer l'édition externe"
|
"Basculer l'édition externe"
|
||||||
],
|
],
|
||||||
|
"Toggle note history, keep notes for": [
|
||||||
|
"Activation historique des notes"
|
||||||
|
],
|
||||||
"Toggle note list": [
|
"Toggle note list": [
|
||||||
"Basculer liste de notes"
|
"Basculer liste de notes"
|
||||||
],
|
],
|
||||||
@@ -3215,6 +3254,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
"Lors de la création d'une tâche :"
|
"Lors de la création d'une tâche :"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
"La fenêtre ne répond pas."
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
"Mots"
|
"Mots"
|
||||||
],
|
],
|
||||||
|
@@ -173,6 +173,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
"Hai unha actualización dispoñíbel, desexa descargala agora?"
|
"Hai unha actualización dispoñíbel, desexa descargala agora?"
|
||||||
],
|
],
|
||||||
@@ -900,6 +903,9 @@
|
|||||||
"Full name": [
|
"Full name": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -1168,7 +1174,7 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
@@ -1204,6 +1210,9 @@
|
|||||||
"Max Item Size": [
|
"Max Item Size": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing Master Keys": [
|
"Missing Master Keys": [
|
||||||
"Faltan as chaves mestras"
|
"Faltan as chaves mestras"
|
||||||
],
|
],
|
||||||
@@ -1381,6 +1390,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"Abrir…"
|
"Abrir…"
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"Operación cancelada"
|
"Operación cancelada"
|
||||||
],
|
],
|
||||||
@@ -2137,6 +2149,9 @@
|
|||||||
"Toggle editor layout": [
|
"Toggle editor layout": [
|
||||||
"Cambiar a disposición do editor"
|
"Cambiar a disposición do editor"
|
||||||
],
|
],
|
||||||
|
"Toggle note history, keep notes for": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Toggle sidebar": [
|
"Toggle sidebar": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
@@ -2317,6 +2332,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
"Cando se crea unha nova tarefa:"
|
"Cando se crea unha nova tarefa:"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
@@ -227,6 +227,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
"Dostupna je nova verzija. Želiš li je sada preuzeti?"
|
"Dostupna je nova verzija. Želiš li je sada preuzeti?"
|
||||||
],
|
],
|
||||||
@@ -1269,6 +1272,9 @@
|
|||||||
"Generiranje poveznica …",
|
"Generiranje poveznica …",
|
||||||
"Generiranje poveznica …"
|
"Generiranje poveznica …"
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
"Nabavi sada:"
|
"Nabavi sada:"
|
||||||
],
|
],
|
||||||
@@ -1625,8 +1631,8 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
"Odjava"
|
"Odjava"
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
"Dnevnici"
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
"Doniraj"
|
"Doniraj"
|
||||||
@@ -1688,6 +1694,9 @@
|
|||||||
"Max Total Size": [
|
"Max Total Size": [
|
||||||
"Maksimalna ukupna veličina"
|
"Maksimalna ukupna veličina"
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing keys": [
|
"Missing keys": [
|
||||||
"Nedostajući ključevi"
|
"Nedostajući ključevi"
|
||||||
],
|
],
|
||||||
@@ -1952,6 +1961,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"Otvori …"
|
"Otvori …"
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"Operacija je prekinuta"
|
"Operacija je prekinuta"
|
||||||
],
|
],
|
||||||
@@ -3167,6 +3179,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
"Prilikom stvaranja novog zadatka:"
|
"Prilikom stvaranja novog zadatka:"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
"Broj riječi"
|
"Broj riječi"
|
||||||
],
|
],
|
||||||
|
@@ -209,6 +209,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
"Egy frissítés elérhető, le szeretné tölteni most?"
|
"Egy frissítés elérhető, le szeretné tölteni most?"
|
||||||
],
|
],
|
||||||
@@ -1159,6 +1162,9 @@
|
|||||||
"Link létrehozása...",
|
"Link létrehozása...",
|
||||||
"Linkek létrehozása..."
|
"Linkek létrehozása..."
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
"Szerezze be most:"
|
"Szerezze be most:"
|
||||||
],
|
],
|
||||||
@@ -1478,8 +1484,8 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
"Kilépés"
|
"Kilépés"
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
"Log-ok"
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
"Adakozzon"
|
"Adakozzon"
|
||||||
@@ -1526,6 +1532,9 @@
|
|||||||
"Max Item Size": [
|
"Max Item Size": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing keys": [
|
"Missing keys": [
|
||||||
"Hiányzó kulcsok"
|
"Hiányzó kulcsok"
|
||||||
],
|
],
|
||||||
@@ -1775,6 +1784,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"Megnyitás..."
|
"Megnyitás..."
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"Művelet megszakítva"
|
"Művelet megszakítva"
|
||||||
],
|
],
|
||||||
@@ -2897,6 +2909,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
"Amikor egy új to-do-t hoz létre:"
|
"Amikor egy új to-do-t hoz létre:"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
"Szavak"
|
"Szavak"
|
||||||
],
|
],
|
||||||
|
@@ -218,6 +218,9 @@
|
|||||||
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
"An autosaved drawing was found. Attach a copy of it to the note?": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"An error occurred: %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"An update is available, do you want to download it now?": [
|
"An update is available, do you want to download it now?": [
|
||||||
"Pembaruan tersedia, apakah Anda ingin mengunduhnya sekarang?"
|
"Pembaruan tersedia, apakah Anda ingin mengunduhnya sekarang?"
|
||||||
],
|
],
|
||||||
@@ -1178,6 +1181,9 @@
|
|||||||
"Generating link...": [
|
"Generating link...": [
|
||||||
"Menghasilkan tautan..."
|
"Menghasilkan tautan..."
|
||||||
],
|
],
|
||||||
|
"Geolocation, spellcheck, editor toolbar, image resize": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Get it now:": [
|
"Get it now:": [
|
||||||
"Dapatkan sekarang:"
|
"Dapatkan sekarang:"
|
||||||
],
|
],
|
||||||
@@ -1511,8 +1517,8 @@
|
|||||||
"Logout": [
|
"Logout": [
|
||||||
"Keluar"
|
"Keluar"
|
||||||
],
|
],
|
||||||
"Logs": [
|
"Logs, profiles, sync status": [
|
||||||
"Log"
|
""
|
||||||
],
|
],
|
||||||
"Make a donation": [
|
"Make a donation": [
|
||||||
"Beri donasi"
|
"Beri donasi"
|
||||||
@@ -1562,6 +1568,9 @@
|
|||||||
"Max Item Size": [
|
"Max Item Size": [
|
||||||
""
|
""
|
||||||
],
|
],
|
||||||
|
"Media player, math, diagrams, table of contents": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Missing keys": [
|
"Missing keys": [
|
||||||
"Kunci yang Hilang"
|
"Kunci yang Hilang"
|
||||||
],
|
],
|
||||||
@@ -1811,6 +1820,9 @@
|
|||||||
"Open...": [
|
"Open...": [
|
||||||
"Buka..."
|
"Buka..."
|
||||||
],
|
],
|
||||||
|
"Opening section %s": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Operation cancelled": [
|
"Operation cancelled": [
|
||||||
"Operasi dibatalkan"
|
"Operasi dibatalkan"
|
||||||
],
|
],
|
||||||
@@ -2966,6 +2978,9 @@
|
|||||||
"When creating a new to-do:": [
|
"When creating a new to-do:": [
|
||||||
"Ketika membuat tugas baru:"
|
"Ketika membuat tugas baru:"
|
||||||
],
|
],
|
||||||
|
"Window unresponsive.": [
|
||||||
|
""
|
||||||
|
],
|
||||||
"Words": [
|
"Words": [
|
||||||
"Kata"
|
"Kata"
|
||||||
],
|
],
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user