import type { Backend } from "./Backend"; import { mockBackend } from "./mock"; import { iios } from "@/lib/iios/client"; import { toInboxItem, toMessage, toTicket } from "@/lib/iios/map"; import manifest from "@/lib/iios/seed-manifest.json"; import type { Bootstrap, InboxState, Message, TicketState } from "@lynkd/messaging-inbox-sdk"; // If the IIOS DB has been seeded (scripts/seed-iios.mjs), a manifest maps the real // IIOS thread ids to a channel directory so the sidebar shows live data. Otherwise // bootstrap/channels fall back to the mock store (IIOS has no channel-list endpoint). const seeded = (manifest as any)?.seeded === true; const seededBootstrap = () => manifest as unknown as Bootstrap; /** * IIOS-backed implementation. Endpoints that map cleanly to the IIOS API are wired * for real (messages, threads, inbox, tickets). Concepts IIOS doesn't expose 1:1 yet * (bootstrap/users/channels, reactions, search, notifications, availability) delegate * to the mock backend so the UI keeps working — replace these as IIOS grows portal/ * directory endpoints. See docs/IIOS_INTEGRATION.md. */ export const iiosBackend: Backend = { // --- bootstrap/channels: from the seed manifest if present, else mock --- bootstrap: () => (seeded ? Promise.resolve(seededBootstrap()) : mockBackend.bootstrap()), listChannels: () => (seeded ? Promise.resolve(seededBootstrap().channels) : mockBackend.listChannels()), createChannel: (input) => mockBackend.createChannel(input), addMember: (channelId, userId) => mockBackend.addMember(channelId, userId), removeMember: (channelId, userId) => mockBackend.removeMember(channelId, userId), markRead: (channelId) => mockBackend.markRead(channelId), setStatus: (text, emoji, clearAt) => mockBackend.setStatus(text, emoji, clearAt), updateMe: (patch) => mockBackend.updateMe(patch), availability: () => mockBackend.availability(), notifications: () => mockBackend.notifications(), // IIOS has no reaction/pin/save/edit/delete endpoints — keep local behavior react: (channelId, messageId, emoji) => mockBackend.react(channelId, messageId, emoji), pin: (channelId, messageId) => mockBackend.pin(channelId, messageId), save: (channelId, messageId) => mockBackend.save(channelId, messageId), editMessage: (channelId, messageId, body) => mockBackend.editMessage(channelId, messageId, body), deleteMessage: (channelId, messageId) => mockBackend.deleteMessage(channelId, messageId), savedMessages: () => mockBackend.savedMessages(), threadParents: () => mockBackend.threadParents(), // IIOS has no cross-entity search endpoint documented — use local search search: (q) => mockBackend.search(q), // --- wired to the real IIOS service --- async channelMessages(channelId) { const r = await iios.getThreadMessages(channelId); const list = Array.isArray(r) ? r : (r.messages ?? []); return list.filter((m: any) => !(m.parentInteractionId ?? m.parentId)).map((m: any) => toMessage(m, channelId)); }, async thread(channelId, parentId) { const r = await iios.getThreadMessages(channelId); const list: Message[] = (Array.isArray(r) ? r : r.messages ?? []).map((m: any) => toMessage(m, channelId)); return { parent: list.find((m) => m.id === parentId) ?? null, replies: list.filter((m) => m.parentId === parentId), }; }, async send(channelId, body, opts) { const idem = `web-${channelId}-${body.length}-${Math.round((opts?.scheduledFor ?? 0) / 1000)}`; const r = await iios.sendMessage(channelId, body, idem); return toMessage(r.message ?? r, channelId); }, async listInbox(state) { const r = await iios.listInbox(state); const list = Array.isArray(r) ? r : r.items ?? []; return list.map(toInboxItem); }, async patchInbox(id, state: InboxState) { const r = await iios.patchInbox(id, state); return toInboxItem(r.item ?? r); }, async listTickets(scope) { const r = await iios.listTickets(scope); const list = Array.isArray(r) ? r : r.tickets ?? []; return list.map(toTicket); }, async getTicket(id) { // IIOS lists tickets; fetch and find (swap for a GET /tickets/:id when available) const r = await iios.listTickets("all"); const list = Array.isArray(r) ? r : r.tickets ?? []; const found = list.find((t: any) => String(t.id) === id || String(t.ref) === id); return found ? toTicket(found) : null; }, async patchTicket(id, state: TicketState) { const r = await iios.patchTicket(id, state); return toTicket(r.ticket ?? r); }, async replyTicket(id, body) { // A ticket reply is a message on the ticket's linked thread. const t = await this.getTicket(id); if (t?.channelId) await iios.sendMessage(t.channelId, body); return this.getTicket(id); }, };