Skip to content

Improve chat state handling and SDK integration - #13

Open
fathullahqiscus wants to merge 33 commits into
qiscus:masterfrom
fathullahqiscus:master
Open

Improve chat state handling and SDK integration#13
fathullahqiscus wants to merge 33 commits into
qiscus:masterfrom
fathullahqiscus:master

Conversation

@fathullahqiscus

Copy link
Copy Markdown

Summary

  • Fix unread counts when entering and leaving chat rooms
  • Prevent ChatProvider notifications while the Flutter widget
    tree is locked
  • Improve room lifecycle, realtime subscriptions, reconnection,
    and synchronization
  • Add message pagination, typing debounce, presence, upload
    progress, and message update handling
  • Refactor authentication and optional Firebase push-token
    lifecycle
  • Add safer SDK configuration through --dart-define
  • Improve Android and iOS platform configuration
  • Remove unused dependencies and runtime log files
  • Rewrite the README with setup, architecture, verification, and
    troubleshooting guidance

Validation

  • dart format --output=none --set-exit-if-changed lib test
  • flutter analyze
  • flutter test
  • Android debug build
  • iOS no-codesign build

All automated tests pass.

amed12 added 30 commits July 24, 2026 10:56
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.
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.
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