@lorikeetai/react-native-sdk 1.2.0 → 2.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/message-circle-plus-icon.tsx","../src/widget.tsx"],"names":["jsxs","Svg","jsx","Path","Line","StyleSheet","forwardRef","WebView","Linking","View","Text","TouchableOpacity","useState","useRef","useEffect","lorikeetClient","SafeAreaView"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { Customisations } from '@lorikeetai/web-sdk/types'\n\nimport type { LorikeetInitOptions } from './types'\n\nexport class LorikeetClient {\n baseUrl: string = 'https://chat.lorikeetcx.ai' // Fallback URL\n customisations: Customisations | null = null\n publicKey: string\n\n constructor({ customisations, api_base, public_key }: LorikeetInitOptions) {\n if (api_base) {\n this.baseUrl = api_base\n }\n this.publicKey = public_key\n\n if (customisations) {\n this.customisations = customisations\n }\n }\n\n getWebviewUrl = ({\n customer_email,\n customer_name,\n token,\n ticket_id,\n }: {\n customer_email?: string\n customer_name?: string\n token: string\n ticket_id?: string\n }) => {\n const url = new URL(this.baseUrl)\n\n url.pathname = ticket_id\n ? `/tickets/${this.publicKey}/${ticket_id}`\n : '/widget'\n url.searchParams.append('public_key', this.publicKey)\n\n if (customer_email) {\n url.searchParams.append('customer_email', customer_email)\n }\n if (customer_name) {\n url.searchParams.append('customer_name', customer_name)\n }\n\n url.searchParams.append('token', token)\n\n if (this.customisations) {\n url.searchParams.append(\n 'customisations',\n btoa(JSON.stringify(this.customisations)),\n )\n }\n\n return url.toString()\n }\n}\n","import Svg, { Line, Path } from 'react-native-svg'\n\ninterface MessageCirclePlusIconProps {\n color?: string\n size?: number\n strokeWidth?: number\n}\n\nexport const MessageCirclePlusIcon = ({\n size = 24,\n color = 'currentColor',\n strokeWidth = 2,\n}: MessageCirclePlusIconProps) => {\n return (\n <Svg fill=\"none\" height={size} viewBox=\"0 0 24 24\" width={size}>\n <Path\n d=\"M7.9 20A9 9 0 1 0 4 16.1L2 22Z\"\n stroke={color}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={strokeWidth}\n />\n <Line\n stroke={color}\n strokeLinecap=\"round\"\n strokeWidth={strokeWidth}\n x1=\"12\"\n x2=\"12\"\n y1=\"8\"\n y2=\"16\"\n />\n <Line\n stroke={color}\n strokeLinecap=\"round\"\n strokeWidth={strokeWidth}\n x1=\"8\"\n x2=\"16\"\n y1=\"12\"\n y2=\"12\"\n />\n </Svg>\n )\n}\n","import { forwardRef, useEffect, useRef, useState } from 'react'\n\nimport { Linking, StyleSheet, Text, TouchableOpacity, View } from 'react-native'\nimport { SafeAreaView } from 'react-native-safe-area-context'\nimport { WebView } from 'react-native-webview'\n\nimport { LorikeetClient } from './client'\nimport { MessageCirclePlusIcon } from './message-circle-plus-icon'\nimport type {\n LorikeetWidgetHeaderProps,\n LorikeetWidgetViewProps,\n LorikeetWidgetWebViewProps,\n} from './types'\n\nconst stylesheet = StyleSheet.create({\n root: { flex: 1, backgroundColor: '#fff' },\n container: {\n flex: 1,\n borderRadius: 8,\n overflow: 'hidden',\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 2 },\n shadowOpacity: 0.1,\n shadowRadius: 4,\n elevation: 3,\n },\n header: {\n padding: 16,\n display: 'flex',\n flexDirection: 'row',\n justifyContent: 'space-between',\n },\n headerIcon: {\n width: 24,\n },\n headerText: {\n textAlign: 'center',\n fontSize: 16,\n },\n webView: {\n flex: 1,\n },\n})\n\nexport const LorikeetWidgetWebView = forwardRef<\n WebView,\n LorikeetWidgetWebViewProps\n>(({ uri, baseUrl = 'https://chat.lorikeetcx.ai', webviewProps = {} }, ref) => {\n return (\n <WebView\n {...webviewProps}\n onNavigationStateChange={(navState) => {\n if (navState.url.includes(baseUrl)) {\n return false\n } else {\n void Linking.openURL(navState.url)\n return false\n }\n }}\n ref={ref}\n source={{\n uri,\n }}\n style={stylesheet.webView}\n />\n )\n})\nLorikeetWidgetWebView.displayName = 'LorikeetWidgetWebView'\n\nexport const LorikeetWidgetHeader = ({\n title,\n style,\n icon = <MessageCirclePlusIcon size={16} />,\n onIconPress,\n}: LorikeetWidgetHeaderProps) => {\n return (\n <View style={stylesheet.header}>\n <View style={stylesheet.headerIcon} />\n <Text style={[stylesheet.headerText, style]}>{title}</Text>\n <TouchableOpacity onPress={onIconPress} style={stylesheet.headerIcon}>\n {icon}\n </TouchableOpacity>\n </View>\n )\n}\n\nexport function LorikeetWidgetView({\n publicKey,\n baseUrl,\n authToken,\n customisations,\n styles,\n onWebViewMessage,\n}: LorikeetWidgetViewProps) {\n const [lorikeetClient, setLorikeetClient] = useState<LorikeetClient | null>(\n null,\n )\n const [uri, setUri] = useState<string | null>(null)\n const [timestamp, setTimestamp] = useState<number>(Date.now())\n const webViewRef = useRef<WebView>(null)\n\n useEffect(() => {\n const lorikeetClient = new LorikeetClient({\n public_key: publicKey,\n api_base: baseUrl,\n customisations: {\n ...customisations,\n hideHeader:\n customisations?.hideHeader === undefined\n ? true\n : customisations.hideHeader,\n },\n })\n setLorikeetClient(lorikeetClient)\n setUri(\n lorikeetClient.getWebviewUrl({\n token: authToken,\n }),\n )\n }, [publicKey, baseUrl])\n\n if (!lorikeetClient || !uri) {\n return null\n }\n\n return (\n <SafeAreaView edges={['top', 'left', 'right']} style={stylesheet.root}>\n <View style={stylesheet.container}>\n <LorikeetWidgetHeader\n onIconPress={() => {\n setTimestamp(Date.now())\n }}\n style={styles?.header}\n title={customisations?.title ?? 'Lorikeet AI Agent'}\n />\n <LorikeetWidgetWebView\n baseUrl={lorikeetClient.baseUrl}\n key={timestamp}\n ref={webViewRef}\n uri={uri}\n webviewProps={{\n onMessage: onWebViewMessage,\n allowsInlineMediaPlayback: true,\n mediaPlaybackRequiresUserAction: false,\n scalesPageToFit: true,\n startInLoadingState: true,\n style: stylesheet.webView,\n }}\n />\n </View>\n </SafeAreaView>\n )\n}\n"]}
1
+ {"version":3,"file":"index.cjs","names":["Svg","Path","Line","StyleSheet","forwardRef","useRef","WebView","Linking","View","Text","TouchableOpacity","useState","useCallback","AppState","SafeAreaView"],"sources":["../src/bridge.ts","../src/encode.ts","../src/client.ts","../src/errors.ts","../src/events.ts","../src/lifecycle.ts","../src/links.ts","../src/message-circle-plus-icon.tsx","../src/widget.tsx"],"sourcesContent":["/**\n * The conversation the live WebView is actually in, which is not necessarily the\n * one the host passed as `ticketId`: a mount without one creates a ticket, and\n * only the bridge knows its id.\n */\nexport type ObservedConversation = {\n /** The credentials this conversation was created under. */\n identity: string\n /**\n * The ticket the mount was aiming for when this was observed, or `undefined`\n * for a mount that created its own. A host switching to another conversation\n * changes it, which retires the observation: without that, a recovery in the\n * window before the new page reports itself would navigate back to the old\n * ticket and undo the switch.\n */\n intendedTicketId?: string\n session?: string\n ticketId?: string\n}\n\n/**\n * The public key, base URL and token are one unit, and a conversation belongs to\n * the credentials that created it. Stamping an observation with them is what\n * stops a remount resuming one environment's ticket against another's key.\n */\nexport const conversationIdentity = ({\n authToken,\n baseUrl,\n publicKey,\n}: {\n authToken: string\n baseUrl?: string\n publicKey: string\n}): string => `${publicKey} ${baseUrl ?? ''} ${authToken}`\n\n/**\n * A remount has to resume the conversation it was in, or the recovery abandons\n * the ticket it was meant to rescue. Only usable when the credentials still\n * match the ones that produced it.\n */\nexport const resumableConversation = (\n observed: ObservedConversation | null,\n identity: string,\n intendedTicketId: string | undefined,\n): { session?: string; ticketId: string } | null => {\n if (\n observed === null ||\n observed.identity !== identity ||\n observed.intendedTicketId !== intendedTicketId ||\n observed.ticketId === undefined\n ) {\n return null\n }\n return { session: observed.session, ticketId: observed.ticketId }\n}\n","const BASE64_ALPHABET =\n 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\n\n/**\n * Base64 of a string's UTF-8 bytes.\n *\n * The chat page decodes this as UTF-8 (`Buffer.from(value, 'base64')`), so\n * UTF-8 is the contract. The global `btoa` cannot meet it: it accepts Latin-1\n * only, so a typographic apostrophe or an emoji in a title throws\n * \"Found invalid character when converting to base64\" and takes the render with\n * it. Hermes also had no `btoa` at all before React Native 0.74, which is\n * inside this package's supported peer range.\n */\nexport function base64EncodeUtf8(value: string): string {\n // encodeURIComponent is UTF-8 by specification, so percent-decoding its\n // output one byte at a time yields the UTF-8 encoding without needing\n // TextEncoder, which has the same React Native availability problem as btoa.\n const bytes = encodeURIComponent(value).replace(\n /%([0-9A-F]{2})/g,\n (_, hex: string) => String.fromCharCode(parseInt(hex, 16)),\n )\n\n let encoded = ''\n for (let index = 0; index < bytes.length; index += 3) {\n const first = bytes.charCodeAt(index)\n const second =\n index + 1 < bytes.length ? bytes.charCodeAt(index + 1) : undefined\n const third =\n index + 2 < bytes.length ? bytes.charCodeAt(index + 2) : undefined\n\n encoded += BASE64_ALPHABET[first >> 2]\n encoded += BASE64_ALPHABET[((first & 0x03) << 4) | ((second ?? 0) >> 4)]\n encoded +=\n second === undefined\n ? '='\n : BASE64_ALPHABET[((second & 0x0f) << 2) | ((third ?? 0) >> 6)]\n encoded += third === undefined ? '=' : BASE64_ALPHABET[third & 0x3f]\n }\n\n return encoded\n}\n","import type { Customisations } from '@lorikeetai/web-sdk/types'\n\nimport type { ConversationMetadata } from './conversation-metadata'\nimport { base64EncodeUtf8 } from './encode'\nimport type { LorikeetInitOptions } from './types'\n\nexport class LorikeetClient {\n baseUrl: string = 'https://chat.lorikeetcx.ai' // Fallback URL\n customisations: Customisations | null = null\n metadata: ConversationMetadata | null = null\n publicKey: string\n\n constructor({\n customisations,\n api_base,\n metadata,\n public_key,\n }: LorikeetInitOptions) {\n if (api_base) {\n this.baseUrl = api_base\n }\n this.publicKey = public_key\n\n if (customisations) {\n this.customisations = customisations\n }\n\n if (metadata) {\n this.metadata = metadata\n }\n }\n\n getWebviewUrl = ({\n customer_email,\n customer_name,\n token,\n ticket_id,\n session,\n }: {\n customer_email?: string\n customer_name?: string\n session?: string\n ticket_id?: string\n token: string\n }) => {\n const url = new URL(this.baseUrl)\n\n url.pathname = ticket_id\n ? `/tickets/${this.publicKey}/${ticket_id}`\n : '/widget'\n url.searchParams.append('public_key', this.publicKey)\n\n if (customer_email) {\n url.searchParams.append('customer_email', customer_email)\n }\n if (customer_name) {\n url.searchParams.append('customer_name', customer_name)\n }\n\n url.searchParams.append('token', token)\n\n // The session authorises exactly the ticket it was issued for, so it only\n // means anything on the resume path. Without it the ticket route cannot\n // authorise a fresh WebView and answers CUSTOMER_ID_MISSING.\n if (ticket_id && session) {\n url.searchParams.append('session', session)\n }\n\n if (this.customisations) {\n url.searchParams.append(\n 'customisations',\n base64EncodeUtf8(JSON.stringify(this.customisations)),\n )\n }\n\n // Plain JSON, unlike customisations. The routes read it with\n // `searchParams.get('metadata')` and parse it directly.\n if (this.metadata) {\n url.searchParams.append('metadata', JSON.stringify(this.metadata))\n }\n\n return url.toString()\n }\n}\n","/**\n * Codes the chat page posts to the host as `{ type: 'error', error: CODE }`.\n * Mirrored here because the page publishes them nowhere else, so a host would\n * otherwise be matching on strings it discovered by observation.\n */\nexport const LORIKEET_ERROR_CODES = [\n 'MISSING_TOKEN',\n 'CORRUPT_TOKEN_DATA',\n 'DECRYPT_FAILED',\n 'EXPIRED_TOKEN',\n 'MALFORMED_TOKEN',\n 'MISSING_PUBLIC_KEY',\n 'UNKNOWN_PUBLIC_KEY',\n 'CUSTOMER_ID_CONFLICT',\n 'CUSTOMER_ID_MISSING',\n 'FATAL_ERROR',\n 'INVALID_SESSION',\n 'DOMAIN_NOT_ALLOWED',\n 'TICKET_NOT_FOUND',\n] as const\n\nexport type LorikeetErrorCode = (typeof LORIKEET_ERROR_CODES)[number]\n\n/**\n * The codes that mean the stored conversation cannot be reopened, as opposed to\n * the trusted token being bad or the integration being misconfigured. Only\n * these are worth abandoning a resume for: a token error fails again with the\n * same token, and a configuration error fails again regardless.\n */\nconst UNUSABLE_CONVERSATION_CODES: readonly LorikeetErrorCode[] = [\n 'CUSTOMER_ID_MISSING',\n 'INVALID_SESSION',\n 'TICKET_NOT_FOUND',\n]\n\n/** Whether an error code means the stored conversation cannot be reopened. */\nexport function isUnusableConversationCode(code: string): boolean {\n return UNUSABLE_CONVERSATION_CODES.includes(code as LorikeetErrorCode)\n}\n\n// oxlint-disable-next-line @lorikeetai/no-unknown-parameters -- Hosts pass raw JSON posted by the chat page, which may be any message.\nexport function isUnusableConversation(message: unknown): boolean {\n if (typeof message !== 'object' || message === null) {\n return false\n }\n const { type, error } = message as { error?: unknown; type?: unknown }\n return type === 'error' && typeof error === 'string'\n ? isUnusableConversationCode(error)\n : false\n}\n","/**\n * What the page reports, as events rather than as raw bridge messages.\n *\n * Names follow the web SDK's (`LorikeetEvent` in `@lorikeetai/web-sdk/types`)\n * wherever it has one, because a host that integrates both should not have to\n * learn two vocabularies. The rest cover what the page tells a host that owns\n * its own layout and storage, which on web the SDK handles internally and never\n * surfaces: the close handshake, the two persistence messages, and error codes.\n *\n * `token_expiry` is deliberately absent. The credential model it belongs to is\n * being rewritten, and an event is the hardest thing to take back.\n */\nexport type LorikeetWidgetEvent =\n /** The page is up and the conversation is usable. */\n | { type: 'ready' }\n /**\n * A ticket that did not exist before this page loaded. Once per ticket, and\n * never for a resume, matching the web SDK.\n */\n | { type: 'ticket_created'; ticketId: string }\n /** The customer sent something. */\n | { type: 'customer_message' }\n /** The agent sent something. */\n | { type: 'agent_message' }\n /**\n * The page closed itself, and why. `ended_by_customer` is followed by a\n * `new_chat`, seconds later, so a host that tears down here never sees the\n * rest of the handshake. `minimised` has nothing behind it: the conversation\n * is intact and hiding it is enough.\n *\n * `reason` is absent from a page older than the release that added it.\n */\n | { type: 'close'; reason?: 'ended_by_customer' | 'minimised' }\n /**\n * The conversation ended without the customer ending it: an agent closed it,\n * or it timed out. Nothing follows, and nothing is asking the host to hide\n * anything. Left unhandled, this is the customer stranded on a conversation\n * that can no longer be used.\n */\n | { type: 'conversation_ended' }\n /**\n * The page asking for a replacement conversation. It cannot navigate itself,\n * so acting on this is the host's half of End chat.\n */\n | { type: 'new_chat' }\n /**\n * Store this against the customer, alongside the session. A resume needs both:\n * the ticket alone fails with `CUSTOMER_ID_MISSING`.\n */\n | { type: 'persist_ticket_id'; ticketId: string }\n /** The other half of the stored pair. Arrives separately and in no order. */\n | { type: 'persist_session'; session: string }\n /** A page-level failure. `LORIKEET_ERROR_CODES` lists the codes. */\n | { type: 'error'; error: string }\n /**\n * The preload page finished warming, and what the server said about it. Only\n * reaches a host that loaded `/preload`.\n */\n | {\n type: 'preload_complete'\n clientStart: boolean\n lazyTicketCreationEnabled: boolean\n }\n\n/** The names, spelled out rather than derived, so callers import a real type. */\nexport type LorikeetWidgetEventType =\n | 'agent_message'\n | 'close'\n | 'conversation_ended'\n | 'customer_message'\n | 'error'\n | 'new_chat'\n | 'persist_session'\n | 'persist_ticket_id'\n | 'preload_complete'\n | 'ready'\n | 'ticket_created'\n\n/** What `JSON.parse` can produce from a bridge message. */\nexport type BridgeMessageJson =\n | string\n | number\n | boolean\n | null\n | BridgeMessageJson[]\n | { [key: string]: BridgeMessageJson }\n\nconst asRecord = (message: BridgeMessageJson | undefined) =>\n typeof message === 'object' && message !== null && !Array.isArray(message)\n ? message\n : null\n\nconst asNonEmptyString = (value: BridgeMessageJson | undefined) =>\n typeof value === 'string' && value !== '' ? value : null\n\n/**\n * The event a bridge message carries, or `null` for one that carries none.\n *\n * `ticket_created` is not derived here: telling a fresh ticket from a resumed\n * one needs the URL the page was loaded with, which only the component holds.\n */\nexport const eventFromMessage = (\n message: BridgeMessageJson | undefined,\n): LorikeetWidgetEvent | null => {\n const record = asRecord(message)\n if (record === null || typeof record.type !== 'string') {\n return null\n }\n switch (record.type) {\n case 'ready':\n return { type: 'ready' }\n case 'customerMessage':\n return { type: 'customer_message' }\n case 'agentMessage':\n return { type: 'agent_message' }\n case 'closeChat': {\n const reason = asNonEmptyString(record.reason)\n return reason === 'ended_by_customer' || reason === 'minimised'\n ? { type: 'close', reason }\n : { type: 'close' }\n }\n case 'conversationEnded':\n return { type: 'conversation_ended' }\n case 'startNewChat':\n return { type: 'new_chat' }\n case 'persistLorikeetTicketId': {\n const ticketId = asNonEmptyString(record.ticketId)\n return ticketId === null ? null : { type: 'persist_ticket_id', ticketId }\n }\n case 'persistLorikeetSession': {\n const session = asNonEmptyString(record.session)\n return session === null ? null : { type: 'persist_session', session }\n }\n case 'error': {\n const error = asNonEmptyString(record.error)\n return error === null ? null : { type: 'error', error }\n }\n case 'preloadComplete':\n return {\n type: 'preload_complete',\n clientStart: record.clientStart === true,\n lazyTicketCreationEnabled: record.lazyTicketCreationEnabled === true,\n }\n default:\n return null\n }\n}\n\n/**\n * The ticket a URL is trying to resume, or `null` for one that creates its own.\n *\n * Read from the URL rather than from any state, so it holds for a host that\n * built the URL itself. An unreadable URL reports nothing, which at worst\n * announces a resumed ticket as created once.\n */\nexport const resumedTicketIdFromUri = (uri: string): string | null => {\n let pathname: string\n try {\n pathname = new URL(uri).pathname\n } catch {\n return null\n }\n // `/tickets/:publicKey/:ticketId`, which is what `getWebviewUrl` builds and\n // what the chat app routes.\n const segments = pathname.split('/').filter((part) => part !== '')\n if (segments.length < 3 || segments[0] !== 'tickets') {\n return null\n }\n return segments[2] ?? null\n}\n","/**\n * The page gives its message stream ten attempts with exponential backoff\n * capped at 30s (`message-stream-utils.tsx`), so the budget runs out roughly\n * 151s after the stream drops, or ~196s once jitter is counted. It then sits at\n * `failed` with nothing to reset it, so a customer who comes back to a\n * backgrounded app is looking at a stream that will never reconnect.\n *\n * Three minutes sits inside that band. Shorter would remount while the page's\n * own retries could still win, which costs the customer their draft message for\n * nothing; much longer leaves a dead stream on screen.\n */\nexport const BACKGROUND_RELOAD_THRESHOLD_MS = 180_000\n\n/**\n * Elapsed time is the only signal available: the bridge reports `ready`,\n * `error` and the persistence messages, but never stream health, so the SDK\n * cannot ask whether the connection actually survived.\n */\nexport const shouldReloadAfterBackground = (\n backgroundedAt: number | null,\n now: number,\n thresholdMs: number = BACKGROUND_RELOAD_THRESHOLD_MS,\n): boolean => {\n if (backgroundedAt === null) {\n return false\n }\n // A clock that moved backwards says nothing about how long we were away.\n const elapsed = now - backgroundedAt\n if (elapsed < 0) {\n return false\n }\n return elapsed >= thresholdMs\n}\n","/**\n * Schemes the WebView resolves for itself. None of them is a page the customer\n * asked to leave the app for, and the OS cannot open them anyway.\n */\nconst WEBVIEW_OWNED_SCHEMES = new Set([\n 'about:',\n 'blob:',\n 'data:',\n 'javascript:',\n])\n\nconst parseUrl = (value: string): URL | null => {\n try {\n return new URL(value)\n } catch {\n return null\n }\n}\n\n/**\n * Whether the OS could be asked to open this at all. `window.open()` with no\n * argument gives `about:blank`, and a scheme the WebView resolves for itself is\n * not something the OS can place.\n */\nexport const canOpenOutsideApp = (url: string): boolean => {\n const target = parseUrl(url)\n return target !== null && !WEBVIEW_OWNED_SCHEMES.has(target.protocol)\n}\n\n/**\n * Whether a navigation belongs inside the widget.\n *\n * An origin comparison rather than the substring match this replaces. `baseUrl`\n * appearing anywhere in a URL counted as internal, so\n * `https://evil.example/?next=https://chat.lorikeetcx.ai` loaded in place of the\n * conversation instead of opening in the browser. Comparing origins rather than\n * matching a prefix also means a port, a trailing slash and a different path all\n * still resolve to the same widget.\n */\nexport const isInternalNavigation = (url: string, baseUrl: string): boolean => {\n const base = parseUrl(baseUrl)\n const target = parseUrl(url)\n // With either side unparseable there is no basis for sending the customer out\n // of the app, so the navigation stays with the WebView. Failing the other way\n // would hand the chat's own URL, and the session in it, to the system browser.\n if (base === null || target === null) {\n return true\n }\n if (WEBVIEW_OWNED_SCHEMES.has(target.protocol)) {\n return true\n }\n return target.origin === base.origin\n}\n","import Svg, { Line, Path } from 'react-native-svg'\n\ninterface MessageCirclePlusIconProps {\n color?: string\n size?: number\n strokeWidth?: number\n}\n\nexport const MessageCirclePlusIcon = ({\n size = 24,\n color = 'currentColor',\n strokeWidth = 2,\n}: MessageCirclePlusIconProps) => {\n return (\n <Svg fill=\"none\" height={size} viewBox=\"0 0 24 24\" width={size}>\n <Path\n d=\"M7.9 20A9 9 0 1 0 4 16.1L2 22Z\"\n stroke={color}\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n strokeWidth={strokeWidth}\n />\n <Line\n stroke={color}\n strokeLinecap=\"round\"\n strokeWidth={strokeWidth}\n x1=\"12\"\n x2=\"12\"\n y1=\"8\"\n y2=\"16\"\n />\n <Line\n stroke={color}\n strokeLinecap=\"round\"\n strokeWidth={strokeWidth}\n x1=\"8\"\n x2=\"16\"\n y1=\"12\"\n y2=\"12\"\n />\n </Svg>\n )\n}\n","import { forwardRef, useCallback, useEffect, useRef, useState } from 'react'\n\nimport {\n AppState,\n Linking,\n StyleSheet,\n Text,\n TouchableOpacity,\n View,\n} from 'react-native'\nimport { SafeAreaView } from 'react-native-safe-area-context'\nimport { WebView } from 'react-native-webview'\nimport type { WebViewMessageEvent } from 'react-native-webview'\n\nimport { conversationIdentity, resumableConversation } from './bridge'\nimport type { ObservedConversation } from './bridge'\nimport { LorikeetClient } from './client'\nimport { isUnusableConversationCode } from './errors'\nimport { eventFromMessage, resumedTicketIdFromUri } from './events'\nimport type { BridgeMessageJson, LorikeetWidgetEvent } from './events'\nimport { shouldReloadAfterBackground } from './lifecycle'\nimport { canOpenOutsideApp, isInternalNavigation } from './links'\nimport { MessageCirclePlusIcon } from './message-circle-plus-icon'\nimport type {\n LorikeetWidgetHeaderProps,\n LorikeetWidgetViewProps,\n LorikeetWidgetWebViewProps,\n} from './types'\n\nconst stylesheet = StyleSheet.create({\n root: { flex: 1, backgroundColor: '#fff' },\n container: {\n flex: 1,\n borderRadius: 8,\n overflow: 'hidden',\n shadowColor: '#000',\n shadowOffset: { width: 0, height: 2 },\n shadowOpacity: 0.1,\n shadowRadius: 4,\n elevation: 3,\n },\n header: {\n padding: 16,\n display: 'flex',\n flexDirection: 'row',\n justifyContent: 'space-between',\n },\n headerIcon: {\n width: 24,\n },\n headerText: {\n textAlign: 'center',\n fontSize: 16,\n },\n webView: {\n flex: 1,\n },\n // A closed page is out of sight but still laid out, still full size and still\n // in the view hierarchy.\n //\n // `display: 'none'` is the obvious way to hide it and is the one that broke:\n // on iOS the page stopped running, and the page is what posts the\n // `startNewChat` this is waiting for. Why it stopped is not established. The\n // hiding alone does not explain it: hiding a WKWebView, by zero size or by\n // `hidden` on it or on its parent, leaves the page running in isolation, so\n // something further up is implicated, with Fabric recycling the component view\n // the leading candidate, since react-native-webview destroys the WebView on\n // recycle. Whatever the cause, the requirement is the same and is worth\n // stating on its own terms: a close must not alter the view structurally,\n // because the page has to keep running for seconds afterwards.\n closed: {\n flex: 1,\n opacity: 0,\n },\n})\n\nexport const LorikeetWidgetWebView = forwardRef<\n WebView,\n LorikeetWidgetWebViewProps\n>(\n (\n { uri, baseUrl = 'https://chat.lorikeetcx.ai', webviewProps = {}, onEvent },\n ref,\n ) => {\n const isInternal = (url: string) => isInternalNavigation(url, baseUrl)\n // Every ticket this component already knows about, whether it announced it\n // or the URL asked to resume it. `ticket_created` fires once per ticket and\n // never for a resume, and this is what both of those come down to.\n //\n // Deliberately not reset when `uri` changes. A page that has been navigated\n // away from can still post, and its late report would otherwise be read\n // against the URL that replaced it: the ticket it names is neither the new\n // URL's resume nor yet announced, so it would be announced a second time.\n // Remembering the ticket rather than the page is what closes that without\n // giving this component a generation to track, which belongs to whoever\n // owns the URL.\n const accounted = useRef<Set<string>>(new Set())\n const seenUri = useRef<string | null>(null)\n if (seenUri.current !== uri) {\n seenUri.current = uri\n const resumed = resumedTicketIdFromUri(uri)\n if (resumed !== null) {\n accounted.current.add(resumed)\n }\n }\n\n const emit = (event: LorikeetWidgetEvent) => {\n onEvent?.(event)\n if (event.type !== 'persist_ticket_id') {\n return\n }\n if (accounted.current.has(event.ticketId)) {\n return\n }\n accounted.current.add(event.ticketId)\n onEvent?.({ ticketId: event.ticketId, type: 'ticket_created' })\n }\n\n return (\n <WebView\n style={stylesheet.webView}\n {...webviewProps}\n // The one place bridge messages are read, so the drop-in and a composed\n // host are told the same things by the same code. The raw handler still\n // runs afterwards: this is additive, not a replacement.\n onMessage={(event) => {\n let message: BridgeMessageJson | undefined\n try {\n message = JSON.parse(event.nativeEvent.data)\n } catch {\n // Not JSON, so not one of the page's messages. Pass it through.\n }\n const widgetEvent = eventFromMessage(message)\n if (widgetEvent !== null) {\n emit(widgetEvent)\n }\n webviewProps.onMessage?.(event)\n }}\n // A backstop for a navigation that never reached\n // `onShouldStartLoadWithRequest`, whose coverage differs by platform and by\n // how the page triggered the navigation. The two cannot both open the same\n // URL: one that was turned away there never becomes the navigation state.\n onNavigationStateChange={(navState) => {\n if (!isInternal(navState.url)) {\n void Linking.openURL(navState.url)\n }\n webviewProps.onNavigationStateChange?.(navState)\n }}\n // A second window is the one thing the WebView cannot give the page, so\n // without this a `target=\"_blank\"` link silently does nothing. That is not a\n // corner: the chat sets `<base target=\"_blank\">`, so it is every link in\n // every message, and this rather than the handler below is where most of\n // them arrive.\n //\n // Every target goes out, the widget's own origin included. A browser answers\n // `target=\"_blank\"` with a new tab and leaves the iframe where it is, and\n // the system browser is the nearest thing to that on a phone. Loading it in\n // this WebView instead would replace the conversation the customer is in,\n // with nothing to go back with.\n onOpenWindow={(event) => {\n const { targetUrl } = event.nativeEvent\n if (canOpenOutsideApp(targetUrl)) {\n void Linking.openURL(targetUrl)\n }\n webviewProps.onOpenWindow?.(event)\n }}\n // The only handler that can stop a navigation rather than report one that\n // already started, so this is where an external link is turned away.\n onShouldStartLoadWithRequest={(request) => {\n // Asked first, so a host can claim a navigation, such as a deep link it\n // wants to route itself. Declining is a decision, not an absence of one, so\n // the SDK does not then second-guess it by opening the OS as well.\n if (webviewProps.onShouldStartLoadWithRequest?.(request) === false) {\n return false\n }\n // iOS reports subframe navigations here as well. A frame the page\n // embedded is not a link the customer followed, and lifting it out of the\n // page would break the page.\n //\n // Only an explicit `false` earns that exemption. The peer range is the\n // whole of `react-native-webview` 13, which does not report this field\n // uniformly, and reading a missing one as a subframe would exempt every\n // navigation and leave external pages loading inside the widget.\n if (request.isTopFrame === false || isInternal(request.url)) {\n return true\n }\n void Linking.openURL(request.url)\n return false\n }}\n ref={ref}\n source={{\n uri,\n }}\n />\n )\n },\n)\nLorikeetWidgetWebView.displayName = 'LorikeetWidgetWebView'\n\nexport const LorikeetWidgetHeader = ({\n title,\n style,\n icon = <MessageCirclePlusIcon size={16} />,\n onIconPress,\n}: LorikeetWidgetHeaderProps) => {\n return (\n <View style={stylesheet.header}>\n <View style={stylesheet.headerIcon} />\n <Text style={[stylesheet.headerText, style]}>{title}</Text>\n <TouchableOpacity onPress={onIconPress} style={stylesheet.headerIcon}>\n {icon}\n </TouchableOpacity>\n </View>\n )\n}\n\nexport function LorikeetWidgetView({\n publicKey,\n baseUrl,\n authToken,\n customisations,\n styles,\n onEvent,\n onWebViewMessage,\n ticketId,\n session,\n metadata,\n webviewProps,\n}: LorikeetWidgetViewProps) {\n const [lorikeetClient, setLorikeetClient] = useState<LorikeetClient | null>(\n null,\n )\n // The URL and the identity it belongs to move together in one piece of state.\n // Keeping the URL alone would let a render see the previous identity's resume\n // URL, and the WebView would load that authorised conversation before the\n // effect could redirect it.\n const [loaded, setLoaded] = useState<{\n identity: string\n intendedTicketId?: string\n uri: string\n } | null>(null)\n // A counter, not a timestamp: two reloads inside the same millisecond would\n // produce the same key and silently not remount at all.\n const [generation, setGeneration] = useState(0)\n // Read during render so a handler can tell whether the WebView it was\n // installed on is still the one mounted. Identity and intended ticket cannot\n // answer that: starting a new chat replaces a self-created conversation with\n // another that has exactly the same pair.\n const generationRef = useRef(generation)\n generationRef.current = generation\n const remount = useCallback(() => {\n setGeneration((previous) => previous + 1)\n }, [])\n const webViewRef = useRef<WebView>(null)\n // A resume the server refuses is a dead end on a phone. The ticket route\n // redirects an unauthorised resume to /widget without the trusted token,\n // which answers CUSTOMER_ID_MISSING, and the host has no reason to stop\n // handing the same stale ticket back on the next mount. Give up on that\n // conversation once and start a fresh one instead.\n const [refusedTicketId, setRefusedTicketId] = useState<string | null>(null)\n // What the bridge has said about the conversation on screen. A mount without\n // `ticketId` creates a ticket the host may not have stored yet, so without\n // this a remount would load `/widget`, mint another one, and abandon the\n // conversation the remount was meant to rescue. Held in a ref, not state:\n // reading it must not re-render, and it is deliberately not persisted, since\n // surviving a process death is the host's job.\n const observedRef = useRef<ObservedConversation | null>(null)\n // Outside the listener effect deliberately. That effect re-subscribes whenever\n // `reload` changes, which a new client instance does, and a local would reset\n // to null mid-background and skip the recovery on return.\n const backgroundedAtRef = useRef<number | null>(null)\n // The URL now loaded, and what it was built for. Bridge messages are stamped\n // from this rather than the current props: when credentials change, this\n // component re-renders and replaces the message handler while the old page is\n // still alive, and stamping a late message from that page with the new\n // identity would let a recovery reopen the previous customer's conversation\n // under the next customer's credentials. The ticket route authorises a\n // ticket-bound session independently of the trusted token, so that would be a\n // cross-customer read, not merely the wrong conversation.\n const uriRef = useRef<string | null>(null)\n const loadedForRef = useRef<{\n identity: string\n intendedTicketId?: string\n }>({ identity: '', intendedTicketId: undefined })\n const identity = conversationIdentity({ authToken, baseUrl, publicKey })\n // The ticket this mount will try to resume, if any.\n const resumingTicketId =\n ticketId !== undefined && ticketId !== refusedTicketId\n ? ticketId\n : undefined\n // The ticket the URL now loaded is actually trying to resume, which is not\n // always the prop: a remount resumes what the bridge reported. Refusal has to\n // key off this, or a bridge-derived resume the server rejects leaves the\n // WebView on the error page with every later recovery retrying the same dead\n // ticket.\n const [attemptedTicketId, setAttemptedTicketId] = useState<\n string | undefined\n >(undefined)\n // Whether the page on screen closed itself. End chat posts `closeChat` and\n // then `startNewChat`, so a page torn down on the first never delivers the\n // second: the host keeps the ended ticket and reopens it read-only. Web has\n // the same pair of messages and hides the iframe rather than unloading it.\n //\n // The gap between the two is not a moment. The page posts the second from a\n // later effect that waits on the end and CSAT requests settling, so whatever\n // this does to the page has to leave it running for seconds afterwards. That\n // is what decides how the close hides; see `stylesheet.closed`.\n //\n // Hiding moves neither `generation` nor `loaded`, so a hidden WebView is still\n // the current page: its bridge messages are still accepted and its\n // process-loss events still recover it. A close belongs to one conversation,\n // and `load` retires it when ownership moves, so nothing is ever born hidden.\n const [closed, setClosed] = useState(false)\n\n const load = useCallback(\n (\n client: LorikeetClient,\n forIdentity: string,\n intendedTicketId: string | undefined,\n resume: { session?: string; ticketId: string } | null,\n // Recovery has to remount even when the URL is identical, which it is for\n // a host-supplied ticket and for every recovery after the first: the point\n // is to replace a dead or stale WebView, not to navigate it.\n force = false,\n ) => {\n const next = client.getWebviewUrl({\n token: authToken,\n ticket_id: resume?.ticketId,\n session: resume?.session,\n })\n const unchanged = uriRef.current === next\n // Retiring the close here rather than scoping it to the identity that\n // closed it is what stops a host switching customers and back from\n // reapplying a close to a conversation nobody closed. Recovery keeps\n // ownership, so a closed chat is not reopened by a remount, and neither is\n // it by the effect re-running for an inline `metadata` object.\n if (\n loadedForRef.current.identity !== forIdentity ||\n loadedForRef.current.intendedTicketId !== intendedTicketId\n ) {\n setClosed(false)\n }\n // Ownership is recorded even when nothing needs to load. After a refused\n // resume the follow-up effect recomputes the same /widget URL, and\n // skipping this would leave ownership naming the rejected ticket, so the\n // fresh page's own reports would be rejected and its conversation\n // forgotten.\n uriRef.current = next\n loadedForRef.current = { identity: forIdentity, intendedTicketId }\n // An unchanged URL must not remount on its own: a host passing an inline\n // object for `customisations` or `metadata` re-runs the effect on every\n // render, and remounting there would cost the customer their place.\n if (unchanged && !force) {\n // Ownership can still have moved: two spellings of the same base URL\n // produce one URL but different identities. `loaded` gates rendering,\n // so leaving it stale here blanks the widget for good.\n setLoaded((previous) =>\n previous === null ||\n previous.identity !== forIdentity ||\n previous.intendedTicketId !== intendedTicketId\n ? { identity: forIdentity, intendedTicketId, uri: next }\n : previous,\n )\n return\n }\n setLoaded({ identity: forIdentity, intendedTicketId, uri: next })\n setAttemptedTicketId(resume?.ticketId)\n remount()\n },\n [authToken, remount],\n )\n\n useEffect(() => {\n const lorikeetClient = new LorikeetClient({\n public_key: publicKey,\n api_base: baseUrl,\n metadata,\n customisations: {\n ...customisations,\n hideHeader:\n customisations?.hideHeader === undefined\n ? true\n : customisations.hideHeader,\n },\n })\n setLorikeetClient(lorikeetClient)\n // Rebuilding for a `metadata`, `customisations` or `session` change must not\n // drop a self-created conversation back to /widget, which would mint a\n // second ticket and abandon the one the customer is in.\n const resume =\n resumingTicketId !== undefined\n ? { session, ticketId: resumingTicketId }\n : resumableConversation(observedRef.current, identity, undefined)\n load(lorikeetClient, identity, resumingTicketId, resume)\n }, [\n publicKey,\n baseUrl,\n authToken,\n customisations,\n metadata,\n session,\n resumingTicketId,\n identity,\n load,\n ])\n\n // A WebView whose renderer process was killed is a permanently blank view: it\n // does not recover on its own and the host has no way to notice. Remounting\n // is the only reliable recovery, on both platforms.\n const reload = useCallback(() => {\n const resume = resumableConversation(\n observedRef.current,\n identity,\n resumingTicketId,\n )\n if (lorikeetClient !== null && resume !== null) {\n load(lorikeetClient, identity, resumingTicketId, resume, true)\n return\n }\n remount()\n }, [identity, load, lorikeetClient, remount, resumingTicketId])\n\n useEffect(() => {\n const subscription = AppState.addEventListener('change', (next) => {\n if (next === 'active') {\n if (\n shouldReloadAfterBackground(backgroundedAtRef.current, Date.now())\n ) {\n reload()\n }\n backgroundedAtRef.current = null\n return\n }\n // Only 'background' counts. On iOS 'inactive' also covers Notification\n // Center, the app switcher and call banners, where the app stays on\n // screen and the stream is never suspended; treating those as an absence\n // would remount and discard the customer's draft for nothing.\n if (next === 'background') {\n backgroundedAtRef.current ??= Date.now()\n }\n })\n return () => {\n subscription.remove()\n }\n }, [reload])\n\n const startNewChat = useCallback(() => {\n // The replacement conversation is the one the customer asked for, so a close\n // that hid the page it replaces must not hide this one too. Explicit because\n // a new chat keeps the same ownership, which is what `load` keys off.\n setClosed(false)\n // After a recovery, `uri` is a resume URL even when the host never supplied\n // a ticket, so remounting would reopen the conversation instead of starting\n // one. Retire what was observed, or the next load would resume it anyway.\n if (lorikeetClient !== null && resumingTicketId === undefined) {\n observedRef.current = null\n load(lorikeetClient, identity, undefined, null, true)\n return\n }\n // With a host-supplied ticket the documented behaviour is to reload it; the\n // host starts a new conversation by clearing its own stored ticket.\n remount()\n }, [identity, load, lorikeetClient, remount, resumingTicketId])\n\n const handleEvent = useCallback(\n (event: LorikeetWidgetEvent) => {\n // A handler outlives its page: when credentials or the intended ticket\n // change, the component re-renders and the old WebView is replaced, but\n // events already in flight still arrive at the handler installed on it.\n //\n // `identity` and `resumingTicketId` here are the ones this handler was\n // created with, and stamping observations with those is what keeps a late\n // report from the old page out of the new one's conversation: it gets the\n // old identity, which `resumableConversation` then refuses. That is the\n // load-bearing part, and it matters because the ticket route authorises a\n // ticket-bound session independently of the trusted token, so accepting\n // one would be a cross-customer read rather than the wrong conversation.\n //\n // The gate below is the narrower guarantee that a replaced page cannot\n // speak for the one that replaced it: its errors must not abandon the new\n // conversation, and its reports must not overwrite the new observation.\n const isCurrentPage =\n generation === generationRef.current &&\n loadedForRef.current.identity === identity &&\n loadedForRef.current.intendedTicketId === resumingTicketId\n\n if (isCurrentPage) {\n if (\n event.type === 'persist_ticket_id' ||\n event.type === 'persist_session'\n ) {\n const current =\n observedRef.current?.identity === identity &&\n observedRef.current.intendedTicketId === resumingTicketId\n ? observedRef.current\n : null\n observedRef.current = {\n ...current,\n ...(event.type === 'persist_ticket_id'\n ? { ticketId: event.ticketId }\n : { session: event.session }),\n identity,\n intendedTicketId: resumingTicketId,\n }\n }\n\n // Hide rather than unmount, so the `new_chat` that follows on End chat\n // still has a page to arrive at.\n //\n // This reaches only as far as the SDK's own rendering. The close is\n // still reported on, and a host that unmounts on it destroys the page\n // before the `new_chat` seconds behind it arrives, which is the failure\n // this hide exists to prevent. The SDK cannot stop that: it has no way\n // to ask a host to keep rendering, and the message it would need does\n // not exist: one that says the conversation has ended and it is safe to\n // tear down. That signal is page-side work whose shape is still being\n // agreed, so nothing here invents one. Until then this fixes hosts that\n // leave the widget mounted, and leaves hosts that unmount as they were.\n if (event.type === 'close') {\n setClosed(true)\n }\n\n // The page cannot replace itself, so without this the ended conversation\n // stays on screen and the host goes on resuming a ticket that only\n // reopens read-only. Same route as the header control rather than a\n // second way to retire a conversation.\n if (event.type === 'new_chat') {\n startNewChat()\n }\n\n // Only a resume can be abandoned; a fresh /widget load reporting the\n // same code would loop if it retried, and a token error survives the\n // retry.\n if (\n event.type === 'error' &&\n attemptedTicketId !== undefined &&\n isUnusableConversationCode(event.error)\n ) {\n // The conversation is gone, so remembering it would send the remount\n // straight back into the same refusal.\n observedRef.current = null\n setRefusedTicketId(attemptedTicketId)\n // When the refused ticket came from the bridge rather than the prop,\n // nothing upstream changes, so no effect would recompute the URL and\n // the customer would be left on the error page. Start fresh here.\n if (lorikeetClient !== null) {\n // Intent is `undefined`, not the ticket being abandoned: this load\n // is a fresh conversation, and stamping it with the rejected ticket\n // would leave ownership naming something no render will ask for.\n load(lorikeetClient, identity, undefined, null, true)\n }\n }\n }\n\n // Reported whatever the gate decided. A host watching the conversation\n // should hear about a page that is on its way out, not only the one the\n // SDK is still acting for.\n onEvent?.(event)\n },\n [\n attemptedTicketId,\n generation,\n identity,\n load,\n lorikeetClient,\n onEvent,\n resumingTicketId,\n startNewChat,\n ],\n )\n\n // The raw bridge, kept working for hosts that read it before there were\n // events. Deprecated rather than removed: the subscriber is live on this.\n const handleMessage = useCallback(\n (event: WebViewMessageEvent) => {\n onWebViewMessage?.(event)\n webviewProps?.onMessage?.(event)\n },\n [onWebViewMessage, webviewProps],\n )\n\n // Nothing is rendered until the URL has been rebuilt for the credentials now\n // in props. Rendering sooner would mount a WebView pointed at the previous\n // customer's conversation, which their session still authorises.\n if (\n lorikeetClient === null ||\n loaded === null ||\n loaded.identity !== identity ||\n loaded.intendedTicketId !== resumingTicketId\n ) {\n return null\n }\n\n return (\n <SafeAreaView edges={['top', 'left', 'right']} style={stylesheet.root}>\n <View style={stylesheet.container}>\n <LorikeetWidgetHeader\n onIconPress={startNewChat}\n style={styles?.header}\n title={customisations?.title ?? 'Lorikeet AI Agent'}\n />\n {/*\n Wrapped rather than styled through `webviewProps`, which the host can\n override: a host passing its own `style` would otherwise be able to\n keep a closed conversation on screen. The header stays, so a close with\n no `startNewChat` behind it, a plain minimise, still leaves the\n customer a way into a new conversation.\n\n Out of sight rather than out of the layout, and the other three props\n are what make that safe. A view at zero opacity is still touchable and\n still read out by a screen reader, so a conversation the customer has\n ended would otherwise remain both, invisibly.\n */}\n <View\n accessibilityElementsHidden={closed}\n importantForAccessibility={closed ? 'no-hide-descendants' : 'auto'}\n pointerEvents={closed ? 'none' : 'auto'}\n style={closed ? stylesheet.closed : stylesheet.webView}\n >\n <LorikeetWidgetWebView\n baseUrl={lorikeetClient.baseUrl}\n // The identity is in the key so that a credential or ticket change\n // unmounts the old WebView on the very render that changes it. Were\n // it keyed on `generation` alone, the old WebView would survive that\n // render holding the new `onMessage`, and a persistence event from\n // the page it is still showing would be recorded against the new\n // identity: a later recovery could then reopen the previous\n // customer's conversation. The window is between the effect that\n // bumps `generation` and the commit that unmounts, so no test here\n // reaches it; the component harness cannot deliver a message between\n // React's commit phases.\n key={`${identity}|${resumingTicketId ?? ''}|${generation}`}\n onEvent={handleEvent}\n ref={webViewRef}\n uri={loaded.uri}\n webviewProps={{\n allowsInlineMediaPlayback: true,\n mediaPlaybackRequiresUserAction: false,\n scalesPageToFit: true,\n startInLoadingState: true,\n style: stylesheet.webView,\n ...webviewProps,\n // The bridge and the crash recovery are the SDK's job, so these\n // wrap the host's handlers instead of yielding to them.\n onMessage: handleMessage,\n // Gated like the bridge handler: a process-loss event queued by a\n // WebView that has since been replaced would reload against the\n // ownership of that render, and the gate on `loaded` would then\n // never be satisfiable again.\n //\n // `onContentProcessDidTerminate` is not exclusively a real process\n // death. `react-native-webview` synthesises one whenever it tears\n // the WebView down: `destroyWebView` ends by nilling `_webView` and\n // then calling this handler, and Fabric calls it from\n // `prepareForRecycle`. So a recycled view can arrive here looking\n // like a crash. Measured on the close path and it did not fire, but\n // the call exists, and the generation gate is what keeps a stray one\n // from reloading against a page that is already gone.\n onRenderProcessGone: (event) => {\n if (generation === generationRef.current) {\n reload()\n }\n webviewProps?.onRenderProcessGone?.(event)\n },\n onContentProcessDidTerminate: (event) => {\n if (generation === generationRef.current) {\n reload()\n }\n webviewProps?.onContentProcessDidTerminate?.(event)\n },\n }}\n />\n </View>\n </View>\n </SafeAreaView>\n )\n}\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,51 +1,103 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import * as react from 'react';
3
- import { WebViewProps, WebViewMessageEvent, WebView } from 'react-native-webview';
4
- import { Customisations } from '@lorikeetai/web-sdk/types';
5
- import { StyleProp, TextStyle } from 'react-native';
6
-
1
+ import { WebView, WebViewMessageEvent, WebViewProps } from "react-native-webview";
2
+ import { Customisations } from "@lorikeetai/web-sdk/types";
3
+ import { StyleProp, TextStyle } from "react-native";
4
+ //#region src/conversation-metadata.d.ts
5
+ type ConversationMetadata = Record<string, unknown>;
6
+ //#endregion
7
+ //#region src/events.d.ts
8
+ type LorikeetWidgetEvent = {
9
+ type: 'ready';
10
+ } | {
11
+ type: 'ticket_created';
12
+ ticketId: string;
13
+ } | {
14
+ type: 'customer_message';
15
+ } | {
16
+ type: 'agent_message';
17
+ } | {
18
+ type: 'close';
19
+ reason?: 'ended_by_customer' | 'minimised';
20
+ } | {
21
+ type: 'conversation_ended';
22
+ } | {
23
+ type: 'new_chat';
24
+ } | {
25
+ type: 'persist_ticket_id';
26
+ ticketId: string;
27
+ } | {
28
+ type: 'persist_session';
29
+ session: string;
30
+ } | {
31
+ type: 'error';
32
+ error: string;
33
+ } | {
34
+ type: 'preload_complete';
35
+ clientStart: boolean;
36
+ lazyTicketCreationEnabled: boolean;
37
+ };
38
+ type LorikeetWidgetEventType = 'agent_message' | 'close' | 'conversation_ended' | 'customer_message' | 'error' | 'new_chat' | 'persist_session' | 'persist_ticket_id' | 'preload_complete' | 'ready' | 'ticket_created';
39
+ //#endregion
40
+ //#region src/types.d.ts
7
41
  interface LorikeetInitOptions {
8
- api_base?: string;
9
- customisations?: Customisations;
10
- public_key: string;
42
+ api_base?: string;
43
+ customisations?: Customisations;
44
+ metadata?: ConversationMetadata;
45
+ public_key: string;
11
46
  }
12
47
  type LorikeetWidgetWebViewProps = {
13
- uri: string;
14
- baseUrl?: string;
15
- webviewProps?: WebViewProps;
48
+ uri: string;
49
+ baseUrl?: string;
50
+ webviewProps?: WebViewProps;
51
+ onEvent?: (event: LorikeetWidgetEvent) => void;
16
52
  };
17
53
  type LorikeetWidgetHeaderProps = {
18
- title: string;
19
- style?: StyleProp<TextStyle>;
20
- icon?: React.ReactNode;
21
- onIconPress?: () => void;
54
+ title: string;
55
+ style?: StyleProp<TextStyle>;
56
+ icon?: React.ReactNode;
57
+ onIconPress?: () => void;
22
58
  };
23
59
  type LorikeetWidgetViewProps = {
24
- publicKey: string;
25
- baseUrl?: string;
26
- authToken: string;
27
- onWebViewMessage?: (event: WebViewMessageEvent) => void;
28
- customisations?: Customisations;
29
- styles?: {
30
- header?: StyleProp<TextStyle>;
31
- };
60
+ publicKey: string;
61
+ baseUrl?: string;
62
+ authToken: string;
63
+ onWebViewMessage?: (event: WebViewMessageEvent) => void;
64
+ onEvent?: (event: LorikeetWidgetEvent) => void;
65
+ customisations?: Customisations;
66
+ styles?: {
67
+ header?: StyleProp<TextStyle>;
68
+ };
69
+ ticketId?: string;
70
+ session?: string;
71
+ metadata?: ConversationMetadata;
72
+ webviewProps?: WebViewProps;
32
73
  };
33
-
34
- declare const LorikeetWidgetWebView: react.ForwardRefExoticComponent<LorikeetWidgetWebViewProps & react.RefAttributes<WebView<{}>>>;
35
- declare const LorikeetWidgetHeader: ({ title, style, icon, onIconPress, }: LorikeetWidgetHeaderProps) => react_jsx_runtime.JSX.Element;
36
- declare function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, styles, onWebViewMessage, }: LorikeetWidgetViewProps): react_jsx_runtime.JSX.Element | null;
37
-
74
+ //#endregion
75
+ //#region src/widget.d.ts
76
+ declare const LorikeetWidgetWebView: import("react").ForwardRefExoticComponent<LorikeetWidgetWebViewProps & import("react").RefAttributes<WebView<{}>>>;
77
+ declare const LorikeetWidgetHeader: ({ title, style, icon, onIconPress }: LorikeetWidgetHeaderProps) => import("react").JSX.Element;
78
+ declare function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, styles, onEvent, onWebViewMessage, ticketId, session, metadata, webviewProps }: LorikeetWidgetViewProps): import("react").JSX.Element | null;
79
+ //#endregion
80
+ //#region src/client.d.ts
38
81
  declare class LorikeetClient {
39
- baseUrl: string;
40
- customisations: Customisations | null;
41
- publicKey: string;
42
- constructor({ customisations, api_base, public_key }: LorikeetInitOptions);
43
- getWebviewUrl: ({ customer_email, customer_name, token, ticket_id, }: {
44
- customer_email?: string;
45
- customer_name?: string;
46
- token: string;
47
- ticket_id?: string;
48
- }) => string;
82
+ baseUrl: string;
83
+ customisations: Customisations | null;
84
+ metadata: ConversationMetadata | null;
85
+ publicKey: string;
86
+ constructor({ customisations, api_base, metadata, public_key }: LorikeetInitOptions);
87
+ getWebviewUrl: ({ customer_email, customer_name, token, ticket_id, session }: {
88
+ customer_email?: string;
89
+ customer_name?: string;
90
+ session?: string;
91
+ ticket_id?: string;
92
+ token: string;
93
+ }) => string;
49
94
  }
