Compare commits

...

6 Commits

Author SHA1 Message Date
zadam
3cf3fc13b9 release 0.39.5 2020-01-08 21:01:24 +01:00
zadam
2b69abf8ab fix filter parser for >=, <=, *=* 2020-01-08 20:23:41 +01:00
zadam
3e49a7dbfa all consistency checkers have now fixers 2020-01-08 19:28:22 +01:00
zadam
f782d2bef9 don't empty script area on save 2020-01-07 22:29:15 +01:00
zadam
ccaa9eae3a fix context submenus, closes #810 2020-01-07 20:53:41 +01:00
zadam
24c5388e0c protection against text note initialization race conditions 2020-01-07 19:48:26 +01:00
11 changed files with 87 additions and 75 deletions

View File

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

View File

@@ -76,8 +76,7 @@ async function initContextMenu(event, contextMenu) {
// in such case we'll position it above click coordinates so it will fit into client
const clickPosition = event.pageY;
const clientHeight = document.documentElement.clientHeight;
const contextMenuHeight = $contextMenuContainer.height();
const contextMenuHeight = $contextMenuContainer.outerHeight() + 30;
let top;
if (clickPosition + contextMenuHeight > clientHeight) {

View File

@@ -201,17 +201,13 @@ async function loadNoteDetail(origNotePath, options = {}) {
const newTab = !!options.newTab;
const activate = !!options.activate;
const notePath = await treeService.resolveNotePath(origNotePath);
let notePath = await treeService.resolveNotePath(origNotePath);
if (!notePath) {
console.error(`Cannot resolve note path ${origNotePath}`);
// fallback to display something
if (tabContexts.length === 0) {
await openEmptyTab();
}
return;
notePath = 'root';
}
const noteId = treeUtils.getNoteIdFromNotePath(notePath);

View File

@@ -47,6 +47,7 @@ class NoteDetailText {
this.ctx = ctx;
this.$component = ctx.$tabContent.find('.note-detail-text');
this.$editorEl = this.$component.find('.note-detail-text-editor');
this.textEditorPromise = null;
this.textEditor = null;
this.$component.on("dblclick", "img", e => {
@@ -67,44 +68,12 @@ class NoteDetailText {
}
async render() {
if (!this.textEditor) {
await libraryLoader.requireLibrary(libraryLoader.CKEDITOR);
const codeBlockLanguages =
(await mimeTypesService.getMimeTypes())
.filter(mt => mt.enabled)
.map(mt => {
return {
language: mt.mime.toLowerCase().replace(/[\W_]+/g,"-"),
label: mt.title
}
});
// CKEditor since version 12 needs the element to be visible before initialization. At the same time
// we want to avoid flicker - i.e. show editor only once everything is ready. That's why we have separate
// display of $component in both branches.
this.$component.show();
// textEditor might have been initialized during previous await so checking again
// looks like double initialization can freeze CKEditor pretty badly
if (!this.textEditor) {
this.textEditor = await BalloonEditor.create(this.$editorEl[0], {
placeholder: "Type the content of your note here ...",
mention: mentionSetup,
codeBlock: {
languages: codeBlockLanguages
}
});
if (glob.isDev && ENABLE_INSPECTOR) {
await import('../../libraries/ckeditor/inspector.js');
CKEditorInspector.attach(this.textEditor);
}
this.onNoteChange(() => this.ctx.noteChanged());
}
if (!this.textEditorPromise) {
this.textEditorPromise = this.initEditor();
}
await this.textEditorPromise;
// lazy loading above can take time and tab might have been already switched to another note
if (this.ctx.note && this.ctx.note.type === 'text') {
this.textEditor.isReadOnly = await this.isReadOnly();
@@ -115,6 +84,42 @@ class NoteDetailText {
}
}
async initEditor() {
await libraryLoader.requireLibrary(libraryLoader.CKEDITOR);
const codeBlockLanguages =
(await mimeTypesService.getMimeTypes())
.filter(mt => mt.enabled)
.map(mt => {
return {
language: mt.mime.toLowerCase().replace(/[\W_]+/g,"-"),
label: mt.title
}
});
// CKEditor since version 12 needs the element to be visible before initialization. At the same time
// we want to avoid flicker - i.e. show editor only once everything is ready. That's why we have separate
// display of $component in both branches.
this.$component.show();
const textEditorInstance = await BalloonEditor.create(this.$editorEl[0], {
placeholder: "Type the content of your note here ...",
mention: mentionSetup,
codeBlock: {
languages: codeBlockLanguages
}
});
if (glob.isDev && ENABLE_INSPECTOR) {
await import('../../libraries/ckeditor/inspector.js');
CKEditorInspector.attach(textEditorInstance);
}
this.textEditor = textEditorInstance;
this.onNoteChange(() => this.ctx.noteChanged());
}
getContent() {
const content = this.textEditor.getData();

View File

@@ -42,6 +42,8 @@ async function setupProtectedSession(password) {
return;
}
$("#container").addClass('protected-session-active');
protectedSessionHolder.setProtectedSessionId(response.protectedSessionId);
protectedSessionHolder.touchProtectedSession();

View File

@@ -350,8 +350,6 @@ class TabContext {
this.$savedIndicator.fadeIn();
this.$scriptArea.empty();
// run async
bundleService.executeRelationBundles(this.note, 'runOnNoteChange', this);

View File

@@ -110,7 +110,7 @@ body {
#context-menu-container {
max-height: 100vh;
overflow: auto; /* make it scrollable when exceeding total height of the window */
/* !!! Cannot set overflow: auto, submenus will break !!! */
}
#context-menu-container, #context-menu-container .dropdown-menu {

View File

@@ -1 +1 @@
module.exports = { buildDate:"2020-01-04T22:01:20+01:00", buildRevision: "3b8b4da149fbc1b17d09253693823f5135a55f2e" };
module.exports = { buildDate:"2020-01-08T21:01:24+01:00", buildRevision: "2b69abf8ab2241f01cd38b31308e54b9faaa74d5" };

View File

@@ -18,18 +18,6 @@ class ConsistencyChecks {
this.fixedIssues = false;
}
async findIssues(query, errorCb) {
const results = await sql.getRows(query);
for (const res of results) {
logError(errorCb(res));
this.unrecoveredConsistencyErrors = true;
}
return results;
}
async findAndFixIssues(query, fixerCb) {
const results = await sql.getRows(query);
@@ -175,13 +163,6 @@ class ConsistencyChecks {
logError(`Relation ${attributeId} references missing note ${noteId}`)
}
});
await this.findIssues(`
SELECT noteRevisionId, note_revisions.noteId
FROM note_revisions
LEFT JOIN notes USING (noteId)
WHERE notes.noteId IS NULL`,
({noteRevisionId, noteId}) => `Note revision ${noteRevisionId} references missing note ${noteId}`);
}
async findExistencyIssues() {
@@ -335,13 +316,22 @@ class ConsistencyChecks {
}
});
await this.findIssues(`
await this.findAndFixIssues(`
SELECT noteId
FROM notes
JOIN note_contents USING (noteId)
WHERE isErased = 1
AND content IS NOT NULL`,
({noteId}) => `Note ${noteId} content is not null even though the note is erased`);
async ({noteId}) => {
if (this.autoFix) {
await sql.execute(`UPDATE note_contents SET content = NULL WHERE noteId = ?`, [noteId]);
logFix(`Note ${noteId} content has been set to null since the note is erased`);
}
else {
logError(`Note ${noteId} content is not null even though the note is erased`);
}
});
await this.findAndFixIssues(`
SELECT noteId, noteRevisionId
@@ -398,20 +388,40 @@ class ConsistencyChecks {
}
});
await this.findIssues(`
await this.findAndFixIssues(`
SELECT noteRevisionId
FROM note_revisions
JOIN note_revision_contents USING (noteRevisionId)
WHERE isErased = 1
AND content IS NOT NULL`,
({noteRevisionId}) => `Note revision ${noteRevisionId} content is not null even though the note revision is erased`);
async ({noteRevisionId}) => {
if (this.autoFix) {
await sql.execute(`UPDATE note_revision_contents SET content = NULL WHERE noteRevisionId = ?`, [noteRevisionId]);
await this.findIssues(`
logFix(`Note revision ${noteRevisionId} content was set to null since the note revision is erased`);
}
else {
logError(`Note revision ${noteRevisionId} content is not null even though the note revision is erased`);
}
});
await this.findAndFixIssues(`
SELECT noteId
FROM notes
WHERE isErased = 1
AND isDeleted = 0`,
({noteId}) => `Note ${noteId} is not deleted even though it is erased`);
async ({noteId}) => {
if (this.autoFix) {
const note = await repository.getNote(noteId);
note.isDeleted = true;
await note.save();
logFix(`Note ${noteId} was set to deleted since it is erased`);
}
else {
logError(`Note ${noteId} is not deleted even though it is erased`);
}
});
await this.findAndFixIssues(`
SELECT parentNoteId

View File

@@ -490,6 +490,8 @@ async function eraseDeletedNotes() {
SET isErased = 1,
title = NULL
WHERE isErased = 0 AND noteId IN (???)`, noteIdsToErase);
log.info(`Erased notes: ${JSON.stringify(noteIdsToErase)}`);
}
async function duplicateNote(noteId, parentNoteId) {

View File

@@ -1,6 +1,6 @@
const dayjs = require("dayjs");
const filterRegex = /(\b(AND|OR)\s+)?@(!?)([\p{L}\p{Number}_]+|"[^"]+")\s*((=|!=|<|<=|>|>=|!?\*=|!?=\*|!?\*=\*)\s*(\S+|"[^"]+"))?/igu;
const filterRegex = /(\b(AND|OR)\s+)?@(!?)([\p{L}\p{Number}_]+|"[^"]+")\s*((=|!=|<|<=|>|>=|!?\*=|!?=\*|!?\*=\*)\s*([^\s=*]+|"[^"]+"))?/igu;
const smartValueRegex = /^(NOW|TODAY|WEEK|MONTH|YEAR) *([+\-] *\d+)?$/i;
function calculateSmartValue(v) {