Compare commits

...

9 Commits

11 changed files with 74 additions and 22 deletions

Binary file not shown.

View File

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

View File

@@ -71,7 +71,6 @@ export default class ButtonWidget extends NoteContextAwareWidget {
}
this.$widget
.attr("title", this.settings.title)
.addClass(this.settings.icon);
}

View File

@@ -1,5 +1,7 @@
import ButtonWidget from "./button_widget.js";
import appContext from "../../services/app_context.js";
import attributeService from "../../services/attributes.js";
import protectedSessionHolder from "../../services/protected_session_holder.js";
export default class EditButton extends ButtonWidget {
isEnabled() {
@@ -22,9 +24,29 @@ export default class EditButton extends ButtonWidget {
}
async refreshWithNote(note) {
if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) {
this.toggleInt(false);
}
else {
// prevent flickering by assuming hidden before async operation
this.toggleInt(false);
// can't do this in isEnabled() since isReadOnly is async
this.toggleInt(await this.noteContext.isReadOnly());
}
await super.refreshWithNote(note);
}
entitiesReloadedEvent({loadResults}) {
if (loadResults.getAttributes().find(
attr => attr.type === 'label'
&& attr.name.toLowerCase().includes("readonly")
&& attributeService.isAffecting(attr, this.note)
)) {
this.noteContext.readOnlyTemporarilyDisabled = false;
this.refresh();
}
}
}

View File

@@ -191,7 +191,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
await this.initialized;
this.textEditor.model.change(writer => {
const insertPosition = this.textEditor.model.document.selection.getFirstPosition();
const insertPosition = this.textEditor.model.document.selection.getLastPosition();
writer.insertText(text, insertPosition);
});
}

View File

