import { useEffect, useRef, useState } from "react"; import * as Api from "./api"; import { TerminalAlert } from "./terminal-alert"; import type { FormEvent, KeyboardEvent } from "react"; function message(error: unknown): string { if (error instanceof Api.ApiError && error.status === 409) { return "A document with this title already exists. Try a different title."; } return error instanceof Error ? error.message : "Could not rename document."; } /** * One server-backed title form shared by room navigation and repository rows. * `inline` renders only the field: Enter or blur commits, Escape reverts. */ export function DocumentRename( { channel, className = "", inline = false, onCancel, onErrorChange, onRenamed, onSavingChange, }: { channel: Pick; className?: string; inline?: boolean; onCancel: () => void; onErrorChange?: (error: unknown) => void; onRenamed: (detail: Api.ChannelDetail) => void; onSavingChange?: (saving: boolean) => void; }, ) { let input = useRef(null); let [title, setTitle] = useState(channel.title); let [error, setError] = useState(); let [saving, setSaving] = useState(false); // Enter and the blur it can cause arrive before React re-renders `saving`. let busy = useRef(false); let cancelled = useRef(false); useEffect(() => { input.current?.focus(); input.current?.select(); }, []); function report(next: unknown) { setError(next); onErrorChange?.(next); } async function submit(event?: FormEvent) { event?.preventDefault(); let next = title.trim(); if (busy.current || cancelled.current) return; if (inline && (!next || next === channel.title)) { cancelled.current = true; onCancel(); return; } if (!next) return; busy.current = true; setSaving(true); onSavingChange?.(true); report(undefined); try { onRenamed(await Api.renameChannel(channel.id, next)); } catch (reason) { report(reason); busy.current = false; setSaving(false); onSavingChange?.(false); } } function keyDown(event: KeyboardEvent) { if (event.key !== "Escape") return; event.preventDefault(); event.stopPropagation(); if (saving) return; cancelled.current = true; onCancel(); } if (inline) { return (
{ // Leaving a rejected title reverts it rather than repeating the failure. if (error === undefined) void submit(); else if (!busy.current) { cancelled.current = true; onCancel(); } }} onChange={event => { setTitle(event.target.value); if (error !== undefined) report(undefined); }} onKeyDown={keyDown} placeholder="Untitled" readOnly={saving} ref={input} value={title} /> {error !== undefined && ( {message(error)} )}
); } return (
{ setTitle(event.target.value); if (error !== undefined) report(undefined); }} onKeyDown={keyDown} ref={input} required value={title} /> {error !== undefined && ( {message(error)} )}
); }