50
-
51
- export { LorikeetClient, type LorikeetInitOptions, LorikeetWidgetHeader, type LorikeetWidgetHeaderProps, LorikeetWidgetView, type LorikeetWidgetViewProps, LorikeetWidgetWebView, type LorikeetWidgetWebViewProps };
95
+ //#endregion
96
+ //#region src/errors.d.ts
97
+ declare const LORIKEET_ERROR_CODES: readonly ["MISSING_TOKEN", "CORRUPT_TOKEN_DATA", "DECRYPT_FAILED", "EXPIRED_TOKEN", "MALFORMED_TOKEN", "MISSING_PUBLIC_KEY", "UNKNOWN_PUBLIC_KEY", "CUSTOMER_ID_CONFLICT", "CUSTOMER_ID_MISSING", "FATAL_ERROR", "INVALID_SESSION", "DOMAIN_NOT_ALLOWED", "TICKET_NOT_FOUND"];
98
+ type LorikeetErrorCode = (typeof LORIKEET_ERROR_CODES)[number];
99
+ declare function isUnusableConversationCode(code: string): boolean;
100
+ declare function isUnusableConversation(message: unknown): boolean;
101
+ //#endregion
102
+ export { LORIKEET_ERROR_CODES, LorikeetClient, type LorikeetErrorCode, type LorikeetInitOptions, type LorikeetWidgetEvent, type LorikeetWidgetEventType, LorikeetWidgetHeader, type LorikeetWidgetHeaderProps, LorikeetWidgetView, type LorikeetWidgetViewProps, LorikeetWidgetWebView, type LorikeetWidgetWebViewProps, isUnusableConversation, isUnusableConversationCode };
103
+ //# sourceMappingURL=index.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/conversation-metadata.ts","../src/events.ts","../src/types.ts","../src/widget.tsx","../src/client.ts","../src/errors.ts"],"mappings":";;;;KAEY,uBAAuB;;;KCUvB;EAEN;;EAKA;EAAwB;;EAExB;;EAEA;;EASA;EAAe;;EAOf;;EAKA;;EAKA;EAA2B;;EAE3B;EAAyB;;EAEzB;EAAe;;EAMf;EACA;EACA;;KAIM;;;UC1DK;EAIf;EAIA,iBAAiB;EAKjB,WAAW;EAIX;;KAGU;EAIV;EAIA;EAIA,eAAe;EASf,WAAW,OAAO;;KAGR;EAIV;EAIA,QAAQ,UAAU;EAIlB,OAAO,MAAM;EAIb;;KAGU;EAIV;EAIA;EAIA;EAQA,oBAAoB,OAAO;EAK3B,WAAW,OAAO;EAIlB,iBAAiB;EAIjB;IACE,SAAS,UAAU;;EASrB;EAMA;EAMA,WAAW;EAOX,eAAe;;;;cCxDJ,uCAAqB,0BAAA,6CAAA,cAAA;cA2HrB,yBAAwB,OAAA,OAAA,MAAA,eAKlC,8CAAyB,IAAA;iBAYZ,qBACd,WACA,SACA,WACA,gBACA,QACA,SACA,kBACA,UACA,SACA,UACA,gBACC,0CAAuB,IAAA;;;cC9Nb;EACX;EACA,gBAAgB;EAChB,UAAU;EACV;EAEY,cACV,gBACA,UACA,UACA,cACC;EAeH,kBAAiB,gBAAA,eAAA,OAAA,WAAA;IAOf;IACA;IACA;IACA;IACA;;;;;cCtCS;KAgBD,4BAA4B;iBAexB,2BAA2B;iBAK3B,uBAAuB"}
package/dist/index.d.ts CHANGED
@@ -1,51 +1,103 @@
1
- import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import * as react from 'react';
3
- import { WebViewProps, WebViewMessageEvent, WebView } from 'react-native-webview';
4
- import { Customisations } from '@lorikeetai/web-sdk/types';
5
- import { StyleProp, TextStyle } from 'react-native';
6
-
1
+ import { StyleProp, TextStyle } from "react-native";
2
+ import { WebView, WebViewMessageEvent, WebViewProps } from "react-native-webview";
3
+ import { Customisations } from "@lorikeetai/web-sdk/types";
4
+ //#region src/conversation-metadata.d.ts
5
+ type ConversationMetadata = Record<string, unknown>;
6
+ //#endregion
7
+ //#region src/events.d.ts
8
+ type LorikeetWidgetEvent = {
9
+ type: 'ready';
10
+ } | {
11
+ type: 'ticket_created';
12
+ ticketId: string;
13
+ } | {
14
+ type: 'customer_message';
15
+ } | {
16
+ type: 'agent_message';
17
+ } | {
18
+ type: 'close';
19
+ reason?: 'ended_by_customer' | 'minimised';
20
+ } | {
21
+ type: 'conversation_ended';
22
+ } | {
23
+ type: 'new_chat';
24
+ } | {
25
+ type: 'persist_ticket_id';
26
+ ticketId: string;
27
+ } | {
28
+ type: 'persist_session';
29
+ session: string;
30
+ } | {
31
+ type: 'error';
32
+ error: string;
33
+ } | {
34
+ type: 'preload_complete';
35
+ clientStart: boolean;
36
+ lazyTicketCreationEnabled: boolean;
37
+ };
38
+ type LorikeetWidgetEventType = 'agent_message' | 'close' | 'conversation_ended' | 'customer_message' | 'error' | 'new_chat' | 'persist_session' | 'persist_ticket_id' | 'preload_complete' | 'ready' | 'ticket_created';
39
+ //#endregion
40
+ //#region src/types.d.ts
7
41
  interface LorikeetInitOptions {
8
- api_base?: string;
9
- customisations?: Customisations;
10
- public_key: string;
42
+ api_base?: string;
43
+ customisations?: Customisations;
44
+ metadata?: ConversationMetadata;
45
+ public_key: string;
11
46
  }
