feat(messenger): socket realtime in the CRM SDK adapter (parity with old messenger)
CrmMessagingAdapter is now hybrid: BFF for the conversation list, thread creation, and directory (server-side tenancy); the IIOS MessageSocket (delegated crm.messenger.realtime token) for everything live — history+join, send, typing, read receipts, and reaction annotations. Falls back to the 4s poll only when no socket is available. - messenger-sdk useRealtimeSocket() opens the MessageSocket from the BFF token and passes it to the adapter (adapter rebuilds live once connected) - socket path maps senderId (userId space) → actorId so 'mine'/seen work consistently, killing the actor-vs-user ambiguity in the poll fallback - reaction annotations tracked per message and re-emitted as full sets Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -5,8 +5,9 @@
|
||||
// UI + messaging logic lives in the SDK. Live path = the be-crm data door (CrmMessagingAdapter);
|
||||
// demo path = the SDK's own MockAdapter.
|
||||
|
||||
import { useMemo } from "react";
|
||||
import { useAppShell, useAuth } from "@abe-kap/appshell-sdk/react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useAppShell, useAuth, useQuery } from "@abe-kap/appshell-sdk/react";
|
||||
import { MessageSocket } from "@insignia/iios-kernel-client";
|
||||
import { MessagingProvider, Messenger as SdkMessenger, type MessagingAdapter } from "@insignia/iios-messaging-ui";
|
||||
import { MockAdapter } from "@insignia/iios-messaging-ui/adapters/mock";
|
||||
import "@insignia/iios-messaging-ui/styles.css";
|
||||
@@ -14,6 +15,27 @@ import { isShellConfigured } from "@/lib/appshell";
|
||||
import { CrmMessagingAdapter, type DataDoor } from "@/lib/crm-messaging-adapter";
|
||||
import { PageHead } from "./ui";
|
||||
|
||||
interface RealtimeDTO { url: string; audience: string; token?: string }
|
||||
|
||||
/** Open the IIOS message socket with the delegated token the BFF mints (crm.messenger.realtime). */
|
||||
function useRealtimeSocket(): MessageSocket | null {
|
||||
const rt = useQuery<RealtimeDTO>("crm.messenger.realtime", {});
|
||||
const [socket, setSocket] = useState<MessageSocket | null>(null);
|
||||
const url = rt.data?.url;
|
||||
const token = rt.data?.token;
|
||||
useEffect(() => {
|
||||
if (!url || !token) return;
|
||||
const s = new MessageSocket({ serviceUrl: url, token, autoConnect: false });
|
||||
s.connect();
|
||||
setSocket(s);
|
||||
return () => {
|
||||
s.disconnect();
|
||||
setSocket(null);
|
||||
};
|
||||
}, [url, token]);
|
||||
return socket;
|
||||
}
|
||||
|
||||
const SHELL = isShellConfigured();
|
||||
|
||||
export function MessengerSdk() {
|
||||
@@ -59,9 +81,11 @@ function DemoHost() {
|
||||
function LiveHost() {
|
||||
const { sdk } = useAppShell();
|
||||
const { user } = useAuth();
|
||||
const socket = useRealtimeSocket();
|
||||
// Rebuilds once the socket connects: the first adapter (no socket) polls; the second runs live.
|
||||
const adapter = useMemo<MessagingAdapter | null>(
|
||||
() => (user?.id ? new CrmMessagingAdapter(sdk as unknown as DataDoor, user.id) : null),
|
||||
[sdk, user?.id],
|
||||
() => (user?.id ? new CrmMessagingAdapter(sdk as unknown as DataDoor, user.id, socket ?? undefined) : null),
|
||||
[sdk, user?.id, socket],
|
||||
);
|
||||
if (!adapter) return <div className="miu-empty">Loading…</div>;
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user