Merge branch 'master' into m43

This commit is contained in:
zadam 2020-05-05 19:38:42 +02:00
commit 768ac83e14
28 changed files with 216 additions and 233 deletions

2
.idea/dataSources.xml generated
View File

@ -5,7 +5,7 @@
<driver-ref>sqlite.xerial</driver-ref> <driver-ref>sqlite.xerial</driver-ref>
<synchronize>true</synchronize> <synchronize>true</synchronize>
<jdbc-driver>org.sqlite.JDBC</jdbc-driver> <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
<jdbc-url>jdbc:sqlite:$PROJECT_DIR$/../trilium-data/document.db</jdbc-url> <jdbc-url>jdbc:sqlite:$USER_HOME$/trilium-data/document.db</jdbc-url>
</data-source> </data-source>
<data-source source="LOCAL" name="document" uuid="066dc5f4-4097-429e-8cf1-3adc0a9d648a"> <data-source source="LOCAL" name="document" uuid="066dc5f4-4097-429e-8cf1-3adc0a9d648a">
<driver-ref>sqlite.xerial</driver-ref> <driver-ref>sqlite.xerial</driver-ref>

View File

@ -1,6 +1,7 @@
<component name="InspectionProjectProfileManager"> <component name="InspectionProjectProfileManager">
<profile version="1.0"> <profile version="1.0">
<option name="myName" value="Project Default" /> <option name="myName" value="Project Default" />
<inspection_tool class="JSUnfilteredForInLoop" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false"> <inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" /> <option name="processCode" value="true" />
<option name="processLiterals" value="true" /> <option name="processLiterals" value="true" />

