refactor: drop type-to-confirm gate on chat delete
The chat-delete dialog required typing the chat name to confirm deletion. Single-user app — typing friction is annoying, not safety. Match the archive dialog pattern in SettingsPane.tsx: title + description naming the chat in mono font, plain Cancel + destructive Delete button. Removes the deleteInput state, deleteExpected / deleteEnabled deriveds, the <Input> field, and its lone <Input> import. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,6 @@ import { Archive, MessageSquare, Send, ChevronDown, ChevronRight, RotateCcw, Tra
|
|||||||
import type { Chat } from '@/api/types';
|
import type { Chat } from '@/api/types';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Textarea } from '@/components/ui/textarea';
|
import { Textarea } from '@/components/ui/textarea';
|
||||||
import { Input } from '@/components/ui/input';
|
|
||||||
import {
|
import {
|
||||||
ContextMenu,
|
ContextMenu,
|
||||||
ContextMenuContent,
|
ContextMenuContent,
|
||||||
@@ -165,7 +164,6 @@ export function SessionLandingPage({
|
|||||||
const [renameValue, setRenameValue] = useState('');
|
const [renameValue, setRenameValue] = useState('');
|
||||||
const [archiveConfirm, setArchiveConfirm] = useState<Chat | null>(null);
|
const [archiveConfirm, setArchiveConfirm] = useState<Chat | null>(null);
|
||||||
const [deleteConfirm, setDeleteConfirm] = useState<Chat | null>(null);
|
const [deleteConfirm, setDeleteConfirm] = useState<Chat | null>(null);
|
||||||
const [deleteInput, setDeleteInput] = useState('');
|
|
||||||
|
|
||||||
const openChats = chats
|
const openChats = chats
|
||||||
.filter((c) => c.status === 'open')
|
.filter((c) => c.status === 'open')
|
||||||
@@ -193,9 +191,6 @@ export function SessionLandingPage({
|
|||||||
setRenamingId(null);
|
setRenamingId(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
const deleteExpected = deleteConfirm?.name ?? '';
|
|
||||||
const deleteEnabled = deleteConfirm !== null && deleteInput === deleteExpected && deleteExpected.length > 0;
|
|
||||||
|
|
||||||
// TODO: Landing page chat counts are a snapshot at mount. New messages in
|
// TODO: Landing page chat counts are a snapshot at mount. New messages in
|
||||||
// visible chats won't update the per-row stats until next mount/navigation.
|
// visible chats won't update the per-row stats until next mount/navigation.
|
||||||
return (
|
return (
|
||||||
@@ -217,7 +212,7 @@ export function SessionLandingPage({
|
|||||||
onCancelRename={() => setRenamingId(null)}
|
onCancelRename={() => setRenamingId(null)}
|
||||||
onContextStartRename={() => startRename(chat)}
|
onContextStartRename={() => startRename(chat)}
|
||||||
onContextArchive={() => setArchiveConfirm(chat)}
|
onContextArchive={() => setArchiveConfirm(chat)}
|
||||||
onContextDelete={() => { setDeleteConfirm(chat); setDeleteInput(''); }}
|
onContextDelete={() => setDeleteConfirm(chat)}
|
||||||
showContextMenu
|
showContextMenu
|
||||||
actions={
|
actions={
|
||||||
<>
|
<>
|
||||||
@@ -242,7 +237,6 @@ export function SessionLandingPage({
|
|||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
setDeleteConfirm(chat);
|
setDeleteConfirm(chat);
|
||||||
setDeleteInput('');
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Trash2 size={14} />
|
<Trash2 size={14} />
|
||||||
@@ -352,36 +346,25 @@ export function SessionLandingPage({
|
|||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
|
||||||
<Dialog open={deleteConfirm !== null} onOpenChange={(open) => { if (!open) { setDeleteConfirm(null); setDeleteInput(''); } }}>
|
<Dialog open={deleteConfirm !== null} onOpenChange={(open) => { if (!open) setDeleteConfirm(null); }}>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<DialogHeader>
|
<DialogHeader>
|
||||||
<DialogTitle>Delete chat?</DialogTitle>
|
<DialogTitle>Delete chat?</DialogTitle>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
Type the chat name to confirm:
|
Permanently delete{' '}
|
||||||
{' '}
|
<span className="font-mono font-medium text-foreground">{deleteConfirm?.name || '(unnamed)'}</span>
|
||||||
<span className="font-mono font-medium text-foreground">{deleteExpected || '(unnamed — cannot type-confirm)'}</span>
|
{' '}and all its messages. This cannot be undone.
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
<Input
|
|
||||||
value={deleteInput}
|
|
||||||
onChange={(e) => setDeleteInput(e.target.value)}
|
|
||||||
placeholder={deleteExpected}
|
|
||||||
disabled={!deleteExpected}
|
|
||||||
/>
|
|
||||||
<div className="text-xs text-muted-foreground">
|
|
||||||
This will permanently delete this chat and all its messages. This cannot be undone.
|
|
||||||
</div>
|
|
||||||
<div className="flex gap-2 justify-end pt-2">
|
<div className="flex gap-2 justify-end pt-2">
|
||||||
<Button variant="outline" onClick={() => { setDeleteConfirm(null); setDeleteInput(''); }}>
|
<Button variant="outline" onClick={() => setDeleteConfirm(null)}>
|
||||||
Cancel
|
Cancel
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
disabled={!deleteEnabled}
|
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (deleteConfirm && deleteEnabled) void onDeleteChat(deleteConfirm.id);
|
if (deleteConfirm) void onDeleteChat(deleteConfirm.id);
|
||||||
setDeleteConfirm(null);
|
setDeleteConfirm(null);
|
||||||
setDeleteInput('');
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Delete
|
Delete
|
||||||
|
|||||||
Reference in New Issue
Block a user