Compare commits

...

15 Commits

Author SHA1 Message Date
zadam
7651c53363 release 0.40.2 2020-02-01 10:17:51 +01:00
zadam
0f25c8a95f autobook should not be active on the mobile interface 2020-02-01 10:17:03 +01:00
zadam
1a49894adf fix tree loading on mobile interface, closes #839 2020-02-01 10:04:18 +01:00
zadam
bd8c078fb9 Merge remote-tracking branch 'origin/master' 2020-02-01 09:30:14 +01:00
zadam
6e060b87b8 fix date parsing in local timezone, closes #845 2020-02-01 09:29:56 +01:00
jasontan056
2375b170ba Pass deleteId to deleteBranch in ensureNoteIsAbsentFromParent (#846) 2020-02-01 09:05:23 +01:00
zadam
828cce0d78 release 0.40.1 2020-01-19 15:45:06 +01:00
zadam
ab535bf147 fixes of the new CopyWithoutFormatting 2020-01-19 09:25:35 +01:00
Heniker
1876664dfb add hotkey to copy contents with line breaks, fixes #349 (#831) 2020-01-19 09:16:36 +01:00
zadam
1690248e24 migration script to fix contentLength = -1 in new notes 2020-01-19 09:08:33 +01:00
zadam
cbeb8ea17e fix setting contentLength 2020-01-19 09:03:26 +01:00
zadam
c9113ae752 Merge branch 'stable' 2020-01-18 09:24:39 +01:00
zadam
0ec11d29ba fix creating root calendar note when missing, #752 2020-01-18 08:59:46 +01:00
zadam
a6cd25071e more robust handling of sync error, fixes #830 2020-01-18 08:48:36 +01:00
zadam
20fdeee048 better error handling for search notes 2020-01-13 19:35:06 +01:00
19 changed files with 101 additions and 31 deletions

3
.idea/.gitignore generated vendored
View File

@@ -2,4 +2,5 @@
/workspace.xml /workspace.xml
# Datasource local storage ignored files # Datasource local storage ignored files
/dataSources.local.xml /dataSources.local.xml
/dataSources/

View File

@@ -0,0 +1 @@
UPDATE notes SET contentLength = COALESCE((SELECT COALESCE(LENGTH(content), 0) FROM note_contents WHERE note_contents.noteId = notes.noteId), -1);

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "trilium", "name": "trilium",
"version": "0.39.5", "version": "0.40.1",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {

View File

@@ -2,7 +2,7 @@
"name": "trilium", "name": "trilium",
"productName": "Trilium Notes", "productName": "Trilium Notes",
"description": "Trilium Notes", "description": "Trilium Notes",
"version": "0.40.0-beta", "version": "0.40.2",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"main": "electron.js", "main": "electron.js",
"bin": { "bin": {

View File

@@ -110,6 +110,10 @@ class Note extends Entity {
async getJsonContent() { async getJsonContent() {
const content = await this.getContent(); const content = await this.getContent();
if (!content || !content.trim()) {
return null;
}
return JSON.parse(content); return JSON.parse(content);
} }
@@ -119,9 +123,11 @@ class Note extends Entity {
throw new Error(`Cannot set null content to note ${this.noteId}`); throw new Error(`Cannot set null content to note ${this.noteId}`);
} }
content = Buffer.isBuffer(content) ? content : Buffer.from(content);
// force updating note itself so that dateModified is represented correctly even for the content // force updating note itself so that dateModified is represented correctly even for the content
this.forcedChange = true; this.forcedChange = true;
this.contentLength = content.length; this.contentLength = content.byteLength;
await this.save(); await this.save();
this.content = content; this.content = content;
@@ -130,7 +136,7 @@ class Note extends Entity {
noteId: this.noteId, noteId: this.noteId,
content: content, content: content,
utcDateModified: dateUtils.utcNowDateTime(), utcDateModified: dateUtils.utcNowDateTime(),
hash: utils.hash(this.noteId + "|" + content) hash: utils.hash(this.noteId + "|" + content.toString())
}; };
if (this.isProtected) { if (this.isProtected) {

View File

@@ -87,6 +87,8 @@ async function showTree() {
}); });
} }
}); });
treeService.setTree($.ui.fancytree.getTree("#tree"));
} }
$detail.on("click", ".note-menu-button", async e => { $detail.on("click", ".note-menu-button", async e => {

View File

@@ -241,7 +241,7 @@ function registerEntrypoints() {
d.showDialog(selectedOrActiveNodes); d.showDialog(selectedOrActiveNodes);
})); }));
keyboardActionService.setGlobalActionHandler("CreateNoteIntoDayNote", async () => { keyboardActionService.setGlobalActionHandler("CreateNoteIntoDayNote", async () => {
const todayNote = await dateNoteService.getTodayNote(); const todayNote = await dateNoteService.getTodayNote();
@@ -288,6 +288,8 @@ function registerEntrypoints() {
searchNotesService.searchInSubtree(node.data.noteId); searchNotesService.searchInSubtree(node.data.noteId);
}); });
keyboardActionService.setGlobalActionHandler("CopyWithoutFormatting", utils.copySelectionToClipboard);
} }
export default { export default {

View File

@@ -5,6 +5,7 @@ import splitService from "./split.js";
import optionService from "./options.js"; import optionService from "./options.js";
import server from "./server.js"; import server from "./server.js";
import noteDetailService from "./note_detail.js"; import noteDetailService from "./note_detail.js";
import utils from "./utils.js";
const $sidebar = $("#right-pane"); const $sidebar = $("#right-pane");
const $sidebarContainer = $('#sidebar-container'); const $sidebarContainer = $('#sidebar-container');
@@ -15,6 +16,10 @@ const $hideSidebarButton = $("#hide-sidebar-button");
optionService.waitForOptions().then(options => toggleSidebar(options.is('rightPaneVisible'))); optionService.waitForOptions().then(options => toggleSidebar(options.is('rightPaneVisible')));
function toggleSidebar(show) { function toggleSidebar(show) {
if (utils.isMobile()) {
return;
}
$sidebar.toggle(show); $sidebar.toggle(show);
$showSidebarButton.toggle(!show); $showSidebarButton.toggle(!show);
$hideSidebarButton.toggle(show); $hideSidebarButton.toggle(show);

View File

@@ -303,7 +303,11 @@ class TabContext {
let type = this.note.type; let type = this.note.type;
if (type === 'text' && !disableAutoBook && utils.isHtmlEmpty(this.note.content) && this.note.hasChildren()) { if (type === 'text'
&& !disableAutoBook
&& utils.isHtmlEmpty(this.note.content)
&& this.note.hasChildren()
&& utils.isDesktop()) {
type = 'book'; type = 'book';
} }

View File

@@ -913,6 +913,10 @@ function getNodeByKey(key) {
return tree.getNodeByKey(key); return tree.getNodeByKey(key);
} }
function setTree(treeInstance) {
tree = treeInstance;
}
keyboardActionService.setGlobalActionHandler('CollapseTree', () => collapseTree()); // don't use shortened form since collapseTree() accepts argument keyboardActionService.setGlobalActionHandler('CollapseTree', () => collapseTree()); // don't use shortened form since collapseTree() accepts argument
$collapseTreeButton.on('click', () => collapseTree()); $collapseTreeButton.on('click', () => collapseTree());
@@ -949,5 +953,6 @@ export default {
focusTree, focusTree,
scrollToActiveNote, scrollToActiveNote,
duplicateNote, duplicateNote,
getNodeByKey getNodeByKey,
setTree
}; };

View File

@@ -241,6 +241,13 @@ function getUrlForDownload(url) {
} }
} }
function copySelectionToClipboard() {
const text = window.getSelection().toString();
if (navigator.clipboard) {
navigator.clipboard.writeText(text);
}
}
export default { export default {
reloadApp, reloadApp,
parseDate, parseDate,
@@ -273,5 +280,6 @@ export default {
isHtmlEmpty, isHtmlEmpty,
clearBrowserCache, clearBrowserCache,
getUrlForDownload, getUrlForDownload,
normalizeShortcut normalizeShortcut,
copySelectionToClipboard
}; };

View File

@@ -41,7 +41,7 @@ class CalendarWidget extends StandardWidget {
} }
init($el, activeDate) { init($el, activeDate) {
this.activeDate = new Date(Date.parse(activeDate)); this.activeDate = new Date(activeDate + "T12:00:00"); // attaching time fixes local timezone handling
this.todaysDate = new Date(); this.todaysDate = new Date();
this.date = new Date(this.activeDate.getTime()); this.date = new Date(this.activeDate.getTime());

View File

@@ -29,8 +29,12 @@ async function searchFromNote(req) {
return [404, `Note ${req.params.noteId} has not been found.`]; return [404, `Note ${req.params.noteId} has not been found.`];
} }
if (note.isDeleted) {
return [400, `Note ${req.params.noteId} is deleted.`];
}
if (note.type !== 'search') { if (note.type !== 'search') {
return [400, '`Note ${req.params.noteId} is not search note.`'] return [400, `Note ${req.params.noteId} is not search note.`]
} }
const json = await note.getJsonContent(); const json = await note.getJsonContent();
@@ -41,18 +45,28 @@ async function searchFromNote(req) {
let noteIds; let noteIds;
if (json.searchString.startsWith('=')) { try {
const relationName = json.searchString.substr(1).trim(); if (json.searchString.startsWith('=')) {
const relationName = json.searchString.substr(1).trim();
noteIds = await searchFromRelation(note, relationName); noteIds = await searchFromRelation(note, relationName);
} else {
noteIds = await searchService.searchForNoteIds(json.searchString);
}
} }
else { catch (e) {
noteIds = await searchService.searchForNoteIds(json.searchString); log.error(`Search failed for note ${note.noteId}: ` + e.message + ": " + e.stack);
throw new Error("Search failed, see logs for details.");
} }
// we won't return search note's own noteId // we won't return search note's own noteId
noteIds = noteIds.filter(noteId => noteId !== note.noteId); noteIds = noteIds.filter(noteId => noteId !== note.noteId);
if (noteIds.length > 200) {
noteIds = noteIds.slice(0, 200);
}
return noteIds.map(noteCacheService.getNotePath).filter(res => !!res); return noteIds.map(noteCacheService.getNotePath).filter(res => !!res);
} }

