Improve chat state handling and SDK integration - #13
Open
fathullahqiscus wants to merge 33 commits into
Open
Conversation
Bump Gradle 8.4 -> 8.9, AGP 8.1.4 -> 8.6.0, and Kotlin 1.9.22 -> 2.1.0 to resolve Flutter's upcoming support deprecation warnings.
Expose onConnected/onReconnecting streams from QiscusService and use them in ChatProvider to re-subscribe the current room, closing the window where room-scoped MQTT events could be missed during a connection drop. Also tighten syncIntervalWhenConnected to 5s.
…oject Update minimum macOS deployment target from 10.11 to 10.15, migrate AppDelegate to @main entry point, and refresh Xcode project settings after upgrading Xcode.
Manifest had no INTERNET permission declared at all, relying on the debug build's implicit merge. Explicit permission is required for release builds and for the MQTT realtime connection to work reliably.
Qiscus SDK subscribes the new-message MQTT topic only once at login, gated by a 1s timeout that silently gives up on slow handshakes (common on Android, rare on Web) with no retry for the rest of the session. Add a forceSynchronize() wrapper around the SDK's public synchronize() REST call and invoke it 2s after login as a safety net for that race. Also stop hardcoding a client's production App ID in source: load it via --dart-define=QISCUS_APP_ID instead, and fail with a clear error if it's not set.
No app-lifecycle handling existed anywhere in the app, so a backgrounded MQTT connection killed by Android Doze/App Standby was never proactively recovered. Add a WidgetsBindingObserver at the app root that, on AppLifecycleState.resumed, forces a REST resync and re-subscribes the currently open room.
Align compileOptions/kotlinOptions across all android library subprojects to Java 11, matching the app module's toolchain.
The manual scroll button re-implemented scroll-to-bottom inline and every message list send triggered an unconditional scroll. Track the last message's uniqueId and only auto-scroll on genuinely new messages, reusing the existing _scrollToBottom for the button too.
Show the outgoing message immediately instead of waiting for the server round-trip, then reconcile it with the sent/failed result. Match on uniqueId instead of id when merging into the message list, since a locally generated pending message has no server id yet.
Chronological record of the root-cause analysis and fixes for the Klikdokter realtime delay ticket: sync interval, reconnect resubscribe, and the Android-specific MQTT subscribe race found in the SDK source.
…ation" This reverts commit f100d4e.
Keep working notes as an untracked local .txt file instead of committing them to the repo.
A burst of incoming messages (e.g. backfill after forceSynchronize or the shortened sync interval) fired one markAsRead call per message, on top of the SDK's own automatic per-message markAsDelivered call to the same endpoint. Debounce markAsRead to a single request for the latest message per 500ms window instead of one per message.
Rebuild only the FAB (via a ValueNotifier) instead of the whole message list frame on every scroll pixel, and only surface the button once the user has actually scrolled away from the bottom. Also jump (not animate) to bottom on first room load instead of animating.
Prevent .agent/, .agents/, and .claude/ working files from being tracked alongside the app source.
- Retain the newest pending read receipt and retry it on reconnect instead of silently dropping it when the debounced markAsRead call fails. - Guard enterChatRoom/leaveChatRoom with a room-session counter so stale async callbacks from a room the user already left can no longer mutate state for the newly entered room. - Track unread count via a revision counter so a slow getTotalUnreadCount response can't clobber a more recent value, and stop incrementing per-room unread counts for the room currently open. - leaveChatRoom is now async so it can flush the pending read receipt before the room is torn down; chat_room_screen fires it unawaited from dispose().
- Listen to the SDK 2.2.4 onRealtimeError stream so REST sync fallback failures and skipped channel registrations are no longer silently swallowed. - Retry markAsRead with backoff when the server responds 429/"rate limiter", since a burst of incoming messages can trip the same quota used by the SDK's automatic markAsDelivered calls even after debouncing on our side.
Keep CLAUDE.md/AGENT.md as local-only AI agent guidance, consistent with the existing .claude/ ignore rule.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ChatProvidernotifications while the Flutter widgettree is locked
and synchronization
progress, and message update handling
lifecycle
--dart-definetroubleshooting guidance
Validation
dart format --output=none --set-exit-if-changed lib testflutter analyzeflutter testAll automated tests pass.