12
47
  type LorikeetWidgetWebViewProps = {
13
- uri: string;
14
- baseUrl?: string;
15
- webviewProps?: WebViewProps;
48
+ uri: string;
49
+ baseUrl?: string;
50
+ webviewProps?: WebViewProps;
51
+ onEvent?: (event: LorikeetWidgetEvent) => void;
16
52
  };
17
53
  type LorikeetWidgetHeaderProps = {
18
- title: string;
19
- style?: StyleProp<TextStyle>;
20
- icon?: React.ReactNode;
21
- onIconPress?: () => void;
54
+ title: string;
55
+ style?: StyleProp<TextStyle>;
56
+ icon?: React.ReactNode;
57
+ onIconPress?: () => void;
22
58
  };
23
59
  type LorikeetWidgetViewProps = {
24
- publicKey: string;
25
- baseUrl?: string;
26
- authToken: string;
27
- onWebViewMessage?: (event: WebViewMessageEvent) => void;
28
- customisations?: Customisations;
29
- styles?: {
30
- header?: StyleProp<TextStyle>;
31
- };
60
+ publicKey: string;
61
+ baseUrl?: string;
62
+ authToken: string;
63
+ onWebViewMessage?: (event: WebViewMessageEvent) => void;
64
+ onEvent?: (event: LorikeetWidgetEvent) => void;
65
+ customisations?: Customisations;
66
+ styles?: {
67
+ header?: StyleProp<TextStyle>;
68
+ };
69
+ ticketId?: string;
70
+ session?: string;
71
+ metadata?: ConversationMetadata;
72
+ webviewProps?: WebViewProps;
32
73
  };
