feat(shared): mark_attachment_viewed + view_once on AttachmentHandle
This commit is contained in:
@@ -25,6 +25,16 @@ export interface AttachmentHandle {
|
||||
// base64-encoded — only readable via per-device envelope decrypt.
|
||||
keyB64: string;
|
||||
nonceB64: string;
|
||||
/** View-once flag — set by sender on the encrypted envelope and mirrored
|
||||
* to `message_attachments.view_once` so the renderer can show a "tap to
|
||||
* view" tombstone and call the mark-viewed RPC on first open. */
|
||||
viewOnce?: boolean;
|
||||
/** ISO timestamp the first non-sender opened the attachment. Populated by
|
||||
* the mark-viewed RPC (server-authoritative); undefined until burnt. */
|
||||
viewedAt?: string | null;
|
||||
/** UUID of the first non-sender who viewed. Populated alongside `viewedAt`
|
||||
* by the mark-viewed RPC so the renderer can render attribution. */
|
||||
viewedBy?: string | null;
|
||||
}
|
||||
|
||||
export type CallEventStatus = 'ended' | 'missed' | 'declined';
|
||||
@@ -256,6 +266,7 @@ export async function insertAttachmentRow(
|
||||
nonce: blobNonceHex,
|
||||
mime_type: handle.mimeType,
|
||||
size_bytes: handle.sizeBytes,
|
||||
view_once: handle.viewOnce ?? false,
|
||||
};
|
||||
if (handle.width !== undefined) row.width = handle.width;
|
||||
if (handle.height !== undefined) row.height = handle.height;
|
||||
|
||||
@@ -9,6 +9,7 @@ export * from './types';
|
||||
export * from './userKeyMigration';
|
||||
export * from './pinnedMessages';
|
||||
export * from './mentions';
|
||||
export * from './viewOnceAttachments';
|
||||
|
||||
// ----- RPC wrappers ---------------------------------------------------------
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import type { AppSupabaseClient } from '../supabase/client';
|
||||
|
||||
export interface ViewOnceResult {
|
||||
viewOnce: boolean;
|
||||
/** ISO timestamp set the moment the first non-sender viewed. */
|
||||
viewedAt?: string | null;
|
||||
/** True iff the caller is the original sender (they don't burn the view). */
|
||||
self?: boolean;
|
||||
/** True iff someone else already viewed before this call. */
|
||||
already?: boolean;
|
||||
}
|
||||
|
||||
export async function markAttachmentViewed(
|
||||
client: AppSupabaseClient,
|
||||
attachmentId: string,
|
||||
): Promise<ViewOnceResult> {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const { data, error } = await (client as any).rpc('mark_attachment_viewed', {
|
||||
p_attachment_id: attachmentId,
|
||||
});
|
||||
if (error) throw error;
|
||||
const d = (data ?? {}) as Record<string, unknown>;
|
||||
return {
|
||||
viewOnce: Boolean(d.view_once),
|
||||
viewedAt: typeof d.viewed_at === 'string' ? d.viewed_at : null,
|
||||
self: Boolean(d.self),
|
||||
already: Boolean(d.already),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user