Compare commits

...

8 Commits

12 changed files with 24407 additions and 12814 deletions

2
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{ {
"name": "trilium", "name": "trilium",
"version": "0.26.1", "version": "0.27.0-beta",
"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.27.0-beta", "version": "0.27.1-beta",
"license": "AGPL-3.0-only", "license": "AGPL-3.0-only",
"main": "electron.js", "main": "electron.js",
"bin": { "bin": {

View File

@@ -85,8 +85,11 @@ async function showAttributes() {
$promotedAttributesContainer.empty().append($tbody); $promotedAttributesContainer.empty().append($tbody);
} }
else if (note.type !== 'relation-map') { else if (note.type !== 'relation-map') {
if (attributes.length > 0) { // display only "own" notes
for (const attribute of attributes) { const ownedAttributes = attributes.filter(attr => attr.noteId === note.noteId);
if (ownedAttributes.length > 0) {
for (const attribute of ownedAttributes) {
if (attribute.type === 'label') { if (attribute.type === 'label') {
$attributeListInner.append(utils.formatLabel(attribute) + " "); $attributeListInner.append(utils.formatLabel(attribute) + " ");
} }
@@ -132,7 +135,9 @@ async function createPromotedAttributeRow(definitionAttr, valueAttr) {
const $inputCell = $("<td>").append($("<div>").addClass("input-group").append($input)); const $inputCell = $("<td>").append($("<div>").addClass("input-group").append($input));
const $actionCell = $("<td>"); const $actionCell = $("<td>");
const $multiplicityCell = $("<td>").addClass("multiplicity"); const $multiplicityCell = $("<td>")
.addClass("multiplicity")
.attr("nowrap", true);
$tr $tr
.append($labelCell) .append($labelCell)

View File

@@ -7,6 +7,7 @@ import treeCache from './tree_cache.js';
import noteDetailService from './note_detail.js'; import noteDetailService from './note_detail.js';
import noteTypeService from './note_type.js'; import noteTypeService from './note_type.js';
import noteTooltipService from './note_tooltip.js'; import noteTooltipService from './note_tooltip.js';
import protectedSessionService from'./protected_session.js';
/** /**
* This is the main frontend API interface for scripts. It's published in the local "api" object. * This is the main frontend API interface for scripts. It's published in the local "api" object.
@@ -42,7 +43,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
this.activateNewNote = async notePath => { this.activateNewNote = async notePath => {
await treeService.reload(); await treeService.reload();
await treeService.activateNote(notePath, true); await treeService.activateNote(notePath, noteDetailService.focusOnTitle);
}; };
/** /**
@@ -244,7 +245,12 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
* @method * @method
* @param {object} $el - jquery object on which to setup the tooltip * @param {object} $el - jquery object on which to setup the tooltip
*/ */
this.setupElementTooltip = noteTooltipService.setupElementTooltip this.setupElementTooltip = noteTooltipService.setupElementTooltip;
/**
* @method
*/
this.protectCurrentNote = protectedSessionService.protectNoteAndSendToServer;
} }
export default FrontendScriptApi; export default FrontendScriptApi;

View File

@@ -37,6 +37,8 @@ let noteChangeDisabled = false;
let isNoteChanged = false; let isNoteChanged = false;
let detailLoadedListeners = [];
const components = { const components = {
'code': noteDetailCode, 'code': noteDetailCode,
'text': noteDetailText, 'text': noteDetailText,
@@ -147,12 +149,6 @@ function setNoteBackgroundIfProtected(note) {
$unprotectButton.prop("disabled", !protectedSessionHolder.isProtectedSessionAvailable()); $unprotectButton.prop("disabled", !protectedSessionHolder.isProtectedSessionAvailable());
} }
let isNewNoteCreated = false;
function newNoteCreated() {
isNewNoteCreated = true;
}
async function handleProtectedSession() { async function handleProtectedSession() {
const newSessionCreated = await protectedSessionService.ensureProtectedSession(currentNote.isProtected, false); const newSessionCreated = await protectedSessionService.ensureProtectedSession(currentNote.isProtected, false);
@@ -191,12 +187,6 @@ async function loadNoteDetail(noteId) {
attributeService.invalidateAttributes(); attributeService.invalidateAttributes();
} }
if (isNewNoteCreated) {
isNewNoteCreated = false;
$noteTitle.focus().select();
}
$noteIdDisplay.html(noteId); $noteIdDisplay.html(noteId);
setNoteBackgroundIfProtected(currentNote); setNoteBackgroundIfProtected(currentNote);
@@ -240,11 +230,13 @@ async function loadNoteDetail(noteId) {
// after loading new note make sure editor is scrolled to the top // after loading new note make sure editor is scrolled to the top
getComponent(currentNote.type).scrollToTop(); getComponent(currentNote.type).scrollToTop();
fireDetailLoaded();
$scriptArea.empty();
await bundleService.executeRelationBundles(getCurrentNote(), 'runOnNoteView');
if (utils.isDesktop()) { if (utils.isDesktop()) {
$scriptArea.empty();
await bundleService.executeRelationBundles(getCurrentNote(), 'runOnNoteView');
await attributeService.showAttributes(); await attributeService.showAttributes();
await showChildrenOverview(); await showChildrenOverview();
@@ -291,6 +283,30 @@ function focusOnTitle() {
$noteTitle.focus(); $noteTitle.focus();
} }
/**
* Since detail loading may take some time and user might just browse through the notes using UP-DOWN keys,
* we intentionally decouple activation of the note in the tree and full load of the note so just avaiting on
* fancytree's activate() won't wait for the full load.
*
* This causes an issue where in some cases you want to do some action after detail is loaded. For this reason
* we provide the listeners here which will be triggered after the detail is loaded and if the loaded note
* is the one registered in the listener.
*/
function addDetailLoadedListener(noteId, callback) {
detailLoadedListeners.push({ noteId, callback });
}
function fireDetailLoaded() {
for (const {noteId, callback} of detailLoadedListeners) {
if (noteId === currentNote.noteId) {
callback();
}
}
// all the listeners are one time only
detailLoadedListeners = [];
}
messagingService.subscribeToSyncMessages(syncData => { messagingService.subscribeToSyncMessages(syncData => {
if (syncData.some(sync => sync.entityName === 'notes' && sync.entityId === getCurrentNoteId())) { if (syncData.some(sync => sync.entityName === 'notes' && sync.entityId === getCurrentNoteId())) {
infoService.showMessage('Reloading note because of background changes'); infoService.showMessage('Reloading note because of background changes');
@@ -325,11 +341,11 @@ export default {
getCurrentNote, getCurrentNote,
getCurrentNoteType, getCurrentNoteType,
getCurrentNoteId, getCurrentNoteId,
newNoteCreated,
focusOnTitle, focusOnTitle,
saveNote, saveNote,
saveNoteIfChanged, saveNoteIfChanged,
noteChanged, noteChanged,
getCurrentNoteContent, getCurrentNoteContent,
onNoteChange onNoteChange,
addDetailLoadedListener
}; };

View File

@@ -184,5 +184,6 @@ export default {
protectSubtree, protectSubtree,
ensureDialogIsClosed, ensureDialogIsClosed,
enterProtectedSession, enterProtectedSession,
leaveProtectedSession leaveProtectedSession,
protectNoteAndSendToServer
}; };

View File

@@ -83,6 +83,10 @@ async function setNodeTitleWithPrefix(node) {
node.setTitle(utils.escapeHtml(title)); node.setTitle(utils.escapeHtml(title));
} }
function getNode(childNoteId, parentNoteId) {
return getNodesByNoteId(childNoteId).find(node => !parentNoteId || node.data.parentNoteId === parentNoteId);
}
async function expandToNote(notePath, expandOpts) { async function expandToNote(notePath, expandOpts) {
utils.assertArguments(notePath); utils.assertArguments(notePath);
@@ -94,7 +98,18 @@ async function expandToNote(notePath, expandOpts) {
for (const childNoteId of runPath) { for (const childNoteId of runPath) {
// for first node (!parentNoteId) it doesn't matter which node is found // for first node (!parentNoteId) it doesn't matter which node is found
const node = getNodesByNoteId(childNoteId).find(node => !parentNoteId || node.data.parentNoteId === parentNoteId); let node = getNode(childNoteId, parentNoteId);
if (!node && parentNoteId) {
const parents = getNodesByNoteId(parentNoteId);
for (const parent of parents) {
// force load parents. This is useful when fancytree doesn't contain recently created notes yet.
await parent.load(true);
}
node = getNode(childNoteId, parentNoteId);
}
if (!node) { if (!node) {
console.error(`Can't find node for noteId=${childNoteId} with parentNoteId=${parentNoteId}`); console.error(`Can't find node for noteId=${childNoteId} with parentNoteId=${parentNoteId}`);
@@ -111,7 +126,7 @@ async function expandToNote(notePath, expandOpts) {
} }
} }
async function activateNote(notePath, newNote) { async function activateNote(notePath, noteLoadedListener) {
utils.assertArguments(notePath); utils.assertArguments(notePath);
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId(); const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
@@ -131,8 +146,8 @@ async function activateNote(notePath, newNote) {
const node = await expandToNote(notePath); const node = await expandToNote(notePath);
if (newNote) { if (noteLoadedListener) {
noteDetailService.newNoteCreated(); noteDetailService.addDetailLoadedListener(node.data.noteId, noteLoadedListener);
} }
// we use noFocus because when we reload the tree because of background changes // we use noFocus because when we reload the tree because of background changes
@@ -547,7 +562,7 @@ async function createNote(node, parentNoteId, target, isProtected, saveSelection
await noteDetailService.saveNoteIfChanged(); await noteDetailService.saveNoteIfChanged();
noteDetailService.newNoteCreated(); noteDetailService.addDetailLoadedListener(note.noteId, noteDetailService.focusOnTitle);
const noteEntity = new NoteShort(treeCache, note); const noteEntity = new NoteShort(treeCache, note);
const branchEntity = new Branch(treeCache, branch); const branchEntity = new Branch(treeCache, branch);

File diff suppressed because one or more lines are too long

View File

@@ -53,6 +53,7 @@
padding: 10px 0 10px 0; padding: 10px 0 10px 0;
margin: 0 10px 0 16px; margin: 0 10px 0 16px;
border: 1px solid #ccc; border: 1px solid #ccc;
border-radius: 5px;
} }
#context-menu-container { #context-menu-container {

View File

@@ -70,6 +70,7 @@ body {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 200px;
} }
.note-detail-component { .note-detail-component {
@@ -311,6 +312,12 @@ div.ui-tooltip {
.cm-matchhighlight {background-color: #eeeeee} .cm-matchhighlight {background-color: #eeeeee}
#attribute-list {
overflow: auto;
/* limiting the size since actual note content is more important */
max-height: 30%;
}
#label-list, #relation-list, #attribute-list { #label-list, #relation-list, #attribute-list {
color: #777777; color: #777777;
padding: 5px; padding: 5px;
@@ -434,8 +441,13 @@ html.theme-dark body {
} }
#note-detail-promoted-attributes { #note-detail-promoted-attributes {
max-width: 70%;
margin: auto; margin: auto;
/* setting the display to block since "table" doesn't support scrolling */
display: block;
flex-basis: content;
flex-shrink: 1;
flex-grow: 100;
overflow: auto;
} }
#note-detail-promoted-attributes td, #note-detail-promoted-attributes th { #note-detail-promoted-attributes td, #note-detail-promoted-attributes th {

View File

@@ -1 +1 @@
module.exports = { buildDate:"2018-12-30T22:38:11+01:00", buildRevision: "32220476aa6795bab036b7dd9057ea3357d7dd51" }; module.exports = { buildDate:"2019-01-01T20:54:23+01:00", buildRevision: "1771ddb78783352970ef64906af8c8fe117183d0" };

View File

@@ -153,7 +153,8 @@ async function createNote(parentNoteId, title, content = "", extraOptions = {})
noteId: note.noteId, noteId: note.noteId,
type: attr.type, type: attr.type,
name: attr.name, name: attr.name,
value: attr.value value: attr.value,
isInheritable: !!attr.isInheritable
}); });
} }
@@ -357,10 +358,12 @@ async function deleteNote(branch) {
const notDeletedBranches = await note.getBranches(); const notDeletedBranches = await note.getBranches();
if (notDeletedBranches.length === 0) { if (notDeletedBranches.length === 0) {
note.isDeleted = true; // maybe a bit counter-intuitively, protected notes can be deleted also outside of protected session
// we don't reset content here, that's postponed and done later to give the user // this is because protected notes offer only confidentiality which makes some things simpler - e.g. deletion UI
// a chance to correct a mistake // to allow this, we just set the isDeleted flag, otherwise saving would fail because of attempt to encrypt
await note.save(); // content with non-existent protected session key
// we don't reset content here, that's postponed and done later to give the user a chance to correct a mistake
await sql.execute("UPDATE notes SET isDeleted = 1 WHERE noteId = ?", [note.noteId]);
for (const noteRevision of await note.getRevisions()) { for (const noteRevision of await note.getRevisions()) {
await noteRevision.save(); await noteRevision.save();