34
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "trilium", "name": "trilium",
"version": "0.41.6", "version": "0.42.0-beta",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -3345,9 +3345,9 @@
} }
}, },
"electron": { "electron": {
"version": "9.0.0-beta.21", "version": "9.0.0-beta.22",
"resolved": "https://registry.npmjs.org/electron/-/electron-9.0.0-beta.21.tgz", "resolved": "https://registry.npmjs.org/electron/-/electron-9.0.0-beta.22.tgz",
"integrity": "sha512-xFOD8I4RB9IkpVKnzoHwHvDNGvGl1IinpYTyQ7o7FAgSnkvP/upI1JtzE5Ff6PlAdyIGnbC+Rz1hJIfmAXxVuQ==", "integrity": "sha512-dfqAf+CXXTKcNDj7DU7mYsmx+oZQcXOvJnZ8ZsgAHjrE9Tv8zsYUgCP3JlO4Z8CIazgleKXYmgh6H2stdK7fEA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@electron/get": "^1.0.1", "@electron/get": "^1.0.1",
@ -3785,9 +3785,9 @@
"dev": true "dev": true
}, },
"mime": { "mime": {
"version": "2.4.4", "version": "2.4.5",
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.4.tgz", "resolved": "https://registry.npmjs.org/mime/-/mime-2.4.5.tgz",
"integrity": "sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==", "integrity": "sha512-3hQhEUF027BuxZjQA3s7rIv/7VCQPa27hN9u9g87sEkWaKwQPuXOkVKtOeiyUrnWqTDiOs8Ed2rwg733mB0R5w==",
"dev": true "dev": true
}, },
"supports-color": { "supports-color": {
@ -4448,9 +4448,9 @@
} }
}, },
"file-type": { "file-type": {
"version": "14.2.0", "version": "14.3.0",
"resolved": "https://registry.npmjs.org/file-type/-/file-type-14.2.0.tgz", "resolved": "https://registry.npmjs.org/file-type/-/file-type-14.3.0.tgz",
"integrity": "sha512-CAkX5G5jq8LIgFu++dpM3giMZadYdU+QVQoPLajjNboo8IzaR4cKpBCVEuz+suhd/vHqoAJeSWhEubKjRPQHJg==", "integrity": "sha512-s71v6jMkbfwVdj87csLeNpL5K93mv4lN+lzgzifoICtPHhnXokDwBa3jrzfg+z6FK872iYJ0vS0i74v8XmoFDA==",
"requires": { "requires": {
"readable-web-to-node-stream": "^2.0.0", "readable-web-to-node-stream": "^2.0.0",
"strtok3": "^6.0.0", "strtok3": "^6.0.0",
@ -9725,7 +9725,6 @@
"version": "2.88.0", "version": "2.88.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
"integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
"dev": true,
"requires": { "requires": {
"aws-sign2": "~0.7.0", "aws-sign2": "~0.7.0",
"aws4": "^1.8.0", "aws4": "^1.8.0",
@ -9752,14 +9751,12 @@
"qs": { "qs": {
"version": "6.5.2", "version": "6.5.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
"dev": true
}, },
"tunnel-agent": { "tunnel-agent": {
"version": "0.6.0", "version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
"dev": true,
"requires": { "requires": {
"safe-buffer": "^5.0.1" "safe-buffer": "^5.0.1"
} }
@ -10351,12 +10348,13 @@
"integrity": "sha512-1bBO+me3gXRfqwRR3K9aNDoSbTkQ87o6fSjj/BE2gSHHsK3qIDR+LoFZHgZ6kSPdFBoLTsy5/w/+8PBBaK+lvg==" "integrity": "sha512-1bBO+me3gXRfqwRR3K9aNDoSbTkQ87o6fSjj/BE2gSHHsK3qIDR+LoFZHgZ6kSPdFBoLTsy5/w/+8PBBaK+lvg=="
}, },
"sqlite3": { "sqlite3": {
"version": "4.2.0", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.2.0.tgz", "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-4.1.1.tgz",
"integrity": "sha512-roEOz41hxui2Q7uYnWsjMOTry6TcNUNmp8audCx18gF10P2NknwdpF+E+HKvz/F2NvPKGGBF4NGc+ZPQ+AABwg==", "integrity": "sha512-CvT5XY+MWnn0HkbwVKJAyWEMfzpAPwnTiB3TobA5Mri44SrTovmmh499NPQP+gatkeOipqPlBLel7rn4E/PCQg==",
"requires": { "requires": {
"nan": "^2.12.1", "nan": "^2.12.1",
"node-pre-gyp": "^0.11.0" "node-pre-gyp": "^0.11.0",
"request": "^2.87.0"
} }
}, },
"squeak": { "squeak": {

View File

@ -2,7 +2,7 @@
"name": "trilium", "name": "trilium",
"productName": "Trilium Notes", "productName": "Trilium Notes",
"description": "Trilium Notes", "description": "Trilium Notes",
"version": "0.41.6", "version": "0.42.0-beta",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"main": "electron.js", "main": "electron.js",
"bin": { "bin": {
@ -37,7 +37,7 @@
"electron-window-state": "5.0.3", "electron-window-state": "5.0.3",
"express": "4.17.1", "express": "4.17.1",
"express-session": "1.17.1", "express-session": "1.17.1",
"file-type": "14.2.0", "file-type": "14.3.0",
"fs-extra": "9.0.0", "fs-extra": "9.0.0",
"helmet": "3.22.0", "helmet": "3.22.0",
"html": "1.0.0", "html": "1.0.0",
@ -67,7 +67,7 @@
"session-file-store": "1.4.0", "session-file-store": "1.4.0",
"simple-node-logger": "18.12.24", "simple-node-logger": "18.12.24",
"sqlite": "4.0.7", "sqlite": "4.0.7",
"sqlite3": "4.2.0", "sqlite3": "4.1.1",
"string-similarity": "4.0.1", "string-similarity": "4.0.1",
"tar-stream": "2.1.2", "tar-stream": "2.1.2",
"turndown": "6.0.0", "turndown": "6.0.0",
@ -78,7 +78,7 @@
"yazl": "^2.5.1" "yazl": "^2.5.1"
}, },
"devDependencies": { "devDependencies": {
"electron": "9.0.0-beta.21", "electron": "9.0.0-beta.22",
"electron-builder": "22.6.0", "electron-builder": "22.6.0",
"electron-packager": "14.2.1", "electron-packager": "14.2.1",
"electron-rebuild": "1.10.1", "electron-rebuild": "1.10.1",

View File

@ -8,6 +8,8 @@ const RELATION = 'relation';
const RELATION_DEFINITION = 'relation-definition'; const RELATION_DEFINITION = 'relation-definition';
/** /**
* FIXME: since there's no "full note" anymore we can rename this to Note
*
* This note's representation is used in note tree and is kept in TreeCache. * This note's representation is used in note tree and is kept in TreeCache.
*/ */
class NoteShort { class NoteShort {

View File

@ -100,19 +100,6 @@ class AppContext extends Component {
getComponentByEl(el) { getComponentByEl(el) {
return $(el).closest(".component").prop('component'); return $(el).closest(".component").prop('component');
} }
async openInNewWindow(notePath) {
if (utils.isElectron()) {
const {ipcRenderer} = utils.dynamicRequire('electron');
ipcRenderer.send('create-extra-window', {notePath});
}
else {
const url = window.location.protocol + '//' + window.location.host + window.location.pathname + '?extra=1#' + notePath;
window.open(url, '', 'width=1000,height=800');
}
}
} }
const appContext = new AppContext(window.glob.isMainWindow); const appContext = new AppContext(window.glob.isMainWindow);

View File

@ -182,4 +182,21 @@ export default class Entrypoints extends Component {
} }
createTopLevelNoteCommand() { noteCreateService.createNewTopLevelNote(); } createTopLevelNoteCommand() { noteCreateService.createNewTopLevelNote(); }
async openInWindowCommand({notePath}) {
if (utils.isElectron()) {
const {ipcRenderer} = utils.dynamicRequire('electron');
ipcRenderer.send('create-extra-window', {notePath});
}
else {
const url = window.location.protocol + '//' + window.location.host + window.location.pathname + '?extra=1#' + notePath;
window.open(url, '', 'width=1000,height=800');
}
}
async openNewWindowCommand() {
this.openInWindowCommand({notePath: ''});
}
} }

View File

@ -114,7 +114,7 @@ function newTabContextMenu(e) {
y: e.pageY, y: e.pageY,
items: [ items: [
{title: "Open note in new tab", command: "openNoteInNewTab", uiIcon: "arrow-up-right"}, {title: "Open note in new tab", command: "openNoteInNewTab", uiIcon: "arrow-up-right"},
{title: "Open note in new window", command: "openNoteInNewWindow", uiIcon: "arrow-up-right"} {title: "Open note in new window", command: "openNoteInNewWindow", uiIcon: "window-open"}
], ],
selectMenuItemHandler: ({command}) => { selectMenuItemHandler: ({command}) => {
if (command === 'openNoteInNewTab') { if (command === 'openNoteInNewTab') {

View File

@ -59,7 +59,7 @@ export default class MainTreeExecutors extends Component {
target: 'after', target: 'after',
targetBranchId: node.data.branchId, targetBranchId: node.data.branchId,
isProtected: isProtected, isProtected: isProtected,
saveSelection: true saveSelection: false
}); });
await ws.waitForMaxKnownSyncId(); await ws.waitForMaxKnownSyncId();

View File

@ -31,6 +31,15 @@ function enterProtectedSession() {
return dfd.promise(); return dfd.promise();
} }
async function reloadData() {
const allNoteIds = Object.keys(treeCache.notes);
await treeCache.loadInitialTree();
// make sure that all notes used in the application are loaded, including the ones not shown in the tree
await treeCache.reloadNotes(allNoteIds, true);
}
async function setupProtectedSession(password) { async function setupProtectedSession(password) {
const response = await enterProtectedSessionOnServer(password); const response = await enterProtectedSessionOnServer(password);
@ -42,7 +51,7 @@ async function setupProtectedSession(password) {
protectedSessionHolder.setProtectedSessionId(response.protectedSessionId); protectedSessionHolder.setProtectedSessionId(response.protectedSessionId);
protectedSessionHolder.touchProtectedSession(); protectedSessionHolder.touchProtectedSession();
await treeCache.loadInitialTree(); await reloadData();
await appContext.triggerEvent('treeCacheReloaded'); await appContext.triggerEvent('treeCacheReloaded');

View File

@ -82,7 +82,7 @@ export default class TabManager extends Component {
if (filteredTabs.length === 0) { if (filteredTabs.length === 0) {
filteredTabs.push({ filteredTabs.push({
notePath: 'root', notePath: this.isMainWindow ? 'root' : '',
active: true active: true
}); });
} }
@ -196,7 +196,9 @@ export default class TabManager extends Component {
async openTabWithNote(notePath, activate, tabId = null) { async openTabWithNote(notePath, activate, tabId = null) {
const tabContext = await this.openEmptyTab(tabId); const tabContext = await this.openEmptyTab(tabId);
await tabContext.setNote(notePath, !activate); // if activate is false then send normal noteSwitched event if (notePath) {
await tabContext.setNote(notePath, !activate); // if activate is false then send normal noteSwitched event
}
if (activate) { if (activate) {
this.activateTab(tabContext.tabId, false); this.activateTab(tabContext.tabId, false);
@ -265,6 +267,9 @@ export default class TabManager extends Component {
this.children = this.children.filter(tc => tc.tabId !== tabId); this.children = this.children.filter(tc => tc.tabId !== tabId);
// remove dangling autocompletes after closing the tab
$(".algolia-autocomplete").remove();
this.triggerEvent('tabRemoved', {tabId}); this.triggerEvent('tabRemoved', {tabId});
this.tabsUpdate.scheduleUpdate(); this.tabsUpdate.scheduleUpdate();
@ -327,7 +332,7 @@ export default class TabManager extends Component {
this.removeTab(tabId); this.removeTab(tabId);
appContext.openInNewWindow(notePath); this.triggerCommand('openInWindow', {notePath});
} }
async hoistedNoteChangedEvent({hoistedNoteId}) { async hoistedNoteChangedEvent({hoistedNoteId}) {

View File

@ -20,6 +20,8 @@ class TreeCache {
async loadInitialTree() { async loadInitialTree() {
const resp = await server.get('tree'); const resp = await server.get('tree');
await this.loadParents(resp, false);
// clear the cache only directly before adding new content which is important for e.g. switching to protected session // clear the cache only directly before adding new content which is important for e.g. switching to protected session
/** @type {Object.<string, NoteShort>} */ /** @type {Object.<string, NoteShort>} */
@ -34,22 +36,22 @@ class TreeCache {
/** @type {Object.<string, Promise<NoteComplement>>} */ /** @type {Object.<string, Promise<NoteComplement>>} */
this.noteComplementPromises = {}; this.noteComplementPromises = {};
await this.loadParents(resp);
this.addResp(resp); this.addResp(resp);
} }
async loadParents(resp) { async loadParents(resp, additiveLoad) {
const noteIds = new Set(resp.notes.map(note => note.noteId)); const noteIds = new Set(resp.notes.map(note => note.noteId));
const missingNoteIds = []; const missingNoteIds = [];
const existingNotes = additiveLoad ? this.notes : {};
for (const branch of resp.branches) { for (const branch of resp.branches) {
if (!(branch.parentNoteId in this.notes) && !noteIds.has(branch.parentNoteId) && branch.parentNoteId !== 'none') { if (!(branch.parentNoteId in existingNotes) && !noteIds.has(branch.parentNoteId) && branch.parentNoteId !== 'none') {
missingNoteIds.push(branch.parentNoteId); missingNoteIds.push(branch.parentNoteId);
} }
} }
for (const attr of resp.attributes) { for (const attr of resp.attributes) {
if (attr.type === 'relation' && attr.name === 'template' && !(attr.value in this.notes) && !noteIds.has(attr.value)) { if (attr.type === 'relation' && attr.name === 'template' && !(attr.value in existingNotes) && !noteIds.has(attr.value)) {
missingNoteIds.push(attr.value); missingNoteIds.push(attr.value);
} }
} }
@ -61,7 +63,7 @@ class TreeCache {
resp.branches = resp.branches.concat(newResp.branches); resp.branches = resp.branches.concat(newResp.branches);
resp.attributes = resp.attributes.concat(newResp.attributes); resp.attributes = resp.attributes.concat(newResp.attributes);
await this.loadParents(resp); await this.loadParents(resp, additiveLoad);
} }
} }
@ -154,7 +156,7 @@ class TreeCache {
const resp = await server.post('tree/load', { noteIds }); const resp = await server.post('tree/load', { noteIds });
await this.loadParents(resp); await this.loadParents(resp, true);
this.addResp(resp); this.addResp(resp);
for (const note of resp.notes) { for (const note of resp.notes) {
@ -231,7 +233,7 @@ class TreeCache {
/** @return {Promise<NoteShort>} */ /** @return {Promise<NoteShort>} */
async getNote(noteId, silentNotFoundError = false) { async getNote(noteId, silentNotFoundError = false) {
if (noteId === 'none') { if (noteId === 'none') {
console.log(`No 'none' note.`); console.trace(`No 'none' note.`);
return null; return null;
} }
else if (!noteId) { else if (!noteId) {
@ -246,10 +248,10 @@ class TreeCache {
return this.notes[noteId]; return this.notes[noteId];
} }
getBranches(branchIds) { getBranches(branchIds, silentNotFoundError = false) {
return branchIds return branchIds
.map(branchId => this.getBranch(branchId)) .map(branchId => this.getBranch(branchId, silentNotFoundError))
.filter(b => b !== null); .filter(b => !!b);
} }
/** @return {Branch} */ /** @return {Branch} */

View File

@ -40,9 +40,9 @@ class TreeContextMenu {
async getMenuItems() { async getMenuItems() {
const note = await treeCache.getNote(this.node.data.noteId); const note = await treeCache.getNote(this.node.data.noteId);
const branch = treeCache.getBranch(this.node.data.branchId); const branch = treeCache.getBranch(this.node.data.branchId);
const parentNote = await treeCache.getNote(branch.parentNoteId);
const isNotRoot = note.noteId !== 'root'; const isNotRoot = note.noteId !== 'root';
const isHoisted = note.noteId === hoistedNoteService.getHoistedNoteId(); const isHoisted = note.noteId === hoistedNoteService.getHoistedNoteId();
const parentNote = isNotRoot ? await treeCache.getNote(branch.parentNoteId) : null;
// some actions don't support multi-note so they are disabled when notes are selected // some actions don't support multi-note so they are disabled when notes are selected
// the only exception is when the only selected note is the one that was right-clicked, then // the only exception is when the only selected note is the one that was right-clicked, then
@ -57,7 +57,7 @@ class TreeContextMenu {
return [ return [
{ title: 'Open in a new tab <kbd>Ctrl+Click</kbd>', command: "openInTab", uiIcon: "empty", enabled: noSelectedNotes }, { title: 'Open in a new tab <kbd>Ctrl+Click</kbd>', command: "openInTab", uiIcon: "empty", enabled: noSelectedNotes },
{ title: 'Open in a new window', command: "openInWindow", uiIcon: "empty", enabled: noSelectedNotes }, { title: 'Open in a new window', command: "openInWindow", uiIcon: "window-open", enabled: noSelectedNotes },
{ title: 'Insert note after <kbd data-command="createNoteAfter"></kbd>', command: "insertNoteAfter", uiIcon: "plus", { title: 'Insert note after <kbd data-command="createNoteAfter"></kbd>', command: "insertNoteAfter", uiIcon: "plus",
items: insertNoteAfterEnabled ? this.getNoteTypeItems("insertNoteAfter") : null, items: insertNoteAfterEnabled ? this.getNoteTypeItems("insertNoteAfter") : null,
enabled: insertNoteAfterEnabled && noSelectedNotes }, enabled: insertNoteAfterEnabled && noSelectedNotes },
@ -113,9 +113,6 @@ class TreeContextMenu {
if (command === 'openInTab') { if (command === 'openInTab') {
appContext.tabManager.openTabWithNote(notePath); appContext.tabManager.openTabWithNote(notePath);
} }
else if (command === 'openInWindow') {
appContext.openInNewWindow(notePath);
}
else if (command === "insertNoteAfter") { else if (command === "insertNoteAfter") {
const parentNoteId = this.node.data.parentNoteId; const parentNoteId = this.node.data.parentNoteId;
const isProtected = await treeService.getParentProtectedStatus(this.node); const isProtected = await treeService.getParentProtectedStatus(this.node);
@ -134,7 +131,7 @@ class TreeContextMenu {
}); });
} }
else { else {
this.treeWidget.triggerCommand(command, {node: this.node}); this.treeWidget.triggerCommand(command, {node: this.node, notePath: notePath});
} }
} }
} }

View File

@ -1,5 +1,4 @@
import BasicWidget from "./basic_widget.js"; import BasicWidget from "./basic_widget.js";
import keyboardActionService from "../services/keyboard_actions.js";
import utils from "../services/utils.js"; import utils from "../services/utils.js";
import syncService from "../services/sync.js"; import syncService from "../services/sync.js";
@ -39,6 +38,12 @@ const TPL = `
Sync (<span id="outstanding-syncs-count">0</span>) Sync (<span id="outstanding-syncs-count">0</span>)
</a> </a>
<a class="dropdown-item" data-trigger-command="openNewWindow">
<span class="bx bx-window-open"></span>
Open new window
<kbd data-command="openNewWindow"></kbd>
</a>
<a class="dropdown-item open-dev-tools-button" data-trigger-command="openDevTools"> <a class="dropdown-item open-dev-tools-button" data-trigger-command="openDevTools">
<span class="bx bx-terminal"></span> <span class="bx bx-terminal"></span>
Open Dev Tools Open Dev Tools

View File

@ -57,6 +57,10 @@ export default class NoteTitleWidget extends TabAwareWidget {
this.$noteTitle.prop("readonly", note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()); this.$noteTitle.prop("readonly", note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable());
this.setProtectedStatus(note);
}
setProtectedStatus(note) {
this.$noteTitle.toggleClass("protected", !!note.isProtected); this.$noteTitle.toggleClass("protected", !!note.isProtected);
} }
@ -88,7 +92,8 @@ export default class NoteTitleWidget extends TabAwareWidget {
entitiesReloadedEvent({loadResults}) { entitiesReloadedEvent({loadResults}) {
if (loadResults.isNoteReloaded(this.noteId)) { if (loadResults.isNoteReloaded(this.noteId)) {
this.refresh(); // not updating the title specifically since the synced title might be older than what the user is currently typing
this.setProtectedStatus(this.note);
} }
} }

View File

@ -51,7 +51,7 @@ const TPL = `
position: absolute; position: absolute;
top: 10px; top: 10px;
right: 20px; right: 20px;
z-index: 1000; z-index: 100;
} }
.tree-settings-popup { .tree-settings-popup {
@ -362,17 +362,13 @@ export default class NoteTreeWidget extends TabAwareWidget {
}, },
// this is done to automatically lazy load all expanded notes after tree load // this is done to automatically lazy load all expanded notes after tree load
loadChildren: (event, data) => { loadChildren: (event, data) => {
// semaphore since the conflict when two processes are trying to load the same data data.node.visit((subNode) => {
// breaks the fancytree // Load all lazy/unloaded child nodes
if (!this.tree || !this.tree.autoLoadingDisabled) { // (which will trigger `loadChildren` recursively)
data.node.visit((subNode) => { if (subNode.isUndefined() && subNode.isExpanded()) {
// Load all lazy/unloaded child nodes subNode.load();
// (which will trigger `loadChildren` recursively) }
if (subNode.isUndefined() && subNode.isExpanded()) { });
subNode.load();
}
});
}
} }
}); });
@ -423,7 +419,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
return labels.map(l => l.value).join(' '); return labels.map(l => l.value).join(' ');
} }
getIcon(note) { getIcon(note, isFolder) {
const hoistedNoteId = hoistedNoteService.getHoistedNoteId(); const hoistedNoteId = hoistedNoteService.getHoistedNoteId();
const iconClass = this.getIconClass(note); const iconClass = this.getIconClass(note);
@ -438,7 +434,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
return "bx bxs-arrow-from-bottom"; return "bx bxs-arrow-from-bottom";
} }
else if (note.type === 'text') { else if (note.type === 'text') {
if (note.hasChildren()) { if (isFolder) {
return "bx bx-folder"; return "bx bx-folder";
} }
else { else {
@ -460,6 +456,8 @@ export default class NoteTreeWidget extends TabAwareWidget {
const title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title; const title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title;
const hoistedNoteId = hoistedNoteService.getHoistedNoteId(); const hoistedNoteId = hoistedNoteService.getHoistedNoteId();
const isFolder = this.isFolder(note);
const node = { const node = {
noteId: note.noteId, noteId: note.noteId,
parentNoteId: branch.parentNoteId, parentNoteId: branch.parentNoteId,
@ -468,10 +466,10 @@ export default class NoteTreeWidget extends TabAwareWidget {
noteType: note.type, noteType: note.type,
title: utils.escapeHtml(title), title: utils.escapeHtml(title),
extraClasses: this.getExtraClasses(note), extraClasses: this.getExtraClasses(note),
icon: this.getIcon(note), icon: this.getIcon(note, isFolder),
refKey: note.noteId, refKey: note.noteId,
lazy: true, lazy: true,
folder: await this.isFolder(note), folder: isFolder,
expanded: branch.isExpanded || hoistedNoteId === note.noteId, expanded: branch.isExpanded || hoistedNoteId === note.noteId,
key: utils.randomString(12) // this should prevent some "duplicate key" errors key: utils.randomString(12) // this should prevent some "duplicate key" errors
}; };
@ -483,12 +481,12 @@ export default class NoteTreeWidget extends TabAwareWidget {
return node; return node;
} }
async isFolder(note) { isFolder(note) {
if (note.type === 'search') { if (note.type === 'search') {
return true; return true;
} }
else { else {
const childBranches = await this.getChildBranches(note); const childBranches = this.getChildBranches(note);
return childBranches.length > 0; return childBranches.length > 0;
} }
@ -499,7 +497,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
const noteList = []; const noteList = [];
for (const branch of await this.getChildBranches(parentNote)) { for (const branch of this.getChildBranches(parentNote)) {
const node = await this.prepareNode(branch); const node = await this.prepareNode(branch);
noteList.push(node); noteList.push(node);
@ -508,7 +506,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
return noteList; return noteList;
} }
async getChildBranches(parentNote) { getChildBranches(parentNote) {
let childBranches = parentNote.getChildBranches(); let childBranches = parentNote.getChildBranches();
if (!childBranches) { if (!childBranches) {
@ -523,20 +521,6 @@ export default class NoteTreeWidget extends TabAwareWidget {
childBranches = childBranches.filter(branch => !imageLinks.find(rel => rel.value === branch.noteId)); childBranches = childBranches.filter(branch => !imageLinks.find(rel => rel.value === branch.noteId));
} }
if (this.hideArchivedNotes) {
const filteredBranches = [];
for (const childBranch of childBranches) {
const childNote = await childBranch.getNote();
if (!childNote.hasLabel('archived')) {
filteredBranches.push(childBranch);
}
}
childBranches = filteredBranches;
}
return childBranches; return childBranches;
} }
@ -596,39 +580,32 @@ export default class NoteTreeWidget extends TabAwareWidget {
return notes; return notes;
} }
async expandTree(node = null) { async setExpandedStatusForSubtree(node, isExpanded) {
if (!node) { if (!node) {
const hoistedNoteId = hoistedNoteService.getHoistedNoteId(); const hoistedNoteId = hoistedNoteService.getHoistedNoteId();
node = this.getNodesByNoteId(hoistedNoteId)[0]; node = this.getNodesByNoteId(hoistedNoteId)[0];
} }
this.batchUpdate(async () => { const {branchIds} = await server.put(`branches/${node.data.branchId}/expanded-subtree/${isExpanded ? 1 : 0}`);
try {
this.tree.autoLoadingDisabled = true;
// trick - first force load of the whole subtree and then visit and expand. treeCache.getBranches(branchIds, true).forEach(branch => branch.isExpanded = isExpanded);
// unfortunately the two steps can't be combined
await node.visitAndLoad(_ => {}, true);
node.visit(node => node.setExpanded(true), true); await this.batchUpdate(async () => {
} await node.load(true);
finally {
this.tree.autoLoadingDisabled = false; if (node.data.noteId !== 'root') { // root is always expanded
await node.setExpanded(isExpanded, {noEvents: true});
} }
}); });
} }
collapseTree(node = null) { async expandTree(node = null) {
if (!node) { await this.setExpandedStatusForSubtree(node, true);
const hoistedNoteId = hoistedNoteService.getHoistedNoteId(); }
node = this.getNodesByNoteId(hoistedNoteId)[0]; async collapseTree(node = null) {
} await this.setExpandedStatusForSubtree(node, false);
this.batchUpdate(() => {
node.visit(node => node.setExpanded(false), true);
});
} }
/** /**
@ -740,14 +717,16 @@ export default class NoteTreeWidget extends TabAwareWidget {
return this.getNodeFromPath(notePath, true, expandOpts); return this.getNodeFromPath(notePath, true, expandOpts);
} }
async updateNode(node) { updateNode(node) {
const note = treeCache.getNoteFromCache(node.data.noteId); const note = treeCache.getNoteFromCache(node.data.noteId);
const branch = treeCache.getBranch(node.data.branchId); const branch = treeCache.getBranch(node.data.branchId);
const isFolder = this.isFolder(note);
node.data.isProtected = note.isProtected; node.data.isProtected = note.isProtected;
node.data.noteType = note.type; node.data.noteType = note.type;
node.folder = await this.isFolder(note); node.folder = isFolder;
node.icon = this.getIcon(note); node.icon = this.getIcon(note, isFolder);
node.extraClasses = this.getExtraClasses(note); node.extraClasses = this.getExtraClasses(note);
node.title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title; node.title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title;
node.renderTitle(); node.renderTitle();
@ -898,18 +877,12 @@ export default class NoteTreeWidget extends TabAwareWidget {
noteIdsToUpdate.add(noteId); noteIdsToUpdate.add(noteId);
} }
for (const noteId of noteIdsToReload) {
for (const node of this.getNodesByNoteId(noteId)) {
await node.load(true);
this.updateNode(node);
}
}
await this.batchUpdate(async () => { await this.batchUpdate(async () => {
for (const noteId of noteIdsToUpdate) { for (const noteId of noteIdsToReload) {
for (const node of this.getNodesByNoteId(noteId)) { for (const node of this.getNodesByNoteId(noteId)) {
this.updateNode(node); await node.load(true);
noteIdsToUpdate.add(noteId);
} }
} }
@ -931,6 +904,13 @@ export default class NoteTreeWidget extends TabAwareWidget {
} }
}); });
// for some reason node update cannot be in the batchUpdate() block (node is not re-rendered)
for (const noteId of noteIdsToUpdate) {
for (const node of this.getNodesByNoteId(noteId)) {
this.updateNode(node);
}
}
if (activeNotePath) { if (activeNotePath) {
let node = await this.expandToNote(activeNotePath); let node = await this.expandToNote(activeNotePath);

View File

@ -258,9 +258,9 @@ export default class TabRowWidget extends BasicWidget {
x: e.pageX, x: e.pageX,
y: e.pageY, y: e.pageY,
items: [ items: [
{title: "Move this tab to a new window", command: "moveTabToNewWindow", uiIcon: "empty"}, {title: "Move this tab to a new window", command: "moveTabToNewWindow", uiIcon: "window-open"},
{title: "Close all tabs", command: "removeAllTabs", uiIcon: "empty"}, {title: "Close all tabs", command: "removeAllTabs", uiIcon: "x"},
{title: "Close all tabs except for this", command: "removeAllTabsExceptForThis", uiIcon: "empty"}, {title: "Close all tabs except for this", command: "removeAllTabsExceptForThis", uiIcon: "x"},
], ],
selectMenuItemHandler: ({command}) => { selectMenuItemHandler: ({command}) => {
this.triggerCommand(command, {tabId}); this.triggerCommand(command, {tabId});

View File

@ -114,8 +114,34 @@ async function moveBranchAfterNote(req) {
async function setExpanded(req) { async function setExpanded(req) {
const {branchId, expanded} = req.params; const {branchId, expanded} = req.params;
await sql.execute("UPDATE branches SET isExpanded = ? WHERE branchId = ?", [expanded, branchId]); if (branchId !== 'root') {
// we don't sync expanded label await sql.execute("UPDATE branches SET isExpanded = ? WHERE branchId = ?", [expanded, branchId]);
// we don't sync expanded label
}
}
async function setExpandedForSubtree(req) {
const {branchId, expanded} = req.params;
let branchIds = await sql.getColumn(`
WITH RECURSIVE
tree(branchId, noteId) AS (
SELECT branchId, noteId FROM branches WHERE branchId = ?
UNION
SELECT branches.branchId, branches.noteId FROM branches
JOIN tree ON branches.parentNoteId = tree.noteId
WHERE branches.isDeleted = 0
)
SELECT branchId FROM tree`, [branchId]);
// root is always expanded
branchIds = branchIds.filter(branchId => branchId !== 'root');
await sql.executeMany(`UPDATE branches SET isExpanded = ${expanded} WHERE branchId IN (???)`, branchIds);
return {
branchIds
};
} }
async function deleteBranch(req) { async function deleteBranch(req) {
@ -149,6 +175,7 @@ module.exports = {
moveBranchBeforeNote, moveBranchBeforeNote,
moveBranchAfterNote, moveBranchAfterNote,
setExpanded, setExpanded,
setExpandedForSubtree,
deleteBranch, deleteBranch,
setPrefix setPrefix
}; };

View File

@ -127,6 +127,7 @@ function register(app) {
apiRoute(PUT, '/api/branches/:branchId/move-before/:beforeBranchId', branchesApiRoute.moveBranchBeforeNote); apiRoute(PUT, '/api/branches/:branchId/move-before/:beforeBranchId', branchesApiRoute.moveBranchBeforeNote);
apiRoute(PUT, '/api/branches/:branchId/move-after/:afterBranchId', branchesApiRoute.moveBranchAfterNote); apiRoute(PUT, '/api/branches/:branchId/move-after/:afterBranchId', branchesApiRoute.moveBranchAfterNote);
apiRoute(PUT, '/api/branches/:branchId/expanded/:expanded', branchesApiRoute.setExpanded); apiRoute(PUT, '/api/branches/:branchId/expanded/:expanded', branchesApiRoute.setExpanded);
apiRoute(PUT, '/api/branches/:branchId/expanded-subtree/:expanded', branchesApiRoute.setExpandedForSubtree);
apiRoute(DELETE, '/api/branches/:branchId', branchesApiRoute.deleteBranch); apiRoute(DELETE, '/api/branches/:branchId', branchesApiRoute.deleteBranch);
apiRoute(GET, '/api/autocomplete', autocompleteApiRoute.getAutocomplete); apiRoute(GET, '/api/autocomplete', autocompleteApiRoute.getAutocomplete);

View File

@ -1 +1 @@
module.exports = { buildDate:"2020-04-27T23:46:48+02:00", buildRevision: "0a9462241360e0baac71863af3ce7fb07cfd8c87" }; module.exports = { buildDate:"2020-05-04T21:59:14+02:00", buildRevision: "cafcb67a8a3a1943acac829590b34ff729b57e09" };

View File

@ -12,9 +12,7 @@ const VIRTUAL_ATTRIBUTES = [
"type", "type",
"mime", "mime",
"text", "text",
"parentCount", "parentCount"
"attributeName",
"attributeValue"
]; ];
module.exports = function(filters, selectedColumns = 'notes.*') { module.exports = function(filters, selectedColumns = 'notes.*') {
@ -35,29 +33,11 @@ module.exports = function(filters, selectedColumns = 'notes.*') {
// forcing to use particular index since SQLite query planner would often choose something pretty bad // forcing to use particular index since SQLite query planner would often choose something pretty bad
joins[alias] = `LEFT JOIN attributes AS ${alias} INDEXED BY IDX_attributes_noteId_index ` joins[alias] = `LEFT JOIN attributes AS ${alias} INDEXED BY IDX_attributes_noteId_index `
+ `ON ${alias}.noteId = notes.noteId AND ${alias}.isDeleted = 0 ` + `ON ${alias}.noteId = notes.noteId `
+ `AND ${alias}.name = '${property}' `; + `AND ${alias}.name = '${property}' AND ${alias}.isDeleted = 0`;
accessor = `${alias}.value`; accessor = `${alias}.value`;
} }
else if (['attributeType', 'attributeName', 'attributeValue'].includes(property)) {
const alias = "attr_filter";
if (!(alias in joins)) {
joins[alias] = `LEFT JOIN attributes AS ${alias} INDEXED BY IDX_attributes_noteId_index `
+ `ON ${alias}.noteId = notes.noteId AND ${alias}.isDeleted = 0`;
}
if (property === 'attributeType') {
accessor = `${alias}.type`
} else if (property === 'attributeName') {
accessor = `${alias}.name`
} else if (property === 'attributeValue') {
accessor = `${alias}.value`
} else {
throw new Error(`Unrecognized property ${property}`);
}
}
else if (property === 'content') { else if (property === 'content') {
const alias = "note_contents"; const alias = "note_contents";
@ -93,40 +73,33 @@ module.exports = function(filters, selectedColumns = 'notes.*') {
}); });
} }
let where = '1';
const params = []; const params = [];
function parseWhereFilters(filters) {
let whereStmt = '';
for (const filter of filters) { for (const filter of filters) {
if (['isarchived', 'in', 'orderby', 'limit'].includes(filter.name.toLowerCase())) { if (['isarchived', 'in', 'orderby', 'limit'].includes(filter.name.toLowerCase())) {
continue; // these are not real filters continue; // these are not real filters
} }
if (whereStmt) { where += " " + filter.relation + " ";
whereStmt += " " + filter.relation + " ";
}
if (filter.children) {
whereStmt += "(" + parseWhereFilters(filter.children) + ")";
continue;
}
const accessor = getAccessor(filter.name); const accessor = getAccessor(filter.name);
if (filter.operator === 'exists') { if (filter.operator === 'exists') {
whereStmt += `${accessor} IS NOT NULL`; where += `${accessor} IS NOT NULL`;
} else if (filter.operator === 'not-exists') { }
whereStmt += `${accessor} IS NULL`; else if (filter.operator === 'not-exists') {
} else if (filter.operator === '=' || filter.operator === '!=') { where += `${accessor} IS NULL`;
whereStmt += `${accessor} ${filter.operator} ?`; }
else if (filter.operator === '=' || filter.operator === '!=') {
where += `${accessor} ${filter.operator} ?`;
params.push(filter.value); params.push(filter.value);
} else if (filter.operator === '*=' || filter.operator === '!*=') { } else if (filter.operator === '*=' || filter.operator === '!*=') {
whereStmt += `${accessor}` where += `${accessor}`
+ (filter.operator.includes('!') ? ' NOT' : '') + (filter.operator.includes('!') ? ' NOT' : '')
+ ` LIKE ` + utils.prepareSqlForLike('%', filter.value, ''); + ` LIKE ` + utils.prepareSqlForLike('%', filter.value, '');
} else if (filter.operator === '=*' || filter.operator === '!=*') { } else if (filter.operator === '=*' || filter.operator === '!=*') {
whereStmt += `${accessor}` where += `${accessor}`
+ (filter.operator.includes('!') ? ' NOT' : '') + (filter.operator.includes('!') ? ' NOT' : '')
+ ` LIKE ` + utils.prepareSqlForLike('', filter.value, '%'); + ` LIKE ` + utils.prepareSqlForLike('', filter.value, '%');
} else if (filter.operator === '*=*' || filter.operator === '!*=*') { } else if (filter.operator === '*=*' || filter.operator === '!*=*') {
@ -145,8 +118,9 @@ module.exports = function(filters, selectedColumns = 'notes.*') {
condition = `(${condition} AND notes.isProtected = 0)`; condition = `(${condition} AND notes.isProtected = 0)`;
} }
whereStmt += condition; where += condition;
} else if ([">", ">=", "<", "<="].includes(filter.operator)) { }
else if ([">", ">=", "<", "<="].includes(filter.operator)) {
let floatParam; let floatParam;
// from https://stackoverflow.com/questions/12643009/regular-expression-for-floating-point-numbers // from https://stackoverflow.com/questions/12643009/regular-expression-for-floating-point-numbers
@ -156,10 +130,10 @@ module.exports = function(filters, selectedColumns = 'notes.*') {
if (floatParam === undefined || isNaN(floatParam)) { if (floatParam === undefined || isNaN(floatParam)) {
// if the value can't be parsed as float then we assume that string comparison should be used instead of numeric // if the value can't be parsed as float then we assume that string comparison should be used instead of numeric
whereStmt += `${accessor} ${filter.operator} ?`; where += `${accessor} ${filter.operator} ?`;
params.push(filter.value); params.push(filter.value);
} else { } else {
whereStmt += `CAST(${accessor} AS DECIMAL) ${filter.operator} ?`; where += `CAST(${accessor} AS DECIMAL) ${filter.operator} ?`;
params.push(floatParam); params.push(floatParam);
} }
} else { } else {
@ -167,11 +141,6 @@ module.exports = function(filters, selectedColumns = 'notes.*') {
} }
} }
return whereStmt;
}
const where = parseWhereFilters(filters);
if (orderBy.length === 0) { if (orderBy.length === 0) {
// if no ordering is given then order at least by note title // if no ordering is given then order at least by note title
orderBy.push("notes.title"); orderBy.push("notes.title");

View File

@ -617,6 +617,9 @@ class ConsistencyChecks {
await this.findSyncRowsIssues(); await this.findSyncRowsIssues();
// root branch should always be expanded
await sql.execute("UPDATE branches SET isExpanded = 1 WHERE branchId = 'root'");
if (this.unrecoveredConsistencyErrors) { if (this.unrecoveredConsistencyErrors) {
// we run this only if basic checks passed since this assumes basic data consistency // we run this only if basic checks passed since this assumes basic data consistency

View File

@ -193,7 +193,7 @@ const DEFAULT_KEYBOARD_ACTIONS = [
{ {
separator: "Tabs" separator: "Tabs & Windows"
}, },
{ {
actionName: "openNewTab", actionName: "openNewTab",
@ -219,6 +219,12 @@ const DEFAULT_KEYBOARD_ACTIONS = [
description: "Activates tab on the left", description: "Activates tab on the left",
scope: "window" scope: "window"
}, },
{
actionName: "openNewWindow",
defaultShortcuts: [],
description: "Open new empty window",
scope: "window"
},
{ {

View File

@ -276,9 +276,9 @@ async function downloadImage(noteId, imageUrl) {
const downloadImagePromises = {}; const downloadImagePromises = {};
function replaceUrl(content, url, imageNote) { function replaceUrl(content, url, imageNote) {
const quoted = url.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); const quoted = url.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&');
return content.replace(new RegExp(`\s+src=[\"']${quoted}[\"']`, "g"), ` src="api/images/${imageNote.noteId}/${imageNote.title}"`); return content.replace(new RegExp(`\\s+src=[\"']${quoted}[\"']`, "g"), ` src="api/images/${imageNote.noteId}/${imageNote.title}"`);
} }
async function downloadImages(noteId, content) { async function downloadImages(noteId, content) {
@ -288,11 +288,11 @@ async function downloadImages(noteId, content) {
const origContent = content; const origContent = content;
while (match = re.exec(origContent)) { while (match = re.exec(origContent)) {
const url = match[1].toLowerCase(); const url = match[1];
if (!url.startsWith('api/images/') if (!url.includes('api/images/')
// this is and exception for the web clipper's "imageId" // this is and exception for the web clipper's "imageId"
&& (url.length !== 20 || url.startsWith('http'))) { && (url.length !== 20 || url.toLowerCase().startsWith('http'))) {
if (url in downloadImagePromises) { if (url in downloadImagePromises) {
// download is already in progress // download is already in progress
continue; continue;
@ -347,7 +347,7 @@ async function downloadImages(noteId, content) {
for (const url in imageUrlToNoteIdMapping) { for (const url in imageUrlToNoteIdMapping) {
const imageNote = imageNotes.find(note => note.noteId === imageUrlToNoteIdMapping[url]); const imageNote = imageNotes.find(note => note.noteId === imageUrlToNoteIdMapping[url]);
if (imageNote) { if (imageNote && !imageNote.isDeleted) {
updatedContent = replaceUrl(updatedContent, url, imageNote); updatedContent = replaceUrl(updatedContent, url, imageNote);
} }
} }
@ -356,6 +356,8 @@ async function downloadImages(noteId, content) {
if (updatedContent !== origContent) { if (updatedContent !== origContent) {
await origNote.setContent(updatedContent); await origNote.setContent(updatedContent);
await scanForLinks(origNote);
console.log(`Fixed the image links for note ${noteId} to the offline saved.`); console.log(`Fixed the image links for note ${noteId} to the offline saved.`);
} }
}, 5000); }, 5000);
@ -376,11 +378,11 @@ async function saveLinks(note, content) {
const foundLinks = []; const foundLinks = [];
if (note.type === 'text') { if (note.type === 'text') {
content = await downloadImages(note.noteId, content);
content = findImageLinks(content, foundLinks); content = findImageLinks(content, foundLinks);
content = findInternalLinks(content, foundLinks); content = findInternalLinks(content, foundLinks);
content = findIncludeNoteLinks(content, foundLinks); content = findIncludeNoteLinks(content, foundLinks);
content = await downloadImages(note.noteId, content);
} }
else if (note.type === 'relation-map') { else if (note.type === 'relation-map') {
findRelationMapLinks(content, foundLinks); findRelationMapLinks(content, foundLinks);

View File

@ -83,8 +83,8 @@ const defaultOptions = [
{ name: 'rightPaneVisible', value: 'true', isSynced: false }, { name: 'rightPaneVisible', value: 'true', isSynced: false },
{ name: 'nativeTitleBarVisible', value: 'false', isSynced: false }, { name: 'nativeTitleBarVisible', value: 'false', isSynced: false },
{ name: 'eraseNotesAfterTimeInSeconds', value: '604800', isSynced: true }, // default is 7 days { name: 'eraseNotesAfterTimeInSeconds', value: '604800', isSynced: true }, // default is 7 days
{ name: 'hideArchivedNotes_main', value: 'false', isSynced: false }, // default is 7 days { name: 'hideArchivedNotes_main', value: 'false', isSynced: false },
{ name: 'hideIncludedImages_main', value: 'true', isSynced: false } // default is 7 days { name: 'hideIncludedImages_main', value: 'true', isSynced: false }
]; ];
async function initStartupOptions() { async function initStartupOptions() {

View File

@ -60,20 +60,6 @@ module.exports = function (searchText) {
operator: '*=*', operator: '*=*',
value: searchText value: searchText
}); });
filters.push({
relation: 'or',
name: 'attributeName',
operator: '*=*',
value: searchText
});
filters.push({
relation: 'or',
name: 'attributeValue',
operator: '*=*',
value: searchText
});
} }
else { else {
const tokens = searchText.split(/\s+/); const tokens = searchText.split(/\s+/);
@ -81,27 +67,9 @@ module.exports = function (searchText) {
for (const token of tokens) { for (const token of tokens) {
filters.push({ filters.push({
relation: 'and', relation: 'and',
name: 'sub',
children: [
{
relation: 'or',
name: 'text', name: 'text',
operator: '*=*', operator: '*=*',
value: token value: token
},
{
relation: 'or',
name: 'attributeName',
operator: '*=*',
value: token
},
{
relation: 'or',
name: 'attributeValue',
operator: '*=*',
value: token
}
]
}); });
} }
} }

View File

@ -163,6 +163,10 @@ async function executeScript(query) {
} }
async function wrap(func, query) { async function wrap(func, query) {
if (!dbConnection) {
throw new Error("DB connection not initialized yet");
}
const thisError = new Error(); const thisError = new Error();
try { try {

View File

@ -13,16 +13,11 @@ const port = require('./port');
const Option = require('../entities/option'); const Option = require('../entities/option');
const TaskContext = require('./task_context.js'); const TaskContext = require('./task_context.js');
async function createConnection() { const dbConnection = new Promise(async (resolve, reject) => {
return await sqlite.open({ const db = await sqlite.open({
filename: dataDir.DOCUMENT_PATH, filename: dataDir.DOCUMENT_PATH,
driver: sqlite3.Database driver: sqlite3.Database
}); });
}
const dbConnection = new Promise(async (resolve, reject) => {
// no need to create new connection now since DB stays the same all the time
const db = await createConnection();
db.run('PRAGMA journal_mode = WAL;'); db.run('PRAGMA journal_mode = WAL;');