You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-08-27 20:29:45 +02:00
Compare commits
67 Commits
v1.0.115
...
android-v1
Author | SHA1 | Date | |
---|---|---|---|
|
c0d679b6c2 | ||
|
eb789b9b9a | ||
|
b1898141c3 | ||
|
3231bfaff0 | ||
|
6bb09c9c30 | ||
|
35d3fe03ab | ||
|
f05929cd17 | ||
|
982c9828da | ||
|
d6eacb2b33 | ||
|
0abe213fc2 | ||
|
a6716d55c5 | ||
|
fa0572de77 | ||
|
6dca4a0d6b | ||
|
eacfe1a9ac | ||
|
c223cdf10a | ||
|
38c42b7a15 | ||
|
56432dc773 | ||
|
d3b4379161 | ||
|
8a6fcdbcae | ||
|
061ce646d2 | ||
|
5ec7c16e3e | ||
|
5d629508c1 | ||
|
0a6f8b0cfe | ||
|
460f826672 | ||
|
cb16a10121 | ||
|
3b6131f1ca | ||
|
57225a36b9 | ||
|
3e313399c2 | ||
|
7947e14792 | ||
|
71098102c5 | ||
|
8e601e80df | ||
|
3b14cfcc54 | ||
|
61a0e43092 | ||
|
d08aaffe41 | ||
|
7d0def30f0 | ||
|
bb45d72a56 | ||
|
3943192c5d | ||
|
18d76807f6 | ||
|
01a30a7ccf | ||
|
3fb35d043b | ||
|
9b51bd484d | ||
|
879b556845 | ||
|
0df2a501dd | ||
|
6f64fdffcc | ||
|
19252af345 | ||
|
897f53b13e | ||
|
45cd8b7e3c | ||
|
922bbdd1b6 | ||
|
c24135577c | ||
|
3240ff40bc | ||
|
58b68cab0c | ||
|
0a0afd7245 | ||
|
de01606bff | ||
|
046474b484 | ||
|
277b2b9298 | ||
|
0b7296ae95 | ||
|
ce87dd55f0 | ||
|
07b724d65b | ||
|
bc1984298f | ||
|
9ed0bdfed2 | ||
|
57628e8986 | ||
|
fc8f53fd0e | ||
|
efd7cc6a0c | ||
|
7bfc3e1256 | ||
|
7f6ca1e527 | ||
|
71d9b1d441 | ||
|
a3d64d0a90 |
@@ -655,6 +655,10 @@ msgstr ""
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Cerca a totes les notes"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "Cerca a totes les notes"
|
||||
|
||||
msgid "View"
|
||||
msgstr "Visualització"
|
||||
|
||||
@@ -1175,6 +1179,9 @@ msgstr ""
|
||||
"No es pot actualitzar el testimoni: manquen les dades d'autenticació. Si "
|
||||
"comenceu altre cop la sincronització, potser es corregeixi el problema."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Sense títol"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1268,9 +1275,6 @@ msgstr "data d'actualització"
|
||||
msgid "created date"
|
||||
msgstr "data de creació"
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Sense títol"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "Aquesta nota no té informació de geolocalització."
|
||||
|
||||
@@ -1345,6 +1349,10 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "Percentatge de zoom global"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "Tipus de lletra de l'editor"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "Tipus de lletra de l'editor"
|
||||
|
||||
@@ -1712,6 +1720,10 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Tipus d'imatge no admesa: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "Adjunta una imatge"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Adjunta una imatge"
|
||||
|
||||
|
@@ -637,6 +637,10 @@ msgstr ""
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Hledat ve všech poznámkách"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "Hledat ve všech poznámkách"
|
||||
|
||||
msgid "View"
|
||||
msgstr "Zobrazit"
|
||||
|
||||
@@ -1145,6 +1149,9 @@ msgstr ""
|
||||
"Nelze obnovit token: chybí autentizační data. Restart synchronizace může "
|
||||
"tento problém vyřešit. "
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Bez názvu"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1242,9 +1249,6 @@ msgstr "Upraveno: %d."
|
||||
msgid "created date"
|
||||
msgstr "Vytvořeno: %d."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Bez názvu"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "Tato poznámka nemá informace o poloze."
|
||||
|
||||
@@ -1320,6 +1324,10 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "Globální zoom"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "Rodina písma v editoru"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "Rodina písma v editoru"
|
||||
|
||||
@@ -1681,6 +1689,10 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Nepodporovaný formát obrázku: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "Přiložit obrázek"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Přiložit obrázek"
|
||||
|
||||
|
@@ -642,6 +642,10 @@ msgstr ""
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Søg i alle noter"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "Søg i alle noter"
|
||||
|
||||
msgid "View"
|
||||
msgstr "Vis"
|
||||
|
||||
@@ -1154,6 +1158,9 @@ msgstr ""
|
||||
"Kan ikke opdatere token: Godkendelses data mangler. Prøv at starte "
|
||||
"synkronisering igen, det kan løse problemet."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Samlet"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1251,9 +1258,6 @@ msgstr "Opdateret %d."
|
||||
msgid "created date"
|
||||
msgstr "Oprettet: %d."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Samlet"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "Denne note har ingen geolokations oplysninger."
|
||||
|
||||
@@ -1329,6 +1333,10 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "Global zoom procent"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "Rediger skrifttype"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "Rediger skrifttype"
|
||||
|
||||
@@ -1690,6 +1698,10 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Ulovlig billedtype: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "Vedhæft foto"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Vedhæft foto"
|
||||
|
||||
|
@@ -663,6 +663,10 @@ msgstr "Im externen Editor bearbeiten"
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Alle Notizen durchsuchen"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "Alle Notizen durchsuchen"
|
||||
|
||||
msgid "View"
|
||||
msgstr "Ansicht"
|
||||
|
||||
@@ -1186,6 +1190,9 @@ msgstr ""
|
||||
"Kann Token nicht erneuern: Authentifikationsdaten nicht vorhanden. Ein "
|
||||
"Neustart der Synchronisation könnte das Problem beheben."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Unbenannt"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1275,14 +1282,11 @@ msgid "title"
|
||||
msgstr "Titel"
|
||||
|
||||
msgid "updated date"
|
||||
msgstr "Aktualsierungsdatum"
|
||||
msgstr "Aktualisierungsdatum"
|
||||
|
||||
msgid "created date"
|
||||
msgstr "Erstelldatum"
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Unbenannt"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "Diese Notiz hat keine Standort-Informationen."
|
||||
|
||||
@@ -1357,6 +1361,10 @@ msgstr "Starte die Anwendung minimiert im Tray"
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "Zoomstufe der Benutzeroberfläche"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "Editor Schriftenfamilie"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "Editor Schriftenfamilie"
|
||||
|
||||
@@ -1738,6 +1746,10 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Nicht unterstütztes Fotoformat: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "Foto anhängen"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Foto anhängen"
|
||||
|
||||
|
@@ -573,6 +573,9 @@ msgstr ""
|
||||
msgid "Search in all the notes"
|
||||
msgstr ""
|
||||
|
||||
msgid "Search in current note"
|
||||
msgstr ""
|
||||
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
|
||||
@@ -1060,6 +1063,9 @@ msgid ""
|
||||
"synchronisation again may fix the problem."
|
||||
msgstr ""
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1147,9 +1153,6 @@ msgstr ""
|
||||
msgid "created date"
|
||||
msgstr ""
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr ""
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr ""
|
||||
|
||||
@@ -1224,6 +1227,9 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr ""
|
||||
|
||||
msgid "Editor font size"
|
||||
msgstr ""
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr ""
|
||||
|
||||
@@ -1562,6 +1568,9 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "Take photo"
|
||||
msgstr ""
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr ""
|
||||
|
||||
|
@@ -651,6 +651,10 @@ msgstr "Editar con un editor externo"
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Buscar en todas las notas"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "Buscar en todas las notas"
|
||||
|
||||
msgid "View"
|
||||
msgstr "Ver"
|
||||
|
||||
@@ -1167,6 +1171,9 @@ msgstr ""
|
||||
"No se ha podido actualizar token: faltan datos de autenticación. Reiniciar "
|
||||
"la sincronización podría solucionar el problema."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Sin título"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1261,9 +1268,6 @@ msgstr "fecha de actualización"
|
||||
msgid "created date"
|
||||
msgstr "fecha de creación"
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Sin título"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "Esta nota no tiene informacion de geolocalización."
|
||||
|
||||
@@ -1338,6 +1342,10 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "Establecer el porcentaje de aumento de la aplicación"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "Fuente del editor"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "Fuente del editor"
|
||||
|
||||
@@ -1710,6 +1718,10 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Tipo de imagen no soportado: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "Adjuntar foto"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Adjuntar foto"
|
||||
|
||||
|
@@ -650,6 +650,10 @@ msgstr ""
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Bilatu ohar guztietan"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "Bilatu ohar guztietan"
|
||||
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
|
||||
@@ -1170,6 +1174,9 @@ msgstr ""
|
||||
"Tokena ezin eguneratu daiteke: egiaztatze-datuak desagertuta daude. Agian, "
|
||||
"berriro sinkronizatzeak arazoa konpon lezake."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Titulu gabekoa"
|
||||
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
@@ -1267,9 +1274,6 @@ msgstr "Eguneratuta: %d."
|
||||
msgid "created date"
|
||||
msgstr "Sortuta: %d."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Titulu gabekoa"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "Ohar honek ez du geokokapen informaziorik."
|
||||
|
||||
@@ -1350,6 +1354,10 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "Ezarri aplikazioaren zoomaren ehunekoa"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "Oharra editatu."
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr ""
|
||||
|
||||
@@ -1711,6 +1719,10 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Irudi formatua ez onartua: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "Argazkia erantsi"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Argazkia erantsi"
|
||||
|
||||
|
@@ -648,6 +648,9 @@ msgstr "Ouvrir dans un éditeur externe"
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Chercher dans toutes les notes"
|
||||
|
||||
msgid "Search in current note"
|
||||
msgstr "Chercher dans la note en cours"
|
||||
|
||||
msgid "View"
|
||||
msgstr "Affichage"
|
||||
|
||||
@@ -1172,6 +1175,9 @@ msgstr ""
|
||||
"Impossible de rafraîchir la connexion à OneDrive. Démarrez la "
|
||||
"synchronisation à nouveau pour corriger le problème."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Sans titre"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1265,9 +1271,6 @@ msgstr "date de modification"
|
||||
msgid "created date"
|
||||
msgstr "date de création"
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Sans titre"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "Cette note n'a pas d'information d'emplacement."
|
||||
|
||||
@@ -1345,6 +1348,9 @@ msgstr "Démarrer minimisé dans la zone de notification"
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "Niveau de zoom"
|
||||
|
||||
msgid "Editor font size"
|
||||
msgstr "Taille police éditeur"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "Police de l'éditeur"
|
||||
|
||||
@@ -1722,6 +1728,9 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Type d'image non géré : %s"
|
||||
|
||||
msgid "Take photo"
|
||||
msgstr "Prendre une photo"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Attacher une photo"
|
||||
|
||||
|
@@ -642,6 +642,10 @@ msgstr ""
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Buscar en todas as notas"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "Buscar en todas as notas"
|
||||
|
||||
msgid "View"
|
||||
msgstr "Vista"
|
||||
|
||||
@@ -1153,6 +1157,9 @@ msgstr ""
|
||||
"Non é posíbel actualizar o «token»: faltan datos da autenticación. Iniciar a "
|
||||
"sincronización de novo pode arranxar o problema."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Sen título"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1250,9 +1257,6 @@ msgstr "Actualizado: %d."
|
||||
msgid "created date"
|
||||
msgstr "Creado: %d."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Sen título"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "Esta nota non ten información de xeolocalización."
|
||||
|
||||
@@ -1328,6 +1332,10 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "Porcentaxe de ampliación"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "Familia de tipos de letra do editor"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "Familia de tipos de letra do editor"
|
||||
|
||||
@@ -1689,6 +1697,10 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Tipo de imaxe incompatíbel: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "Anexar foto"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Anexar foto"
|
||||
|
||||
|
@@ -647,6 +647,10 @@ msgstr ""
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Pretraži u svim bilješkama"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "Pretraži u svim bilješkama"
|
||||
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
|
||||
@@ -1151,6 +1155,9 @@ msgid ""
|
||||
"synchronisation again may fix the problem."
|
||||
msgstr "Nedostaju podaci za ovjeru. Pokušaj ponovo započeti sinkronizaciju."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Nenaslovljen"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1249,9 +1256,6 @@ msgstr "Ažurirano: %d."
|
||||
msgid "created date"
|
||||
msgstr "Stvoreno: %d."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Nenaslovljen"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "Ova bilješka nema geolokacijske informacije."
|
||||
|
||||
@@ -1332,6 +1336,10 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "Uredi bilješku."
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr ""
|
||||
|
||||
@@ -1687,6 +1695,10 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Nepodržana vrsta slike: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "Priloži sliku"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Priloži sliku"
|
||||
|
||||
|
@@ -648,6 +648,10 @@ msgstr "Modifica in un editor esterno"
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Cerca in tutte le note"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "Cerca in tutte le note"
|
||||
|
||||
msgid "View"
|
||||
msgstr "Vista"
|
||||
|
||||
@@ -1168,6 +1172,9 @@ msgstr ""
|
||||
"Non è possibile aggiornare il token. mancano i dati di autenticazione. "
|
||||
"Ricominciare la sincronizzazione da capo potrebbe risolvere il problema."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Senza titolo"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1261,9 +1268,6 @@ msgstr "Data di aggiornamento"
|
||||
msgid "created date"
|
||||
msgstr "Data di creazione"
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Senza titolo"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "Questa nota non ha informazione sulla geolocalizzazione."
|
||||
|
||||
@@ -1338,6 +1342,10 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "Percentuale di zoom globale"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "Editor Famiglia Caratteri"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "Editor Famiglia Caratteri"
|
||||
|
||||
@@ -1710,6 +1718,10 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Tipo di immagine non supportata: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "Allega foto"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Allega foto"
|
||||
|
||||
|
@@ -637,6 +637,10 @@ msgstr "外部エディターで編集"
|
||||
msgid "Search in all the notes"
|
||||
msgstr "すべてのノートを検索"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "すべてのノートを検索"
|
||||
|
||||
msgid "View"
|
||||
msgstr "表示"
|
||||
|
||||
@@ -1151,6 +1155,9 @@ msgstr ""
|
||||
"トークンの更新ができませんでした。認証データがありません。同期を再度行うこと"
|
||||
"で解決することがあります。"
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "名称未設定"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1246,9 +1253,6 @@ msgstr "アップデート日"
|
||||
msgid "created date"
|
||||
msgstr "作成日"
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "名称未設定"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "このノートには位置情報がありません。"
|
||||
|
||||
@@ -1323,6 +1327,10 @@ msgstr "アプリケーションをトレイアンコンで最小化して起動
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "全体ズームの割合"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "エディターのフォントファミリー"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "エディターのフォントファミリー"
|
||||
|
||||
@@ -1690,6 +1698,10 @@ msgstr "Joplinモバイルアプリは次のタイプのリンクをまだサポ
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "サポートされていない画像の形式: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "写真を添付"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "写真を添付"
|
||||
|
||||
|
@@ -573,6 +573,9 @@ msgstr ""
|
||||
msgid "Search in all the notes"
|
||||
msgstr ""
|
||||
|
||||
msgid "Search in current note"
|
||||
msgstr ""
|
||||
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
|
||||
@@ -1060,6 +1063,9 @@ msgid ""
|
||||
"synchronisation again may fix the problem."
|
||||
msgstr ""
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1147,9 +1153,6 @@ msgstr ""
|
||||
msgid "created date"
|
||||
msgstr ""
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr ""
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr ""
|
||||
|
||||
@@ -1224,6 +1227,9 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr ""
|
||||
|
||||
msgid "Editor font size"
|
||||
msgstr ""
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr ""
|
||||
|
||||
@@ -1562,6 +1568,9 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "Take photo"
|
||||
msgstr ""
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr ""
|
||||
|
||||
|
@@ -634,6 +634,10 @@ msgstr "외부 편집기에서 편집하기"
|
||||
msgid "Search in all the notes"
|
||||
msgstr "모든 노트에서 검색"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "모든 노트에서 검색"
|
||||
|
||||
msgid "View"
|
||||
msgstr "보기"
|
||||
|
||||
@@ -1145,6 +1149,9 @@ msgstr ""
|
||||
"토큰 새로고침 불가: 인증 데이터를 찾을 수 없습니다. 동기화를 다시 시도하면 문"
|
||||
"제가 해결될수도 있습니다."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "제목 없음"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1238,9 +1245,6 @@ msgstr "업데이트된 날짜"
|
||||
msgid "created date"
|
||||
msgstr "만들어진 날짜"
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "제목 없음"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "이 노트는 지리적 위치 정보를 포함하고 있지 않습니다."
|
||||
|
||||
@@ -1315,6 +1319,10 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "전체적 확대 비율"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "편집기 글꼴 집합"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "편집기 글꼴 집합"
|
||||
|
||||
@@ -1680,6 +1688,10 @@ msgstr "조플린 모바일 앱은 현재 해당 형식의 링크를 지원하
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "지원하지 않는 이미지 형식: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "사진 첨부"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "사진 첨부"
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -652,6 +652,10 @@ msgstr ""
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Zoek in alle notities"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "Zoek in alle notities"
|
||||
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
|
||||
@@ -1171,6 +1175,9 @@ msgstr ""
|
||||
"Kan token niet vernieuwen: authenticatiedata ontbreekt. Herstarten van de "
|
||||
"synchronisatie kan het probleem eventueel oplossen. "
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Untitled"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1270,9 +1277,6 @@ msgstr "Bijgewerkt: %d."
|
||||
msgid "created date"
|
||||
msgstr "Aangemaakt: %d."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Untitled"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "Deze notitie bevat geen geo-locatie informatie."
|
||||
|
||||
@@ -1352,6 +1356,10 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "Bewerk notitie."
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr ""
|
||||
|
||||
@@ -1713,6 +1721,10 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Afbeeldingstype %s wordt niet ondersteund"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "Voeg foto toe"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Voeg foto toe"
|
||||
|
||||
|
@@ -651,6 +651,10 @@ msgstr "Bewerken in externe bewerker"
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Alle notities doorzoeken"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "Alle notities doorzoeken"
|
||||
|
||||
msgid "View"
|
||||
msgstr "Beeld"
|
||||
|
||||
@@ -1173,6 +1177,9 @@ msgstr ""
|
||||
"Kan toegangssleutel niet verversen: de authenticatiegegevens ontbreken. "
|
||||
"Probeer om de synchronisatie opnieuw te starten."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Naamloos"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1266,9 +1273,6 @@ msgstr "bijgewerkt op"
|
||||
msgid "created date"
|
||||
msgstr "gecreëerd op"
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Naamloos"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "Deze notitie bevat geen locatie-informatie."
|
||||
|
||||
@@ -1343,6 +1347,10 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "Globaal zoompercentage"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "Lettertype van bewerker"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "Lettertype van bewerker"
|
||||
|
||||
@@ -1716,6 +1724,10 @@ msgstr "De mobiele Joplin-app ondersteunt momenteel niet dit soort links: %s"
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Niet-ondersteunde afbeeldingssoort: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "Foto bijvoegen"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Foto bijvoegen"
|
||||
|
||||
|
@@ -1,7 +1,8 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# pt_BR locale strings - Joplin.
|
||||
# Copyright (C) YEAR Laurent Cozic
|
||||
# This file is distributed under the same license as the Joplin-CLI package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
# Updated by Renato Xavier da Silveira Rosa <renatoxsr@gmail.com>, 2018.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
@@ -13,7 +14,7 @@ msgstr ""
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 2.0.7\n"
|
||||
"X-Generator: Poedit 2.2\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
msgid "To delete a tag, untag the associated notes."
|
||||
@@ -147,7 +148,7 @@ msgid "Completed decryption."
|
||||
msgstr "Decriptação completada."
|
||||
|
||||
msgid "Enabled"
|
||||
msgstr "Desabilitado"
|
||||
msgstr "Habilitado"
|
||||
|
||||
msgid "Disabled"
|
||||
msgstr "Desabilitado"
|
||||
@@ -455,12 +456,11 @@ msgid "Starting synchronisation..."
|
||||
msgstr "Iniciando sincronização..."
|
||||
|
||||
msgid "Downloading resources..."
|
||||
msgstr ""
|
||||
msgstr "Baixando os recursos..."
|
||||
|
||||
msgid "Cancelling... Please wait."
|
||||
msgstr "Cancelando... Aguarde."
|
||||
|
||||
#, 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 "
|
||||
@@ -468,7 +468,8 @@ msgid ""
|
||||
msgstr ""
|
||||
"<tag-command> pode ser \"add\", \"remove\" ou \"list\" para atribuir ou "
|
||||
"remover [tag] de [nota], ou para listar as notas associadas a [tag]. O "
|
||||
"comando `taglist` pode ser usado para listar todas as tags."
|
||||
"comando `tag list` pode ser usado para listar todas as tags (use -l para "
|
||||
"opção longa)."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Invalid command: \"%s\""
|
||||
@@ -628,9 +629,8 @@ msgstr "Cortar"
|
||||
msgid "Paste"
|
||||
msgstr "Colar"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Select all"
|
||||
msgstr "Selecionar data"
|
||||
msgstr "Selecionar tudo"
|
||||
|
||||
msgid "Bold"
|
||||
msgstr "Negrito"
|
||||
@@ -647,6 +647,10 @@ msgstr "Editar com editor externo"
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Pesquisar em todas as notas"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "Pesquisar em todas as notas"
|
||||
|
||||
msgid "View"
|
||||
msgstr "Visualizar"
|
||||
|
||||
@@ -716,7 +720,7 @@ msgid "No"
|
||||
msgstr "Não"
|
||||
|
||||
msgid "Token has been copied to the clipboard!"
|
||||
msgstr ""
|
||||
msgstr "Token foi copiado para a \\u00e1rea de transfer\\u00eancia!"
|
||||
|
||||
msgid "The web clipper service is enabled and set to auto-start."
|
||||
msgstr ""
|
||||
@@ -728,7 +732,7 @@ msgstr "Status: Iniciado, na porta %d"
|
||||
|
||||
#, javascript-format
|
||||
msgid "Status: %s"
|
||||
msgstr "Status: \"%s\"."
|
||||
msgstr "Status: %s"
|
||||
|
||||
msgid "Disable Web Clipper Service"
|
||||
msgstr "Desabilitar serviço Web Clipper"
|
||||
@@ -767,20 +771,21 @@ msgstr "Passo 2: Instalar a extensão"
|
||||
msgid "Download and install the relevant extension for your browser:"
|
||||
msgstr "Baixe e instale a extensão relevante para seu browser:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Advanced options"
|
||||
msgstr "Mostrar opções avançadas"
|
||||
|
||||
msgid "Authorisation token:"
|
||||
msgstr ""
|
||||
msgstr "Token de autoriza\\u00e7\\u00e3o:"
|
||||
|
||||
msgid "Copy token"
|
||||
msgstr ""
|
||||
msgstr "Copira token"
|
||||
|
||||
msgid ""
|
||||
"This authorisation token is only needed to allow third-party applications to "
|
||||
"access Joplin."
|
||||
msgstr ""
|
||||
"Esse token de autoriza\\u00e7\\u00e3o só é necess\\u00e1rio para permitir "
|
||||
"que aplicativos de terceiros acessem o Joplin."
|
||||
|
||||
msgid "Check synchronisation configuration"
|
||||
msgstr "Verificar a configuração da sincronização"
|
||||
@@ -946,13 +951,11 @@ 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"
|
||||
msgstr "Alternar para o tipo Nota"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Switch to to-do type"
|
||||
msgstr "Alternar entre os tipos Nota e Tarefa"
|
||||
msgstr "Alternar para o tipo Tarefa"
|
||||
|
||||
msgid "Copy Markdown link"
|
||||
msgstr "Copiar link de Markdown"
|
||||
@@ -971,13 +974,13 @@ msgid ""
|
||||
msgstr "Atualmente, não há cadernos. Crie um, clicando em \"Novo caderno\"."
|
||||
|
||||
msgid "Location"
|
||||
msgstr ""
|
||||
msgstr "Localização"
|
||||
|
||||
msgid "URL"
|
||||
msgstr ""
|
||||
msgstr "URL"
|
||||
|
||||
msgid "Note properties"
|
||||
msgstr ""
|
||||
msgstr "Propriedades da nota"
|
||||
|
||||
msgid "Open..."
|
||||
msgstr "Abrir..."
|
||||
@@ -996,7 +999,7 @@ msgid "Copy Link Address"
|
||||
msgstr "Copiar endereço do link"
|
||||
|
||||
msgid "This attachment is not downloaded or not decrypted yet."
|
||||
msgstr ""
|
||||
msgstr "O anexo ainda não foi baixado ou decriptado."
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
@@ -1060,7 +1063,7 @@ msgid "Click to stop external editing"
|
||||
msgstr "Clique para encerrar edição externa"
|
||||
|
||||
msgid "Watching..."
|
||||
msgstr "Verificando..."
|
||||
msgstr "Observando alterações..."
|
||||
|
||||
msgid "to-do"
|
||||
msgstr "tarefa"
|
||||
@@ -1113,11 +1116,11 @@ msgstr "Cadernos"
|
||||
|
||||
#, javascript-format
|
||||
msgid "Decrypting items: %d/%d"
|
||||
msgstr "Decriptando itens: %d/%d."
|
||||
msgstr "Decriptando itens: %d/%d"
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "Fetching resources: %d"
|
||||
msgstr "Recursos: %d."
|
||||
msgstr "Buscando recursos: %d"
|
||||
|
||||
msgid "Please select where the sync status should be exported to"
|
||||
msgstr ""
|
||||
@@ -1165,6 +1168,9 @@ msgstr ""
|
||||
"Não é possível atualizar token: faltam dados de autenticação. Iniciar a "
|
||||
"sincronização novamente pode corrigir o problema."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Sem título"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1259,9 +1265,6 @@ msgstr "data de ataualização"
|
||||
msgid "created date"
|
||||
msgstr "data de criação"
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Sem título"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "Esta nota não possui informações de geolocalização."
|
||||
|
||||
@@ -1301,7 +1304,7 @@ msgid "Sort notes by"
|
||||
msgstr "Ordenar notas por"
|
||||
|
||||
msgid "Reverse sort order"
|
||||
msgstr "Inverter ordem de classificação."
|
||||
msgstr "Inverter ordem de classificação"
|
||||
|
||||
msgid "Save geo-location with notes"
|
||||
msgstr "Salvar geolocalização com notas"
|
||||
@@ -1322,20 +1325,27 @@ msgid "Show tray icon"
|
||||
msgstr "Exibir tray icon"
|
||||
|
||||
msgid "Note: Does not work in all desktop environments."
|
||||
msgstr "Nota: não funciona em todos os ambientes de desktop"
|
||||
msgstr "Nota: não funciona em todos os ambientes de desktop."
|
||||
|
||||
msgid ""
|
||||
"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."
|
||||
msgstr ""
|
||||
"Isso irá permitir que o Joplin continue sendo executado em segundo plano. É "
|
||||
"recomendado que você habilita essa configuração para que suas notas "
|
||||
"sejamconstantemente sincronizadas, de modo a reduzir as chances de conflitos."
|
||||
|
||||
msgid "Start application minimised in the tray icon"
|
||||
msgstr ""
|
||||
msgstr "Iniciar aplicativo minimizado na barra de tarefas"
|
||||
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "Porcentagem global do zoom"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "Família de fontes do editor"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "Família de fontes do editor"
|
||||
|
||||
@@ -1407,6 +1417,9 @@ msgid ""
|
||||
"to it before syncing, otherwise all files will be removed! See the FAQ for "
|
||||
"more details: %s"
|
||||
msgstr ""
|
||||
"Atenção: Se você modificar esse local, tenha certeza de copiar todo o seu "
|
||||
"conteúdo para lá antes de sincronizar, do contrário todos os seus arquivos "
|
||||
"serão removidos! Veja o FAQ para mais detalhes: %s"
|
||||
|
||||
msgid "Nextcloud username"
|
||||
msgstr "Usuário da Nextcloud"
|
||||
@@ -1436,7 +1449,7 @@ msgstr ""
|
||||
"os certificados, ou caminhos para arquivos cert. Por exemplo, /my/cert_dir, /"
|
||||
"other/custom.pem. Note que se você fizer mudanças nas configurações de TLS, "
|
||||
"você tem que salvar as mudanças antes de clicar em \"Verificar a "
|
||||
"configuração da sincronização\""
|
||||
"configuração da sincronização\"."
|
||||
|
||||
msgid "Ignore TLS certificate errors"
|
||||
msgstr "Ignorar erros de certificados TLS"
|
||||
@@ -1447,7 +1460,7 @@ msgstr "Valor da opção inválida: \"%s\". Os valores possíveis são: %s."
|
||||
|
||||
#, javascript-format
|
||||
msgid "The tag \"%s\" already exists. Please choose a different name."
|
||||
msgstr ""
|
||||
msgstr "A tag \"%s\" já existe. Escolha um nome diferente."
|
||||
|
||||
msgid "Joplin Export File"
|
||||
msgstr "Arquivo de Exportação do Joplin"
|
||||
@@ -1461,12 +1474,11 @@ msgstr "Diretório de Exportação do Joplin"
|
||||
msgid "Evernote Export File"
|
||||
msgstr "Arquivo de Exportação do Evernote"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Json Export Directory"
|
||||
msgstr "Diretório de Exportação do Joplin"
|
||||
msgstr "Diretório de Exportação JSON"
|
||||
|
||||
msgid "Directory"
|
||||
msgstr "DIretório"
|
||||
msgstr "Diretório"
|
||||
|
||||
#, javascript-format
|
||||
msgid "Cannot load \"%s\" module for format \"%s\""
|
||||
@@ -1541,10 +1553,10 @@ msgid "On %s: %s"
|
||||
msgstr "Em %s: %s"
|
||||
|
||||
msgid "Permission to use camera"
|
||||
msgstr ""
|
||||
msgstr "Permissão para utilizar sua c\\u00e2mera"
|
||||
|
||||
msgid "Your permission to use your camera is required."
|
||||
msgstr ""
|
||||
msgstr "É necessária a sua permissão para utilizar sua c\\u00e2mera."
|
||||
|
||||
msgid "There are currently no notes. Create one by clicking on the (+) button."
|
||||
msgstr "Atualmente, não há notas. Crie uma, clicando no botão (+)."
|
||||
@@ -1574,9 +1586,8 @@ 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"
|
||||
msgstr "Limpar alarme"
|
||||
|
||||
msgid "Save alarm"
|
||||
msgstr "Salvar alarme"
|
||||
@@ -1590,22 +1601,22 @@ msgstr "Confirmar"
|
||||
msgid "Cancel synchronisation"
|
||||
msgstr "Cancelar sincronização"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Checking... Please wait."
|
||||
msgstr "Cancelando... Aguarde."
|
||||
msgstr "Verificando... Por favor aguarde."
|
||||
|
||||
#, fuzzy
|
||||
msgid "Success! Synchronisation configuration appears to be correct."
|
||||
msgstr "Verificar a configuração da sincronização"
|
||||
msgstr "Sucesso! A configuração da sincronização parece estar correta."
|
||||
|
||||
msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
"Erro. Verifique se a URL, nome de usuário, senha, etc., estão corretos e "
|
||||
"tenha certeza que o destino da sincronização está acessível. O erro "
|
||||
"reportado foi:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
msgstr "O aplicativo foi autorizado com sucesso."
|
||||
msgstr "O aplicativo foi autorizado!"
|
||||
|
||||
#, javascript-format
|
||||
msgid ""
|
||||
@@ -1615,10 +1626,15 @@ msgid ""
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
"Não foi possível autorizar o aplicativo:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Por favor tente novamente."
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
#, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
msgstr "Decriptando itens: %d/%d."
|
||||
msgstr "Itens decriptados: %s / %s"
|
||||
|
||||
msgid "New tags:"
|
||||
msgstr "Novas tags:"
|
||||
@@ -1638,8 +1654,8 @@ msgid ""
|
||||
"- Storage: to allow attaching files to notes and to enable filesystem "
|
||||
"synchronisation."
|
||||
msgstr ""
|
||||
"- Armazenamento: para permitir anexar arquivos a notas, e para permitir a "
|
||||
"sincronização do sistema de arquivos "
|
||||
"- Armazenamento: para permitir anexar arquivos a notas e para permitir a "
|
||||
"sincronização do sistema de arquivos."
|
||||
|
||||
msgid "- Camera: to allow taking a picture and attaching it to a note."
|
||||
msgstr "- Câmera: para permitir tirar fotos e anexar a uma nota."
|
||||
@@ -1706,6 +1722,10 @@ msgstr "O app mobile do Joplin não suporta, atualmente, esse tipo de link: %s"
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Tipo de imagem não suportada: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "Anexar foto"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Anexar foto"
|
||||
|
||||
@@ -1731,7 +1751,7 @@ msgid "View on map"
|
||||
msgstr "Ver no mapa"
|
||||
|
||||
msgid "Go to source URL"
|
||||
msgstr ""
|
||||
msgstr "Ir para a URL de origem"
|
||||
|
||||
msgid "Delete notebook"
|
||||
msgstr "Excluir caderno"
|
||||
|
@@ -587,6 +587,10 @@ msgstr "Editați într-un editor extern"
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Căutați în toate notițele"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "Căutați în toate notițele"
|
||||
|
||||
msgid "View"
|
||||
msgstr "Vizualizați"
|
||||
|
||||
@@ -1077,6 +1081,9 @@ msgid ""
|
||||
"synchronisation again may fix the problem."
|
||||
msgstr ""
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Fără denumire"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1164,9 +1171,6 @@ msgstr "data actualizării"
|
||||
msgid "created date"
|
||||
msgstr "data creării"
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Fără denumire"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr ""
|
||||
|
||||
@@ -1241,6 +1245,10 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "Editează notiță."
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr ""
|
||||
|
||||
@@ -1581,6 +1589,10 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr ""
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "Atașează imagine"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Atașează imagine"
|
||||
|
||||
|
@@ -649,6 +649,10 @@ msgstr ""
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Поиск во всех заметках"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "Поиск во всех заметках"
|
||||
|
||||
msgid "View"
|
||||
msgstr "Вид"
|
||||
|
||||
@@ -1163,6 +1167,9 @@ msgstr ""
|
||||
"Не удалось обновить токен: отсутствуют данные аутентификации. Повторный "
|
||||
"запуск синхронизации может решить проблему."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Без имени"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1260,9 +1267,6 @@ msgstr "Обновлено: %d."
|
||||
msgid "created date"
|
||||
msgstr "Создано: %d."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Без имени"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "Эта заметка не содержит информации о геолокации."
|
||||
|
||||
@@ -1338,6 +1342,10 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "Глобальный масштаб в процентах"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "Семейство шрифтов редактора"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "Семейство шрифтов редактора"
|
||||
|
||||
@@ -1700,6 +1708,10 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Неподдерживаемый формат изображения: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "Прикрепить фото"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Прикрепить фото"
|
||||
|
||||
|
@@ -648,6 +648,10 @@ msgstr ""
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Išči znotraj vseh zabeležk"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "Išči znotraj vseh zabeležk"
|
||||
|
||||
msgid "View"
|
||||
msgstr "Pogled"
|
||||
|
||||
@@ -1167,6 +1171,9 @@ msgstr ""
|
||||
"Ne gre osvežiti tokena: manjkajo podatki o avtentikaciji. Ponovno zaženite "
|
||||
"sinhronizacijo, da morda popravite težavo."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Neimenovano"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1264,9 +1271,6 @@ msgstr "Posodobljeno: %d."
|
||||
msgid "created date"
|
||||
msgstr "Ustvarjeno: %d."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Neimenovano"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "Ta zabeležke nima geografske lokacije."
|
||||
|
||||
@@ -1342,6 +1346,10 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "Celokupen procent povečave"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "Družina urejevalnika besedilnega stila"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "Družina urejevalnika besedilnega stila"
|
||||
|
||||
@@ -1703,6 +1711,10 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Nepodprt tip slike: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "Pripni fotografijo"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Pripni fotografijo"
|
||||
|
||||
|
@@ -655,6 +655,10 @@ msgstr "Redigera i extern redigerare"
|
||||
msgid "Search in all the notes"
|
||||
msgstr "Sök i alla anteckningarna"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "Sök i alla anteckningarna"
|
||||
|
||||
msgid "View"
|
||||
msgstr "Visa"
|
||||
|
||||
@@ -1174,6 +1178,9 @@ msgstr ""
|
||||
"Kan inte uppdatera token: autentiseringsdata saknas. Om du startar "
|
||||
"synkroniseringen igen kan det lösa problemet."
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Utan titel"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1268,9 +1275,6 @@ msgstr "uppdaterad datum"
|
||||
msgid "created date"
|
||||
msgstr "Skapad datum"
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "Utan titel"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "Denna anteckning har inte geolokaliseringsinformation."
|
||||
|
||||
@@ -1345,6 +1349,10 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "Global zoomprocent"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "Redigerarens typsnittsfamilj"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "Redigerarens typsnittsfamilj"
|
||||
|
||||
@@ -1719,6 +1727,10 @@ msgstr ""
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "Bildstorlek som inte stöds: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "Bifoga foto"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "Bifoga foto"
|
||||
|
||||
|
@@ -605,7 +605,7 @@ msgid "Italic"
|
||||
msgstr "斜体"
|
||||
|
||||
msgid "Insert Date Time"
|
||||
msgstr ""
|
||||
msgstr "插入时间"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Edit in external editor"
|
||||
@@ -614,6 +614,10 @@ msgstr "在外部编辑器中打开"
|
||||
msgid "Search in all the notes"
|
||||
msgstr "在所有笔记内搜索"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "在所有笔记内搜索"
|
||||
|
||||
msgid "View"
|
||||
msgstr "显示"
|
||||
|
||||
@@ -683,7 +687,7 @@ msgid "No"
|
||||
msgstr "否"
|
||||
|
||||
msgid "Token has been copied to the clipboard!"
|
||||
msgstr ""
|
||||
msgstr "Token 已被复制到粘贴板"
|
||||
|
||||
msgid "The web clipper service is enabled and set to auto-start."
|
||||
msgstr "网页剪辑服务已启用并设置为自动启动。"
|
||||
@@ -735,10 +739,10 @@ msgid "Advanced options"
|
||||
msgstr "显示高级选项"
|
||||
|
||||
msgid "Authorisation token:"
|
||||
msgstr ""
|
||||
msgstr "授权码:"
|
||||
|
||||
msgid "Copy token"
|
||||
msgstr ""
|
||||
msgstr "拷贝 token"
|
||||
|
||||
msgid ""
|
||||
"This authorisation token is only needed to allow third-party applications to "
|
||||
@@ -753,7 +757,7 @@ msgid "Notes and settings are stored in: %s"
|
||||
msgstr "笔记与设置文件储存目录为:%s"
|
||||
|
||||
msgid "Apply"
|
||||
msgstr ""
|
||||
msgstr "应用"
|
||||
|
||||
msgid "Submit"
|
||||
msgstr "提交"
|
||||
@@ -927,13 +931,13 @@ msgid ""
|
||||
msgstr "此处没有任何笔记本。点击\"新笔记本\"创建。"
|
||||
|
||||
msgid "Location"
|
||||
msgstr ""
|
||||
msgstr "位置"
|
||||
|
||||
msgid "URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "Note properties"
|
||||
msgstr ""
|
||||
msgstr "笔记属性"
|
||||
|
||||
msgid "Open..."
|
||||
msgstr "打开…"
|
||||
@@ -949,10 +953,10 @@ msgid "Copy path to clipboard"
|
||||
msgstr "复制路径到剪切板"
|
||||
|
||||
msgid "Copy Link Address"
|
||||
msgstr ""
|
||||
msgstr "拷贝链接地址"
|
||||
|
||||
msgid "This attachment is not downloaded or not decrypted yet."
|
||||
msgstr ""
|
||||
msgstr "该附件没有下载或者没有解密"
|
||||
|
||||
#, javascript-format
|
||||
msgid "Unsupported link or message: %s"
|
||||
@@ -1115,6 +1119,9 @@ msgid ""
|
||||
"synchronisation again may fix the problem."
|
||||
msgstr "无法刷新令牌:缺失认证数据。重新开始同步可能会修复此错误。"
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "无标题"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1208,9 +1215,6 @@ msgstr "更新日期"
|
||||
msgid "created date"
|
||||
msgstr "创建日期"
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "无标题"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "此笔记不包含地理定位信息。"
|
||||
|
||||
@@ -1278,6 +1282,8 @@ msgid ""
|
||||
"this setting so that your notes are constantly being synchronised, thus "
|
||||
"reducing the number of conflicts."
|
||||
msgstr ""
|
||||
"该选项允许 Joplin 在后台运行,如果你的笔记时常发生变化,推荐启用该设置来减少"
|
||||
"可能的冲突"
|
||||
|
||||
msgid "Start application minimised in the tray icon"
|
||||
msgstr "启动应用程序时在托盘中最小化"
|
||||
@@ -1285,6 +1291,10 @@ msgstr "启动应用程序时在托盘中最小化"
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "全局缩放比例"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "编辑器字体"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "编辑器字体"
|
||||
|
||||
@@ -1483,10 +1493,10 @@ msgid "On %s: %s"
|
||||
msgstr "%s:%s"
|
||||
|
||||
msgid "Permission to use camera"
|
||||
msgstr ""
|
||||
msgstr "使用摄像头的权限"
|
||||
|
||||
msgid "Your permission to use your camera is required."
|
||||
msgstr ""
|
||||
msgstr "使用摄像头的权限是必须的"
|
||||
|
||||
msgid "There are currently no notes. Create one by clicking on the (+) button."
|
||||
msgstr "当前没有任何笔记。点击(+)按钮创建。"
|
||||
@@ -1544,6 +1554,8 @@ msgid ""
|
||||
"Error. Please check that URL, username, password, etc. are correct and that "
|
||||
"the sync target is accessible. The reported error was:"
|
||||
msgstr ""
|
||||
"发生错误。请检查 URL,用户名,密码等等是否正确并且确保同步目的地可被访问。报"
|
||||
"告的错误如下:"
|
||||
|
||||
#, fuzzy
|
||||
msgid "The application has been authorised!"
|
||||
@@ -1557,6 +1569,11 @@ msgid ""
|
||||
"\n"
|
||||
"Please try again."
|
||||
msgstr ""
|
||||
"无法授权应用:\n"
|
||||
"\n"
|
||||
"%s\n"
|
||||
"\n"
|
||||
"请重新尝试"
|
||||
|
||||
#, fuzzy, javascript-format
|
||||
msgid "Decrypted items: %s / %s"
|
||||
@@ -1643,6 +1660,10 @@ msgstr "Joplin 手机应用目前不支持这种类型的链接:%s"
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "不支持的图片格式:%s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "附加照片"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "附加照片"
|
||||
|
||||
@@ -1668,7 +1689,7 @@ msgid "View on map"
|
||||
msgstr "查看地图"
|
||||
|
||||
msgid "Go to source URL"
|
||||
msgstr ""
|
||||
msgstr "定位到源 URL"
|
||||
|
||||
msgid "Delete notebook"
|
||||
msgstr "删除笔记本"
|
||||
|
@@ -614,6 +614,10 @@ msgstr "使用外部編輯器編輯"
|
||||
msgid "Search in all the notes"
|
||||
msgstr "在所有記事中搜尋"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Search in current note"
|
||||
msgstr "在所有記事中搜尋"
|
||||
|
||||
msgid "View"
|
||||
msgstr "檢視"
|
||||
|
||||
@@ -1115,6 +1119,9 @@ msgid ""
|
||||
"synchronisation again may fix the problem."
|
||||
msgstr "無法刷新 token: 缺少身份驗證資料。再次啟動同步可能會解決此問題。"
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "未命名"
|
||||
|
||||
msgid ""
|
||||
"Could not synchronize with OneDrive.\n"
|
||||
"\n"
|
||||
@@ -1207,9 +1214,6 @@ msgstr "更新日期"
|
||||
msgid "created date"
|
||||
msgstr "建立日期"
|
||||
|
||||
msgid "Untitled"
|
||||
msgstr "未命名"
|
||||
|
||||
msgid "This note does not have geolocation information."
|
||||
msgstr "此記事沒有地理位置定位資訊。"
|
||||
|
||||
@@ -1284,6 +1288,10 @@ msgstr ""
|
||||
msgid "Global zoom percentage"
|
||||
msgstr "整體縮放比例 (%)"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Editor font size"
|
||||
msgstr "編輯器字型系列"
|
||||
|
||||
msgid "Editor font family"
|
||||
msgstr "編輯器字型系列"
|
||||
|
||||
@@ -1640,6 +1648,10 @@ msgstr "Joplin 移動應用程式暫時不支援此類型的連結: %s"
|
||||
msgid "Unsupported image type: %s"
|
||||
msgstr "不支援的圖像類型: %s"
|
||||
|
||||
#, fuzzy
|
||||
msgid "Take photo"
|
||||
msgstr "附加相片"
|
||||
|
||||
msgid "Attach photo"
|
||||
msgstr "附加相片"
|
||||
|
||||
|
7
CliClient/package-lock.json
generated
7
CliClient/package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "joplin",
|
||||
"version": "1.0.117",
|
||||
"version": "1.0.118",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -2890,11 +2890,6 @@
|
||||
"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.117",
|
||||
"version": "1.0.118",
|
||||
"bin": {
|
||||
"joplin": "./main.js"
|
||||
},
|
||||
@@ -68,7 +68,6 @@
|
||||
"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",
|
||||
|
@@ -30,6 +30,7 @@ npm test tests-build/models_Folder.js
|
||||
npm test tests-build/models_Note.js
|
||||
npm test tests-build/models_Tag.js
|
||||
npm test tests-build/models_Setting.js
|
||||
npm test tests-build/pathUtils.js
|
||||
npm test tests-build/services_InteropService.js
|
||||
npm test tests-build/services_ResourceService.js
|
||||
npm test tests-build/urlUtils.js
|
||||
|
@@ -5,6 +5,7 @@ const { asyncTest, fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const ArrayUtils = require('lib/ArrayUtils.js');
|
||||
const { shim } = require('lib/shim');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
@@ -35,13 +36,36 @@ describe('models_Note', function() {
|
||||
expect(items[0].type_).toBe(BaseModel.TYPE_NOTE);
|
||||
expect(items[1].type_).toBe(BaseModel.TYPE_RESOURCE);
|
||||
|
||||
const resource = items[1];
|
||||
note2.body += '<img alt="bla" src=":/' + resource.id + '"/>';
|
||||
note2.body += '<img src=\':/' + resource.id + '\' />';
|
||||
const resource2 = await shim.createResourceFromPath(__dirname + '/../tests/support/photo.jpg');
|
||||
const resource3 = await shim.createResourceFromPath(__dirname + '/../tests/support/photo.jpg');
|
||||
note2.body += '<img alt="bla" src=":/' + resource2.id + '"/>';
|
||||
note2.body += '<img src=\':/' + resource3.id + '\' />';
|
||||
items = await Note.linkedItems(note2.body);
|
||||
expect(items.length).toBe(4);
|
||||
}));
|
||||
|
||||
it('should find linked items', asyncTest(async () => {
|
||||
const testCases = [
|
||||
['[](:/06894e83b8f84d3d8cbe0f1587f9e226)', ['06894e83b8f84d3d8cbe0f1587f9e226']],
|
||||
['[](:/06894e83b8f84d3d8cbe0f1587f9e226) [](:/06894e83b8f84d3d8cbe0f1587f9e226)', ['06894e83b8f84d3d8cbe0f1587f9e226']],
|
||||
['[](:/06894e83b8f84d3d8cbe0f1587f9e226) [](:/06894e83b8f84d3d8cbe0f1587f9e227)', ['06894e83b8f84d3d8cbe0f1587f9e226', '06894e83b8f84d3d8cbe0f1587f9e227']],
|
||||
['[](:/06894e83b8f84d3d8cbe0f1587f9e226 "some title")', ['06894e83b8f84d3d8cbe0f1587f9e226']],
|
||||
];
|
||||
|
||||
for (let i = 0; i < testCases.length; i++) {
|
||||
const t = testCases[i];
|
||||
|
||||
const input = t[0];
|
||||
const expected = t[1];
|
||||
const actual = Note.linkedItemIds(input);
|
||||
const contentEquals = ArrayUtils.contentEquals(actual, expected);
|
||||
|
||||
// console.info(contentEquals, input, expected, actual);
|
||||
|
||||
expect(contentEquals).toBe(true);
|
||||
}
|
||||
}));
|
||||
|
||||
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 });
|
||||
|
39
CliClient/tests/pathUtils.js
Normal file
39
CliClient/tests/pathUtils.js
Normal file
@@ -0,0 +1,39 @@
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { friendlySafeFilename } = require('lib/path-utils.js');
|
||||
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
describe('pathUtils', function() {
|
||||
|
||||
beforeEach(async (done) => {
|
||||
done();
|
||||
});
|
||||
|
||||
it('should create friendly safe filename', async (done) => {
|
||||
const testCases = [
|
||||
['生活', '生活'],
|
||||
['not/good', 'not_good'],
|
||||
['really/not/good', 'really_not_good'],
|
||||
['con', '___'],
|
||||
['no space at the end ', 'no space at the end'],
|
||||
['nor dots...', 'nor dots'],
|
||||
[' no space before either', 'no space before either'],
|
||||
['thatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylong', 'thatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylongthatsreallylong'],
|
||||
];
|
||||
|
||||
for (let i = 0; i < testCases.length; i++) {
|
||||
const t = testCases[i];
|
||||
expect(friendlySafeFilename(t[0])).toBe(t[1]);
|
||||
}
|
||||
|
||||
expect(!!friendlySafeFilename('')).toBe(true);
|
||||
expect(!!friendlySafeFilename('...')).toBe(true);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
@@ -331,4 +331,30 @@ describe('services_InteropService', function() {
|
||||
expect(obj.body).toBe(items[i].body);
|
||||
}
|
||||
}));
|
||||
|
||||
it('should export MD with unicode filenames', asyncTest(async () => {
|
||||
const service = new InteropService();
|
||||
let folder1 = await Folder.save({ title: 'folder1' });
|
||||
let folder2 = await Folder.save({ title: 'ジョプリン' });
|
||||
let note1 = await Note.save({ title: '生活', parent_id: folder1.id });
|
||||
let note2 = await Note.save({ title: '生活', parent_id: folder1.id });
|
||||
let note2b = await Note.save({ title: '生活', parent_id: folder1.id });
|
||||
let note3 = await Note.save({ title: '', parent_id: folder1.id });
|
||||
let note4 = await Note.save({ title: '', parent_id: folder1.id });
|
||||
let note5 = await Note.save({ title: 'salut, ça roule ?', parent_id: folder1.id });
|
||||
let note6 = await Note.save({ title: 'ジョプリン', parent_id: folder2.id });
|
||||
|
||||
const outDir = exportDir();
|
||||
|
||||
await service.export({ path: outDir, format: 'md' });
|
||||
|
||||
expect(await shim.fsDriver().exists(outDir + '/folder1/生活.md')).toBe(true);
|
||||
expect(await shim.fsDriver().exists(outDir + '/folder1/生活 (1).md')).toBe(true);
|
||||
expect(await shim.fsDriver().exists(outDir + '/folder1/生活 (2).md')).toBe(true);
|
||||
expect(await shim.fsDriver().exists(outDir + '/folder1/Untitled.md')).toBe(true);
|
||||
expect(await shim.fsDriver().exists(outDir + '/folder1/Untitled (1).md')).toBe(true);
|
||||
expect(await shim.fsDriver().exists(outDir + '/folder1/salut, ça roule _.md')).toBe(true);
|
||||
expect(await shim.fsDriver().exists(outDir + '/ジョプリン/ジョプリン.md')).toBe(true);
|
||||
}));
|
||||
|
||||
});
|
@@ -8,6 +8,7 @@ const Note = require('lib/models/Note.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const NoteTag = require('lib/models/NoteTag.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const ItemChange = require('lib/models/ItemChange.js');
|
||||
const NoteResource = require('lib/models/NoteResource.js');
|
||||
const ResourceService = require('lib/services/ResourceService.js');
|
||||
const fs = require('fs-extra');
|
||||
@@ -124,4 +125,25 @@ describe('services_ResourceService', function() {
|
||||
expect(!!(await Resource.load(resource1.id))).toBe(true);
|
||||
}));
|
||||
|
||||
it('should not process twice the same change', asyncTest(async () => {
|
||||
const service = new ResourceService();
|
||||
|
||||
let folder1 = await Folder.save({ title: "folder1" });
|
||||
let note1 = await Note.save({ title: 'ma note', parent_id: folder1.id });
|
||||
note1 = await shim.attachFileToNote(note1, __dirname + '/../tests/support/photo.jpg');
|
||||
let resource1 = (await Resource.all())[0];
|
||||
|
||||
await service.indexNoteResources();
|
||||
|
||||
const before = (await NoteResource.all())[0];
|
||||
|
||||
await time.sleep(0.1);
|
||||
|
||||
await service.indexNoteResources();
|
||||
|
||||
const after = (await NoteResource.all())[0];
|
||||
|
||||
expect(before.last_seen_time).toBe(after.last_seen_time);
|
||||
}));
|
||||
|
||||
});
|
188
CliClient/tests/services_SearchEngine.js
Normal file
188
CliClient/tests/services_SearchEngine.js
Normal file
@@ -0,0 +1,188 @@
|
||||
require('app-module-path').addPath(__dirname);
|
||||
|
||||
const { time } = require('lib/time-utils.js');
|
||||
const { fileContentEqual, setupDatabase, setupDatabaseAndSynchronizer, db, synchronizer, fileApi, sleep, clearDatabase, switchClient, syncTargetId, objectsEqual, checkThrowAsync } = require('test-utils.js');
|
||||
const SearchEngine = require('lib/services/SearchEngine');
|
||||
const Note = require('lib/models/Note');
|
||||
|
||||
process.on('unhandledRejection', (reason, p) => {
|
||||
console.log('Unhandled Rejection at: Promise', p, 'reason:', reason);
|
||||
});
|
||||
|
||||
let engine = null;
|
||||
|
||||
describe('services_SearchEngine', function() {
|
||||
|
||||
beforeEach(async (done) => {
|
||||
await setupDatabaseAndSynchronizer(1);
|
||||
await switchClient(1);
|
||||
|
||||
engine = new SearchEngine();
|
||||
engine.setDb(db());
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should keep the content and FTS table in sync', async (done) => {
|
||||
let rows, n1, n2, n3;
|
||||
|
||||
n1 = await Note.save({ title: "a" });
|
||||
n2 = await Note.save({ title: "b" });
|
||||
rows = await engine.search('a');
|
||||
expect(rows.length).toBe(1);
|
||||
expect(rows[0].title).toBe('a');
|
||||
|
||||
await Note.delete(n1.id);
|
||||
rows = await engine.search('a');
|
||||
expect(rows.length).toBe(0);
|
||||
rows = await engine.search('b');
|
||||
expect(rows[0].title).toBe('b');
|
||||
|
||||
await Note.save({ id: n2.id, title: 'c' });
|
||||
rows = await engine.search('b');
|
||||
expect(rows.length).toBe(0);
|
||||
rows = await engine.search('c');
|
||||
expect(rows[0].title).toBe('c');
|
||||
|
||||
await Note.save({ id: n2.id, encryption_applied: 1 });
|
||||
rows = await engine.search('c');
|
||||
expect(rows.length).toBe(0);
|
||||
|
||||
await Note.save({ id: n2.id, encryption_applied: 0 });
|
||||
rows = await engine.search('c');
|
||||
expect(rows.length).toBe(1);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should order search results by relevance (1)', async (done) => {
|
||||
const n1 = await Note.save({ title: "abcd efgh" }); // 3
|
||||
const n2 = await Note.save({ title: "abcd aaaaa abcd abcd" }); // 1
|
||||
const n3 = await Note.save({ title: "abcd aaaaa bbbb eeee abcd" }); // 2
|
||||
|
||||
const rows = await engine.search('abcd');
|
||||
|
||||
expect(rows[0].id).toBe(n2.id);
|
||||
expect(rows[1].id).toBe(n3.id);
|
||||
expect(rows[2].id).toBe(n1.id);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should order search results by relevance (2)', async (done) => {
|
||||
// 1
|
||||
const n1 = await Note.save({ title: "abcd efgh", body: "XX abcd XX efgh" });
|
||||
// 4
|
||||
const n2 = await Note.save({ title: "abcd aaaaa bbbb eeee efgh" });
|
||||
// 3
|
||||
const n3 = await Note.save({ title: "abcd aaaaa efgh" });
|
||||
// 2
|
||||
const n4 = await Note.save({ title: "blablablabla blabla bla abcd X efgh" });
|
||||
// 5
|
||||
const n5 = await Note.save({ title: "occurence many times but very abcd spread appart spread appart spread appart spread appart spread appart efgh occurence many times but very abcd spread appart spread appart spread appart spread appart spread appart efgh occurence many times but very abcd spread appart spread appart spread appart spread appart spread appart efgh occurence many times but very abcd spread appart spread appart spread appart spread appart spread appart efgh occurence many times but very abcd spread appart spread appart spread appart spread appart spread appart efgh" });
|
||||
|
||||
const rows = await engine.search('abcd efgh');
|
||||
|
||||
expect(rows[0].id).toBe(n1.id);
|
||||
expect(rows[1].id).toBe(n4.id);
|
||||
expect(rows[2].id).toBe(n3.id);
|
||||
expect(rows[3].id).toBe(n2.id);
|
||||
expect(rows[4].id).toBe(n5.id);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should supports various query types', async (done) => {
|
||||
let rows;
|
||||
|
||||
const n1 = await Note.save({ title: "abcd efgh ijkl", body: "aaaa bbbb" });
|
||||
const n2 = await Note.save({ title: "iiii efgh bbbb", body: "aaaa bbbb" });
|
||||
|
||||
rows = await engine.search('abcd ijkl');
|
||||
expect(rows.length).toBe(1);
|
||||
|
||||
rows = await engine.search('"abcd ijkl"');
|
||||
expect(rows.length).toBe(0);
|
||||
|
||||
rows = await engine.search('"abcd efgh"');
|
||||
expect(rows.length).toBe(1);
|
||||
|
||||
rows = await engine.search('title:abcd');
|
||||
expect(rows.length).toBe(1);
|
||||
|
||||
rows = await engine.search('title:efgh');
|
||||
expect(rows.length).toBe(2);
|
||||
|
||||
rows = await engine.search('body:abcd');
|
||||
expect(rows.length).toBe(0);
|
||||
|
||||
rows = await engine.search('body:bbbb');
|
||||
expect(rows.length).toBe(2);
|
||||
|
||||
rows = await engine.search('body:bbbb iiii');
|
||||
expect(rows.length).toBe(1);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should parse normal query strings', async (done) => {
|
||||
let rows;
|
||||
|
||||
const testCases = [
|
||||
['abcd efgh', { _: ['abcd', 'efgh'] }],
|
||||
['abcd efgh', { _: ['abcd', 'efgh'] }],
|
||||
['title:abcd efgh', { _: ['efgh'], title: ['abcd'] }],
|
||||
['title:abcd', { title: ['abcd'] }],
|
||||
['"abcd efgh"', { _: ['abcd efgh'] }],
|
||||
];
|
||||
|
||||
for (let i = 0; i < testCases.length; i++) {
|
||||
const t = testCases[i];
|
||||
const input = t[0];
|
||||
const expected = t[1];
|
||||
const actual = engine.parseQuery(input);
|
||||
|
||||
expect(JSON.stringify(actual.terms._)).toBe(JSON.stringify(expected._));
|
||||
expect(JSON.stringify(actual.terms.title)).toBe(JSON.stringify(expected.title));
|
||||
expect(JSON.stringify(actual.terms.body)).toBe(JSON.stringify(expected.body));
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should parse query strings with wildcards', async (done) => {
|
||||
let rows;
|
||||
|
||||
const testCases = [
|
||||
['do*', ['do', 'dog', 'domino'], [] ],
|
||||
// "*" is a wildcard only when used at the end (to searhc for documents with the specified prefix)
|
||||
// If it's at the beginning, it's ignored, if it's in the middle, it's treated as a litteral "*".
|
||||
['*an*', ['an', 'anneau'], ['piano', 'plan'] ],
|
||||
['no*no', ['no*no'], ['nonono'] ],
|
||||
];
|
||||
|
||||
for (let i = 0; i < testCases.length; i++) {
|
||||
const t = testCases[i];
|
||||
const input = t[0];
|
||||
const shouldMatch = t[1];
|
||||
const shouldNotMatch = t[2];
|
||||
const regex = new RegExp(engine.parseQuery(input).terms._[0].value, 'gmi');
|
||||
|
||||
for (let j = 0; j < shouldMatch.length; j++) {
|
||||
const r = shouldMatch[j].match(regex);
|
||||
expect(!!r).toBe(true, '"' + input + '" should match "' + shouldMatch[j] + '"');
|
||||
}
|
||||
|
||||
// for (let j = 0; j < shouldNotMatch.length; j++) {
|
||||
// const r = shouldNotMatch[j].match(regex);
|
||||
// // console.info(input, shouldNotMatch)
|
||||
// expect(!!r).toBe(false, '"' + input + '" should not match "' + shouldNotMatch[j] + '"');
|
||||
// }
|
||||
}
|
||||
|
||||
expect(engine.parseQuery('*').termCount).toBe(0);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
@@ -4,6 +4,7 @@ const { DatabaseDriverNode } = require('lib/database-driver-node.js');
|
||||
const BaseModel = require('lib/BaseModel.js');
|
||||
const Folder = require('lib/models/Folder.js');
|
||||
const Note = require('lib/models/Note.js');
|
||||
const ItemChange = require('lib/models/ItemChange.js');
|
||||
const Resource = require('lib/models/Resource.js');
|
||||
const Tag = require('lib/models/Tag.js');
|
||||
const NoteTag = require('lib/models/NoteTag.js');
|
||||
@@ -122,6 +123,8 @@ async function switchClient(id) {
|
||||
async function clearDatabase(id = null) {
|
||||
if (id === null) id = currentClient_;
|
||||
|
||||
await ItemChange.waitForAllSaved();
|
||||
|
||||
let queries = [
|
||||
'DELETE FROM notes',
|
||||
'DELETE FROM folders',
|
||||
|
@@ -18,7 +18,10 @@ class AppComponent extends Component {
|
||||
});
|
||||
|
||||
this.confirm_click = () => {
|
||||
bridge().sendContentToJoplin(this.props.clippedContent);
|
||||
const content = Object.assign({}, this.props.clippedContent);
|
||||
content.tags = this.state.selectedTags.join(',');
|
||||
content.parent_id = this.props.selectedFolderId;
|
||||
bridge().sendContentToJoplin(content);
|
||||
}
|
||||
|
||||
this.contentTitle_change = (event) => {
|
||||
@@ -31,24 +34,18 @@ class AppComponent extends Component {
|
||||
this.clipSimplified_click = () => {
|
||||
bridge().sendCommandToActiveTab({
|
||||
name: 'simplifiedPageHtml',
|
||||
parent_id: this.props.selectedFolderId,
|
||||
tags: this.state.selectedTags.join(','),
|
||||
});
|
||||
}
|
||||
|
||||
this.clipComplete_click = () => {
|
||||
bridge().sendCommandToActiveTab({
|
||||
name: 'completePageHtml',
|
||||
parent_id: this.props.selectedFolderId,
|
||||
tags: this.state.selectedTags.join(','),
|
||||
});
|
||||
}
|
||||
|
||||
this.clipSelection_click = () => {
|
||||
bridge().sendCommandToActiveTab({
|
||||
name: 'selectedHtml',
|
||||
parent_id: this.props.selectedFolderId,
|
||||
tags: this.state.selectedTags.join(','),
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -23,8 +23,9 @@ const DecryptionWorker = require('lib/services/DecryptionWorker');
|
||||
const InteropService = require('lib/services/InteropService');
|
||||
const InteropServiceHelper = require('./InteropServiceHelper.js');
|
||||
const ResourceService = require('lib/services/ResourceService');
|
||||
const SearchEngine = require('lib/services/SearchEngine');
|
||||
const ClipperServer = require('lib/ClipperServer');
|
||||
|
||||
const ExternalEditWatcher = require('lib/services/ExternalEditWatcher');
|
||||
const { bridge } = require('electron').remote.require('./bridge');
|
||||
const Menu = bridge().Menu;
|
||||
const MenuItem = bridge().MenuItem;
|
||||
@@ -459,14 +460,27 @@ class Application extends BaseApplication {
|
||||
name: 'commandStartExternalEditing',
|
||||
});
|
||||
},
|
||||
}, {
|
||||
type: 'separator',
|
||||
screens: ['Main'],
|
||||
}, {
|
||||
label: _('Search in all the notes'),
|
||||
screens: ['Main'],
|
||||
accelerator: 'F6',
|
||||
click: () => {
|
||||
this.dispatch({
|
||||
type: 'WINDOW_COMMAND',
|
||||
name: 'focus_search',
|
||||
});
|
||||
},
|
||||
}, {
|
||||
label: _('Search in current note'),
|
||||
screens: ['Main'],
|
||||
accelerator: 'CommandOrControl+F',
|
||||
click: () => {
|
||||
this.dispatch({
|
||||
type: 'WINDOW_COMMAND',
|
||||
name: 'focus_search',
|
||||
name: 'showLocalSearch',
|
||||
});
|
||||
},
|
||||
}],
|
||||
@@ -780,6 +794,9 @@ class Application extends BaseApplication {
|
||||
|
||||
ResourceService.runInBackground();
|
||||
|
||||
SearchEngine.instance().setDb(reg.db());
|
||||
SearchEngine.instance().setLogger(reg.logger());
|
||||
|
||||
if (Setting.value('env') === 'dev') {
|
||||
AlarmService.updateAllNotifications();
|
||||
} else {
|
||||
@@ -802,6 +819,9 @@ class Application extends BaseApplication {
|
||||
if (Setting.value('clipperServer.autoStart')) {
|
||||
ClipperServer.instance().start();
|
||||
}
|
||||
|
||||
ExternalEditWatcher.instance().setLogger(reg.logger());
|
||||
ExternalEditWatcher.instance().dispatch = this.store().dispatch;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -13,9 +13,16 @@ const InteropService = require('lib/services/InteropService');
|
||||
const InteropServiceHelper = require('../InteropServiceHelper.js');
|
||||
const Search = require('lib/models/Search');
|
||||
const Mark = require('mark.js/dist/mark.min.js');
|
||||
const SearchEngine = require('lib/services/SearchEngine');
|
||||
|
||||
class NoteListComponent extends React.Component {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.itemRenderer = this.itemRenderer.bind(this);
|
||||
}
|
||||
|
||||
style() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
|
||||
@@ -169,7 +176,10 @@ class NoteListComponent extends React.Component {
|
||||
menu.popup(bridge().window());
|
||||
}
|
||||
|
||||
itemRenderer(item, theme, width) {
|
||||
itemRenderer(item) {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
const width = this.props.style.width;
|
||||
|
||||
const onTitleClick = async (event, item) => {
|
||||
if (event.ctrlKey) {
|
||||
event.preventDefault();
|
||||
@@ -225,8 +235,11 @@ class NoteListComponent extends React.Component {
|
||||
|
||||
let highlightedWords = [];
|
||||
if (this.props.notesParentType === 'Search') {
|
||||
const search = BaseModel.byId(this.props.searches, this.props.selectedSearchId);
|
||||
highlightedWords = search ? Search.keywords(search.query_pattern) : [];
|
||||
const query = BaseModel.byId(this.props.searches, this.props.selectedSearchId);
|
||||
if (query) {
|
||||
const parsedQuery = SearchEngine.instance().parseQuery(query.query_pattern);
|
||||
highlightedWords = SearchEngine.instance().allParsedQueryTerms(parsedQuery);
|
||||
}
|
||||
}
|
||||
|
||||
let style = Object.assign({ width: width }, this.style().listItem);
|
||||
@@ -257,7 +270,18 @@ class NoteListComponent extends React.Component {
|
||||
exclude: ['img'],
|
||||
acrossElements: true,
|
||||
});
|
||||
mark.mark(highlightedWords);
|
||||
|
||||
mark.unmark();
|
||||
|
||||
for (let i = 0; i < highlightedWords.length; i++) {
|
||||
const w = highlightedWords[i];
|
||||
|
||||
if (w.type === 'regex') {
|
||||
mark.markRegExp(new RegExp(w.value, 'gmi'), { acrossElements: true });
|
||||
} else {
|
||||
mark.mark([w]);
|
||||
}
|
||||
}
|
||||
|
||||
// Note: in this case it is safe to use dangerouslySetInnerHTML because titleElement
|
||||
// is a span tag that we created and that contains data that's been inserted as plain text
|
||||
@@ -269,6 +293,14 @@ class NoteListComponent extends React.Component {
|
||||
titleComp = <span>{displayTitle}</span>
|
||||
}
|
||||
|
||||
const watchedIconStyle = {
|
||||
paddingRight: 4,
|
||||
color: theme.color,
|
||||
};
|
||||
const watchedIcon = this.props.watchedNoteFiles.indexOf(item.id) < 0 ? null : (
|
||||
<i style={watchedIconStyle} className={"fa fa-external-link"}></i>
|
||||
);
|
||||
|
||||
// Need to include "todo_completed" in key so that checkbox is updated when
|
||||
// item is changed via sync.
|
||||
return <div key={item.id + '_' + item.todo_completed} style={style}>
|
||||
@@ -283,6 +315,7 @@ class NoteListComponent extends React.Component {
|
||||
onDragStart={(event) => onDragStart(event) }
|
||||
data-id={item.id}
|
||||
>
|
||||
{watchedIcon}
|
||||
{titleComp}
|
||||
</a>
|
||||
</div>
|
||||
@@ -313,7 +346,7 @@ class NoteListComponent extends React.Component {
|
||||
style={style}
|
||||
className={"note-list"}
|
||||
items={notes}
|
||||
itemRenderer={ (item) => { return this.itemRenderer(item, theme, style.width) } }
|
||||
itemRenderer={this.itemRenderer}
|
||||
></ItemList>
|
||||
);
|
||||
}
|
||||
@@ -329,6 +362,7 @@ const mapStateToProps = (state) => {
|
||||
notesParentType: state.notesParentType,
|
||||
searches: state.searches,
|
||||
selectedSearchId: state.selectedSearchId,
|
||||
watchedNoteFiles: state.watchedNoteFiles,
|
||||
};
|
||||
};
|
||||
|
||||
|
@@ -110,12 +110,12 @@ class NotePropertiesDialog extends React.Component {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
backgroundColor: 'rgba(0,0,0,0.6)',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'flex-start',
|
||||
justifyContent: 'center',
|
||||
};
|
||||
|
||||
this.styles_.dialogBox = {
|
||||
backgroundColor: 'white',
|
||||
backgroundColor: theme.backgroundColor,
|
||||
padding: 16,
|
||||
boxShadow: '6px 6px 20px rgba(0,0,0,0.5)',
|
||||
marginTop: 20,
|
||||
@@ -123,17 +123,33 @@ class NotePropertiesDialog extends React.Component {
|
||||
|
||||
this.styles_.controlBox = {
|
||||
marginBottom: '1em',
|
||||
color: 'black', //This will apply for the calendar
|
||||
};
|
||||
|
||||
this.styles_.button = {
|
||||
minWidth: theme.buttonMinWidth,
|
||||
minHeight: theme.buttonMinHeight,
|
||||
marginLeft: 5,
|
||||
color: theme.color,
|
||||
backgroundColor: theme.backgroundColor,
|
||||
border: '1px solid',
|
||||
borderColor: theme.dividerColor,
|
||||
};
|
||||
|
||||
this.styles_.editPropertyButton = {
|
||||
color: theme.color,
|
||||
textDecoration: 'none',
|
||||
backgroundColor: theme.backgroundColor,
|
||||
border: '1px solid',
|
||||
borderColor: theme.dividerColor,
|
||||
};
|
||||
|
||||
this.styles_.input = {
|
||||
display:'inline-block',
|
||||
color: theme.color,
|
||||
backgroundColor: theme.backgroundColor,
|
||||
border: '1px solid',
|
||||
borderColor: theme.dividerColor,
|
||||
};
|
||||
|
||||
this.styles_.dialogTitle = Object.assign({}, theme.h1Style, { marginBottom: '1.2em' });
|
||||
@@ -239,7 +255,8 @@ class NotePropertiesDialog extends React.Component {
|
||||
dateFormat={time.dateFormat()}
|
||||
timeFormat={time.timeFormat()}
|
||||
inputProps={{
|
||||
onKeyDown: (event) => onKeyDown(event, key)
|
||||
onKeyDown: (event) => onKeyDown(event, key),
|
||||
style: styles.input
|
||||
}}
|
||||
onChange={(momentObject) => {this.setState({ editedValue: momentObject })}}
|
||||
/>
|
||||
@@ -254,7 +271,7 @@ class NotePropertiesDialog extends React.Component {
|
||||
ref="editField"
|
||||
onChange={(event) => {this.setState({ editedValue: event.target.value })}}
|
||||
onKeyDown={(event) => onKeyDown(event)}
|
||||
style={{display:'inline-block'}}
|
||||
style={styles.input}
|
||||
/>
|
||||
}
|
||||
} else {
|
||||
|
122
ElectronClient/app/gui/NoteSearchBar.jsx
Normal file
122
ElectronClient/app/gui/NoteSearchBar.jsx
Normal file
@@ -0,0 +1,122 @@
|
||||
const React = require('react');
|
||||
const { connect } = require('react-redux');
|
||||
const { themeStyle } = require('../theme.js');
|
||||
const { _ } = require('lib/locale.js');
|
||||
|
||||
class NoteSearchBarComponent extends React.Component {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
query: '',
|
||||
};
|
||||
|
||||
this.searchInput_change = this.searchInput_change.bind(this);
|
||||
this.previousButton_click = this.previousButton_click.bind(this);
|
||||
this.nextButton_click = this.nextButton_click.bind(this);
|
||||
this.closeButton_click = this.closeButton_click.bind(this);
|
||||
}
|
||||
|
||||
style() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
|
||||
let style = {
|
||||
root: Object.assign({}, theme.textStyle, {
|
||||
backgroundColor: theme.backgroundColor,
|
||||
color: theme.colorFaded,
|
||||
}),
|
||||
};
|
||||
|
||||
return style;
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.refs.searchInput.focus();
|
||||
}
|
||||
|
||||
buttonIconComponent(iconName, clickHandler) {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
|
||||
const searchButton = {
|
||||
paddingLeft: 4,
|
||||
paddingRight: 4,
|
||||
paddingTop: 2,
|
||||
paddingBottom: 2,
|
||||
textDecoration: 'none',
|
||||
marginRight: 5,
|
||||
};
|
||||
|
||||
const iconStyle = {
|
||||
display: 'flex',
|
||||
fontSize: Math.round(theme.fontSize) * 1.2,
|
||||
color: theme.color,
|
||||
};
|
||||
|
||||
const icon = <i style={iconStyle} className={"fa " + iconName}></i>
|
||||
|
||||
return (
|
||||
<a
|
||||
href="#"
|
||||
style={searchButton}
|
||||
onClick={clickHandler}
|
||||
>{icon}</a>
|
||||
);
|
||||
}
|
||||
|
||||
searchInput_change(event) {
|
||||
const query = event.currentTarget.value;
|
||||
this.setState({ query: query });
|
||||
this.triggerOnChange(query);
|
||||
}
|
||||
|
||||
previousButton_click(event) {
|
||||
if (this.props.onPrevious) this.props.onPrevious();
|
||||
}
|
||||
|
||||
nextButton_click(event) {
|
||||
if (this.props.onNext) this.props.onNext();
|
||||
}
|
||||
|
||||
closeButton_click(event) {
|
||||
if (this.props.onClose) this.props.onClose();
|
||||
}
|
||||
|
||||
triggerOnChange(query) {
|
||||
if (this.props.onChange) this.props.onChange(query);
|
||||
}
|
||||
|
||||
focus() {
|
||||
this.refs.searchInput.focus();
|
||||
}
|
||||
|
||||
render() {
|
||||
const theme = themeStyle(this.props.theme);
|
||||
|
||||
const closeButton = this.buttonIconComponent('fa-times', this.closeButton_click);
|
||||
const previousButton = this.buttonIconComponent('fa-chevron-up', this.previousButton_click);
|
||||
const nextButton = this.buttonIconComponent('fa-chevron-down', this.nextButton_click);
|
||||
|
||||
return (
|
||||
<div style={this.props.style}>
|
||||
<div style={{display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
{ closeButton }
|
||||
<input placeholder={_('Search...')} value={this.state.query} onChange={this.searchInput_change} ref="searchInput" type="text" style={{width: 200, marginRight: 5}}></input>
|
||||
{ nextButton }
|
||||
{ previousButton }
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => {
|
||||
return {
|
||||
theme: state.settings.theme,
|
||||
};
|
||||
};
|
||||
|
||||
const NoteSearchBar = connect(mapStateToProps, null, null, { withRef: true })(NoteSearchBarComponent);
|
||||
|
||||
module.exports = NoteSearchBar;
|
@@ -25,13 +25,16 @@ const fs = require('fs-extra');
|
||||
const md5 = require('md5');
|
||||
const mimeUtils = require('lib/mime-utils.js').mime;
|
||||
const ArrayUtils = require('lib/ArrayUtils');
|
||||
const ObjectUtils = require('lib/ObjectUtils');
|
||||
const urlUtils = require('lib/urlUtils');
|
||||
const dialogs = require('./dialogs');
|
||||
const NoteSearchBar = require('./NoteSearchBar.min.js');
|
||||
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');
|
||||
const SearchEngine = require('lib/services/SearchEngine');
|
||||
|
||||
require('brace/mode/markdown');
|
||||
// https://ace.c9.io/build/kitchen-sink.html
|
||||
@@ -39,11 +42,19 @@ require('brace/mode/markdown');
|
||||
require('brace/theme/chrome');
|
||||
require('brace/theme/twilight');
|
||||
|
||||
const NOTE_TAG_BAR_FEATURE_ENABLED = false;
|
||||
|
||||
class NoteTextComponent extends React.Component {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.localSearchDefaultState = {
|
||||
query: '',
|
||||
selectedIndex: 0,
|
||||
resultCount: 0,
|
||||
};
|
||||
|
||||
this.state = {
|
||||
note: null,
|
||||
noteMetadata: '',
|
||||
@@ -63,6 +74,8 @@ class NoteTextComponent extends React.Component {
|
||||
newAndNoTitleChangeNoteId: null,
|
||||
bodyHtml: '',
|
||||
lastKeys: [],
|
||||
showLocalSearch: false,
|
||||
localSearch: Object.assign({}, this.localSearchDefaultState),
|
||||
};
|
||||
|
||||
this.lastLoadedNoteId_ = null;
|
||||
@@ -72,8 +85,10 @@ class NoteTextComponent extends React.Component {
|
||||
this.scheduleSaveTimeout_ = null;
|
||||
this.restoreScrollTop_ = null;
|
||||
this.lastSetHtml_ = '';
|
||||
this.lastSetMarkers_ = [];
|
||||
this.lastSetMarkers_ = '';
|
||||
this.lastSetMarkersOptions_ = {};
|
||||
this.selectionRange_ = null;
|
||||
this.noteSearchBar_ = React.createRef();
|
||||
|
||||
// Complicated but reliable method to get editor content height
|
||||
// https://github.com/ajaxorg/ace/issues/2046
|
||||
@@ -212,6 +227,36 @@ class NoteTextComponent extends React.Component {
|
||||
this.updateHtml(this.state.note.body);
|
||||
}
|
||||
}
|
||||
|
||||
this.noteSearchBar_change = (query) => {
|
||||
this.setState({ localSearch: {
|
||||
query: query,
|
||||
selectedIndex: 0,
|
||||
}});
|
||||
}
|
||||
|
||||
const noteSearchBarNextPrevious = (inc) => {
|
||||
const ls = Object.assign({}, this.state.localSearch);
|
||||
ls.selectedIndex += inc;
|
||||
if (ls.selectedIndex < 0) ls.selectedIndex = ls.resultCount - 1;
|
||||
if (ls.selectedIndex >= ls.resultCount) ls.selectedIndex = 0;
|
||||
|
||||
this.setState({ localSearch: ls });
|
||||
}
|
||||
|
||||
this.noteSearchBar_next = () => {
|
||||
noteSearchBarNextPrevious(+1);
|
||||
}
|
||||
|
||||
this.noteSearchBar_previous = () => {
|
||||
noteSearchBarNextPrevious(-1);
|
||||
}
|
||||
|
||||
this.noteSearchBar_close = () => {
|
||||
this.setState({
|
||||
showLocalSearch: false,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Note:
|
||||
@@ -305,6 +350,7 @@ class NoteTextComponent extends React.Component {
|
||||
eventManager.on('todoToggle', this.onTodoToggle_);
|
||||
|
||||
ResourceFetcher.instance().on('downloadComplete', this.resourceFetcher_downloadComplete);
|
||||
ExternalEditWatcher.instance().on('noteChange', this.externalEditWatcher_noteChange);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
@@ -318,8 +364,7 @@ class NoteTextComponent extends React.Component {
|
||||
eventManager.removeListener('todoToggle', this.onTodoToggle_);
|
||||
|
||||
ResourceFetcher.instance().off('downloadComplete', this.resourceFetcher_downloadComplete);
|
||||
|
||||
this.destroyExternalEditWatcher();
|
||||
ExternalEditWatcher.instance().off('noteChange', this.externalEditWatcher_noteChange);
|
||||
}
|
||||
|
||||
async saveIfNeeded(saveIfNewNote = false) {
|
||||
@@ -332,7 +377,7 @@ class NoteTextComponent extends React.Component {
|
||||
}
|
||||
await shared.saveNoteButton_press(this);
|
||||
|
||||
this.externalEditWatcherUpdateNoteFile(this.state.note);
|
||||
ExternalEditWatcher.instance().updateNoteFile(this.state.note);
|
||||
}
|
||||
|
||||
async saveOneProperty(name, value) {
|
||||
@@ -371,7 +416,6 @@ class NoteTextComponent extends React.Component {
|
||||
if (props.newNote) {
|
||||
note = Object.assign({}, props.newNote);
|
||||
this.lastLoadedNoteId_ = null;
|
||||
this.externalEditWatcherStopWatchingAll();
|
||||
} else {
|
||||
noteId = props.noteId;
|
||||
loadingNewNote = stateNoteId !== noteId;
|
||||
@@ -398,8 +442,6 @@ class NoteTextComponent extends React.Component {
|
||||
|
||||
// Scroll back to top when loading new note
|
||||
if (loadingNewNote) {
|
||||
this.externalEditWatcherStopWatchingAll();
|
||||
|
||||
this.editorMaxScrollTop_ = 0;
|
||||
|
||||
// HACK: To go around a bug in Ace editor, we first set the scroll position to 1
|
||||
@@ -442,8 +484,7 @@ class NoteTextComponent extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
if (note)
|
||||
{
|
||||
if (note) {
|
||||
parentFolder = Folder.byId(props.folders, note.parent_id);
|
||||
}
|
||||
|
||||
@@ -462,8 +503,14 @@ class NoteTextComponent extends React.Component {
|
||||
newState.newAndNoTitleChangeNoteId = null;
|
||||
}
|
||||
|
||||
if (!note || loadingNewNote) {
|
||||
newState.showLocalSearch = false;
|
||||
newState.localSearch = Object.assign({}, this.localSearchDefaultState);
|
||||
}
|
||||
|
||||
this.lastSetHtml_ = '';
|
||||
this.lastSetMarkers_ = [];
|
||||
this.lastSetMarkers_ = '';
|
||||
this.lastSetMarkersOptions_ = {};
|
||||
|
||||
this.setState(newState);
|
||||
|
||||
@@ -472,13 +519,15 @@ class NoteTextComponent extends React.Component {
|
||||
// Since I'm updating the state, the componentWillReceiveProps was getting triggered again, where nextProps.newNote was still true, causing reloadNote to trigger again and again.
|
||||
// Notes from Laurent: The selected note tags are part of the global Redux state because they need to be updated whenever tags are changed or deleted
|
||||
// anywhere in the app. Thus it's not possible simple to load the tags here (as we won't have a way to know if they're updated afterwards).
|
||||
// Perhaps a better way would be to move that code in the middleware, check for TAGS_DELETE, TAGS_UPDATE, etc. actions and update the
|
||||
// Perhaps a better way would be to move that code in the middleware, check for TAGS_DELETE, TAGS_UPDATE, etc. actions and update the
|
||||
// selected note tags accordingly.
|
||||
if (!this.props.newNote) {
|
||||
this.props.dispatch({
|
||||
type: "SET_NOTE_TAGS",
|
||||
items: noteTags,
|
||||
});
|
||||
if (NOTE_TAG_BAR_FEATURE_ENABLED) {
|
||||
if (!this.props.newNote) {
|
||||
this.props.dispatch({
|
||||
type: "SET_NOTE_TAGS",
|
||||
items: noteTags,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.updateHtml(newState.note ? newState.note.body : '');
|
||||
@@ -509,6 +558,8 @@ class NoteTextComponent extends React.Component {
|
||||
}
|
||||
|
||||
areNoteTagsModified(newTags, oldTags) {
|
||||
if (!NOTE_TAG_BAR_FEATURE_ENABLED) return false;
|
||||
|
||||
if (!oldTags) return true;
|
||||
|
||||
if (newTags.length !== oldTags.length) return true;
|
||||
@@ -562,6 +613,10 @@ class NoteTextComponent extends React.Component {
|
||||
|
||||
const newBody = this.mdToHtml_.handleCheckboxClick(msg, this.state.note.body);
|
||||
this.saveOneProperty('body', newBody);
|
||||
} else if (msg === 'setMarkerCount') {
|
||||
const ls = Object.assign({}, this.state.localSearch);
|
||||
ls.resultCount = arg0;
|
||||
this.setState({ localSearch: ls });
|
||||
} else if (msg === 'percentScroll') {
|
||||
this.ignoreNextEditorScroll_ = true;
|
||||
this.setEditorPercentScroll(arg0);
|
||||
@@ -856,30 +911,48 @@ class NoteTextComponent extends React.Component {
|
||||
async doCommand(command) {
|
||||
if (!command || !this.state.note) return;
|
||||
|
||||
let commandProcessed = true;
|
||||
let fn = null;
|
||||
|
||||
if (command.name === 'exportPdf' && this.webview_) {
|
||||
this.commandSavePdf();
|
||||
fn = this.commandSavePdf;
|
||||
} else if (command.name === 'print' && this.webview_) {
|
||||
this.webview_.print();
|
||||
fn = this.commandPrint;
|
||||
} else if (command.name === 'textBold') {
|
||||
this.commandTextBold();
|
||||
fn = this.commandTextBold;
|
||||
} else if (command.name === 'textItalic') {
|
||||
this.commandTextItalic();
|
||||
fn = this.commandTextItalic;
|
||||
} else if (command.name === 'insertDateTime' ) {
|
||||
this.commandDateTime();
|
||||
fn = this.commandDateTime;
|
||||
} else if (command.name === 'commandStartExternalEditing') {
|
||||
this.commandStartExternalEditing();
|
||||
} else {
|
||||
commandProcessed = false;
|
||||
fn = this.commandStartExternalEditing;
|
||||
} else if (command.name === 'showLocalSearch') {
|
||||
fn = this.commandShowLocalSearch;
|
||||
}
|
||||
|
||||
if (commandProcessed) {
|
||||
this.props.dispatch({
|
||||
type: 'WINDOW_COMMAND',
|
||||
name: null,
|
||||
});
|
||||
if (!fn) return;
|
||||
|
||||
this.props.dispatch({
|
||||
type: 'WINDOW_COMMAND',
|
||||
name: null,
|
||||
});
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
fn = fn.bind(this);
|
||||
fn();
|
||||
});
|
||||
}
|
||||
|
||||
commandShowLocalSearch() {
|
||||
if (this.state.showLocalSearch) {
|
||||
this.noteSearchBar_.current.wrappedInstance.focus();
|
||||
} else {
|
||||
this.setState({ showLocalSearch: true });
|
||||
}
|
||||
|
||||
this.props.dispatch({
|
||||
type: 'NOTE_VISIBLE_PANES_SET',
|
||||
panes: ['editor', 'viewer'],
|
||||
});
|
||||
}
|
||||
|
||||
async commandAttachFile(filePaths = null) {
|
||||
@@ -924,6 +997,41 @@ class NoteTextComponent extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
printTo_(target, options) {
|
||||
const previousBody = this.state.note.body;
|
||||
const tempBody = "# " + this.state.note.title + "\n\n" + previousBody;
|
||||
|
||||
const previousTheme = Setting.value('theme');
|
||||
Setting.setValue('theme', Setting.THEME_LIGHT);
|
||||
this.lastSetHtml_ = '';
|
||||
this.updateHtml(tempBody);
|
||||
this.forceUpdate();
|
||||
|
||||
const restoreSettings = () => {
|
||||
Setting.setValue('theme', previousTheme);
|
||||
this.lastSetHtml_ = '';
|
||||
this.updateHtml(previousBody);
|
||||
this.forceUpdate();
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (target === 'pdf') {
|
||||
this.webview_.printToPDF({}, (error, data) => {
|
||||
restoreSettings();
|
||||
|
||||
if (error) {
|
||||
bridge().showErrorMessageBox(error.message);
|
||||
} else {
|
||||
shim.fsDriver().writeFile(options.path, data, 'buffer');
|
||||
}
|
||||
});
|
||||
} else if (target === 'printer') {
|
||||
this.webview_.print();
|
||||
restoreSettings();
|
||||
}
|
||||
}, 100);
|
||||
}
|
||||
|
||||
commandSavePdf() {
|
||||
const path = bridge().showSaveDialog({
|
||||
filters: [{ name: _('PDF File'), extensions: ['pdf']}],
|
||||
@@ -931,73 +1039,24 @@ class NoteTextComponent extends React.Component {
|
||||
});
|
||||
|
||||
if (path) {
|
||||
// Temporarily add a <h2> title in the webview
|
||||
const newHtml = this.insertHtmlHeading_(this.lastSetHtml_, this.state.note.title);
|
||||
this.webview_.send('setHtml', newHtml);
|
||||
|
||||
setTimeout(() => {
|
||||
this.webview_.printToPDF({}, (error, data) => {
|
||||
if (error) {
|
||||
bridge().showErrorMessageBox(error.message);
|
||||
} else {
|
||||
shim.fsDriver().writeFile(path, data, 'buffer');
|
||||
}
|
||||
|
||||
// Refresh the webview, restoring the previous content
|
||||
this.lastSetHtml_ = '';
|
||||
this.forceUpdate();
|
||||
});
|
||||
}, 100);
|
||||
this.printTo_('pdf', { path: path });
|
||||
}
|
||||
}
|
||||
|
||||
insertHtmlHeading_(s, heading) {
|
||||
const tag = 'h2';
|
||||
const marker = '<!-- START_OF_DOCUMENT -->'
|
||||
let splitStyle = s.split(marker);
|
||||
const index = splitStyle.length > 1 ? 1 : 0;
|
||||
let toInsert = escapeHtml(heading);
|
||||
toInsert = '<' + tag + '>' + toInsert + '</' + tag + '>';
|
||||
splitStyle[index] = toInsert + splitStyle[index];
|
||||
return splitStyle.join(marker);
|
||||
}
|
||||
|
||||
externalEditWatcher() {
|
||||
if (!this.externalEditWatcher_) {
|
||||
this.externalEditWatcher_ = new ExternalEditWatcher((action) => { return this.props.dispatch(action) });
|
||||
this.externalEditWatcher_.setLogger(reg.logger());
|
||||
this.externalEditWatcher_.on('noteChange', this.externalEditWatcher_noteChange);
|
||||
}
|
||||
|
||||
return this.externalEditWatcher_;
|
||||
}
|
||||
|
||||
externalEditWatcherUpdateNoteFile(note) {
|
||||
if (this.externalEditWatcher_) this.externalEditWatcher().updateNoteFile(note);
|
||||
}
|
||||
|
||||
externalEditWatcherStopWatchingAll() {
|
||||
if (this.externalEditWatcher_) this.externalEditWatcher().stopWatchingAll();
|
||||
}
|
||||
|
||||
destroyExternalEditWatcher() {
|
||||
if (!this.externalEditWatcher_) return;
|
||||
|
||||
this.externalEditWatcher_.off('noteChange', this.externalEditWatcher_noteChange);
|
||||
this.externalEditWatcher_.stopWatchingAll();
|
||||
this.externalEditWatcher_ = null;
|
||||
commandPrint() {
|
||||
this.printTo_('printer');
|
||||
}
|
||||
|
||||
async commandStartExternalEditing() {
|
||||
try {
|
||||
await this.externalEditWatcher().openAndWatch(this.state.note);
|
||||
await ExternalEditWatcher.instance().openAndWatch(this.state.note);
|
||||
} catch (error) {
|
||||
bridge().showErrorMessageBox(_('Error opening note in editor: %s', error.message));
|
||||
}
|
||||
}
|
||||
|
||||
async commandStopExternalEditing() {
|
||||
this.externalEditWatcherStopWatchingAll();
|
||||
ExternalEditWatcher.instance().stopWatching(this.state.note.id);
|
||||
}
|
||||
|
||||
async commandSetTags() {
|
||||
@@ -1434,10 +1493,21 @@ class NoteTextComponent extends React.Component {
|
||||
|
||||
const tagStyle = {
|
||||
marginBottom: 10,
|
||||
height: 30
|
||||
};
|
||||
|
||||
const bottomRowHeight = rootStyle.height - titleBarStyle.height - titleBarStyle.marginBottom - titleBarStyle.marginTop - theme.toolbarHeight;
|
||||
const searchBarHeight = this.state.showLocalSearch ? 35 : 0;
|
||||
|
||||
let bottomRowHeight = 0;
|
||||
if (NOTE_TAG_BAR_FEATURE_ENABLED) {
|
||||
bottomRowHeight = rootStyle.height - titleBarStyle.height - titleBarStyle.marginBottom - titleBarStyle.marginTop - theme.toolbarHeight - tagStyle.height - tagStyle.marginBottom;
|
||||
} else {
|
||||
toolbarStyle.marginBottom = 10;
|
||||
bottomRowHeight = rootStyle.height - titleBarStyle.height - titleBarStyle.marginBottom - titleBarStyle.marginTop - theme.toolbarHeight - toolbarStyle.marginBottom;
|
||||
}
|
||||
|
||||
bottomRowHeight -= searchBarHeight;
|
||||
|
||||
const viewerStyle = {
|
||||
width: Math.floor(innerWidth / 2),
|
||||
height: bottomRowHeight,
|
||||
@@ -1457,7 +1527,7 @@ class NoteTextComponent extends React.Component {
|
||||
verticalAlign: 'top',
|
||||
paddingTop: paddingTop + 'px',
|
||||
lineHeight: theme.textAreaLineHeight + 'px',
|
||||
fontSize: theme.fontSize + 'px',
|
||||
fontSize: theme.editorFontSize + 'px',
|
||||
color: theme.color,
|
||||
backgroundColor: theme.backgroundColor,
|
||||
editorTheme: theme.editorTheme,
|
||||
@@ -1496,12 +1566,25 @@ class NoteTextComponent extends React.Component {
|
||||
this.lastSetHtml_ = html;
|
||||
}
|
||||
|
||||
const search = BaseModel.byId(this.props.searches, this.props.selectedSearchId);
|
||||
const keywords = search ? Search.keywords(search.query_pattern) : [];
|
||||
let keywords = [];
|
||||
const markerOptions = {};
|
||||
|
||||
if (htmlHasChanged || !ArrayUtils.contentEquals(this.lastSetMarkers_, keywords)) {
|
||||
this.lastSetMarkers_ = [];
|
||||
this.webview_.send('setMarkers', keywords);
|
||||
if (this.state.showLocalSearch) {
|
||||
keywords = [this.state.localSearch.query];
|
||||
markerOptions.selectedIndex = this.state.localSearch.selectedIndex;
|
||||
} else {
|
||||
const search = BaseModel.byId(this.props.searches, this.props.selectedSearchId);
|
||||
if (search) {
|
||||
const parsedQuery = SearchEngine.instance().parseQuery(search.query_pattern);
|
||||
keywords = SearchEngine.instance().allParsedQueryTerms(parsedQuery);
|
||||
}
|
||||
}
|
||||
|
||||
const keywordHash = JSON.stringify(keywords);
|
||||
if (htmlHasChanged || keywordHash !== this.lastSetMarkers_ || !ObjectUtils.fieldsEqual(this.lastSetMarkersOptions_, markerOptions)) {
|
||||
this.lastSetMarkers_ = keywordHash;
|
||||
this.lastSetMarkersOptions_ = Object.assign({}, markerOptions);
|
||||
this.webview_.send('setMarkers', keywords, markerOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1521,7 +1604,7 @@ class NoteTextComponent extends React.Component {
|
||||
placeholder={ this.props.newNote ? _('Creating new %s...', isTodo ? _('to-do') : _('note')) : '' }
|
||||
/>
|
||||
|
||||
const tagList = <TagList
|
||||
const tagList = !NOTE_TAG_BAR_FEATURE_ENABLED ? null : <TagList
|
||||
style={tagStyle}
|
||||
items={this.state.noteTags}
|
||||
/>;
|
||||
@@ -1589,6 +1672,17 @@ class NoteTextComponent extends React.Component {
|
||||
highlightActiveLine={false}
|
||||
/>
|
||||
|
||||
const noteSearchBarComp = !this.state.showLocalSearch ? null : (
|
||||
<NoteSearchBar
|
||||
ref={this.noteSearchBar_}
|
||||
style={{display: 'flex', height:searchBarHeight,width:innerWidth, borderTop: '1px solid ' + theme.dividerColor}}
|
||||
onChange={this.noteSearchBar_change}
|
||||
onNext={this.noteSearchBar_next}
|
||||
onPrevious={this.noteSearchBar_previous}
|
||||
onClose={this.noteSearchBar_close}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<div style={rootStyle} onDrop={this.onDrop_}>
|
||||
<div style={titleBarStyle}>
|
||||
@@ -1600,6 +1694,8 @@ class NoteTextComponent extends React.Component {
|
||||
{ tagList }
|
||||
{ editor }
|
||||
{ viewer }
|
||||
<div style={{clear:'both'}}/>
|
||||
{ noteSearchBarComp }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
|
||||
Atom One Dark With support for ReasonML by Gidi Morris, based off work by Daniel Gamage
|
||||
|
||||
Original One Dark Syntax theme from https://github.com/atom/one-dark-syntax
|
||||
|
||||
*/
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
line-height: 1.3em;
|
||||
color: #abb2bf;
|
||||
background: #282c34;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.hljs-keyword, .hljs-operator {
|
||||
color: #F92672;
|
||||
}
|
||||
.hljs-pattern-match {
|
||||
color: #F92672;
|
||||
}
|
||||
.hljs-pattern-match .hljs-constructor {
|
||||
color: #61aeee;
|
||||
}
|
||||
.hljs-function {
|
||||
color: #61aeee;
|
||||
}
|
||||
.hljs-function .hljs-params {
|
||||
color: #A6E22E;
|
||||
}
|
||||
.hljs-function .hljs-params .hljs-typing {
|
||||
color: #FD971F;
|
||||
}
|
||||
.hljs-module-access .hljs-module {
|
||||
color: #7e57c2;
|
||||
}
|
||||
.hljs-constructor {
|
||||
color: #e2b93d;
|
||||
}
|
||||
.hljs-constructor .hljs-string {
|
||||
color: #9CCC65;
|
||||
}
|
||||
.hljs-comment, .hljs-quote {
|
||||
color: #b18eb1;
|
||||
font-style: italic;
|
||||
}
|
||||
.hljs-doctag, .hljs-formula {
|
||||
color: #c678dd;
|
||||
}
|
||||
.hljs-section, .hljs-name, .hljs-selector-tag, .hljs-deletion, .hljs-subst {
|
||||
color: #e06c75;
|
||||
}
|
||||
.hljs-literal {
|
||||
color: #56b6c2;
|
||||
}
|
||||
.hljs-string, .hljs-regexp, .hljs-addition, .hljs-attribute, .hljs-meta-string {
|
||||
color: #98c379;
|
||||
}
|
||||
.hljs-built_in, .hljs-class .hljs-title {
|
||||
color: #e6c07b;
|
||||
}
|
||||
.hljs-attr, .hljs-variable, .hljs-template-variable, .hljs-type, .hljs-selector-class, .hljs-selector-attr, .hljs-selector-pseudo, .hljs-number {
|
||||
color: #d19a66;
|
||||
}
|
||||
.hljs-symbol, .hljs-bullet, .hljs-link, .hljs-meta, .hljs-selector-id, .hljs-title {
|
||||
color: #61aeee;
|
||||
}
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
@@ -16,6 +16,11 @@
|
||||
}
|
||||
|
||||
mark {
|
||||
background: #F3B717;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.mark-selected {
|
||||
background: #CF3F00;
|
||||
color: white;
|
||||
}
|
||||
@@ -138,6 +143,8 @@
|
||||
ipc.setHtml = (event) => {
|
||||
const html = event.html;
|
||||
|
||||
markJsHackMarkerInserted_ = false;
|
||||
|
||||
updateBodyHeight();
|
||||
|
||||
contentElement.innerHTML = html;
|
||||
@@ -191,8 +198,34 @@
|
||||
setPercentScroll(percent);
|
||||
}
|
||||
|
||||
// HACK for Mark.js bug - https://github.com/julmot/mark.js/issues/127
|
||||
let markJsHackMarkerInserted_ = false;
|
||||
function addMarkJsSpaceHack(document) {
|
||||
if (markJsHackMarkerInserted_) return;
|
||||
|
||||
const prepareElementsForMarkJs = (elements, type) => {
|
||||
// const markJsHackMarker_ = '​ ​'
|
||||
const markJsHackMarker_ = ' ';
|
||||
for (let i = 0; i < elements.length; i++) {
|
||||
if (!type) {
|
||||
elements[i].innerHTML = elements[i].innerHTML + markJsHackMarker_;
|
||||
} else if (type === 'insertBefore') {
|
||||
elements[i].insertAdjacentHTML('beforeBegin', markJsHackMarker_);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
prepareElementsForMarkJs(document.getElementsByTagName('p'));
|
||||
prepareElementsForMarkJs(document.getElementsByTagName('div'));
|
||||
prepareElementsForMarkJs(document.getElementsByTagName('br'), 'insertBefore');
|
||||
markJsHackMarkerInserted_ = true;
|
||||
}
|
||||
|
||||
let mark_ = null;
|
||||
function setMarkers(keywords) {
|
||||
let markSelectedElement_ = null;
|
||||
function setMarkers(keywords, options = null) {
|
||||
if (!options) options = {};
|
||||
|
||||
if (!mark_) {
|
||||
mark_ = new Mark(document.getElementById('content'), {
|
||||
exclude: ['img'],
|
||||
@@ -200,26 +233,66 @@
|
||||
});
|
||||
}
|
||||
|
||||
mark_.mark(keywords);
|
||||
addMarkJsSpaceHack(document);
|
||||
|
||||
mark_.unmark()
|
||||
|
||||
if (markSelectedElement_) markSelectedElement_.classList.remove('mark-selected');
|
||||
|
||||
let selectedElement = null;
|
||||
let elementIndex = 0;
|
||||
|
||||
const onEachElement = (element) => {
|
||||
if (!('selectedIndex' in options)) return;
|
||||
|
||||
if (('selectedIndex' in options) && elementIndex === options.selectedIndex) {
|
||||
markSelectedElement_ = element;
|
||||
element.classList.add('mark-selected');
|
||||
selectedElement = element;
|
||||
}
|
||||
|
||||
elementIndex++;
|
||||
}
|
||||
|
||||
for (let i = 0; i < keywords.length; i++) {
|
||||
const keyword = keywords[i];
|
||||
|
||||
if (keyword.type === 'regex') {
|
||||
mark_.markRegExp(new RegExp(keyword.value, 'gmi'), {
|
||||
each: onEachElement,
|
||||
acrossElements: true,
|
||||
});
|
||||
} else {
|
||||
mark_.mark([keyword], {
|
||||
each: onEachElement,
|
||||
accuracy: 'exactly',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ipcProxySendToHost('setMarkerCount', elementIndex);
|
||||
|
||||
if (selectedElement) selectedElement.scrollIntoView();
|
||||
}
|
||||
|
||||
let markLoaded_ = false;
|
||||
ipc.setMarkers = (event) => {
|
||||
const keywords = event.keywords;
|
||||
const options = event.options;
|
||||
|
||||
if (!keywords.length && !markLoaded_) return;
|
||||
|
||||
if (!markLoaded_) {
|
||||
const script = document.createElement('script');
|
||||
script.onload = function() {
|
||||
setMarkers(keywords);
|
||||
setMarkers(keywords, options);
|
||||
};
|
||||
|
||||
script.src = '../../node_modules/mark.js/dist/mark.min.js';
|
||||
document.getElementById('markScriptContainer').appendChild(script);
|
||||
markLoaded_ = true;
|
||||
} else {
|
||||
setMarkers(keywords);
|
||||
setMarkers(keywords, options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,24 +349,32 @@
|
||||
}
|
||||
});
|
||||
|
||||
function handleInternalLink(event, anchorNode) {
|
||||
const href = anchorNode.getAttribute('href');
|
||||
if (href.indexOf('#') === 0) {
|
||||
event.preventDefault();
|
||||
location.hash = href;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
document.addEventListener('click', function(event) {
|
||||
const t = event.target;
|
||||
|
||||
// Prevent URLs added via <a> tags from being opened within the application itself
|
||||
// otherwise it would open the whole website within the WebView.
|
||||
if (t && t.nodeName === 'A' && !t.hasAttribute('data-from-md')) {
|
||||
if (handleInternalLink(event, t)) return;
|
||||
|
||||
event.preventDefault();
|
||||
ipcProxySendToHost(t.getAttribute('href'));
|
||||
return;
|
||||
}
|
||||
|
||||
// IF this is an internal link, jump to the anchor directly
|
||||
// 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;
|
||||
}
|
||||
if (handleInternalLink(event, t)) return;
|
||||
}
|
||||
});
|
||||
|
||||
|
@@ -13,8 +13,8 @@ ipcRenderer.on('setPercentScroll', (event, percent) => {
|
||||
window.postMessage({ target: 'webview', name: 'setPercentScroll', data: { percent: percent } }, '*');
|
||||
});
|
||||
|
||||
ipcRenderer.on('setMarkers', (event, keywords) => {
|
||||
window.postMessage({ target: 'webview', name: 'setMarkers', data: { keywords: keywords } }, '*');
|
||||
ipcRenderer.on('setMarkers', (event, keywords, options) => {
|
||||
window.postMessage({ target: 'webview', name: 'setMarkers', data: { keywords: keywords, options: options } }, '*');
|
||||
});
|
||||
|
||||
window.addEventListener('message', (event) => {
|
||||
|
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
@@ -12,9 +12,9 @@ locales['hr_HR'] = require('./hr_HR.json');
|
||||
locales['it_IT'] = require('./it_IT.json');
|
||||
locales['ja_JP'] = require('./ja_JP.json');
|
||||
locales['ko'] = require('./ko.json');
|
||||
locales['nb_NO'] = require('./nb_NO.json');
|
||||
locales['nl_BE'] = require('./nl_BE.json');
|
||||
locales['nl_NL'] = require('./nl_NL.json');
|
||||
locales['no'] = require('./no.json');
|
||||
locales['pt_BR'] = require('./pt_BR.json');
|
||||
locales['ro'] = require('./ro.json');
|
||||
locales['ru_RU'] = require('./ru_RU.json');
|
||||
|
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
1
ElectronClient/app/locales/nb_NO.json
Normal file
1
ElectronClient/app/locales/nb_NO.json
Normal file
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.115",
|
||||
"version": "1.0.119",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -6981,11 +6981,6 @@
|
||||
"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.115",
|
||||
"version": "1.0.119",
|
||||
"description": "Joplin for Desktop",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
@@ -8,7 +8,7 @@
|
||||
"pack": "node_modules/.bin/electron-builder --dir",
|
||||
"dist": "node_modules/.bin/electron-builder",
|
||||
"publish": "build -p always",
|
||||
"postinstall": "node compile-jsx.js && node compile-package-info.js && node ../../Tools/copycss.js --copy-fonts",
|
||||
"postinstall": "node compile-jsx.js && node compile-package-info.js && node ../../Tools/copycss.js --copy-fonts && install-app-deps",
|
||||
"compile": "node compile-jsx.js && node compile-package-info.js && node ../../Tools/copycss.js --copy-fonts"
|
||||
},
|
||||
"repository": {
|
||||
@@ -71,8 +71,8 @@
|
||||
"electron-builder": "20.14.7"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"7zip-bin-mac": "^1.0.1",
|
||||
"7zip-bin-linux": "^1.0.1",
|
||||
"7zip-bin-mac": "^1.0.1",
|
||||
"7zip-bin-win": "^2.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -121,13 +121,12 @@
|
||||
"server-destroy": "^1.0.1",
|
||||
"smalltalk": "^2.5.1",
|
||||
"sprintf-js": "^1.1.1",
|
||||
"sqlite3": "^3.1.13",
|
||||
"sqlite3": "^4.0.4",
|
||||
"string-padding": "^1.0.2",
|
||||
"string-to-stream": "^1.1.1",
|
||||
"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",
|
||||
|
@@ -1,6 +1,7 @@
|
||||
const Setting = require('lib/models/Setting.js');
|
||||
|
||||
const zoomRatio = Setting.value('style.zoom') / 100;
|
||||
const editorFontSize = Setting.value('style.editor.fontSize');
|
||||
|
||||
// globalStyle should be used for properties that do not change across themes
|
||||
// i.e. should not be used for colors
|
||||
@@ -14,7 +15,8 @@ const globalStyle = {
|
||||
disabledOpacity: 0.3,
|
||||
buttonMinWidth: 50,
|
||||
buttonMinHeight: 30,
|
||||
textAreaLineHeight: 17,
|
||||
editorFontSize: editorFontSize,
|
||||
textAreaLineHeight: Math.round(17 * editorFontSize / 12),
|
||||
|
||||
headerHeight: 35,
|
||||
headerButtonHPadding: 6,
|
||||
|
@@ -11,6 +11,14 @@ echo " | | "
|
||||
echo " |_| "
|
||||
echo ""
|
||||
|
||||
# Check and warn if running as root.
|
||||
if [[ $EUID = 0 ]] ; then
|
||||
if [[ $* != *--allow-root* ]] ; then
|
||||
echo "It is not recommended (nor necessary) to run this script as root. To do so anyway, please use '--allow-root'"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
#-----------------------------------------------------
|
||||
# Download Joplin
|
||||
#-----------------------------------------------------
|
||||
@@ -19,7 +27,6 @@ echo ""
|
||||
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
|
||||
@@ -51,9 +58,9 @@ if [[ $(< ~/.joplin/VERSION) != "$version" ]]; then
|
||||
desktop=${desktop,,} # convert to lower case
|
||||
|
||||
# Create icon for Gnome
|
||||
if [[ $desktop =~ .*gnome.* ]]
|
||||
if [[ $desktop =~ .*gnome.* ]] || [[ $desktop =~ .*kde.* ]]
|
||||
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
|
||||
echo -e "[Desktop Entry]\nEncoding=UTF-8\nName=Joplin\nExec=/home/$USER/.joplin/Joplin.AppImage\nIcon=/home/$USER/.joplin/Icon512.png\nType=Application\nCategories=Application;" >> ~/.local/share/applications/joplin.desktop
|
||||
fi
|
||||
|
||||
#-----------------------------------------------------
|
||||
|
56
README.md
56
README.md
@@ -20,9 +20,9 @@ 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.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).
|
||||
Windows (32 and 64-bit) | <a href='https://github.com/laurent22/joplin/releases/download/v1.0.117/Joplin-Setup-1.0.117.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.117/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.117/Joplin-1.0.117.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.117/Joplin-1.0.117-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.
|
||||
|
||||
@@ -36,7 +36,7 @@ wget -O - https://raw.githubusercontent.com/laurent22/joplin/master/Joplin_insta
|
||||
|
||||
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.175/joplin-v1.0.175.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.178/joplin-v1.0.178.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
|
||||
@@ -81,7 +81,7 @@ The Web Clipper is a browser extension that allows you to save web pages and scr
|
||||
- [Donate](https://github.com/laurent22/joplin/blob/master/readme/donate.md)
|
||||
<!-- TOC -->
|
||||
|
||||
# Features
|
||||
# Features
|
||||
|
||||
- Desktop, mobile and terminal applications.
|
||||
- [Web Clipper](https://github.com/laurent22/joplin/blob/master/readme/clipper.md) for Firefox and Chrome.
|
||||
@@ -98,10 +98,11 @@ The Web Clipper is a browser extension that allows you to save web pages and scr
|
||||
- Search functionality.
|
||||
- Geo-location support.
|
||||
- Supports multiple languages
|
||||
- External editor support - open notes in your favorite external editor with one click in Joplin.
|
||||
|
||||
# Importing
|
||||
|
||||
## Importing from Evernote
|
||||
## Importing from Evernote
|
||||
|
||||
Joplin was designed as a replacement for Evernote and so can import complete Evernote notebooks, as well as notes, tags, resources (attached files) and note metadata (such as author, geo-location, etc.) via ENEX files. In terms of data, the only two things that might slightly differ are:
|
||||
|
||||
@@ -173,6 +174,7 @@ Select the "WebDAV" synchronisation target and follow the same instructions as f
|
||||
|
||||
WebDAV-compatible services that are known to work with Joplin:
|
||||
|
||||
- [Apache WebDAV Module](https://httpd.apache.org/docs/current/mod/mod_dav.html)
|
||||
- [Box.com](https://www.box.com/)
|
||||
- [DriveHQ](https://www.drivehq.com)
|
||||
- [Fastmail](https://www.fastmail.com/)
|
||||
@@ -284,6 +286,20 @@ It is generally recommended to enter the notes as Markdown as it makes the notes
|
||||
|
||||
Rendered markdown can be customized by placing a userstyle file in the profile directory `~/.config/joplin-desktop/userstyle.css` (This path might be different on your device - check at the top of the Config screen for the exact path). This file supports standard CSS syntax.
|
||||
|
||||
# Searching
|
||||
|
||||
Joplin implements the SQLite Full Text Search (FTS4) extension. It means the content of all the notes is indexed in real time and search queries return results very fast. Both [Simple FTS Queries](https://www.sqlite.org/fts3.html#simple_fts_queries) and [Full-Text Index Queries](https://www.sqlite.org/fts3.html#full_text_index_queries) are supported. See below for the list of supported queries:
|
||||
|
||||
Search type | Description | Example
|
||||
------------|-------------|---------
|
||||
Single word | Returns all the notes that contain this term. | `dog`, `cat`
|
||||
Multiples words | Returns all the notes that contain **all** these words, but not necessarily next to each other. | `dog cat` - will return any notes that contain the words "dog" and "cat" anywhere in the note, no necessarily in that order nor next to each others. It will **not** return results that contain "dog" or "cat" only.
|
||||
Phrase query | Add double quotes to return the notes that contain exactly this phrase. | `"shopping list"` - will return the notes that contain these **exact terms** next to each others and in this order. It will **not** return for example a note that contain "going shopping with my list".
|
||||
Prefix | Add a wildmark to return all the notes that contain a term with a specified prefix. | `swim*` - will return all the notes that contain eg. "swim", but also "swimming", "swimsuit", etc. IMPORTANT: The wildcard **can only be at the end** - it will be ignored at the beginning of a word (eg. `*swim`) and will be treated as a literal asterisk in the middle of a word (eg. `ast*rix`)
|
||||
Field restricted | Add either `title:` or `body:` before a note to restrict your search to just the title, or just the body. | `title:shopping`, `body:egg`
|
||||
|
||||
Notes are sorted by "relevance". Currently it means the notes that contain the requested terms the most times are on top. For queries with multiple terms, it also matter how close to each others are the terms. This is a bit experimental so if you notice a search query that returns unexpected results, please report it in the forum, providing as much details as possible to replicate the issue.
|
||||
|
||||
# Donations
|
||||
|
||||
Donations to Joplin support the development of the project. Developing quality applications mostly takes time, but there are also some expenses, such as digital certificates to sign the applications, app store fees, hosting, etc. Most of all, your donation will make it possible to keep up the current development standard.
|
||||
@@ -320,28 +336,28 @@ Current translations:
|
||||
| Language | Po File | Last translator | Percent done
|
||||
---|---|---|---|---
|
||||
 | Basque | [eu](https://github.com/laurent22/joplin/blob/master/CliClient/locales/eu.po) | juan.abasolo@ehu.eus | 61%
|
||||
 | Catalan | [ca](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ca.po) | jmontane, 2018 | 87%
|
||||
 | Catalan | [ca](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ca.po) | jmontane, 2018 | 86%
|
||||
 | Croatian | [hr_HR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/hr_HR.po) | Hrvoje Mandić (trbuhom@net.hr) | 49%
|
||||
 | Czech | [cs_CZ](https://github.com/laurent22/joplin/blob/master/CliClient/locales/cs_CZ.po) | Lukas Helebrandt (lukas@aiya.cz) | 77%
|
||||
 | Czech | [cs_CZ](https://github.com/laurent22/joplin/blob/master/CliClient/locales/cs_CZ.po) | Lukas Helebrandt (lukas@aiya.cz) | 76%
|
||||
 | Dansk | [da_DK](https://github.com/laurent22/joplin/blob/master/CliClient/locales/da_DK.po) | Morten Juhl-Johansen Zölde-Fejér (mjjzf@syntaktisk. | 78%
|
||||
 | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Michael Sonntag (ms@editorei.de) | 98%
|
||||
 | Deutsch | [de_DE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/de_DE.po) | Michael Sonntag (ms@editorei.de) | 97%
|
||||
 | 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) | 93%
|
||||
 | Español | [es_ES](https://github.com/laurent22/joplin/blob/master/CliClient/locales/es_ES.po) | Fernando Martín (f@mrtn.es) | 92%
|
||||
 | 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) | 77%
|
||||
 | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/master/CliClient/locales/it_IT.po) | | 92%
|
||||
 | Nederlands | [nl_NL](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_NL.po) | Heimen Stoffels (vistausss@outlook.com) | 93%
|
||||
 | Italiano | [it_IT](https://github.com/laurent22/joplin/blob/master/CliClient/locales/it_IT.po) | | 91%
|
||||
 | Nederlands | [nl_BE](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_BE.po) | | 61%
|
||||
 | Norwegian | [no](https://github.com/laurent22/joplin/blob/master/CliClient/locales/no.po) | | 83%
|
||||
 | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/pt_BR.po) | Renato Nunes Bastos (rnbastos@gmail.com) | 93%
|
||||
 | Română | [ro](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ro.po) | | 61%
|
||||
 | Nederlands | [nl_NL](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nl_NL.po) | Heimen Stoffels (vistausss@outlook.com) | 92%
|
||||
 | Norwegian | [nb_NO](https://github.com/laurent22/joplin/blob/master/CliClient/locales/nb_NO.po) | Mats Estensen (matsest@mxe.no) | 99%
|
||||
 | Português (Brasil) | [pt_BR](https://github.com/laurent22/joplin/blob/master/CliClient/locales/pt_BR.po) | Renato Nunes Bastos (rnbastos@gmail.com) | 99%
|
||||
 | Română | [ro](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ro.po) | | 60%
|
||||
 | Slovenian | [sl_SI](https://github.com/laurent22/joplin/blob/master/CliClient/locales/sl_SI.po) | | 76%
|
||||
 | Svenska | [sv](https://github.com/laurent22/joplin/blob/master/CliClient/locales/sv.po) | Jonatan Nyberg (jonatan@autistici.org) | 92%
|
||||
 | Svenska | [sv](https://github.com/laurent22/joplin/blob/master/CliClient/locales/sv.po) | Jonatan Nyberg (jonatan@autistici.org) | 91%
|
||||
 | Русский | [ru_RU](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ru_RU.po) | Artyom Karlov (artyom.karlov@gmail.com) | 76%
|
||||
 | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po) | | 93%
|
||||
 | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_TW.po) | penguinsam (samliu@gmail.com) | 93%
|
||||
 | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ja_JP.po) | AWASHIRO Ikuya (ikunya@gmail.com) | 94%
|
||||
 | 한국말 | [ko](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ko.po) | | 93%
|
||||
 | 中文 (简体) | [zh_CN](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_CN.po) | | 95%
|
||||
 | 中文 (繁體) | [zh_TW](https://github.com/laurent22/joplin/blob/master/CliClient/locales/zh_TW.po) | penguinsam (samliu@gmail.com) | 92%
|
||||
 | 日本語 | [ja_JP](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ja_JP.po) | AWASHIRO Ikuya (ikunya@gmail.com) | 93%
|
||||
 | 한국말 | [ko](https://github.com/laurent22/joplin/blob/master/CliClient/locales/ko.po) | | 92%
|
||||
<!-- LOCALE-TABLE-AUTO-GENERATED -->
|
||||
|
||||
# Known bugs
|
||||
|
@@ -90,8 +90,8 @@ android {
|
||||
applicationId "net.cozic.joplin"
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 2097411
|
||||
versionName "1.0.175"
|
||||
versionCode 2097414
|
||||
versionName "1.0.178"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
}
|
||||
@@ -137,24 +137,39 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(':react-native-camera')
|
||||
compile project(':react-native-file-viewer')
|
||||
compile project(':react-native-securerandom')
|
||||
compile project(':react-native-push-notification')
|
||||
compile project(':react-native-fs')
|
||||
compile project(':react-native-image-picker')
|
||||
compile project(':react-native-vector-icons')
|
||||
compile project(':react-native-fs')
|
||||
implementation project(':react-native-firebase')
|
||||
implementation (project(':react-native-camera')) {
|
||||
// This is required because com.google.firebase requires v16.0.x of com.google.android.gms
|
||||
// while react-native-camera requires v15.x, which results in broken dependencies with
|
||||
// this error message:
|
||||
//
|
||||
// The library com.google.android.gms:play-services-base is being requested by various other libraries at [[15.0.1,15.0.1]], but resolves to 16.0.1
|
||||
//
|
||||
// For the record: found solution by removing all Firebase stuff here and running "gradlew.bat :app:dependencies"
|
||||
// That shows that react-native-camera was the one requiring v15.0.1.
|
||||
exclude group: "com.google.android.gms"
|
||||
}
|
||||
implementation project(':react-native-file-viewer')
|
||||
implementation project(':react-native-securerandom')
|
||||
implementation project(':react-native-fs')
|
||||
implementation project(':react-native-image-picker')
|
||||
implementation project(':react-native-vector-icons')
|
||||
implementation project(':react-native-fs')
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
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(':rn-fetch-blob')
|
||||
compile project(':react-native-document-picker')
|
||||
compile project(':react-native-image-resizer')
|
||||
compile project(':react-native-share-extension')
|
||||
compile project(':react-native-version-info')
|
||||
compile "com.facebook.react:react-native:+"
|
||||
implementation project(':react-native-sqlite-storage')
|
||||
implementation project(':rn-fetch-blob')
|
||||
implementation project(':react-native-document-picker')
|
||||
implementation project(':react-native-image-resizer')
|
||||
implementation project(':react-native-share-extension')
|
||||
implementation project(':react-native-version-info')
|
||||
implementation "com.facebook.react:react-native:+"
|
||||
|
||||
implementation "com.google.android.gms:play-services-base:16.0.1" // For Firebase
|
||||
implementation "com.google.firebase:firebase-core:16.0.4" // For Firebase
|
||||
implementation "com.google.firebase:firebase-messaging:17.3.4" // For Firebase
|
||||
implementation 'me.leolin:ShortcutBadger:1.1.21@aar' // For Firebase - this line if you wish to use badge on Android
|
||||
|
||||
// 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
|
||||
@@ -183,3 +198,4 @@ task copyDownloadableDepsToLibs(type: Copy) {
|
||||
}
|
||||
|
||||
apply from: "../../node_modules/react-native-vector-icons/fonts.gradle"
|
||||
apply plugin: 'com.google.gms.google-services' // For Firebase
|
42
ReactNativeClient/android/app/google-services.json
Normal file
42
ReactNativeClient/android/app/google-services.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"project_info": {
|
||||
"project_number": "790045682275",
|
||||
"firebase_url": "https://joplin-b5b20.firebaseio.com",
|
||||
"project_id": "joplin-b5b20",
|
||||
"storage_bucket": "joplin-b5b20.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:790045682275:android:8b68903cf881e9f7",
|
||||
"android_client_info": {
|
||||
"package_name": "net.cozic.joplin"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "790045682275-fkusmvsm7gv3nve7h0sg0uuor9njf4sm.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyCbHjUWAKcbldLTuoN7JybJ8dfznwBG_gM"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"analytics_service": {
|
||||
"status": 1
|
||||
},
|
||||
"appinvite_service": {
|
||||
"status": 1,
|
||||
"other_platform_oauth_client": []
|
||||
},
|
||||
"ads_service": {
|
||||
"status": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
@@ -11,25 +11,23 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove"/>
|
||||
|
||||
|
||||
<!-- ============================= -->
|
||||
<!-- START RNFirebaseNotifications -->
|
||||
<!-- ============================= -->
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<!-- ============================= -->
|
||||
<!-- END RNFirebaseNotifications -->
|
||||
<!-- ============================= -->
|
||||
|
||||
|
||||
|
||||
<!-- Make these features optional to enable Chromebooks -->
|
||||
<!-- https://github.com/laurent22/joplin/issues/37 -->
|
||||
<uses-feature android:name="android.hardware.camera" android:required="false" />
|
||||
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
|
||||
|
||||
<!-- ==================================== -->
|
||||
<!-- START react-native-push-notification -->
|
||||
<!-- ==================================== -->
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<permission
|
||||
android:name="${applicationId}.permission.C2D_MESSAGE"
|
||||
android:protectionLevel="signature" />
|
||||
<uses-permission android:name="${applicationId}.permission.C2D_MESSAGE" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
|
||||
<!-- ================================== -->
|
||||
<!-- END react-native-push-notification -->
|
||||
<!-- ================================== -->
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="16"
|
||||
android:targetSdkVersion="26" />
|
||||
@@ -41,27 +39,62 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<!-- ==================================== -->
|
||||
<!-- START react-native-push-notification -->
|
||||
<!-- ==================================== -->
|
||||
|
||||
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationPublisher" />
|
||||
<receiver android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationBootEventReceiver">
|
||||
|
||||
<!-- ============================= -->
|
||||
<!-- START RNFirebaseNotifications -->
|
||||
<!-- ============================= -->
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.firebase.messaging.default_notification_icon"
|
||||
android:resource="@drawable/ic_stat_access_alarm" />
|
||||
<receiver android:name="io.invertase.firebase.notifications.RNFirebaseNotificationReceiver"/>
|
||||
<receiver android:enabled="true" android:exported="true" android:name="io.invertase.firebase.notifications.RNFirebaseNotificationsRebootReceiver">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||
<action android:name="android.intent.action.BOOT_COMPLETED"/>
|
||||
<action android:name="android.intent.action.QUICKBOOT_POWERON"/>
|
||||
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
<service android:name="com.dieam.reactnativepushnotification.modules.RNPushNotificationRegistrationService"/>
|
||||
<!-- ================================== -->
|
||||
<!-- END react-native-push-notification -->
|
||||
<!-- ================================== -->
|
||||
<!-- ============================= -->
|
||||
<!-- END RNFirebaseNotifications -->
|
||||
<!-- ============================= -->
|
||||
|
||||
|
||||
|
||||
<!-- ============================= -->
|
||||
<!-- START RNFirebaseNotifications -->
|
||||
<!-- ============================= -->
|
||||
<service android:name="io.invertase.firebase.messaging.RNFirebaseMessagingService">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.MESSAGING_EVENT" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service android:name="io.invertase.firebase.messaging.RNFirebaseInstanceIdService">
|
||||
<intent-filter>
|
||||
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"/>
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<!-- "If you want to be able to react to data-only messages when your app is in the background, e.g. to display a heads up notification" -->
|
||||
<service android:name="io.invertase.firebase.messaging.RNFirebaseBackgroundMessagingService" />
|
||||
<!-- ============================= -->
|
||||
<!-- END RNFirebaseNotifications -->
|
||||
<!-- ============================= -->
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- 2018-12-16: Changed android:launchMode from "singleInstance" to "singleTop" for Firebase notification -->
|
||||
<!-- Previously singleInstance was necessary to prevent multiple instance of the RN app from running at the same time, but maybe no longer needed. -->
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:label="@string/app_name"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:launchMode="singleInstance">
|
||||
android:launchMode="singleTop">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
@@ -3,10 +3,12 @@ package net.cozic.joplin;
|
||||
import android.app.Application;
|
||||
|
||||
import com.facebook.react.ReactApplication;
|
||||
import io.invertase.firebase.RNFirebasePackage;
|
||||
import io.invertase.firebase.notifications.RNFirebaseNotificationsPackage;
|
||||
import io.invertase.firebase.messaging.RNFirebaseMessagingPackage;
|
||||
import org.reactnative.camera.RNCameraPackage;
|
||||
import com.vinzscam.reactnativefileviewer.RNFileViewerPackage;
|
||||
import net.rhogan.rnsecurerandom.RNSecureRandomPackage;
|
||||
import com.dieam.reactnativepushnotification.ReactNativePushNotificationPackage;
|
||||
import com.imagepicker.ImagePickerPackage;
|
||||
import com.facebook.react.ReactInstanceManager;
|
||||
import com.facebook.react.ReactNativeHost;
|
||||
@@ -38,12 +40,14 @@ public class MainApplication extends Application implements ReactApplication {
|
||||
@Override
|
||||
protected List<ReactPackage> getPackages() {
|
||||
return Arrays.<ReactPackage>asList(
|
||||
new ImageResizerPackage(),
|
||||
new MainReactPackage(),
|
||||
new RNCameraPackage(),
|
||||
new RNFileViewerPackage(),
|
||||
new RNSecureRandomPackage(),
|
||||
new ReactNativePushNotificationPackage(),
|
||||
new ImageResizerPackage(),
|
||||
new RNFirebasePackage(),
|
||||
new RNFirebaseMessagingPackage(),
|
||||
new RNFirebaseNotificationsPackage(),
|
||||
new RNCameraPackage(),
|
||||
new RNFileViewerPackage(),
|
||||
new RNSecureRandomPackage(),
|
||||
new ImagePickerPackage(),
|
||||
new ReactNativeDocumentPicker(),
|
||||
new RNFetchBlobPackage(),
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 1.0 KiB |
Binary file not shown.
After Width: | Height: | Size: 548 B |
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
@@ -13,7 +13,8 @@ buildscript {
|
||||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.1.4'
|
||||
classpath 'com.android.tools.build:gradle:3.2.0' // Upgraded from 3.1.4 to 3.2.0 for Firebase
|
||||
classpath 'com.google.gms:google-services:4.0.1' // For Firebase
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
@@ -23,6 +24,8 @@ buildscript {
|
||||
allprojects {
|
||||
repositories {
|
||||
mavenLocal()
|
||||
google()
|
||||
jcenter() // Was added by me - still needed?
|
||||
maven {
|
||||
url "https://maven.google.com"
|
||||
}
|
||||
@@ -30,8 +33,6 @@ allprojects {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url "$rootDir/../node_modules/react-native/android"
|
||||
}
|
||||
jcenter() // Was added by me - still needed?
|
||||
google()
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip
|
||||
|
@@ -1,12 +1,12 @@
|
||||
rootProject.name = 'Joplin'
|
||||
include ':react-native-firebase'
|
||||
project(':react-native-firebase').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-firebase/android')
|
||||
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'
|
||||
project(':react-native-securerandom').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-securerandom/android')
|
||||
include ':react-native-push-notification'
|
||||
project(':react-native-push-notification').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-push-notification/android')
|
||||
include ':react-native-fs'
|
||||
project(':react-native-fs').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fs/android')
|
||||
include ':react-native-image-picker'
|
||||
|
@@ -34,6 +34,7 @@ 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 SearchEngineUtils = require('lib/services/SearchEngineUtils');
|
||||
const DecryptionWorker = require('lib/services/DecryptionWorker');
|
||||
const BaseService = require('lib/services/BaseService');
|
||||
|
||||
@@ -218,12 +219,8 @@ class BaseApplication {
|
||||
} else if (parentType === Tag.modelType()) {
|
||||
notes = await Tag.notes(parentId, options);
|
||||
} else if (parentType === BaseModel.TYPE_SEARCH) {
|
||||
let fields = Note.previewFields();
|
||||
let search = BaseModel.byId(state.searches, parentId);
|
||||
notes = await Note.previews(null, {
|
||||
fields: fields,
|
||||
anywherePattern: '*' + search.query_pattern + '*',
|
||||
});
|
||||
const search = BaseModel.byId(state.searches, parentId);
|
||||
notes = await SearchEngineUtils.notesForQuery(search.query_pattern);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -7,6 +7,7 @@ const { shim } = require('lib/shim.js');
|
||||
const { _ } = require('lib/locale');
|
||||
const md5 = require('md5');
|
||||
const MdToHtml_Katex = require('lib/MdToHtml_Katex');
|
||||
const { pregQuote } = require('lib/string-utils.js');
|
||||
|
||||
class MdToHtml {
|
||||
|
||||
@@ -311,6 +312,8 @@ class MdToHtml {
|
||||
output.push(t.content);
|
||||
} else if (t.type === 'softbreak') {
|
||||
output.push('<br/>');
|
||||
} else if (t.type === 'hardbreak') {
|
||||
output.push('<br/>');
|
||||
} else if (t.type === 'hr') {
|
||||
output.push('<hr/>');
|
||||
} else {
|
||||
@@ -395,8 +398,6 @@ class MdToHtml {
|
||||
previousToken = t;
|
||||
}
|
||||
|
||||
output.unshift('<!-- START_OF_DOCUMENT -->');
|
||||
|
||||
// Insert the extra CSS at the top of the HTML
|
||||
|
||||
if (!ObjectUtils.isEmpty(extraCssBlocks)) {
|
||||
@@ -413,10 +414,30 @@ class MdToHtml {
|
||||
return output.join('');
|
||||
}
|
||||
|
||||
applyHighlightedKeywords_(body, keywords) {
|
||||
for (let i = 0; i < keywords.length; i++) {
|
||||
const k = keywords[i];
|
||||
|
||||
let regexString = '';
|
||||
|
||||
if (k.type === 'regex') {
|
||||
regexString = k.value;
|
||||
} else {
|
||||
regexString = pregQuote(k);
|
||||
}
|
||||
|
||||
const re = new RegExp('(^|\n|\b)(' + regexString + ')(\n|\b|$)', 'gi');
|
||||
body = body.replace(re, '$1<span class="highlighted-keyword">$2</span>$3');
|
||||
}
|
||||
|
||||
return body;
|
||||
}
|
||||
|
||||
render(body, style, options = null) {
|
||||
if (!options) options = {};
|
||||
if (!options.postMessageSyntax) options.postMessageSyntax = 'postMessage';
|
||||
if (!options.paddingBottom) options.paddingBottom = '0';
|
||||
if (!options.highlightedKeywords) options.highlightedKeywords = [];
|
||||
|
||||
const cacheKey = this.makeContentKey(this.loadedResources_, body, style, options);
|
||||
if (this.cachedContentKey_ === cacheKey) return this.cachedContent_;
|
||||
@@ -427,38 +448,34 @@ class MdToHtml {
|
||||
html: true,
|
||||
});
|
||||
|
||||
body = this.applyHighlightedKeywords_(body, options.highlightedKeywords);
|
||||
|
||||
// Add `file:` protocol in linkify to allow text in the format of "file://..." to translate into
|
||||
// file-URL links in html view
|
||||
md.linkify.add('file:', {
|
||||
|
||||
validate: function (text, pos, self) {
|
||||
var tail = text.slice(pos);
|
||||
|
||||
if (!self.re.file) {
|
||||
self.re.file = new RegExp(
|
||||
'^[\\/]{2,3}[\\S]+' // matches all local file URI on Win/Unix/MacOS systems including reserved characters in some OS (i.e. no OS specific sanity check)
|
||||
);
|
||||
}
|
||||
if (self.re.file.test(tail)) {
|
||||
return tail.match(self.re.file)[0].length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
validate: function (text, pos, self) {
|
||||
var tail = text.slice(pos);
|
||||
if (!self.re.file) {
|
||||
// matches all local file URI on Win/Unix/MacOS systems including reserved characters in some OS (i.e. no OS specific sanity check)
|
||||
self.re.file = new RegExp('^[\\/]{2,3}[\\S]+');
|
||||
}
|
||||
if (self.re.file.test(tail)) {
|
||||
return tail.match(self.re.file)[0].length;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
// enable file link URLs in MarkdownIt. Keeps other URL restrictions of MarkdownIt untouched.
|
||||
// Format [link name](file://...)
|
||||
md.validateLink = function (url) {
|
||||
|
||||
var BAD_PROTO_RE = /^(vbscript|javascript|data):/;
|
||||
var GOOD_DATA_RE = /^data:image\/(gif|png|jpeg|webp);/;
|
||||
|
||||
// url should be normalized at this point, and existing entities are decoded
|
||||
// url should be normalized at this point, and existing entities are decoded
|
||||
var str = url.trim().toLowerCase();
|
||||
|
||||
return BAD_PROTO_RE.test(str) ? (GOOD_DATA_RE.test(str) ? true : false) : true;
|
||||
|
||||
}
|
||||
|
||||
// This is currently used only so that the $expression$ and $$\nexpression\n$$ blocks are translated
|
||||
@@ -609,6 +626,11 @@ class MdToHtml {
|
||||
padding-left: .2em;
|
||||
}
|
||||
|
||||
.highlighted-keyword {
|
||||
background-color: #F3B717;
|
||||
color: black;
|
||||
}
|
||||
|
||||
/*
|
||||
This is to fix https://github.com/laurent22/joplin/issues/764
|
||||
Without this, the tag attached to an equation float at an absoluate position of the page,
|
||||
|
@@ -322,6 +322,17 @@ class WebDavApi {
|
||||
|
||||
const output = await loadResponseJson();
|
||||
|
||||
// Trying to fix 404 error issue with Nginx WebDAV server.
|
||||
// https://github.com/laurent22/joplin/issues/624
|
||||
// https://github.com/laurent22/joplin/issues/808
|
||||
// Not tested but someone confirmed it worked - https://github.com/laurent22/joplin/issues/808#issuecomment-443552858
|
||||
// and fix is narrowly scoped so shouldn't affect anything outside this particular edge case.
|
||||
const responseArray = this.arrayFromJson(output, ['d:multistatus', 'd:response']);
|
||||
if (responseArray && responseArray.length === 1) {
|
||||
const status = this.stringFromJson(output, ['d:multistatus', 'd:response', 0, 'd:propstat', 0, 'd:status', 0]);
|
||||
if (status && status.indexOf('404') >= 0) throw newError('Not found', 404);
|
||||
}
|
||||
|
||||
// Check that we didn't get for example an HTML page (as an error) instead of the JSON response
|
||||
// null responses are possible, for example for DELETE calls
|
||||
if (output !== null && typeof output === 'object' && !('d:multistatus' in output)) throw newError('Not a valid WebDAV response');
|
||||
|
@@ -34,7 +34,7 @@ class Dropdown extends React.Component {
|
||||
// Dimensions doesn't return quite the right dimensions so leave an extra gap to make
|
||||
// sure nothing is off screen.
|
||||
const listMaxHeight = windowHeight;
|
||||
const listHeight = Math.min(items.length * itemHeight, listMaxHeight); //Dimensions.get('window').height - this.state.headerSize.y - this.state.headerSize.height - 50;
|
||||
const listHeight = Math.min(items.length * itemHeight, listMaxHeight);
|
||||
const maxListTop = windowHeight - listHeight;
|
||||
const listTop = Math.min(maxListTop, this.state.headerSize.y + this.state.headerSize.height);
|
||||
|
||||
@@ -60,10 +60,6 @@ class Dropdown extends React.Component {
|
||||
|
||||
const headerWrapperStyle = Object.assign({}, this.props.headerWrapperStyle ? this.props.headerWrapperStyle : {}, {
|
||||
height: 35,
|
||||
// borderWidth: 1,
|
||||
// borderColor: '#ccc',
|
||||
//paddingLeft: 20,
|
||||
//paddingRight: 20,
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
@@ -91,6 +87,8 @@ class Dropdown extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
if (this.props.labelTransform && this.props.labelTransform === 'trim') headerLabel = headerLabel.trim();
|
||||
|
||||
const closeList = () => {
|
||||
this.setState({ listVisible: false });
|
||||
}
|
||||
|
@@ -13,9 +13,10 @@ const globalStyle = {
|
||||
colorFaded: "#777777", // For less important text
|
||||
fontSizeSmaller: 14,
|
||||
dividerColor: "#dddddd",
|
||||
strongDividerColor: "#aaaaaa",
|
||||
selectedColor: '#e5e5e5',
|
||||
disabledOpacity: 0.2,
|
||||
colorUrl: '#000CFF',
|
||||
colorUrl: '#7B81FF',
|
||||
textSelectionColor: "#0096FF",
|
||||
|
||||
raisedBackgroundColor: "#0080EF",
|
||||
@@ -72,6 +73,9 @@ function addExtraStyles(style) {
|
||||
style.lineInput = {
|
||||
color: style.color,
|
||||
backgroundColor: style.backgroundColor,
|
||||
borderBottomWidth: 1,
|
||||
borderColor: style.strongDividerColor,
|
||||
paddingBottom: 0,
|
||||
};
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
@@ -114,6 +118,7 @@ function themeStyle(theme) {
|
||||
output.color = '#dddddd';
|
||||
output.colorFaded = '#777777';
|
||||
output.dividerColor = '#555555';
|
||||
output.strongDividerColor = '#888888';
|
||||
output.selectedColor = '#333333';
|
||||
output.textSelectionColor = '#00AEFF';
|
||||
|
||||
@@ -125,6 +130,8 @@ function themeStyle(theme) {
|
||||
output.htmlBackgroundColor = 'rgb(29,32,36)';
|
||||
output.htmlLinkColor = 'rgb(166,166,255)';
|
||||
|
||||
output.colorUrl = '#7B81FF';
|
||||
|
||||
themeCache_[theme] = output;
|
||||
return addExtraStyles(themeCache_[theme]);
|
||||
}
|
||||
|
@@ -76,9 +76,18 @@ class NoteBodyViewer extends Component {
|
||||
|
||||
const mdOptions = {
|
||||
onResourceLoaded: () => {
|
||||
this.forceUpdate();
|
||||
if (this.resourceLoadedTimeoutId_) {
|
||||
clearTimeout(this.resourceLoadedTimeoutId_);
|
||||
this.resourceLoadedTimeoutId_ = null;
|
||||
}
|
||||
|
||||
this.resourceLoadedTimeoutId_ = setTimeout(() => {
|
||||
this.resourceLoadedTimeoutId_ = null;
|
||||
this.forceUpdate();
|
||||
}, 100);
|
||||
},
|
||||
paddingBottom: '3.8em', // Extra bottom padding to make it possible to scroll past the action button (so that it doesn't overlap the text)
|
||||
highlightedKeywords: this.props.highlightedKeywords,
|
||||
};
|
||||
|
||||
let html = this.mdToHtml_.render(note ? note.body : '', this.props.webViewStyle, mdOptions);
|
||||
|
@@ -362,18 +362,25 @@ class ScreenHeaderComponent extends Component {
|
||||
|
||||
if (folderPickerOptions && folderPickerOptions.enabled) {
|
||||
|
||||
const addFolderChildren = (folders, pickerItems, indent) => {
|
||||
folders.sort((a, b) => {
|
||||
return a.title.toLowerCase() < b.title.toLowerCase() ? -1 : +1;
|
||||
});
|
||||
|
||||
for (let i = 0; i < folders.length; i++) {
|
||||
const f = folders[i];
|
||||
pickerItems.push({ label: ' '.repeat(indent) + ' ' + Folder.displayTitle(f), value: f.id });
|
||||
pickerItems = addFolderChildren(f.children, pickerItems, indent + 1);
|
||||
}
|
||||
|
||||
return pickerItems;
|
||||
}
|
||||
|
||||
const titlePickerItems = (mustSelect) => {
|
||||
let output = [];
|
||||
if (mustSelect) output.push({ label: _('Move to notebook...'), value: null });
|
||||
for (let i = 0; i < this.props.folders.length; i++) {
|
||||
let f = this.props.folders[i];
|
||||
output.push({ label: Folder.displayTitle(f), value: f.id });
|
||||
}
|
||||
output.sort((a, b) => {
|
||||
if (a.value === null) return -1;
|
||||
if (b.value === null) return +1;
|
||||
return a.label.toLowerCase() < b.label.toLowerCase() ? -1 : +1;
|
||||
});
|
||||
const folderTree = Folder.buildTree(this.props.folders);
|
||||
output = addFolderChildren(folderTree, output, 0);
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -381,6 +388,7 @@ class ScreenHeaderComponent extends Component {
|
||||
<Dropdown
|
||||
items={titlePickerItems(!!folderPickerOptions.mustSelect)}
|
||||
itemHeight={35}
|
||||
labelTransform="trim"
|
||||
selectedValue={('selectedFolderId' in folderPickerOptions) ? folderPickerOptions.selectedFolderId : null}
|
||||
itemListStyle={{
|
||||
backgroundColor: theme.backgroundColor,
|
||||
|
@@ -83,10 +83,10 @@ class ConfigScreenComponent extends BaseScreenComponent {
|
||||
},
|
||||
}
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
// if (Platform.OS === 'ios') {
|
||||
styles.settingControl.borderBottomWidth = 1;
|
||||
styles.settingControl.borderBottomColor = theme.dividerColor;
|
||||
}
|
||||
styles.settingControl.borderBottomColor = theme.strongDividerColor;
|
||||
// }
|
||||
|
||||
styles.switchSettingText = Object.assign({}, styles.settingText);
|
||||
styles.switchSettingText.width = '80%';
|
||||
|
@@ -62,8 +62,8 @@ class DropboxLoginScreenComponent extends BaseScreenComponent {
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<Text style={this.styles().stepText}>{_('Step 2: Enter the code provided by Dropbox:')}</Text>
|
||||
<TextInput selectionColor={theme.textSelectionColor} value={this.state.authCode} onChangeText={this.shared_.authCodeInput_change} style={theme.lineInput}/>
|
||||
|
||||
<TextInput placeholder={_('Enter code here')} placeholderTextColor={theme.colorFaded} selectionColor={theme.textSelectionColor} value={this.state.authCode} onChangeText={this.shared_.authCodeInput_change} style={theme.lineInput}/>
|
||||
<View style={{height:10}}></View>
|
||||
<Button disabled={this.state.checkingAuthToken} title={_("Submit")} onPress={this.shared_.submit_click}></Button>
|
||||
|
||||
{/* Add this extra padding to make sure the view is scrollable when the keyboard is visible on small screens (iPhone SE) */}
|
||||
|
@@ -110,12 +110,9 @@ class EncryptionConfigScreenComponent extends BaseScreenComponent {
|
||||
const active = this.props.activeMasterKeyId === mk.id ? '✔' : '';
|
||||
|
||||
const inputStyle = {flex:1, marginRight: 10, color: theme.color};
|
||||
|
||||
if (Platform.OS === 'ios') {
|
||||
inputStyle.borderBottomWidth = 1;
|
||||
inputStyle.borderBottomColor = theme.dividerColor;
|
||||
}
|
||||
|
||||
inputStyle.borderBottomWidth = 1;
|
||||
inputStyle.borderBottomColor = theme.strongDividerColor;
|
||||
|
||||
return (
|
||||
<View key={mk.id}>
|
||||
<Text style={this.styles().titleText}>{_('Master Key %s', mk.id.substr(0,6))}</Text>
|
||||
|
@@ -37,6 +37,7 @@ 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');
|
||||
const SearchEngine = require('lib/services/SearchEngine');
|
||||
|
||||
import FileViewer from 'react-native-file-viewer';
|
||||
|
||||
@@ -156,12 +157,12 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
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) {
|
||||
if (resourceIds.indexOf(resource.id) >= 0 && this.refs.noteBodyViewer) {
|
||||
this.refs.noteBodyViewer.rebuildMd();
|
||||
}
|
||||
}
|
||||
|
||||
this.attachPhoto_onPress = this.attachPhoto_onPress.bind(this);
|
||||
this.takePhoto_onPress = this.takePhoto_onPress.bind(this);
|
||||
this.cameraView_onPhoto = this.cameraView_onPhoto.bind(this);
|
||||
this.cameraView_onCancel = this.cameraView_onCancel.bind(this);
|
||||
}
|
||||
@@ -306,7 +307,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
|
||||
showImagePicker(options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
ImagePicker.showImagePicker(options, (response) => {
|
||||
ImagePicker.launchImageLibrary(options, (response) => {
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
@@ -380,7 +381,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
let resource = Resource.new();
|
||||
resource.id = uuid.create();
|
||||
resource.mime = mimeType;
|
||||
resource.title = pickerResponse.fileName ? pickerResponse.fileName : _('Untitled');
|
||||
resource.title = pickerResponse.fileName ? pickerResponse.fileName : '';
|
||||
resource.file_extension = safeFileExtension(fileExtension(pickerResponse.fileName ? pickerResponse.fileName : localFilePath));
|
||||
|
||||
if (!resource.mime) resource.mime = 'application/octet-stream';
|
||||
@@ -419,15 +420,12 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
this.setState({ note: newNote });
|
||||
}
|
||||
|
||||
async attachImage_onPress() {
|
||||
const options = {
|
||||
mediaType: 'photo',
|
||||
};
|
||||
const response = await this.showImagePicker(options);
|
||||
async attachPhoto_onPress() {
|
||||
const response = await this.showImagePicker({ mediaType: 'photo' });
|
||||
await this.attachFile(response, 'image');
|
||||
}
|
||||
|
||||
attachPhoto_onPress() {
|
||||
takePhoto_onPress() {
|
||||
this.setState({ showCamera: true });
|
||||
}
|
||||
|
||||
@@ -530,6 +528,7 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
let canAttachPicture = true;
|
||||
if (Platform.OS === 'android' && Platform.Version < 21) canAttachPicture = false;
|
||||
if (canAttachPicture) {
|
||||
output.push({ title: _('Take photo'), onPress: () => { this.takePhoto_onPress(); } });
|
||||
output.push({ title: _('Attach photo'), onPress: () => { this.attachPhoto_onPress(); } });
|
||||
output.push({ title: _('Attach any file'), onPress: () => { this.attachFile_onPress(); } });
|
||||
output.push({ isDivider: true });
|
||||
@@ -583,21 +582,25 @@ class NoteScreenComponent extends BaseScreenComponent {
|
||||
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) => {
|
||||
this.saveOneProperty('body', newBody);
|
||||
};
|
||||
|
||||
let keywords = [];
|
||||
if (this.props.searchQuery) {
|
||||
const parsedQuery = SearchEngine.instance().parseQuery(this.props.searchQuery);
|
||||
keywords = SearchEngine.instance().allParsedQueryTerms(parsedQuery);
|
||||
}
|
||||
|
||||
bodyComponent = <NoteBodyViewer
|
||||
onJoplinLinkClick={this.onJoplinLinkClick_}
|
||||
ref="noteBodyViewer"
|
||||
style={this.styles().noteBodyViewer}
|
||||
webViewStyle={theme}
|
||||
note={note}
|
||||
highlightedKeywords={keywords}
|
||||
onCheckboxChange={(newBody) => { onCheckboxChange(newBody) }}
|
||||
/>
|
||||
} else {
|
||||
@@ -742,6 +745,7 @@ const NoteScreen = connect(
|
||||
folderId: state.selectedFolderId,
|
||||
itemType: state.selectedItemType,
|
||||
folders: state.folders,
|
||||
searchQuery: state.searchQuery,
|
||||
theme: state.settings.theme,
|
||||
sharedData: state.sharedData,
|
||||
showAdvancedOptions: state.settings.showAdvancedOptions,
|
||||
|
@@ -203,7 +203,7 @@ class NotesScreenComponent extends BaseScreenComponent {
|
||||
let title = parent ? parent.title : null;
|
||||
const addFolderNoteButtons = this.props.selectedFolderId && this.props.selectedFolderId != Folder.conflictFolderId();
|
||||
const thisComp = this;
|
||||
const actionButtonComp = this.props.noteSelectionEnabled ? null : <ActionButton addFolderNoteButtons={addFolderNoteButtons} parentFolderId={this.props.selectedFolderId}></ActionButton>
|
||||
const actionButtonComp = this.props.noteSelectionEnabled || !this.props.visible ? null : <ActionButton addFolderNoteButtons={addFolderNoteButtons} parentFolderId={this.props.selectedFolderId}></ActionButton>
|
||||
|
||||
return (
|
||||
<View style={rootStyle}>
|
||||
|
@@ -9,6 +9,7 @@ const { NoteItem } = require('lib/components/note-item.js');
|
||||
const { BaseScreenComponent } = require('lib/components/base-screen.js');
|
||||
const { themeStyle } = require('lib/components/global-style.js');
|
||||
const { dialogs } = require('lib/dialogs.js');
|
||||
const SearchEngineUtils = require('lib/services/SearchEngineUtils');
|
||||
const DialogBox = require('react-native-dialogbox').default;
|
||||
|
||||
class SearchScreenComponent extends BaseScreenComponent {
|
||||
@@ -105,17 +106,22 @@ class SearchScreenComponent extends BaseScreenComponent {
|
||||
let notes = []
|
||||
|
||||
if (query) {
|
||||
let p = query.split(' ');
|
||||
let temp = [];
|
||||
for (let i = 0; i < p.length; i++) {
|
||||
let t = p[i].trim();
|
||||
if (!t) continue;
|
||||
temp.push(t);
|
||||
}
|
||||
notes = await SearchEngineUtils.notesForQuery(query);
|
||||
|
||||
notes = await Note.previews(null, {
|
||||
anywherePattern: '*' + temp.join('*') + '*',
|
||||
});
|
||||
// Keeping the code below in case of compatibility issue with old versions
|
||||
// of Android and SQLite FTS.
|
||||
|
||||
// let p = query.split(' ');
|
||||
// let temp = [];
|
||||
// for (let i = 0; i < p.length; i++) {
|
||||
// let t = p[i].trim();
|
||||
// if (!t) continue;
|
||||
// temp.push(t);
|
||||
// }
|
||||
|
||||
// notes = await Note.previews(null, {
|
||||
// anywherePattern: '*' + temp.join('*') + '*',
|
||||
// });
|
||||
}
|
||||
|
||||
if (!this.isMounted_) return;
|
||||
|
@@ -49,8 +49,6 @@ class TagScreenComponent extends BaseScreenComponent {
|
||||
render() {
|
||||
let title = tag ? tag.title : '';
|
||||
|
||||
// <ActionButton addFolderNoteButtons={true} parentFolderId={this.props.selectedFolderId}></ActionButton>
|
||||
|
||||
const { navigate } = this.props.navigation;
|
||||
return (
|
||||
<View style={this.styles().screen}>
|
||||
|
@@ -44,7 +44,7 @@ class WelcomeScreenComponent extends BaseScreenComponent {
|
||||
<View style={this.rootStyle(this.props.theme).root} >
|
||||
<ScreenHeader title={_('Welcome')}/>
|
||||
<Text style={this.styles().message}>{message}</Text>
|
||||
<ActionButton addFolderNoteButtons={true}/>
|
||||
<ActionButton addFolderNoteButtons={true} parentFolderId={this.props.selectedFolderId}/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
@@ -56,6 +56,7 @@ const WelcomeScreen = connect(
|
||||
return {
|
||||
folders: state.folders,
|
||||
theme: state.settings.theme,
|
||||
selectedFolderId: state.selectedFolderId,
|
||||
};
|
||||
}
|
||||
)(WelcomeScreenComponent)
|
||||
|
@@ -1,3 +1,5 @@
|
||||
const { filename, fileExtension } = require('lib/path-utils');
|
||||
|
||||
class FsDriverBase {
|
||||
|
||||
async isDirectory(path) {
|
||||
@@ -19,6 +21,23 @@ class FsDriverBase {
|
||||
return output;
|
||||
}
|
||||
|
||||
async findUniqueFilename(name) {
|
||||
let counter = 1;
|
||||
|
||||
let nameNoExt = filename(name, true);
|
||||
let extension = fileExtension(name);
|
||||
if (extension) extension = '.' + extension;
|
||||
let nameToTry = nameNoExt + extension;
|
||||
while (true) {
|
||||
const exists = await this.exists(nameToTry);
|
||||
if (!exists) return nameToTry;
|
||||
nameToTry = nameNoExt + ' (' + counter + ')' + extension;
|
||||
counter++;
|
||||
if (counter >= 1000) nameToTry = nameNoExt + ' (' + ((new Date()).getTime()) + ')' + extension;
|
||||
if (counter >= 10000) throw new Error('Cannot find unique title');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = FsDriverBase;
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user