trilium/src/public/javascripts/services/note_detail.js

256 lines
6.2 KiB
JavaScript

import treeService from './tree.js';
import noteTypeService from './note_type.js';
import protectedSessionService from './protected_session.js';
import protectedSessionHolder from './protected_session_holder.js';
import utils from './utils.js';
import server from './server.js';
import messagingService from "./messaging.js";
import infoService from "./info.js";
import treeCache from "./tree_cache.js";
import NoteFull from "../entities/note_full.js";
import noteDetailCode from './note_detail_code.js';
import noteDetailText from './note_detail_text.js';
import noteDetailFile from './note_detail_file.js';
import noteDetailSearch from './note_detail_search.js';
import noteDetailRender from './note_detail_render.js';
const $noteTitle = $("#note-title");
const $noteDetailComponents = $(".note-detail-component");
const $protectButton = $("#protect-button");
const $unprotectButton = $("#unprotect-button");
const $noteDetailWrapper = $("#note-detail-wrapper");
const $noteIdDisplay = $("#note-id-display");
const $labelList = $("#label-list");
const $labelListInner = $("#label-list-inner");
let currentNote = null;
let noteChangeDisabled = false;
let isNoteChanged = false;
const components = {
'code': noteDetailCode,
'text': noteDetailText,
'file': noteDetailFile,
'search': noteDetailSearch,
'render': noteDetailRender
};
function getComponent(type) {
if (components[type]) {
return components[type];
}
else {
infoService.throwError("Unrecognized type: " + type);
}
}
function getCurrentNote() {
return currentNote;
}
function getCurrentNoteId() {
return currentNote ? currentNote.noteId : null;
}
function getCurrentNoteType() {
const currentNote = getCurrentNote();
return currentNote ? currentNote.type : null;
}
function noteChanged() {
if (noteChangeDisabled) {
return;
}
isNoteChanged = true;
}
async function reload() {
// no saving here
await loadNoteToEditor(getCurrentNoteId());
}
async function switchToNote(noteId) {
if (getCurrentNoteId() !== noteId) {
await saveNoteIfChanged();
await loadNoteToEditor(noteId);
}
}
async function saveNoteIfChanged() {
if (!isNoteChanged) {
return;
}
const note = getCurrentNote();
updateNoteFromInputs(note);
await saveNoteToServer(note);
if (note.isProtected) {
protectedSessionHolder.touchProtectedSession();
}
}
function updateNoteFromInputs(note) {
note.title = $noteTitle.val();
note.content = getComponent(note.type).getContent();
treeService.setNoteTitle(note.noteId, note.title);
}
async function saveNoteToServer(note) {
await server.put('notes/' + note.noteId, note);
isNoteChanged = false;
infoService.showMessage("Saved!");
}
function setNoteBackgroundIfProtected(note) {
const isProtected = !!note.isProtected;
$noteDetailWrapper.toggleClass("protected", isProtected);
$protectButton.toggle(!isProtected);
$unprotectButton.toggle(isProtected);
}
let isNewNoteCreated = false;
function newNoteCreated() {
isNewNoteCreated = true;
}
async function handleProtectedSession() {
await protectedSessionService.ensureProtectedSession(currentNote.isProtected, false);
if (currentNote.isProtected) {
protectedSessionHolder.touchProtectedSession();
}
// this might be important if we focused on protected note when not in protected note and we got a dialog
// to login, but we chose instead to come to another node - at that point the dialog is still visible and this will close it.
protectedSessionService.ensureDialogIsClosed();
}
async function loadNoteToEditor(noteId) {
currentNote = await loadNote(noteId);
if (isNewNoteCreated) {
isNewNoteCreated = false;
$noteTitle.focus().select();
}
$noteIdDisplay.html(noteId);
await handleProtectedSession();
$noteDetailWrapper.show();
noteChangeDisabled = true;
try {
$noteTitle.val(currentNote.title);
noteTypeService.setNoteType(currentNote.type);
noteTypeService.setNoteMime(currentNote.mime);
$noteDetailComponents.hide();
await getComponent(currentNote.type).show();
}
finally {
noteChangeDisabled = false;
}
setNoteBackgroundIfProtected(currentNote);
treeService.setBranchBackgroundBasedOnProtectedStatus(noteId);
// after loading new note make sure editor is scrolled to the top
$noteDetailWrapper.scrollTop(0);
await loadLabelList();
}
async function loadLabelList() {
const noteId = getCurrentNoteId();
const labels = await server.get('notes/' + noteId + '/labels');
$labelListInner.html('');
if (labels.length > 0) {
for (const label of labels) {
$labelListInner.append(utils.formatLabel(label) + " ");
}
$labelList.show();
}
else {
$labelList.hide();
}
}
async function loadNote(noteId) {
const row = await server.get('notes/' + noteId);
return new NoteFull(treeCache, row);
}
function focus() {
const note = getCurrentNote();
getComponent(note.type).focus();
}
messagingService.subscribeToMessages(syncData => {
if (syncData.some(sync => sync.entityName === 'notes' && sync.entityId === getCurrentNoteId())) {
infoService.showMessage('Reloading note because of background changes');
reload();
}
});
$(document).ready(() => {
$noteTitle.on('input', () => {
noteChanged();
const title = $noteTitle.val();
treeService.setNoteTitle(getCurrentNoteId(), title);
});
noteDetailText.focus();
});
// 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
$(window).on('beforeunload', () => { saveNoteIfChanged(); }); // don't convert to short form, handler doesn't like returned promise
setInterval(saveNoteIfChanged, 5000);
export default {
reload,
switchToNote,
updateNoteFromInputs,
saveNoteToServer,
setNoteBackgroundIfProtected,
loadNote,
getCurrentNote,
getCurrentNoteType,
getCurrentNoteId,
newNoteCreated,
focus,
loadLabelList,
saveNoteIfChanged,
noteChanged
};