diff --git a/CliClient/app/app-gui.js b/CliClient/app/app-gui.js index 51857ba21..d8edf2814 100644 --- a/CliClient/app/app-gui.js +++ b/CliClient/app/app-gui.js @@ -19,6 +19,7 @@ const RootWidget = require('tkwidgets/RootWidget.js'); const WindowWidget = require('tkwidgets/WindowWidget.js'); const NoteWidget = require('./gui/NoteWidget.js'); +const FolderListWidget = require('./gui/FolderListWidget.js'); class AppGui { @@ -50,20 +51,22 @@ class AppGui { this.rootWidget_ = new ReduxRootWidget(this.store_); this.rootWidget_.setName('rootWidget'); - const folderList = new ListWidget(); - folderList.items = []; - folderList.setItemRenderer((item) => { - return item.title; - }); - folderList.setStyle({ - borderBottomWidth: 1, - }); + const folderList = new FolderListWidget(); + folderList.setStyle({ borderBottomWidth: 1 }); folderList.setName('folderList'); folderList.setVStretch(true); folderList.on('currentItemChange', async () => { const folder = folderList.currentItem; - this.app().switchCurrentFolder(folder); - await this.updateNoteList(folder ? folder.id : null); + this.store_.dispatch({ + type: 'FOLDERS_SELECT', + folderId: folder ? folder.id : 0, + }); + }); + this.rootWidget_.connect(folderList, (state) => { + return { + selectedFolderId: state.selectedFolderId, + items: state.folders, + }; }); const noteList = new ListWidget(); @@ -84,27 +87,22 @@ class AppGui { }); noteList.on('currentItemChange', async () => { let note = noteList.currentItem; - if (note) { - if (!('body' in note)) { - note = await Note.load(note.id); - } - noteList.setCurrentItem(note); - } - this.store_.dispatch({ type: 'NOTES_SELECT', noteId: note ? note.id : 0, }); - //await this.updateNoteText(note); + }); + this.rootWidget_.connect(noteList, (state) => { + return { + selectedNoteId: state.selectedNoteId, + items: state.notes, + }; }); const noteText = new NoteWidget(); noteText.setVStretch(true); noteText.setName('noteText'); - noteText.setStyle({ - borderBottomWidth: 1, - }); - + noteText.setStyle({ borderBottomWidth: 1 }); this.rootWidget_.connect(noteText, (state) => { return { noteId: state.selectedNoteId }; }); @@ -265,7 +263,7 @@ class AppGui { const consoleWidget = this.widget('console'); - await this.updateFolderList(); + //await this.updateFolderList(); term.grabInput(); @@ -295,6 +293,13 @@ class AppGui { termutils.showCursor(term); console.error(error); } + + process.on('unhandledRejection', (reason, p) => { + term.fullscreen(false); + termutils.showCursor(term); + console.error('Unhandled promise rejection', p, 'reason:', reason); + process.exit(1); + }); } } diff --git a/CliClient/app/app.js b/CliClient/app/app.js index 8ac0bf6c8..436df547e 100644 --- a/CliClient/app/app.js +++ b/CliClient/app/app.js @@ -1,7 +1,8 @@ -import { createStore } from 'redux'; +import { createStore, applyMiddleware } from 'redux'; import { reducer, defaultState } from 'lib/reducer.js'; import { JoplinDatabase } from 'lib/joplin-database.js'; import { Database } from 'lib/database.js'; +import { FoldersScreenUtils } from 'lib/folders-screen-utils.js'; import { DatabaseDriverNode } from 'lib/database-driver-node.js'; import { BaseModel } from 'lib/base-model.js'; import { Folder } from 'lib/models/folder.js'; @@ -40,6 +41,10 @@ class Application { return this.logger_; } + store() { + return this.store_; + } + currentFolder() { return this.currentFolder_; } @@ -369,6 +374,45 @@ class Application { return this.activeCommand_; } + async refreshNotes() { + const state = this.store().getState(); + + let options = { + order: state.notesOrder, + uncompletedTodosOnTop: Setting.value('uncompletedTodosOnTop'), + }; + + const notes = await Note.previews(state.selectedFolderId, options); + + this.store().dispatch({ + type: 'NOTES_UPDATE_ALL', + notes: notes, + }); + + this.store().dispatch({ + type: 'NOTES_SELECT', + noteId: notes.length ? notes[0].id : null, + }); + } + + generalMiddleware() { + const middleware = store => next => async (action) => { + this.logger().info('Middleware reducer action', action.type); + + const result = next(action); + const newState = store.getState(); + + if (action.type == 'FOLDERS_SELECT') { + Setting.setValue('activeFolderId', newState.selectedFolderId); + await this.refreshNotes(); + } + + return result; + } + + return middleware; + } + async start() { let argv = process.argv; let startFlags = await this.handleStartFlags_(argv); @@ -406,6 +450,10 @@ class Application { this.dbLogger_.addTarget('file', { path: profileDir + '/log-database.txt' }); this.dbLogger_.setLevel(initArgs.logLevel); + if (Setting.value('env') === 'dev') { + this.dbLogger_.setLevel(Logger.LEVEL_WARN); + } + const packageJson = require('./package.json'); this.logger_.info(sprintf('Starting %s %s (%s)...', packageJson.name, packageJson.version, Setting.value('env'))); this.logger_.info('Profile directory: ' + profileDir); @@ -417,7 +465,6 @@ class Application { reg.setDb(this.database_); BaseModel.db_ = this.database_; - //BaseModel.dispatch = (action) => { this.baseModelListener(action) } await Setting.load(); @@ -439,14 +486,22 @@ class Application { if (!this.currentFolder_) this.currentFolder_ = await Folder.defaultFolder(); Setting.setValue('activeFolderId', this.currentFolder_ ? this.currentFolder_.id : ''); - let store = createStore(reducer); - BaseModel.dispatch = store.dispatch; + this.store_ = createStore(reducer, applyMiddleware(this.generalMiddleware())); + BaseModel.dispatch = this.store().dispatch; + FoldersScreenUtils.dispatch = this.store().dispatch; const AppGui = require('./app-gui.js'); - this.gui_ = new AppGui(this, store); + this.gui_ = new AppGui(this, this.store()); this.gui_.setLogger(this.logger_); await this.gui_.start(); + await FoldersScreenUtils.refreshFolders(); + + this.store().dispatch({ + type: 'FOLDERS_SELECT', + folderId: Setting.value('activeFolderId'), + }); + // if (this.autocompletion_.active) { // if (this.autocompletion_.install) { // try { diff --git a/CliClient/app/gui/FolderListWidget.js b/CliClient/app/gui/FolderListWidget.js new file mode 100644 index 000000000..3d6bfe392 --- /dev/null +++ b/CliClient/app/gui/FolderListWidget.js @@ -0,0 +1,28 @@ +const Folder = require('lib/models/folder.js').Folder; +const ListWidget = require('tkwidgets/ListWidget.js'); + +class FolderListWidget extends ListWidget { + + constructor() { + super(); + this.selectedFolderId_ = 0; + + this.setItemRenderer((item) => { + return item.title; + }); + } + + get selectedFolderId() { + return this.selectedFolderId_; + } + + set selectedFolderId(v) { + if (v === this.selectedFolderId_) return; + this.selectedFolderId_ = v; + const index = this.itemIndexByKey('id', this.selectedFolderId_); + this.currentIndex = index >= 0 ? index : 0; + } + +} + +module.exports = FolderListWidget; \ No newline at end of file diff --git a/CliClient/app/gui/NoteListWidget.js b/CliClient/app/gui/NoteListWidget.js new file mode 100644 index 000000000..9db88e4de --- /dev/null +++ b/CliClient/app/gui/NoteListWidget.js @@ -0,0 +1,27 @@ +const Note = require('lib/models/note.js').Note; +const ListWidget = require('tkwidgets/ListWidget.js'); + +class NoteListWidget extends ListWidget { + + constructor() { + super(); + this.selectedNoteId_ = 0; + this.setItemRenderer((item) => { + return item.title; + }); + } + + get selectedNoteId() { + return this.selectedNoteId_; + } + + set selectedNoteId(v) { + if (v === this.selectedNoteId_) return; + this.selectedNoteId_ = v; + const index = this.itemIndexByKey('id', this.selectedNoteId_); + this.currentIndex = index >= 0 ? index : 0; + } + +} + +module.exports = NoteListWidget; \ No newline at end of file diff --git a/CliClient/app/gui/NoteWidget.js b/CliClient/app/gui/NoteWidget.js index 8dab0b708..6bf03f018 100644 --- a/CliClient/app/gui/NoteWidget.js +++ b/CliClient/app/gui/NoteWidget.js @@ -1,5 +1,3 @@ -//import { Note } from 'lib/models/note.js'; - const Note = require('lib/models/note.js').Note; const TextWidget = require('tkwidgets/TextWidget.js'); @@ -25,7 +23,7 @@ class NoteWidget extends TextWidget { async willRender() { if (!this.note_ && this.noteId_) { this.note_ = await Note.load(this.noteId_); - this.text = this.note_.body; + this.text = this.note_.title + "\n\n" + this.note_.body; } } diff --git a/CliClient/app/main.js b/CliClient/app/main.js index e3c1246c7..2bd4c6fcc 100644 --- a/CliClient/app/main.js +++ b/CliClient/app/main.js @@ -19,10 +19,6 @@ import { FsDriverNode } from './fs-driver-node.js'; import { shimInit } from 'lib/shim-init-node.js'; import { _ } from 'lib/locale.js'; -process.on('unhandledRejection', (reason, p) => { - console.error('Unhandled promise rejection', p, 'reason:', reason); -}); - const fsDriver = new FsDriverNode(); Logger.fsDriver_ = fsDriver; Resource.fsDriver_ = fsDriver; diff --git a/ReactNativeClient/lib/components/side-menu-content.js b/ReactNativeClient/lib/components/side-menu-content.js index 7e09a1fd9..ba1510afe 100644 --- a/ReactNativeClient/lib/components/side-menu-content.js +++ b/ReactNativeClient/lib/components/side-menu-content.js @@ -6,7 +6,7 @@ import { Log } from 'lib/log.js'; import { Tag } from 'lib/models/tag.js'; import { Note } from 'lib/models/note.js'; import { Setting } from 'lib/models/setting.js'; -import { FoldersScreenUtils } from 'lib/components/screens/folders-utils.js' +import { FoldersScreenUtils } from 'lib/folders-screen-utils.js' import { Synchronizer } from 'lib/synchronizer.js'; import { reg } from 'lib/registry.js'; import { _ } from 'lib/locale.js'; diff --git a/ReactNativeClient/lib/components/screens/folders-utils.js b/ReactNativeClient/lib/folders-screen-utils.js similarity index 99% rename from ReactNativeClient/lib/components/screens/folders-utils.js rename to ReactNativeClient/lib/folders-screen-utils.js index b323a270f..ebdca557d 100644 --- a/ReactNativeClient/lib/components/screens/folders-utils.js +++ b/ReactNativeClient/lib/folders-screen-utils.js @@ -4,6 +4,7 @@ class FoldersScreenUtils { static async refreshFolders() { let initialFolders = await Folder.all({ includeConflictFolder: true }); + this.dispatch({ type: 'FOLDERS_UPDATE_ALL', folders: initialFolders, diff --git a/ReactNativeClient/lib/reducer.js b/ReactNativeClient/lib/reducer.js index 9d8a825eb..faa5fbb87 100644 --- a/ReactNativeClient/lib/reducer.js +++ b/ReactNativeClient/lib/reducer.js @@ -124,6 +124,12 @@ const reducer = (state = defaultState, action) => { newState.selectedNoteId = action.noteId; break; + case 'FOLDERS_SELECT': + + newState = Object.assign({}, state); + newState.selectedFolderId = action.folderId; + break; + case 'SETTINGS_UPDATE_ALL': newState = Object.assign({}, state); diff --git a/ReactNativeClient/root.js b/ReactNativeClient/root.js index e216d8392..3527c4708 100644 --- a/ReactNativeClient/root.js +++ b/ReactNativeClient/root.js @@ -9,7 +9,7 @@ import { AppNav } from 'lib/components/app-nav.js' import { Logger } from 'lib/logger.js' import { Note } from 'lib/models/note.js' import { Folder } from 'lib/models/folder.js' -import { FoldersScreenUtils } from 'lib/components/screens/folders-utils.js'; +import { FoldersScreenUtils } from 'lib/folders-screen-utils.js'; import { Resource } from 'lib/models/resource.js' import { Tag } from 'lib/models/tag.js' import { NoteTag } from 'lib/models/note-tag.js'