restore/undo for deleted messages — backend + UI

- POST /api/channels/{id}/messages/{msg_id}/restore — undeletes a message
- Any user can restore (not just the deleter)
- Broadcasts restored message via WebSocket
- UI: "undo" button in floating pill on deleted messages
- API: restoreMessage() in api.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-29 22:08:52 +02:00
parent d4ed76ce12
commit ca2e604b4e
5 changed files with 71 additions and 2 deletions

View File

@@ -11,6 +11,7 @@ interface Props {
replyTarget?: Message;
onSelect: (id: string) => void;
onDelete: (channelId: string, msgId: string) => void;
onRestore: (channelId: string, msgId: string) => void;
currentUsername: string;
selected: boolean;
}
@@ -72,7 +73,7 @@ function userHue(username: string): number {
return Math.abs(hash) % 360;
}
export function MessageItem({ message, compact, replyTarget, onSelect, onDelete, currentUsername, selected }: Props) {
export function MessageItem({ message, compact, replyTarget, onSelect, onDelete, onRestore, currentUsername, selected }: Props) {
const [metaOpen, setMetaOpen] = useState(false);
const isAgent = message.user.role === "agent";
const isDeleted = !!message.deleted_at;
@@ -193,6 +194,16 @@ export function MessageItem({ message, compact, replyTarget, onSelect, onDelete,
×
</button>
)}
{isDeleted && (
<button
type="button"
onClick={(e) => { e.stopPropagation(); onRestore(message.channel_id, message.id); }}
className="px-2.5 py-1.5 text-[10px] font-mono text-muted-foreground hover:text-primary hover:bg-muted/50 transition-colors border-l-2 border-border"
title="Restore deleted message"
>
undo
</button>
)}
</div>
{/* Content */}