Skip to content

feat: add message deletion#557

Merged
torlando-tech merged 4 commits intotorlando-tech:mainfrom
MatthieuTexier:feature/delete-message
Feb 27, 2026
Merged

feat: add message deletion#557
torlando-tech merged 4 commits intotorlando-tech:mainfrom
MatthieuTexier:feature/delete-message

Conversation

@MatthieuTexier
Copy link
Copy Markdown
Contributor

Summary

Add the ability to delete individual messages from conversations.

Changes

Data layer (ConversationRepository)

  • deleteMessage(messageId, conversationHash) — deletes the message and updates the conversation's lastMessage/lastMessageTimestamp to reflect the new most recent message (or clears them if no messages remain)

ViewModel (MessagingViewModel)

  • deleteMessage(messageId) — orchestrates deletion and invalidates cached reply previews that reference the deleted message, replacing them with a "Message deleted" placeholder

UI

  • Delete button (trash icon) added to MessageActionButtons in the long-press overlay
  • Confirmation dialog before deletion: "This message will be permanently deleted from this device."
  • ReplyPreviewBubble hides empty sender name for deleted message placeholders

Behavior

  • Local-only deletion — the message is not recalled from the recipient
  • Orphaned replies are handled gracefully: reply previews pointing to a deleted message show "Message deleted"
  • Tapping an orphaned reply preview is a no-op (no crash, no scroll)

Tests

  • deleteMessage invalidates reply preview cache for deleted message
  • deleteMessage without active conversation does not modify state

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Feb 25, 2026

Greptile Summary

This PR implements local message deletion with proper handling of orphaned reply previews. The implementation follows a clean architecture pattern from UI → ViewModel → Repository → DAO.

Key changes:

  • Data layer: ConversationRepository.deleteMessage() uses @Transaction to atomically delete the message and update the conversation's last message preview
  • ViewModel: Invalidates reply preview cache entries that reference deleted messages, replacing them with empty sender name and "Message deleted" text
  • UI: Delete button added to message long-press overlay with confirmation dialog
  • Graceful degradation: Orphaned reply previews display placeholder text, tapping them is a no-op (no crash)

Observations:

  • Local-only deletion (message not recalled from recipient) is clearly documented
  • Test coverage validates reply preview cache invalidation and null-safety when no active conversation
  • The PR also includes unrelated fixes: RNS Discovery import and LXST-kt submodule update

Confidence Score: 5/5

  • Safe to merge with minimal risk
  • Clean implementation with proper transaction handling, comprehensive test coverage for edge cases, and graceful handling of orphaned replies. No logic errors, race conditions, or security issues found.
  • No files require special attention

Important Files Changed

Filename Overview
data/src/main/java/com/lxmf/messenger/data/repository/ConversationRepository.kt Adds deleteMessage() with proper transaction handling and last message preview updates
app/src/main/java/com/lxmf/messenger/viewmodel/MessagingViewModel.kt Adds deleteMessage() that invalidates orphaned reply previews with "Message deleted" placeholder
app/src/main/java/com/lxmf/messenger/ui/screens/MessagingScreen.kt Adds delete confirmation dialog with proper state management and error-colored confirm button
app/src/main/java/com/lxmf/messenger/ui/components/ReactionComponents.kt Adds delete button (trash icon) to message action buttons with proper callback handling
app/src/main/java/com/lxmf/messenger/ui/components/ReplyComponents.kt Conditionally hides sender name when empty (for deleted message placeholders)
app/src/test/java/com/lxmf/messenger/viewmodel/MessagingViewModelTest.kt Adds tests for reply preview cache invalidation and no-op behavior without active conversation

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User long-presses message] --> B[MessageActionButtons displayed]
    B --> C[User taps Delete button]
    C --> D[Delete confirmation dialog shown]
    D --> E{User confirms?}
    E -->|Cancel| F[Dialog dismissed, return to chat]
    E -->|Delete| G[ViewModel.deleteMessage called]
    G --> H[ConversationRepository.deleteMessage]
    H --> I[Delete message from DB]
    I --> J[Get new last message]
    J --> K{Messages remain?}
    K -->|Yes| L[Update conversation lastMessage/timestamp]
    K -->|No| M[Clear conversation lastMessage/timestamp]
    L --> N[ViewModel updates reply preview cache]
    M --> N
    N --> O{Reply previews reference deleted message?}
    O -->|Yes| P[Replace with 'Message deleted' placeholder]
    O -->|No| Q[Cache unchanged]
    P --> R[UI updates, orphaned replies show placeholder]
    Q --> R
    R --> S[User taps orphaned reply preview]
    S --> T{Message exists in messagePositions?}
    T -->|No| U[No-op, no scroll]
    T -->|Yes| V[Scroll to message]
Loading

Last reviewed commit: 1fabe9f

@sentry
Copy link
Copy Markdown
Contributor

sentry bot commented Feb 25, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

- Add deleteMessage() to ConversationRepository with conversation
  lastMessage update after deletion
- Add deleteMessage() to MessagingViewModel
- Add onDelete callback to ReactionModeOverlay and MessageActionButtons
- Wire delete action with confirmation dialog in MessagingScreen
- Add unit tests for deleteMessage in MessagingViewModelTest
- Invalidate reply preview cache entries referencing deleted messages,
  replacing them with a "Message deleted" placeholder
- Hide empty sender name in ReplyPreviewBubble for deleted message
  placeholders (avoids blank line in the UI)
- deleteMessage test now asserts reply preview cache invalidation
- no-conversation test asserts state is unchanged
- Fixes detekt NoVerifyOnlyTests rule violations
… dialog

The delete button was calling handleDismiss() which started the overlay
exit animation (~3s), causing the confirmation dialog to auto-dismiss
when the animation completed. Now the dialog stays in the overlay
(same pattern as the full emoji picker) and exitReactionMode() is
called only after the user confirms or cancels.
@torlando-tech torlando-tech merged commit 4b8df40 into torlando-tech:main Feb 27, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants