diff --git a/CliClient/app/app.js b/CliClient/app/app.js index 6e6ecc336..3ce70c334 100644 --- a/CliClient/app/app.js +++ b/CliClient/app/app.js @@ -520,7 +520,7 @@ class Application { const result = next(action); const newState = store.getState(); - if (action.type == 'FOLDERS_SELECT') { + if (action.type == 'FOLDERS_SELECT' || action.type === 'FOLDER_DELETE') { Setting.setValue('activeFolderId', newState.selectedFolderId); this.currentFolder_ = newState.selectedFolderId ? await Folder.load(newState.selectedFolderId) : null; await this.refreshNotes(Folder.modelType(), newState.selectedFolderId); diff --git a/CliClient/app/gui/FolderListWidget.js b/CliClient/app/gui/FolderListWidget.js index 1b40fe592..8b9f5e15e 100644 --- a/CliClient/app/gui/FolderListWidget.js +++ b/CliClient/app/gui/FolderListWidget.js @@ -43,8 +43,8 @@ class FolderListWidget extends ListWidget { } set selectedFolderId(v) { - this.updateIndexFromSelectedItemId() this.selectedFolderId_ = v; + this.updateIndexFromSelectedItemId() this.invalidate(); } @@ -53,8 +53,8 @@ class FolderListWidget extends ListWidget { } set selectedSearchId(v) { - this.updateIndexFromSelectedItemId() this.selectedSearchId_ = v; + this.updateIndexFromSelectedItemId() this.invalidate(); } @@ -63,8 +63,8 @@ class FolderListWidget extends ListWidget { } set selectedTagId(v) { - this.updateIndexFromSelectedItemId() this.selectedTagId_ = v; + this.updateIndexFromSelectedItemId() this.invalidate(); } @@ -73,7 +73,7 @@ class FolderListWidget extends ListWidget { } set notesParentType(v) { - if (this.notesParentType_ === v) return; + //if (this.notesParentType_ === v) return; this.notesParentType_ = v; this.updateIndexFromSelectedItemId() this.invalidate(); @@ -84,8 +84,6 @@ class FolderListWidget extends ListWidget { } set searches(v) { - if (this.searches_ === v) return; - this.searches_ = v; this.updateItems_ = true; this.updateIndexFromSelectedItemId() @@ -97,8 +95,6 @@ class FolderListWidget extends ListWidget { } set tags(v) { - if (this.tags_ === v) return; - this.tags_ = v; this.updateItems_ = true; this.updateIndexFromSelectedItemId() @@ -110,15 +106,13 @@ class FolderListWidget extends ListWidget { } set folders(v) { - if (this.folders_ === v) return; - this.folders_ = v; this.updateItems_ = true; this.updateIndexFromSelectedItemId() this.invalidate(); } - - async onWillRender() { + + render() { if (this.updateItems_) { this.logger().debug('Rebuilding items...', this.notesParentType, this.selectedJoplinItemId, this.selectedSearchId); const wasSelectedItemId = this.selectedJoplinItemId; @@ -136,12 +130,14 @@ class FolderListWidget extends ListWidget { newItems = newItems.concat(this.searches); } - this.items = newItems; + this.items = newItems; this.notesParentType = previousParentType; this.updateIndexFromSelectedItemId(wasSelectedItemId) this.updateItems_ = false; } + + super.render(); } get selectedJoplinItemId() { diff --git a/README.md b/README.md index 61a96035d..3c81b2be6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,12 @@ # Joplin -INTRODUCTION +Joplin is a free, open source note taking and todo application, which can handle a large number of notes organised into notebooks. The notes are searchable, can be copied, tagged and modified with your own text editor. + +Notes exported from Evenotes via .enex files [can be imported](#importing-notes-from-evernote) into Joplin, including the formatted content (which is converted to markdown), resources (images, attachments, etc.) and complete metadata (geolocation, updated time, created time, etc.). + +The notes can be [synchronised](#synchronisation) with various targets including the file system (for example with a network directory) or with Microsoft OneDrive. When synchronising the notes, notebooks, tags and other metadata are saved to plain text files which can be easily inspected, backed up and moved around. + +The application is still under development but is out of Beta and should be suitable for every day use. ![Joplin Terminal Screenshot](https://github.com/laurent22/joplin/blob/master/docs/images/ScreenshotTerminal.png) @@ -114,19 +120,21 @@ INFO ABOUT SYNC # Android client -LINK TO ANDROID CLIENT +An Android client is available and can synchronise with the terminal client via OneDrive: + +Get it on Google Play # URLs By pressing Ctrl+Click on a URL, most terminals will open that URL in the default browser. However, one issue especially with long URLs is that they can end up like this: - + Not only it makes the text hard to read, but the link, being cut in two, will also not be clickable. As a solution Joplin tries to start a mini-server in the background and, if successful, all the links will be converted to a much shorter, local URL: - + With this it means that not only the text will be more readable but links are also unlikely to be cut. Note that both resources (files that are attached to notes) and external links are handled in this way. @@ -147,8 +155,6 @@ The applications is currently available in English and French. If you would like There are two types of shortcuts: those that manipulate the user interface directly, such as TAB to move from one widget to another, and those that are simply shortcuts to actual commands. In a way similar to Vim, these commands are generally a verb followed by an object. For example, typing `mn` ([m]ake [n]ote), is used to create a new note, it will switch the interface to command line mode and pre-fill it with `mknote ""` from where the title of the note can be entered. -*List of shortcuts:* - Tab Give focus to next widget Shift+Tab Give focus to previous widget : Enter command line mode diff --git a/ReactNativeClient/lib/reducer.js b/ReactNativeClient/lib/reducer.js index 8124d7041..02593ab57 100644 --- a/ReactNativeClient/lib/reducer.js +++ b/ReactNativeClient/lib/reducer.js @@ -39,6 +39,38 @@ function historyCanGoBackTo(route) { return true; } +function folderOrNoteDelete(state, action) { + let newState = Object.assign({}, state); + + const idKey = action.type === 'FOLDER_DELETE' ? 'folderId' : 'noteId'; + const listKey = action.type === 'FOLDER_DELETE' ? 'folders' : 'notes'; + const selectedItemKey = action.type === 'FOLDER_DELETE' ? 'selectedFolderId' : 'selectedNoteId'; + + let previousIndex = 0; + let newItems = []; + const items = state[listKey]; + for (let i = 0; i < items.length; i++) { + let f = items[i]; + if (f.id == action[idKey]) { + previousIndex = i; + continue; + } + newItems.push(f); + } + + newState = Object.assign({}, state); + newState[listKey] = newItems; + + if (previousIndex >= newItems.length) { + previousIndex = newItems.length - 1; + } + + const newIndex = previousIndex >= 0 ? newItems[previousIndex].id : null; + newState[selectedItemKey] = newIndex; + + return newState; +} + function updateOneTagOrFolder(state, action) { let newItems = action.type === 'TAGS_UPDATE_ONE' ? state.tags.splice(0) : state.folders.splice(0); let item = action.type === 'TAGS_UPDATE_ONE' ? action.tag : action.folder; @@ -87,7 +119,6 @@ const reducer = (state = defaultState, action) => { try { switch (action.type) { - case 'NAV_BACK': if (!navHistory.length) break; @@ -246,25 +277,7 @@ const reducer = (state = defaultState, action) => { case 'NOTES_DELETE': - var previousIndex = 0; - var newNotes = []; - for (let i = 0; i < state.notes.length; i++) { - let f = state.notes[i]; - if (f.id == action.noteId) { - previousIndex = i; - continue; - } - newNotes.push(f); - } - - newState = Object.assign({}, state); - newState.notes = newNotes; - - if (previousIndex >= newNotes.length) { - previousIndex = newNotes.length - 1; - } - - newState.selectedNoteId = previousIndex >= 0 ? newNotes[previousIndex].id : null; + newState = folderOrNoteDelete(state, action); break; case 'FOLDERS_UPDATE_ALL': @@ -293,55 +306,16 @@ const reducer = (state = defaultState, action) => { case 'TAGS_UPDATE_ONE': newState = updateOneTagOrFolder(state, action); - - // var newTags = state.tags.splice(0); - // var found = false; - // for (let i = 0; i < newTags.length; i++) { - // let n = newTags[i]; - // if (n.id == action.tag.id) { - // newTags[i] = Object.assign(newTags[i], action.tag); - // found = true; - // break; - // } - // } - - // if (!found) newTags.push(action.tag); - - // newState = Object.assign({}, state); - // newState.tags = newTags; break; case 'FOLDERS_UPDATE_ONE': newState = updateOneTagOrFolder(state, action); - // var newFolders = state.folders.splice(0); - // var found = false; - // for (let i = 0; i < newFolders.length; i++) { - // let n = newFolders[i]; - // if (n.id == action.folder.id) { - // newFolders[i] = Object.assign(newFolders[i], action.folder); - // found = true; - // break; - // } - // } - - // if (!found) newFolders.push(action.folder); - - // newState = Object.assign({}, state); - // newState.folders = newFolders; break; case 'FOLDER_DELETE': - var newFolders = []; - for (let i = 0; i < state.folders.length; i++) { - let f = state.folders[i]; - if (f.id == action.folderId) continue; - newFolders.push(f); - } - - newState = Object.assign({}, state); - newState.folders = newFolders; + newState = folderOrNoteDelete(state, action); break; case 'SIDE_MENU_TOGGLE':