Notes/src/public/app/services/note_create.ts

171 lines
5.5 KiB
TypeScript
Raw Normal View History

2022-12-01 13:07:23 +01:00
import appContext from "../components/app_context.js";
import protectedSessionHolder from "./protected_session_holder.js";
import server from "./server.js";
import ws from "./ws.js";
2021-04-16 23:01:56 +02:00
import froca from "./froca.js";
import treeService from "./tree.js";
import toastService from "./toast.js";
import { t } from "./i18n.js";
import FNote from "../entities/fnote.js";
import FBranch from "../entities/fbranch.js";
import { ChooseNoteTypeResponse } from "../widgets/dialogs/note_type_chooser.js";
interface CreateNoteOpts {
isProtected?: boolean;
saveSelection?: boolean;
title?: string | null;
content?: string | null;
type?: string;
mime?: string;
templateNoteId?: string;
activate?: boolean;
focus?: "title" | "content";
target?: string;
targetBranchId?: string;
textEditor?: {
// TODO: Replace with interface once note_context.js is converted.
getSelectedHtml(): string;
removeSelection(): void;
}
}
interface Response {
// TODO: Deduplicate with server once we have client/server architecture.
note: FNote;
branch: FBranch;
}
interface DuplicateResponse {
// TODO: Deduplicate with server once we have client/server architecture.
note: FNote;
}
2024-12-21 23:16:01 +02:00
async function createNote(parentNotePath: string | undefined, options: CreateNoteOpts = {}) {
options = Object.assign({
activate: true,
focus: 'title',
target: 'into'
}, options);
2023-05-05 23:41:11 +02:00
// if isProtected isn't available (user didn't enter password yet), then note is created as unencrypted,
// but this is quite weird since the user doesn't see WHERE the note is being created, so it shouldn't occur often
if (!options.isProtected || !protectedSessionHolder.isProtectedSessionAvailable()) {
options.isProtected = false;
}
2021-05-22 12:35:41 +02:00
if (appContext.tabManager.getActiveContextNoteType() !== 'text') {
options.saveSelection = false;
}
if (options.saveSelection && options.textEditor) {
2022-06-03 22:05:18 +02:00
[options.title, options.content] = parseSelectedHtml(options.textEditor.getSelectedHtml());
}
const parentNoteId = treeService.getNoteIdFromUrl(parentNotePath);
2021-09-30 10:18:03 +02:00
if (options.type === 'mermaid' && !options.content) {
options.content = `graph TD;
A-->B;
A-->C;
B-->D;
C-->D;`
}
const {note, branch} = await server.post<Response>(`notes/${parentNoteId}/children?target=${options.target}&targetBranchId=${options.targetBranchId || ""}`, {
2022-05-15 15:21:35 +02:00
title: options.title,
content: options.content || "",
isProtected: options.isProtected,
type: options.type,
2022-05-31 23:27:45 +02:00
mime: options.mime,
templateNoteId: options.templateNoteId
});
2022-06-03 22:05:18 +02:00
if (options.saveSelection) {
// we remove the selection only after it was saved to server to make sure we don't lose anything
options.textEditor?.removeSelection();
}
await ws.waitForMaxKnownEntityChangeId();
2020-07-24 00:02:15 +02:00
if (options.activate) {
2021-05-22 12:35:41 +02:00
const activeNoteContext = appContext.tabManager.getActiveContext();
2021-05-22 12:26:45 +02:00
await activeNoteContext.setNote(`${parentNotePath}/${note.noteId}`);
if (options.focus === 'title') {
appContext.triggerEvent('focusAndSelectTitle', {isNewNote: true});
}
else if (options.focus === 'content') {
2021-05-22 12:26:45 +02:00
appContext.triggerEvent('focusOnDetail', {ntxId: activeNoteContext.ntxId});
}
}
2021-04-16 22:57:37 +02:00
const noteEntity = await froca.getNote(note.noteId);
const branchEntity = froca.getBranch(branch.branchId);
return {
note: noteEntity,
branch: branchEntity
};
}
async function chooseNoteType() {
return new Promise<ChooseNoteTypeResponse>(res => {
// TODO: Remove ignore after callback for chooseNoteType is defined in app_context.ts
//@ts-ignore
appContext.triggerCommand("chooseNoteType", {callback: res});
});
}
async function createNoteWithTypePrompt(parentNotePath: string, options: CreateNoteOpts = {}) {
const {success, noteType, templateNoteId} = await chooseNoteType();
if (!success) {
return;
}
options.type = noteType;
options.templateNoteId = templateNoteId;
return await createNote(parentNotePath, options);
}
2023-05-05 23:41:11 +02:00
/* If the first element is heading, parse it out and use it as a new heading. */
function parseSelectedHtml(selectedHtml: string) {
const dom = $.parseHTML(selectedHtml);
// TODO: tagName and outerHTML appear to be missing.
//@ts-ignore
if (dom.length > 0 && dom[0].tagName && dom[0].tagName.match(/h[1-6]/i)) {
const title = $(dom[0]).text();
// remove the title from content (only first occurrence)
// TODO: tagName and outerHTML appear to be missing.
//@ts-ignore
const content = selectedHtml.replace(dom[0].outerHTML, "");
return [title, content];
}
else {
return [null, selectedHtml];
}
}
async function duplicateSubtree(noteId: string, parentNotePath: string) {
const parentNoteId = treeService.getNoteIdFromUrl(parentNotePath);
const {note} = await server.post<DuplicateResponse>(`notes/${noteId}/duplicate/${parentNoteId}`);
await ws.waitForMaxKnownEntityChangeId();
2021-05-22 12:35:41 +02:00
const activeNoteContext = appContext.tabManager.getActiveContext();
2021-05-22 12:26:45 +02:00
activeNoteContext.setNote(`${parentNotePath}/${note.noteId}`);
2021-04-16 22:57:37 +02:00
const origNote = await froca.getNote(noteId);
toastService.showMessage(t("note_create.duplicated", { title: origNote?.title }));
}
export default {
createNote,
createNoteWithTypePrompt,
duplicateSubtree,
chooseNoteType
};