mirror of
https://github.com/laurent22/joplin.git
synced 2025-01-23 18:53:36 +02:00
Various terminal gui changes
This commit is contained in:
parent
2b83ddc273
commit
824b385e83
@ -2,11 +2,13 @@ import { Logger } from 'lib/logger.js';
|
|||||||
import { Folder } from 'lib/models/folder.js';
|
import { Folder } from 'lib/models/folder.js';
|
||||||
import { Note } from 'lib/models/note.js';
|
import { Note } from 'lib/models/note.js';
|
||||||
import { cliUtils } from './cli-utils.js';
|
import { cliUtils } from './cli-utils.js';
|
||||||
|
import { reducer, defaultState } from 'lib/reducer.js';
|
||||||
|
|
||||||
const tk = require('terminal-kit');
|
const tk = require('terminal-kit');
|
||||||
const termutils = require('tkwidgets/framework/termutils.js');
|
const termutils = require('tkwidgets/framework/termutils.js');
|
||||||
const Renderer = require('tkwidgets/framework/Renderer.js');
|
const Renderer = require('tkwidgets/framework/Renderer.js');
|
||||||
|
|
||||||
|
const BaseWidget = require('tkwidgets/BaseWidget.js');
|
||||||
const ListWidget = require('tkwidgets/ListWidget.js');
|
const ListWidget = require('tkwidgets/ListWidget.js');
|
||||||
const TextWidget = require('tkwidgets/TextWidget.js');
|
const TextWidget = require('tkwidgets/TextWidget.js');
|
||||||
const ConsoleWidget = require('tkwidgets/ConsoleWidget.js');
|
const ConsoleWidget = require('tkwidgets/ConsoleWidget.js');
|
||||||
@ -19,6 +21,9 @@ class AppGui {
|
|||||||
|
|
||||||
constructor(app) {
|
constructor(app) {
|
||||||
this.app_ = app;
|
this.app_ = app;
|
||||||
|
|
||||||
|
BaseWidget.setLogger(app.logger());
|
||||||
|
|
||||||
this.term_ = tk.terminal;
|
this.term_ = tk.terminal;
|
||||||
this.renderer_ = null;
|
this.renderer_ = null;
|
||||||
this.logger_ = new Logger();
|
this.logger_ = new Logger();
|
||||||
@ -28,6 +33,8 @@ class AppGui {
|
|||||||
this.app_.on('modelAction', async (event) => {
|
this.app_.on('modelAction', async (event) => {
|
||||||
await this.handleModelAction(event.action);
|
await this.handleModelAction(event.action);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.shortcuts_ = this.setupShortcuts();
|
||||||
}
|
}
|
||||||
|
|
||||||
buildUi() {
|
buildUi() {
|
||||||
@ -91,11 +98,13 @@ class AppGui {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const hLayout = new HLayoutWidget();
|
const hLayout = new HLayoutWidget();
|
||||||
|
hLayout.setName('hLayout');
|
||||||
hLayout.addChild(folderList, { type: 'stretch', factor: 1 });
|
hLayout.addChild(folderList, { type: 'stretch', factor: 1 });
|
||||||
hLayout.addChild(noteList, { type: 'stretch', factor: 1 });
|
hLayout.addChild(noteList, { type: 'stretch', factor: 1 });
|
||||||
hLayout.addChild(noteText, { type: 'stretch', factor: 1 });
|
hLayout.addChild(noteText, { type: 'stretch', factor: 1 });
|
||||||
|
|
||||||
const vLayout = new VLayoutWidget();
|
const vLayout = new VLayoutWidget();
|
||||||
|
vLayout.setName('vLayout');
|
||||||
vLayout.addChild(hLayout, { type: 'stretch', factor: 1 });
|
vLayout.addChild(hLayout, { type: 'stretch', factor: 1 });
|
||||||
vLayout.addChild(consoleWidget, { type: 'fixed', factor: 5 });
|
vLayout.addChild(consoleWidget, { type: 'fixed', factor: 5 });
|
||||||
|
|
||||||
@ -108,6 +117,16 @@ class AppGui {
|
|||||||
return rootWidget;
|
return rootWidget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupShortcuts() {
|
||||||
|
const shortcuts = {};
|
||||||
|
|
||||||
|
shortcuts['t'] = 'todo toggle $n';
|
||||||
|
shortcuts['c'] = () => { this.widget('console').focus(); };
|
||||||
|
shortcuts[' '] = 'edit $n';
|
||||||
|
|
||||||
|
return shortcuts;
|
||||||
|
}
|
||||||
|
|
||||||
widget(name) {
|
widget(name) {
|
||||||
return this.rootWidget_.childByName(name);
|
return this.rootWidget_.childByName(name);
|
||||||
}
|
}
|
||||||
@ -128,43 +147,74 @@ class AppGui {
|
|||||||
return this.term_;
|
return this.term_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
activeListItem() {
|
||||||
|
const widget = this.widget('mainWindow').focusedWidget();
|
||||||
|
if (!widget) return null;
|
||||||
|
|
||||||
|
if (widget.name() == 'noteList' || widget.name() == 'folderList') {
|
||||||
|
return widget.currentItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
async handleModelAction(action) {
|
async handleModelAction(action) {
|
||||||
this.logger().info(action);
|
let state = Object.assign({}, defaultState);
|
||||||
|
state.notes = this.widget('noteList').items();
|
||||||
|
|
||||||
// "{"action":{"type":"NOTES_UPDATE_ONE","note":{"id":"56d0e2ea61004324b33a307983c9722c","todo_completed":1507306904833,"updated_time":1507306904834,"user_updated_time":1507306904834,"type_":1}}}"
|
let newState = reducer(state, action);
|
||||||
switch (action.type) {
|
|
||||||
|
|
||||||
case 'NOTES_UPDATE_ONE':
|
|
||||||
|
|
||||||
const folder = this.widget('folderList').currentItem();
|
|
||||||
if (!folder) return;
|
|
||||||
|
|
||||||
const note = action.note;
|
|
||||||
this.logger().info(folder, note);
|
|
||||||
if (note.parent_id != folder.id) return;
|
|
||||||
|
|
||||||
|
|
||||||
const notes = await Note.previews(folder.id);
|
|
||||||
|
|
||||||
this.widget('noteList').setItems(notes);
|
|
||||||
break;
|
|
||||||
|
|
||||||
|
if (newState !== state) {
|
||||||
|
this.widget('noteList').setItems(newState.notes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async processCommand(cmd) {
|
async processCommand(cmd) {
|
||||||
|
if (!cmd) return;
|
||||||
|
cmd = cmd.trim();
|
||||||
|
if (!cmd.length) return;
|
||||||
|
|
||||||
|
const consoleWidget = this.widget('console');
|
||||||
|
|
||||||
|
const metaCmd = cmd.substr(0, 2);
|
||||||
|
|
||||||
|
if (metaCmd === ':m') {
|
||||||
|
if (consoleWidget.isMaximized__ === undefined) {
|
||||||
|
consoleWidget.isMaximized__ = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let constraints = {
|
||||||
|
type: 'fixed',
|
||||||
|
factor: consoleWidget.isMaximized__ ? 5 : this.widget('vLayout').height() - 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
consoleWidget.isMaximized__ = !consoleWidget.isMaximized__;
|
||||||
|
|
||||||
|
this.widget('vLayout').setWidgetConstraints(consoleWidget, constraints);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let note = this.widget('noteList').currentItem();
|
let note = this.widget('noteList').currentItem();
|
||||||
let folder = this.widget('folderList').currentItem();
|
let folder = this.widget('folderList').currentItem();
|
||||||
let args = cliUtils.splitCommandString(cmd);
|
let args = cliUtils.splitCommandString(cmd);
|
||||||
|
|
||||||
for (let i = 0; i < args.length; i++) {
|
for (let i = 0; i < args.length; i++) {
|
||||||
if (note && args[i] == '%n') args[i] = note.id;
|
if (note && args[i] == '$n') {
|
||||||
if (folder && args[i] == '%b') args[i] = folder.id;
|
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 : '';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.app().execCommand(args);
|
try {
|
||||||
|
await this.app().execCommand(args);
|
||||||
//this.logger().info(args);
|
} catch (error) {
|
||||||
|
consoleWidget.bufferPush(error.message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateFolderList() {
|
async updateFolderList() {
|
||||||
@ -193,19 +243,31 @@ class AppGui {
|
|||||||
try {
|
try {
|
||||||
this.renderer_.start();
|
this.renderer_.start();
|
||||||
|
|
||||||
|
const consoleWidget = this.widget('console');
|
||||||
|
|
||||||
await this.updateFolderList();
|
await this.updateFolderList();
|
||||||
|
|
||||||
term.grabInput();
|
term.grabInput();
|
||||||
|
|
||||||
term.on('key', (name, matches, data) => {
|
term.on('key', async (name, matches, data) => {
|
||||||
|
|
||||||
if (name === 'CTRL_C' ) {
|
if (name === 'CTRL_C' ) {
|
||||||
termutils.showCursor(term);
|
termutils.showCursor(term);
|
||||||
term.fullscreen(false);
|
term.fullscreen(false);
|
||||||
process.exit();
|
process.exit();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (name == 'c') {
|
if (!consoleWidget.hasFocus()) {
|
||||||
this.widget('console').focus();
|
if (name in this.shortcuts_) {
|
||||||
|
const cmd = this.shortcuts_[name];
|
||||||
|
if (typeof cmd === 'function') {
|
||||||
|
cmd();
|
||||||
|
} else {
|
||||||
|
consoleWidget.bufferPush(cmd);
|
||||||
|
await this.processCommand(cmd);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -34,6 +34,10 @@ class Application {
|
|||||||
this.eventEmitter_ = new EventEmitter();
|
this.eventEmitter_ = new EventEmitter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger() {
|
||||||
|
return this.logger_;
|
||||||
|
}
|
||||||
|
|
||||||
currentFolder() {
|
currentFolder() {
|
||||||
return this.currentFolder_;
|
return this.currentFolder_;
|
||||||
}
|
}
|
||||||
@ -241,18 +245,6 @@ class Application {
|
|||||||
|
|
||||||
baseModelListener(action) {
|
baseModelListener(action) {
|
||||||
this.eventEmitter_.emit('modelAction', { action: action });
|
this.eventEmitter_.emit('modelAction', { action: action });
|
||||||
|
|
||||||
// switch (action.type) {
|
|
||||||
|
|
||||||
// case 'NOTES_UPDATE_ONE':
|
|
||||||
// case 'NOTES_DELETE':
|
|
||||||
// case 'FOLDERS_UPDATE_ONE':
|
|
||||||
// case 'FOLDER_DELETE':
|
|
||||||
|
|
||||||
// //reg.scheduleSync();
|
|
||||||
// break;
|
|
||||||
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
on(eventName, callback) {
|
on(eventName, callback) {
|
||||||
@ -271,9 +263,9 @@ class Application {
|
|||||||
let cmd = new CommandClass();
|
let cmd = new CommandClass();
|
||||||
if (!cmd.enabled()) return;
|
if (!cmd.enabled()) return;
|
||||||
|
|
||||||
cmd.log = (...object) => {
|
cmd.setStdout((...object) => {
|
||||||
return console.log(...object);
|
this.commandStdout(...object);
|
||||||
}
|
});
|
||||||
|
|
||||||
this.commands_[cmd.name()] = cmd;
|
this.commands_[cmd.name()] = cmd;
|
||||||
});
|
});
|
||||||
@ -293,6 +285,13 @@ class Application {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
commandStdout(...object) {
|
||||||
|
const consoleWidget = this.gui_.widget('console');
|
||||||
|
for (let i = 0; i < object.length; i++) {
|
||||||
|
consoleWidget.bufferPush(object[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async commandMetadata() {
|
async commandMetadata() {
|
||||||
if (this.commandMetadata_) return this.commandMetadata_;
|
if (this.commandMetadata_) return this.commandMetadata_;
|
||||||
|
|
||||||
@ -336,14 +335,9 @@ class Application {
|
|||||||
let cmd = new CommandClass();
|
let cmd = new CommandClass();
|
||||||
cmd.buffer_ = [];
|
cmd.buffer_ = [];
|
||||||
|
|
||||||
cmd.log = (...object) => {
|
cmd.setStdout((...object) => {
|
||||||
cmd.buffer_ = cmd.buffer_.concat(object);
|
this.commandStdout(...object);
|
||||||
//return console.log(...object);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
cmd.buffer = () => {
|
|
||||||
return cmd.buffer_;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.commands_[name] = cmd;
|
this.commands_[name] = cmd;
|
||||||
return this.commands_[name];
|
return this.commands_[name];
|
||||||
@ -351,6 +345,7 @@ class Application {
|
|||||||
|
|
||||||
async execCommand(argv) {
|
async execCommand(argv) {
|
||||||
if (!argv.length) return this.execCommand(['help']);
|
if (!argv.length) return this.execCommand(['help']);
|
||||||
|
reg.logger().info('execCommand()', argv);
|
||||||
const commandName = argv[0];
|
const commandName = argv[0];
|
||||||
this.activeCommand_ = this.findCommandByName(commandName);
|
this.activeCommand_ = this.findCommandByName(commandName);
|
||||||
const cmdArgs = cliUtils.makeCommandArgs(this.activeCommand_, argv);
|
const cmdArgs = cliUtils.makeCommandArgs(this.activeCommand_, argv);
|
||||||
@ -404,6 +399,7 @@ class Application {
|
|||||||
|
|
||||||
this.database_ = new JoplinDatabase(new DatabaseDriverNode());
|
this.database_ = new JoplinDatabase(new DatabaseDriverNode());
|
||||||
this.database_.setLogger(this.dbLogger_);
|
this.database_.setLogger(this.dbLogger_);
|
||||||
|
this.database_.setLogExcludedQueryTypes(['SELECT']);
|
||||||
await this.database_.open({ name: profileDir + '/database.sqlite' });
|
await this.database_.open({ name: profileDir + '/database.sqlite' });
|
||||||
|
|
||||||
reg.setDb(this.database_);
|
reg.setDb(this.database_);
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
class BaseCommand {
|
class BaseCommand {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.stdout_ = null;
|
||||||
|
}
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
throw new Error('Usage not defined');
|
throw new Error('Usage not defined');
|
||||||
}
|
}
|
||||||
@ -39,6 +43,14 @@ class BaseCommand {
|
|||||||
return r[0];
|
return r[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setStdout(fn) {
|
||||||
|
this.stdout_ = fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
stdout(...object) {
|
||||||
|
if (this.stdout_) this.stdout_(...object);
|
||||||
|
}
|
||||||
|
|
||||||
metadata() {
|
metadata() {
|
||||||
return {
|
return {
|
||||||
name: this.name(),
|
name: this.name(),
|
||||||
|
@ -7,11 +7,15 @@ const cliUtils = {};
|
|||||||
|
|
||||||
// Split a command string into an argument array
|
// Split a command string into an argument array
|
||||||
cliUtils.splitCommandString = function(s) {
|
cliUtils.splitCommandString = function(s) {
|
||||||
s = s.replace(/--/g, 'JOP_DASH_JOP_DASH');
|
s = s.replace(/--/g, '__JOP_DASH_JOP_DASH__');
|
||||||
|
s = s.replace(/-/g, '__JOP_DASH__');
|
||||||
let r = yargParser(s);
|
let r = yargParser(s);
|
||||||
let output = [];
|
let output = [];
|
||||||
for (let i = 0; i < r._.length; i++) {
|
for (let i = 0; i < r._.length; i++) {
|
||||||
output.push(r._[i].replace(/JOP_DASH_JOP_DASH/g, '--'));
|
let a = r._[i];
|
||||||
|
a = a.replace(/__JOP_DASH_JOP_DASH__/g, '--');
|
||||||
|
a = a.replace(/__JOP_DASH__/g, '-');
|
||||||
|
output.push(a);
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ class Command extends BaseCommand {
|
|||||||
if (!item) throw new Error(_('Cannot find "%s".', title));
|
if (!item) throw new Error(_('Cannot find "%s".', title));
|
||||||
|
|
||||||
const content = args.options.verbose ? await Note.serialize(item) : await Note.serializeForEdit(item);
|
const content = args.options.verbose ? await Note.serialize(item) : await Note.serializeForEdit(item);
|
||||||
this.log(content);
|
this.stdout(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -36,13 +36,13 @@ class Command extends BaseCommand {
|
|||||||
for (let i = 0; i < keys.length; i++) {
|
for (let i = 0; i < keys.length; i++) {
|
||||||
const value = Setting.value(keys[i]);
|
const value = Setting.value(keys[i]);
|
||||||
if (!verbose && !value) continue;
|
if (!verbose && !value) continue;
|
||||||
this.log(renderKeyValue(keys[i]));
|
this.stdout(renderKeyValue(keys[i]));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.name && !args.value) {
|
if (args.name && !args.value) {
|
||||||
this.log(renderKeyValue(args.name));
|
this.stdout(renderKeyValue(args.name));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ class Command extends BaseCommand {
|
|||||||
|
|
||||||
items = items.concat(tags);
|
items = items.concat(tags);
|
||||||
|
|
||||||
this.log(JSON.stringify(items));
|
this.stdout(JSON.stringify(items));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ class Command extends BaseCommand {
|
|||||||
if (watcher) watcher.close();
|
if (watcher) watcher.close();
|
||||||
//app().vorpal().show();
|
//app().vorpal().show();
|
||||||
newNote = null;
|
newNote = null;
|
||||||
this.log(_('Done editing.'));
|
this.stdout(_('Done editing.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const textEditorPath = () => {
|
const textEditorPath = () => {
|
||||||
@ -61,7 +61,7 @@ class Command extends BaseCommand {
|
|||||||
|
|
||||||
const spawn = require('child_process').spawn;
|
const spawn = require('child_process').spawn;
|
||||||
|
|
||||||
this.log(_('Starting to edit note. Close the editor to get back to the prompt.'));
|
this.stdout(_('Starting to edit note. Close the editor to get back to the prompt.'));
|
||||||
|
|
||||||
await fs.writeFile(tempFilePath, content);
|
await fs.writeFile(tempFilePath, content);
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ class Command extends BaseCommand {
|
|||||||
let item = await app().loadItem(BaseModel.TYPE_NOTE, title, { parent: app().currentFolder() });
|
let item = await app().loadItem(BaseModel.TYPE_NOTE, title, { parent: app().currentFolder() });
|
||||||
if (!item) throw new Error(_('Cannot find "%s".', title));
|
if (!item) throw new Error(_('Cannot find "%s".', title));
|
||||||
const url = Note.geolocationUrl(item);
|
const url = Note.geolocationUrl(item);
|
||||||
this.log(url);
|
this.stdout(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ class Command extends BaseCommand {
|
|||||||
|
|
||||||
output.sort();
|
output.sort();
|
||||||
|
|
||||||
this.log(output.join("\n\n"));
|
this.stdout(output.join("\n\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -49,12 +49,12 @@ class Command extends BaseCommand {
|
|||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
let s = error.trace ? error.trace : error.toString();
|
let s = error.trace ? error.trace : error.toString();
|
||||||
this.log(s);
|
this.stdout(s);
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
folder = !folder ? await Folder.save({ title: folderTitle }) : folder;
|
folder = !folder ? await Folder.save({ title: folderTitle }) : folder;
|
||||||
this.log(_('Importing notes...'));
|
this.stdout(_('Importing notes...'));
|
||||||
await importEnex(folder.id, filePath, options);
|
await importEnex(folder.id, filePath, options);
|
||||||
cliUtils.redrawDone();
|
cliUtils.redrawDone();
|
||||||
}
|
}
|
||||||
|
@ -63,7 +63,7 @@ class Command extends BaseCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (options.format && options.format == 'json') {
|
if (options.format && options.format == 'json') {
|
||||||
this.log(JSON.stringify(items));
|
this.stdout(JSON.stringify(items));
|
||||||
} else {
|
} else {
|
||||||
let hasTodos = false;
|
let hasTodos = false;
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
@ -112,7 +112,7 @@ class Command extends BaseCommand {
|
|||||||
rows.push(row);
|
rows.push(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
cliUtils.printArray(this.log, rows);
|
cliUtils.printArray(this.stdout, rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -53,7 +53,7 @@ class Command extends BaseCommand {
|
|||||||
line = sprintf('%s: %s / %s', BaseModel.shortId(note.id), parent.title, note.title);
|
line = sprintf('%s: %s / %s', BaseModel.shortId(note.id), parent.title, note.title);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.log(line);
|
this.stdout(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,15 +21,15 @@ class Command extends BaseCommand {
|
|||||||
for (let i = 0; i < report.length; i++) {
|
for (let i = 0; i < report.length; i++) {
|
||||||
let section = report[i];
|
let section = report[i];
|
||||||
|
|
||||||
if (i > 0) this.log('');
|
if (i > 0) this.stdout('');
|
||||||
|
|
||||||
this.log('# ' + section.title);
|
this.stdout('# ' + section.title);
|
||||||
this.log('');
|
this.stdout('');
|
||||||
|
|
||||||
for (let n in section.body) {
|
for (let n in section.body) {
|
||||||
if (!section.body.hasOwnProperty(n)) continue;
|
if (!section.body.hasOwnProperty(n)) continue;
|
||||||
let line = section.body[n];
|
let line = section.body[n];
|
||||||
this.log(line);
|
this.stdout(line);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ class Command extends BaseCommand {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code == 'ELOCKED') {
|
if (error.code == 'ELOCKED') {
|
||||||
const msg = _('Lock file is already being hold. If you know that no synchronisation is taking place, you may delete the lock file at "%s" and resume the operation.', error.file);
|
const msg = _('Lock file is already being hold. If you know that no synchronisation is taking place, you may delete the lock file at "%s" and resume the operation.', error.file);
|
||||||
this.log(msg);
|
this.stdout(msg);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
@ -101,16 +101,16 @@ class Command extends BaseCommand {
|
|||||||
},
|
},
|
||||||
onMessage: (msg) => {
|
onMessage: (msg) => {
|
||||||
cliUtils.redrawDone();
|
cliUtils.redrawDone();
|
||||||
this.log(msg);
|
this.stdout(msg);
|
||||||
},
|
},
|
||||||
randomFailures: args.options['random-failures'] === true,
|
randomFailures: args.options['random-failures'] === true,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.log(_('Synchronisation target: %s (%s)', Setting.enumOptionLabel('sync.target', this.syncTarget_), this.syncTarget_));
|
this.stdout(_('Synchronisation target: %s (%s)', Setting.enumOptionLabel('sync.target', this.syncTarget_), this.syncTarget_));
|
||||||
|
|
||||||
if (!sync) throw new Error(_('Cannot initialize synchroniser.'));
|
if (!sync) throw new Error(_('Cannot initialize synchroniser.'));
|
||||||
|
|
||||||
this.log(_('Starting synchronisation...'));
|
this.stdout(_('Starting synchronisation...'));
|
||||||
|
|
||||||
const contextKey = 'sync.' + this.syncTarget_ + '.context';
|
const contextKey = 'sync.' + this.syncTarget_ + '.context';
|
||||||
let context = Setting.value(contextKey);
|
let context = Setting.value(contextKey);
|
||||||
@ -123,7 +123,7 @@ class Command extends BaseCommand {
|
|||||||
Setting.setValue(contextKey, JSON.stringify(newContext));
|
Setting.setValue(contextKey, JSON.stringify(newContext));
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error.code == 'alreadyStarted') {
|
if (error.code == 'alreadyStarted') {
|
||||||
this.log(error.message);
|
this.stdout(error.message);
|
||||||
} else {
|
} else {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
@ -147,7 +147,7 @@ class Command extends BaseCommand {
|
|||||||
|
|
||||||
cliUtils.redrawDone();
|
cliUtils.redrawDone();
|
||||||
|
|
||||||
this.log(_('Cancelling... Please wait.'));
|
this.stdout(_('Cancelling... Please wait.'));
|
||||||
|
|
||||||
if (reg.syncHasAuth(target)) {
|
if (reg.syncHasAuth(target)) {
|
||||||
let sync = await reg.synchronizer(target);
|
let sync = await reg.synchronizer(target);
|
||||||
|
@ -41,10 +41,10 @@ class Command extends BaseCommand {
|
|||||||
} else if (command == 'list') {
|
} else if (command == 'list') {
|
||||||
if (tag) {
|
if (tag) {
|
||||||
let notes = await Tag.notes(tag.id);
|
let notes = await Tag.notes(tag.id);
|
||||||
notes.map((note) => { this.log(note.title); });
|
notes.map((note) => { this.stdout(note.title); });
|
||||||
} else {
|
} else {
|
||||||
let tags = await Tag.all();
|
let tags = await Tag.all();
|
||||||
tags.map((tag) => { this.log(tag.title); });
|
tags.map((tag) => { this.stdout(tag.title); });
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error(_('Invalid command: "%s"', command));
|
throw new Error(_('Invalid command: "%s"', command));
|
||||||
|
@ -14,7 +14,7 @@ class Command extends BaseCommand {
|
|||||||
|
|
||||||
async action(args) {
|
async action(args) {
|
||||||
const p = require('./package.json');
|
const p = require('./package.json');
|
||||||
this.log(_('%s %s (%s)', p.name, p.version, Setting.value('env')));
|
this.stdout(_('%s %s (%s)', p.name, p.version, Setting.value('env')));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,11 @@ class Database {
|
|||||||
this.inTransaction_ = false;
|
this.inTransaction_ = false;
|
||||||
|
|
||||||
this.logger_ = new Logger();
|
this.logger_ = new Logger();
|
||||||
|
this.logExcludedQueryTypes_ = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
setLogExcludedQueryTypes(v) {
|
||||||
|
this.logExcludedQueryTypes_ = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Converts the SQLite error to a regular JS error
|
// Converts the SQLite error to a regular JS error
|
||||||
@ -185,6 +190,13 @@ class Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logQuery(sql, params = null) {
|
logQuery(sql, params = null) {
|
||||||
|
if (this.logExcludedQueryTypes_.length) {
|
||||||
|
const temp = sql.toLowerCase();
|
||||||
|
for (let i = 0; i < this.logExcludedQueryTypes_.length; i++) {
|
||||||
|
if (temp.indexOf(this.logExcludedQueryTypes_[i].toLowerCase()) === 0) return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.logger().debug(sql);
|
this.logger().debug(sql);
|
||||||
if (params !== null && params.length) this.logger().debug(JSON.stringify(params));
|
if (params !== null && params.length) this.logger().debug(JSON.stringify(params));
|
||||||
}
|
}
|
||||||
|
294
ReactNativeClient/lib/reducer.js
Normal file
294
ReactNativeClient/lib/reducer.js
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
import { Note } from 'lib/models/note.js';
|
||||||
|
|
||||||
|
const defaultState = {
|
||||||
|
notes: [],
|
||||||
|
notesSource: '',
|
||||||
|
notesParentType: null,
|
||||||
|
folders: [],
|
||||||
|
tags: [],
|
||||||
|
selectedNoteId: null,
|
||||||
|
selectedFolderId: null,
|
||||||
|
selectedTagId: null,
|
||||||
|
selectedItemType: 'note',
|
||||||
|
showSideMenu: false,
|
||||||
|
screens: {},
|
||||||
|
historyCanGoBack: false,
|
||||||
|
notesOrder: [
|
||||||
|
{ by: 'user_updated_time', dir: 'DESC' },
|
||||||
|
],
|
||||||
|
syncStarted: false,
|
||||||
|
syncReport: {},
|
||||||
|
searchQuery: '',
|
||||||
|
settings: {},
|
||||||
|
appState: 'starting',
|
||||||
|
route: {
|
||||||
|
type: 'NAV_GO',
|
||||||
|
routeName: 'Welcome',
|
||||||
|
params: {},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let navHistory = [];
|
||||||
|
|
||||||
|
function historyCanGoBackTo(route) {
|
||||||
|
if (route.routeName == 'Note') return false;
|
||||||
|
if (route.routeName == 'Folder') return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reducer = (state = defaultState, action) => {
|
||||||
|
let newState = state;
|
||||||
|
let historyGoingBack = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
switch (action.type) {
|
||||||
|
|
||||||
|
|
||||||
|
case 'NAV_BACK':
|
||||||
|
|
||||||
|
if (!navHistory.length) break;
|
||||||
|
|
||||||
|
let newAction = null;
|
||||||
|
while (navHistory.length) {
|
||||||
|
newAction = navHistory.pop();
|
||||||
|
if (newAction.routeName != state.route.routeName) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
action = newAction ? newAction : navHistory.pop();
|
||||||
|
|
||||||
|
historyGoingBack = true;
|
||||||
|
|
||||||
|
// Fall throught
|
||||||
|
|
||||||
|
case 'NAV_GO':
|
||||||
|
|
||||||
|
const currentRoute = state.route;
|
||||||
|
const currentRouteName = currentRoute ? currentRoute.routeName : '';
|
||||||
|
|
||||||
|
if (!historyGoingBack && historyCanGoBackTo(currentRoute)) {
|
||||||
|
// If the route *name* is the same (even if the other parameters are different), we
|
||||||
|
// overwrite the last route in the history with the current one. If the route name
|
||||||
|
// is different, we push a new history entry.
|
||||||
|
if (currentRoute.routeName == action.routeName) {
|
||||||
|
// nothing
|
||||||
|
} else {
|
||||||
|
navHistory.push(currentRoute);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HACK: whenever a new screen is loaded, all the previous screens of that type
|
||||||
|
// are overwritten with the new screen parameters. This is because the way notes
|
||||||
|
// are currently loaded is not optimal (doesn't retain history properly) so
|
||||||
|
// this is a simple fix without doing a big refactoring to change the way notes
|
||||||
|
// are loaded. Might be good enough since going back to different folders
|
||||||
|
// is probably not a common workflow.
|
||||||
|
for (let i = 0; i < navHistory.length; i++) {
|
||||||
|
let n = navHistory[i];
|
||||||
|
if (n.routeName == action.routeName) {
|
||||||
|
navHistory[i] = Object.assign({}, action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.routeName == 'Welcome') navHistory = [];
|
||||||
|
|
||||||
|
reg.logger().info('Route: ' + currentRouteName + ' => ' + action.routeName);
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
|
||||||
|
if ('noteId' in action) {
|
||||||
|
newState.selectedNoteId = action.noteId;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('folderId' in action) {
|
||||||
|
newState.selectedFolderId = action.folderId;
|
||||||
|
newState.notesParentType = 'Folder';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('tagId' in action) {
|
||||||
|
newState.selectedTagId = action.tagId;
|
||||||
|
newState.notesParentType = 'Tag';
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('itemType' in action) {
|
||||||
|
newState.selectedItemType = action.itemType;
|
||||||
|
}
|
||||||
|
|
||||||
|
newState.route = action;
|
||||||
|
newState.historyCanGoBack = !!navHistory.length;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SETTINGS_UPDATE_ALL':
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.settings = action.settings;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SETTINGS_UPDATE_ONE':
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
let newSettings = Object.assign({}, state.settings);
|
||||||
|
newSettings[action.key] = action.value;
|
||||||
|
newState.settings = newSettings;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Replace all the notes with the provided array
|
||||||
|
case 'NOTES_UPDATE_ALL':
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.notes = action.notes;
|
||||||
|
newState.notesSource = action.notesSource;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Insert the note into the note list if it's new, or
|
||||||
|
// update it within the note array if it already exists.
|
||||||
|
case 'NOTES_UPDATE_ONE':
|
||||||
|
|
||||||
|
const modNote = action.note;
|
||||||
|
|
||||||
|
let newNotes = state.notes.slice();
|
||||||
|
var found = false;
|
||||||
|
for (let i = 0; i < newNotes.length; i++) {
|
||||||
|
let n = newNotes[i];
|
||||||
|
if (n.id == modNote.id) {
|
||||||
|
|
||||||
|
if (!('parent_id' in modNote) || modNote.parent_id == n.parent_id) {
|
||||||
|
// Merge the properties that have changed (in modNote) into
|
||||||
|
// the object we already have.
|
||||||
|
newNotes[i] = Object.assign({}, newNotes[i]);
|
||||||
|
|
||||||
|
for (let n in modNote) {
|
||||||
|
if (!modNote.hasOwnProperty(n)) continue;
|
||||||
|
newNotes[i][n] = modNote[n];
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
newNotes.splice(i, 1);
|
||||||
|
}
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found && ('parent_id' in modNote) && modNote.parent_id == state.selectedFolderId) newNotes.push(modNote);
|
||||||
|
|
||||||
|
newNotes = Note.sortNotes(newNotes, state.notesOrder, newState.settings.uncompletedTodosOnTop);
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.notes = newNotes;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'NOTES_DELETE':
|
||||||
|
|
||||||
|
var newNotes = [];
|
||||||
|
for (let i = 0; i < state.notes.length; i++) {
|
||||||
|
let f = state.notes[i];
|
||||||
|
if (f.id == action.noteId) continue;
|
||||||
|
newNotes.push(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.notes = newNotes;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'FOLDERS_UPDATE_ALL':
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.folders = action.folders;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'TAGS_UPDATE_ALL':
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.tags = action.tags;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'FOLDERS_UPDATE_ONE':
|
||||||
|
|
||||||
|
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;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SIDE_MENU_TOGGLE':
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.showSideMenu = !newState.showSideMenu
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SIDE_MENU_OPEN':
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.showSideMenu = true
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SIDE_MENU_CLOSE':
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.showSideMenu = false
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SYNC_STARTED':
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.syncStarted = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SYNC_COMPLETED':
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.syncStarted = false;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SYNC_REPORT_UPDATE':
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.syncReport = action.report;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SEARCH_QUERY':
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.searchQuery = action.query.trim();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'SET_APP_STATE':
|
||||||
|
|
||||||
|
newState = Object.assign({}, state);
|
||||||
|
newState.appState = action.state;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
error.message = 'In reducer: ' + error.message + ' Action: ' + JSON.stringify(action);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return newState;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { reducer, defaultState };
|
@ -35,334 +35,7 @@ import { reg } from 'lib/registry.js';
|
|||||||
import { _, setLocale, closestSupportedLocale, defaultLocale } from 'lib/locale.js';
|
import { _, setLocale, closestSupportedLocale, defaultLocale } from 'lib/locale.js';
|
||||||
import RNFetchBlob from 'react-native-fetch-blob';
|
import RNFetchBlob from 'react-native-fetch-blob';
|
||||||
import { PoorManIntervals } from 'lib/poor-man-intervals.js';
|
import { PoorManIntervals } from 'lib/poor-man-intervals.js';
|
||||||
|
import { reducer, defaultState } from 'lib/reducer.js';
|
||||||
let defaultState = {
|
|
||||||
notes: [],
|
|
||||||
notesSource: '',
|
|
||||||
notesParentType: null,
|
|
||||||
folders: [],
|
|
||||||
tags: [],
|
|
||||||
selectedNoteId: null,
|
|
||||||
selectedFolderId: null,
|
|
||||||
selectedTagId: null,
|
|
||||||
selectedItemType: 'note',
|
|
||||||
showSideMenu: false,
|
|
||||||
screens: {},
|
|
||||||
historyCanGoBack: false,
|
|
||||||
notesOrder: [
|
|
||||||
{ by: 'user_updated_time', dir: 'DESC' },
|
|
||||||
],
|
|
||||||
syncStarted: false,
|
|
||||||
syncReport: {},
|
|
||||||
searchQuery: '',
|
|
||||||
settings: {},
|
|
||||||
appState: 'starting',
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialRoute = {
|
|
||||||
type: 'NAV_GO',
|
|
||||||
routeName: 'Welcome',
|
|
||||||
params: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
defaultState.route = initialRoute;
|
|
||||||
|
|
||||||
let navHistory = [];
|
|
||||||
|
|
||||||
function historyCanGoBackTo(route) {
|
|
||||||
if (route.routeName == 'Note') return false;
|
|
||||||
if (route.routeName == 'Folder') return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function reducerActionsAreSame(a1, a2) {
|
|
||||||
if (Object.getOwnPropertyNames(a1).length !== Object.getOwnPropertyNames(a2).length) return false;
|
|
||||||
|
|
||||||
for (let n in a1) {
|
|
||||||
if (!a1.hasOwnProperty(n)) continue;
|
|
||||||
if (a1[n] !== a2[n]) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateStateFromSettings(action, newState) {
|
|
||||||
// if (action.type == 'SETTINGS_UPDATE_ALL' || action.key == 'uncompletedTodosOnTop') {
|
|
||||||
// let newNotesOrder = [];
|
|
||||||
// for (let i = 0; i < newState.notesOrder.length; i++) {
|
|
||||||
// const o = newState.notesOrder[i];
|
|
||||||
// if (o.by == 'is_todo') continue;
|
|
||||||
// newNotesOrder.push(o);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (newState.settings['uncompletedTodosOnTop']) {
|
|
||||||
// newNotesOrder.unshift({ by: 'is_todo', dir: 'DESC' });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// newState.notesOrder = newNotesOrder;
|
|
||||||
|
|
||||||
// console.info('NEW', newNotesOrder);
|
|
||||||
// }
|
|
||||||
|
|
||||||
return newState;
|
|
||||||
}
|
|
||||||
|
|
||||||
const reducer = (state = defaultState, action) => {
|
|
||||||
let newState = state;
|
|
||||||
let historyGoingBack = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
switch (action.type) {
|
|
||||||
|
|
||||||
|
|
||||||
case 'NAV_BACK':
|
|
||||||
|
|
||||||
if (!navHistory.length) break;
|
|
||||||
|
|
||||||
let newAction = null;
|
|
||||||
while (navHistory.length) {
|
|
||||||
newAction = navHistory.pop();
|
|
||||||
if (newAction.routeName != state.route.routeName) break;
|
|
||||||
}
|
|
||||||
|
|
||||||
action = newAction ? newAction : navHistory.pop();
|
|
||||||
|
|
||||||
historyGoingBack = true;
|
|
||||||
|
|
||||||
// Fall throught
|
|
||||||
|
|
||||||
case 'NAV_GO':
|
|
||||||
|
|
||||||
const currentRoute = state.route;
|
|
||||||
const currentRouteName = currentRoute ? currentRoute.routeName : '';
|
|
||||||
|
|
||||||
if (!historyGoingBack && historyCanGoBackTo(currentRoute)) {
|
|
||||||
// If the route *name* is the same (even if the other parameters are different), we
|
|
||||||
// overwrite the last route in the history with the current one. If the route name
|
|
||||||
// is different, we push a new history entry.
|
|
||||||
if (currentRoute.routeName == action.routeName) {
|
|
||||||
// nothing
|
|
||||||
} else {
|
|
||||||
navHistory.push(currentRoute);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// HACK: whenever a new screen is loaded, all the previous screens of that type
|
|
||||||
// are overwritten with the new screen parameters. This is because the way notes
|
|
||||||
// are currently loaded is not optimal (doesn't retain history properly) so
|
|
||||||
// this is a simple fix without doing a big refactoring to change the way notes
|
|
||||||
// are loaded. Might be good enough since going back to different folders
|
|
||||||
// is probably not a common workflow.
|
|
||||||
for (let i = 0; i < navHistory.length; i++) {
|
|
||||||
let n = navHistory[i];
|
|
||||||
if (n.routeName == action.routeName) {
|
|
||||||
navHistory[i] = Object.assign({}, action);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (action.routeName == 'Welcome') navHistory = [];
|
|
||||||
|
|
||||||
reg.logger().info('Route: ' + currentRouteName + ' => ' + action.routeName);
|
|
||||||
|
|
||||||
newState = Object.assign({}, state);
|
|
||||||
|
|
||||||
if ('noteId' in action) {
|
|
||||||
newState.selectedNoteId = action.noteId;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('folderId' in action) {
|
|
||||||
newState.selectedFolderId = action.folderId;
|
|
||||||
newState.notesParentType = 'Folder';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('tagId' in action) {
|
|
||||||
newState.selectedTagId = action.tagId;
|
|
||||||
newState.notesParentType = 'Tag';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ('itemType' in action) {
|
|
||||||
newState.selectedItemType = action.itemType;
|
|
||||||
}
|
|
||||||
|
|
||||||
newState.route = action;
|
|
||||||
newState.historyCanGoBack = !!navHistory.length;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'SETTINGS_UPDATE_ALL':
|
|
||||||
|
|
||||||
newState = Object.assign({}, state);
|
|
||||||
newState.settings = action.settings;
|
|
||||||
newState = updateStateFromSettings(action, newState);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'SETTINGS_UPDATE_ONE':
|
|
||||||
|
|
||||||
newState = Object.assign({}, state);
|
|
||||||
let newSettings = Object.assign({}, state.settings);
|
|
||||||
newSettings[action.key] = action.value;
|
|
||||||
newState.settings = newSettings;
|
|
||||||
newState = updateStateFromSettings(action, newState);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Replace all the notes with the provided array
|
|
||||||
case 'NOTES_UPDATE_ALL':
|
|
||||||
|
|
||||||
newState = Object.assign({}, state);
|
|
||||||
newState.notes = action.notes;
|
|
||||||
newState.notesSource = action.notesSource;
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Insert the note into the note list if it's new, or
|
|
||||||
// update it within the note array if it already exists.
|
|
||||||
case 'NOTES_UPDATE_ONE':
|
|
||||||
|
|
||||||
const modNote = action.note;
|
|
||||||
|
|
||||||
let newNotes = state.notes.slice();
|
|
||||||
var found = false;
|
|
||||||
for (let i = 0; i < newNotes.length; i++) {
|
|
||||||
let n = newNotes[i];
|
|
||||||
if (n.id == modNote.id) {
|
|
||||||
|
|
||||||
if (!('parent_id' in modNote) || modNote.parent_id == n.parent_id) {
|
|
||||||
// Merge the properties that have changed (in modNote) into
|
|
||||||
// the object we already have.
|
|
||||||
newNotes[i] = Object.assign({}, newNotes[i]);
|
|
||||||
|
|
||||||
for (let n in modNote) {
|
|
||||||
if (!modNote.hasOwnProperty(n)) continue;
|
|
||||||
newNotes[i][n] = modNote[n];
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
newNotes.splice(i, 1);
|
|
||||||
}
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found && ('parent_id' in modNote) && modNote.parent_id == state.selectedFolderId) newNotes.push(modNote);
|
|
||||||
|
|
||||||
newNotes = Note.sortNotes(newNotes, state.notesOrder, newState.settings.uncompletedTodosOnTop);
|
|
||||||
newState = Object.assign({}, state);
|
|
||||||
newState.notes = newNotes;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'NOTES_DELETE':
|
|
||||||
|
|
||||||
var newNotes = [];
|
|
||||||
for (let i = 0; i < state.notes.length; i++) {
|
|
||||||
let f = state.notes[i];
|
|
||||||
if (f.id == action.noteId) continue;
|
|
||||||
newNotes.push(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
newState = Object.assign({}, state);
|
|
||||||
newState.notes = newNotes;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'FOLDERS_UPDATE_ALL':
|
|
||||||
|
|
||||||
newState = Object.assign({}, state);
|
|
||||||
newState.folders = action.folders;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'TAGS_UPDATE_ALL':
|
|
||||||
|
|
||||||
newState = Object.assign({}, state);
|
|
||||||
newState.tags = action.tags;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'FOLDERS_UPDATE_ONE':
|
|
||||||
|
|
||||||
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;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'SIDE_MENU_TOGGLE':
|
|
||||||
|
|
||||||
newState = Object.assign({}, state);
|
|
||||||
newState.showSideMenu = !newState.showSideMenu
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'SIDE_MENU_OPEN':
|
|
||||||
|
|
||||||
newState = Object.assign({}, state);
|
|
||||||
newState.showSideMenu = true
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'SIDE_MENU_CLOSE':
|
|
||||||
|
|
||||||
newState = Object.assign({}, state);
|
|
||||||
newState.showSideMenu = false
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'SYNC_STARTED':
|
|
||||||
|
|
||||||
newState = Object.assign({}, state);
|
|
||||||
newState.syncStarted = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'SYNC_COMPLETED':
|
|
||||||
|
|
||||||
newState = Object.assign({}, state);
|
|
||||||
newState.syncStarted = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'SYNC_REPORT_UPDATE':
|
|
||||||
|
|
||||||
newState = Object.assign({}, state);
|
|
||||||
newState.syncReport = action.report;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'SEARCH_QUERY':
|
|
||||||
|
|
||||||
newState = Object.assign({}, state);
|
|
||||||
newState.searchQuery = action.query.trim();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'SET_APP_STATE':
|
|
||||||
|
|
||||||
newState = Object.assign({}, state);
|
|
||||||
newState.appState = action.state;
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
error.message = 'In reducer: ' + error.message + ' Action: ' + JSON.stringify(action);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return newState;
|
|
||||||
}
|
|
||||||
|
|
||||||
const generalMiddleware = store => next => async (action) => {
|
const generalMiddleware = store => next => async (action) => {
|
||||||
reg.logger().info('Reducer action', action.type);
|
reg.logger().info('Reducer action', action.type);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user