33
-
34
- declare const LorikeetWidgetWebView: react.ForwardRefExoticComponent<LorikeetWidgetWebViewProps & react.RefAttributes<WebView<{}>>>;
35
- declare const LorikeetWidgetHeader: ({ title, style, icon, onIconPress, }: LorikeetWidgetHeaderProps) => react_jsx_runtime.JSX.Element;
36
- declare function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, styles, onWebViewMessage, }: LorikeetWidgetViewProps): react_jsx_runtime.JSX.Element | null;
37
-
74
+ //#endregion
75
+ //#region src/widget.d.ts
76
+ declare const LorikeetWidgetWebView: import("react").ForwardRefExoticComponent<LorikeetWidgetWebViewProps & import("react").RefAttributes<WebView<{}>>>;
77
+ declare const LorikeetWidgetHeader: ({ title, style, icon, onIconPress }: LorikeetWidgetHeaderProps) => import("react").JSX.Element;
78
+ declare function LorikeetWidgetView({ publicKey, baseUrl, authToken, customisations, styles, onEvent, onWebViewMessage, ticketId, session, metadata, webviewProps }: LorikeetWidgetViewProps): import("react").JSX.Element | null;
79
+ //#endregion
80
+ //#region src/client.d.ts
38
81
  declare class LorikeetClient {
39
- baseUrl: string;
40
- customisations: Customisations | null;
41
- publicKey: string;
42
- constructor({ customisations, api_base, public_key }: LorikeetInitOptions);
43
- getWebviewUrl: ({ customer_email, customer_name, token, ticket_id, }: {
44
- customer_email?: string;
45
- customer_name?: string;
46
- token: string;
47
- ticket_id?: string;
48
- }) => string;
82
+ baseUrl: string;
83
+ customisations: Customisations | null;
84
+ metadata: ConversationMetadata | null;
85
+ publicKey: string;
86
+ constructor({ customisations, api_base, metadata, public_key }: LorikeetInitOptions);
87
+ getWebviewUrl: ({ customer_email, customer_name, token, ticket_id, session }: {
88
+ customer_email?: string;
89
+ customer_name?: string;
90
+ session?: string;
91
+ ticket_id?: string;
92
+ token: string;
93
+ }) => string;
49
94
  }
