You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-08-13 22:12:50 +02:00
Improve browsing of notes
This commit is contained in:
@@ -196,18 +196,13 @@ async function main() {
|
|||||||
usage: 'use <notebook-title>',
|
usage: 'use <notebook-title>',
|
||||||
aliases: ['cd'],
|
aliases: ['cd'],
|
||||||
description: 'Switches to [notebook-title] - all further operations will happen within this notebook.',
|
description: 'Switches to [notebook-title] - all further operations will happen within this notebook.',
|
||||||
action: function(args, end) {
|
action: async function(args, end) {
|
||||||
let folderTitle = args['notebook-title'];
|
let folderTitle = args['notebook-title'];
|
||||||
|
|
||||||
if (folderTitle == '.' || folderTitle == '..') {
|
let folder = await Folder.loadByField('title', folderTitle);
|
||||||
end();
|
if (!folder) return commandError(this, _('Invalid folder title: %s', folderTitle), end);
|
||||||
return;
|
switchCurrentFolder(folder);
|
||||||
}
|
end();
|
||||||
|
|
||||||
Folder.loadByField('title', folderTitle).then((folder) => {
|
|
||||||
switchCurrentFolder(folder);
|
|
||||||
end();
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
autocomplete: autocompleteFolders,
|
autocomplete: autocompleteFolders,
|
||||||
});
|
});
|
||||||
@@ -355,14 +350,32 @@ async function main() {
|
|||||||
description: 'Displays the notes in [notebook-title]. Use `ls ..` to display the list of notebooks.',
|
description: 'Displays the notes in [notebook-title]. Use `ls ..` to display the list of notebooks.',
|
||||||
options: [
|
options: [
|
||||||
['-n, --lines <num>', 'Displays only the first top <num> lines.'],
|
['-n, --lines <num>', 'Displays only the first top <num> lines.'],
|
||||||
|
['-s, --sort <field>', 'Sorts the item by <field> (eg. title, updated_time, created_time).'],
|
||||||
|
['-r, --reverse', 'Reverses the sorting order.'],
|
||||||
|
['-t, --type <type>', 'Displays only the items of the specific type(s). Can be `n` for notes, `t` for todos, or `nt` for notes and todos (eg. `-tt` would display only the todos, while `-ttd` would display notes and todos.'],
|
||||||
],
|
],
|
||||||
action: async function(args, end) {
|
action: async function(args, end) {
|
||||||
let folderTitle = args['notebook-title'];
|
let folderTitle = args['notebook-title'];
|
||||||
let suffix = '';
|
let suffix = '';
|
||||||
let items = [];
|
let items = [];
|
||||||
|
let options = args.options;
|
||||||
|
|
||||||
|
let queryOptions = {};
|
||||||
|
if (options.lines) queryOptions.limit = options.lines;
|
||||||
|
if (options.sort) {
|
||||||
|
queryOptions.orderBy = options.sort;
|
||||||
|
queryOptions.orderByDir = 'ASC';
|
||||||
|
}
|
||||||
|
if (options.reverse === true) queryOptions.orderByDir = queryOptions.orderByDir == 'ASC' ? 'DESC' : 'ASC';
|
||||||
|
queryOptions.caseInsensitive = true;
|
||||||
|
if (options.type) {
|
||||||
|
queryOptions.itemTypes = [];
|
||||||
|
if (options.type.indexOf('n') >= 0) queryOptions.itemTypes.push('note');
|
||||||
|
if (options.type.indexOf('t') >= 0) queryOptions.itemTypes.push('todo');
|
||||||
|
}
|
||||||
|
|
||||||
if (folderTitle == '..') {
|
if (folderTitle == '..') {
|
||||||
items = await Folder.all();
|
items = await Folder.all(queryOptions);
|
||||||
suffix = '/';
|
suffix = '/';
|
||||||
} else {
|
} else {
|
||||||
let folder = null;
|
let folder = null;
|
||||||
@@ -375,12 +388,17 @@ async function main() {
|
|||||||
|
|
||||||
if (!folder) return commandError(this, _('Unknown notebook: "%s"', folderTitle), end);
|
if (!folder) return commandError(this, _('Unknown notebook: "%s"', folderTitle), end);
|
||||||
|
|
||||||
items = await Note.previews(folder.id);
|
items = await Note.previews(folder.id, queryOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
let item = items[i];
|
let item = items[i];
|
||||||
this.log(item.title + suffix);
|
let line = '';
|
||||||
|
if (!!item.is_todo) {
|
||||||
|
line += sprintf('[%s] ', !!item.todo_completed ? 'X' : ' ');
|
||||||
|
}
|
||||||
|
line += item.title + suffix;
|
||||||
|
this.log(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
end();
|
end();
|
||||||
|
@@ -46,7 +46,7 @@ describe('Synchronizer', function() {
|
|||||||
let folder = await Folder.save({ title: "folder1" });
|
let folder = await Folder.save({ title: "folder1" });
|
||||||
await Note.save({ title: "un", parent_id: folder.id });
|
await Note.save({ title: "un", parent_id: folder.id });
|
||||||
|
|
||||||
let all = await Folder.all(true);
|
let all = await Folder.all({ includeNotes: true });
|
||||||
|
|
||||||
await synchronizer().start();
|
await synchronizer().start();
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ describe('Synchronizer', function() {
|
|||||||
|
|
||||||
await Note.save({ title: "un UPDATE", id: note.id });
|
await Note.save({ title: "un UPDATE", id: note.id });
|
||||||
|
|
||||||
let all = await Folder.all(true);
|
let all = await Folder.all({ includeNotes: true });
|
||||||
await synchronizer().start();
|
await synchronizer().start();
|
||||||
|
|
||||||
await localItemsSameAsRemote(all, expect);
|
await localItemsSameAsRemote(all, expect);
|
||||||
@@ -81,7 +81,7 @@ describe('Synchronizer', function() {
|
|||||||
|
|
||||||
await synchronizer().start();
|
await synchronizer().start();
|
||||||
|
|
||||||
let all = await Folder.all(true);
|
let all = await Folder.all({ includeNotes: true });
|
||||||
await localItemsSameAsRemote(all, expect);
|
await localItemsSameAsRemote(all, expect);
|
||||||
|
|
||||||
done();
|
done();
|
||||||
@@ -109,7 +109,7 @@ describe('Synchronizer', function() {
|
|||||||
|
|
||||||
await synchronizer().start();
|
await synchronizer().start();
|
||||||
|
|
||||||
let all = await Folder.all(true);
|
let all = await Folder.all({ includeNotes: true });
|
||||||
let files = await fileApi().list();
|
let files = await fileApi().list();
|
||||||
|
|
||||||
await localItemsSameAsRemote(all, expect);
|
await localItemsSameAsRemote(all, expect);
|
||||||
@@ -250,7 +250,7 @@ describe('Synchronizer', function() {
|
|||||||
|
|
||||||
await synchronizer().start();
|
await synchronizer().start();
|
||||||
|
|
||||||
let items = await Folder.all(true);
|
let items = await Folder.all({ includeNotes: true });
|
||||||
|
|
||||||
expect(items.length).toBe(1);
|
expect(items.length).toBe(1);
|
||||||
|
|
||||||
@@ -288,7 +288,7 @@ describe('Synchronizer', function() {
|
|||||||
expect(conflictedNotes.length).toBe(1);
|
expect(conflictedNotes.length).toBe(1);
|
||||||
expect(conflictedNotes[0].title).toBe(newTitle);
|
expect(conflictedNotes[0].title).toBe(newTitle);
|
||||||
|
|
||||||
let items = await Folder.all(true);
|
let items = await Folder.all({ includeNotes: true });
|
||||||
|
|
||||||
expect(items.length).toBe(1);
|
expect(items.length).toBe(1);
|
||||||
|
|
||||||
@@ -319,7 +319,7 @@ describe('Synchronizer', function() {
|
|||||||
|
|
||||||
await synchronizer().start();
|
await synchronizer().start();
|
||||||
|
|
||||||
let items = await Folder.all(true);
|
let items = await Folder.all({ includeNotes: true });
|
||||||
|
|
||||||
expect(items.length).toBe(0);
|
expect(items.length).toBe(0);
|
||||||
|
|
||||||
|
@@ -120,6 +120,24 @@ class BaseModel {
|
|||||||
return this.loadByField('id', id);
|
return this.loadByField('id', id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static applySqlOptions(options, sql, params = null) {
|
||||||
|
if (!options) options = {};
|
||||||
|
|
||||||
|
if (options.orderBy) {
|
||||||
|
sql += ' ORDER BY ' + options.orderBy;
|
||||||
|
if (options.caseInsensitive === true) sql += ' COLLATE NOCASE';
|
||||||
|
if (options.orderByDir) sql += ' ' + options.orderByDir;
|
||||||
|
}
|
||||||
|
if (options.limit) sql += ' LIMIT ' + options.limit;
|
||||||
|
|
||||||
|
return { sql: sql, params: params };
|
||||||
|
}
|
||||||
|
|
||||||
|
static async all(options = null) {
|
||||||
|
let q = this.applySqlOptions(options, 'SELECT * FROM `' + this.tableName() + '`');
|
||||||
|
return await this.modelSelectAll(q.sql);
|
||||||
|
}
|
||||||
|
|
||||||
static modelSelectOne(sql, params = null) {
|
static modelSelectOne(sql, params = null) {
|
||||||
if (params === null) params = [];
|
if (params === null) params = [];
|
||||||
return this.db().selectOne(sql, params).then((model) => {
|
return this.db().selectOne(sql, params).then((model) => {
|
||||||
|
@@ -81,11 +81,15 @@ class Folder extends BaseItem {
|
|||||||
return this.modelSelectOne('SELECT * FROM notes WHERE is_conflict = 0 AND `parent_id` = ? AND `' + field + '` = ?', [folderId, value]);
|
return this.modelSelectOne('SELECT * FROM notes WHERE is_conflict = 0 AND `parent_id` = ? AND `' + field + '` = ?', [folderId, value]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static async all(includeNotes = false) {
|
static async all(options = null) {
|
||||||
let folders = await Folder.modelSelectAll('SELECT * FROM folders');
|
if (!options) options = {};
|
||||||
if (!includeNotes) return folders;
|
|
||||||
|
|
||||||
let notes = await Note.modelSelectAll('SELECT * FROM notes WHERE is_conflict = 0');
|
let folders = await super.all(options);
|
||||||
|
if (!options.includeNotes) return folders;
|
||||||
|
|
||||||
|
if (options.limit) options.limit -= folders.length;
|
||||||
|
|
||||||
|
let notes = await Note.all(options);
|
||||||
return folders.concat(notes);
|
return folders.concat(notes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -43,8 +43,24 @@ class Note extends BaseItem {
|
|||||||
return '`id`, `title`, `body`, `is_todo`, `todo_completed`, `parent_id`, `updated_time`'
|
return '`id`, `title`, `body`, `is_todo`, `todo_completed`, `parent_id`, `updated_time`'
|
||||||
}
|
}
|
||||||
|
|
||||||
static previews(parentId) {
|
static previews(parentId, options = null) {
|
||||||
return this.modelSelectAll('SELECT ' + this.previewFieldsSql() + ' FROM notes WHERE is_conflict = 0 AND parent_id = ?', [parentId]);
|
if (!options) options = {};
|
||||||
|
if (!options.orderBy) options.orderBy = 'updated_time';
|
||||||
|
if (!options.orderByDir) options.orderByDir = 'DESC';
|
||||||
|
|
||||||
|
let sql = 'SELECT ' + this.previewFieldsSql() + ' FROM notes WHERE is_conflict = 0 AND parent_id = ?';
|
||||||
|
if (options.itemTypes && options.itemTypes.length) {
|
||||||
|
if (options.itemTypes.indexOf('note') >= 0 && options.itemTypes.indexOf('todo') >= 0) {
|
||||||
|
// Fetch everything
|
||||||
|
} else if (options.itemTypes.indexOf('note') >= 0) {
|
||||||
|
sql += ' AND is_todo = 0';
|
||||||
|
} else if (options.itemTypes.indexOf('todo') >= 0) {
|
||||||
|
sql += ' AND is_todo = 1';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let query = this.applySqlOptions(options, sql, [parentId]);
|
||||||
|
|
||||||
|
return this.modelSelectAll(query.sql, query.params);
|
||||||
}
|
}
|
||||||
|
|
||||||
static preview(noteId) {
|
static preview(noteId) {
|
||||||
|
Reference in New Issue
Block a user