mirror of
https://github.com/zadam/trilium.git
synced 2025-01-16 20:21:43 +08:00
continuing refactoring
This commit is contained in:
parent
f98a20928c
commit
7963de0abc
12 changed files with 138 additions and 118 deletions
|
@ -20,8 +20,10 @@ import WhatLinksHereWidget from "../widgets/what_links_here.js";
|
|||
import AttributesWidget from "../widgets/attributes.js";
|
||||
import TitleBarButtonsWidget from "../widgets/title_bar_buttons.js";
|
||||
import GlobalMenuWidget from "../widgets/global_menu.js";
|
||||
import HorizontalFlexContainer from "../widgets/horizontal_flex_container.js";
|
||||
import RowFlexContainer from "../widgets/row_flex_container.js";
|
||||
import StandardTopWidget from "../widgets/standard_top_widget.js";
|
||||
import treeCache from "./tree_cache.js";
|
||||
import treeUtils from "./tree_utils.js";
|
||||
|
||||
class AppContext {
|
||||
constructor() {
|
||||
|
@ -38,7 +40,7 @@ class AppContext {
|
|||
this.tabRow = new TabRowWidget(this);
|
||||
|
||||
const topPaneWidgets = [
|
||||
new HorizontalFlexContainer(this, [
|
||||
new RowFlexContainer(this, [
|
||||
new GlobalMenuWidget(this),
|
||||
this.tabRow,
|
||||
new TitleBarButtonsWidget(this)
|
||||
|
@ -105,6 +107,10 @@ class AppContext {
|
|||
trigger(name, data) {
|
||||
this.eventReceived(name, data);
|
||||
|
||||
for (const tabContext of this.tabContexts) {
|
||||
tabContext.eventReceived(name, data);
|
||||
}
|
||||
|
||||
for (const widget of this.widgets) {
|
||||
widget.eventReceived(name, data);
|
||||
}
|
||||
|
@ -118,6 +124,36 @@ class AppContext {
|
|||
}
|
||||
}
|
||||
|
||||
activateNote(notePath) {
|
||||
const activeTabContext = this.getActiveTabContext();
|
||||
|
||||
activeTabContext.setNote(notePath);
|
||||
|
||||
this._setTitleBar();
|
||||
this._setCurrentNotePathToHash();
|
||||
}
|
||||
|
||||
_setCurrentNotePathToHash() {
|
||||
const activeTabContext = this.getActiveTabContext();
|
||||
|
||||
if (activeTabContext && activeTabContext.notePath) {
|
||||
document.location.hash = (activeTabContext.notePath || "") + "-" + activeTabContext.tabId;
|
||||
}
|
||||
}
|
||||
|
||||
async _setTitleBar() {
|
||||
document.title = "Trilium Notes";
|
||||
|
||||
const activeTabContext = this.getActiveTabContext();
|
||||
|
||||
if (activeTabContext && activeTabContext.notePath) {
|
||||
const note = await treeCache.getNote(treeUtils.getNoteIdFromNotePath(activeTabContext.notePath));
|
||||
|
||||
// it helps navigating in history if note title is included in the title
|
||||
document.title += " - " + note.title;
|
||||
}
|
||||
}
|
||||
|
||||
/** @return {TabContext[]} */
|
||||
getTabContexts() {
|
||||
return this.tabContexts;
|
||||
|
@ -219,7 +255,7 @@ class AppContext {
|
|||
getTab(newTab, state) {
|
||||
if (!this.getActiveTabContext() || newTab) {
|
||||
// if it's a new tab explicitly by user then it's in background
|
||||
const ctx = new TabContext(this.tabRow, state);
|
||||
const ctx = new TabContext(this, this.tabRow, state);
|
||||
this.tabContexts.push(ctx);
|
||||
|
||||
return ctx;
|
||||
|
@ -249,7 +285,7 @@ class AppContext {
|
|||
}
|
||||
|
||||
async openEmptyTab() {
|
||||
const ctx = new TabContext(this.tabRow);
|
||||
const ctx = new TabContext(this, this.tabRow);
|
||||
this.tabContexts.push(ctx);
|
||||
|
||||
await this.tabRow.activateTab(ctx.$tab[0]);
|
||||
|
|
|
@ -2,13 +2,17 @@ import server from "./server.js";
|
|||
import ws from "./ws.js";
|
||||
import treeUtils from "./tree_utils.js";
|
||||
import noteAutocompleteService from "./note_autocomplete.js";
|
||||
import Component from "../widgets/component.js";
|
||||
import utils from "./utils.js";
|
||||
|
||||
class Attributes {
|
||||
class Attributes extends Component {
|
||||
/**
|
||||
* @param {TabContext} ctx
|
||||
* @param {AppContext} appContext
|
||||
* @param {TabContext} tabContext
|
||||
*/
|
||||
constructor(ctx) {
|
||||
this.ctx = ctx;
|
||||
constructor(appContext, tabContext) {
|
||||
super(appContext);
|
||||
this.tabContext = tabContext;
|
||||
this.attributePromise = null;
|
||||
}
|
||||
|
||||
|
@ -17,7 +21,7 @@ class Attributes {
|
|||
}
|
||||
|
||||
reloadAttributes() {
|
||||
this.attributePromise = server.get(`notes/${this.ctx.note.noteId}/attributes`);
|
||||
this.attributePromise = server.get(`notes/${this.tabContext.note.noteId}/attributes`);
|
||||
}
|
||||
|
||||
async refreshAttributes() {
|
||||
|
@ -32,15 +36,18 @@ class Attributes {
|
|||
return this.attributePromise;
|
||||
}
|
||||
|
||||
eventReceived(name, data) {
|
||||
if (!this.ctx.note) {
|
||||
return;
|
||||
syncDataListener({data}) {
|
||||
if (this.tabContext.note && data.find(sd => sd.entityName === 'attributes' && sd.noteId === this.tabContext.note.noteId)) {
|
||||
this.reloadAttributes();
|
||||
}
|
||||
}
|
||||
|
||||
if (name === 'syncData') {
|
||||
if (data.find(sd => sd.entityName === 'attributes' && sd.noteId === this.ctx.note.noteId)) {
|
||||
this.reloadAttributes();
|
||||
}
|
||||
activeNoteChangedListener() {
|
||||
if (utils.isDesktop()) {
|
||||
this.attributes.refreshAttributes();
|
||||
} else {
|
||||
// mobile usually doesn't need attributes so we just invalidate
|
||||
this.attributes.invalidateAttributes();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,7 @@ async function reload() {
|
|||
}
|
||||
|
||||
async function reloadNote(tabContext) {
|
||||
const note = await loadNote(tabContext.note.noteId);
|
||||
|
||||
await loadNoteDetailToContext(tabContext, note, tabContext.notePath);
|
||||
await loadNoteDetailToContext(tabContext, tabContext.notePath);
|
||||
}
|
||||
|
||||
async function openInTab(notePath, activate) {
|
||||
|
@ -79,11 +77,10 @@ async function activateOrOpenNote(noteId) {
|
|||
|
||||
/**
|
||||
* @param {TabContext} ctx
|
||||
* @param {NoteFull} note
|
||||
* @param {string} notePath
|
||||
*/
|
||||
async function loadNoteDetailToContext(ctx, note, notePath) {
|
||||
await ctx.setNote(note, notePath);
|
||||
async function loadNoteDetailToContext(ctx, notePath) {
|
||||
await ctx.setNote(notePath);
|
||||
|
||||
appContext.openTabsChanged();
|
||||
|
||||
|
@ -104,7 +101,6 @@ async function loadNoteDetail(origNotePath, options = {}) {
|
|||
}
|
||||
|
||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||
const loadedNote = await loadNote(noteId);
|
||||
const ctx = appContext.getTab(newTab, options.state);
|
||||
|
||||
// we will try to render the new note only if it's still the active one in the tree
|
||||
|
@ -112,11 +108,11 @@ async function loadNoteDetail(origNotePath, options = {}) {
|
|||
// try to render all those loaded notes one after each other. This only guarantees that correct note
|
||||
// will be displayed independent of timing
|
||||
const currentTreeNode = appContext.getMainNoteTree().getActiveNode();
|
||||
if (!newTab && currentTreeNode && currentTreeNode.data.noteId !== loadedNote.noteId) {
|
||||
if (!newTab && currentTreeNode && currentTreeNode.data.noteId !== noteId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const loadPromise = loadNoteDetailToContext(ctx, loadedNote, notePath).then(() => {
|
||||
const loadPromise = loadNoteDetailToContext(ctx, notePath).then(() => {
|
||||
if (activate) {
|
||||
return appContext.activateTab(ctx);
|
||||
}
|
||||
|
@ -201,9 +197,7 @@ ws.subscribeToOutsideSyncMessages(syncData => {
|
|||
});
|
||||
|
||||
ws.subscribeToAllSyncMessages(syncData => {
|
||||
for (const tc of appContext.getTabContexts()) {
|
||||
tc.eventReceived('syncData', syncData);
|
||||
}
|
||||
appContext.trigger('syncData', {data: syncData});
|
||||
});
|
||||
|
||||
$tabContentsContainer.on("dragover", e => e.preventDefault());
|
||||
|
|
|
@ -43,7 +43,7 @@ async function mouseEnterHandler() {
|
|||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||
|
||||
const notePromise = noteDetailService.loadNote(noteId);
|
||||
const attributePromise = server.get('notes/' + noteId + '/attributes');
|
||||
const attributePromise = server.get(`notes/${noteId}/attributes`);
|
||||
|
||||
const [note, attributes] = await Promise.all([notePromise, attributePromise]);
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@ import Attributes from "./attributes.js";
|
|||
import utils from "./utils.js";
|
||||
import optionsService from "./options.js";
|
||||
import appContext from "./app_context.js";
|
||||
import treeUtils from "./tree_utils.js";
|
||||
import noteDetailService from "./note_detail.js";
|
||||
import Component from "../widgets/component.js";
|
||||
|
||||
let showSidebarInNewTab = true;
|
||||
|
||||
|
@ -13,12 +16,15 @@ optionsService.addLoadListener(options => {
|
|||
showSidebarInNewTab = options.is('showSidebarInNewTab');
|
||||
});
|
||||
|
||||
class TabContext {
|
||||
class TabContext extends Component {
|
||||
/**
|
||||
* @param {AppContext} appContext
|
||||
* @param {TabRowWidget} tabRow
|
||||
* @param {object} state
|
||||
*/
|
||||
constructor(tabRow, state = {}) {
|
||||
constructor(appContext, tabRow, state = {}) {
|
||||
super(appContext);
|
||||
|
||||
this.tabRow = tabRow;
|
||||
this.tabId = state.tabId || utils.randomString(4);
|
||||
this.$tab = $(this.tabRow.addTab(this.tabId));
|
||||
|
@ -37,40 +43,26 @@ class TabContext {
|
|||
|
||||
this.noteChangeDisabled = false;
|
||||
this.isNoteChanged = false;
|
||||
this.attributes = new Attributes(this);
|
||||
this.attributes = new Attributes(this.appContext, this);
|
||||
|
||||
this.children.push(this.attributes);
|
||||
}
|
||||
|
||||
async setNote(note, notePath) {
|
||||
/** @property {NoteFull} */
|
||||
this.note = note;
|
||||
async setNote(notePath) {
|
||||
this.notePath = notePath;
|
||||
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
||||
|
||||
/** @property {NoteFull} */
|
||||
this.note = await noteDetailService.loadNote(noteId);
|
||||
|
||||
this.tabRow.updateTab(this.$tab[0], {title: this.note.title});
|
||||
|
||||
if (!this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (utils.isDesktop()) {
|
||||
this.attributes.refreshAttributes();
|
||||
} else {
|
||||
// mobile usually doesn't need attributes so we just invalidate
|
||||
this.attributes.invalidateAttributes();
|
||||
}
|
||||
|
||||
this.setupClasses();
|
||||
|
||||
this.setCurrentNotePathToHash();
|
||||
|
||||
this.noteChangeDisabled = true;
|
||||
|
||||
try {
|
||||
|
||||
} finally {
|
||||
this.noteChangeDisabled = false;
|
||||
}
|
||||
|
||||
this.setTitleBar();
|
||||
|
||||
this.cleanup(); // esp. on windows autocomplete is not getting closed automatically
|
||||
|
||||
setTimeout(async () => {
|
||||
|
@ -96,8 +88,8 @@ class TabContext {
|
|||
if (!this.initialized) {
|
||||
await this.initTabContent();
|
||||
|
||||
if (this.note) {
|
||||
await this.setNote(this.note, this.notePath);
|
||||
if (this.notePath) {
|
||||
await this.setNote(this.notePath);
|
||||
}
|
||||
else {
|
||||
// FIXME
|
||||
|
@ -106,38 +98,18 @@ class TabContext {
|
|||
}
|
||||
|
||||
this.setCurrentNotePathToHash();
|
||||
this.setTitleBar();
|
||||
}
|
||||
|
||||
async renderComponent(disableAutoBook = false) {
|
||||
// FIXME
|
||||
}
|
||||
|
||||
setTitleBar() {
|
||||
if (!this.$tabContent.is(":visible")) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.title = "Trilium Notes";
|
||||
|
||||
if (this.note) {
|
||||
// it helps navigating in history if note title is included in the title
|
||||
document.title += " - " + this.note.title;
|
||||
}
|
||||
}
|
||||
|
||||
hide() {
|
||||
if (this.initialized) {
|
||||
this.$tabContent.hide();
|
||||
}
|
||||
}
|
||||
|
||||
setCurrentNotePathToHash() {
|
||||
if (this.isActive()) {
|
||||
document.location.hash = (this.notePath || "") + "-" + this.tabId;
|
||||
}
|
||||
}
|
||||
|
||||
isActive() {
|
||||
return this.$tab[0] === this.tabRow.activeTabEl;
|
||||
}
|
||||
|
@ -149,21 +121,9 @@ class TabContext {
|
|||
}
|
||||
}
|
||||
|
||||
for (const clazz of Array.from(this.$tabContent[0].classList)) { // create copy to safely iterate over while removing classes
|
||||
if (clazz !== 'note-tab-content') {
|
||||
this.$tabContent.removeClass(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
this.$tab.addClass(this.note.cssClass);
|
||||
this.$tab.addClass(utils.getNoteTypeClass(this.note.type));
|
||||
this.$tab.addClass(utils.getMimeTypeClass(this.note.mime));
|
||||
|
||||
this.$tabContent.addClass(this.note.cssClass);
|
||||
this.$tabContent.addClass(utils.getNoteTypeClass(this.note.type));
|
||||
this.$tabContent.addClass(utils.getMimeTypeClass(this.note.mime));
|
||||
|
||||
this.$tabContent.toggleClass("protected", this.note.isProtected);
|
||||
}
|
||||
|
||||
getComponent() {
|
||||
|
@ -247,14 +207,6 @@ class TabContext {
|
|||
}
|
||||
}
|
||||
|
||||
eventReceived(name, data) {
|
||||
if (!this.initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.attributes.eventReceived(name, data);
|
||||
}
|
||||
|
||||
getTabState() {
|
||||
if (!this.notePath) {
|
||||
return null;
|
||||
|
|
|
@ -172,6 +172,10 @@ class TreeCache {
|
|||
return (await this.getNotes([noteId], silentNotFoundError))[0];
|
||||
}
|
||||
|
||||
getNoteFromCache(noteId) {
|
||||
return this.notes[noteId];
|
||||
}
|
||||
|
||||
getBranches(branchIds) {
|
||||
return branchIds
|
||||
.map(branchId => this.getBranch(branchId))
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
class BasicWidget {
|
||||
/**
|
||||
* @param {AppContext} appContext
|
||||
*/
|
||||
import Component from "./component.js";
|
||||
|
||||
class BasicWidget extends Component {
|
||||
constructor(appContext) {
|
||||
this.appContext = appContext;
|
||||
super(appContext);
|
||||
this.widgetId = `widget-${this.constructor.name}`;
|
||||
}
|
||||
|
||||
|
@ -25,20 +24,6 @@ class BasicWidget {
|
|||
*/
|
||||
doRender() {}
|
||||
|
||||
eventReceived(name, data) {
|
||||
console.log("received", name, "to", this.widgetId);
|
||||
|
||||
const fun = this[name + 'Listener'];
|
||||
|
||||
if (typeof fun === 'function') {
|
||||
fun.call(this, data);
|
||||
}
|
||||
}
|
||||
|
||||
trigger(name, data) {
|
||||
this.appContext.trigger(name, data);
|
||||
}
|
||||
|
||||
toggle(show) {
|
||||
this.$widget.toggle(show);
|
||||
}
|
||||
|
|
24
src/public/javascripts/widgets/component.js
Normal file
24
src/public/javascripts/widgets/component.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
export default class Component {
|
||||
/** @param {AppContext} appContext */
|
||||
constructor(appContext) {
|
||||
this.appContext = appContext;
|
||||
/** @type Component[] */
|
||||
this.children = [];
|
||||
}
|
||||
|
||||
eventReceived(name, data) {
|
||||
const fun = this[name + 'Listener'];
|
||||
|
||||
if (typeof fun === 'function') {
|
||||
fun.call(this, data);
|
||||
}
|
||||
|
||||
for (const child of this.children) {
|
||||
child.eventReceived(name, data);
|
||||
}
|
||||
}
|
||||
|
||||
trigger(name, data) {
|
||||
this.appContext.trigger(name, data);
|
||||
}
|
||||
}
|
|
@ -58,6 +58,24 @@ export default class NoteDetailWidget extends TabAwareWidget {
|
|||
|
||||
this.getComponent().show();
|
||||
await this.getComponent().render();
|
||||
|
||||
this.setupClasses();
|
||||
}
|
||||
|
||||
setupClasses() {
|
||||
const note = this.tabContext.note;
|
||||
|
||||
for (const clazz of Array.from(this.$widget[0].classList)) { // create copy to safely iterate over while removing classes
|
||||
if (clazz !== 'note-detail') {
|
||||
this.$widget.removeClass(clazz);
|
||||
}
|
||||
}
|
||||
|
||||
this.$widget.addClass(note.cssClass);
|
||||
this.$widget.addClass(utils.getNoteTypeClass(note.type));
|
||||
this.$widget.addClass(utils.getMimeTypeClass(note.mime));
|
||||
|
||||
this.$widget.toggleClass("protected", note.isProtected);
|
||||
}
|
||||
|
||||
getComponent() {
|
||||
|
|
|
@ -101,7 +101,7 @@ export default class NoteTreeWidget extends BasicWidget {
|
|||
|
||||
const notePath = await treeUtils.getNotePath(data.node);
|
||||
|
||||
noteDetailService.switchToNote(notePath);
|
||||
this.appContext.activateNote(notePath);
|
||||
},
|
||||
expand: (event, data) => treeService.setExpandedToServer(data.node.data.branchId, true),
|
||||
collapse: (event, data) => treeService.setExpandedToServer(data.node.data.branchId, false),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import BasicWidget from "./basic_widget.js";
|
||||
|
||||
export default class HorizontalFlexContainer extends BasicWidget {
|
||||
export default class RowFlexContainer extends BasicWidget {
|
||||
constructor(appContext, widgets) {
|
||||
super(appContext);
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
<div id="toast-container" class="d-flex flex-column justify-content-center align-items-center"></div>
|
||||
|
||||
<div id="container" style="display: none;">
|
||||
<div id="top-pane" class="hide-in-zen-mode"></div>
|
||||
<div id="top-pane"></div>
|
||||
|
||||
<div style="display: flex; flex-grow: 1; flex-shrink: 1; min-height: 0;">
|
||||
<div id="left-pane" class="hide-in-zen-mode"></div>
|
||||
|
|
Loading…
Reference in a new issue