From 3e9951b3a8096a9c381d3d39e5bd51f0e2d3d042 Mon Sep 17 00:00:00 2001 From: maaz519 Date: Sat, 18 Jul 2026 00:45:03 +0530 Subject: [PATCH] feat: define MessagingAdapter contract --- packages/iios-messaging-ui/src/adapter.ts | 56 +++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 packages/iios-messaging-ui/src/adapter.ts diff --git a/packages/iios-messaging-ui/src/adapter.ts b/packages/iios-messaging-ui/src/adapter.ts new file mode 100644 index 0000000..1a13ff4 --- /dev/null +++ b/packages/iios-messaging-ui/src/adapter.ts @@ -0,0 +1,56 @@ +import type { + Attachment, + Conversation, + Membership, + Message, + MessageEvent, + SendOpts, + Unsubscribe, +} from './types'; + +/** + * The one seam of this SDK. Hosts implement this; the SDK renders it. + * + * Lifted from lynkeduppro-crm's MessengerData/ThreadData, which already survived + * two implementations (live data-door + mock) — the minimum real evidence that a + * seam is genuine rather than imagined. + * + * Optional methods degrade gracefully: the UI hides the reaction picker when + * `react` is absent, and the attach button when `upload` is absent. That is how one + * component set serves both a full CRM messenger and a stripped-down widget with no + * `mode` prop. + */ +export interface MessagingAdapter { + listConversations(): Promise; + + openThread(p: { participantIds: string[]; membership?: Membership; subject?: string }): Promise<{ threadId: string }>; + + history(threadId: string): Promise; + + send(threadId: string, content: string, opts?: SendOpts): Promise; + + /** Returns an unsubscribe fn. Implementations MUST be idempotent on repeat unsubscribe. */ + subscribe(threadId: string, cb: (e: MessageEvent) => void): Unsubscribe; + + sendTyping(threadId: string): void; + + markRead(threadId: string, messageId: string): Promise; + + /** + * The current user's actor id, or null if not yet known. + * + * MUST NOT be inferred from message history. The CRM's bug was exactly that: + * scanning for a sent message meant every message read as not-yours until you + * had spoken. Adapters derive this from auth/session. + */ + currentActorId(): string | null; + + /** Absent => the UI hides reactions entirely. */ + react?(threadId: string, messageId: string, emoji: string): Promise; + + /** Absent => the UI hides attachments. Storage/auth/limits are the host's concern. */ + upload?(file: File): Promise; + + /** Absent => treated as always connected (e.g. a pure-REST adapter). */ + isConnected?(): boolean; +}