convokit_flutter 0.8.0 copy "convokit_flutter: ^0.8.0" to clipboard
convokit_flutter: ^0.8.0 copied to clipboard

Flutter SDK for ConvoKit - real-time messaging integration.

0.8.0 #

  • Add ConvoKit.editMessage(messageId, {required String? text, required int revision}) (PATCH /api/v1/messages/:id/own) returning the updated Message, and ConvoKit.deleteMessage(messageId) (DELETE /api/v1/messages/:id/own) returning void. Both are author-tier calls for the current user's own messages, separate from the administrative PATCH/DELETE /api/v1/messages/:id your server calls with the client secret. The edit body always carries both keys, {"text": ..., "revision": n}, with a null text serialised as JSON null so a caption can be cleared on a message that has attachments (a text-only message cannot be emptied: a plain 400 ConvoKitValidationException without a code; only a malformed text or revision body is a 400 with code INVALID_ARGUMENT); attachments are never changed by an edit. revision must be an integer in 0..2147483647 and throws ArgumentError before any request otherwise.
  • Add Message.revision (trailing named constructor parameter, default 0) and the derived Message.isEdited (revision > 0). The backend sets 0 at creation and increments by one on every content edit, author or administrative (media-only administrative edits included); the value is carried by REST rows, inbox previews (InboxSummary.latestMessage) and Realtime UPDATE row images. updatedAt is never the edited signal. The parser treats a missing or null revision (a 0.7 backend) as 0 and fails on a present but malformed value like every other model field. Message keeps identity equality; existing literals are unchanged.
  • Conflicts: pass the revision of the row the user was shown. When it no longer matches, the backend answers 409 with code REVISION_CONFLICT (ConvoKitValidationException with statusCode == 409); reload the row with getMessage and retry with its revision. A message that does not exist, was deleted, or belongs to a room the caller is not an active member of answers 404 with code MESSAGE_NOT_FOUND (ConvoKitNotFoundException) for both calls; another member's message is a 403. Consumers that merge rows for one id should let the higher revision win and fall back to updatedAt ?? createdAt only for equal revisions.
  • Deleting is unconditional and cannot be undone: other devices receive onMessageDeleted and the inbox preview moves to the previous surviving message. Files already received or downloaded cannot be retracted; stored files are reclaimed by the existing user or app deletion cleanup, not by this call. Requires the coordinated 0.8 backend release: an older backend answers the /own routes with an unmatched-route 404 without a code, which must not be treated as "message gone".

0.7.0 #

  • Add ConvoKit.markConversationUnread(conversationId) (POST /api/v1/conversations/:id/unread) returning a ConversationPrivateState (conversationId, unreadMarkedAt, privateStateVersion), and ConvoKit.clearConversationUnread(conversationId, {int? ifVersion}) (DELETE .../unread[?privateStateVersion=]) returning a ClearUnreadResult (the same state plus cleared). The marker is private to the caller's own membership; every mark bumps the version, also a repeat mark. A clear whose ifVersion no longer matches, or with nothing marked, is a 200 with cleared == false, not an error. ifVersion must be an integer in 0..2147483647 and throws ArgumentError before any request otherwise.
  • Add privateStateVersion to ConvoKit.markConversationRead, serialised only when given: the acknowledgement clears the caller's marker only when the sent version equals the current one; the position rule and the exact-204 contract are unchanged, and the legacy body never clears. A version-only call clears the marker in an empty room; in a non-empty one it also acknowledges through the newest stored message, like the legacy form. Values outside 0..2147483647 throw ArgumentError.
  • Add ConversationMembership (role, lastReadAt, readPosition, unreadMarkedAt, privateStateVersion; value equality) and the nullable Conversation.membership, parsed from the self-only membership sibling of GET /api/v1/conversations/:id. Conversation.fromJson accepts an optional named membership; list responses and existing literals are unchanged and the field is null against a 0.6 backend. Capture membership?.privateStateVersion when a room opens and send it with every acknowledgement of that open.
  • Add isUnread, unreadMarkedAt and privateStateVersion to InboxSummary and InboxEntry (trailing named constructor parameters; isUnread is derived as unreadCount > 0 || unreadCountCapped || unreadMarkedAt != null when omitted, unreadMarkedAt defaults to null and privateStateVersion to 0, so existing literals keep their badges). The parser applies the same defaults for a 0.6 backend and fails on a present but malformed value. unreadCount is never inflated by a marker: render a numberless dot when isUnread is true with a zero count. Value equality, hashCode and toString cover the new fields.
  • onInboxActivity also fires after the caller's own marker changes (a mark, a clear, or an acknowledgement that clears it), so other devices refetch listInbox. No new realtime event. Requires the coordinated 0.7 backend release; an older backend answers the /unread routes with 404.

