From 8e7ba6724896efd0de4bba274477bbb1ab40a60a Mon Sep 17 00:00:00 2001 From: asepharyana Date: Sat, 1 Aug 2026 10:59:54 +0700 Subject: [PATCH] fix: message detail shows only its own images, not everyone's useMessageDetail fetch attachments per-channel -> detail view nampilin 10 image terbaru di channel itu (semua orang), bukan image pesan yang diklik. Root cause: getAttachmentsByChannel cuma filter channel_id. - backend: messageQuerySchema + messageId optional; repository tambah eq(message_id) pas messageId ada. Sudah ada index idx_attachments_message. - frontend: getAttachments(channelId, limit, cursor, messageId); useMessageDetail pass id pesan. Verifikasi: backend tsc PASS, frontend tsc PASS + next build PASS. E2E local (tsx + env prod, port 3901): tanpa messageId -> 3 row (termasuk image.png punya message lain); dengan messageId -> tepat 2 row milik pesan itu. psql juga konfirmasi channel-only 3 vs message-filtered 2. --- .../backend/src/modules/messages/messages.repository.ts | 6 ++++++ services/backend/src/modules/messages/messages.schema.ts | 2 ++ services/frontend/src/hooks/use-messages.ts | 4 +++- services/frontend/src/lib/api/messages.ts | 8 +++++++- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/services/backend/src/modules/messages/messages.repository.ts b/services/backend/src/modules/messages/messages.repository.ts index a080d0d..dcc42e5 100644 --- a/services/backend/src/modules/messages/messages.repository.ts +++ b/services/backend/src/modules/messages/messages.repository.ts @@ -385,6 +385,12 @@ export class MessagesRepository { const limit = query.limit ?? 50; const conditions: SQL[] = [eq(pgAttachmentsTable.channel_id, channelId)]; + // Detail view: narrow to the selected message so we don't show + // everyone else's images from the same channel. + if (query.messageId) { + conditions.push(eq(pgAttachmentsTable.message_id, query.messageId)); + } + if (query.cursor) { conditions.push(lt(pgAttachmentsTable.created_at, Number(query.cursor))); } diff --git a/services/backend/src/modules/messages/messages.schema.ts b/services/backend/src/modules/messages/messages.schema.ts index 943d722..d0cd0ae 100644 --- a/services/backend/src/modules/messages/messages.schema.ts +++ b/services/backend/src/modules/messages/messages.schema.ts @@ -8,6 +8,8 @@ export const messageQuerySchema = z.object({ limit: z.coerce.number().int().positive().default(50), offset: z.coerce.number().int().nonnegative().default(0), cursor: z.string().optional(), + // Filter attachments to a single message (used by the message detail view) + messageId: z.string().optional(), }); export const messageCreateSchema = z.object({ diff --git a/services/frontend/src/hooks/use-messages.ts b/services/frontend/src/hooks/use-messages.ts index ecdbe89..e532403 100644 --- a/services/frontend/src/hooks/use-messages.ts +++ b/services/frontend/src/hooks/use-messages.ts @@ -136,7 +136,9 @@ export function useMessageDetail(id: string | null) { // can race the detail load and see detail.data === undefined. const cid = detail.data?.channel_id; if (!cid) return []; - const res = await messagesApi.getAttachments(cid, 10); + // messageId filter: attachment list must show only this message's + // images, not the latest images from everyone in the channel. + const res = await messagesApi.getAttachments(cid, 10, undefined, id ?? ""); return res.data; }, ); diff --git a/services/frontend/src/lib/api/messages.ts b/services/frontend/src/lib/api/messages.ts index 7f364ba..26a4731 100644 --- a/services/frontend/src/lib/api/messages.ts +++ b/services/frontend/src/lib/api/messages.ts @@ -40,10 +40,16 @@ export const messagesApi = { ); }, - getAttachments: (channelId: string, limit?: number, cursor?: string) => { + getAttachments: ( + channelId: string, + limit?: number, + cursor?: string, + messageId?: string, + ) => { const params = new URLSearchParams(); if (limit) params.set("limit", String(limit)); if (cursor) params.set("cursor", cursor); + if (messageId) params.set("messageId", messageId); const qs = params.toString(); return api.get<{ data: AttachmentRecord[]; nextCursor: string | null }>( `/api/messages/${channelId}/attachments${qs ? `?${qs}` : ""}`,