diff --git a/CliClient/app/command-done.js b/CliClient/app/command-done.js new file mode 100644 index 0000000000..d23e0bb865 --- /dev/null +++ b/CliClient/app/command-done.js @@ -0,0 +1,40 @@ +import { BaseCommand } from './base-command.js'; +import { app } from './app.js'; +import { _ } from 'lib/locale.js'; +import { BaseModel } from 'lib/base-model.js'; +import { Folder } from 'lib/models/folder.js'; +import { Note } from 'lib/models/note.js'; +import { time } from 'lib/time-utils.js'; + +class Command extends BaseCommand { + + usage() { + return 'done '; + } + + description() { + return _('Marks a todo as done.'); + } + + static async handleAction(args, isCompleted) { + const note = await app().loadItem(BaseModel.TYPE_NOTE, args.note); + if (!note) throw new Error(_('Cannot find "%s".', args.note)); + if (!note.is_todo) throw new Error(_('Note is not a todo: "%s"', args.note)); + + const todoCompleted = !!note.todo_completed; + + if (isCompleted === todoCompleted) return; + + await Note.save({ + id: note.id, + todo_completed: isCompleted ? time.unixMs() : 0, + }); + } + + async action(args) { + Command.handleAction(args, true); + } + +} + +module.exports = Command; \ No newline at end of file diff --git a/CliClient/app/command-undone.js b/CliClient/app/command-undone.js new file mode 100644 index 0000000000..8c3c7012c1 --- /dev/null +++ b/CliClient/app/command-undone.js @@ -0,0 +1,27 @@ +import { BaseCommand } from './base-command.js'; +import { app } from './app.js'; +import { _ } from 'lib/locale.js'; +import { BaseModel } from 'lib/base-model.js'; +import { Folder } from 'lib/models/folder.js'; +import { Note } from 'lib/models/note.js'; +import { time } from 'lib/time-utils.js'; + +const CommandDone = require('./command-done.js'); + +class Command extends BaseCommand { + + usage() { + return 'undone '; + } + + description() { + return _('Marks a todo as non-completed.'); + } + + async action(args) { + CommandDone.handleAction(args, false); + } + +} + +module.exports = Command; \ No newline at end of file diff --git a/CliClient/locales/en_GB.po b/CliClient/locales/en_GB.po index 845c38dc20..6c8f7e7c4d 100644 --- a/CliClient/locales/en_GB.po +++ b/CliClient/locales/en_GB.po @@ -102,6 +102,13 @@ msgid "" "specified the note is duplicated in the current notebook." msgstr "" +msgid "Marks a todo as done." +msgstr "" + +#, javascript-format +msgid "Note is not a todo: \"%s\"" +msgstr "" + msgid "Edit note." msgstr "" @@ -291,6 +298,9 @@ msgid "" "the todo back to a regular note." msgstr "" +msgid "Marks a todo as non-completed." +msgstr "" + msgid "" "Switches to [notebook] - all further operations will happen within this " "notebook." @@ -367,10 +377,6 @@ msgstr "" msgid "State: \"%s\"." msgstr "" -#, javascript-format -msgid "Last error: %s (stacktrace in log)." -msgstr "" - msgid "Cancelling..." msgstr "" @@ -393,6 +399,9 @@ msgstr "" msgid "Notebooks cannot be named \"%s\", which is a reserved title." msgstr "" +msgid "Untitled" +msgstr "" + msgid "This note does not have geolocation information." msgstr "" @@ -532,9 +541,6 @@ msgstr "" msgid "Refresh" msgstr "" -msgid "Untitled" -msgstr "" - msgid "Delete note?" msgstr "" diff --git a/CliClient/locales/fr_FR.po b/CliClient/locales/fr_FR.po index 687a5e65bb..f5fb256bb0 100644 --- a/CliClient/locales/fr_FR.po +++ b/CliClient/locales/fr_FR.po @@ -110,6 +110,13 @@ msgstr "" "Copie les notes correspondant à [nom] vers [carnet]. Si aucun carnet n'est " "spécifié, la note est dupliqué sur place." +msgid "Marks a todo as done." +msgstr "" + +#, javascript-format +msgid "Note is not a todo: \"%s\"" +msgstr "" + msgid "Edit note." msgstr "Editer la note." @@ -333,6 +340,9 @@ msgstr "" "terminé (Si la cible est une note, elle sera convertie en tâche). Utilisez " "\"clear\" pour convertir la tâche en note." +msgid "Marks a todo as non-completed." +msgstr "" + msgid "" "Switches to [notebook] - all further operations will happen within this " "notebook." @@ -414,10 +424,6 @@ msgstr "Objets distants supprimés : %d." msgid "State: \"%s\"." msgstr "Etat : %s." -#, javascript-format -msgid "Last error: %s (stacktrace in log)." -msgstr "Dernière erreur : %s (Plus d'information dans le journal d'erreurs)" - msgid "Cancelling..." msgstr "Annulation..." @@ -440,6 +446,9 @@ msgstr "Un carnet avec ce titre existe déjà : \"%s\"" msgid "Notebooks cannot be named \"%s\", which is a reserved title." msgstr "Les carnets ne peuvent être nommés \"%s\" car c'est un nom réservé." +msgid "Untitled" +msgstr "Sans titre" + msgid "This note does not have geolocation information." msgstr "Cette note n'a pas d'information d'emplacement." @@ -581,9 +590,6 @@ msgstr "Editer le carnet" msgid "Refresh" msgstr "Rafraîchir" -msgid "Untitled" -msgstr "Sans titre" - msgid "Delete note?" msgstr "Supprimer la note ?" @@ -638,6 +644,9 @@ msgstr "" msgid "Welcome" msgstr "Bienvenue" +#~ msgid "Last error: %s (stacktrace in log)." +#~ msgstr "Dernière erreur : %s (Plus d'information dans le journal d'erreurs)" + #, fuzzy #~ msgid "Cancelling command..." #~ msgstr "Annulation..." diff --git a/CliClient/locales/joplin.pot b/CliClient/locales/joplin.pot index 845c38dc20..6c8f7e7c4d 100644 --- a/CliClient/locales/joplin.pot +++ b/CliClient/locales/joplin.pot @@ -102,6 +102,13 @@ msgid "" "specified the note is duplicated in the current notebook." msgstr "" +msgid "Marks a todo as done." +msgstr "" + +#, javascript-format +msgid "Note is not a todo: \"%s\"" +msgstr "" + msgid "Edit note." msgstr "" @@ -291,6 +298,9 @@ msgid "" "the todo back to a regular note." msgstr "" +msgid "Marks a todo as non-completed." +msgstr "" + msgid "" "Switches to [notebook] - all further operations will happen within this " "notebook." @@ -367,10 +377,6 @@ msgstr "" msgid "State: \"%s\"." msgstr "" -#, javascript-format -msgid "Last error: %s (stacktrace in log)." -msgstr "" - msgid "Cancelling..." msgstr "" @@ -393,6 +399,9 @@ msgstr "" msgid "Notebooks cannot be named \"%s\", which is a reserved title." msgstr "" +msgid "Untitled" +msgstr "" + msgid "This note does not have geolocation information." msgstr "" @@ -532,9 +541,6 @@ msgstr "" msgid "Refresh" msgstr "" -msgid "Untitled" -msgstr "" - msgid "Delete note?" msgstr "" diff --git a/ReactNativeClient/lib/components/note-body-viewer.js b/ReactNativeClient/lib/components/note-body-viewer.js index d22edef274..20dbf1a908 100644 --- a/ReactNativeClient/lib/components/note-body-viewer.js +++ b/ReactNativeClient/lib/components/note-body-viewer.js @@ -13,7 +13,10 @@ class NoteBodyViewer extends Component { super(); this.state = { resources: {}, + webViewLoaded: false, } + + this.isMounted_ = false; } async loadResource(id) { @@ -25,6 +28,14 @@ class NoteBodyViewer extends Component { this.setState({ resources: newResources }); } + componentWillMount() { + this.isMounted_ = true; + } + + componentWillUnmount() { + this.isMounted_ = false; + } + toggleTickAt(body, index) { let counter = -1; while (body.indexOf('- [ ]') >= 0 || body.indexOf('- [X]') >= 0) { @@ -162,20 +173,36 @@ class NoteBodyViewer extends Component { return html; } + onLoadEnd() { + if (this.state.webViewLoaded) return; + + // Need to display after a delay to avoid a white flash before + // the content is displayed. + setTimeout(() => { + if (!this.isMounted_) return; + + this.setState({ webViewLoaded: true }); + }, 100); + } + render() { const note = this.props.note; const style = this.props.style; const onCheckboxChange = this.props.onCheckboxChange; + const html = this.markdownToHtml(note ? note.body : '', this.props.webViewStyle); + + let webViewStyle = {} + if (!this.state.webViewLoaded) webViewStyle.display = 'none'; return ( this.onLoadEnd()} onMessage={(event) => { let msg = event.nativeEvent.data; - //reg.logger().info('postMessage received: ' + msg); - if (msg.indexOf('checkboxclick:') === 0) { msg = msg.split(':'); let index = Number(msg[msg.length - 1]); diff --git a/ReactNativeClient/lib/components/screens/note.js b/ReactNativeClient/lib/components/screens/note.js index 39340f49e4..24a16913b0 100644 --- a/ReactNativeClient/lib/components/screens/note.js +++ b/ReactNativeClient/lib/components/screens/note.js @@ -196,7 +196,11 @@ class NoteScreenComponent extends BaseScreenComponent { } let isNew = !note.id; - if (!note.title) note.title = _('Untitled'); + + if (isNew && !note.title) { + note.title = Note.defaultTitle(note); + } + note = await Note.save(note); this.setState({ lastSavedNote: Object.assign({}, note), diff --git a/ReactNativeClient/lib/models/note.js b/ReactNativeClient/lib/models/note.js index aeeab847bd..bcae8f5352 100644 --- a/ReactNativeClient/lib/models/note.js +++ b/ReactNativeClient/lib/models/note.js @@ -41,6 +41,17 @@ class Note extends BaseItem { return super.serialize(note, 'note', fieldNames); } + static defaultTitle(note) { + if (note.title && note.title.length) return note.title; + + if (note.body && note.body.length) { + const lines = note.body.trim().split("\n"); + return lines[0].trim().substr(0, 80).trim(); + } + + return _('Untitled'); + } + static geolocationUrl(note) { if (!('latitude' in note) || !('longitude' in note)) throw new Error('Latitude or longitude is missing'); if (!Number(note.latitude) && !Number(note.longitude)) throw new Error(_('This note does not have geolocation information.'));