0.6.0 #

  • Add ConvoKit.listInbox({limit = 30, cursor, archived = false}) returning an InboxPage from GET /api/v1/inbox: entries ordered by activity time then conversation id (both descending) and an opaque nextCursor (null on the last page). limit must be an integer in 1..100 and throws ArgumentError before any request otherwise; cursor is omitted from the query when null. A rejected cursor surfaces as ConvoKitValidationException with code == 'INVALID_CURSOR'. getConversations (creation order, offset paging) is unchanged.
  • Add InboxEntry (conversation plus summary) and InboxSummary (latestMessage in the getMessages row shape or null, unreadCount, unreadCountCapped, the caller's readPosition and lastReadAt, and activityAt), parsed from camelCase or snake_case with value equality. The entry's conversation.participants is bounded to ten members and the preview's media to four items. Unread counts follow the readThrough rule (messages from others after the caller's position; own messages never count) over a 1,000-message window, with unreadCountCapped marking a lower bound.
  • Add ConvoKit.realtime.onInboxActivity(appId): an empty private app-hub signal after a message insert or edit and after a read-position advance. Unlike onInboxChanged it is never synthesised on a verified join or rejoin; keep listening to onInboxChanged for structural changes and reconnect reconciliation. Requires the coordinated backend release; an older backend answers listInbox with 404 and never broadcasts the event.

0.5.0 #

  • Add throughMessageId to markConversationRead so a read acknowledges a concrete message. The backend stores that message's (createdAt, id) cursor as a monotonic ReadPosition, separate from the lastReadAt acknowledgement time. The request body is now { "throughMessageId": ... } or {}, the conversation id path segment is percent-encoded, and the exact-204 success check is unchanged. Requires the coordinated backend release; an older backend ignores the target.
  • Add ReadPosition (messageId, UTC createdAt, covers(message), value equality), Participant.readPosition and ReadEvent.readPosition (both nullable, parsed from camelCase or snake_case), and the top-level readThrough(message, readPosition:, lastReadAt:) rule shared by every ConvoKit client: position when present, otherwise lastReadAt >= createdAt.
  • Add optional ConvoKitException.code parsed from the error body, for example MESSAGE_NOT_FOUND on a targeted read whose message is gone or foreign. ConvoKitSessionException.code is unchanged.
  • Mixed fleet: precise receipts need sender and reader on 0.5. 0.4 readers keep timestamp semantics and still parse the additive payloads.

0.4.0 #

  • Add private onInboxChanged(appId) signals on mutations and verified subscription/reconnect, sharing the app presence channel.
  • Replace the unimplemented onConversationUpdate() placeholder with that explicit inbox API. This requires the matching backend/UI release.
  • Generate and preserve clientMessageId through REST, history and live rows; expose createClientMessageId() for custom optimistic UIs and accept the backend's HTTP 200 result for an identical retry.