@@ -1,4 +1,5 @@
import NoteContextAwareWidget from "../note_context_aware_widget.js";
import appContext from "../../services/app_context.js";
export default class TypeWidget extends NoteContextAwareWidget {
// for overriding
@@ -34,7 +35,7 @@ export default class TypeWidget extends NoteContextAwareWidget {
}
isActive() {
return this.$widget.is(":visible");
return this.$widget.is(":visible") && this.noteContext?.ntxId === appContext.tabManager.activeNtxId;
}
getContent() {}

View File

@@ -1 +1 @@
module.exports = { buildDate:"2022-04-22T00:07:59+02:00", buildRevision: "3b58b83f8bb93c04263081f60d75f211320ed065" };
module.exports = { buildDate:"2022-05-01T23:18:35+02:00", buildRevision: "b3763eed610fa3f2aabbcbdbd21efca704a5dd08" };

View File

@@ -10,7 +10,7 @@ const utils = require("../../utils");
// FIXME: create common subclass with NoteContentUnprotectedFulltextExp to avoid duplication
class NoteContentProtectedFulltextExp extends Expression {
constructor(operator, tokens, raw) {
constructor(operator, {tokens, raw, flatText}) {
super();
if (operator !== '*=*') {
@@ -19,6 +19,7 @@ class NoteContentProtectedFulltextExp extends Expression {
this.tokens = tokens;
this.raw = !!raw;
this.flatText = !!flatText;
}
execute(inputNoteSet) {
@@ -33,7 +34,7 @@ class NoteContentProtectedFulltextExp extends Expression {
for (let {noteId, type, mime, content} of sql.iterateRows(`
SELECT noteId, type, mime, content
FROM notes JOIN note_contents USING (noteId)
WHERE type IN ('text', 'code') AND isDeleted = 0 AND isProtected = 1`)) {
WHERE type IN ('text', 'code', 'mermaid') AND isDeleted = 0 AND isProtected = 1`)) {
if (!inputNoteSet.hasNoteId(noteId) || !(noteId in becca.notes)) {
continue;
@@ -49,7 +50,17 @@ class NoteContentProtectedFulltextExp extends Expression {
content = this.preprocessContent(content, type, mime);
if (!this.tokens.find(token => !content.includes(token))) {
const nonMatchingToken = this.tokens.find(token =>
!content.includes(token) &&
(
// in case of default fulltext search we should consider both title, attrs and content
// so e.g. "hello world" should match when "hello" is in title and "world" in content
!this.flatText
|| !becca.notes[noteId].getFlatText().includes(token)
)
);
if (!nonMatchingToken) {
resultNoteSet.add(becca.notes[noteId]);
}
}

View File

@@ -8,7 +8,7 @@ const utils = require("../../utils");
// FIXME: create common subclass with NoteContentProtectedFulltextExp to avoid duplication
class NoteContentUnprotectedFulltextExp extends Expression {
constructor(operator, tokens, raw) {
constructor(operator, {tokens, raw, flatText}) {
super();
if (operator !== '*=*') {
@@ -17,6 +17,7 @@ class NoteContentUnprotectedFulltextExp extends Expression {
this.tokens = tokens;
this.raw = !!raw;
this.flatText = !!flatText;
}
execute(inputNoteSet) {
@@ -27,7 +28,7 @@ class NoteContentUnprotectedFulltextExp extends Expression {
for (let {noteId, type, mime, content} of sql.iterateRows(`
SELECT noteId, type, mime, content
FROM notes JOIN note_contents USING (noteId)
WHERE type IN ('text', 'code') AND isDeleted = 0 AND isProtected = 0`)) {
WHERE type IN ('text', 'code', 'mermaid') AND isDeleted = 0 AND isProtected = 0`)) {
if (!inputNoteSet.hasNoteId(noteId) || !(noteId in becca.notes)) {
continue;
@@ -35,7 +36,17 @@ class NoteContentUnprotectedFulltextExp extends Expression {
content = this.preprocessContent(content, type, mime);
if (!this.tokens.find(token => !content.includes(token))) {
const nonMatchingToken = this.tokens.find(token =>
!content.includes(token) &&
(
// in case of default fulltext search we should consider both title, attrs and content
// so e.g. "hello world" should match when "hello" is in title and "world" in content
!this.flatText
|| !becca.notes[noteId].getFlatText().includes(token)
)
);
if (!nonMatchingToken) {
resultNoteSet.add(becca.notes[noteId]);
}
}

View File

@@ -32,8 +32,8 @@ function getFulltext(tokens, searchContext) {
if (!searchContext.fastSearch) {
return new OrExp([
new NoteFlatTextExp(tokens),
new NoteContentProtectedFulltextExp('*=*', tokens),
new NoteContentUnprotectedFulltextExp('*=*', tokens)
new NoteContentProtectedFulltextExp('*=*', {tokens, flatText: true}),
new NoteContentUnprotectedFulltextExp('*=*', {tokens, flatText: true})
]);
}
else {
@@ -141,8 +141,8 @@ function getExpression(tokens, searchContext, level = 0) {
i++;
return new OrExp([
new NoteContentUnprotectedFulltextExp(operator, [tokens[i].token], raw),
new NoteContentProtectedFulltextExp(operator, [tokens[i].token], raw)
new NoteContentUnprotectedFulltextExp(operator, {tokens: [tokens[i].token], raw }),
new NoteContentProtectedFulltextExp(operator, {tokens: [tokens[i].token], raw })
]);
}
@@ -196,8 +196,8 @@ function getExpression(tokens, searchContext, level = 0) {
return new OrExp([
new PropertyComparisonExp(searchContext, 'title', '*=*', tokens[i].token),
new NoteContentProtectedFulltextExp('*=*', [tokens[i].token]),
new NoteContentUnprotectedFulltextExp('*=*', [tokens[i].token])
new NoteContentProtectedFulltextExp('*=*', {tokens: [tokens[i].token]}),
new NoteContentUnprotectedFulltextExp('*=*', {tokens: [tokens[i].token]})
]);
}

View File

@@ -62,16 +62,18 @@ function register(router) {
});
router.get('/share/:shareId', (req, res, next) => {
const {shareId} = req.params;
shacaLoader.ensureLoad();
const {shareId} = req.params;
const note = shaca.aliasToNote[shareId] || shaca.notes[shareId];
renderNote(note, res);
});
router.get('/share/api/notes/:noteId', (req, res, next) => {
shacaLoader.ensureLoad();
const {noteId} = req.params;
const note = shaca.getNote(noteId);
@@ -85,6 +87,8 @@ function register(router) {
});
router.get('/share/api/notes/:noteId/download', (req, res, next) => {
shacaLoader.ensureLoad();
const {noteId} = req.params;
const note = shaca.getNote(noteId);
@@ -107,6 +111,8 @@ function register(router) {
});
router.get('/share/api/images/:noteId/:filename', (req, res, next) => {
shacaLoader.ensureLoad();
const image = shaca.getNote(req.params.noteId);
if (!image) {
@@ -118,13 +124,15 @@ function register(router) {
addNoIndexHeader(image, res);
res.set('Content-Type', image.mime);
res.setHeader('Content-Type', image.mime);
res.send(image.getContent());
});
// used for PDF viewing
router.get('/share/api/notes/:noteId/view', (req, res, next) => {
shacaLoader.ensureLoad();
const {noteId} = req.params;
const note = shaca.getNote(noteId);