View File

@@ -4,7 +4,7 @@ const build = require('./build');
const packageJson = require('../../package'); const packageJson = require('../../package');
const {TRILIUM_DATA_DIR} = require('./data_dir'); const {TRILIUM_DATA_DIR} = require('./data_dir');
const APP_DB_VERSION = 156; const APP_DB_VERSION = 157;
const SYNC_VERSION = 14; const SYNC_VERSION = 14;
const CLIPPER_PROTOCOL_VERSION = "1.0"; const CLIPPER_PROTOCOL_VERSION = "1.0";

View File

@@ -1 +1 @@
module.exports = { buildDate:"2020-01-11T09:54:31+01:00", buildRevision: "5e91b1b5e0fa04affbc1a5a43a874cadc382fe11" }; module.exports = { buildDate:"2020-02-01T10:17:51+01:00", buildRevision: "0f25c8a95f381d99b66735b9c0af3e319edb72ed" };

View File

@@ -7,6 +7,7 @@ const noteService = require('./notes');
const repository = require('./repository'); const repository = require('./repository');
const Branch = require('../entities/branch'); const Branch = require('../entities/branch');
const TaskContext = require("./task_context.js"); const TaskContext = require("./task_context.js");
const utils = require('./utils');
async function cloneNoteToParent(noteId, parentNoteId, prefix) { async function cloneNoteToParent(noteId, parentNoteId, prefix) {
if (await isNoteDeleted(noteId) || await isNoteDeleted(parentNoteId)) { if (await isNoteDeleted(noteId) || await isNoteDeleted(parentNoteId)) {
@@ -54,7 +55,8 @@ async function ensureNoteIsAbsentFromParent(noteId, parentNoteId) {
const branch = await repository.getEntity(`SELECT * FROM branches WHERE noteId = ? AND parentNoteId = ? AND isDeleted = 0`, [noteId, parentNoteId]); const branch = await repository.getEntity(`SELECT * FROM branches WHERE noteId = ? AND parentNoteId = ? AND isDeleted = 0`, [noteId, parentNoteId]);
if (branch) { if (branch) {
await noteService.deleteBranch(branch, new TaskContext()); const deleteId = utils.randomString(10);
await noteService.deleteBranch(branch, deleteId, new TaskContext());
} }
} }

View File

@@ -40,7 +40,9 @@ async function getRootCalendarNote() {
parentNoteId: 'root', parentNoteId: 'root',
title: 'Calendar', title: 'Calendar',
target: 'into', target: 'into',
isProtected: false isProtected: false,
type: 'text',
content: ''
})).note; })).note;
await attributeService.createLabel(rootNote.noteId, CALENDAR_ROOT_LABEL); await attributeService.createLabel(rootNote.noteId, CALENDAR_ROOT_LABEL);

