2018-03-25 13:41:29 -04:00
|
|
|
import treeService from './tree.js';
|
2019-05-10 21:43:40 +02:00
|
|
|
import TabContext from './tab_context.js';
|
2018-03-25 11:09:17 -04:00
|
|
|
import server from './server.js';
|
2019-08-26 20:21:43 +02:00
|
|
|
import ws from "./ws.js";
|
2018-03-25 23:25:17 -04:00
|
|
|
import treeCache from "./tree_cache.js";
|
|
|
|
|
import NoteFull from "../entities/note_full.js";
|
2019-05-08 20:14:41 +02:00
|
|
|
import treeUtils from "./tree_utils.js";
|
2020-01-14 21:52:18 +01:00
|
|
|
import tabRow from "../widgets/tab_row.js";
|
2020-01-12 11:15:23 +01:00
|
|
|
import appContext from "./app_context.js";
|
2019-05-05 10:59:34 +02:00
|
|
|
|
2019-01-01 19:32:34 +01:00
|
|
|
let detailLoadedListeners = [];
|
|
|
|
|
|
2018-03-25 11:09:17 -04:00
|
|
|
async function reload() {
|
|
|
|
|
// no saving here
|
2017-11-04 17:54:27 -04:00
|
|
|
|
2020-01-12 12:30:30 +01:00
|
|
|
await loadNoteDetail(appContext.getActiveTabNotePath());
|
2018-03-25 11:09:17 -04:00
|
|
|
}
|
2017-11-04 17:54:27 -04:00
|
|
|
|
2020-01-12 12:30:30 +01:00
|
|
|
async function reloadNote(tabContext) {
|
2020-01-15 21:36:01 +01:00
|
|
|
await loadNoteDetailToContext(tabContext, tabContext.notePath);
|
2019-05-20 22:25:04 +02:00
|
|
|
}
|
|
|
|
|
|
2019-11-24 21:40:50 +01:00
|
|
|
async function openInTab(notePath, activate) {
|
|
|
|
|
await loadNoteDetail(notePath, { newTab: true, activate });
|
2019-05-02 22:24:43 +02:00
|
|
|
}
|
2019-05-01 23:06:18 +02:00
|
|
|
|
2019-05-08 20:14:41 +02:00
|
|
|
async function switchToNote(notePath) {
|
|
|
|
|
await loadNoteDetail(notePath);
|
2019-05-11 19:27:33 +02:00
|
|
|
|
2020-01-12 12:48:17 +01:00
|
|
|
appContext.openTabsChanged();
|
2018-03-25 11:09:17 -04:00
|
|
|
}
|
2017-11-04 17:54:27 -04:00
|
|
|
|
2019-05-19 09:13:13 +02:00
|
|
|
function getActiveEditor() {
|
2020-01-12 12:37:44 +01:00
|
|
|
const activeTabContext = appContext.getActiveTabContext();
|
2019-05-19 09:13:13 +02:00
|
|
|
|
|
|
|
|
if (activeTabContext && activeTabContext.note && activeTabContext.note.type === 'text') {
|
|
|
|
|
return activeTabContext.getComponent().getEditor();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-07 11:15:55 +02:00
|
|
|
async function activateOrOpenNote(noteId) {
|
2020-01-12 12:37:44 +01:00
|
|
|
for (const tabContext of appContext.getTabContexts()) {
|
2019-07-07 11:15:55 +02:00
|
|
|
if (tabContext.note && tabContext.note.noteId === noteId) {
|
|
|
|
|
await tabContext.activate();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// if no tab with this note has been found we'll create new tab
|
|
|
|
|
|
|
|
|
|
await loadNoteDetail(noteId, {
|
|
|
|
|
newTab: true,
|
|
|
|
|
activate: true
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-05 20:45:07 +02:00
|
|
|
/**
|
2019-05-08 19:55:24 +02:00
|
|
|
* @param {TabContext} ctx
|
2019-09-04 22:13:22 +02:00
|
|
|
* @param {string} notePath
|
2019-05-05 20:45:07 +02:00
|
|
|
*/
|
2020-01-15 21:36:01 +01:00
|
|
|
async function loadNoteDetailToContext(ctx, notePath) {
|
|
|
|
|
await ctx.setNote(notePath);
|
2018-10-31 19:08:31 +01:00
|
|
|
|
2020-01-12 12:48:17 +01:00
|
|
|
appContext.openTabsChanged();
|
2019-05-12 12:58:55 +02:00
|
|
|
|
2019-01-01 19:32:34 +01:00
|
|
|
fireDetailLoaded();
|
2018-04-08 22:38:52 -04:00
|
|
|
}
|
|
|
|
|
|
2019-05-12 12:58:55 +02:00
|
|
|
async function loadNoteDetail(origNotePath, options = {}) {
|
2019-05-10 21:43:40 +02:00
|
|
|
const newTab = !!options.newTab;
|
|
|
|
|
const activate = !!options.activate;
|
|
|
|
|
|
2020-01-07 19:48:26 +01:00
|
|
|
let notePath = await treeService.resolveNotePath(origNotePath);
|
2019-05-12 12:58:55 +02:00
|
|
|
|
|
|
|
|
if (!notePath) {
|
|
|
|
|
console.error(`Cannot resolve note path ${origNotePath}`);
|
2019-06-01 20:32:11 +02:00
|
|
|
|
|
|
|
|
// fallback to display something
|
2020-01-07 19:48:26 +01:00
|
|
|
notePath = 'root';
|
2019-05-12 12:58:55 +02:00
|
|
|
}
|
2019-05-11 21:27:27 +02:00
|
|
|
|
2019-05-08 20:14:41 +02:00
|
|
|
const noteId = treeUtils.getNoteIdFromNotePath(notePath);
|
2020-01-12 12:30:30 +01:00
|
|
|
const ctx = appContext.getTab(newTab, options.state);
|
2019-05-05 20:45:07 +02:00
|
|
|
|
|
|
|
|
// we will try to render the new note only if it's still the active one in the tree
|
|
|
|
|
// this is useful when user quickly switches notes (by e.g. holding down arrow) so that we don't
|
|
|
|
|
// try to render all those loaded notes one after each other. This only guarantees that correct note
|
|
|
|
|
// will be displayed independent of timing
|
2020-01-12 11:15:23 +01:00
|
|
|
const currentTreeNode = appContext.getMainNoteTree().getActiveNode();
|
2020-01-15 21:36:01 +01:00
|
|
|
if (!newTab && currentTreeNode && currentTreeNode.data.noteId !== noteId) {
|
2019-05-05 20:45:07 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2019-09-03 22:01:45 +02:00
|
|
|
|
2020-01-15 21:36:01 +01:00
|
|
|
const loadPromise = loadNoteDetailToContext(ctx, notePath).then(() => {
|
2019-09-03 22:01:45 +02:00
|
|
|
if (activate) {
|
2020-01-19 21:12:53 +01:00
|
|
|
return appContext.activateTab(ctx.tabId);
|
2019-09-03 22:01:45 +02:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
}
|
|
|
|
|
});
|
2019-05-05 20:45:07 +02:00
|
|
|
|
2019-09-03 22:01:45 +02:00
|
|
|
if (!options.async) {
|
|
|
|
|
await loadPromise;
|
2019-05-09 21:30:08 +02:00
|
|
|
}
|
2019-05-05 20:45:07 +02:00
|
|
|
}
|
|
|
|
|
|
2018-08-29 22:28:58 +02:00
|
|
|
async function loadNote(noteId) {
|
|
|
|
|
const row = await server.get('notes/' + noteId);
|
|
|
|
|
|
2019-10-26 09:51:08 +02:00
|
|
|
const noteShort = await treeCache.getNote(noteId);
|
|
|
|
|
|
|
|
|
|
return new NoteFull(treeCache, row, noteShort);
|
2018-08-29 22:28:58 +02:00
|
|
|
}
|
|
|
|
|
|
2019-05-20 21:50:01 +02:00
|
|
|
async function noteDeleted(noteId) {
|
2020-01-12 12:37:44 +01:00
|
|
|
for (const tc of appContext.getTabContexts()) {
|
2019-11-26 20:42:34 +01:00
|
|
|
// not removing active even if it contains deleted note since that one will move to another note (handled by deletion logic)
|
|
|
|
|
// and we would lose tab context state (e.g. sidebar visibility)
|
|
|
|
|
if (!tc.isActive() && tc.notePath && tc.notePath.split("/").includes(noteId)) {
|
2020-01-15 22:27:52 +01:00
|
|
|
tabRow.removeTab(tc.tabId);
|
2019-05-20 21:50:01 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-07 10:50:05 +02:00
|
|
|
function focusOnTitle() {
|
2020-01-12 12:37:44 +01:00
|
|
|
appContext.getActiveTabContext().$noteTitle.trigger('focus');
|
2018-08-29 22:28:58 +02:00
|
|
|
}
|
|
|
|
|
|
2019-01-10 22:46:08 +01:00
|
|
|
function focusAndSelectTitle() {
|
2020-01-12 12:37:44 +01:00
|
|
|
appContext.getActiveTabContext()
|
2019-11-09 17:45:22 +01:00
|
|
|
.$noteTitle
|
|
|
|
|
.trigger('focus')
|
|
|
|
|
.trigger('select');
|
2019-01-10 22:46:08 +01:00
|
|
|
}
|
|
|
|
|
|
2019-01-01 19:32:34 +01:00
|
|
|
/**
|
|
|
|
|
* 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) {
|
2020-01-12 12:37:44 +01:00
|
|
|
if (noteId === appContext.getActiveTabNoteId()) {
|
2019-01-01 19:32:34 +01:00
|
|
|
callback();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// all the listeners are one time only
|
|
|
|
|
detailLoadedListeners = [];
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-26 20:21:43 +02:00
|
|
|
ws.subscribeToOutsideSyncMessages(syncData => {
|
2019-06-01 12:14:09 +02:00
|
|
|
const noteIdsToRefresh = new Set();
|
|
|
|
|
|
|
|
|
|
syncData
|
|
|
|
|
.filter(sync => sync.entityName === 'notes')
|
|
|
|
|
.forEach(sync => noteIdsToRefresh.add(sync.entityId));
|
|
|
|
|
|
2019-08-06 22:39:27 +02:00
|
|
|
// we need to reload because of promoted attributes
|
2019-06-01 12:14:09 +02:00
|
|
|
syncData
|
|
|
|
|
.filter(sync => sync.entityName === 'attributes')
|
|
|
|
|
.forEach(sync => noteIdsToRefresh.add(sync.noteId));
|
|
|
|
|
|
2020-01-19 21:24:14 +01:00
|
|
|
// FIXME
|
2019-06-01 12:14:09 +02:00
|
|
|
for (const noteId of noteIdsToRefresh) {
|
2020-01-12 12:30:30 +01:00
|
|
|
appContext.refreshTabs(null, noteId);
|
2018-08-29 22:28:58 +02:00
|
|
|
}
|
2018-08-06 14:43:42 +02:00
|
|
|
});
|
|
|
|
|
|
2019-08-26 20:21:43 +02:00
|
|
|
ws.subscribeToAllSyncMessages(syncData => {
|
2020-01-15 21:36:01 +01:00
|
|
|
appContext.trigger('syncData', {data: syncData});
|
2019-08-06 22:39:27 +02:00
|
|
|
});
|
|
|
|
|
|
2019-06-12 19:59:52 +02:00
|
|
|
function noteChanged() {
|
2020-01-12 12:37:44 +01:00
|
|
|
const activeTabContext = appContext.getActiveTabContext();
|
2019-06-12 19:59:52 +02:00
|
|
|
|
|
|
|
|
if (activeTabContext) {
|
|
|
|
|
activeTabContext.noteChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-26 22:29:14 -04:00
|
|
|
// this makes sure that when user e.g. reloads the page or navigates away from the page, the note's content is saved
|
|
|
|
|
// this sends the request asynchronously and doesn't wait for result
|
2020-01-19 21:40:23 +01:00
|
|
|
// FIXME
|
2020-01-19 22:05:45 +01:00
|
|
|
$(window).on('beforeunload', () => {
|
|
|
|
|
//saveNotesIfChanged();
|
|
|
|
|
});
|
2018-03-26 22:29:14 -04:00
|
|
|
|
2018-03-25 11:09:17 -04:00
|
|
|
export default {
|
|
|
|
|
reload,
|
2019-05-02 22:24:43 +02:00
|
|
|
openInTab,
|
2018-03-25 11:09:17 -04:00
|
|
|
switchToNote,
|
|
|
|
|
loadNote,
|
2019-05-07 22:33:53 +02:00
|
|
|
loadNoteDetail,
|
2018-09-07 10:50:05 +02:00
|
|
|
focusOnTitle,
|
2019-01-10 22:46:08 +01:00
|
|
|
focusAndSelectTitle,
|
2019-05-04 22:44:25 +02:00
|
|
|
addDetailLoadedListener,
|
2019-05-19 09:13:13 +02:00
|
|
|
getActiveEditor,
|
2019-07-07 11:15:55 +02:00
|
|
|
activateOrOpenNote,
|
2019-05-20 22:25:04 +02:00
|
|
|
noteDeleted,
|
2019-08-15 10:04:03 +02:00
|
|
|
noteChanged,
|
2020-01-12 12:30:30 +01:00
|
|
|
reloadNote
|
2018-03-25 11:09:17 -04:00
|
|
|
};
|