From 60ebefebdcb75a59574877f5d5ba0837dae96cd3 Mon Sep 17 00:00:00 2001 From: Abhinav Raut Date: Wed, 8 Jul 2026 01:00:24 +0530 Subject: [PATCH] fix edge cases in private note soft-delete --- .../conversation/message/MessageBubble.vue | 4 +++- frontend/apps/main/src/stores/conversation.js | 3 ++- internal/conversation/message_delete.go | 20 +++---------------- internal/conversation/queries.sql | 9 +++++++-- 4 files changed, 15 insertions(+), 21 deletions(-) diff --git a/frontend/apps/main/src/features/conversation/message/MessageBubble.vue b/frontend/apps/main/src/features/conversation/message/MessageBubble.vue index 7109cd9c..07804d61 100644 --- a/frontend/apps/main/src/features/conversation/message/MessageBubble.vue +++ b/frontend/apps/main/src/features/conversation/message/MessageBubble.vue @@ -317,7 +317,9 @@ const userStore = useUserStore() const alertOpen = ref(false) const deleteNote = () => { - convStore.deleteMessage(convStore.current?.uuid, props.message.uuid) + const conversationUUID = convStore.current?.uuid + if (!conversationUUID) return + convStore.deleteMessage(conversationUUID, props.message.uuid) alertOpen.value = false } diff --git a/frontend/apps/main/src/stores/conversation.js b/frontend/apps/main/src/stores/conversation.js index a83d6148..ec13ac1c 100644 --- a/frontend/apps/main/src/stores/conversation.js +++ b/frontend/apps/main/src/stores/conversation.js @@ -515,10 +515,11 @@ export const useConversationStore = defineStore('conversation', () => { try { const resp = await api.deleteMessage(conversationUUID, messageUUID) const deletedText = resp.data.data.content + const existing = messages.data.getAllPagesMessages(conversationUUID).find(m => m.uuid === messageUUID) messages.data.updateMessage(conversationUUID, messageUUID, { content: deletedText, text_content: deletedText, - meta: { deleted_at: new Date().toISOString() } + meta: { ...(existing?.meta || {}), deleted_at: new Date().toISOString() } }) incrementMessageVersion() } catch (error) { diff --git a/internal/conversation/message_delete.go b/internal/conversation/message_delete.go index 9b350e0e..32aa0a43 100644 --- a/internal/conversation/message_delete.go +++ b/internal/conversation/message_delete.go @@ -12,12 +12,9 @@ import ( func (m *Manager) DeletePrivateMessage(conversationUUID, messageUUID string) (string, error) { m.lo.Info("deleting private note", "conversation_uuid", conversationUUID, "message_uuid", messageUUID) - var res struct { - MessageID int `db:"message_id"` - PreviewUpdated bool `db:"preview_updated"` - } + var previewUpdated bool deletedPreview := m.i18n.T("conversation.privateNoteDeleted") - if err := m.q.DeletePrivateMessage.Get(&res, messageUUID, conversationUUID, deletedPreview); err != nil { + if err := m.q.DeletePrivateMessage.Get(&previewUpdated, messageUUID, conversationUUID, deletedPreview); err != nil { if errors.Is(err, sql.ErrNoRows) { return "", envelope.NewError(envelope.NotFoundError, m.i18n.Ts("globals.messages.notFound", "name", m.i18n.Ts("globals.terms.message")), nil) } @@ -30,19 +27,8 @@ func (m *Manager) DeletePrivateMessage(conversationUUID, messageUUID string) (st "text_content": deletedPreview, "meta": map[string]any{"deleted_at": time.Now()}, }) - if res.PreviewUpdated { + if previewUpdated { m.BroadcastConversationUpdate(conversationUUID, map[string]any{"last_message": deletedPreview}) } - - media, err := m.mediaStore.GetByModel(res.MessageID, "messages") - if err != nil { - m.lo.Error("error fetching private note media to unlink", "message_id", res.MessageID, "error", err) - return deletedPreview, nil - } - for _, md := range media { - if err := m.mediaStore.Attach(md.ID, "messages", 0); err != nil { - m.lo.Error("error unlinking private note media", "media_id", md.ID, "error", err) - } - } return deletedPreview, nil } diff --git a/internal/conversation/queries.sql b/internal/conversation/queries.sql index 4a0da72d..9e0bdeb1 100644 --- a/internal/conversation/queries.sql +++ b/internal/conversation/queries.sql @@ -613,13 +613,18 @@ END; WITH deleted AS ( UPDATE conversation_messages SET content = $3, text_content = $3, updated_at = NOW(), - meta = meta || jsonb_build_object('deleted_at', NOW()) + meta = COALESCE(meta, '{}'::jsonb) || jsonb_build_object('deleted_at', NOW()) WHERE uuid = $1 AND private = true AND meta->>'deleted_at' IS NULL AND conversation_id = (SELECT id FROM conversations WHERE uuid = $2) RETURNING id, conversation_id, created_at ), +media_unlink AS ( + UPDATE media SET model_id = 0 + FROM deleted d + WHERE media.model_type = 'messages' AND media.model_id = d.id +), preview AS ( UPDATE conversations c SET last_message = $3, updated_at = NOW() @@ -632,7 +637,7 @@ preview AS ( ) RETURNING c.id ) -SELECT d.id AS message_id, EXISTS (SELECT 1 FROM preview) AS preview_updated +SELECT EXISTS (SELECT 1 FROM preview) AS preview_updated FROM deleted d; -- name: get-message-source-ids