View File

@@ -306,6 +306,10 @@ const DEFAULT_KEYBOARD_ACTIONS = [
{ {
actionName: "ZoomIn", actionName: "ZoomIn",
defaultShortcuts: ["CommandOrControl+="] defaultShortcuts: ["CommandOrControl+="]
},
{
actionName: "CopyWithoutFormatting",
defaultShortcuts: ["CommandOrControl+Alt+C"]
} }
]; ];

View File

@@ -71,22 +71,36 @@ function sendMessageToAllClients(message) {
} }
} }
async function fillInAdditionalProperties(sync) {
// fill in some extra data needed by the frontend
if (sync.entityName === 'attributes') {
sync.noteId = await sql.getValue(`SELECT noteId
FROM attributes
WHERE attributeId = ?`, [sync.entityId]);
} else if (sync.entityName === 'note_revisions') {
sync.noteId = await sql.getValue(`SELECT noteId
FROM note_revisions
WHERE noteRevisionId = ?`, [sync.entityId]);
} else if (sync.entityName === 'branches') {
const {noteId, parentNoteId} = await sql.getRow(`SELECT noteId, parentNoteId
FROM branches
WHERE branchId = ?`, [sync.entityId]);
sync.noteId = noteId;
sync.parentNoteId = parentNoteId;
}
}
async function sendPing(client) { async function sendPing(client) {
const syncData = require('./sync_table').getEntitySyncsNewerThan(lastAcceptedSyncIds[client.id]); const syncData = require('./sync_table').getEntitySyncsNewerThan(lastAcceptedSyncIds[client.id]);
for (const sync of syncData) { for (const sync of syncData) {
// fill in some extra data needed by the frontend try {
if (sync.entityName === 'attributes') { await fillInAdditionalProperties(sync);
sync.noteId = await sql.getValue(`SELECT noteId FROM attributes WHERE attributeId = ?`, [sync.entityId]);
} }
else if (sync.entityName === 'note_revisions') { catch (e) {
sync.noteId = await sql.getValue(`SELECT noteId FROM note_revisions WHERE noteRevisionId = ?`, [sync.entityId]); log.error("Could not fill additional properties for sync " + JSON.stringify(sync)
} + " because of error: " + e.message + ": " + e.stack);
else if (sync.entityName === 'branches') {
const {noteId, parentNoteId} = await sql.getRow(`SELECT noteId, parentNoteId FROM branches WHERE branchId = ?`, [sync.entityId]);
sync.noteId = noteId;
sync.parentNoteId = parentNoteId;
} }
} }