From b3eb027071311124c840458e0e5f948b87245e7e Mon Sep 17 00:00:00 2001 From: maaz519 Date: Thu, 23 Jul 2026 14:35:22 +0530 Subject: [PATCH] feat(messaging-ui): focusThreadId on Messenger + Inbox for search deep-links (0.1.6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A host can pass focusThreadId to select/open a specific conversation — used by the CRM's global search to jump to the exact thread. Mail switches to Open + selects the matching item; messenger selects the thread. (Source for the already-published 0.1.6.) Co-Authored-By: Claude Opus 4.8 --- packages/iios-messaging-ui/package.json | 2 +- .../src/components/messenger.tsx | 10 +++++++++- packages/iios-messaging-ui/src/inbox/inbox.tsx | 17 +++++++++++++++-- 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/packages/iios-messaging-ui/package.json b/packages/iios-messaging-ui/package.json index e785610..3274af2 100644 --- a/packages/iios-messaging-ui/package.json +++ b/packages/iios-messaging-ui/package.json @@ -1,6 +1,6 @@ { "name": "@insignia/iios-messaging-ui", - "version": "0.1.5", + "version": "0.1.6", "type": "module", "main": "dist/index.js", "module": "dist/index.js", diff --git a/packages/iios-messaging-ui/src/components/messenger.tsx b/packages/iios-messaging-ui/src/components/messenger.tsx index e28c2b7..dd2effc 100644 --- a/packages/iios-messaging-ui/src/components/messenger.tsx +++ b/packages/iios-messaging-ui/src/components/messenger.tsx @@ -12,7 +12,7 @@ import { ThreadPane } from './thread-pane'; * with a channel browser when the adapter supports channels. Owns only selection + browse state; * all data flows through the injected adapter via the hooks. */ -export function Messenger() { +export function Messenger({ focusThreadId }: { focusThreadId?: string | null } = {}) { const { conversations, loading, error, refetch } = useConversations(); const adapter = useAdapter(); const channelsSupported = typeof adapter.browseChannels === 'function'; @@ -32,6 +32,14 @@ export function Messenger() { // Switching conversations (or into browse/compose) closes any open thread pane. useEffect(() => setActiveRoot(null), [selected, browsing, composing]); + // Deep link (e.g. from global search): focus the requested conversation. + useEffect(() => { + if (!focusThreadId) return; + setBrowsing(false); + setComposing(false); + setSelected(focusThreadId); + }, [focusThreadId]); + const channels = useMemo(() => conversations.filter((c) => c.membership === 'channel'), [conversations]); const dms = useMemo(() => conversations.filter((c) => c.membership !== 'channel'), [conversations]); const selectedConversation = useMemo(() => conversations.find((c) => c.threadId === selected) ?? null, [conversations, selected]); diff --git a/packages/iios-messaging-ui/src/inbox/inbox.tsx b/packages/iios-messaging-ui/src/inbox/inbox.tsx index a4e42b2..e3b5c8c 100644 --- a/packages/iios-messaging-ui/src/inbox/inbox.tsx +++ b/packages/iios-messaging-ui/src/inbox/inbox.tsx @@ -32,17 +32,30 @@ const timeOf = (iso?: string): string => { }; /** The unified inbox: work items + mail in one list; click a threaded row to read + reply. */ -export function Inbox() { +export function Inbox({ focusThreadId }: { focusThreadId?: string | null } = {}) { const [filter, setFilter] = useState('OPEN'); const { items, loading, error, transition, refetch } = useInbox(filter); const compose = useCompose(); const [selectedId, setSelectedId] = useState(null); const [composing, setComposing] = useState(false); + // Deep link (e.g. from global search): mail lives in the Open view, so switch there and let the + // selection effect pick the matching thread once the list loads. useEffect(() => { + if (focusThreadId) setFilter('OPEN'); + }, [focusThreadId]); + + useEffect(() => { + if (focusThreadId) { + const hit = items.find((i) => i.threadId === focusThreadId); + if (hit) { + setSelectedId(hit.id); + return; + } + } if (selectedId && items.some((i) => i.id === selectedId)) return; setSelectedId(items[0]?.id ?? null); - }, [items, selectedId]); + }, [items, selectedId, focusThreadId]); const selected = items.find((i) => i.id === selectedId) ?? null;