Open chat attachments and use a proper edit icon

This commit is contained in:
ZacharyZhang-NY committed 2026-05-28 23:03:02 -04:00
1 parent 207e108ae5
commit 6f182d1347
5 files changed
+18 -5

No files matched your search

@@ -35,6 +35,7 @@ export function ChatConversation({
onDecline,
onEditMessage,
onDeleteMessage,
onOpenFile,
onSubmit,
composerRef,
}: {
@@ -58,6 +59,7 @@ export function ChatConversation({
onDecline: (approvalId: string) => void;
onEditMessage: (messageId: string, bodyMd: string) => void;
onDeleteMessage: (messageId: string) => void;
onOpenFile: (fileId: string) => void;
onSubmit: (event: FormEvent<HTMLFormElement>) => void;
composerRef: MutableRefObject<HTMLTextAreaElement | null>;
}) {
@@ -87,6 +89,7 @@ export function ChatConversation({
onDecline={onDecline}
onEditMessage={onEditMessage}
onDeleteMessage={onDeleteMessage}
onOpenFile={onOpenFile}
/>
)}
</div>
@@ -156,7 +159,6 @@ function ConversationHeader({
)}
{!searchOpen && participants.length > 0 && <AvatarStack names={participants.slice(0, 5)} bot />}
<IconButton icon="search" label="Search" onClick={toggleSearch} />
<IconButton icon="more" label="More" />
</div>
</header>
);
@@ -23,6 +23,7 @@ export function ChatMessageRow({
canModify = false,
onEdit,
onDelete,
onOpenFile,
}: {
message: ChatMessageRecord;
attachments: ChatAttachmentRecord[];
@@ -36,6 +37,7 @@ export function ChatMessageRow({
canModify?: boolean;
onEdit?: (bodyMd: string) => void;
onDelete?: () => void;
onOpenFile?: (fileId: string) => void;
}) {
const [editing, setEditing] = useState(false);
const name = senderName(message, context);
@@ -61,7 +63,7 @@ export function ChatMessageRow({
{message.editedAt && !deleted && <span style={{ fontSize: 11, color: "var(--text-3)" }}>(edited)</span>}
{canModify && !editing && (
<span style={{ marginLeft: "auto", display: "flex", gap: 2 }}>
<IconButton icon="more" label="Edit message" size={13} onClick={() => setEditing(true)} />
<IconButton icon="edit" label="Edit message" size={13} onClick={() => setEditing(true)} />
<IconButton icon="x" label="Delete message" size={13} onClick={onDelete} />
</span>
)}
@@ -98,7 +100,7 @@ export function ChatMessageRow({
{messageFiles.map((file) => {
const attachment = attachments.find((item) => item.messageId === message.id && item.fileId === file.id);
const handoff = bot && attachment?.transferType === "handoff";
return <AttachmentCard key={file.id} file={file} handoff={handoff} />;
return <AttachmentCard key={file.id} file={file} handoff={handoff} onOpen={onOpenFile ? () => onOpenFile(file.id) : null} />;
})}
</div>
)}
@@ -173,7 +175,8 @@ function mentionLabel(mention: ChatMentionRecord, context: MessageContext): stri
throw new Error("Message mention target type is unsupported.");
}
function AttachmentCard({ file, handoff = false }: { file: FileObjectRecord; handoff?: boolean }) {
function AttachmentCard({ file, handoff = false, onOpen }: { file: FileObjectRecord; handoff?: boolean; onOpen: (() => void) | null }) {
const canOpen = onOpen !== null && file.scanState === "approved";
return (
<div
style={{
@@ -211,7 +214,7 @@ function AttachmentCard({ file, handoff = false }: { file: FileObjectRecord; han
</div>
<div style={{ fontSize: 11, color: "var(--text-3)" }}>{file.kind.toUpperCase()} · {file.sha256.slice(0, 8)}</div>
</div>
<IconButton icon="arrowUpRight" label="Open" />
{canOpen && <IconButton icon="arrowUpRight" label={`Open ${file.name}`} onClick={onOpen ?? undefined} />}
</div>
);
}
@@ -29,6 +29,7 @@ export function ChatThreadPanel({
onClose,
onSubmit,
onBoardSubmit,
onOpenFile,
}: {
thread: ChatThreadRecord;
rootMessage: ChatMessageRecord | null;
@@ -44,6 +45,7 @@ export function ChatThreadPanel({
onClose: () => void;
onSubmit: (event: FormEvent<HTMLFormElement>) => void;
onBoardSubmit: (event: FormEvent<HTMLFormElement>) => void;
onOpenFile: (fileId: string) => void;
}) {
return (
<aside
@@ -87,6 +89,7 @@ export function ChatThreadPanel({
replyCount={0}
onOpenThread={null}
context={context}
onOpenFile={onOpenFile}
compact
/>
</div>
@@ -103,6 +106,7 @@ export function ChatThreadPanel({
replyCount={0}
onOpenThread={null}
context={context}
onOpenFile={onOpenFile}
compact
/>
))}
@@ -28,6 +28,7 @@ export function ChatTimeline({
onDecline,
onEditMessage,
onDeleteMessage,
onOpenFile,
}: {
rootMessages: ChatMessageRecord[];
messagesInSpace: ChatMessageRecord[];
@@ -44,6 +45,7 @@ export function ChatTimeline({
onDecline: (approvalId: string) => void;
onEditMessage: (messageId: string, bodyMd: string) => void;
onDeleteMessage: (messageId: string) => void;
onOpenFile: (fileId: string) => void;
}) {
const elements: ReactNode[] = [];
let lastDay = "";
@@ -71,6 +73,7 @@ export function ChatTimeline({
canModify={message.senderType === "employee" && message.senderId === currentEmployeeId && !message.deletedAt}
onEdit={(bodyMd) => onEditMessage(message.id, bodyMd)}
onDelete={() => onDeleteMessage(message.id)}
onOpenFile={onOpenFile}
/>,
);
});
@@ -17,6 +17,7 @@ const PATHS: Record<string, ReactNode> = {
chevronDown: <path d="m6 9 6 6 6-6" />,
chevronLeft: <path d="m15 18-6-6 6-6" />,
more: <><circle cx="5" cy="12" r="1" /><circle cx="12" cy="12" r="1" /><circle cx="19" cy="12" r="1" /></>,
edit: <><path d="M12 20h9" /><path d="M16.5 3.5a2.1 2.1 0 0 1 3 3L7 19l-4 1 1-4Z" /></>,
filter: <path d="M3 5h18M6 12h12M10 19h4" />,
file: <><path d="M14 3H6a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z" /><path d="M14 3v6h6" /></>,
folder: <path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2Z" />,