2017-10-05 19:17:56 +02:00
|
|
|
import { Logger } from 'lib/logger.js';
|
|
|
|
import { Folder } from 'lib/models/folder.js';
|
|
|
|
import { Note } from 'lib/models/note.js';
|
2017-10-06 19:38:17 +02:00
|
|
|
import { cliUtils } from './cli-utils.js';
|
2017-10-07 18:30:27 +02:00
|
|
|
import { reducer, defaultState } from 'lib/reducer.js';
|
2017-10-05 19:17:56 +02:00
|
|
|
|
|
|
|
const tk = require('terminal-kit');
|
|
|
|
const termutils = require('tkwidgets/framework/termutils.js');
|
|
|
|
const Renderer = require('tkwidgets/framework/Renderer.js');
|
|
|
|
|
2017-10-07 18:30:27 +02:00
|
|
|
const BaseWidget = require('tkwidgets/BaseWidget.js');
|
2017-10-05 19:17:56 +02:00
|
|
|
const ListWidget = require('tkwidgets/ListWidget.js');
|
|
|
|
const TextWidget = require('tkwidgets/TextWidget.js');
|
|
|
|
const ConsoleWidget = require('tkwidgets/ConsoleWidget.js');
|
|
|
|
const HLayoutWidget = require('tkwidgets/HLayoutWidget.js');
|
|
|
|
const VLayoutWidget = require('tkwidgets/VLayoutWidget.js');
|
2017-10-07 23:01:03 +02:00
|
|
|
const ReduxRootWidget = require('tkwidgets/ReduxRootWidget.js');
|
2017-10-05 19:17:56 +02:00
|
|
|
const RootWidget = require('tkwidgets/RootWidget.js');
|
|
|
|
const WindowWidget = require('tkwidgets/WindowWidget.js');
|
|
|
|
|
2017-10-07 23:01:03 +02:00
|
|
|
const NoteWidget = require('./gui/NoteWidget.js');
|
|
|
|
|
2017-10-05 19:17:56 +02:00
|
|
|
class AppGui {
|
|
|
|
|
2017-10-07 23:01:03 +02:00
|
|
|
constructor(app, store) {
|
2017-10-05 19:17:56 +02:00
|
|
|
this.app_ = app;
|
2017-10-07 23:01:03 +02:00
|
|
|
this.store_ = store;
|
2017-10-07 18:30:27 +02:00
|
|
|
|
|
|
|
BaseWidget.setLogger(app.logger());
|
|
|
|
|
2017-10-05 19:17:56 +02:00
|
|
|
this.term_ = tk.terminal;
|
|
|
|
this.renderer_ = null;
|
|
|
|
this.logger_ = new Logger();
|
2017-10-07 23:01:03 +02:00
|
|
|
this.buildUi();
|
|
|
|
|
2017-10-06 19:38:17 +02:00
|
|
|
this.renderer_ = new Renderer(this.term(), this.rootWidget_);
|
|
|
|
|
2017-10-07 20:05:35 +02:00
|
|
|
this.renderer_.on('renderDone', async (event) => {
|
|
|
|
if (this.widget('console').hasFocus()) this.widget('console').resetCursor();
|
|
|
|
});
|
|
|
|
|
2017-10-06 19:38:17 +02:00
|
|
|
this.app_.on('modelAction', async (event) => {
|
|
|
|
await this.handleModelAction(event.action);
|
|
|
|
});
|
2017-10-07 18:30:27 +02:00
|
|
|
|
|
|
|
this.shortcuts_ = this.setupShortcuts();
|
2017-10-06 19:38:17 +02:00
|
|
|
}
|
2017-10-05 19:17:56 +02:00
|
|
|
|
2017-10-06 19:38:17 +02:00
|
|
|
buildUi() {
|
2017-10-07 23:01:03 +02:00
|
|
|
this.rootWidget_ = new ReduxRootWidget(this.store_);
|
|
|
|
this.rootWidget_.setName('rootWidget');
|
2017-10-05 19:17:56 +02:00
|
|
|
|
|
|
|
const folderList = new ListWidget();
|
2017-10-07 22:04:53 +02:00
|
|
|
folderList.items = [];
|
2017-10-05 19:17:56 +02:00
|
|
|
folderList.setItemRenderer((item) => {
|
|
|
|
return item.title;
|
|
|
|
});
|
|
|
|
folderList.setStyle({
|
|
|
|
borderBottomWidth: 1,
|
|
|
|
});
|
|
|
|
folderList.setName('folderList');
|
|
|
|
folderList.setVStretch(true);
|
|
|
|
folderList.on('currentItemChange', async () => {
|
2017-10-07 22:04:53 +02:00
|
|
|
const folder = folderList.currentItem;
|
2017-10-07 20:05:35 +02:00
|
|
|
this.app().switchCurrentFolder(folder);
|
2017-10-05 19:17:56 +02:00
|
|
|
await this.updateNoteList(folder ? folder.id : null);
|
|
|
|
});
|
|
|
|
|
|
|
|
const noteList = new ListWidget();
|
2017-10-07 22:04:53 +02:00
|
|
|
noteList.items = [];
|
2017-10-06 19:38:17 +02:00
|
|
|
noteList.setItemRenderer((note) => {
|
|
|
|
let label = note.title;
|
|
|
|
if (note.is_todo) {
|
|
|
|
label = '[' + (note.todo_completed ? 'X' : ' ') + '] ' + label;
|
|
|
|
}
|
|
|
|
return label;
|
2017-10-05 19:17:56 +02:00
|
|
|
});
|
|
|
|
noteList.setName('noteList');
|
|
|
|
noteList.setVStretch(true);
|
|
|
|
noteList.setStyle({
|
|
|
|
borderBottomWidth: 1,
|
|
|
|
borderLeftWidth: 1,
|
|
|
|
borderRightWidth: 1,
|
|
|
|
});
|
|
|
|
noteList.on('currentItemChange', async () => {
|
2017-10-07 22:04:53 +02:00
|
|
|
let note = noteList.currentItem;
|
2017-10-06 19:38:17 +02:00
|
|
|
if (note) {
|
|
|
|
if (!('body' in note)) {
|
|
|
|
note = await Note.load(note.id);
|
|
|
|
}
|
|
|
|
noteList.setCurrentItem(note);
|
|
|
|
}
|
2017-10-07 23:01:03 +02:00
|
|
|
|
|
|
|
this.store_.dispatch({
|
|
|
|
type: 'NOTES_SELECT',
|
|
|
|
noteId: note ? note.id : 0,
|
|
|
|
});
|
|
|
|
//await this.updateNoteText(note);
|
2017-10-05 19:17:56 +02:00
|
|
|
});
|
|
|
|
|
2017-10-07 23:01:03 +02:00
|
|
|
const noteText = new NoteWidget();
|
2017-10-05 19:17:56 +02:00
|
|
|
noteText.setVStretch(true);
|
|
|
|
noteText.setName('noteText');
|
|
|
|
noteText.setStyle({
|
|
|
|
borderBottomWidth: 1,
|
|
|
|
});
|
|
|
|
|
2017-10-07 23:01:03 +02:00
|
|
|
this.rootWidget_.connect(noteText, (state) => {
|
|
|
|
return { noteId: state.selectedNoteId };
|
|
|
|
});
|
|
|
|
|
2017-10-06 19:38:17 +02:00
|
|
|
const consoleWidget = new ConsoleWidget();
|
|
|
|
consoleWidget.setHStretch(true);
|
|
|
|
consoleWidget.setName('console');
|
|
|
|
consoleWidget.on('accept', (event) => {
|
|
|
|
this.processCommand(event.input);
|
|
|
|
});
|
|
|
|
|
2017-10-06 20:01:10 +02:00
|
|
|
const hLayout = new HLayoutWidget();
|
2017-10-07 18:30:27 +02:00
|
|
|
hLayout.setName('hLayout');
|
2017-10-06 20:01:10 +02:00
|
|
|
hLayout.addChild(folderList, { type: 'stretch', factor: 1 });
|
|
|
|
hLayout.addChild(noteList, { type: 'stretch', factor: 1 });
|
|
|
|
hLayout.addChild(noteText, { type: 'stretch', factor: 1 });
|
2017-10-05 19:17:56 +02:00
|
|
|
|
2017-10-06 19:38:17 +02:00
|
|
|
const vLayout = new VLayoutWidget();
|
2017-10-07 18:30:27 +02:00
|
|
|
vLayout.setName('vLayout');
|
2017-10-06 19:38:17 +02:00
|
|
|
vLayout.addChild(hLayout, { type: 'stretch', factor: 1 });
|
|
|
|
vLayout.addChild(consoleWidget, { type: 'fixed', factor: 5 });
|
2017-10-05 19:17:56 +02:00
|
|
|
|
|
|
|
const win1 = new WindowWidget();
|
2017-10-06 19:38:17 +02:00
|
|
|
win1.addChild(vLayout);
|
2017-10-05 19:17:56 +02:00
|
|
|
win1.setName('mainWindow');
|
|
|
|
|
2017-10-07 23:01:03 +02:00
|
|
|
this.rootWidget_.addChild(win1);
|
2017-10-05 19:17:56 +02:00
|
|
|
}
|
|
|
|
|
2017-10-07 18:30:27 +02:00
|
|
|
setupShortcuts() {
|
|
|
|
const shortcuts = {};
|
|
|
|
|
2017-10-07 19:07:38 +02:00
|
|
|
shortcuts['DELETE'] = 'rm $n';
|
2017-10-07 18:30:27 +02:00
|
|
|
shortcuts['t'] = 'todo toggle $n';
|
|
|
|
shortcuts['c'] = () => { this.widget('console').focus(); };
|
|
|
|
shortcuts[' '] = 'edit $n';
|
|
|
|
|
|
|
|
return shortcuts;
|
|
|
|
}
|
|
|
|
|
2017-10-05 19:17:56 +02:00
|
|
|
widget(name) {
|
|
|
|
return this.rootWidget_.childByName(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
app() {
|
|
|
|
return this.app_;
|
|
|
|
}
|
|
|
|
|
|
|
|
setLogger(l) {
|
|
|
|
this.logger_ = l;
|
|
|
|
}
|
|
|
|
|
|
|
|
logger() {
|
|
|
|
return this.logger_;
|
|
|
|
}
|
|
|
|
|
|
|
|
term() {
|
|
|
|
return this.term_;
|
|
|
|
}
|
|
|
|
|
2017-10-07 18:30:27 +02:00
|
|
|
activeListItem() {
|
|
|
|
const widget = this.widget('mainWindow').focusedWidget();
|
|
|
|
if (!widget) return null;
|
|
|
|
|
|
|
|
if (widget.name() == 'noteList' || widget.name() == 'folderList') {
|
2017-10-07 22:04:53 +02:00
|
|
|
return widget.currentItem;
|
2017-10-07 18:30:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2017-10-06 19:38:17 +02:00
|
|
|
async handleModelAction(action) {
|
2017-10-07 20:05:35 +02:00
|
|
|
this.logger().info('Action:', action);
|
|
|
|
|
2017-10-07 18:30:27 +02:00
|
|
|
let state = Object.assign({}, defaultState);
|
2017-10-07 22:04:53 +02:00
|
|
|
state.notes = this.widget('noteList').items;
|
2017-10-06 19:38:17 +02:00
|
|
|
|
2017-10-07 18:30:27 +02:00
|
|
|
let newState = reducer(state, action);
|
2017-10-06 19:38:17 +02:00
|
|
|
|
2017-10-07 18:30:27 +02:00
|
|
|
if (newState !== state) {
|
2017-10-07 22:04:53 +02:00
|
|
|
this.widget('noteList').items = newState.notes;
|
2017-10-07 18:30:27 +02:00
|
|
|
}
|
|
|
|
}
|
2017-10-06 19:38:17 +02:00
|
|
|
|
2017-10-07 18:30:27 +02:00
|
|
|
async processCommand(cmd) {
|
|
|
|
if (!cmd) return;
|
|
|
|
cmd = cmd.trim();
|
|
|
|
if (!cmd.length) return;
|
|
|
|
|
|
|
|
const consoleWidget = this.widget('console');
|
2017-10-06 19:38:17 +02:00
|
|
|
|
2017-10-07 18:30:27 +02:00
|
|
|
const metaCmd = cmd.substr(0, 2);
|
|
|
|
|
|
|
|
if (metaCmd === ':m') {
|
|
|
|
if (consoleWidget.isMaximized__ === undefined) {
|
|
|
|
consoleWidget.isMaximized__ = false;
|
|
|
|
}
|
2017-10-06 19:38:17 +02:00
|
|
|
|
2017-10-07 18:30:27 +02:00
|
|
|
let constraints = {
|
|
|
|
type: 'fixed',
|
|
|
|
factor: consoleWidget.isMaximized__ ? 5 : this.widget('vLayout').height() - 4,
|
|
|
|
};
|
2017-10-06 19:38:17 +02:00
|
|
|
|
2017-10-07 18:30:27 +02:00
|
|
|
consoleWidget.isMaximized__ = !consoleWidget.isMaximized__;
|
2017-10-06 19:38:17 +02:00
|
|
|
|
2017-10-07 18:30:27 +02:00
|
|
|
this.widget('vLayout').setWidgetConstraints(consoleWidget, constraints);
|
2017-10-06 19:38:17 +02:00
|
|
|
|
2017-10-07 18:30:27 +02:00
|
|
|
return;
|
2017-10-06 19:38:17 +02:00
|
|
|
}
|
|
|
|
|
2017-10-07 22:04:53 +02:00
|
|
|
let note = this.widget('noteList').currentItem;
|
|
|
|
let folder = this.widget('folderList').currentItem;
|
2017-10-06 19:38:17 +02:00
|
|
|
let args = cliUtils.splitCommandString(cmd);
|
|
|
|
|
|
|
|
for (let i = 0; i < args.length; i++) {
|
2017-10-07 18:30:27 +02:00
|
|
|
if (note && args[i] == '$n') {
|
|
|
|
args[i] = note.id;
|
|
|
|
} else if (folder && args[i] == '$b') {
|
|
|
|
args[i] = folder.id;
|
|
|
|
} else if (args[i] == '$c') {
|
|
|
|
const item = this.activeListItem();
|
|
|
|
args[i] = item ? item.id : '';
|
|
|
|
}
|
2017-10-06 19:38:17 +02:00
|
|
|
}
|
|
|
|
|
2017-10-07 18:30:27 +02:00
|
|
|
try {
|
|
|
|
await this.app().execCommand(args);
|
|
|
|
} catch (error) {
|
|
|
|
consoleWidget.bufferPush(error.message);
|
|
|
|
}
|
2017-10-06 19:38:17 +02:00
|
|
|
}
|
|
|
|
|
2017-10-05 19:17:56 +02:00
|
|
|
async updateFolderList() {
|
|
|
|
const folders = await Folder.all();
|
2017-10-07 22:04:53 +02:00
|
|
|
this.widget('folderList').items = folders;
|
2017-10-05 19:17:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
async updateNoteList(folderId) {
|
2017-10-06 19:38:17 +02:00
|
|
|
const fields = Note.previewFields();
|
|
|
|
fields.splice(fields.indexOf('body'), 1);
|
|
|
|
const notes = folderId ? await Note.previews(folderId, { fields: fields }) : [];
|
2017-10-07 22:04:53 +02:00
|
|
|
this.widget('noteList').items = notes;
|
2017-10-05 19:17:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
async updateNoteText(note) {
|
|
|
|
const text = note ? note.body : '';
|
2017-10-07 22:04:53 +02:00
|
|
|
this.widget('noteText').text = text;
|
2017-10-05 19:17:56 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
async start() {
|
|
|
|
const term = this.term();
|
|
|
|
|
|
|
|
term.fullscreen();
|
|
|
|
termutils.hideCursor(term);
|
|
|
|
|
|
|
|
try {
|
|
|
|
this.renderer_.start();
|
|
|
|
|
2017-10-07 18:30:27 +02:00
|
|
|
const consoleWidget = this.widget('console');
|
|
|
|
|
2017-10-05 19:17:56 +02:00
|
|
|
await this.updateFolderList();
|
|
|
|
|
|
|
|
term.grabInput();
|
|
|
|
|
2017-10-07 18:30:27 +02:00
|
|
|
term.on('key', async (name, matches, data) => {
|
2017-10-05 19:17:56 +02:00
|
|
|
if (name === 'CTRL_C' ) {
|
|
|
|
termutils.showCursor(term);
|
|
|
|
term.fullscreen(false);
|
|
|
|
process.exit();
|
2017-10-07 18:30:27 +02:00
|
|
|
return;
|
2017-10-05 19:17:56 +02:00
|
|
|
}
|
|
|
|
|
2017-10-07 18:30:27 +02:00
|
|
|
if (!consoleWidget.hasFocus()) {
|
|
|
|
if (name in this.shortcuts_) {
|
|
|
|
const cmd = this.shortcuts_[name];
|
|
|
|
if (typeof cmd === 'function') {
|
|
|
|
cmd();
|
|
|
|
} else {
|
|
|
|
consoleWidget.bufferPush(cmd);
|
|
|
|
await this.processCommand(cmd);
|
|
|
|
}
|
|
|
|
}
|
2017-10-05 19:17:56 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
this.logger().error(error);
|
|
|
|
term.fullscreen(false);
|
|
|
|
termutils.showCursor(term);
|
|
|
|
console.error(error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = AppGui;
|