utd_chat_kit 0.9.2
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
chatcapability (persistent messaging + history + receipts), billed separately fromsignaling. The session mint now surfaces it on the client:UTDChatClient.chatEnabled— aValueListenable<bool?>set onconnect().nullbefore connect / on an older engine that omits the flag ("unknown, let the server decide"). This is the proactive pre-check; the existingmessagingUnavailableremains the reactive, after-a-forbidden-send signal.UTDChatClient.planLimits— a nullableUTDPlanLimits(historyDays,maxGroupMembers,maxFriends,maxChatrooms) so the UI can enforce caps client-side for UX (e.g. cap the group-member picker atmaxGroupMembers) 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-exportedUTDPlanLimits. - Billing is MAU-based — no per-message charge. Bumped
utd_signalingto0.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 initsflutter_local_notifications, requests notification permission (iOS / Android 13+), resolves the FCM token (on iOS it callsgetAPNSToken()first), registers it, and re-registers ononTokenRefresh. On dispose/logout it unregisters. Every step is best-effort and never breaksconnect(). - Receive surfaces —
FirebaseMessaging.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), andgetInitialMessage()(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 router —
UTDNotificationRouterparses{type:'message', conversation_id, ...}payloads into aUTDChatDeepLinkand 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.notificationRouterexposes it. - Foreground suppression —
UTDActiveConversationtracks the on-screen thread (UTDChatScreenenters/leaves it) so a push for the conversation the user is reading does not produce a redundant banner. - Config flag —
UTDPushConfig(onUTDChatConfig.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_coreconfig:GoogleService-Info.plist/google-services.jsonvia the Firebase CLI / FlutterFire), registerutdChatFirebaseBackgroundHandlerviaFirebaseMessaging.onBackgroundMessagebeforerunApp, declare push entitlements/permissions, and supply aUTDNotificationRouterhandler 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 aUTDUploadGrant;putBytes()PUTs the bytes to the presigned R2 URL with progress + cancel support. A 503 (storage not configured) surfaces as a clearUTDStreamExceptionwithcode == UTDErrorCode.uploadsUnavailable. Client-side 100 MB guard. - Upload controller —
UTDUploadControllerorchestrates pick → optional image compression → presign → PUT (progress/cancel) → optimistic send via the outbox. Exposes active uploads astasks(aChangeNotifier). - Pickers — photo (gallery), camera capture, video (gallery), and arbitrary
file. Content-type is inferred via
mime; images are compressed withflutter_image_compress. - Optimistic send —
Outbox.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 themessage_idexactly as the text path does. Sender-side echo collapsing now matches media echoes by (type + object URL) since media content is usually empty. - Rendering —
UTDMediaMessagebubbles: image thumbnail with full-screen pinch-zoom viewer (tap to open), file/audio/video tiles with name + size. NewUTDUploadProgressStripshows in-flight uploads with progress + cancel. The composer gains an attach (paperclip) button + a photo/camera/video/file sheet. - Cache — media
metadatanow persists in Drift (schema v3, additivesync_outbox.metadatacolumn) and is rehydrated by the history pull (_upsertServerMessageno longer dropsmetadata), so media bubbles render from cache offline.UTDMediaAttachmentparsesmetadata.urlfor 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-relatedUTDChatStrings.UTDApi.uploadsexposes 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).