utd_chat_kit 0.9.2 copy "utd_chat_kit: ^0.9.2" to clipboard
utd_chat_kit: ^0.9.2 copied to clipboard

WhatsApp-style 1:1 & group chat for Flutter — independent UTD-Stream kit.

0.9.2 #

Chat capability + plan limits surfacing (additive, non-breaking).

  • Chat is the paid chat capability (persistent messaging + history + receipts), billed separately from signaling. The session mint now surfaces it on the client:
    • UTDChatClient.chatEnabled — a ValueListenable<bool?> set on connect(). null before connect / on an older engine that omits the flag ("unknown, let the server decide"). This is the proactive pre-check; the existing messagingUnavailable remains the reactive, after-a-forbidden-send signal.
    • UTDChatClient.planLimits — a nullable UTDPlanLimits (historyDays, maxGroupMembers, maxFriends, maxChatrooms) so the UI can enforce caps client-side for UX (e.g. cap the group-member picker at maxGroupMembers) and show history-retention info (historyDays). Advisory only — the engine stays authoritative. Absent flags never hard-break the kit.
  • Public API additions (additive, non-breaking): UTDChatClient.chatEnabled, UTDChatClient.planLimits, and the re-exported UTDPlanLimits.
  • Billing is MAU-based — no per-message charge. Bumped utd_signaling to 0.1.1.

0.9.0 #

Background message push (FCM) + deep-linking.

  • Push token registration — new UTDApi.registerPushToken() / unregisterPushToken() (POST/DELETE /api/v1/devices/push-token, Bearer). The engine binds the token to the bearer's identity.
  • Push bridge — new PushBridge (lib/src/push/push_bridge.dart) wires Firebase Cloud Messaging: on connect it inits flutter_local_notifications, requests notification permission (iOS / Android 13+), resolves the FCM token (on iOS it calls getAPNSToken() first), registers it, and re-registers on onTokenRefresh. On dispose/logout it unregisters. Every step is best-effort and never breaks connect().
  • Receive surfacesFirebaseMessaging.onMessage (foreground: shows a local-notification banner UNLESS the user is currently viewing that conversation, in which case it's suppressed), onMessageOpenedApp (background tap → deep-link), and getInitialMessage() (cold start → deep-link). A top-level @pragma('vm:entry-point') background handler (utdChatFirebaseBackgroundHandler) renders a banner for data-only messages delivered while the app is terminated/backgrounded.
  • Deep-link routerUTDNotificationRouter parses {type:'message', conversation_id, ...} payloads into a UTDChatDeepLink and routes it to a host-supplied navigation handler, GUARDED behind chat-client readiness: a deep-link that fires before the client is connected / before the host registers its handler is QUEUED (most-recent wins) and flushed once both are ready. UTDChatClient.notificationRouter exposes it.
  • Foreground suppressionUTDActiveConversation tracks the on-screen thread (UTDChatScreen enters/leaves it) so a push for the conversation the user is reading does not produce a redundant banner.
  • Config flagUTDPushConfig (on UTDChatConfig.push, default enabled) toggles all FCM wiring, the permission prompt, the Android channel id/name, and an optional fixed foreground-banner title.
  • Public API additions (additive, non-breaking): PushBridge, utdChatFirebaseBackgroundHandler, UTDPushConfig, UTDNotificationRouter, UTDChatDeepLink, UTDChatDeepLinkHandler, UTDActiveConversation, UTDApi.registerPushToken/unregisterPushToken, UTDChatClient.notificationRouter, UTDChatConfig.push.
  • Deps: firebase_core, firebase_messaging, flutter_local_notifications.

Device QA pending — push, permission prompts, APNs/FCM token resolution, foreground/background/cold-start delivery, and notification-tap deep-linking CANNOT be runtime-verified without real devices + a provisioned engine, so this ships as 0.9.0. The 1.0.0 release is gated on device QA (see the checklist in the README).

Host config required — the app must add Firebase (firebase_core config: GoogleService-Info.plist / google-services.json via the Firebase CLI / FlutterFire), register utdChatFirebaseBackgroundHandler via FirebaseMessaging.onBackgroundMessage before runApp, declare push entitlements/permissions, and supply a UTDNotificationRouter handler for navigation. The engine's FCM/APNs env must be provisioned — until then registration succeeds but no pushes are sent (server-side gate). See README.

0.6.0 #

Media attachments via the engine R2 presign endpoint.

  • Upload pipeline — new UTDUploadApi (lib/src/api/upload_api.dart): presign() (POST /api/v1/signal/uploads, Bearer) returns a UTDUploadGrant; putBytes() PUTs the bytes to the presigned R2 URL with progress + cancel support. A 503 (storage not configured) surfaces as a clear UTDStreamException with code == UTDErrorCode.uploadsUnavailable. Client-side 100 MB guard.
  • Upload controllerUTDUploadController orchestrates pick → optional image compression → presign → PUT (progress/cancel) → optimistic send via the outbox. Exposes active uploads as tasks (a ChangeNotifier).
  • Pickers — photo (gallery), camera capture, video (gallery), and arbitrary file. Content-type is inferred via mime; images are compressed with flutter_image_compress.
  • Optimistic sendOutbox.enqueueMedia() writes the local row + outbox row carrying the message metadata ({url, name, size, mime, …}), so a media send survives an offline resend. The engine ack stamps the message_id exactly as the text path does. Sender-side echo collapsing now matches media echoes by (type + object URL) since media content is usually empty.
  • RenderingUTDMediaMessage bubbles: image thumbnail with full-screen pinch-zoom viewer (tap to open), file/audio/video tiles with name + size. New UTDUploadProgressStrip shows in-flight uploads with progress + cancel. The composer gains an attach (paperclip) button + a photo/camera/video/file sheet.
  • Cache — media metadata now persists in Drift (schema v3, additive sync_outbox.metadata column) and is rehydrated by the history pull (_upsertServerMessage no longer drops metadata), so media bubbles render from cache offline. UTDMediaAttachment parses metadata.url for the UI.
  • Public API additions (additive, non-breaking): UTDUploadApi, UTDUploadGrant, UTDMediaKind, kUTDMaxUploadBytes, UTDUploadController, UTDUploadTask, UTDUploadStatus, UTDMediaAttachment, UTDMediaMessage, UTDUploadProgressStrip, UTDComposer.onAttach, UTDChatMessage.attachment/.isMedia, UTDErrorCode.uploadsUnavailable, and the media-related UTDChatStrings. UTDApi.uploads exposes the upload API.
  • Deps: image_picker, file_picker, flutter_image_compress, mime, path.

Device QA pending — pickers, camera, and image compression cannot be runtime-verified without real devices; this ships as 0.6.0. The 1.0.0 release is gated on device QA (pickers/camera/upload) and push notifications.

Host config required — the app must declare platform permissions (camera/photos) in its own Info.plist / AndroidManifest.xml (see README). The engine's R2/storage env must be provisioned, or presign returns 503.

0.4.0 #

  • Ephemeral typing indicators wired to the engine typing relay (debounced typing.start/typing.stop; 1:1 and group "is typing" UI). Never persisted.

0.3.0 #

  • Groups, roles (owner/admin/member), synthesized system messages for group-lifecycle events, and presence (online / last-seen) on tiles + the chat header. Drift schema v2 (group membership cache).

0.1.0 #

  • Local-first, text-only 1:1 chat: Drift store as source of truth, a sync engine (REST history + signalling WebSocket live pushes), an optimistic outbox, and a themeable default UI (conversation list + chat thread).