You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-08-24 20:19:10 +02:00
Compare commits
75 Commits
android-v1
...
cli-v1.0.1
Author | SHA1 | Date | |
---|---|---|---|
|
7b85c33213 | ||
|
4b4d0e8b25 | ||
|
4fb6af3c62 | ||
|
d7ffe7e294 | ||
|
3ff139d445 | ||
|
40443e0134 | ||
|
1f927c1285 | ||
|
5e82e62335 | ||
|
de954827df | ||
|
2cb24bf198 | ||
|
739a6a4a9c | ||
|
dfcf1193dc | ||
|
c72f92e22f | ||
|
f6d01ce7e1 | ||
|
fed9700587 | ||
|
12a3a9a89e | ||
|
590c62c371 | ||
|
df41f64b3c | ||
|
1849355245 | ||
|
fa1b471ea4 | ||
|
0a67f8c947 | ||
|
621d0260f4 | ||
|
f93fca7c5b | ||
|
f4d830c2ef | ||
|
1aa2844efa | ||
|
f22b2adaad | ||
|
b547f9aa13 | ||
|
e4166e9da7 | ||
|
1634fdb421 | ||
|
7f51035f91 | ||
|
70e71cbc2a | ||
|
ffd03bf34c | ||
|
f59a3dee78 | ||
|
3ba3037242 | ||
|
dbb269fef6 | ||
|
e209189faa | ||
|
2d7065cde2 | ||
|
59f5972c93 | ||
|
8bac5275c3 | ||
|
58d748e235 | ||
|
e69ac3e62a | ||
|
7fc8ac4c0f | ||
|
069dce69cd | ||
|
3bdf621026 | ||
|
2f62897fb6 | ||
|
dbdd602f50 | ||
|
d66fa87b2b | ||
|
124a959c8d | ||
|
127dce1cd6 | ||
|
44986a35a4 | ||
|
ea516301fd | ||
|
90b684457a | ||
|
8517e2aa42 | ||
|
b880be8b7c | ||
|
57fd1a7588 | ||
|
5ed458f634 | ||
|
ac12143d00 | ||
|
b6c36d1961 | ||
|
3c2de70baa | ||
|
f6c5620682 | ||
|
79b6f64bd0 | ||
|
ed89f55bff | ||
|
8841a92142 | ||
|
0bd19c97eb | ||
|
2fd026d107 | ||
|
5e7eb37ca7 | ||
|
6b10d5d821 | ||
|
0f4dbfbcbf | ||
|
99493174ec | ||
|
333253fd4f | ||
|
01470e8d3b | ||
|
bda2fe6717 | ||
|
d1f4c5be18 | ||
|
377adea51d | ||
|
30165e8d6a |
0
.gitignore
vendored
Executable file → Normal file
0
.gitignore
vendored
Executable file → Normal file
@@ -21,7 +21,6 @@ const { _, setLocale, defaultLocale, closestSupportedLocale } = require('lib/loc
|
||||
const os = require('os');
|
||||
const fs = require('fs-extra');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
const EventEmitter = require('events');
|
||||
const Cache = require('lib/Cache');
|
||||
|
||||
class Application extends BaseApplication {
|
||||
|
@@ -152,6 +152,11 @@ class Command extends BaseCommand {
|
||||
type: Database.enumId('fieldType', 'text'),
|
||||
description: 'If an image is provided, you can also specify an optional rectangle that will be used to crop the image. In format `{ x: x, y: y, width: width, height: height }`',
|
||||
});
|
||||
tableFields.push({
|
||||
name: 'tags',
|
||||
type: Database.enumId('fieldType', 'text'),
|
||||
description: 'Comma-separated list of tags. eg. `tag1,tag2`.',
|
||||
});
|
||||
}
|
||||
|
||||
lines.push('# ' + toTitleCase(tableName));
|
||||
|
@@ -4,6 +4,7 @@ const { _ } = require('lib/locale.js');
|
||||
const { OneDriveApiNodeUtils } = require('./onedrive-api-node-utils.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const BaseItem = require('lib/models/BaseItem.js');
|
||||
const ResourceFetcher = require('lib/services/ResourceFetcher');
|
||||
const { Synchronizer } = require('lib/synchronizer.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { cliUtils } = require('./cli-utils.js');
|
||||
@@ -191,6 +192,14 @@ class Command extends BaseCommand {
|
||||
}
|
||||
}
|
||||
|
||||
// When using the tool in command line mode, the ResourceFetcher service is
|
||||
// not going to be running in the background, so the resources need to be
|
||||
// explicitely downloaded below.
|
||||
if (!app().hasGui()) {
|
||||
await ResourceFetcher.instance().fetchAll();
|
||||
await ResourceFetcher.instance().waitForAllFinished();
|
||||
}
|
||||
|
||||
await app().refreshCurrentFolder();
|
||||
} catch (error) {
|
||||
cleanUp();
|
||||
|
@@ -633,6 +633,10 @@ msgstr "Retalla"
|
||||
msgid "Paste"
|
||||
msgstr "Enganxa"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Seleccioneu una data"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr ""
|
||||
|
||||
@@ -948,6 +952,14 @@ msgstr "Copia"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Alterna entre el tipus nota i tasques pendents"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "Alterna entre el tipus nota i tasques pendents"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Alterna entre el tipus nota i tasques pendents"
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Copia l'enllaç Markdown"
|
||||
|
||||
@@ -1541,6 +1553,10 @@ msgstr "Voleu moure %d notes al bloc de notes «%s»?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "Premeu per a establir la contrasenya de desxifratge."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "Estableix una alarma"
|
||||
|
||||
msgid "Save alarm"
|
||||
msgstr "Desa l'alarma"
|
||||
|
||||
@@ -1557,6 +1573,36 @@ msgstr "Cancel·la la sincronització"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Elements obtinguts: %d/%d."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "S'està cancel·lant... Espereu."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "Comprova la configuració de la sincronització"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "L'aplicació s'ha autoritzat correctament."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Elements obtinguts: %d/%d."
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "Etiquetes noves:"
|
||||
|
||||
|
@@ -615,6 +615,10 @@ msgstr "Vyjmout"
|
||||
msgid "Paste"
|
||||
msgstr "Vložit"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Vybrat datum"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr ""
|
||||
|
||||
@@ -920,6 +924,14 @@ msgstr "Kopírovat"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Přepnout mezi poznámkou a to-do"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "Přepnout mezi poznámkou a to-do"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Přepnout mezi poznámkou a to-do"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Markdown"
|
||||
@@ -1516,6 +1528,10 @@ msgstr "Přesunout poznámky %d do zápisníku \"%s\"?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "Stiskněte pro zadání hesla k dešifrování."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "Nastavit alarm"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Save alarm"
|
||||
msgstr "Nastavit alarm"
|
||||
@@ -1533,6 +1549,36 @@ msgstr "Zrušit synchronizaci"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Získané položky: %d/%d."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Zastavuji, chvíli strpení."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "Zkontrolujte nastavení synchronizace"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "Aplikace byla úspěšně autorizována."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Získané položky: %d/%d."
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "Nové tagy:"
|
||||
|
||||
|
@@ -620,6 +620,10 @@ msgstr "Klip"
|
||||
msgid "Paste"
|
||||
msgstr "Indsæt"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Vælg dato"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr ""
|
||||
|
||||
@@ -929,6 +933,14 @@ msgstr "Kopier"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Skift mellem note- og opgave type"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "Skift mellem note- og opgave type"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Skift mellem note- og opgave type"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Markdown"
|
||||
@@ -1525,6 +1537,10 @@ msgstr "Flyt %d noter til notesbogen \"%s\"?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "Klik for at gemme dekrypterings kodeord."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "Indstil alarm"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Save alarm"
|
||||
msgstr "Indstil alarm"
|
||||
@@ -1542,6 +1558,36 @@ msgstr "Afbryd synkronisering"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Hentede emner: %d/%d."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Annullerer... Vent venligst."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "Check synkroniserings Indstillinger"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "Denne app er succesfuldt godkendt."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Hentede emner: %d/%d."
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "Nye tags:"
|
||||
|
||||
|
@@ -15,11 +15,12 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 2.1.1\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"POT-Creation-Date: \n"
|
||||
"PO-Revision-Date: \n"
|
||||
|
||||
msgid "To delete a tag, untag the associated notes."
|
||||
msgstr ""
|
||||
"Um ein Schlagwort zu löschen, entferne es bei allen damit verbundenen "
|
||||
"Notizen."
|
||||
"Um ein Tag zu löschen, entferne es bei allen damit verbundenen Notizen."
|
||||
|
||||
msgid "Please select the note or notebook to be deleted first."
|
||||
msgstr ""
|
||||
@@ -197,8 +198,8 @@ msgid ""
|
||||
"Exports Joplin data to the given path. By default, it will export the "
|
||||
"complete database including notebooks, notes, tags and resources."
|
||||
msgstr ""
|
||||
"Exportiert Joplin Dateien in den angegebenen Pfad. Standardmäßig wird die "
|
||||
"komplette Datenbank inklusive Notizbüchern, Notizen, Markierungen und "
|
||||
"Exportiert Joplin-Dateien in den angegebenen Pfad. Standardmäßig wird die "
|
||||
"komplette Datenbank inklusive Notizbüchern, Notizen, Schlagwörtern und "
|
||||
"Anhängen exportiert."
|
||||
|
||||
#, javascript-format
|
||||
@@ -306,7 +307,7 @@ msgstr "Anhänge: %d."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Tagged: %d."
|
||||
msgstr "Markiert: %d."
|
||||
msgstr "Verschlagwortet: %d."
|
||||
|
||||
msgid "Importing notes..."
|
||||
msgstr "Importiere Notizen..."
|
||||
@@ -475,16 +476,15 @@ msgstr "Starte Synchronisation..."
|
||||
msgid "Cancelling... Please wait."
|
||||
msgstr "Abbrechen… Bitte warten."
|
||||
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"<tag-command> can be \"add\", \"remove\" or \"list\" to assign or remove "
|
||||
"[tag] from [note], or to list the notes associated with [tag]. The command "
|
||||
"`tag list` can be used to list all the tags (use -l for long option)."
|
||||
msgstr ""
|
||||
"<tag-command> kann \"add\", \"remove\" or \"list\" sein, um ein [Schlagwort] "
|
||||
"zu [Notiz] zuzuweisen oder zu entfernen, oder um mit [Schlagwort] markierte "
|
||||
"Notizen anzuzeigen. Mit dem Befehl `tag list` können alle Schlagwörter "
|
||||
"angezeigt werden."
|
||||
"<tag-command> kann „add“, „remove“ oder „list“ sein um [tag] zu [note] "
|
||||
"hinzuzufügen oder zu entfernen, oder um die Notizen aufzulisten die [tag] "
|
||||
"zugeordnet sind. Der Befehl `tag list` kann benutzt werden, um alle "
|
||||
"Schlagwörter anzuzeigen (nutze -l für die lange Option)."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Invalid command: \"%s\""
|
||||
@@ -644,6 +644,9 @@ msgstr "Ausschneiden"
|
||||
msgid "Paste"
|
||||
msgstr "Einfügen"
|
||||
|
||||
msgid "Select all"
|
||||
msgstr "Alle auswählen"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr "Fett"
|
||||
|
||||
@@ -666,7 +669,7 @@ msgid "Toggle sidebar"
|
||||
msgstr "Seitenleiste ein/aus"
|
||||
|
||||
msgid "Toggle editor layout"
|
||||
msgstr "Editor Layout umschalten"
|
||||
msgstr "Editor-Layout umschalten"
|
||||
|
||||
msgid "Tools"
|
||||
msgstr "Werkzeuge"
|
||||
@@ -675,7 +678,7 @@ msgid "Synchronisation status"
|
||||
msgstr "Status der Synchronisation"
|
||||
|
||||
msgid "Web clipper options"
|
||||
msgstr "Web Clipper Optionen"
|
||||
msgstr "Web-Clipper Optionen"
|
||||
|
||||
msgid "Encryption options"
|
||||
msgstr "Verschlüsselungsoptionen"
|
||||
@@ -719,7 +722,7 @@ msgid "Current version is up-to-date."
|
||||
msgstr "Die aktuelle Version ist up-to-date."
|
||||
|
||||
msgid "An update is available, do you want to download it now?"
|
||||
msgstr "Es ist ein Update verfügbar! Soll es jetzt heruntergeladen werden?"
|
||||
msgstr "Es ist ein Update verfügbar. Soll es jetzt heruntergeladen werden?"
|
||||
|
||||
msgid "Yes"
|
||||
msgstr "Ja"
|
||||
@@ -728,11 +731,11 @@ msgid "No"
|
||||
msgstr "Nein"
|
||||
|
||||
msgid "Token has been copied to the clipboard!"
|
||||
msgstr ""
|
||||
msgstr "Token wurde in die Zwischenablage kopiert."
|
||||
|
||||
msgid "The web clipper service is enabled and set to auto-start."
|
||||
msgstr ""
|
||||
"Der Webclipperservice ist bereits aktiviert und auf Autostart eingestellt."
|
||||
"Der Web-Clipper-Service ist bereits aktiviert und auf Autostart eingestellt."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Status: Started on port %d"
|
||||
@@ -743,26 +746,26 @@ msgid "Status: %s"
|
||||
msgstr "Status: %s"
|
||||
|
||||
msgid "Disable Web Clipper Service"
|
||||
msgstr "Web Clipper Service deaktivieren"
|
||||
msgstr "Web-Clipper-Service deaktivieren"
|
||||
|
||||
msgid "The web clipper service is not enabled."
|
||||
msgstr "Der Web Clipper Service ist nicht aktiviert."
|
||||
msgstr "Der Web-Clipper-Service ist nicht aktiviert."
|
||||
|
||||
msgid "Enable Web Clipper Service"
|
||||
msgstr "Web Clipper Service aktivieren"
|
||||
msgstr "Web-Clipper-Service aktivieren"
|
||||
|
||||
msgid ""
|
||||
"Joplin Web Clipper allows saving web pages and screenshots from your browser "
|
||||
"to Joplin."
|
||||
msgstr ""
|
||||
"Joplin Web Clipper erlaubt im Browser das Speichern von Webseiten und "
|
||||
"Joplin Web-Clipper erlaubt im Browser das Speichern von Webseiten und "
|
||||
"Screenshots nach Joplin."
|
||||
|
||||
msgid "In order to use the web clipper, you need to do the following:"
|
||||
msgstr "Um den Web Clipper zu benutzen, musst du folgendes machen:"
|
||||
msgstr "Um den Web-Clipper zu benutzen, musst du folgendes machen:"
|
||||
|
||||
msgid "Step 1: Enable the clipper service"
|
||||
msgstr "Schritt 1: Clipper Service aktivieren"
|
||||
msgstr "Schritt 1: Clipper-Service aktivieren"
|
||||
|
||||
msgid ""
|
||||
"This service allows the browser extension to communicate with Joplin. When "
|
||||
@@ -771,7 +774,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Dieser Service erlaubt es der Browser-Erweiterung mit Joplin zu "
|
||||
"kommunizieren. Beim Aktivieren kann deine Firewall dich nach der Erlaubnis "
|
||||
"bitten, dass Joplin auf einen bestimmten Port mithört."
|
||||
"bitten, dass Joplin auf einen bestimmten Port mithören darf."
|
||||
|
||||
msgid "Step 2: Install the extension"
|
||||
msgstr "Schritt 2: Erweiterung installieren"
|
||||
@@ -781,20 +784,21 @@ msgstr ""
|
||||
"Lade die entsprechende Erweiterung für deinen Browser herunter und "
|
||||
"installiere sie:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Advanced options"
|
||||
msgstr "Erweiterte Optionen anzeigen"
|
||||
msgstr "Erweiterte Optionen"
|
||||
|
||||
msgid "Authorisation token:"
|
||||
msgstr ""
|
||||
msgstr "Autorisierung-Token:"
|
||||
|
||||
msgid "Copy token"
|
||||
msgstr ""
|
||||
msgstr "Token kopieren"
|
||||
|
||||
msgid ""
|
||||
"This authorisation token is only needed to allow third-party applications to "
|
||||
"access Joplin."
|
||||
msgstr ""
|
||||
"Dieses Autorisierung-Token wird nur benötigt, um Drittanbieter-Anwendungen "
|
||||
"Zugriff auf Joplin zu gewähren."
|
||||
|
||||
msgid "Check synchronisation configuration"
|
||||
msgstr "Überprüfen der Synchronisationseinstellungen"
|
||||
@@ -874,13 +878,14 @@ msgstr ""
|
||||
"Notizbücher ursprünglich verschlüsselt wurden."
|
||||
|
||||
msgid "Missing Master Keys"
|
||||
msgstr "Fehlender Master-Key"
|
||||
msgstr "Fehlender Hauptschlüssel"
|
||||
|
||||
msgid ""
|
||||
"The master keys with these IDs are used to encrypt some of your items, "
|
||||
"however the application does not currently have access to them. It is likely "
|
||||
"they will eventually be downloaded via synchronisation."
|
||||
msgstr "Die Master-Keas dieser IDs werden für die Verschlüsselung einiger ..."
|
||||
msgstr ""
|
||||
"Die Hauptschlüssel dieser IDs werden für die Verschlüsselung einiger ..."
|
||||
|
||||
msgid ""
|
||||
"For more information about End-To-End Encryption (E2EE) and advices on how "
|
||||
@@ -912,10 +917,10 @@ msgid "Please create a notebook first"
|
||||
msgstr "Bitte erstelle zuerst ein Notizbuch"
|
||||
|
||||
msgid "Notebook title:"
|
||||
msgstr "Notizbuch Titel:"
|
||||
msgstr "Notizbuch-Titel:"
|
||||
|
||||
msgid "Add or remove tags:"
|
||||
msgstr "Füge hinzu oder entferne Schlagwörter:"
|
||||
msgstr "Schlagwörter hinzufügen oder entfernen:"
|
||||
|
||||
msgid "Separate each tag by a comma."
|
||||
msgstr "Trenne jedes Schlagwort mit einem Komma."
|
||||
@@ -960,6 +965,12 @@ msgstr "%s kopieren"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Zwischen Notiz und To-Do Typ wechseln"
|
||||
|
||||
msgid "Switch to note type"
|
||||
msgstr "Zu Notiz-Typ wechseln"
|
||||
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Zu To-Do-Typ wechseln"
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Markdown-Link kopieren"
|
||||
|
||||
@@ -971,8 +982,7 @@ msgstr "Notizen löschen?"
|
||||
|
||||
msgid "No notes in here. Create one by clicking on \"New note\"."
|
||||
msgstr ""
|
||||
"Hier sind noch keine Notizen. Erstelle eine, indem du auf \"Neue Notiz\" "
|
||||
"drückst."
|
||||
"Keine Notizen vorhanden. Erstelle eine, indem du auf \"Neue Notiz\" drückst."
|
||||
|
||||
msgid ""
|
||||
"There is currently no notebook. Create one by clicking on \"New notebook\"."
|
||||
@@ -984,7 +994,7 @@ msgid "Location"
|
||||
msgstr "Ablageort"
|
||||
|
||||
msgid "URL"
|
||||
msgstr ""
|
||||
msgstr "URL"
|
||||
|
||||
msgid "Open..."
|
||||
msgstr "Öffne..."
|
||||
@@ -994,7 +1004,7 @@ msgid "This file could not be opened: %s"
|
||||
msgstr "Dieses Notizbuch konnte nicht geöffnet werden: %s"
|
||||
|
||||
msgid "Save as..."
|
||||
msgstr "Sichern unter..."
|
||||
msgstr "Speichern unter..."
|
||||
|
||||
msgid "Copy path to clipboard"
|
||||
msgstr "Pfad in Zwischenablage kopieren"
|
||||
@@ -1030,7 +1040,7 @@ msgid "Attach file"
|
||||
msgstr "Datei anhängen"
|
||||
|
||||
msgid "Tags"
|
||||
msgstr "Markierungen"
|
||||
msgstr "Schlagwörter"
|
||||
|
||||
msgid "Set alarm"
|
||||
msgstr "Alarm erstellen"
|
||||
@@ -1066,9 +1076,8 @@ msgstr "Horizontale Linie"
|
||||
msgid "Click to stop external editing"
|
||||
msgstr "Klicken Sie hier, um die externe Bearbeitung anzuhalten"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Watching..."
|
||||
msgstr "Ansehen…"
|
||||
msgstr "Zuschauend…"
|
||||
|
||||
msgid "to-do"
|
||||
msgstr "To-Do"
|
||||
@@ -1087,22 +1096,22 @@ msgid "Clear"
|
||||
msgstr "Leeren"
|
||||
|
||||
msgid "OneDrive Login"
|
||||
msgstr "OneDrive Anmeldung"
|
||||
msgstr "OneDrive-Anmeldung"
|
||||
|
||||
msgid "Dropbox Login"
|
||||
msgstr "Dropbox Anmeldung"
|
||||
msgstr "Dropbox-Anmeldung"
|
||||
|
||||
msgid "Options"
|
||||
msgstr "Optionen"
|
||||
|
||||
msgid "Synchronisation Status"
|
||||
msgstr "Synchronisations Status"
|
||||
msgstr "Synchronisations-Status"
|
||||
|
||||
msgid "Encryption Options"
|
||||
msgstr "Verschlüsselungsoptionen"
|
||||
|
||||
msgid "Clipper Options"
|
||||
msgstr "Clipper Einstellungen"
|
||||
msgstr "Clipper-Einstellungen"
|
||||
|
||||
msgid "Remove this tag from all the notes?"
|
||||
msgstr "Dieses Schlagwort von allen Notizen entfernen?"
|
||||
@@ -1121,7 +1130,7 @@ msgstr "Notizbücher"
|
||||
|
||||
msgid "Please select where the sync status should be exported to"
|
||||
msgstr ""
|
||||
"Bitte wähle aus, wohin der Synchronisations Status exportiert werden soll"
|
||||
"Bitte wähle aus, wohin der Synchronisations-Status exportiert werden soll"
|
||||
|
||||
#, javascript-format
|
||||
msgid "Usage: %s"
|
||||
@@ -1151,11 +1160,11 @@ msgstr "WebDAV"
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unknown log level: %s"
|
||||
msgstr "Unbekanntes Log Level: %s"
|
||||
msgstr "Unbekanntes Log-Level: %s"
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unknown level ID: %s"
|
||||
msgstr "Unbekannte Level ID: %s"
|
||||
msgstr "Unbekannte Level-ID: %s"
|
||||
|
||||
msgid ""
|
||||
"Cannot refresh token: authentication data is missing. Starting the "
|
||||
@@ -1177,7 +1186,7 @@ msgstr ""
|
||||
"Dieser Fehler kommt oft vor, wenn OneDrive Business benutzt wird, das leider "
|
||||
"nicht unterstützt wird.\n"
|
||||
"\n"
|
||||
"Bitte benutze stattdessen einen normalen OneDrive Account."
|
||||
"Bitte benutze stattdessen einen normalen OneDrive-Account."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Cannot access %s"
|
||||
@@ -1193,11 +1202,11 @@ msgstr "Lokale Objekte aktualisiert: %d."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Created remote items: %d."
|
||||
msgstr "Remote Objekte erstellt: %d."
|
||||
msgstr "Remote-Objekte erstellt: %d."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Updated remote items: %d."
|
||||
msgstr "Remote Objekte aktualisiert: %d."
|
||||
msgstr "Remote-Objekte aktualisiert: %d."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Deleted local items: %d."
|
||||
@@ -1205,7 +1214,7 @@ msgstr "Lokale Objekte gelöscht: %d."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Deleted remote items: %d."
|
||||
msgstr "Remote Objekte gelöscht: %d."
|
||||
msgstr "Remote-Objekte gelöscht: %d."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Fetched items: %d/%d."
|
||||
@@ -1216,7 +1225,7 @@ msgid "State: %s."
|
||||
msgstr "Status: %s."
|
||||
|
||||
msgid "Cancelling..."
|
||||
msgstr "Abbrechen…"
|
||||
msgstr "Breche ab…"
|
||||
|
||||
#, javascript-format
|
||||
msgid "Completed: %s"
|
||||
@@ -1251,7 +1260,7 @@ msgstr "Kann Notizbuch nicht an diesen Ort verschieben"
|
||||
#, javascript-format
|
||||
msgid "Notebooks cannot be named \"%s\", which is a reserved title."
|
||||
msgstr ""
|
||||
"Notizbuch kann nicht \"%s\" genannt werden. Dies ist ein reservierter Titel."
|
||||
"Notizbuch kann nicht \"%s\" genannt werden. Dieser Name ist reserviert.."
|
||||
|
||||
msgid "title"
|
||||
msgstr "Titel"
|
||||
@@ -1298,7 +1307,7 @@ msgid "Uncompleted to-dos on top"
|
||||
msgstr "Zeige unvollständige To-Dos an oberster Stelle"
|
||||
|
||||
msgid "Show completed to-dos"
|
||||
msgstr "Abgeschlossene ToDos anzeigen"
|
||||
msgstr "Abgeschlossene To-Dos anzeigen"
|
||||
|
||||
msgid "Sort notes by"
|
||||
msgstr "Sortiere Notizen nach"
|
||||
@@ -1322,7 +1331,7 @@ msgid "When creating a new note:"
|
||||
msgstr "Wenn eine neue Notiz erstellt wird:"
|
||||
|
||||
msgid "Show tray icon"
|
||||
msgstr "Zeige Tray Icon"
|
||||
msgstr "Zeige Tray-Icon"
|
||||
|
||||
msgid "Note: Does not work in all desktop environments."
|
||||
msgstr "Hinweis: Funktioniert nicht in allen Desktopumgebungen."
|
||||
@@ -1387,7 +1396,7 @@ msgstr ""
|
||||
"dokumentiert) bezeichnet werden."
|
||||
|
||||
msgid "Directory to synchronise with (absolute path)"
|
||||
msgstr "Verzeichnis zum synchronisieren (absoluter Pfad)"
|
||||
msgstr "Verzeichnis mit dem synchronisiert werden soll (absoluter Pfad)"
|
||||
|
||||
msgid ""
|
||||
"The path to synchronise with when file system synchronisation is enabled. "
|
||||
@@ -1397,7 +1406,7 @@ msgstr ""
|
||||
"Synchronisation aktiviert ist. Siehe `sync.target`."
|
||||
|
||||
msgid "Nextcloud WebDAV URL"
|
||||
msgstr "Nextcloud WebDAV URL"
|
||||
msgstr "Nextcloud WebDAV-URL"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
@@ -1410,19 +1419,19 @@ msgstr ""
|
||||
"die FAQs hierzu: %s"
|
||||
|
||||
msgid "Nextcloud username"
|
||||
msgstr "Nextcloud Benutzername"
|
||||
msgstr "Nextcloud-Benutzername"
|
||||
|
||||
msgid "Nextcloud password"
|
||||
msgstr "Nextcloud Passwort"
|
||||
msgstr "Nextcloud-Passwort"
|
||||
|
||||
msgid "WebDAV URL"
|
||||
msgstr "WebDAV URL"
|
||||
msgstr "WebDAV-URL"
|
||||
|
||||
msgid "WebDAV username"
|
||||
msgstr "WebDAV Benutzername"
|
||||
msgstr "WebDAV-Benutzername"
|
||||
|
||||
msgid "WebDAV password"
|
||||
msgstr "WebDAV Passwort"
|
||||
msgstr "WebDAV-Passwort"
|
||||
|
||||
msgid "Custom TLS certificates"
|
||||
msgstr "Benutzerdefinierte TLS-Zertifikate"
|
||||
@@ -1434,9 +1443,9 @@ msgid ""
|
||||
"changes before clicking on \"Check synchronisation configuration\"."
|
||||
msgstr ""
|
||||
"Kommagetrennte Liste von Pfaden zu Verzeichnissen, aus denen die Zertifikate "
|
||||
"geladen werden, oder Pfad zu einzelnen Zertifikatsdateien. Zum Beispiel: / "
|
||||
"my / cert_dir, /other/custom.pem. Wenn Sie Änderungen an den TLS-"
|
||||
"Einstellungen vornehmen, müssen Sie Ihre Änderungen speichern, bevor Sie auf "
|
||||
"geladen werden, oder Pfad zu einzelnen Zertifikatsdateien. Zum Beispiel: /my/"
|
||||
"cert_dir, /other/custom.pem. Wenn Sie Änderungen an den TLS-Einstellungen "
|
||||
"vornehmen, müssen Sie Ihre Änderungen speichern, bevor Sie auf "
|
||||
"\"Synchronisierungskonfiguration prüfen\" klicken."
|
||||
|
||||
msgid "Ignore TLS certificate errors"
|
||||
@@ -1568,6 +1577,9 @@ msgstr "%d Notizen in das Notizbuch \"%s\" verschieben?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "Tippe hier, um das Entschlüsselungspasswort festzulegen."
|
||||
|
||||
msgid "Clear alarm"
|
||||
msgstr "Alarm löschen"
|
||||
|
||||
msgid "Save alarm"
|
||||
msgstr "Alarm speichern"
|
||||
|
||||
@@ -1584,6 +1596,41 @@ msgstr "Synchronisation abbrechen"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Entschlüsselte Objekte: %d/%d"
|
||||
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Überprüfe… Bitte warten."
|
||||
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr ""
|
||||
"Erfolgreich. Die Synchronisation-Konfiguration scheint korrekt zu sein."
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
"Fehler. Bitte überprüfe, ob die URL, der Benutzername, das Passwort. usw. "
|
||||
"korrekt sind und das das Synchronisierungsziel erreichbar ist. Fehlermeldung:"
|
||||
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "Das Programm wurde erfolgreich autorisiert."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
"Konnte Applikation nicht autorisieren:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Bitte versuche es erneut."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Entschlüsselte Objekte: %s / %s"
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "Neue Schlagwörter:"
|
||||
|
||||
@@ -1713,8 +1760,8 @@ msgstr ""
|
||||
|
||||
msgid "You currently have no notebook. Create one by clicking on (+) button."
|
||||
msgstr ""
|
||||
"Du hast noch kein Notizbuch. Erstelle eines, indem du auf den (+) Knopf "
|
||||
"drückst."
|
||||
"Du hast noch kein Notizbuch angelegt. Erstelle eines, indem du auf den (+) "
|
||||
"Knopf drückst."
|
||||
|
||||
msgid "Welcome"
|
||||
msgstr "Willkommen"
|
||||
|
@@ -552,6 +552,9 @@ msgstr ""
|
||||
msgid "Paste"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select all"
|
||||
msgstr ""
|
||||
|
||||
msgid "Bold"
|
||||
msgstr ""
|
||||
|
||||
@@ -843,6 +846,12 @@ msgstr ""
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr ""
|
||||
|
||||
msgid "Switch to note type"
|
||||
msgstr ""
|
||||
|
||||
msgid "Switch to to-do type"
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr ""
|
||||
|
||||
@@ -1407,6 +1416,9 @@ msgstr ""
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr ""
|
||||
|
||||
msgid "Clear alarm"
|
||||
msgstr ""
|
||||
|
||||
msgid "Save alarm"
|
||||
msgstr ""
|
||||
|
||||
@@ -1423,6 +1435,33 @@ msgstr ""
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checking... Please wait."
|
||||
msgstr ""
|
||||
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
msgid "The application has been authorised!"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr ""
|
||||
|
||||
|
@@ -629,6 +629,10 @@ msgstr "Cortar"
|
||||
msgid "Paste"
|
||||
msgstr "Pegar"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Seleccione fecha"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr "Negrita"
|
||||
|
||||
@@ -943,6 +947,14 @@ msgstr "%s - Copiar"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Cambiar entre nota y lista de tareas"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "Cambiar entre nota y lista de tareas"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Cambiar entre nota y lista de tareas"
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Copiar el enlace de Markdown"
|
||||
|
||||
@@ -1539,6 +1551,10 @@ msgstr "¿Desea mover %d notas a libreta «%s»?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "Presione para establecer la contraseña de descifrado."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "Establecer alarma"
|
||||
|
||||
msgid "Save alarm"
|
||||
msgstr "Establecer alarma"
|
||||
|
||||
@@ -1555,6 +1571,36 @@ msgstr "Cancelar sincronización"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Descifrando elementos: %d/%d."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Cancelando... Por favor espere."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "Comprobar sincronización"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "La aplicacion ha sido autorizada éxitosamente."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Descifrando elementos: %d/%d."
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "Nuevas etiquetas:"
|
||||
|
||||
|
@@ -628,6 +628,10 @@ msgstr "Moztu"
|
||||
msgid "Paste"
|
||||
msgstr "Itsatsi"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Data aukeratu"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr ""
|
||||
|
||||
@@ -939,6 +943,14 @@ msgstr "Kopiatu"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Aldatu oharra eta zeregin eren artean."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "Aldatu oharra eta zeregin eren artean."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Aldatu oharra eta zeregin eren artean."
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr ""
|
||||
|
||||
@@ -1546,6 +1558,10 @@ msgstr "Mugitu %d oharrak \"%s\" koadernora?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "Sakatu deszifratze pasahitza ezartzeko."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "Ezarri alarma"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Save alarm"
|
||||
msgstr "Ezarri alarma"
|
||||
@@ -1563,6 +1579,36 @@ msgstr "Sinkronizazioa utzi"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Itemak eskuratuta: %d%d."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Bertan behera uzten... itxaron, mesedez."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "Sinkronizazioa utzi"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "Aplikazioak baimena hartu du."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Itemak eskuratuta: %d%d."
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr ""
|
||||
|
||||
|
@@ -627,6 +627,9 @@ msgstr "Couper"
|
||||
msgid "Paste"
|
||||
msgstr "Coller"
|
||||
|
||||
msgid "Select all"
|
||||
msgstr "Sélectionner tout"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr "Gras"
|
||||
|
||||
@@ -947,6 +950,12 @@ msgstr "%s - Copie"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Alterner entre note et tâche"
|
||||
|
||||
msgid "Switch to note type"
|
||||
msgstr "Convertir en note"
|
||||
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Convertir en tâche"
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Copier lien Markdown"
|
||||
|
||||
@@ -1550,6 +1559,9 @@ msgstr "Déplacer %d notes vers carnet \"%s\" ?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "Définir mot de passe de synchronisation."
|
||||
|
||||
msgid "Clear alarm"
|
||||
msgstr "Enlever l'alarme"
|
||||
|
||||
msgid "Save alarm"
|
||||
msgstr "Enregistrer alarme"
|
||||
|
||||
@@ -1566,6 +1578,40 @@ msgstr "Annuler synchronisation"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Déchiffrement des objets : %d/%d"
|
||||
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Vérification... Veuillez attendre."
|
||||
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "La configuration de la synchronisation semble correcte."
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
"Erreur. Veuillez vérifier que l'URL, le nom, le mot de passe, etc. sont "
|
||||
"corrects et que la destination est accessible. L'erreur reportée est :"
|
||||
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "Le logiciel a été autorisé !"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
"Impossible d'autoriser le logiciel :\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Veuillez réessayer."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Déchiffrement : %s / %s"
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "Nouvelles étiquettes :"
|
||||
|
||||
|
@@ -620,6 +620,10 @@ msgstr "Cortar"
|
||||
msgid "Paste"
|
||||
msgstr "Pegar"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Seleccionar data"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr ""
|
||||
|
||||
@@ -926,6 +930,14 @@ msgstr "Copiar"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Cambiar entre notas e tarefas"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "Cambiar entre notas e tarefas"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Cambiar entre notas e tarefas"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Markdown"
|
||||
@@ -1524,6 +1536,10 @@ msgstr "Mover %d notas para o caderno \"%s\"?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "Prema para estabelecer o contrasinal de descifrado."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "Estabelecer alarma"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Save alarm"
|
||||
msgstr "Estabelecer alarma"
|
||||
@@ -1541,6 +1557,36 @@ msgstr "Cancelar sincronización"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Elementos obtidos: %d/%d."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Cancelando... Agarde."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "Comprobar a configuración da sincronización"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "O aplicativo foi autorizado correctamente."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Elementos obtidos: %d/%d."
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "Etiquetas novas:"
|
||||
|
||||
|
@@ -625,6 +625,10 @@ msgstr "Izreži"
|
||||
msgid "Paste"
|
||||
msgstr "Zalijepi"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Odaberi datum"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr ""
|
||||
|
||||
@@ -926,6 +930,14 @@ msgstr "Kopiraj"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Zamijeni bilješku i zadatak"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "Zamijeni bilješku i zadatak"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Zamijeni bilješku i zadatak"
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr ""
|
||||
|
||||
@@ -1521,6 +1533,10 @@ msgstr "Premjesti %d bilješke u bilježnicu \"%s\"?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "Postavi upozorenje"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Save alarm"
|
||||
msgstr "Postavi upozorenje"
|
||||
@@ -1538,6 +1554,36 @@ msgstr "Prekini sinkronizaciju"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Stvorene lokalne stavke: %d."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Prekidam... Pričekaj."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "Prekini sinkronizaciju"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "Aplikacija je uspješno autorizirana."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Stvorene lokalne stavke: %d."
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr ""
|
||||
|
||||
|
@@ -626,6 +626,10 @@ msgstr "Taglia"
|
||||
msgid "Paste"
|
||||
msgstr "Incolla"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Seleziona la data"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr "Grasseto"
|
||||
|
||||
@@ -941,6 +945,14 @@ msgstr "%s - Copia"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Converti nota in \"Cose-da-fare\" e viceversa"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "Converti nota in \"Cose-da-fare\" e viceversa"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Converti nota in \"Cose-da-fare\" e viceversa"
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Copia il link Markdown"
|
||||
|
||||
@@ -1538,6 +1550,10 @@ msgstr "Spostare le note %d sul Taccuino \"%s\"?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "Premere per impostare la password di decrittografia."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "Imposta Allarme"
|
||||
|
||||
msgid "Save alarm"
|
||||
msgstr "Salva Allarme"
|
||||
|
||||
@@ -1554,6 +1570,36 @@ msgstr "Cancella la sincronizzazione"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Decrittografia Elementi: %d/%d"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Cancellazione... Attendere per favore."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "Controlla la configurazione della sincronizzazione"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "L'applicazione è stata autorizzata con successo."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Decrittografia Elementi: %d/%d"
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "Nuovi tag:"
|
||||
|
||||
|
@@ -615,6 +615,10 @@ msgstr "切り取り"
|
||||
msgid "Paste"
|
||||
msgstr "貼り付け"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "日付の選択"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr "太字"
|
||||
|
||||
@@ -927,6 +931,14 @@ msgstr "%s - コピー"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "ノートとToDoを切り替え"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "ノートとToDoを切り替え"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "ノートとToDoを切り替え"
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Markdownのリンクをコピー"
|
||||
|
||||
@@ -1523,6 +1535,10 @@ msgstr "%d個のノートを\"%s\"に移動しますか?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "復号するパスワードを入力してください。"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "アラームをセット"
|
||||
|
||||
msgid "Save alarm"
|
||||
msgstr "アラームの保存"
|
||||
|
||||
@@ -1539,6 +1555,36 @@ msgstr "同期の中止"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "復号中のアイテム: %d/%d"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "中止中...お待ちください。"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "同期の設定を確認する"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "アプリケーションは問題なく認証されました。"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "復号中のアイテム: %d/%d"
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "新しいタグ:"
|
||||
|
||||
|
@@ -552,6 +552,9 @@ msgstr ""
|
||||
msgid "Paste"
|
||||
msgstr ""
|
||||
|
||||
msgid "Select all"
|
||||
msgstr ""
|
||||
|
||||
msgid "Bold"
|
||||
msgstr ""
|
||||
|
||||
@@ -843,6 +846,12 @@ msgstr ""
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr ""
|
||||
|
||||
msgid "Switch to note type"
|
||||
msgstr ""
|
||||
|
||||
msgid "Switch to to-do type"
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr ""
|
||||
|
||||
@@ -1407,6 +1416,9 @@ msgstr ""
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr ""
|
||||
|
||||
msgid "Clear alarm"
|
||||
msgstr ""
|
||||
|
||||
msgid "Save alarm"
|
||||
msgstr ""
|
||||
|
||||
@@ -1423,6 +1435,33 @@ msgstr ""
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr ""
|
||||
|
||||
msgid "Checking... Please wait."
|
||||
msgstr ""
|
||||
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
msgid "The application has been authorised!"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr ""
|
||||
|
||||
|
@@ -612,6 +612,10 @@ msgstr "잘라내기"
|
||||
msgid "Paste"
|
||||
msgstr "붙여넣기"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "날짜 선택"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr "굵게"
|
||||
|
||||
@@ -921,6 +925,14 @@ msgstr "%s - 복사"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "'노트' 또는'할 일' 형식으로 전환합니다."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "'노트' 또는'할 일' 형식으로 전환합니다."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "'노트' 또는'할 일' 형식으로 전환합니다."
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "마크다운 링크 복사"
|
||||
|
||||
@@ -1513,6 +1525,10 @@ msgstr "%d 노트를 \"%s\" 노트북으로 옮길까요?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "복호화 암호를 설정하려면 누르세요."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "알람 설정"
|
||||
|
||||
msgid "Save alarm"
|
||||
msgstr "알람 저장"
|
||||
|
||||
@@ -1529,6 +1545,36 @@ msgstr "동기화 취소"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "복호화 항목: %d/%d"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "취소하는 중입니다... 잠시만 기다리세요."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "동기화 설정 확인"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "애플리케이션이 성공적으로 허가되었습니다."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "복호화 항목: %d/%d"
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "새 태그:"
|
||||
|
||||
|
@@ -630,6 +630,10 @@ msgstr "Knip"
|
||||
msgid "Paste"
|
||||
msgstr "Plak"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Selecteer datum"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr ""
|
||||
|
||||
@@ -941,6 +945,14 @@ msgstr "Kopieer"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Wissel tussen notitie en to-do type"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "Wissel tussen notitie en to-do type"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Wissel tussen notitie en to-do type"
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr ""
|
||||
|
||||
@@ -1548,6 +1560,10 @@ msgstr "Verplaats %d notities naar notitieboek \"%s\"?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "Klik om het decryptie wachtwoord in te stellen"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "Zet melding"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Save alarm"
|
||||
msgstr "Zet melding"
|
||||
@@ -1565,6 +1581,36 @@ msgstr "Annuleer synchronisatie"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Opgehaalde items: %d/%d."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Annuleren.. Even geduld."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "Annuleer synchronisatie"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "De applicatie is succesvol geauthenticeerd."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Opgehaalde items: %d/%d."
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr ""
|
||||
|
||||
|
@@ -629,6 +629,10 @@ msgstr "Knippen"
|
||||
msgid "Paste"
|
||||
msgstr "Plakken"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Datum kiezen"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr "Vetgedrukt"
|
||||
|
||||
@@ -945,6 +949,14 @@ msgstr "%s - kopiëren"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Schakelen tussen notitie en taak"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "Schakelen tussen notitie en taak"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Schakelen tussen notitie en taak"
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Markdownlink kopiëren"
|
||||
|
||||
@@ -1548,6 +1560,10 @@ msgstr "%d notities verplaatsen naar notitieboek \"%s\"?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "Druk om het ontsleutelwachtwoord in te stellen."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "Alarm instellen"
|
||||
|
||||
msgid "Save alarm"
|
||||
msgstr "Alarm opslaam"
|
||||
|
||||
@@ -1564,6 +1580,36 @@ msgstr "Synchronisatie annuleren"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Bezig met ontsleutelen van items: %d/%d"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Bezig met annuleren... Even geduld."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "Synchronisatieconfiguratie controleren"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "De applicatie is geautoriseerd."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Bezig met ontsleutelen van items: %d/%d"
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "Nieuwe labels:"
|
||||
|
||||
|
@@ -623,6 +623,10 @@ msgstr "Klipp ut"
|
||||
msgid "Paste"
|
||||
msgstr "Lim inn"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Velg dato"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr ""
|
||||
|
||||
@@ -933,6 +937,14 @@ msgstr "Kopier"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Bytt mellom notat og gjøremål"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "Bytt mellom notat og gjøremål"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Bytt mellom notat og gjøremål"
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Kopier Link"
|
||||
|
||||
@@ -1530,6 +1542,10 @@ msgstr "Flytt %d notater til notisblokk \"%s\"?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "Trykk for å sette krypteringspassordet"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "Angi Alarm"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Save alarm"
|
||||
msgstr "Angi Alarm"
|
||||
@@ -1547,6 +1563,36 @@ msgstr "Stopp Synkronisering"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Hentede elementer: %d/%d."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Stopper… Vennligst vent."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "Sjekk synkroniseringskonfigurasjonen"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "Applikasjonen har blitt godkjent."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Hentede elementer: %d/%d."
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "Nye merkelapper:"
|
||||
|
||||
|
@@ -625,6 +625,10 @@ msgstr "Cortar"
|
||||
msgid "Paste"
|
||||
msgstr "Colar"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Selecionar data"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr "Negrito"
|
||||
|
||||
@@ -939,6 +943,14 @@ msgstr "%s - Copiar"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Alternar entre os tipos Nota e Tarefa"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "Alternar entre os tipos Nota e Tarefa"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Alternar entre os tipos Nota e Tarefa"
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Copiar link de Markdown"
|
||||
|
||||
@@ -1536,6 +1548,10 @@ msgstr "Mover %d notas para o caderno \"%s\"?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "Pressione para configurar a senha de decriptação."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "Definir alarme"
|
||||
|
||||
msgid "Save alarm"
|
||||
msgstr "Salvar alarme"
|
||||
|
||||
@@ -1552,6 +1568,36 @@ msgstr "Cancelar sincronização"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Decriptando itens: %d/%d."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Cancelando... Aguarde."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "Verificar a configuração da sincronização"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "O aplicativo foi autorizado com sucesso."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Decriptando itens: %d/%d."
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "Novas tags:"
|
||||
|
||||
|
@@ -565,6 +565,10 @@ msgstr "Tăiați"
|
||||
msgid "Paste"
|
||||
msgstr "Lipește"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Selectați data"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr ""
|
||||
|
||||
@@ -857,6 +861,14 @@ msgstr "%s - Copiați"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Schimbați între notiță și sarcină"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "Schimbați între notiță și sarcină"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Schimbați între notiță și sarcină"
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Copiați link-ul Markdown"
|
||||
|
||||
@@ -1421,6 +1433,10 @@ msgstr "Mutați %d notițe în caietul de notițe \"%s\"?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "Setați alarma"
|
||||
|
||||
msgid "Save alarm"
|
||||
msgstr "Salvați alarma"
|
||||
|
||||
@@ -1437,6 +1453,34 @@ msgstr "Amânați sincronizarea"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Se decriptează itemi: %d/%d"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Se anulează... Vă rugăm să așteptați."
|
||||
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
msgid "The application has been authorised!"
|
||||
msgstr ""
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Se decriptează itemi: %d/%d"
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "Etichete noi:"
|
||||
|
||||
|
@@ -627,6 +627,10 @@ msgstr "Вырезать"
|
||||
msgid "Paste"
|
||||
msgstr "Вставить"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Выбрать дату"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr ""
|
||||
|
||||
@@ -936,6 +940,14 @@ msgstr "Копировать"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Переключить тип между заметкой и задачей"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "Переключить тип между заметкой и задачей"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Переключить тип между заметкой и задачей"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Markdown"
|
||||
@@ -1535,6 +1547,10 @@ msgstr "Переместить %d заметок в блокнот «%s»?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "Нажмите, чтобы установить пароль для расшифровки."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "Установить напоминание"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Save alarm"
|
||||
msgstr "Установить напоминание"
|
||||
@@ -1552,6 +1568,36 @@ msgstr "Отменить синхронизацию"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Получено элементов: %d/%d."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Отмена... Пожалуйста, ожидайте."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "Проверить настройки синхронизации"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "Приложение успешно авторизовано."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Получено элементов: %d/%d."
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr ""
|
||||
|
||||
|
@@ -626,6 +626,10 @@ msgstr "Izreži"
|
||||
msgid "Paste"
|
||||
msgstr "Prilepi"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Izberi datum"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr ""
|
||||
|
||||
@@ -936,6 +940,14 @@ msgstr "Kopiraj"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Menjaj med zabeležko in seznamom opravil"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "Menjaj med zabeležko in seznamom opravil"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Menjaj med zabeležko in seznamom opravil"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Sistem označevanja"
|
||||
@@ -1538,6 +1550,10 @@ msgstr "Premakni %d zabeležk v beležnico \"%s\"?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "Klikni za nastavitev dekripcijskega gesla."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "Nastavi alarm"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Save alarm"
|
||||
msgstr "Nastavi alarm"
|
||||
@@ -1555,6 +1571,36 @@ msgstr "Prekliči sinhronizacijo"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Preneseni predmeti: %d/%d."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "V preklicu...Prosim počakajte."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "Preveri nastavitve sinhronizacije"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "Aplikacija je bila uspešno avtorizirana."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Preneseni predmeti: %d/%d."
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr ""
|
||||
|
||||
|
@@ -633,6 +633,10 @@ msgstr "Klipp ut"
|
||||
msgid "Paste"
|
||||
msgstr "Klistra in"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Välj datum"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr "Fet"
|
||||
|
||||
@@ -948,6 +952,14 @@ msgstr "%s - Kopiera"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "Växla mellan antecknings- och att-göra-typ"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "Växla mellan antecknings- och att-göra-typ"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Växla mellan antecknings- och att-göra-typ"
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Kopiera Markdown-länk"
|
||||
|
||||
@@ -1547,6 +1559,10 @@ msgstr "Flytta %d anteckningar till anteckningsboken \"%s\"?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "Tryck för att ställa in dekrypteringslösenordet."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "Sätt alarm"
|
||||
|
||||
msgid "Save alarm"
|
||||
msgstr "Spara alarm"
|
||||
|
||||
@@ -1563,6 +1579,36 @@ msgstr "Avbryt synkronisering"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Dekrypterar objekt: %d/%d"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Avbryter... vänta."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "Kontrollera synkroniseringskonfigurationen"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "Programmet har godkänts."
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Dekrypterar objekt: %d/%d"
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "Nya taggar:"
|
||||
|
||||
|
@@ -591,6 +591,10 @@ msgstr "剪切"
|
||||
msgid "Paste"
|
||||
msgstr "粘贴"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "选择日期"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr "粗体"
|
||||
|
||||
@@ -895,6 +899,14 @@ msgstr "%s - 副本"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "在笔记和待办事项类型之间切换"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "在笔记和待办事项类型之间切换"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "在笔记和待办事项类型之间切换"
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "复制 Markdown 链接"
|
||||
|
||||
@@ -1478,6 +1490,10 @@ msgstr "移动%d条笔记至笔记本\"%s\"?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "按键将设置解密密码。"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "设置提醒"
|
||||
|
||||
msgid "Save alarm"
|
||||
msgstr "保存提醒"
|
||||
|
||||
@@ -1494,6 +1510,36 @@ msgstr "取消同步"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "解密项目:%d/%d"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "正在取消... 请稍后。"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "检查同步配置"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "此程序已被成功授权。"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "解密项目:%d/%d"
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "新标签:"
|
||||
|
||||
|
@@ -592,6 +592,10 @@ msgstr "剪下"
|
||||
msgid "Paste"
|
||||
msgstr "貼上"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "選擇日期"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr "粗體"
|
||||
|
||||
@@ -895,6 +899,14 @@ msgstr "%s - 複本"
|
||||
msgid "Switch between note and to-do type"
|
||||
msgstr "切換到記事 / 待辦事項"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to note type"
|
||||
msgstr "切換到記事 / 待辦事項"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "切換到記事 / 待辦事項"
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "複製 Markdown 連結"
|
||||
|
||||
@@ -1475,6 +1487,10 @@ msgstr "移動 %d 記事到記事本 \"%s\"?"
|
||||
msgid "Press to set the decryption password."
|
||||
msgstr "按下以設置解密密碼。"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Clear alarm"
|
||||
msgstr "設置提醒"
|
||||
|
||||
msgid "Save alarm"
|
||||
msgstr "儲存提醒事項"
|
||||
|
||||
@@ -1491,6 +1507,36 @@ msgstr "取消同步"
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "正在解密項目: %d/%d 項"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "正在取消中...請稍候。"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "檢測同步設置"
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "應用程式已成功取得權限。"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
"Could not authorise application:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "正在解密項目: %d/%d 項"
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "新增標籤:"
|
||||
|
||||
|
7
CliClient/package-lock.json
generated
7
CliClient/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "joplin",
|
||||
"version": "1.0.116",
|
||||
"version": "1.0.117",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -2890,6 +2890,11 @@
|
||||
"resolved": "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz",
|
||||
"integrity": "sha1-5z3T17DXxe2G+6xrCufYxqadUPo="
|
||||
},
|
||||
"unidecode": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/unidecode/-/unidecode-0.1.8.tgz",
|
||||
"integrity": "sha1-77swFTi8RSRqmsjFWdcvAVMFBT4="
|
||||
},
|
||||
"uniq": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
|
||||
|
@@ -19,7 +19,7 @@
|
||||
],
|
||||
"owner": "Laurent Cozic"
|
||||
},
|
||||
"version": "1.0.116",
|
||||
"version": "1.0.117",
|
||||
"bin": {
|
||||
"joplin": "./main.js"
|
||||
},
|
||||
@@ -68,6 +68,7 @@
|
||||
"tar": "^4.4.0",
|
||||
"tcp-port-used": "^0.1.2",
|
||||
"tkwidgets": "^0.5.26",
|
||||
"unidecode": "^0.1.8",
|
||||
"url-parse": "^1.2.0",
|
||||
"uuid": "^3.0.1",
|
||||
"valid-url": "^1.0.9",
|
||||
|
@@ -42,4 +42,25 @@ describe('models_Note', function() {
|
||||
expect(items.length).toBe(4);
|
||||
}));
|
||||
|
||||
it('should change the type of notes', asyncTest(async () => {
|
||||
let folder1 = await Folder.save({ title: "folder1" });
|
||||
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
|
||||
note1 = await Note.load(note1.id);
|
||||
|
||||
let changedNote = Note.changeNoteType(note1, 'todo');
|
||||
expect(changedNote === note1).toBe(false);
|
||||
expect(!!changedNote.is_todo).toBe(true);
|
||||
await Note.save(changedNote);
|
||||
|
||||
note1 = await Note.load(note1.id);
|
||||
changedNote = Note.changeNoteType(note1, 'todo');
|
||||
expect(changedNote === note1).toBe(true);
|
||||
expect(!!changedNote.is_todo).toBe(true);
|
||||
|
||||
note1 = await Note.load(note1.id);
|
||||
changedNote = Note.changeNoteType(note1, 'note');
|
||||
expect(changedNote === note1).toBe(false);
|
||||
expect(!!changedNote.is_todo).toBe(false);
|
||||
}));
|
||||
|
||||
});
|
31
CliClient/tests/models_Resource.js
Normal file
31
CliClient/tests/models_Resource.js
Normal file
@@ -0,0 +1,31 @@
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const { shim } = require('lib/shim');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
describe('models_Resource', function() {
|
||||
|
||||
beforeEach(async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should have a "done" fetch_status when created locally', asyncTest(async () => {
|
||||
let folder1 = await Folder.save({ title: "folder1" });
|
||||
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
|
||||
await shim.attachFileToNote(note1, __dirname + '/../tests/support/photo.jpg');
|
||||
let resource1 = (await Resource.all())[0];
|
||||
console.info(resource1);
|
||||
}));
|
||||
|
||||
});
|
@@ -7,6 +7,7 @@ const fs = require('fs-extra');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const ResourceFetcher = require('lib/services/ResourceFetcher');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
@@ -872,12 +873,46 @@ describe('Synchronizer', function() {
|
||||
let allResources = await Resource.all();
|
||||
expect(allResources.length).toBe(1);
|
||||
let resource1_2 = allResources[0];
|
||||
let resourcePath1_2 = Resource.fullPath(resource1_2);
|
||||
|
||||
expect(resource1_2.id).toBe(resource1.id);
|
||||
expect(resource1_2.fetch_status).toBe(Resource.FETCH_STATUS_IDLE);
|
||||
|
||||
const fetcher = new ResourceFetcher(() => { return synchronizer().api() });
|
||||
fetcher.queueDownload(resource1_2.id);
|
||||
await fetcher.waitForAllFinished();
|
||||
|
||||
resource1_2 = await Resource.load(resource1.id);
|
||||
expect(resource1_2.fetch_status).toBe(Resource.FETCH_STATUS_DONE);
|
||||
|
||||
let resourcePath1_2 = Resource.fullPath(resource1_2);
|
||||
expect(fileContentEqual(resourcePath1, resourcePath1_2)).toBe(true);
|
||||
}));
|
||||
|
||||
it('should handle resource download errors', asyncTest(async () => {
|
||||
while (insideBeforeEach) await time.msleep(500);
|
||||
|
||||
let folder1 = await Folder.save({ title: "folder1" });
|
||||
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
|
||||
await shim.attachFileToNote(note1, __dirname + '/../tests/support/photo.jpg');
|
||||
let resource1 = (await Resource.all())[0];
|
||||
let resourcePath1 = Resource.fullPath(resource1);
|
||||
await synchronizer().start();
|
||||
|
||||
await switchClient(2);
|
||||
|
||||
await synchronizer().start();
|
||||
|
||||
const fetcher = new ResourceFetcher(() => { return {
|
||||
// Simulate a failed download
|
||||
get: () => { return new Promise((resolve, reject) => { reject(new Error('did not work')) }); }
|
||||
} });
|
||||
fetcher.queueDownload(resource1.id);
|
||||
await fetcher.waitForAllFinished();
|
||||
|
||||
resource1 = await Resource.load(resource1.id);
|
||||
expect(resource1.fetch_status).toBe(Resource.FETCH_STATUS_ERROR);
|
||||
expect(resource1.fetch_error).toBe('did not work');
|
||||
}));
|
||||
|
||||
it('should delete resources', asyncTest(async () => {
|
||||
while (insideBeforeEach) await time.msleep(500);
|
||||
|
||||
@@ -926,6 +961,10 @@ describe('Synchronizer', function() {
|
||||
Setting.setObjectKey('encryption.passwordCache', masterKey.id, '123456');
|
||||
await encryptionService().loadMasterKeysFromSettings();
|
||||
|
||||
const fetcher = new ResourceFetcher(() => { return synchronizer().api() });
|
||||
fetcher.queueDownload(resource1.id);
|
||||
await fetcher.waitForAllFinished();
|
||||
|
||||
let resource1_2 = (await Resource.all())[0];
|
||||
resource1_2 = await Resource.decrypt(resource1_2);
|
||||
let resourcePath1_2 = Resource.fullPath(resource1_2);
|
||||
|
0
CliClientDemo/publish.sh
Normal file → Executable file
0
CliClientDemo/publish.sh
Normal file → Executable file
@@ -1,20 +1,20 @@
|
||||
function randomClipperPort(state, env) {
|
||||
const startPorts = {
|
||||
prod: 41184,
|
||||
dev: 27583,
|
||||
};
|
||||
const startPorts = {
|
||||
prod: 41184,
|
||||
dev: 27583,
|
||||
};
|
||||
|
||||
const startPort = env === 'prod' ? startPorts.prod : startPorts.dev;
|
||||
const startPort = env === 'prod' ? startPorts.prod : startPorts.dev;
|
||||
|
||||
if (!state) {
|
||||
state = { offset: 0 };
|
||||
} else {
|
||||
state.offset++;
|
||||
}
|
||||
if (!state) {
|
||||
state = { offset: 0 };
|
||||
} else {
|
||||
state.offset++;
|
||||
}
|
||||
|
||||
state.port = startPort + state.offset;
|
||||
state.port = startPort + state.offset;
|
||||
|
||||
return state;
|
||||
return state;
|
||||
}
|
||||
|
||||
module.exports = randomClipperPort;
|
@@ -49,7 +49,6 @@ class Application extends BaseApplication {
|
||||
constructor() {
|
||||
super();
|
||||
this.lastMenuScreen_ = null;
|
||||
this.powerSaveBlockerId_ = null;
|
||||
}
|
||||
|
||||
hasGui() {
|
||||
@@ -221,17 +220,6 @@ class Application extends BaseApplication {
|
||||
Setting.setValue('sidebarVisibility', newState.sidebarVisibility);
|
||||
}
|
||||
|
||||
if (action.type === 'SYNC_STARTED') {
|
||||
if (!this.powerSaveBlockerId_) this.powerSaveBlockerId_ = bridge().powerSaveBlockerStart('prevent-app-suspension');
|
||||
}
|
||||
|
||||
if (action.type === 'SYNC_COMPLETED') {
|
||||
if (this.powerSaveBlockerId_) {
|
||||
bridge().powerSaveBlockerStop(this.powerSaveBlockerId_);
|
||||
this.powerSaveBlockerId_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -420,6 +408,10 @@ class Application extends BaseApplication {
|
||||
label: _('Paste'),
|
||||
role: 'paste',
|
||||
accelerator: 'CommandOrControl+V',
|
||||
}, {
|
||||
label: _('Select all'),
|
||||
role: 'selectall',
|
||||
accelerator: 'CommandOrControl+A',
|
||||
}, {
|
||||
type: 'separator',
|
||||
screens: ['Main'],
|
||||
@@ -737,8 +729,6 @@ class Application extends BaseApplication {
|
||||
ids: Setting.value('collapsedFolderIds'),
|
||||
});
|
||||
|
||||
if (shim.isLinux()) bridge().setAllowPowerSaveBlockerToggle(true);
|
||||
|
||||
// Note: Auto-update currently doesn't work in Linux: it downloads the update
|
||||
// but then doesn't install it on exit.
|
||||
if (shim.isWindows() || shim.isMac()) {
|
||||
|
@@ -1,7 +1,6 @@
|
||||
const { _, setLocale } = require('lib/locale.js');
|
||||
const { dirname } = require('lib/path-utils.js');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const { powerSaveBlocker } = require('electron');
|
||||
|
||||
class Bridge {
|
||||
|
||||
@@ -9,7 +8,6 @@ class Bridge {
|
||||
this.electronWrapper_ = electronWrapper;
|
||||
this.autoUpdateLogger_ = null;
|
||||
this.lastSelectedPath_ = null;
|
||||
this.allowPowerSaveBlockerToggle_ = false;
|
||||
}
|
||||
|
||||
electronApp() {
|
||||
@@ -24,10 +22,6 @@ class Bridge {
|
||||
return this.electronWrapper_.window();
|
||||
}
|
||||
|
||||
setAllowPowerSaveBlockerToggle(v) {
|
||||
this.allowPowerSaveBlockerToggle_ = v;
|
||||
}
|
||||
|
||||
windowContentSize() {
|
||||
if (!this.window()) return { width: 0, height: 0 };
|
||||
const s = this.window().getContentSize();
|
||||
@@ -126,19 +120,7 @@ class Bridge {
|
||||
const { checkForUpdates } = require('./checkForUpdates.js');
|
||||
checkForUpdates(inBackground, window, logFilePath);
|
||||
}
|
||||
|
||||
powerSaveBlockerStart(type) {
|
||||
if (!this.allowPowerSaveBlockerToggle_) return null;
|
||||
console.info('Enable powerSaveBlockerStart: ' + type);
|
||||
return powerSaveBlocker.start(type);
|
||||
}
|
||||
|
||||
powerSaveBlockerStop(id) {
|
||||
if (!this.allowPowerSaveBlockerToggle_) return null;
|
||||
console.info('Disable powerSaveBlocker: ' + id);
|
||||
return powerSaveBlocker.stop(id);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
let bridge_ = null;
|
||||
|
@@ -97,13 +97,33 @@ class NoteListComponent extends React.Component {
|
||||
}
|
||||
}}));
|
||||
|
||||
menu.append(new MenuItem({label: _('Switch between note and to-do type'), click: async () => {
|
||||
for (let i = 0; i < noteIds.length; i++) {
|
||||
const note = await Note.load(noteIds[i]);
|
||||
await Note.save(Note.toggleIsTodo(note), { userSideValidation: true });
|
||||
eventManager.emit('noteTypeToggle', { noteId: note.id });
|
||||
if (noteIds.length <= 1) {
|
||||
menu.append(new MenuItem({label: _('Switch between note and to-do type'), click: async () => {
|
||||
for (let i = 0; i < noteIds.length; i++) {
|
||||
const note = await Note.load(noteIds[i]);
|
||||
await Note.save(Note.toggleIsTodo(note), { userSideValidation: true });
|
||||
eventManager.emit('noteTypeToggle', { noteId: note.id });
|
||||
}
|
||||
}}));
|
||||
} else {
|
||||
const switchNoteType = async (noteIds, type) => {
|
||||
for (let i = 0; i < noteIds.length; i++) {
|
||||
const note = await Note.load(noteIds[i]);
|
||||
const newNote = Note.changeNoteType(note, type);
|
||||
if (newNote === note) continue;
|
||||
await Note.save(newNote, { userSideValidation: true });
|
||||
eventManager.emit('noteTypeToggle', { noteId: note.id });
|
||||
}
|
||||
}
|
||||
}}));
|
||||
|
||||
menu.append(new MenuItem({label: _('Switch to note type'), click: async () => {
|
||||
await switchNoteType(noteIds, 'note');
|
||||
}}));
|
||||
|
||||
menu.append(new MenuItem({label: _('Switch to to-do type'), click: async () => {
|
||||
await switchNoteType(noteIds, 'todo');
|
||||
}}));
|
||||
}
|
||||
|
||||
menu.append(new MenuItem({label: _('Copy Markdown link'), click: async () => {
|
||||
const { clipboard } = require('electron');
|
||||
|
@@ -6,6 +6,7 @@ const Search = require('lib/models/Search.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
const { IconButton } = require('./IconButton.min.js');
|
||||
const { urlDecode } = require('lib/string-utils');
|
||||
const Toolbar = require('./Toolbar.min.js');
|
||||
const { connect } = require('react-redux');
|
||||
const { _ } = require('lib/locale.js');
|
||||
@@ -27,6 +28,7 @@ const urlUtils = require('lib/urlUtils');
|
||||
const dialogs = require('./dialogs');
|
||||
const markdownUtils = require('lib/markdownUtils');
|
||||
const ExternalEditWatcher = require('lib/services/ExternalEditWatcher');
|
||||
const ResourceFetcher = require('lib/services/ResourceFetcher');
|
||||
const { toSystemSlashes, safeFilename } = require('lib/path-utils');
|
||||
const { clipboard } = require('electron');
|
||||
|
||||
@@ -170,6 +172,10 @@ class NoteTextComponent extends React.Component {
|
||||
}
|
||||
|
||||
const updateSelectionRange = () => {
|
||||
if (!this.rawEditor()) {
|
||||
this.selectionRange_ = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const ranges = this.rawEditor().getSelection().getAllRanges();
|
||||
if (!ranges || !ranges.length || !this.state.note) {
|
||||
@@ -193,6 +199,16 @@ class NoteTextComponent extends React.Component {
|
||||
this.reloadNote(this.props);
|
||||
}
|
||||
}
|
||||
|
||||
this.resourceFetcher_downloadComplete = async (resource) => {
|
||||
if (!this.state.note || !this.state.note.body) return;
|
||||
const resourceIds = await Note.linkedResourceIds(this.state.note.body);
|
||||
if (resourceIds.indexOf(resource.id) >= 0) {
|
||||
this.mdToHtml().clearCache();
|
||||
this.lastSetHtml_ = '';
|
||||
this.updateHtml(this.state.note.body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Note:
|
||||
@@ -282,6 +298,8 @@ class NoteTextComponent extends React.Component {
|
||||
eventManager.on('alarmChange', this.onAlarmChange_);
|
||||
eventManager.on('noteTypeToggle', this.onNoteTypeToggle_);
|
||||
eventManager.on('todoToggle', this.onTodoToggle_);
|
||||
|
||||
ResourceFetcher.instance().on('downloadComplete', this.resourceFetcher_downloadComplete);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@@ -294,6 +312,8 @@ class NoteTextComponent extends React.Component {
|
||||
eventManager.removeListener('noteTypeToggle', this.onNoteTypeToggle_);
|
||||
eventManager.removeListener('todoToggle', this.onTodoToggle_);
|
||||
|
||||
ResourceFetcher.instance().off('downloadComplete', this.resourceFetcher_downloadComplete);
|
||||
|
||||
this.destroyExternalEditWatcher();
|
||||
}
|
||||
|
||||
@@ -547,6 +567,10 @@ class NoteTextComponent extends React.Component {
|
||||
if (!item) throw new Error('No item with ID ' + itemId);
|
||||
|
||||
if (item.type_ === BaseModel.TYPE_RESOURCE) {
|
||||
if (item.fetch_status !== Resource.FETCH_STATUS_DONE || !!item.encryption_blob_encrypted) {
|
||||
bridge().showErrorMessageBox(_('This attachment is not downloaded or not decrypted yet.'));
|
||||
return;
|
||||
}
|
||||
const filePath = Resource.fullPath(item);
|
||||
bridge().openItem(filePath);
|
||||
} else if (item.type_ === BaseModel.TYPE_NOTE) {
|
||||
@@ -565,7 +589,14 @@ class NoteTextComponent extends React.Component {
|
||||
throw new Error('Unsupported item type: ' + item.type_);
|
||||
}
|
||||
} else if (urlUtils.urlProtocol(msg)) {
|
||||
require('electron').shell.openExternal(msg);
|
||||
if (msg.indexOf('file://') === 0) {
|
||||
// When using the file:// protocol, openExternal doesn't work (does nothing) with URL-encoded paths
|
||||
require('electron').shell.openExternal(urlDecode(msg));
|
||||
} else {
|
||||
require('electron').shell.openExternal(msg);
|
||||
}
|
||||
} else if (msg.indexOf('#') === 0) {
|
||||
// This is an internal anchor, which is handled by the WebView so skip this case
|
||||
} else {
|
||||
bridge().showErrorMessageBox(_('Unsupported link or message: %s', msg));
|
||||
}
|
||||
@@ -1366,7 +1397,11 @@ class NoteTextComponent extends React.Component {
|
||||
}
|
||||
|
||||
if (visiblePanes.indexOf('editor') < 0) {
|
||||
editorStyle.display = 'none';
|
||||
// Note: Ideally we'd set the display to "none" to take the editor out
|
||||
// of the DOM but if we do that, certain things won't work, in particular
|
||||
// things related to scroll, which are based on the editor. See
|
||||
// editorScrollTop_, restoreScrollTop_, etc.
|
||||
editorStyle.width = 0;
|
||||
viewerStyle.width = innerWidth;
|
||||
}
|
||||
|
||||
|
@@ -12,6 +12,7 @@ const { bridge } = require("electron").remote.require("./bridge");
|
||||
const Menu = bridge().Menu;
|
||||
const MenuItem = bridge().MenuItem;
|
||||
const InteropServiceHelper = require("../InteropServiceHelper.js");
|
||||
const { shim } = require('lib/shim');
|
||||
|
||||
class SideBarComponent extends React.Component {
|
||||
|
||||
@@ -178,6 +179,35 @@ class SideBarComponent extends React.Component {
|
||||
return style;
|
||||
}
|
||||
|
||||
clearForceUpdateDuringSync() {
|
||||
if (this.forceUpdateDuringSyncIID_) {
|
||||
clearInterval(this.forceUpdateDuringSyncIID_);
|
||||
this.forceUpdateDuringSyncIID_ = null;
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
if (shim.isLinux()) {
|
||||
// For some reason, the UI seems to sleep in some Linux distro during
|
||||
// sync. Cannot find the reason for it and cannot replicate, so here
|
||||
// as a test force the update at regular intervals.
|
||||
// https://github.com/laurent22/joplin/issues/312#issuecomment-429472193
|
||||
if (!prevProps.syncStarted && this.props.syncStarted) {
|
||||
this.clearForceUpdateDuringSync();
|
||||
|
||||
this.forceUpdateDuringSyncIID_ = setInterval(() => {
|
||||
this.forceUpdate();
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
if (prevProps.syncStarted && !this.props.syncStarted) this.clearForceUpdateDuringSync();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.clearForceUpdateDuringSync();
|
||||
}
|
||||
|
||||
itemContextMenu(event) {
|
||||
const itemId = event.target.getAttribute("data-id");
|
||||
if (itemId === Folder.conflictFolderId()) return;
|
||||
@@ -470,7 +500,13 @@ class SideBarComponent extends React.Component {
|
||||
);
|
||||
}
|
||||
|
||||
let decryptionReportText = '';
|
||||
if (this.props.decryptionWorker && this.props.decryptionWorker.state !== 'idle' && this.props.decryptionWorker.itemCount) {
|
||||
decryptionReportText = _('Decrypting items: %d/%d', this.props.decryptionWorker.itemIndex + 1, this.props.decryptionWorker.itemCount);
|
||||
}
|
||||
|
||||
let lines = Synchronizer.reportToLines(this.props.syncReport);
|
||||
if (decryptionReportText) lines.push(decryptionReportText);
|
||||
const syncReportText = [];
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
syncReportText.push(
|
||||
@@ -510,6 +546,7 @@ const mapStateToProps = state => {
|
||||
locale: state.settings.locale,
|
||||
theme: state.settings.theme,
|
||||
collapsedFolderIds: state.collapsedFolderIds,
|
||||
decryptionWorker: state.decryptionWorker,
|
||||
};
|
||||
};
|
||||
|
||||
|
@@ -276,6 +276,27 @@
|
||||
}
|
||||
});
|
||||
|
||||
document.addEventListener('click', function(event) {
|
||||
const t = event.target;
|
||||
|
||||
// Prevent URLs added via <a> tags from being opened within the application itself
|
||||
if (t && t.nodeName === 'A' && !t.hasAttribute('data-from-md')) {
|
||||
event.preventDefault();
|
||||
ipcProxySendToHost(t.getAttribute('href'));
|
||||
return;
|
||||
}
|
||||
|
||||
// IF this is an internal link, jump to the anchor directly
|
||||
if (t && t.nodeName === 'A' && t.hasAttribute('data-from-md')) {
|
||||
const href = t.getAttribute('href');
|
||||
if (href.indexOf('#') === 0) {
|
||||
event.preventDefault();
|
||||
location.hash = href;
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Disable drag and drop otherwise it's possible to drop a URL
|
||||
// on it and it will open in the view as a website.
|
||||
document.addEventListener('drop', function(e) {
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
7
ElectronClient/app/package-lock.json
generated
7
ElectronClient/app/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Joplin",
|
||||
"version": "1.0.111",
|
||||
"version": "1.0.114",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -6981,6 +6981,11 @@
|
||||
"random-bytes": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"unidecode": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/unidecode/-/unidecode-0.1.8.tgz",
|
||||
"integrity": "sha1-77swFTi8RSRqmsjFWdcvAVMFBT4="
|
||||
},
|
||||
"union-value": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz",
|
||||
|
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Joplin",
|
||||
"version": "1.0.111",
|
||||
"version": "1.0.114",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
@@ -127,6 +127,7 @@
|
||||
"syswide-cas": "^5.1.0",
|
||||
"tar": "^4.4.4",
|
||||
"tcp-port-used": "^0.1.2",
|
||||
"unidecode": "^0.1.8",
|
||||
"url-parse": "^1.4.1",
|
||||
"uuid": "^3.2.1",
|
||||
"valid-url": "^1.0.9",
|
||||
|
0
ElectronClient/run-prod.sh
Normal file → Executable file
0
ElectronClient/run-prod.sh
Normal file → Executable file
70
Joplin_install_and_update.sh
Executable file
70
Joplin_install_and_update.sh
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
# Title
|
||||
echo " _ _ _ _ _ _ "
|
||||
echo " | | (_) (_) | | | | | "
|
||||
echo " | | ___ _ __ _ _ __ _ _ __ ___| |_ __ _| | | ___ _ __ "
|
||||
echo " _ | |/ _ \\\| '_ \| | '_ \\ | | '_ \\\/ __| __/ _\` | | |/ _ \ '__|"
|
||||
echo " | |__| | (_) | |_) | | | | | | | | | \__ \ || (_| | | | __/ | "
|
||||
echo " \____/ \___/| .__/|_|_| |_| |_|_| |_|___/\__\__,_|_|_|\___|_| "
|
||||
echo " | | "
|
||||
echo " |_| "
|
||||
echo ""
|
||||
|
||||
#-----------------------------------------------------
|
||||
# Download Joplin
|
||||
#-----------------------------------------------------
|
||||
|
||||
# Get the latest version to download
|
||||
version=$(curl --silent "https://api.github.com/repos/laurent22/joplin/releases/latest" | grep -Po '"tag_name": "v\K.*?(?=")')
|
||||
|
||||
# Check if it's in the latest version
|
||||
touch VERSION
|
||||
if [[ $(< ~/.joplin/VERSION) != "$version" ]]; then
|
||||
|
||||
# Delete previous version
|
||||
rm -f ~/.joplin/*.AppImage ~/.local/share/applications/joplin.desktop ~/.joplin/VERSION
|
||||
|
||||
# Creates the folder where the binary will be stored
|
||||
mkdir -p ~/.joplin/
|
||||
|
||||
# Download the latest version
|
||||
wget -O ~/.joplin/Joplin.AppImage https://github.com/laurent22/joplin/releases/download/v$version/Joplin-$version-x86_64.AppImage
|
||||
|
||||
# Gives execution privileges
|
||||
chmod +x ~/.joplin/Joplin.AppImage
|
||||
|
||||
#-----------------------------------------------------
|
||||
# Icon
|
||||
#-----------------------------------------------------
|
||||
|
||||
# Download icon
|
||||
wget -O ~/.joplin/Icon512.png https://joplin.cozic.net/images/Icon512.png
|
||||
|
||||
# Detect desktop environment
|
||||
if [ "$XDG_CURRENT_DESKTOP" = "" ]
|
||||
then
|
||||
desktop=$(echo "$XDG_DATA_DIRS" | sed 's/.*\(xfce\|kde\|gnome\).*/\1/')
|
||||
else
|
||||
desktop=$XDG_CURRENT_DESKTOP
|
||||
fi
|
||||
desktop=${desktop,,} # convert to lower case
|
||||
|
||||
# Create icon for Gnome
|
||||
if [[ $desktop =~ .*gnome.* ]]
|
||||
then
|
||||
echo -e "[Desktop Entry]\nEncoding=UTF-8\nName=Joplin\nExec=/home/$USER/.joplin/Joplin-$version-x86_64.AppImage\nIcon=/home/$USER/.joplin/Icon512.png\nType=Application\nCategories=Application;" >> ~/.local/share/applications/joplin.desktop
|
||||
fi
|
||||
|
||||
#-----------------------------------------------------
|
||||
# Finish
|
||||
#-----------------------------------------------------
|
||||
|
||||
# Informs the user that it has been installed and cleans variables
|
||||
echo 'Joplin installed in the version' $version
|
||||
# Add version
|
||||
echo $version > ~/.joplin/VERSION
|
||||
else
|
||||
echo 'You are now in the latest version.'
|
||||
fi
|
||||
unset version
|
73
README.md
73
README.md
@@ -2,6 +2,20 @@
|
||||
|
||||
[](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=E8JMYD2LQ8MMA&lc=GB&item_name=Joplin+Development¤cy_code=EUR&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted) [](https://www.patreon.com/joplin) [](https://travis-ci.org/laurent22/joplin) [](https://ci.appveyor.com/project/laurent22/joplin)
|
||||
|
||||
* * *
|
||||
**Joplin and Hacktobertfest 2018 :jack_o_lantern:**
|
||||
|
||||
The [Hacktobertfest event](https://hacktoberfest.digitalocean.com/) has started - it allows you to contribute to Joplin and, at the end of the month, after having done 5 PR, you'll earn a limited edition T-shirt.
|
||||
|
||||
To participate, go on [https://hacktoberfest.digitalocean.com/ ](https://hacktoberfest.digitalocean.com/) log in (with you github account) and you are ready to get in.
|
||||
|
||||
Next, go dive into the Joplin issues list labelled ["Hacktoberfest"](https://github.com/laurent22/joplin/labels/Hacktoberfest%20%3Ajack_o_lantern%3A)
|
||||
|
||||
We hope you will enjoy that event by contributing to the project which is a nice moment of sharing good vibe :jack_o_lantern: :tada:
|
||||
|
||||
_PS: the 5 Pull Request don't have to be done __only__ on Joplin project, those can be done on any FOSS projects._
|
||||
* * *
|
||||
|
||||
Joplin is a free, open source note taking and to-do application, which can handle a large number of notes organised into notebooks. The notes are searchable, can be copied, tagged and modified either from the applications directly or from your own text editor. The notes are in [Markdown format](#markdown).
|
||||
|
||||
Notes exported from Evernote via .enex files [can be imported](#importing) into Joplin, including the formatted content (which is converted to Markdown), resources (images, attachments, etc.) and complete metadata (geolocation, updated time, created time, etc.). Plain Markdown files can also be imported.
|
||||
@@ -20,23 +34,23 @@ Three types of applications are available: for the **desktop** (Windows, macOS a
|
||||
|
||||
Operating System | Download | Alternative
|
||||
-----------------|--------|-------------------
|
||||
Windows (32 and 64-bit) | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.111/Joplin-Setup-1.0.111.exe'><img alt='Get it on Windows' height="40px" src='https://joplin.cozic.net/images/BadgeWindows.png'/></a> | or Get the <a href='https://github.com/laurent22/joplin/releases/download/v1.0.111/JoplinPortable.exe'>Portable version</a><br>(to run from a USB key, etc.)
|
||||
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.111/Joplin-1.0.111.dmg'><img alt='Get it on macOS' height="40px" src='https://joplin.cozic.net/images/BadgeMacOS.png'/></a> |
|
||||
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.111/Joplin-1.0.111-x86_64.AppImage'><img alt='Get it on Linux' height="40px" src='https://joplin.cozic.net/images/BadgeLinux.png'/></a> | An Arch Linux package<br>[is also available](#terminal-application).
|
||||
Windows (32 and 64-bit) | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.114/Joplin-Setup-1.0.114.exe'><img alt='Get it on Windows' height="40px" src='https://joplin.cozic.net/images/BadgeWindows.png'/></a> | or Get the <a href='https://github.com/laurent22/joplin/releases/download/v1.0.114/JoplinPortable.exe'>Portable version</a><br>(to run from a USB key, etc.)
|
||||
macOS | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.114/Joplin-1.0.114.dmg'><img alt='Get it on macOS' height="40px" src='https://joplin.cozic.net/images/BadgeMacOS.png'/></a> |
|
||||
Linux | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.114/Joplin-1.0.114-x86_64.AppImage'><img alt='Get it on Linux' height="40px" src='https://joplin.cozic.net/images/BadgeLinux.png'/></a> | An Arch Linux package<br>[is also available](#terminal-application).
|
||||
|
||||
The [portable application](https://en.wikipedia.org/wiki/Portable_application) allows installing the software on a portable device such as a USB key. Simply copy the file JoplinPortable.exe in any directory on that USB key ; the application will then create a directory called "JoplinProfile" next to the executable file.
|
||||
|
||||
### Install and Update Ubuntu/Debian (Gnome Shell)
|
||||
On Linux, if it works with your distribution (it has been tested on Ubuntu, Fedora, Gnome and Mint), the recommended way is to use this script as it will handle the desktop icon too:
|
||||
|
||||
``` sh
|
||||
wget -O - https://raw.githubusercontent.com/laurent22/joplin/master/install_ubuntu.sh | bash
|
||||
wget -O - https://raw.githubusercontent.com/laurent22/joplin/master/Joplin_install_and_update.sh | bash
|
||||
```
|
||||
|
||||
## Mobile applications
|
||||
|
||||
Operating System | Download | Alt. Download
|
||||
-----------------|----------|----------------
|
||||
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://joplin.cozic.net/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.138/joplin-v1.0.138.apk)
|
||||
Android | <a href='https://play.google.com/store/apps/details?id=net.cozic.joplin&utm_source=GitHub&utm_campaign=README&pcampaignid=MKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1'><img alt='Get it on Google Play' height="40px" src='https://joplin.cozic.net/images/BadgeAndroid.png'/></a> | or [Download APK File](https://github.com/laurent22/joplin-android/releases/download/android-v1.0.175/joplin-v1.0.175.apk)
|
||||
iOS | <a href='https://itunes.apple.com/us/app/joplin/id1315599797'><img alt='Get it on the App Store' height="40px" src='https://joplin.cozic.net/images/BadgeIOS.png'/></a> | -
|
||||
|
||||
## Terminal application
|
||||
@@ -159,7 +173,7 @@ If synchronisation does not work, please consult the logs in the app profile dir
|
||||
|
||||
## Dropbox synchronisation
|
||||
|
||||
When syncing with Dropbox, Joplin creates a sub-directory in Dropbox, in /Apps/Joplin and read/write the notes and notebooks from it. The application does not have access to anything outside this directory.
|
||||
When syncing with Dropbox, Joplin creates a sub-directory in Dropbox, in `/Apps/Joplin` and read/write the notes and notebooks from it. The application does not have access to anything outside this directory.
|
||||
|
||||
On the **desktop application** or **mobile application**, select "Dropbox" as the synchronisation target in the config screen (it is selected by default). Then, to initiate the synchronisation process, click on the "Synchronise" button in the sidebar and follow the instructions.
|
||||
|
||||
@@ -178,6 +192,7 @@ WebDAV-compatible services that are known to work with Joplin:
|
||||
- [Fastmail](https://www.fastmail.com/)
|
||||
- [HiDrive](https://www.strato.fr/stockage-en-ligne/) from Strato. [Setup help](https://github.com/laurent22/joplin/issues/309)
|
||||
- [Nginx WebDAV Module](https://nginx.org/en/docs/http/ngx_http_dav_module.html)
|
||||
- [NextCloud](https://nextcloud.com/)
|
||||
- [OwnCloud](https://owncloud.org/)
|
||||
- [Seafile](https://www.seafile.com/)
|
||||
- [Stack](https://www.transip.nl/stack/)
|
||||
@@ -245,7 +260,7 @@ Since getting the ID of a note is not straightforward, each app provides a way t
|
||||
|
||||
## Math notation
|
||||
|
||||
Math expressions can be added using the [Katex notation](https://khan.github.io/KaTeX/). To add an inline equation, wrap the expression in `$EXPRESSION$`, eg. `$\sqrt{3x-1}+(1+x)^2$`. To create an expression block, wrap it as follow:
|
||||
Math expressions can be added using the [KaTeX notation](https://khan.github.io/KaTeX/). To add an inline equation, wrap the expression in `$EXPRESSION$`, eg. `$\sqrt{3x-1}+(1+x)^2$`. To create an expression block, wrap it as follow:
|
||||
|
||||
$$
|
||||
EXPRESSION
|
||||
@@ -314,29 +329,29 @@ Current translations:
|
||||
<!-- LOCALE-TABLE-AUTO-GENERATED -->
|
||||
| Language | Po File | Last translator | Percent done
|
||||
---|---|---|---|---
|
||||
 | Basque | [eu](https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po) | juan.abasolo@ehu.eus | 63%
|
||||
 | Catalan | [ca](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ca.po) | jmontane, 2018 | 90%
|
||||
 | Croatian | [hr_HR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/hr_HR.po) | Hrvoje Mandić (trbuhom@net.hr) | 51%
|
||||
 | Czech | [cs_CZ](https://github.com/laurent22/joplin/blob/master/CliClient/locales/cs_CZ.po) | Lukas Helebrandt (lukas@aiya.cz) | 80%
|
||||
 | Dansk | [da_DK](https://github.com/laurent22/joplin/blob/master/CliClient/locales/da_DK.po) | Morten Juhl-Johansen Zölde-Fejér (mjjzf@syntaktisk. | 82%
|
||||
 | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Michael Sonntag (ms@editorei.de) | 98%
|
||||
 | Basque | [eu](https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po) | juan.abasolo@ehu.eus | 62%
|
||||
 | Catalan | [ca](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ca.po) | jmontane, 2018 | 88%
|
||||
 | Croatian | [hr_HR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/hr_HR.po) | Hrvoje Mandić (trbuhom@net.hr) | 50%
|
||||
 | Czech | [cs_CZ](https://github.com/laurent22/joplin/blob/master/CliClient/locales/cs_CZ.po) | Lukas Helebrandt (lukas@aiya.cz) | 78%
|
||||
 | Dansk | [da_DK](https://github.com/laurent22/joplin/blob/master/CliClient/locales/da_DK.po) | Morten Juhl-Johansen Zölde-Fejér (mjjzf@syntaktisk. | 80%
|
||||
 | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Michael Sonntag (ms@editorei.de) | 100%
|
||||
 | English | [en_GB](https://github.com/laurent22/joplin/blob/master/CliClient/locales/en_GB.po) | | 100%
|
||||
 | Español | [es_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/es_ES.po) | Fernando Martín (f@mrtn.es) | 97%
|
||||
 | Español | [es_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/es_ES.po) | Fernando Martín (f@mrtn.es) | 95%
|
||||
 | Français | [fr_FR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/fr_FR.po) | Laurent Cozic | 100%
|
||||
 | Galician | [gl_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/gl_ES.po) | Marcos Lans (marcoslansgarza@gmail.com) | 80%
|
||||
 | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/master/CliClient/locales/it_IT.po) | | 95%
|
||||
 | Nederlands | [nl_NL](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_NL.po) | Heimen Stoffels (vistausss@outlook.com) | 97%
|
||||
 | Nederlands | [nl_BE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_BE.po) | | 64%
|
||||
 | Norwegian | [no](https://github.com/laurent22/joplin/blob/master/CliClient/locales/no.po) | | 86%
|
||||
 | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/pt_BR.po) | Renato Nunes Bastos (rnbastos@gmail.com) | 97%
|
||||
 | Română | [ro](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ro.po) | | 63%
|
||||
 | Slovenian | [sl_SI](https://github.com/laurent22/joplin/blob/master/CliClient/locales/sl_SI.po) | | 79%
|
||||
 | Svenska | [sv](https://github.com/laurent22/joplin/blob/master/CliClient/locales/sv.po) | Jonatan Nyberg (jonatan@autistici.org) | 96%
|
||||
 | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po) | Artyom Karlov (artyom.karlov@gmail.com) | 79%
|
||||
 | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po) | | 97%
|
||||
 | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_TW.po) | penguinsam (samliu@gmail.com) | 97%
|
||||
 | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ja_JP.po) | AWASHIRO Ikuya (ikunya@gmail.com) | 98%
|
||||
 | 한국말 | [ko](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ko.po) | | 97%
|
||||
 | Galician | [gl_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/gl_ES.po) | Marcos Lans (marcoslansgarza@gmail.com) | 79%
|
||||
 | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/master/CliClient/locales/it_IT.po) | | 93%
|
||||
 | Nederlands | [nl_NL](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_NL.po) | Heimen Stoffels (vistausss@outlook.com) | 95%
|
||||
 | Nederlands | [nl_BE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_BE.po) | | 62%
|
||||
 | Norwegian | [no](https://github.com/laurent22/joplin/blob/master/CliClient/locales/no.po) | | 84%
|
||||
 | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/pt_BR.po) | Renato Nunes Bastos (rnbastos@gmail.com) | 95%
|
||||
 | Română | [ro](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ro.po) | | 62%
|
||||
 | Slovenian | [sl_SI](https://github.com/laurent22/joplin/blob/master/CliClient/locales/sl_SI.po) | | 78%
|
||||
 | Svenska | [sv](https://github.com/laurent22/joplin/blob/master/CliClient/locales/sv.po) | Jonatan Nyberg (jonatan@autistici.org) | 94%
|
||||
 | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po) | Artyom Karlov (artyom.karlov@gmail.com) | 78%
|
||||
 | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po) | | 94%
|
||||
 | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_TW.po) | penguinsam (samliu@gmail.com) | 95%
|
||||
 | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ja_JP.po) | AWASHIRO Ikuya (ikunya@gmail.com) | 95%
|
||||
 | 한국말 | [ko](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ko.po) | | 95%
|
||||
<!-- LOCALE-TABLE-AUTO-GENERATED -->
|
||||
|
||||
# Known bugs
|
||||
|
@@ -90,8 +90,8 @@ android {
|
||||
applicationId "net.cozic.joplin"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 2097316
|
||||
versionName "1.0.138"
|
||||
versionCode 2097411
|
||||
versionName "1.0.175"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
}
|
||||
@@ -137,6 +137,7 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':react-native-camera')
|
||||
compile project(':react-native-file-viewer')
|
||||
compile project(':react-native-securerandom')
|
||||
compile project(':react-native-push-notification')
|
||||
@@ -148,11 +149,29 @@ dependencies {
|
||||
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
|
||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
||||
compile project(':react-native-sqlite-storage')
|
||||
compile project(':react-native-fetch-blob')
|
||||
compile project(':rn-fetch-blob')
|
||||
compile project(':react-native-document-picker')
|
||||
compile project(':react-native-image-resizer')
|
||||
compile project(':react-native-share-extension')
|
||||
compile "com.facebook.react:react-native:+"
|
||||
|
||||
// To fix the error below, which happened after adding react-native-camera.
|
||||
// Doesn't make any sense since rn-camera neither defines v26 nor 27 but
|
||||
// v25.0.2 in build.gradle, but anyway now it works ¯\_(ツ)_/¯
|
||||
// --------------------------------------------------------------------------------------
|
||||
// Fatal error
|
||||
// { Error: Command failed: ./gradlew assembleRelease -PbuildDir=build --console plain
|
||||
//
|
||||
// FAILURE: Build failed with an exception.
|
||||
//
|
||||
// * What went wrong:
|
||||
// Execution failed for task ':app:preReleaseBuild'.
|
||||
// > Android dependency 'com.android.support:support-v4' has different version for the compile (26.1.0) and runtime (27.1.1) classpath. You should manually set the same version via DependencyResolution
|
||||
// --------------------------------------------------------------------------------------
|
||||
// https://github.com/react-native-community/react-native-camera/issues/1532#issuecomment-386434771
|
||||
compile ("com.android.support:support-v4:26.0.1") {
|
||||
force = true //<-- force dependency resolution to 26.0.1 in my case
|
||||
}
|
||||
}
|
||||
|
||||
// Run this once to be able to run the application with BUCK
|
||||
|
@@ -3,6 +3,7 @@ package net.cozic.joplin;
|
||||
import android.app.Application;
|
||||
|
||||
import com.facebook.react.ReactApplication;
|
||||
import org.reactnative.camera.RNCameraPackage;
|
||||
import com.vinzscam.reactnativefileviewer.RNFileViewerPackage;
|
||||
import net.rhogan.rnsecurerandom.RNSecureRandomPackage;
|
||||
import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage;
|
||||
@@ -37,6 +38,7 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
return Arrays.<ReactPackage>asList(
|
||||
new ImageResizerPackage(),
|
||||
new MainReactPackage(),
|
||||
new RNCameraPackage(),
|
||||
new RNFileViewerPackage(),
|
||||
new RNSecureRandomPackage(),
|
||||
new ReactNativePushNotificationPackage(),
|
||||
|
@@ -1,4 +1,6 @@
|
||||
rootProject.name = 'Joplin'
|
||||
include ':react-native-camera'
|
||||
project(':react-native-camera').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-camera/android')
|
||||
include ':react-native-file-viewer'
|
||||
project(':react-native-file-viewer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-file-viewer/android')
|
||||
include ':react-native-securerandom'
|
||||
@@ -21,8 +23,8 @@ include ':app'
|
||||
include ':react-native-sqlite-storage'
|
||||
project(':react-native-sqlite-storage').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sqlite-storage/src/android')
|
||||
|
||||
include ':react-native-fetch-blob'
|
||||
project(':react-native-fetch-blob').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fetch-blob/android')
|
||||
include ':rn-fetch-blob'
|
||||
project(':rn-fetch-blob').projectDir = new File(rootProject.projectDir, '../node_modules/rn-fetch-blob/android')
|
||||
|
||||
include ':react-native-document-picker'
|
||||
project(':react-native-document-picker').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-document-picker/android')
|
||||
|
0
ReactNativeClient/debug_log.sh
Normal file → Executable file
0
ReactNativeClient/debug_log.sh
Normal file → Executable file
@@ -5,6 +5,7 @@
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
00C302E51ABCBA2D00DB3ED1 /* libRCTActionSheet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302AC1ABCB8CE00DB3ED1 /* libRCTActionSheet.a */; };
|
||||
00C302E71ABCBA2D00DB3ED1 /* libRCTGeolocation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302BA1ABCB90400DB3ED1 /* libRCTGeolocation.a */; };
|
||||
@@ -12,6 +13,7 @@
|
||||
00C302E91ABCBA2D00DB3ED1 /* libRCTNetwork.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302DC1ABCB9D200DB3ED1 /* libRCTNetwork.a */; };
|
||||
00C302EA1ABCBA2D00DB3ED1 /* libRCTVibration.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 00C302E41ABCB9EE00DB3ED1 /* libRCTVibration.a */; };
|
||||
0DAD2E67F6A14BDC8250B927 /* libRNDocumentPicker.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 82214D3345D846709A314868 /* libRNDocumentPicker.a */; };
|
||||
12AE298E1C0E445682922DAB /* libRNCamera.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E132B594F4FB4C96A2E2B0FF /* libRNCamera.a */; };
|
||||
133E29F31AD74F7200F7D852 /* libRCTLinking.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 78C398B91ACF4ADC00677621 /* libRCTLinking.a */; };
|
||||
139105C61AF99C1200B5F7CC /* libRCTSettings.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139105C11AF99BAD00B5F7CC /* libRCTSettings.a */; };
|
||||
139FDEF61B0652A700C62182 /* libRCTWebSocket.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 139FDEF41B06529B00C62182 /* libRCTWebSocket.a */; };
|
||||
@@ -27,6 +29,7 @@
|
||||
5E9157361DD0AC6A00FF2AA8 /* libRCTAnimation.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E9157331DD0AC6500FF2AA8 /* libRCTAnimation.a */; };
|
||||
725A77EC604947A0AFF12C2B /* libRNFetchBlob.a in Frameworks */ = {isa = PBXBuildFile; fileRef = F5E37D05726A4A08B2EE323A /* libRNFetchBlob.a */; };
|
||||
73F8D08845494D1396B6CD0B /* Entypo.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 1F79F2CD7CED446B986A6252 /* Entypo.ttf */; };
|
||||
82C61D3DAE0A4666883001E9 /* libRNFileViewer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CCDC2774CD86466F897D88E2 /* libRNFileViewer.a */; };
|
||||
832341BD1AAA6AB300B99B32 /* libRCTText.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 832341B51AAA6A8300B99B32 /* libRCTText.a */; };
|
||||
929CA5ABC80E4D398AFC4E44 /* libSQLite.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 87BABCF4ED0A406B9546CCE9 /* libSQLite.a */; };
|
||||
A47605BC216B4DB3B3C2ED42 /* FontAwesome.ttf in Resources */ = {isa = PBXBuildFile; fileRef = E2638D52624B477FABB52B8F /* FontAwesome.ttf */; };
|
||||
@@ -43,7 +46,6 @@
|
||||
EC11356C90E9419799A2626F /* EvilIcons.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 51BCEC3BC28046C8BB19531F /* EvilIcons.ttf */; };
|
||||
F3D0BB525E6C490294D73075 /* libRNSecureRandom.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 22647ACF9A4C45918C44C599 /* libRNSecureRandom.a */; };
|
||||
FBF57CE2F0F448FA9A8985E2 /* libsqlite3.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 0EB8BCAEA9AA41CAAE460443 /* libsqlite3.0.tbd */; };
|
||||
82C61D3DAE0A4666883001E9 /* libRNFileViewer.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CCDC2774CD86466F897D88E2 /* libRNFileViewer.a */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -334,6 +336,20 @@
|
||||
remoteGlobalIDString = 9936F32F1F5F2E5B0010BF04;
|
||||
remoteInfo = "privatedata-tvOS";
|
||||
};
|
||||
4D8B719B2163E8C500136BBC /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 59F5448FAF7345F8B568BD00 /* RNFileViewer.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 134814201AA4EA6300B7C361;
|
||||
remoteInfo = RNFileViewer;
|
||||
};
|
||||
4D8C5643217161BF00E93280 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = FC908F114F494130A324B402 /* RNCamera.xcodeproj */;
|
||||
proxyType = 2;
|
||||
remoteGlobalIDString = 4107012F1ACB723B00C6AA39;
|
||||
remoteInfo = RNCamera;
|
||||
};
|
||||
4DA7F80C1FC1DA9C00353191 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = A4716DB8654B431D894F89E1 /* RNImagePicker.xcodeproj */;
|
||||
@@ -415,6 +431,7 @@
|
||||
4DDA31011FC88EEA00B5A80D /* RCTPushNotification.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTPushNotification.xcodeproj; path = "../node_modules/react-native/Libraries/PushNotificationIOS/RCTPushNotification.xcodeproj"; sourceTree = "<group>"; };
|
||||
508DD20D1EA341CA8F730F22 /* libRCTImageResizer.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRCTImageResizer.a; sourceTree = "<group>"; };
|
||||
51BCEC3BC28046C8BB19531F /* EvilIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = EvilIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/EvilIcons.ttf"; sourceTree = "<group>"; };
|
||||
59F5448FAF7345F8B568BD00 /* RNFileViewer.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNFileViewer.xcodeproj; path = "../node_modules/react-native-file-viewer/ios/RNFileViewer.xcodeproj"; sourceTree = "<group>"; };
|
||||
5E91572D1DD0AC6500FF2AA8 /* RCTAnimation.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTAnimation.xcodeproj; path = "../node_modules/react-native/Libraries/NativeAnimation/RCTAnimation.xcodeproj"; sourceTree = "<group>"; };
|
||||
69B8EE98BFBC4AABA4885BB0 /* SimpleLineIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = SimpleLineIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/SimpleLineIcons.ttf"; sourceTree = "<group>"; };
|
||||
711CBD21F0894B83A2D8E234 /* RNVectorIcons.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNVectorIcons.xcodeproj; path = "../node_modules/react-native-vector-icons/RNVectorIcons.xcodeproj"; sourceTree = "<group>"; };
|
||||
@@ -426,18 +443,19 @@
|
||||
9D8705D0D07C4A098FD912EB /* RNFS.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNFS.xcodeproj; path = "../node_modules/react-native-fs/RNFS.xcodeproj"; sourceTree = "<group>"; };
|
||||
A4716DB8654B431D894F89E1 /* RNImagePicker.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNImagePicker.xcodeproj; path = "../node_modules/react-native-image-picker/ios/RNImagePicker.xcodeproj"; sourceTree = "<group>"; };
|
||||
ADBDB91F1DFEBF0600ED6528 /* RCTBlob.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = RCTBlob.xcodeproj; path = "../node_modules/react-native/Libraries/Blob/RCTBlob.xcodeproj"; sourceTree = "<group>"; };
|
||||
B8C44254005A4B80AD5CA558 /* RNFetchBlob.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNFetchBlob.xcodeproj; path = "../node_modules/react-native-fetch-blob/ios/RNFetchBlob.xcodeproj"; sourceTree = "<group>"; };
|
||||
B8C44254005A4B80AD5CA558 /* RNFetchBlob.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNFetchBlob.xcodeproj; path = "../node_modules/rn-fetch-blob/ios/RNFetchBlob.xcodeproj"; sourceTree = "<group>"; };
|
||||
C77F905EF22646F39B673572 /* MaterialCommunityIcons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = MaterialCommunityIcons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/MaterialCommunityIcons.ttf"; sourceTree = "<group>"; };
|
||||
CC3CF4044A914711B8D30D1A /* Zocial.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Zocial.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Zocial.ttf"; sourceTree = "<group>"; };
|
||||
CCDC2774CD86466F897D88E2 /* libRNFileViewer.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFileViewer.a; sourceTree = "<group>"; };
|
||||
CCDE9E9AF09B45F391B1C2AF /* SQLite.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = SQLite.xcodeproj; path = "../node_modules/react-native-sqlite-storage/src/ios/SQLite.xcodeproj"; sourceTree = "<group>"; };
|
||||
DF1C50EBC11E46A3AF87F80A /* RCTImageResizer.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RCTImageResizer.xcodeproj; path = "../node_modules/react-native-image-resizer/ios/RCTImageResizer.xcodeproj"; sourceTree = "<group>"; };
|
||||
E132B594F4FB4C96A2E2B0FF /* libRNCamera.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNCamera.a; sourceTree = "<group>"; };
|
||||
E2638D52624B477FABB52B8F /* FontAwesome.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = FontAwesome.ttf; path = "../node_modules/react-native-vector-icons/Fonts/FontAwesome.ttf"; sourceTree = "<group>"; };
|
||||
F098E1ACCB594C828C851A57 /* libRNFS.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFS.a; sourceTree = "<group>"; };
|
||||
F5E37D05726A4A08B2EE323A /* libRNFetchBlob.a */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = archive.ar; path = libRNFetchBlob.a; sourceTree = "<group>"; };
|
||||
FC908F114F494130A324B402 /* RNCamera.xcodeproj */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = "wrapper.pb-project"; name = RNCamera.xcodeproj; path = "../node_modules/react-native-camera/ios/RNCamera.xcodeproj"; sourceTree = "<group>"; };
|
||||
FD370E24D76E461D960DD85D /* Feather.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Feather.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Feather.ttf"; sourceTree = "<group>"; };
|
||||
FF411B45E68B4A8CBCC35777 /* Ionicons.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = Ionicons.ttf; path = "../node_modules/react-native-vector-icons/Fonts/Ionicons.ttf"; sourceTree = "<group>"; };
|
||||
59F5448FAF7345F8B568BD00 /* RNFileViewer.xcodeproj */ = {isa = PBXFileReference; name = "RNFileViewer.xcodeproj"; path = "../node_modules/react-native-file-viewer/ios/RNFileViewer.xcodeproj"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = wrapper.pb-project; explicitFileType = undefined; includeInIndex = 0; };
|
||||
CCDC2774CD86466F897D88E2 /* libRNFileViewer.a */ = {isa = PBXFileReference; name = "libRNFileViewer.a"; path = "libRNFileViewer.a"; sourceTree = "<group>"; fileEncoding = undefined; lastKnownFileType = archive.ar; explicitFileType = undefined; includeInIndex = 0; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
@@ -469,6 +487,7 @@
|
||||
AF99EEC6C55042F7BFC87583 /* libRNImagePicker.a in Frameworks */,
|
||||
F3D0BB525E6C490294D73075 /* libRNSecureRandom.a in Frameworks */,
|
||||
82C61D3DAE0A4666883001E9 /* libRNFileViewer.a in Frameworks */,
|
||||
12AE298E1C0E445682922DAB /* libRNCamera.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -602,6 +621,8 @@
|
||||
381C047F2739439CB3E6452A /* libRNVectorIcons.a */,
|
||||
44A39642217548C8ADA91CBA /* libRNImagePicker.a */,
|
||||
22647ACF9A4C45918C44C599 /* libRNSecureRandom.a */,
|
||||
CCDC2774CD86466F897D88E2 /* libRNFileViewer.a */,
|
||||
E132B594F4FB4C96A2E2B0FF /* libRNCamera.a */,
|
||||
);
|
||||
name = "Recovered References";
|
||||
sourceTree = "<group>";
|
||||
@@ -655,6 +676,22 @@
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4D8B71982163E8C500136BBC /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4D8B719C2163E8C500136BBC /* libRNFileViewer.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4D8C5640217161BF00E93280 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
4D8C5644217161BF00E93280 /* libRNCamera.a */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
4DA7F8091FC1DA9C00353191 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -733,6 +770,7 @@
|
||||
A4716DB8654B431D894F89E1 /* RNImagePicker.xcodeproj */,
|
||||
252BD7B86BF7435B960DA901 /* RNSecureRandom.xcodeproj */,
|
||||
59F5448FAF7345F8B568BD00 /* RNFileViewer.xcodeproj */,
|
||||
FC908F114F494130A324B402 /* RNCamera.xcodeproj */,
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
@@ -890,6 +928,10 @@
|
||||
ProductGroup = 146834001AC3E56700842450 /* Products */;
|
||||
ProjectRef = 146833FF1AC3E56700842450 /* React.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 4D8C5640217161BF00E93280 /* Products */;
|
||||
ProjectRef = FC908F114F494130A324B402 /* RNCamera.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 4D2A85AD1FBCE3AC0028537D /* Products */;
|
||||
ProjectRef = 02C42EA98156482DB00BF86D /* RNDocumentPicker.xcodeproj */;
|
||||
@@ -898,6 +940,10 @@
|
||||
ProductGroup = 4D2A85AF1FBCE3AC0028537D /* Products */;
|
||||
ProjectRef = B8C44254005A4B80AD5CA558 /* RNFetchBlob.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 4D8B71982163E8C500136BBC /* Products */;
|
||||
ProjectRef = 59F5448FAF7345F8B568BD00 /* RNFileViewer.xcodeproj */;
|
||||
},
|
||||
{
|
||||
ProductGroup = 4D2A85B11FBCE3AC0028537D /* Products */;
|
||||
ProjectRef = 9D8705D0D07C4A098FD912EB /* RNFS.xcodeproj */;
|
||||
@@ -1214,6 +1260,20 @@
|
||||
remoteRef = 4D7F8DA620A32BA0008B757D /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
4D8B719C2163E8C500136BBC /* libRNFileViewer.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRNFileViewer.a;
|
||||
remoteRef = 4D8B719B2163E8C500136BBC /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
4D8C5644217161BF00E93280 /* libRNCamera.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
path = libRNCamera.a;
|
||||
remoteRef = 4D8C5643217161BF00E93280 /* PBXContainerItemProxy */;
|
||||
sourceTree = BUILT_PRODUCTS_DIR;
|
||||
};
|
||||
4DA7F80D1FC1DA9C00353191 /* libRNImagePicker.a */ = {
|
||||
isa = PBXReferenceProxy;
|
||||
fileType = archive.ar;
|
||||
@@ -1343,14 +1403,15 @@
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
|
||||
"$(SRCROOT)/../node_modules/react-native-fetch-blob/ios/**",
|
||||
"$(SRCROOT)/../node_modules/rn-fetch-blob/ios/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-fs/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-image-resizer/ios/RCTImageResizer",
|
||||
"$(SRCROOT)/../node_modules/react-native-sqlite-storage/src/ios",
|
||||
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
|
||||
"$(SRCROOT)..\node_modules\neact-native-image-pickerios",
|
||||
"$(SRCROOT)..\node_modules\neact-native-securerandomios",
|
||||
"$(SRCROOT)\..\node_modules\react-native-file-viewer\ios",
|
||||
"$(SRCROOT)..\node_modules\neact-native-file-viewerios",
|
||||
"$(SRCROOT)/../node_modules/react-native-camera/ios/**",
|
||||
);
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
@@ -1358,6 +1419,7 @@
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/Joplin\"",
|
||||
"\"$(SRCROOT)/Joplin\"",
|
||||
"\"$(SRCROOT)/Joplin\"",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
@@ -1385,14 +1447,15 @@
|
||||
HEADER_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(SRCROOT)/../node_modules/react-native-document-picker/ios/RNDocumentPicker",
|
||||
"$(SRCROOT)/../node_modules/react-native-fetch-blob/ios/**",
|
||||
"$(SRCROOT)/../node_modules/rn-fetch-blob/ios/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-fs/**",
|
||||
"$(SRCROOT)/../node_modules/react-native-image-resizer/ios/RCTImageResizer",
|
||||
"$(SRCROOT)/../node_modules/react-native-sqlite-storage/src/ios",
|
||||
"$(SRCROOT)/../node_modules/react-native-vector-icons/RNVectorIconsManager",
|
||||
"$(SRCROOT)..\node_modules\neact-native-image-pickerios",
|
||||
"$(SRCROOT)..\node_modules\neact-native-securerandomios",
|
||||
"$(SRCROOT)\..\node_modules\react-native-file-viewer\ios",
|
||||
"$(SRCROOT)..\node_modules\neact-native-file-viewerios",
|
||||
"$(SRCROOT)/../node_modules/react-native-camera/ios/**",
|
||||
);
|
||||
INFOPLIST_FILE = Joplin/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
@@ -1400,6 +1463,7 @@
|
||||
"$(inherited)",
|
||||
"\"$(SRCROOT)/Joplin\"",
|
||||
"\"$(SRCROOT)/Joplin\"",
|
||||
"\"$(SRCROOT)/Joplin\"",
|
||||
);
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
|
@@ -17,11 +17,11 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>10.0.24</string>
|
||||
<string>10.0.26</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>24</string>
|
||||
<string>26</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
|
@@ -33,6 +33,7 @@ const SyncTargetNextcloud = require('lib/SyncTargetNextcloud.js');
|
||||
const SyncTargetWebDAV = require('lib/SyncTargetWebDAV.js');
|
||||
const SyncTargetDropbox = require('lib/SyncTargetDropbox.js');
|
||||
const EncryptionService = require('lib/services/EncryptionService');
|
||||
const ResourceFetcher = require('lib/services/ResourceFetcher');
|
||||
const DecryptionWorker = require('lib/services/DecryptionWorker');
|
||||
const BaseService = require('lib/services/BaseService');
|
||||
|
||||
@@ -356,6 +357,10 @@ class BaseApplication {
|
||||
DecryptionWorker.instance().scheduleStart();
|
||||
}
|
||||
|
||||
if (this.hasGui() && action.type === 'SYNC_CREATED_RESOURCE') {
|
||||
ResourceFetcher.instance().queueDownload(action.id);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -454,7 +459,7 @@ class BaseApplication {
|
||||
initArgs = Object.assign(initArgs, extraFlags);
|
||||
|
||||
this.logger_.addTarget('file', { path: profileDir + '/log.txt' });
|
||||
//this.logger_.addTarget('console');
|
||||
// if (Setting.value('env') === 'dev') this.logger_.addTarget('console');
|
||||
this.logger_.setLevel(initArgs.logLevel);
|
||||
|
||||
reg.setLogger(this.logger_);
|
||||
@@ -510,6 +515,10 @@ class BaseApplication {
|
||||
DecryptionWorker.instance().setEncryptionService(EncryptionService.instance());
|
||||
await EncryptionService.instance().loadMasterKeysFromSettings();
|
||||
|
||||
ResourceFetcher.instance().setFileApi(() => { return reg.syncTarget().fileApi() });
|
||||
ResourceFetcher.instance().setLogger(this.logger_);
|
||||
ResourceFetcher.instance().start();
|
||||
|
||||
let currentFolderId = Setting.value('activeFolderId');
|
||||
let currentFolder = null;
|
||||
if (currentFolderId) currentFolder = await Folder.load(currentFolderId);
|
||||
|
@@ -14,6 +14,10 @@ class BaseSyncTarget {
|
||||
return false;
|
||||
}
|
||||
|
||||
static resourceDirName() {
|
||||
return '.resource';
|
||||
}
|
||||
|
||||
option(name, defaultValue = null) {
|
||||
return this.options_ && (name in this.options_) ? this.options_[name] : defaultValue;
|
||||
}
|
||||
|
@@ -147,6 +147,7 @@ class ClipperServer {
|
||||
const response = await this.api_.route(request.method, url.pathname, url.query, body, files);
|
||||
writeResponse(200, response);
|
||||
} catch (error) {
|
||||
this.logger().error(error);
|
||||
writeResponse(error.httpCode ? error.httpCode : 500, error.message);
|
||||
}
|
||||
}
|
||||
@@ -173,7 +174,7 @@ class ClipperServer {
|
||||
}
|
||||
});
|
||||
} else {
|
||||
if (request.method === 'POST') {
|
||||
if (request.method === 'POST' || request.method === 'PUT') {
|
||||
let body = '';
|
||||
|
||||
request.on('data', (data) => {
|
||||
|
@@ -2,7 +2,6 @@ const MarkdownIt = require('markdown-it');
|
||||
const Entities = require('html-entities').AllHtmlEntities;
|
||||
const htmlentities = (new Entities()).encode;
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const ModelCache = require('lib/ModelCache');
|
||||
const ObjectUtils = require('lib/ObjectUtils');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const { _ } = require('lib/locale');
|
||||
@@ -17,7 +16,6 @@ class MdToHtml {
|
||||
this.loadedResources_ = {};
|
||||
this.cachedContent_ = null;
|
||||
this.cachedContentKey_ = null;
|
||||
this.modelCache_ = new ModelCache();
|
||||
|
||||
// Must include last "/"
|
||||
this.resourceBaseUrl_ = ('resourceBaseUrl' in options) ? options.resourceBaseUrl : null;
|
||||
@@ -30,12 +28,18 @@ class MdToHtml {
|
||||
const r = resources[n];
|
||||
k.push(r.id);
|
||||
}
|
||||
|
||||
k.push(md5(escape(body))); // https://github.com/pvorb/node-md5/issues/41
|
||||
k.push(md5(JSON.stringify(style)));
|
||||
k.push(md5(JSON.stringify(options)));
|
||||
return k.join('_');
|
||||
}
|
||||
|
||||
clearCache() {
|
||||
this.cachedContent_ = null;
|
||||
this.cachedContentKey_ = null;
|
||||
}
|
||||
|
||||
renderAttrs_(attrs) {
|
||||
if (!attrs) return '';
|
||||
|
||||
@@ -74,8 +78,6 @@ class MdToHtml {
|
||||
}
|
||||
|
||||
async loadResource(id, options) {
|
||||
// console.info('Loading resource: ' + id);
|
||||
|
||||
// Initially set to to an empty object to make
|
||||
// it clear that it is being loaded. Otherwise
|
||||
// it sometimes results in multiple calls to
|
||||
@@ -83,12 +85,18 @@ class MdToHtml {
|
||||
this.loadedResources_[id] = {};
|
||||
|
||||
const resource = await Resource.load(id);
|
||||
//const resource = await this.modelCache_.load(Resource, id);
|
||||
|
||||
if (!resource) {
|
||||
// Can happen for example if an image is attached to a note, but the resource hasn't
|
||||
// been downloaded from the sync target yet.
|
||||
console.warn('Cannot load resource: ' + id);
|
||||
console.info('Cannot load resource: ' + id);
|
||||
delete this.loadedResources_[id];
|
||||
return;
|
||||
}
|
||||
|
||||
if (resource.fetch_status !== Resource.FETCH_STATUS_DONE) {
|
||||
delete this.loadedResources_[id];
|
||||
console.info('Resource not yet fetched: ' + id);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -102,7 +110,7 @@ class MdToHtml {
|
||||
const href = this.getAttr_(attrs, 'src');
|
||||
|
||||
if (!Resource.isResourceUrl(href)) {
|
||||
return '<img title="' + htmlentities(title) + '" src="' + href + '"/>';
|
||||
return '<img data-from-md title="' + htmlentities(title) + '" src="' + href + '"/>';
|
||||
}
|
||||
|
||||
const resourceId = Resource.urlToId(href);
|
||||
@@ -118,7 +126,7 @@ class MdToHtml {
|
||||
if (Resource.isSupportedImageMimeType(mime)) {
|
||||
let src = './' + Resource.filename(resource);
|
||||
if (this.resourceBaseUrl_ !== null) src = this.resourceBaseUrl_ + src;
|
||||
let output = '<img data-resource-id="' + resource.id + '" title="' + htmlentities(title) + '" src="' + src + '"/>';
|
||||
let output = '<img data-from-md data-resource-id="' + resource.id + '" title="' + htmlentities(title) + '" src="' + src + '"/>';
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -167,7 +175,7 @@ class MdToHtml {
|
||||
}
|
||||
|
||||
const js = options.postMessageSyntax + "(" + JSON.stringify(href) + "); return false;";
|
||||
let output = "<a " + resourceIdAttr + " title='" + htmlentities(title) + "' href='" + hrefAttr + "' onclick='" + js + "'>" + icon;
|
||||
let output = "<a data-from-md " + resourceIdAttr + " title='" + htmlentities(title) + "' href='" + hrefAttr + "' onclick='" + js + "'>" + icon;
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -209,7 +217,6 @@ class MdToHtml {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
renderTokens_(markdownIt, tokens, options) {
|
||||
let output = [];
|
||||
let previousToken = null;
|
||||
@@ -487,7 +494,7 @@ class MdToHtml {
|
||||
while (renderedBody.indexOf('mJOPm') >= 0) {
|
||||
renderedBody = renderedBody.replace(/mJOPmCHECKBOXm([A-Z]+)m(\d+)m/, function(v, type, index) {
|
||||
const js = options.postMessageSyntax + "('checkboxclick:" + type + ':' + index + "'); this.classList.contains('tick') ? this.classList.remove('tick') : this.classList.add('tick'); return false;";
|
||||
return '<a href="#" onclick="' + js + '" class="checkbox ' + (type == 'NOTICK' ? '' : 'tick') + '"><span>' + '' + '</span></a>';
|
||||
return '<a data-from-md href="#" onclick="' + js + '" class="checkbox ' + (type == 'NOTICK' ? '' : 'tick') + '"><span>' + '' + '</span></a>';
|
||||
});
|
||||
if (loopCount++ >= 9999) break;
|
||||
}
|
||||
|
83
ReactNativeClient/lib/components/CameraView.js
Normal file
83
ReactNativeClient/lib/components/CameraView.js
Normal file
@@ -0,0 +1,83 @@
|
||||
const React = require('react'); const Component = React.Component;
|
||||
const { View, Button, StyleSheet, TouchableOpacity } = require('react-native');
|
||||
const { globalStyle, themeStyle } = require('lib/components/global-style.js');
|
||||
import { RNCamera } from 'react-native-camera';
|
||||
const Icon = require('react-native-vector-icons/Ionicons').default;
|
||||
const { _ } = require('lib/locale.js');
|
||||
|
||||
class CameraView extends Component {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
snapping: false,
|
||||
};
|
||||
|
||||
this.back_onPress = this.back_onPress.bind(this);
|
||||
this.photo_onPress = this.photo_onPress.bind(this);
|
||||
}
|
||||
|
||||
back_onPress() {
|
||||
if (this.props.onCancel) this.props.onCancel();
|
||||
}
|
||||
|
||||
async photo_onPress() {
|
||||
if (!this.camera || !this.props.onPhoto) return;
|
||||
|
||||
this.setState({ snapping: true });
|
||||
|
||||
const result = await this.camera.takePictureAsync({
|
||||
quality: 0.8,
|
||||
exif: true,
|
||||
fixOrientation: true
|
||||
});
|
||||
|
||||
if (this.props.onPhoto) this.props.onPhoto(result);
|
||||
|
||||
this.setState({ snapping: false });
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const photoIcon = this.state.snapping ? 'md-checkmark' : 'md-camera';
|
||||
|
||||
return (
|
||||
<View style={this.props.style}>
|
||||
<RNCamera
|
||||
style={{flex:1}}
|
||||
ref={ref => { this.camera = ref; }}
|
||||
type={RNCamera.Constants.Type.back}
|
||||
permissionDialogTitle={_('Permission to use camera')}
|
||||
permissionDialogMessage={_('Your permission to use your camera is required.')}
|
||||
>
|
||||
<View style={{flex:1, justifyContent:'space-between', flexDirection:'column'}}>
|
||||
<View style={{flex:1, justifyContent:'flex-start'}}>
|
||||
<TouchableOpacity onPress={this.back_onPress}>
|
||||
<View style={{ marginLeft:5, marginTop:5, borderRadius:90, width:50,height:50, display:'flex', backgroundColor:'#ffffff55', justifyContent:'center', alignItems:'center'}}>
|
||||
<Icon name={'md-arrow-back'} style={{
|
||||
fontSize: 40,
|
||||
color: 'black',
|
||||
}} />
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View style={{flex:1, justifyContent:'center', alignItems:'flex-end', flexDirection:'row'}}>
|
||||
<TouchableOpacity onPress={this.photo_onPress}>
|
||||
<View style={{marginBottom:20, borderRadius:90, width:90,height:90,backgroundColor:'#ffffffaa', display:'flex', justifyContent:'center', alignItems:'center'}}>
|
||||
<Icon name={photoIcon} style={{
|
||||
fontSize: 60,
|
||||
color: 'black',
|
||||
}} />
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</RNCamera>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = CameraView;
|
@@ -40,6 +40,13 @@ class NoteBodyViewer extends Component {
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
|
||||
const safeGetNoteProp = (props, propName) => {
|
||||
if (!props) return null;
|
||||
if (!props.note) return null;
|
||||
return props.note[propName];
|
||||
}
|
||||
|
||||
// To address https://github.com/laurent22/joplin/issues/433
|
||||
// If a checkbox in a note is ticked, the body changes, which normally would trigger a re-render
|
||||
// of this component, which has the unfortunate side effect of making the view scroll back to the top.
|
||||
@@ -47,9 +54,19 @@ class NoteBodyViewer extends Component {
|
||||
// So here, if the note has not changed, we prevent the component from updating.
|
||||
// This fixes the above issue. A drawback of this is if the note is updated via sync, this change
|
||||
// will not be displayed immediately.
|
||||
const currentNoteId = this.props && this.props.note ? this.props.note.id : null;
|
||||
const nextNoteId = nextProps && nextProps.note ? nextProps.note.id : null;
|
||||
return currentNoteId !== nextNoteId || nextState.webViewLoaded !== this.state.webViewLoaded;
|
||||
const currentNoteId = safeGetNoteProp(this.props, 'id');
|
||||
const nextNoteId = safeGetNoteProp(nextProps, 'id');
|
||||
|
||||
if (currentNoteId !== nextNoteId || nextState.webViewLoaded !== this.state.webViewLoaded) return true;
|
||||
|
||||
// If the length of the body has changed, then it's something other than a checkbox that has changed,
|
||||
// for example a resource that has been attached to the note while in View mode. In that case, update.
|
||||
return (safeGetNoteProp(this.props, 'body') + '').length !== (safeGetNoteProp(nextProps, 'body') + '').length;
|
||||
}
|
||||
|
||||
rebuildMd() {
|
||||
this.mdToHtml_.clearCache();
|
||||
this.forceUpdate();
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@@ -22,12 +22,13 @@ const { Checkbox } = require('lib/components/checkbox.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
const { reg } = require('lib/registry.js');
|
||||
const { shim } = require('lib/shim.js');
|
||||
const ResourceFetcher = require('lib/services/ResourceFetcher');
|
||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||
const { globalStyle, themeStyle } = require('lib/components/global-style.js');
|
||||
const { dialogs } = require('lib/dialogs.js');
|
||||
const DialogBox = require('react-native-dialogbox').default;
|
||||
const { NoteBodyViewer } = require('lib/components/note-body-viewer.js');
|
||||
const RNFetchBlob = require('react-native-fetch-blob').default;
|
||||
const RNFetchBlob = require('rn-fetch-blob').default;
|
||||
const { DocumentPicker, DocumentPickerUtil } = require('react-native-document-picker');
|
||||
const ImageResizer = require('react-native-image-resizer').default;
|
||||
const shared = require('lib/components/shared/note-screen-shared.js');
|
||||
@@ -35,6 +36,7 @@ const ImagePicker = require('react-native-image-picker');
|
||||
const AlarmService = require('lib/services/AlarmService.js');
|
||||
const { SelectDateTimeDialog } = require('lib/components/select-date-time-dialog.js');
|
||||
const ShareExtension = require('react-native-share-extension').default;
|
||||
const CameraView = require('lib/components/CameraView');
|
||||
|
||||
import FileViewer from 'react-native-file-viewer';
|
||||
|
||||
@@ -59,6 +61,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
heightBumpView:0,
|
||||
noteTagDialogShown: false,
|
||||
fromShare: false,
|
||||
showCamera: false,
|
||||
};
|
||||
|
||||
// iOS doesn't support multiline text fields properly so disable it
|
||||
@@ -136,6 +139,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
});
|
||||
}, 5);
|
||||
} else if (item.type_ === BaseModel.TYPE_RESOURCE) {
|
||||
if (!Resource.isReady(item)) throw new Error(_('This attachment is not downloaded or not decrypted yet.'));
|
||||
const resourcePath = Resource.fullPath(item);
|
||||
await FileViewer.open(resourcePath);
|
||||
} else {
|
||||
@@ -148,6 +152,18 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
dialogs.error(this, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
this.resourceFetcher_downloadComplete = async (resource) => {
|
||||
if (!this.state.note || !this.state.note.body) return;
|
||||
const resourceIds = await Note.linkedResourceIds(this.state.note.body);
|
||||
if (resourceIds.indexOf(resource.id) >= 0) {
|
||||
this.refs.noteBodyViewer.rebuildMd();
|
||||
}
|
||||
}
|
||||
|
||||
this.attachPhoto_onPress = this.attachPhoto_onPress.bind(this);
|
||||
this.cameraView_onPhoto = this.cameraView_onPhoto.bind(this);
|
||||
this.cameraView_onCancel = this.cameraView_onCancel.bind(this);
|
||||
}
|
||||
|
||||
styles() {
|
||||
@@ -205,6 +221,8 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
BackButtonService.addHandler(this.backHandler);
|
||||
NavService.addHandler(this.navHandler);
|
||||
|
||||
ResourceFetcher.instance().on('downloadComplete', this.resourceFetcher_downloadComplete);
|
||||
|
||||
await shared.initState(this);
|
||||
|
||||
this.refreshNoteMetadata();
|
||||
@@ -218,7 +236,9 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
BackButtonService.removeHandler(this.backHandler);
|
||||
NavService.removeHandler(this.navHandler);
|
||||
|
||||
if (this.state.fromShare) {
|
||||
ResourceFetcher.instance().off('downloadComplete', this.resourceFetcher_downloadComplete);
|
||||
|
||||
if (Platform.OS !== 'ios' && this.state.fromShare) {
|
||||
ShareExtension.close();
|
||||
}
|
||||
}
|
||||
@@ -321,7 +341,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
}
|
||||
}
|
||||
|
||||
async attachFile(pickerResponse, fileType) {
|
||||
async attachFile(pickerResponse, fileType) {
|
||||
if (!pickerResponse) {
|
||||
reg.logger().warn('Got no response from picker');
|
||||
return;
|
||||
@@ -361,7 +381,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
resource.id = uuid.create();
|
||||
resource.mime = mimeType;
|
||||
resource.title = pickerResponse.fileName ? pickerResponse.fileName : _('Untitled');
|
||||
resource.file_extension = safeFileExtension(fileExtension(pickerResponse.fileName));
|
||||
resource.file_extension = safeFileExtension(fileExtension(pickerResponse.fileName ? pickerResponse.fileName : localFilePath));
|
||||
|
||||
if (!resource.mime) resource.mime = 'application/octet-stream';
|
||||
|
||||
@@ -375,7 +395,8 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
dialogs.error(this, _('Unsupported image type: %s', mimeType));
|
||||
return;
|
||||
} else {
|
||||
await RNFetchBlob.fs.cp(localFilePath, targetPath);
|
||||
await shim.fsDriver().copy(localFilePath, targetPath);
|
||||
|
||||
const stat = await shim.fsDriver().stat(targetPath);
|
||||
if (stat.size >= 10000000) {
|
||||
await shim.fsDriver().remove(targetPath);
|
||||
@@ -406,6 +427,25 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
await this.attachFile(response, 'image');
|
||||
}
|
||||
|
||||
attachPhoto_onPress() {
|
||||
this.setState({ showCamera: true });
|
||||
}
|
||||
|
||||
cameraView_onPhoto(data) {
|
||||
this.attachFile({
|
||||
uri: data.uri,
|
||||
didCancel: false,
|
||||
error: null,
|
||||
type: 'image/jpg',
|
||||
}, 'image');
|
||||
|
||||
this.setState({ showCamera: false });
|
||||
}
|
||||
|
||||
cameraView_onCancel() {
|
||||
this.setState({ showCamera: false });
|
||||
}
|
||||
|
||||
async attachFile_onPress() {
|
||||
const response = await this.pickDocument();
|
||||
await this.attachFile(response, 'all');
|
||||
@@ -461,6 +501,17 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
}
|
||||
}
|
||||
|
||||
async showSource_onPress() {
|
||||
if (!this.state.note.id) return;
|
||||
|
||||
let note = await Note.load(this.state.note.id);
|
||||
try {
|
||||
Linking.openURL(note.source_url);
|
||||
} catch (error) {
|
||||
await dialogs.error(this, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
copyMarkdownLink_onPress() {
|
||||
const note = this.state.note;
|
||||
Clipboard.setString(Note.markdownTag(note));
|
||||
@@ -470,6 +521,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
const note = this.state.note;
|
||||
const isTodo = note && !!note.is_todo;
|
||||
const isSaved = note && note.id;
|
||||
const hasSource = note && note.source_url;
|
||||
|
||||
let output = [];
|
||||
|
||||
@@ -478,7 +530,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
let canAttachPicture = true;
|
||||
if (Platform.OS === 'android' && Platform.Version < 21) canAttachPicture = false;
|
||||
if (canAttachPicture) {
|
||||
output.push({ title: _('Attach photo'), onPress: () => { this.attachImage_onPress(); } });
|
||||
output.push({ title: _('Attach photo'), onPress: () => { this.attachPhoto_onPress(); } });
|
||||
output.push({ title: _('Attach any file'), onPress: () => { this.attachFile_onPress(); } });
|
||||
output.push({ isDivider: true });
|
||||
}
|
||||
@@ -494,6 +546,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
output.push({ isDivider: true });
|
||||
if (this.props.showAdvancedOptions) output.push({ title: this.state.showNoteMetadata ? _('Hide metadata') : _('Show metadata'), onPress: () => { this.showMetadata_onPress(); } });
|
||||
output.push({ title: _('View on map'), onPress: () => { this.showOnMap_onPress(); } });
|
||||
if (hasSource) output.push({ title: _('Go to source URL'), onPress: () => { this.showSource_onPress(); } });
|
||||
output.push({ isDivider: true });
|
||||
output.push({ title: _('Delete'), onPress: () => { this.deleteNote_onPress(); } });
|
||||
|
||||
@@ -526,6 +579,13 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
const folder = this.state.folder;
|
||||
const isNew = !note.id;
|
||||
|
||||
if (this.state.showCamera) {
|
||||
return <CameraView theme={this.props.theme} style={{flex:1}} onPhoto={this.cameraView_onPhoto} onCancel={this.cameraView_onCancel}/>
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
let bodyComponent = null;
|
||||
if (this.state.mode == 'view') {
|
||||
const onCheckboxChange = (newBody) => {
|
||||
@@ -534,6 +594,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
|
||||
bodyComponent = <NoteBodyViewer
|
||||
onJoplinLinkClick={this.onJoplinLinkClick_}
|
||||
ref="noteBodyViewer"
|
||||
style={this.styles().noteBodyViewer}
|
||||
webViewStyle={theme}
|
||||
note={note}
|
||||
|
@@ -59,9 +59,11 @@ class SelectDateTimeDialog extends Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const clearAlarmText = _("Clear alarm"); // For unknown reasons, this particular string doesn't get translated if it's directly in the text property below
|
||||
|
||||
const popupActions = [
|
||||
<DialogButton text={_("Save alarm")} align="center" onPress={() => this.onAccept()} key="saveButton" />,
|
||||
<DialogButton text={_("Clear alarm")} align="center" onPress={() => this.onClear()} key="clearButton" />,
|
||||
<DialogButton text={clearAlarmText} align="center" onPress={() => this.onClear()} key="clearButton" />,
|
||||
<DialogButton text={_("Cancel")} align="center" onPress={() => this.onReject()} key="cancelButton" />,
|
||||
];
|
||||
|
||||
|
@@ -13,7 +13,7 @@ class GeolocationNode {
|
||||
|
||||
response = await response.json();
|
||||
|
||||
if (!('lat' in response) || !('lon' in response)) throw new Error('Invalid geolocation response: ' . JSON.stringify(response));
|
||||
if (!('lat' in response) || !('lon' in response)) throw new Error('Invalid geolocation response: ' + (response ? JSON.stringify(response) : '<null>'));
|
||||
|
||||
return {
|
||||
timestamp: (new Date()).getTime(),
|
||||
|
@@ -3,6 +3,7 @@ const { promiseChain } = require('lib/promise-utils.js');
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { Database } = require('lib/database.js');
|
||||
const { sprintf } = require('sprintf-js');
|
||||
const Resource = require('lib/models/Resource');
|
||||
|
||||
const structureSql = `
|
||||
CREATE TABLE folders (
|
||||
@@ -249,7 +250,7 @@ class JoplinDatabase extends Database {
|
||||
// default value and thus might cause problems. In that case, the default value
|
||||
// must be set in the synchronizer too.
|
||||
|
||||
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
|
||||
const existingDatabaseVersions = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13];
|
||||
|
||||
let currentVersionIndex = existingDatabaseVersions.indexOf(fromVersion);
|
||||
|
||||
@@ -395,6 +396,12 @@ class JoplinDatabase extends Database {
|
||||
queries.push('ALTER TABLE folders ADD COLUMN parent_id TEXT NOT NULL DEFAULT ""');
|
||||
}
|
||||
|
||||
if (targetVersion == 13) {
|
||||
queries.push('ALTER TABLE resources ADD COLUMN fetch_status INT NOT NULL DEFAULT "2"');
|
||||
queries.push('ALTER TABLE resources ADD COLUMN fetch_error TEXT NOT NULL DEFAULT ""');
|
||||
queries.push({ sql: 'UPDATE resources SET fetch_status = ?', params: [Resource.FETCH_STATUS_DONE] });
|
||||
}
|
||||
|
||||
queries.push({ sql: 'UPDATE version SET version = ?', params: [targetVersion] });
|
||||
await this.transactionExecBatch(queries);
|
||||
|
||||
|
@@ -236,7 +236,12 @@ class BaseItem extends BaseModel {
|
||||
return propValue;
|
||||
}
|
||||
|
||||
static async serialize(item, type = null, shownKeys = null) {
|
||||
static async serialize(item, shownKeys = null) {
|
||||
if (shownKeys === null) {
|
||||
shownKeys = this.itemClass(item).fieldNames();
|
||||
shownKeys.push('type_');
|
||||
}
|
||||
|
||||
item = this.filter(item);
|
||||
|
||||
let output = {};
|
||||
@@ -283,7 +288,19 @@ class BaseItem extends BaseModel {
|
||||
|
||||
static async serializeForSync(item) {
|
||||
const ItemClass = this.itemClass(item);
|
||||
let serialized = await ItemClass.serialize(item);
|
||||
let shownKeys = ItemClass.fieldNames();
|
||||
shownKeys.push('type_');
|
||||
|
||||
if (ItemClass.syncExcludedKeys) {
|
||||
const keys = ItemClass.syncExcludedKeys();
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const idx = shownKeys.indexOf(keys[i]);
|
||||
shownKeys.splice(idx, 1);
|
||||
}
|
||||
}
|
||||
|
||||
const serialized = await ItemClass.serialize(item, shownKeys);
|
||||
|
||||
if (!Setting.value('encryption.enabled') || !ItemClass.encryptionSupported()) {
|
||||
// Normally not possible since itemsThatNeedSync should only return decrypted items
|
||||
if (!!item.encryption_applied) throw new JoplinError('Item is encrypted but encryption is currently disabled', 'cannotSyncEncrypted');
|
||||
@@ -307,7 +324,6 @@ class BaseItem extends BaseModel {
|
||||
|
||||
reducedItem.encryption_applied = 1;
|
||||
reducedItem.encryption_cipher_text = cipherText;
|
||||
|
||||
return ItemClass.serialize(reducedItem)
|
||||
}
|
||||
|
||||
|
@@ -15,13 +15,6 @@ class Folder extends BaseItem {
|
||||
return 'folders';
|
||||
}
|
||||
|
||||
static async serialize(folder) {
|
||||
let fieldNames = this.fieldNames();
|
||||
fieldNames.push('type_');
|
||||
//lodash.pull(fieldNames, 'parent_id');
|
||||
return super.serialize(folder, 'folder', fieldNames);
|
||||
}
|
||||
|
||||
static modelType() {
|
||||
return BaseModel.TYPE_FOLDER;
|
||||
}
|
||||
|
@@ -19,12 +19,6 @@ class MasterKey extends BaseItem {
|
||||
return this.modelSelectOne('SELECT * FROM master_keys WHERE created_time >= (SELECT max(created_time) FROM master_keys)');
|
||||
}
|
||||
|
||||
static async serialize(item, type = null, shownKeys = null) {
|
||||
let fieldNames = this.fieldNames();
|
||||
fieldNames.push('type_');
|
||||
return super.serialize(item, 'master_key', fieldNames);
|
||||
}
|
||||
|
||||
static async save(o, options = null) {
|
||||
return super.save(o, options).then((item) => {
|
||||
this.dispatch({
|
||||
|
@@ -25,14 +25,8 @@ class Note extends BaseItem {
|
||||
return field in fieldsToLabels ? fieldsToLabels[field] : field;
|
||||
}
|
||||
|
||||
static async serialize(note, type = null, shownKeys = null) {
|
||||
let fieldNames = this.fieldNames();
|
||||
fieldNames.push('type_');
|
||||
return super.serialize(note, 'note', fieldNames);
|
||||
}
|
||||
|
||||
static async serializeForEdit(note) {
|
||||
return super.serialize(note, 'note', ['title', 'body']);
|
||||
return super.serialize(note, ['title', 'body']);
|
||||
}
|
||||
|
||||
static async unserializeForEdit(content) {
|
||||
@@ -47,7 +41,7 @@ class Note extends BaseItem {
|
||||
let fieldNames = this.fieldNames();
|
||||
fieldNames.push('type_');
|
||||
lodash.pull(fieldNames, 'title', 'body');
|
||||
return super.serialize(note, 'note', fieldNames);
|
||||
return super.serialize(note, fieldNames);
|
||||
}
|
||||
|
||||
static minimalSerializeForDisplay(note) {
|
||||
@@ -75,12 +69,16 @@ class Note extends BaseItem {
|
||||
lodash.pull(fieldNames, 'updated_time');
|
||||
lodash.pull(fieldNames, 'order');
|
||||
|
||||
return super.serialize(n, 'note', fieldNames);
|
||||
return super.serialize(n, fieldNames);
|
||||
}
|
||||
|
||||
static defaultTitle(note) {
|
||||
if (note.body && note.body.length) {
|
||||
const lines = note.body.trim().split("\n");
|
||||
return this.defaultTitleFromBody(note.body);
|
||||
}
|
||||
|
||||
static defaultTitleFromBody(body) {
|
||||
if (body && body.length) {
|
||||
const lines = body.trim().split("\n");
|
||||
let output = lines[0].trim();
|
||||
// Remove the first #, *, etc.
|
||||
while (output.length) {
|
||||
@@ -424,17 +422,25 @@ class Note extends BaseItem {
|
||||
return Note.save(modifiedNote, { autoTimestamp: false });
|
||||
}
|
||||
|
||||
static toggleIsTodo(note) {
|
||||
static changeNoteType(note, type) {
|
||||
if (!('is_todo' in note)) throw new Error('Missing "is_todo" property');
|
||||
|
||||
let output = Object.assign({}, note);
|
||||
output.is_todo = output.is_todo ? 0 : 1;
|
||||
const newIsTodo = type === 'todo' ? 1 : 0;
|
||||
|
||||
if (Number(note.is_todo) === newIsTodo) return note;
|
||||
|
||||
const output = Object.assign({}, note);
|
||||
output.is_todo = newIsTodo;
|
||||
output.todo_due = 0;
|
||||
output.todo_completed = 0;
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static toggleIsTodo(note) {
|
||||
return this.changeNoteType(note, !!note.is_todo ? 'note' : 'todo');
|
||||
}
|
||||
|
||||
static async duplicate(noteId, options = null) {
|
||||
const changes = options && options.changes;
|
||||
const uniqueTitle = options && options.uniqueTitle;
|
||||
|
@@ -11,12 +11,6 @@ class NoteTag extends BaseItem {
|
||||
return BaseModel.TYPE_NOTE_TAG;
|
||||
}
|
||||
|
||||
static async serialize(item, type = null, shownKeys = null) {
|
||||
let fieldNames = this.fieldNames();
|
||||
fieldNames.push('type_');
|
||||
return super.serialize(item, 'note_tag', fieldNames);
|
||||
}
|
||||
|
||||
static async byNoteIds(noteIds) {
|
||||
if (!noteIds.length) return [];
|
||||
return this.modelSelectAll('SELECT * FROM note_tags WHERE note_id IN ("' + noteIds.join('","') + '")');
|
||||
|
@@ -26,22 +26,36 @@ class Resource extends BaseItem {
|
||||
}
|
||||
|
||||
static isSupportedImageMimeType(type) {
|
||||
const imageMimeTypes = ["image/jpg", "image/jpeg", "image/png", "image/gif", "image/svg+xml"];
|
||||
const imageMimeTypes = ["image/jpg", "image/jpeg", "image/png", "image/gif", "image/svg+xml", "image/webp"];
|
||||
return imageMimeTypes.indexOf(type.toLowerCase()) >= 0;
|
||||
}
|
||||
|
||||
static resetStartedFetchStatus() {
|
||||
return this.db().exec('UPDATE resources SET fetch_status = ? WHERE fetch_status = ?', [Resource.FETCH_STATUS_IDLE, Resource.FETCH_STATUS_STARTED]);
|
||||
}
|
||||
|
||||
static needToBeFetched(limit = null) {
|
||||
let sql = 'SELECT * FROM resources WHERE fetch_status = ? ORDER BY updated_time DESC';
|
||||
if (limit !== null) sql += ' LIMIT ' + limit;
|
||||
return this.modelSelectAll(sql, [Resource.FETCH_STATUS_IDLE]);
|
||||
}
|
||||
|
||||
static async saveFetchStatus(resourceId, status, error = null) {
|
||||
const o = {
|
||||
id: resourceId,
|
||||
fetch_status: status,
|
||||
}
|
||||
|
||||
if (error !== null) o.fetch_error = error;
|
||||
|
||||
return Resource.save(o, { autoTimestamp: false });
|
||||
}
|
||||
|
||||
static fsDriver() {
|
||||
if (!Resource.fsDriver_) Resource.fsDriver_ = new FsDriverDummy();
|
||||
return Resource.fsDriver_;
|
||||
}
|
||||
|
||||
static async serialize(item, type = null, shownKeys = null) {
|
||||
let fieldNames = this.fieldNames();
|
||||
fieldNames.push('type_');
|
||||
//fieldNames = ArrayUtils.removeElement(fieldNames, 'encryption_blob_encrypted');
|
||||
return super.serialize(item, 'resource', fieldNames);
|
||||
}
|
||||
|
||||
static filename(resource, encryptedBlob = false) {
|
||||
let extension = encryptedBlob ? 'crypted' : resource.file_extension;
|
||||
if (!extension) extension = resource.mime ? mime.toFileExtension(resource.mime) : '';
|
||||
@@ -49,6 +63,10 @@ class Resource extends BaseItem {
|
||||
return resource.id + extension;
|
||||
}
|
||||
|
||||
static syncExcludedKeys() {
|
||||
return ['fetch_status', 'fetch_error'];
|
||||
}
|
||||
|
||||
static friendlyFilename(resource) {
|
||||
let output = safeFilename(resource.title); // Make sure not to allow spaces or any special characters as it's not supported in HTTP headers
|
||||
if (!output) output = resource.id;
|
||||
@@ -62,6 +80,10 @@ class Resource extends BaseItem {
|
||||
return Setting.value('resourceDir') + '/' + this.filename(resource, encryptedBlob);
|
||||
}
|
||||
|
||||
static isReady(resource) {
|
||||
return resource && resource.fetch_status === Resource.FETCH_STATUS_DONE && !resource.encryption_blob_encrypted;
|
||||
}
|
||||
|
||||
// For resources, we need to decrypt the item (metadata) and the resource binary blob.
|
||||
static async decrypt(item) {
|
||||
// The item might already be decrypted but not the blob (for instance if it crashes while
|
||||
@@ -72,7 +94,7 @@ class Resource extends BaseItem {
|
||||
const plainTextPath = this.fullPath(decryptedItem);
|
||||
const encryptedPath = this.fullPath(decryptedItem, true);
|
||||
const noExtPath = pathUtils.dirname(encryptedPath) + '/' + pathUtils.filename(encryptedPath);
|
||||
|
||||
|
||||
// When the resource blob is downloaded by the synchroniser, it's initially a file with no
|
||||
// extension (since it's encrypted, so we don't know its extension). So here rename it
|
||||
// to a file with a ".crypted" extension so that it's better identified, and then decrypt it.
|
||||
@@ -199,4 +221,9 @@ class Resource extends BaseItem {
|
||||
|
||||
Resource.IMAGE_MAX_DIMENSION = 1920;
|
||||
|
||||
Resource.FETCH_STATUS_IDLE = 0;
|
||||
Resource.FETCH_STATUS_STARTED = 1;
|
||||
Resource.FETCH_STATUS_DONE = 2;
|
||||
Resource.FETCH_STATUS_ERROR = 3;
|
||||
|
||||
module.exports = Resource;
|
@@ -90,7 +90,7 @@ class Setting extends BaseModel {
|
||||
// Might be fixed in Electron 18.x but no non-beta release yet. So for now
|
||||
// by default we disable it on Linux.
|
||||
'showTrayIcon': { value: platform !== 'linux', type: Setting.TYPE_BOOL, public: true, appTypes: ['desktop'], label: () => _('Show tray icon'), description: () => {
|
||||
return platform === 'linux' ? _('Note: Does not work in all desktop environments.') : null;
|
||||
return platform === 'linux' ? _('Note: Does not work in all desktop environments.') : _('This will allow Joplin to run in the background. It is recommended to enable this setting so that your notes are constantly being synchronised, thus reducing the number of conflicts.');
|
||||
}},
|
||||
|
||||
'startMinimized': { value: false, type: Setting.TYPE_BOOL, public: true, appTypes: ['desktop'], label: () => _('Start application minimised in the tray icon') },
|
||||
|
@@ -15,12 +15,6 @@ class Tag extends BaseItem {
|
||||
return BaseModel.TYPE_TAG;
|
||||
}
|
||||
|
||||
static async serialize(item, type = null, shownKeys = null) {
|
||||
let fieldNames = this.fieldNames();
|
||||
fieldNames.push('type_');
|
||||
return super.serialize(item, 'tag', fieldNames);
|
||||
}
|
||||
|
||||
static async noteIds(tagId) {
|
||||
let rows = await this.db().selectAll('SELECT note_id FROM note_tags WHERE tag_id = ?', [tagId]);
|
||||
let output = [];
|
||||
|
@@ -1,4 +1,5 @@
|
||||
const BaseItem = require('lib/models/BaseItem');
|
||||
const Resource = require('lib/models/Resource');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
|
||||
class DecryptionWorker {
|
||||
@@ -43,7 +44,7 @@ class DecryptionWorker {
|
||||
this.scheduleId_ = setTimeout(() => {
|
||||
this.scheduleId_ = null;
|
||||
this.start({
|
||||
materKeyNotLoadedHandler: 'dispatch',
|
||||
masterKeyNotLoadedHandler: 'dispatch',
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
@@ -56,7 +57,7 @@ class DecryptionWorker {
|
||||
|
||||
async start(options = null) {
|
||||
if (options === null) options = {};
|
||||
if (!('materKeyNotLoadedHandler' in options)) options.materKeyNotLoadedHandler = 'throw';
|
||||
if (!('masterKeyNotLoadedHandler' in options)) options.masterKeyNotLoadedHandler = 'throw';
|
||||
|
||||
if (this.state_ !== 'idle') {
|
||||
this.logger().info('DecryptionWorker: cannot start because state is "' + this.state_ + '"');
|
||||
@@ -83,6 +84,8 @@ class DecryptionWorker {
|
||||
|
||||
const ItemClass = BaseItem.itemClass(item);
|
||||
|
||||
if (('fetch_status' in item) && item.fetch_status !== Resource.FETCH_STATUS_DONE) continue;
|
||||
|
||||
this.dispatchReport({
|
||||
itemIndex: i,
|
||||
itemCount: items.length,
|
||||
@@ -95,7 +98,7 @@ class DecryptionWorker {
|
||||
} catch (error) {
|
||||
excludedIds.push(item.id);
|
||||
|
||||
if (error.code === 'masterKeyNotLoaded' && options.materKeyNotLoadedHandler === 'dispatch') {
|
||||
if (error.code === 'masterKeyNotLoaded' && options.masterKeyNotLoadedHandler === 'dispatch') {
|
||||
if (notLoadedMasterKeyDisptaches.indexOf(error.masterKeyId) < 0) {
|
||||
this.dispatch({
|
||||
type: 'MASTERKEY_ADD_NOT_LOADED',
|
||||
@@ -106,11 +109,11 @@ class DecryptionWorker {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (error.code === 'masterKeyNotLoaded' && options.materKeyNotLoadedHandler === 'throw') {
|
||||
if (error.code === 'masterKeyNotLoaded' && options.masterKeyNotLoadedHandler === 'throw') {
|
||||
throw error;
|
||||
}
|
||||
|
||||
this.logger().warn('DecryptionWorker: error for: ' + item.id + ' (' + ItemClass.tableName() + ')', error);
|
||||
this.logger().warn('DecryptionWorker: error for: ' + item.id + ' (' + ItemClass.tableName() + ')', error, item);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -4,6 +4,7 @@ const BaseModel = require('lib/BaseModel');
|
||||
const Folder = require('lib/models/Folder');
|
||||
const Note = require('lib/models/Note');
|
||||
const { shim } = require('lib/shim');
|
||||
const unidecode = require('unidecode');
|
||||
|
||||
class InteropService_Exporter_Md extends InteropService_Exporter_Base {
|
||||
|
||||
@@ -40,7 +41,7 @@ class InteropService_Exporter_Md extends InteropService_Exporter_Base {
|
||||
}
|
||||
|
||||
if (item.type_ === BaseModel.TYPE_NOTE) {
|
||||
const noteFilePath = dirPath + '/' + safeFilename(item.title, null, true) + '.md';
|
||||
const noteFilePath = dirPath + '/' + safeFilename(unidecode(item.title), null, true) + '.md';
|
||||
const noteContent = await Note.serializeForEdit(item);
|
||||
await shim.fsDriver().writeFile(noteFilePath, noteContent, 'utf-8');
|
||||
}
|
||||
|
182
ReactNativeClient/lib/services/ResourceFetcher.js
Normal file
182
ReactNativeClient/lib/services/ResourceFetcher.js
Normal file
@@ -0,0 +1,182 @@
|
||||
const Resource = require('lib/models/Resource');
|
||||
const BaseService = require('lib/services/BaseService');
|
||||
const BaseSyncTarget = require('lib/BaseSyncTarget');
|
||||
const { Logger } = require('lib/logger.js');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
class ResourceFetcher extends BaseService {
|
||||
|
||||
constructor(fileApi = null) {
|
||||
super();
|
||||
|
||||
this.setFileApi(fileApi);
|
||||
this.logger_ = new Logger();
|
||||
this.queue_ = [];
|
||||
this.fetchingItems_ = {};
|
||||
this.resourceDirName_ = BaseSyncTarget.resourceDirName();
|
||||
this.maxDownloads_ = 3;
|
||||
this.addingResources_ = false;
|
||||
this.eventEmitter_ = new EventEmitter();
|
||||
}
|
||||
|
||||
static instance() {
|
||||
if (this.instance_) return this.instance_;
|
||||
this.instance_ = new ResourceFetcher();
|
||||
return this.instance_;
|
||||
}
|
||||
|
||||
on(eventName, callback) {
|
||||
return this.eventEmitter_.on(eventName, callback);
|
||||
}
|
||||
|
||||
off(eventName, callback) {
|
||||
return this.eventEmitter_.removeListener(eventName, callback);
|
||||
}
|
||||
|
||||
setLogger(logger) {
|
||||
this.logger_ = logger;
|
||||
}
|
||||
|
||||
logger() {
|
||||
return this.logger_;
|
||||
}
|
||||
|
||||
setFileApi(v) {
|
||||
if (v !== null && typeof v !== 'function') throw new Error('fileApi must be a function that returns the API. Type is ' + (typeof v));
|
||||
this.fileApi_ = v;
|
||||
}
|
||||
|
||||
async fileApi() {
|
||||
return this.fileApi_();
|
||||
}
|
||||
|
||||
queuedItemIndex_(resourceId) {
|
||||
for (let i = 0; i < this.fetchingItems_.length; i++) {
|
||||
const item = this.fetchingItems_[i];
|
||||
if (item.id === resourceId) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
queueDownload(resourceId, priority = null) {
|
||||
if (priority === null) priority = 'normal';
|
||||
|
||||
const index = this.queuedItemIndex_(resourceId);
|
||||
if (index >= 0) return false;
|
||||
|
||||
const item = { id: resourceId };
|
||||
|
||||
if (priority === 'high') {
|
||||
this.queue_.splice(0, 0, item);
|
||||
} else {
|
||||
this.queue_.push(item);
|
||||
}
|
||||
|
||||
this.scheduleQueueProcess();
|
||||
return true;
|
||||
}
|
||||
|
||||
async startDownload_(resourceId) {
|
||||
if (this.fetchingItems_[resourceId]) return;
|
||||
this.fetchingItems_[resourceId] = true;
|
||||
|
||||
const completeDownload = (emitDownloadComplete = true) => {
|
||||
delete this.fetchingItems_[resource.id];
|
||||
this.scheduleQueueProcess();
|
||||
if (emitDownloadComplete) this.eventEmitter_.emit('downloadComplete', { id: resource.id });
|
||||
}
|
||||
|
||||
const resource = await Resource.load(resourceId);
|
||||
|
||||
// Shouldn't happen, but just to be safe don't re-download the
|
||||
// resource if it's already been downloaded.
|
||||
if (resource.fetch_status === Resource.FETCH_STATUS_DONE) {
|
||||
completeDownload(false);
|
||||
return;
|
||||
}
|
||||
|
||||
this.fetchingItems_[resourceId] = resource;
|
||||
|
||||
const localResourceContentPath = Resource.fullPath(resource);
|
||||
const remoteResourceContentPath = this.resourceDirName_ + "/" + resource.id;
|
||||
|
||||
await Resource.saveFetchStatus(resource.id, Resource.FETCH_STATUS_STARTED);
|
||||
|
||||
const fileApi = await this.fileApi();
|
||||
|
||||
this.logger().debug('ResourceFetcher: Downloading resource: ' + resource.id);
|
||||
|
||||
fileApi.get(remoteResourceContentPath, { path: localResourceContentPath, target: "file" }).then(async () => {
|
||||
await Resource.saveFetchStatus(resource.id, Resource.FETCH_STATUS_DONE);
|
||||
this.logger().debug('ResourceFetcher: Resource downloaded: ' + resource.id);
|
||||
completeDownload();
|
||||
}).catch(async (error) => {
|
||||
this.logger().error('ResourceFetcher: Could not download resource: ' + resource.id, error);
|
||||
await Resource.saveFetchStatus(resource.id, Resource.FETCH_STATUS_ERROR, error.message);
|
||||
completeDownload();
|
||||
});
|
||||
}
|
||||
|
||||
processQueue_() {
|
||||
while (Object.getOwnPropertyNames(this.fetchingItems_).length < this.maxDownloads_) {
|
||||
if (!this.queue_.length) break;
|
||||
const item = this.queue_.splice(0, 1)[0];
|
||||
this.startDownload_(item.id);
|
||||
}
|
||||
|
||||
if (!this.queue_.length) {
|
||||
this.autoAddResources(10);
|
||||
}
|
||||
}
|
||||
|
||||
async waitForAllFinished() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const iid = setInterval(() => {
|
||||
if (!this.queue_.length && !Object.getOwnPropertyNames(this.fetchingItems_).length) {
|
||||
clearInterval(iid);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
|
||||
async autoAddResources(limit) {
|
||||
if (this.addingResources_) return;
|
||||
this.addingResources_ = true;
|
||||
|
||||
let count = 0;
|
||||
const resources = await Resource.needToBeFetched(limit);
|
||||
for (let i = 0; i < resources.length; i++) {
|
||||
const added = this.queueDownload(resources[i].id);
|
||||
if (added) count++;
|
||||
}
|
||||
|
||||
this.logger().info('ResourceFetcher: Auto-added resources: ' + count);
|
||||
this.addingResources_ = false;
|
||||
}
|
||||
|
||||
async start() {
|
||||
await Resource.resetStartedFetchStatus();
|
||||
this.autoAddResources(10);
|
||||
}
|
||||
|
||||
scheduleQueueProcess() {
|
||||
if (this.scheduleQueueProcessIID_) {
|
||||
clearTimeout(this.scheduleQueueProcessIID_);
|
||||
this.scheduleQueueProcessIID_ = null;
|
||||
}
|
||||
|
||||
this.scheduleQueueProcessIID_ = setTimeout(() => {
|
||||
this.processQueue_();
|
||||
this.scheduleQueueProcessIID_ = null;
|
||||
}, 100);
|
||||
}
|
||||
|
||||
async fetchAll() {
|
||||
await Resource.resetStartedFetchStatus();
|
||||
this.autoAddResources(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = ResourceFetcher;
|
@@ -265,20 +265,22 @@ class Api {
|
||||
// size: 164394
|
||||
|
||||
if (request.method === 'GET') {
|
||||
if (link !== 'file') throw new ErrorNotFound();
|
||||
if (link === 'file') {
|
||||
const resource = await Resource.load(id);
|
||||
if (!resource) throw new ErrorNotFound();
|
||||
|
||||
const resource = await Resource.load(id);
|
||||
if (!resource) throw new ErrorNotFound();
|
||||
const filePath = Resource.fullPath(resource);
|
||||
const buffer = await shim.fsDriver().readFile(filePath, 'Buffer');
|
||||
|
||||
const response = new ApiResponse();
|
||||
response.type = 'attachment';
|
||||
response.body = buffer;
|
||||
response.contentType = resource.mime;
|
||||
response.attachmentFilename = Resource.friendlyFilename(resource);
|
||||
return response;
|
||||
}
|
||||
|
||||
const filePath = Resource.fullPath(resource);
|
||||
const buffer = await shim.fsDriver().readFile(filePath, 'Buffer');
|
||||
|
||||
const response = new ApiResponse();
|
||||
response.type = 'attachment';
|
||||
response.body = buffer;
|
||||
response.contentType = resource.mime;
|
||||
response.attachmentFilename = Resource.friendlyFilename(resource);
|
||||
return response;
|
||||
if (link) throw new ErrorNotFound();
|
||||
}
|
||||
|
||||
if (request.method === 'POST') {
|
||||
|
@@ -1,7 +1,7 @@
|
||||
const { shim } = require('lib/shim.js');
|
||||
const { GeolocationReact } = require('lib/geolocation-react.js');
|
||||
const { PoorManIntervals } = require('lib/poor-man-intervals.js');
|
||||
const RNFetchBlob = require('react-native-fetch-blob').default;
|
||||
const RNFetchBlob = require('rn-fetch-blob').default;
|
||||
const { generateSecureRandom } = require('react-native-securerandom');
|
||||
const FsDriverRN = require('lib/fs-driver-rn.js').FsDriverRN;
|
||||
const urlValidator = require('valid-url');
|
||||
|
@@ -7,6 +7,10 @@ shim.isNode = () => {
|
||||
};
|
||||
|
||||
shim.isReactNative = () => {
|
||||
if (typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('ReactNativeDebugger') >= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !shim.isNode();
|
||||
};
|
||||
|
||||
@@ -14,6 +18,10 @@ shim.isLinux = () => {
|
||||
return process && process.platform === 'linux';
|
||||
}
|
||||
|
||||
shim.isFreeBSD = () => {
|
||||
return process && process.platform === 'freebsd';
|
||||
}
|
||||
|
||||
shim.isWindows = () => {
|
||||
return process && process.platform === 'win32';
|
||||
}
|
||||
@@ -27,6 +35,7 @@ shim.platformName = function() {
|
||||
if (shim.isMac()) return 'darwin';
|
||||
if (shim.isWindows()) return 'win32';
|
||||
if (shim.isLinux()) return 'linux';
|
||||
if (shim.isFreeBSD()) return 'freebsd';
|
||||
throw new Error('Cannot determine platform');
|
||||
}
|
||||
|
||||
@@ -141,4 +150,4 @@ shim.Buffer = null;
|
||||
shim.openUrl = () => { throw new Error('Not implemented'); }
|
||||
shim.waitForFrame = () => { throw new Error('Not implemented'); }
|
||||
|
||||
module.exports = { shim };
|
||||
module.exports = { shim };
|
||||
|
@@ -206,4 +206,8 @@ function toTitleCase(string) {
|
||||
return string.charAt(0).toUpperCase() + string.slice(1);
|
||||
}
|
||||
|
||||
function urlDecode(string) {
|
||||
return decodeURIComponent((string+'').replace(/\+/g, '%20'));
|
||||
}
|
||||
|
||||
module.exports = { removeDiacritics, escapeFilename, wrap, splitCommandString, padLeft, toTitleCase };
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user