0.3.1 #

  • Include the minimum-Flutter test lint correction. The 0.3.0 publishing run stopped at validation; its tag is retained without moving or replacing it.
  • Automatically discover rotating private Broadcast topics and rejoin remaining room/app listeners after membership removal or reconnect. Requires the matching backend cutover; no retired-topic fallback or customer Supabase configuration.

  • Preserve optional server Message.updatedAt revisions for consistent reconciliation of REST responses and Realtime edits. Creation timestamps and device-local display behavior are unchanged.

  • Add value-based beforeCreatedAt / beforeId history cursors so pagination stays stable when rows are inserted or the previous page's last row is deleted.

  • Own a dedicated Supabase client per connected user. Do not initialize or modify the host app's global Supabase instance.

  • Coordinate proactive and 401-triggered renewal of both user and Realtime tokens, with single-flight refresh, bounded backoff and independent expiry.

  • Reject pending work on logout/reconfiguration/user switching; bind all upload stages to their initiating session and never retry storage PUTs implicitly.

  • Share private room channels across typing/read listeners and release channels when their final listener cancels. Refresh credentials after delayed joins.

  • Serialize channel cleanup/re-entry and recover unexpected provider channel closes without losing listeners; discard paused events after session retirement.

  • Keep Realtime authentication explicitly owned by ConvoKit, avoiding a provider async-callback bug that resends pending joins with empty references. Preserve the Supabase HTTP user-token callback and never substitute a project key.

  • Expose sanitized session/Realtime errors and per-topic connection events for reconnect reconciliation. HTTP 403/5xx/network failures do not replay writes.

  • Breaking: replace MessageChangeType.delete with the separate onMessageDeleted() / MessageDeletedEvent(id, conversationId) contract. Receive only the backend's private ID-only deletion broadcast, validate its room, and share typing/read channel lifecycle, errors and session disposal. Never subscribe to raw Postgres DELETE or fabricate old Message records. Explicit message deletion requires the coordinated backend release; missed events need REST reconciliation; cascade inbox discovery requires refresh/reopen.

  • Require supabase_flutter >=2.14.0 <3.0.0, Flutter >=3.19, and Dart >=3.3. The former dependency floor admitted releases missing the SDK's auth APIs.

  • Validate minimum and current Flutter/dependency resolutions in CI and before publishing, including loopback tests using the actual Supabase WebSocket client. Live security smoke tests still require explicit staging fixtures.

0.2.0 #

  • Use the Supabase publishable key discovered from the ConvoKit token service.
  • Authenticate Realtime before joining private message, typing, read-receipt, and presence channels.
  • Keep connectUser, token-provider, and managed API endpoint interfaces unchanged.

0.0.4 #

  • Use the managed https://api.convokit.app endpoint by default.
  • Keep backendUrl as an optional override for local testing and self-hosting.

0.0.3 #

  • Breaking: RealtimeService.onMessage() now returns Stream<MessageEvent> instead of Stream<Message>, and delivers UPDATE/DELETE events on Message in addition to INSERT. Switch on MessageEvent.type and read MessageEvent.message.
  • Added automatic session keep-alive: the SDK refreshes the session proactively ahead of token expiry, and reactively (refresh-and-retry once) on any REST call that comes back 401.
  • Added a typed error hierarchy: ConvoKitException is now a base type with ConvoKitAuthException, ConvoKitNotFoundException, ConvoKitValidationException, ConvoKitServerException, and ConvoKitNetworkException subtypes.
  • Added ConvoKit.getUser() / ConvoKit.getUsers() and a new AppUser model for looking up app user profiles, including lastSeenAt.
  • Added ConvoKit.getMessage() to fetch a single message by id.

0.0.2 #

  • Finalize R2 uploads with the backend before returning media URLs.
  • Validate signed-upload object keys and surface upload-completion failures.

0.0.1 #

  • Initial release of the ConvoKit Flutter SDK.
  • Added SDK configuration, user connection, and token exchange helpers.
  • Added conversation, message, realtime, presence, read receipt, typing, and media upload APIs.
0
likes
0
points
699
downloads

Publisher

verified publisherconvokit.app

Weekly Downloads

Flutter SDK for ConvoKit - real-time messaging integration.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, http, supabase_flutter

More

Packages that depend on convokit_flutter