feat(P7.T4): per-attachment view-once toggle on AttachmentPreview

This commit is contained in:
byGalax
2026-05-17 01:25:08 +02:00
parent 940432d287
commit 7f704e80f6
3 changed files with 112 additions and 26 deletions
@@ -75,7 +75,7 @@ export function useConversationMessages({ conversationId, userId, deviceId }: Ar
text: string,
images?: File[],
replyToId?: string | null,
opts?: { viewOnce?: boolean },
opts?: { viewOnceFlags?: boolean[] },
) => Promise<void>;
refresh: () => Promise<void>;
pending: OutboxItem[];
@@ -569,7 +569,7 @@ export function useConversationMessages({ conversationId, userId, deviceId }: Ar
text: string,
images: File[] = [],
replyToId: string | null = null,
opts: { viewOnce?: boolean } = {},
opts: { viewOnceFlags?: boolean[] } = {},
) => {
const trimmed = text.trim();
if ((!trimmed && images.length === 0) || !conversationId || !userId || !deviceId) return;
@@ -626,9 +626,13 @@ export function useConversationMessages({ conversationId, userId, deviceId }: Ar
// 1. Upload + encrypt each image. Collect handles + raw blob nonces
// (so the public attachment row can reference the blob-level nonce).
// P7.T4: view-once is now a per-attachment flag rather than a
// composer-wide toggle. `opts.viewOnceFlags` is a parallel array;
// missing entries (or whole-array absence) default to false.
const handles: AttachmentHandle[] = [];
const blobNonceHexByHandleId = new Map<string, string>();
for (const file of images) {
for (let i = 0; i < images.length; i++) {
const file = images[i]!;
if (file.size > MAX_ATTACHMENT_BYTES) {
throw new Error('attachment exceeds max size (10 MB)');
}
@@ -649,12 +653,12 @@ export function useConversationMessages({ conversationId, userId, deviceId }: Ar
...(dims.height !== undefined ? { height: dims.height } : {}),
...(thumbBlob ? { thumbBlob } : {}),
});
// Stamp the view-once flag on each handle the caller requested it
// for. The flag rides inside the encrypted payload (so peers can
// render the locked card without leaking who-sent-what to the
// server) AND lands on the public message_attachments row via
// insertAttachmentRow below (where the mark-viewed RPC enforces it).
if (opts.viewOnce) {
// Stamp the view-once flag on each handle the caller flagged. The
// flag rides inside the encrypted payload (so peers can render the
// locked card without leaking who-sent-what to the server) AND
// lands on the public message_attachments row via insertAttachmentRow
// below (where the mark-viewed RPC enforces it).
if (opts.viewOnceFlags?.[i]) {
res.handle.viewOnce = true;
}
handles.push(res.handle);