import { useEffect, useRef, useState } from "preact/hooks"; import { t } from "../services/i18n"; import FormTextBox from "./react/FormTextBox"; import { useNoteContext, useNoteProperty, useSpacedUpdate, useTriliumEvent, useTriliumEvents } from "./react/hooks"; import protected_session_holder from "../services/protected_session_holder"; import server from "../services/server"; import "./note_title.css"; import { isLaunchBarConfig } from "../services/utils"; import appContext from "../components/app_context"; import branches from "../services/branches"; import { isIMEComposing } from "../services/shortcuts"; export default function NoteTitleWidget() { const { note, noteId, componentId, viewScope, noteContext, parentComponent } = useNoteContext(); const title = useNoteProperty(note, "title", componentId); const isProtected = useNoteProperty(note, "isProtected"); const newTitle = useRef(""); const [ isReadOnly, setReadOnly ] = useState(false); const [ navigationTitle, setNavigationTitle ] = useState(null); // Manage read-only useEffect(() => { const isReadOnly = note === null || note === undefined || (note.isProtected && !protected_session_holder.isProtectedSessionAvailable()) || isLaunchBarConfig(note.noteId) || viewScope?.viewMode !== "default"; setReadOnly(isReadOnly); }, [ note, note?.noteId, note?.isProtected, viewScope?.viewMode ]); // Manage the title for read-only notes useEffect(() => { if (isReadOnly) { noteContext?.getNavigationTitle().then(setNavigationTitle); } }, [noteContext, isReadOnly]); // Save changes to title. const spacedUpdate = useSpacedUpdate(async () => { if (!note) { return; } protected_session_holder.touchProtectedSessionIfNecessary(note); await server.put(`notes/${noteId}/title`, { title: newTitle.current }, componentId); }); // Prevent user from navigating away if the spaced update is not done. useEffect(() => { appContext.addBeforeUnloadListener(() => spacedUpdate.isAllSavedAndTriggerUpdate()); }, []); useTriliumEvents([ "beforeNoteSwitch", "beforeNoteContextRemove" ], () => spacedUpdate.updateNowIfNecessary()); // Manage focus. const textBoxRef = useRef(null); const isNewNote = useRef(); useTriliumEvents([ "focusOnTitle", "focusAndSelectTitle" ], (e, eventName) => { if (noteContext?.isActive() && textBoxRef.current) { textBoxRef.current.focus(); if (eventName === "focusAndSelectTitle") { textBoxRef.current.select(); } isNewNote.current = ("isNewNote" in e ? e.isNewNote : false); } }); return (
{note && { newTitle.current = newValue; spacedUpdate.scheduleUpdate(); }} onKeyDown={(e) => { // Skip processing if IME is composing to prevent interference // with text input in CJK languages if (isIMEComposing(e)) { return; } // Focus on the note content when pressing enter. if (e.key === "Enter") { e.preventDefault(); parentComponent.triggerCommand("focusOnDetail", { ntxId: noteContext?.ntxId }); return; } if (e.key === "Escape" && isNewNote.current && noteContext?.isActive() && note) { branches.deleteNotes(Object.values(note.parentToBranch)); } }} onBlur={() => { spacedUpdate.updateNowIfNecessary(); isNewNote.current = false; }} />}
); }