client: Persist draft messages optimistically and on draft events - #6625
client: Persist draft messages optimistically and on draft events#6625gpunto wants to merge 3 commits into
Conversation
PR checklist ✅All required conditions are satisfied:
🎉 Great job! This PR is ready for review. |
SDK Size Comparison 📏
|
d293859 to
536adab
Compare
4ec056e to
5241e1d
Compare
WalkthroughDraft creation and deletion now notify plugins before results, update local state and offline storage optimistically, and support channel or thread draft deletion. Draft events update offline storage. Tests cover callbacks, persistence, event handling, and isolation. ChangesDraft message flow
Estimated code review effort: 3 (Moderate) | ~25 minutes Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/plugin/listeners/DraftMessageListener.kt (1)
40-44: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd default implementations for
DraftMessageListener.The existing direct listeners are already updated, but adding no-op bodies to the new
DraftMessageListenermethods still protects external Kotlin implementations that extend only this interface.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@stream-chat-android-client/src/main/java/io/getstream/chat/android/client/plugin/listeners/DraftMessageListener.kt` around lines 40 - 44, Add no-op default method bodies to the methods declared by DraftMessageListener, including onCreateDraftMessageRequest, so external Kotlin implementations can implement only the callbacks they need. Preserve the existing method signatures and behavior of direct listeners.Source: Coding guidelines
stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/event/handler/internal/EventHandlerSequentialTest.kt (1)
356-375: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a thread draft state assertion.
This test verifies repository routing only. Seed the thread draft in
MutableGlobalState, then assert that the thread draft state is empty afterhandleEvents. This validates the local-state half of thread deletion.As per coding guidelines, “add or refresh tests for changed behavior.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/event/handler/internal/EventHandlerSequentialTest.kt` around lines 356 - 375, Update the test around EventHandlerSequentialTest’s thread DraftMessageDeletedEvent case to seed the thread draft in MutableGlobalState before handling the event, then assert that the corresponding thread draft state is empty afterward. Retain the existing repository deleteDraftMessage verification so both local-state removal and repository routing are covered.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/plugin/listener/internal/DraftMessageListenerState.kt`:
- Around line 66-67: The draft create-result handlers can restore a draft after
a later delete; enforce per-draft mutation ordering or deletion tombstones
before applying successful create results. Update DraftMessageListenerState.kt
lines 66-67 and DraftMessageListenerDatabase.kt lines 64-65 so server drafts are
applied or persisted only when no later delete exists, and add deterministic
coverage for create, delete, then delayed successful create.
In
`@stream-chat-android-client/src/main/java/io/getstream/chat/android/client/plugin/listeners/DraftMessageListener.kt`:
- Around line 33-39: Correct the KDoc for the delete operation near
DraftMessageListener by replacing update-related wording with deletion-specific
descriptions, and remove duplicated “the” wording in the channel ID
documentation. Ensure the parameter and operation descriptions consistently
describe deleting a draft message.
---
Nitpick comments:
In
`@stream-chat-android-client/src/main/java/io/getstream/chat/android/client/plugin/listeners/DraftMessageListener.kt`:
- Around line 40-44: Add no-op default method bodies to the methods declared by
DraftMessageListener, including onCreateDraftMessageRequest, so external Kotlin
implementations can implement only the callbacks they need. Preserve the
existing method signatures and behavior of direct listeners.
In
`@stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/event/handler/internal/EventHandlerSequentialTest.kt`:
- Around line 356-375: Update the test around EventHandlerSequentialTest’s
thread DraftMessageDeletedEvent case to seed the thread draft in
MutableGlobalState before handling the event, then assert that the corresponding
thread draft state is empty afterward. Retain the existing repository
deleteDraftMessage verification so both local-state removal and repository
routing are covered.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: cb36cdad-1957-4434-b80c-8fc36492f13d
📒 Files selected for processing (17)
stream-chat-android-client-test/src/main/java/io/getstream/chat/android/client/test/Mother.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/ChatClient.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/offline/plugin/listener/internal/DraftMessageListenerDatabase.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/offline/repository/domain/message/internal/DatabaseMessageRepository.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/offline/repository/domain/message/internal/MessageDao.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/event/handler/internal/EventHandlerSequential.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/plugin/listener/internal/DraftMessageListenerState.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/persistance/repository/MessageRepository.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/persistance/repository/noop/NoOpMessageRepository.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/plugin/Plugin.ktstream-chat-android-client/src/main/java/io/getstream/chat/android/client/plugin/listeners/DraftMessageListener.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/ChatClientDraftsApiTests.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/offline/plugin/listener/internal/DraftMessageListenerDatabaseTest.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/offline/repository/DraftMessageRepositoryIntegrationTest.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/channel/controller/attachment/UploadAttachmentsIntegrationTests.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/event/handler/internal/EventHandlerSequentialTest.ktstream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/plugin/listener/internal/DraftMessageListenerStateTest.kt
|


Goal
Stop draft messages from being lost when a request does not complete, and from reappearing after they have been deleted.
Closes AND-1373
Contributes to #6388
Implementation
Aligns three gaps with the iOS SDK (
DraftMessagesRepository,DraftUpdaterMiddleware):onCreateDraftMessageRequest/onDeleteDraftMessagesRequestlistener hooks, dispatched fromdoOnStart, so the local write no longer waits for the response and survives a process death mid-request.draft.updated/draft.deletedonly touchedMutableGlobalState, so a draft deleted on another device left its Room row behind andSyncManagerre-pushed it on every reconnect.draft.deletedhas an empty message id, so deletion keys on cid (or parentId for threads), which needed a new DAO query.No public API change: neither
DraftMessageListenernorMessageRepositoryis in the API dump.Testing
Two emulators against the demo backend:
develop, now stays gone.Unit tests cover both precommit paths and the channel and thread cases for
draft.deleted.Summary by CodeRabbit