feat(chat): edit message (#4)

Sender can edit their own text messages: a pencil action on the bubble loads the text into the
composer in an 'Editing' mode; saving updates the body, marks it 'edited', and pushes the change
live to the other side/tabs (chat-edited, mirroring delete). Adds messages.edited_at + editBody().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-02 17:45:20 +05:30
parent 9bc109528e
commit 51e206279b
4 changed files with 66 additions and 6 deletions
+20
View File
@@ -1282,6 +1282,26 @@ route('POST', '/api/messages/delete', async (req, res) => {
else { try { CHAT.pushToUser(m.recipient_id, evt); } catch (_) {} try { CHAT.pushToUser(u.id, evt); } catch (_) {} }
json(res, 200, { ok: true });
});
// Edit a message (sender only, text only) — updates the body + marks it edited, and pushes the
// change live to the other side / other tabs (mirrors the delete broadcast).
route('POST', '/api/messages/edit', async (req, res) => {
const u = currentUser(req);
if (!u) return json(res, 401, { error: 'unauthorized' });
const { id, body } = await readBody(req);
if (!id || typeof body !== 'string') return json(res, 400, { error: 'id and body required' });
const text = body.trim();
if (!text) return json(res, 400, { error: 'message cannot be empty' });
const m = R.messages.byId(id);
if (!m || m.team_id !== u.team_id) return json(res, 404, { error: 'not found' });
if (m.sender_id !== u.id) return json(res, 403, { error: 'you can only edit your own messages' });
if (m.deleted) return json(res, 400, { error: 'cannot edit a deleted message' });
R.messages.editBody(id, text);
const edited = R.messages.byId(id);
const evt = { type: 'chat-edited', id, body: text, edited_at: edited.edited_at, conversation_id: m.conversation_id || null };
if (m.conversation_id) { for (const mid of R.conversations.members(m.conversation_id)) { try { CHAT.pushToUser(mid, evt); } catch (_) {} } }
else { try { CHAT.pushToUser(m.recipient_id, evt); } catch (_) {} try { CHAT.pushToUser(u.id, evt); } catch (_) {} }
json(res, 200, { ok: true, edited_at: edited.edited_at });
});
// Favourite/unfavourite a conversation (per user). target = 'dm:<userId>' or 'group:<groupId>'.
route('POST', '/api/favorites', async (req, res) => {
const u = currentUser(req);