50
-
51
- export { LorikeetClient, type LorikeetInitOptions, LorikeetWidgetHeader, type LorikeetWidgetHeaderProps, LorikeetWidgetView, type LorikeetWidgetViewProps, LorikeetWidgetWebView, type LorikeetWidgetWebViewProps };
95
+ //#endregion
96
+ //#region src/errors.d.ts
97
+ declare const LORIKEET_ERROR_CODES: readonly ["MISSING_TOKEN", "CORRUPT_TOKEN_DATA", "DECRYPT_FAILED", "EXPIRED_TOKEN", "MALFORMED_TOKEN", "MISSING_PUBLIC_KEY", "UNKNOWN_PUBLIC_KEY", "CUSTOMER_ID_CONFLICT", "CUSTOMER_ID_MISSING", "FATAL_ERROR", "INVALID_SESSION", "DOMAIN_NOT_ALLOWED", "TICKET_NOT_FOUND"];
98
+ type LorikeetErrorCode = (typeof LORIKEET_ERROR_CODES)[number];
99
+ declare function isUnusableConversationCode(code: string): boolean;
100
+ declare function isUnusableConversation(message: unknown): boolean;
101
+ //#endregion
102
+ export { LORIKEET_ERROR_CODES, LorikeetClient, type LorikeetErrorCode, type LorikeetInitOptions, type LorikeetWidgetEvent, type LorikeetWidgetEventType, LorikeetWidgetHeader, type LorikeetWidgetHeaderProps, LorikeetWidgetView, type LorikeetWidgetViewProps, LorikeetWidgetWebView, type LorikeetWidgetWebViewProps, isUnusableConversation, isUnusableConversationCode };
103
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/conversation-metadata.ts","../src/events.ts","../src/types.ts","../src/widget.tsx","../src/client.ts","../src/errors.ts"],"mappings":";;;;KAEY,uBAAuB;;;KCUvB;EAEN;;EAKA;EAAwB;;EAExB;;EAEA;;EASA;EAAe;;EAOf;;EAKA;;EAKA;EAA2B;;EAE3B;EAAyB;;EAEzB;EAAe;;EAMf;EACA;EACA;;KAIM;;;UC1DK;EAIf;EAIA,iBAAiB;EAKjB,WAAW;EAIX;;KAGU;EAIV;EAIA;EAIA,eAAe;EASf,WAAW,OAAO;;KAGR;EAIV;EAIA,QAAQ,UAAU;EAIlB,OAAO,MAAM;EAIb;;KAGU;EAIV;EAIA;EAIA;EAQA,oBAAoB,OAAO;EAK3B,WAAW,OAAO;EAIlB,iBAAiB;EAIjB;IACE,SAAS,UAAU;;EASrB;EAMA;EAMA,WAAW;EAOX,eAAe;;;;cCxDJ,uCAAqB,0BAAA,6CAAA,cAAA;cA2HrB,yBAAwB,OAAA,OAAA,MAAA,eAKlC,8CAAyB,IAAA;iBAYZ,qBACd,WACA,SACA,WACA,gBACA,QACA,SACA,kBACA,UACA,SACA,UACA,gBACC,0CAAuB,IAAA;;;cC9Nb;EACX;EACA,gBAAgB;EAChB,UAAU;EACV;EAEY,cACV,gBACA,UACA,UACA,cACC;EAeH,kBAAiB,gBAAA,eAAA,OAAA,WAAA;IAOf;IACA;IACA;IACA;IACA;;;;;cCtCS;KAgBD,4BAA4B;iBAexB,2BAA2B;iBAK3B,uBAAuB"}