@lorikeetai/react-native-sdk 2.1.0 → 2.2.0-beta.2

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"voice.js","names":["styles"],"sources":["../src/voice/call-icons.tsx","../src/voice/call-theme.ts","../src/voice/call-wave.tsx","../src/voice/use-lorikeet-call.ts","../src/voice/lorikeet-call-view.tsx"],"sourcesContent":["import type { ReactNode } from 'react'\n\nimport Svg, { Line, Path } from 'react-native-svg'\n\n// The web Voice Widget's Lucide icons (ISC licence, https://lucide.dev).\n\ninterface IconProps {\n color: string\n}\n\nconst Icon = ({ children }: { children: ReactNode }) => (\n <Svg fill=\"none\" height={24} viewBox=\"0 0 24 24\" width={24}>\n {children}\n </Svg>\n)\n\nconst stroke = (color: string) =>\n ({\n stroke: color,\n strokeLinecap: 'round',\n strokeLinejoin: 'round',\n strokeWidth: 2,\n }) as const\n\nexport const MicIcon = ({ color }: IconProps) => (\n <Icon>\n <Path\n d=\"M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z\"\n {...stroke(color)}\n />\n <Path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" {...stroke(color)} />\n <Line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\" {...stroke(color)} />\n </Icon>\n)\n\nexport const MicOffIcon = ({ color }: IconProps) => (\n <Icon>\n <Line x1=\"2\" x2=\"22\" y1=\"2\" y2=\"22\" {...stroke(color)} />\n <Path d=\"M18.89 13.23A7.12 7.12 0 0 0 19 12v-2\" {...stroke(color)} />\n <Path d=\"M5 10v2a7 7 0 0 0 12 5\" {...stroke(color)} />\n <Path d=\"M15 9.34V5a3 3 0 0 0-5.68-1.33\" {...stroke(color)} />\n <Path d=\"M9 9v3a3 3 0 0 0 5.12 2.12\" {...stroke(color)} />\n <Line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\" {...stroke(color)} />\n </Icon>\n)\n\nexport const PhoneOffIcon = ({ color }: IconProps) => (\n <Icon>\n <Path\n d=\"M10.68 13.31a16 16 0 0 0 3.41 2.6l1.27-1.27a2 2 0 0 1 2.11-.45 12.84 12.84 0 0 0 2.81.7 2 2 0 0 1 1.72 2v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.42 19.42 0 0 1-3.33-2.67m-2.67-3.34a19.79 19.79 0 0 1-3.07-8.63A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72 12.84 12.84 0 0 0 .7 2.81 2 2 0 0 1-.45 2.11L8.09 9.91\"\n {...stroke(color)}\n />\n <Line x1=\"22\" x2=\"2\" y1=\"2\" y2=\"22\" {...stroke(color)} />\n </Icon>\n)\n\nconst SPEAKER =\n 'M11 4.702a.705.705 0 0 0-1.203-.498L6.413 7.587A1.4 1.4 0 0 1 5.416 8H3a1 1 0 0 0-1 1v6a1 1 0 0 0 1 1h2.416a1.4 1.4 0 0 1 .997.413l3.383 3.384A.705.705 0 0 0 11 19.298z'\n\nexport const Volume2Icon = ({ color }: IconProps) => (\n <Icon>\n <Path d={SPEAKER} {...stroke(color)} />\n <Path d=\"M16 9a5 5 0 0 1 0 6\" {...stroke(color)} />\n <Path d=\"M19.364 18.364a9 9 0 0 0 0-12.728\" {...stroke(color)} />\n </Icon>\n)\n\nexport const VolumeXIcon = ({ color }: IconProps) => (\n <Icon>\n <Path d={SPEAKER} {...stroke(color)} />\n <Line x1=\"22\" x2=\"16\" y1=\"9\" y2=\"15\" {...stroke(color)} />\n <Line x1=\"16\" x2=\"22\" y1=\"9\" y2=\"15\" {...stroke(color)} />\n </Icon>\n)\n\n/** The design system's `LorikeetLogoMark`. `color` paints the whole mark in one colour. */\nexport const LorikeetLogoMark = ({\n color,\n size,\n}: {\n color?: string\n size: number\n}) => (\n <Svg fill=\"none\" height={size} viewBox=\"-45 -20 274 274\" width={size}>\n <Path\n d=\"M112.661 179.008C152.133 154.751 173.914 119.21 161.82 96.8859L81.0549 140.608L10.1309 179.008H112.667H112.661Z\"\n fill={color ?? '#FF29E5'}\n />\n <Path\n d=\"M0.242676 184.284C0.242676 211.383 41.3656 233.356 92.0903 233.356C142.815 233.356 183.938 211.389 183.938 184.29H0.242676V184.284Z\"\n fill={color ?? '#4348DC'}\n />\n <Path\n d=\"M83.4604 133.156C84.7013 131.069 85.9155 128.94 87.0924 126.763C111.185 82.1465 111.376 35.539 87.5131 22.6682L43.8872 103.459L4.88379 175.692L83.4604 133.156Z\"\n fill={color ?? '#EA4921'}\n />\n <Path\n d=\"M49.0555 82.6487C46.4408 36.5522 25.5864 0.643799 0.242676 0.643799V92.4482V173.048L49.0555 82.6487Z\"\n fill={color ?? '#FACB12'}\n />\n </Svg>\n)\n","import { processColor } from 'react-native'\n\nimport type { CustomisationTheme } from '../customisations'\n\n/** The colours the call screen paints, as the web Voice Widget's CSS variables. */\nexport interface CallColours {\n accent: string\n card: string\n /** The background of the mute buttons. */\n control: string\n inverseText: string\n /** Drawn at 80% opacity, as on the web. */\n ring: string\n shell: string\n subtleText: string\n text: string\n}\n\n// The design-system tokens the web Voice Widget falls back to.\nconst LIGHT: CallColours = {\n accent: 'hsl(233, 69%, 55%)',\n card: 'hsl(240, 3%, 97%)',\n control: 'hsl(0, 0%, 100%)',\n inverseText: 'hsl(0, 0%, 100%)',\n ring: 'hsl(240, 31%, 94%)',\n shell: 'hsl(0, 0%, 100%)',\n subtleText: 'hsl(240, 3%, 31%)',\n text: 'hsl(240, 3%, 4%)',\n}\n\nconst DARK: CallColours = {\n accent: 'hsl(233, 68%, 65%)',\n card: 'hsl(240, 3%, 8%)',\n control: 'hsl(240, 3%, 4%)',\n inverseText: 'hsl(240, 0%, 2%)',\n ring: 'hsl(233, 69%, 15%)',\n shell: 'hsl(240, 0%, 2%)',\n subtleText: 'hsl(240, 3%, 61%)',\n text: 'hsl(240, 3%, 81%)',\n}\n\n// A colour React Native cannot parse paints nothing, so it falls back instead.\n// That covers a CSS gradient, which React Native cannot draw.\nconst paintable = (colour: string | undefined) =>\n colour !== undefined && (processColor(colour) ?? undefined) !== undefined\n ? colour\n : undefined\n\n// Theme colours are HSL components, as the web reads them.\nconst hsl = (components: string | undefined) =>\n components ? paintable(`hsl(${components})`) : undefined\n\n/** `generateVoiceWidgetThemeStyle` from the web Voice Widget, for React Native. */\nexport function callColours(\n theme: CustomisationTheme | undefined,\n): CallColours {\n const base = theme?.colorMode === 'dark' ? DARK : LIGHT\n return {\n accent: hsl(theme?.brandColor) ?? base.accent,\n card:\n paintable(theme?.headerBackground) ??\n hsl(theme?.brandAccentColor) ??\n base.card,\n control: base.control,\n inverseText: hsl(theme?.inverseTextColor) ?? base.inverseText,\n ring: hsl(theme?.subtleBrandColor) ?? base.ring,\n shell:\n paintable(theme?.background) ?? hsl(theme?.backgroundColor) ?? base.shell,\n subtleText: base.subtleText,\n text: hsl(theme?.textColor) ?? base.text,\n }\n}\n","import { useEffect, useMemo, useRef, useState } from 'react'\n\nimport {\n AccessibilityInfo,\n Animated,\n Easing,\n StyleSheet,\n View,\n} from 'react-native'\n\n// The web Voice Widget's wave. Every eighth bar shares a phase, and each phase\n// starts `STAGGER_MS` after the one before.\nconst BARS = 24\nconst PHASES = 8\nconst CYCLE_MS = 1_440\nconst STAGGER_MS = 90\nconst MIN_HEIGHT = 8\nconst MAX_HEIGHT = 38\n// The web's reduced-motion height for a bar of an active call.\nconst STILL_HEIGHT = 20\n\nconst triangle = (x: number) => 1 - Math.abs(2 * x - 1)\n\n/**\n * Where one bar's height peaks and troughs as the loop runs from 0 to 1, so a\n * single animated value drives every phase.\n */\nconst phaseKeyframes = (phase: number) => {\n const frames = new Map<number, number>([\n [0, triangle(phase)],\n [1, triangle(phase)],\n ])\n for (const x of [0, 0.5]) {\n frames.set((x - phase + 1) % 1, triangle(x))\n }\n const sorted = [...frames.entries()].sort(([a], [b]) => a - b)\n return {\n inputRange: sorted.map(([at]) => at),\n outputRange: sorted.map(\n ([, height]) =>\n (MIN_HEIGHT + height * (MAX_HEIGHT - MIN_HEIGHT)) / MAX_HEIGHT,\n ),\n }\n}\n\nconst useReduceMotion = () => {\n const [reduceMotion, setReduceMotion] = useState(false)\n useEffect(() => {\n let mounted = true\n void AccessibilityInfo.isReduceMotionEnabled().then((enabled) => {\n if (mounted) {\n setReduceMotion(enabled)\n }\n })\n const subscription = AccessibilityInfo.addEventListener(\n 'reduceMotionChanged',\n setReduceMotion,\n )\n return () => {\n mounted = false\n subscription.remove()\n }\n }, [])\n return reduceMotion\n}\n\nexport function CallWave({\n active,\n color,\n}: {\n active: boolean\n color: string\n}) {\n const reduceMotion = useReduceMotion()\n const progress = useRef(new Animated.Value(0)).current\n const animate = active && !reduceMotion\n\n useEffect(() => {\n if (!animate) {\n return\n }\n const loop = Animated.loop(\n Animated.timing(progress, {\n duration: CYCLE_MS,\n easing: Easing.linear,\n toValue: 1,\n useNativeDriver: true,\n }),\n )\n loop.start()\n return () => {\n loop.stop()\n progress.setValue(0)\n }\n }, [animate, progress])\n\n const scales = useMemo(\n () =>\n Array.from({ length: PHASES }, (_, index) =>\n progress.interpolate(phaseKeyframes((index * STAGGER_MS) / CYCLE_MS)),\n ),\n [progress],\n )\n\n const stillScale = (active ? STILL_HEIGHT : MIN_HEIGHT) / MAX_HEIGHT\n return (\n <View\n accessibilityElementsHidden\n importantForAccessibility=\"no-hide-descendants\"\n style={styles.wave}\n >\n {Array.from({ length: BARS }, (_, index) => (\n <Animated.View\n key={index}\n style={[\n styles.bar,\n { backgroundColor: color },\n {\n transform: [\n {\n scaleY: animate\n ? (scales[index % PHASES] ?? stillScale)\n : stillScale,\n },\n ],\n },\n ]}\n />\n ))}\n </View>\n )\n}\n\nconst styles = StyleSheet.create({\n bar: {\n borderRadius: 999,\n height: MAX_HEIGHT,\n opacity: 0.45,\n width: 3,\n },\n wave: {\n alignItems: 'center',\n flexDirection: 'row',\n gap: 3,\n height: 48,\n justifyContent: 'center',\n },\n})\n","import { useCallback, useEffect, useRef, useState } from 'react'\n\nimport { AudioSession, registerGlobals } from '@livekit/react-native'\nimport {\n createLocalAudioTrack,\n type LocalAudioTrack,\n RemoteAudioTrack,\n Room,\n RoomEvent,\n Track,\n} from 'livekit-client'\n\nimport type {\n LorikeetCall,\n LorikeetCallError,\n LorikeetCallSession,\n LorikeetCallState,\n UseLorikeetCallOptions,\n} from './types'\n\n/** The server's `LORIKEET_AGENT_IDENTITY`. The call ends when it leaves. */\nexport const LORIKEET_AGENT_IDENTITY = 'lorikeet-agent'\n\n// react-native-webrtc rejects a refused permission with a plain object named\n// `SecurityError`, not a DOMException, so match on the name alone.\nconst errorFor = (error: unknown): LorikeetCallError => {\n if (typeof error !== 'object' || error === null || !('name' in error)) {\n return 'failed'\n }\n return error.name === 'SecurityError' || error.name === 'NotAllowedError'\n ? 'microphone_denied'\n : 'failed'\n}\n\nconst isSession = (value: unknown): value is LorikeetCallSession =>\n typeof value === 'object' &&\n value !== null &&\n 'conversationId' in value &&\n typeof value.conversationId === 'string' &&\n 'token' in value &&\n typeof value.token === 'string' &&\n 'wsUrl' in value &&\n typeof value.wsUrl === 'string'\n\n/**\n * Runs a Lorikeet voice call over LiveKit's native audio, with the web Voice\n * Widget's states and errors.\n */\nexport function useLorikeetCall({\n getSession,\n onError,\n}: UseLorikeetCallOptions): LorikeetCall {\n const [state, setState] = useState<LorikeetCallState>('idle')\n const [error, setError] = useState<LorikeetCallError | undefined>()\n const [ended, setEnded] = useState(false)\n const [microphoneMuted, setMicrophoneMutedState] = useState(false)\n const [speakerMuted, setSpeakerMutedState] = useState(false)\n\n const stateRef = useRef(state)\n const generationRef = useRef(0)\n const localTrackRef = useRef<LocalAudioTrack | null>(null)\n const roomRef = useRef<Room | null>(null)\n const speakerMutedRef = useRef(false)\n const audioSessionActiveRef = useRef(false)\n // The previous call's teardown, so the next call cannot start its audio\n // session only to have that teardown stop it.\n const audioSessionStoppedRef = useRef<Promise<void>>(Promise.resolve())\n // Read at call time, so a host passing inline functions keeps `start` stable.\n const optionsRef = useRef({ getSession, onError })\n optionsRef.current = { getSession, onError }\n\n const transitionTo = useCallback((next: LorikeetCallState) => {\n stateRef.current = next\n setState(next)\n }, [])\n\n const cleanup = useCallback(() => {\n localTrackRef.current?.stop()\n localTrackRef.current = null\n const room = roomRef.current\n roomRef.current = null\n // Nothing is left to report a teardown failure to, and a failed disconnect\n // must not leave the native audio session running.\n const disconnected = (room ? room.disconnect() : Promise.resolve()).catch(\n () => undefined,\n )\n if (audioSessionActiveRef.current) {\n audioSessionActiveRef.current = false\n audioSessionStoppedRef.current = disconnected\n .then(() => AudioSession.stopAudioSession())\n .catch(() => undefined)\n }\n }, [])\n\n const resetMutes = useCallback(() => {\n setMicrophoneMutedState(false)\n setSpeakerMutedState(false)\n speakerMutedRef.current = false\n }, [])\n\n const end = useCallback(() => {\n generationRef.current += 1\n cleanup()\n setEnded(stateRef.current === 'connecting' || stateRef.current === 'active')\n setError(undefined)\n resetMutes()\n transitionTo('idle')\n }, [cleanup, resetMutes, transitionTo])\n\n const fail = useCallback(\n (callError: LorikeetCallError) => {\n generationRef.current += 1\n cleanup()\n setEnded(false)\n setError(callError)\n transitionTo('error')\n },\n [cleanup, transitionTo],\n )\n\n useEffect(() => {\n return () => {\n generationRef.current += 1\n cleanup()\n }\n }, [cleanup])\n\n const start = useCallback(async () => {\n if (stateRef.current !== 'idle' && stateRef.current !== 'error') {\n return\n }\n\n setError(undefined)\n setEnded(false)\n resetMutes()\n transitionTo('starting')\n const generation = ++generationRef.current\n const isStale = () => generationRef.current !== generation\n\n try {\n // `registerGlobals` sets this. Calling it again would replace any iOS\n // audio setup a host that already uses LiveKit configured itself.\n if (!('LiveKitReactNativeGlobal' in globalThis)) {\n registerGlobals()\n }\n await audioSessionStoppedRef.current\n if (isStale()) {\n return\n }\n audioSessionActiveRef.current = true\n await AudioSession.startAudioSession()\n if (isStale()) {\n return\n }\n\n const localTrack = await createLocalAudioTrack()\n if (isStale()) {\n localTrack.stop()\n return\n }\n localTrackRef.current = localTrack\n\n const session = await optionsRef.current.getSession()\n if (isStale()) {\n return\n }\n if (session === null) {\n fail('not_available')\n return\n }\n if (!isSession(session)) {\n throw new TypeError(\n 'getSession must resolve to { conversationId, wsUrl, token } or null',\n )\n }\n transitionTo('connecting')\n\n const room = new Room()\n roomRef.current = room\n const isCurrentRoom = () => roomRef.current === room\n room.on(RoomEvent.ParticipantDisconnected, (participant) => {\n if (\n isCurrentRoom() &&\n participant.identity === LORIKEET_AGENT_IDENTITY\n ) {\n end()\n }\n })\n room.on(RoomEvent.TrackSubscribed, (track) => {\n if (isCurrentRoom() && track instanceof RemoteAudioTrack) {\n track.setVolume(speakerMutedRef.current ? 0 : 1)\n }\n })\n // A failed join also emits Disconnected, just before `connect` rejects,\n // and has to surface as `failed` rather than as a hang-up.\n let joined = false\n room.on(RoomEvent.Disconnected, () => {\n if (isCurrentRoom() && joined) {\n end()\n }\n })\n\n await room.connect(session.wsUrl, session.token, { autoSubscribe: true })\n if (isStale()) {\n return\n }\n joined = true\n await room.localParticipant.publishTrack(localTrack, {\n source: Track.Source.Microphone,\n })\n if (isStale()) {\n return\n }\n transitionTo('active')\n } catch (caught) {\n if (isStale()) {\n return\n }\n const callError = errorFor(caught)\n fail(callError)\n if (callError === 'failed') {\n try {\n optionsRef.current.onError?.(caught)\n } catch {\n // A host's failing error handler must not turn into an unhandled\n // rejection from a `start()` that callers fire and forget.\n }\n }\n }\n }, [end, fail, resetMutes, transitionTo])\n\n const setMicrophoneMuted = useCallback((muted: boolean) => {\n const track = localTrackRef.current\n if (!track) {\n return\n }\n // Report what the track did rather than what was asked, so a mute that\n // fails is not shown as muted while the customer is still heard.\n const reconcile = () => {\n if (localTrackRef.current === track) {\n setMicrophoneMutedState(track.isMuted)\n }\n }\n void (muted ? track.mute() : track.unmute()).then(reconcile, reconcile)\n }, [])\n\n const setSpeakerMuted = useCallback((muted: boolean) => {\n speakerMutedRef.current = muted\n const participants = roomRef.current?.remoteParticipants.values() ?? []\n for (const participant of participants) {\n for (const { track } of participant.audioTrackPublications.values()) {\n if (track instanceof RemoteAudioTrack) {\n track.setVolume(muted ? 0 : 1)\n }\n }\n }\n setSpeakerMutedState(muted)\n }, [])\n\n return {\n end,\n ended,\n error,\n microphoneMuted,\n setMicrophoneMuted,\n setSpeakerMuted,\n speakerMuted,\n start,\n state,\n }\n}\n","import { type ReactNode, useEffect, useState } from 'react'\n\nimport {\n AccessibilityInfo,\n Image,\n Linking,\n Platform,\n type StyleProp,\n StyleSheet,\n Text,\n TouchableOpacity,\n View,\n type ViewStyle,\n} from 'react-native'\nimport { SafeAreaView } from 'react-native-safe-area-context'\nimport { SvgUri } from 'react-native-svg'\n\nimport type { Customisations } from '../customisations'\n\nimport {\n LorikeetLogoMark,\n MicIcon,\n MicOffIcon,\n PhoneOffIcon,\n Volume2Icon,\n VolumeXIcon,\n} from './call-icons'\nimport { type CallColours, callColours } from './call-theme'\nimport { CallWave } from './call-wave'\nimport type {\n LorikeetCall,\n LorikeetCallError,\n LorikeetCallState,\n UseLorikeetCallOptions,\n} from './types'\nimport { useLorikeetCall } from './use-lorikeet-call'\n\nexport interface LorikeetCallViewProps extends UseLorikeetCallOptions {\n /** The chat widget's customisations that apply to a call. */\n customisations?: Pick<\n Customisations,\n 'logoUrl' | 'theme' | 'title' | 'useSubtleBranding'\n >\n style?: StyleProp<ViewStyle>\n}\n\n// The web Voice Widget's copy, so a customer reads the same on both.\nconst ERROR_MESSAGES: Record<LorikeetCallError, string> = {\n failed: 'The call could not start. Please try again.',\n microphone_denied: 'Allow microphone access, then try again.',\n microphone_system_denied:\n 'Enable microphone access for your browser in system settings.',\n not_available: \"We can't start a call on this page. Please try again later.\",\n}\n\nconst errorHeading = (error: LorikeetCallError) =>\n error === 'not_available'\n ? \"Calling isn't available right now\"\n : 'Something went wrong'\n\n// A running clock, which `Intl.DurationFormat` cannot show.\nconst formatDuration = (seconds: number): string =>\n `${Math.floor(seconds / 60)}:${String(seconds % 60).padStart(2, '0')}`\n\nconst statusLabel = (\n state: LorikeetCallState,\n seconds: number,\n): string | undefined => {\n switch (state) {\n case 'active':\n return formatDuration(seconds)\n case 'starting':\n return 'Starting call'\n case 'connecting':\n return 'Connecting'\n case 'error':\n case 'idle':\n return undefined\n }\n}\n\n/** What a screen reader hears when the call state changes. The clock is left out. */\nconst announcementFor = ({\n ended,\n error,\n state,\n}: LorikeetCall): string | undefined => {\n switch (state) {\n case 'active':\n return 'Listening'\n case 'error':\n return `${errorHeading(error ?? 'failed')}. ${ERROR_MESSAGES[error ?? 'failed']}`\n case 'idle':\n return ended ? 'Call ended' : undefined\n case 'starting':\n case 'connecting':\n return statusLabel(state, 0)\n }\n}\n\nconst useCallSeconds = (state: LorikeetCallState) => {\n const [seconds, setSeconds] = useState(0)\n useEffect(() => {\n if (state !== 'active') {\n setSeconds(0)\n return\n }\n const interval = setInterval(() => setSeconds((value) => value + 1), 1_000)\n return () => clearInterval(interval)\n }, [state])\n return seconds\n}\n\nfunction CallLogo({ logoUrl }: { logoUrl: string | undefined }) {\n if (!logoUrl) {\n return <LorikeetLogoMark size={48} />\n }\n // `Image` draws no SVG, and logos are often SVG.\n if (/\\.svg($|[?#])/i.test(logoUrl)) {\n return <SvgUri height={48} uri={logoUrl} width={48} />\n }\n return (\n <Image resizeMode=\"contain\" source={{ uri: logoUrl }} style={styles.logo} />\n )\n}\n\nfunction CallMessage({\n call,\n colours,\n}: {\n call: LorikeetCall\n colours: CallColours\n}) {\n if (call.state === 'error') {\n const error = call.error ?? 'failed'\n return (\n <>\n <Text style={[styles.bold, { color: colours.text }]}>\n {errorHeading(error)}\n </Text>\n <Text style={[styles.body, { color: colours.subtleText }]}>\n {ERROR_MESSAGES[error]}\n </Text>\n </>\n )\n }\n if (call.state === 'active') {\n return <Text style={[styles.body, { color: colours.text }]}>Listening</Text>\n }\n if (call.ended) {\n return (\n <View style={styles.marker}>\n <View\n style={[styles.markerRule, { backgroundColor: colours.subtleText }]}\n />\n <Text style={[styles.small, { color: colours.subtleText }]}>\n Call ended\n </Text>\n <View\n style={[styles.markerRule, { backgroundColor: colours.subtleText }]}\n />\n </View>\n )\n }\n return null\n}\n\nfunction RoundButton({\n children,\n disabled = false,\n label,\n onPress,\n style,\n}: {\n children: ReactNode\n disabled?: boolean\n label: string\n onPress: () => void\n style: StyleProp<ViewStyle>\n}) {\n return (\n <TouchableOpacity\n accessibilityLabel={label}\n accessibilityRole=\"button\"\n accessibilityState={{ disabled }}\n disabled={disabled}\n onPress={onPress}\n style={[styles.roundButton, style, disabled && styles.disabled]}\n >\n {children}\n </TouchableOpacity>\n )\n}\n\nfunction CallActions({\n call,\n colours,\n}: {\n call: LorikeetCall\n colours: CallColours\n}) {\n if (call.state === 'idle' || call.state === 'error') {\n return (\n <TouchableOpacity\n accessibilityRole=\"button\"\n onPress={() => void call.start()}\n style={[styles.startButton, { backgroundColor: colours.accent }]}\n >\n <Text style={[styles.bold, { color: colours.inverseText }]}>\n Start call\n </Text>\n </TouchableOpacity>\n )\n }\n\n const inactive = call.state !== 'active'\n const control = { backgroundColor: colours.control }\n return (\n <View style={styles.controls}>\n <RoundButton\n disabled={inactive}\n label={call.microphoneMuted ? 'Unmute microphone' : 'Mute microphone'}\n onPress={() => call.setMicrophoneMuted(!call.microphoneMuted)}\n style={control}\n >\n {call.microphoneMuted ? (\n <MicOffIcon color={colours.accent} />\n ) : (\n <MicIcon color={colours.accent} />\n )}\n </RoundButton>\n <RoundButton\n label=\"End call\"\n onPress={call.end}\n style={{ backgroundColor: colours.accent }}\n >\n <PhoneOffIcon color={colours.inverseText} />\n </RoundButton>\n <RoundButton\n disabled={inactive}\n label={call.speakerMuted ? 'Unmute speaker' : 'Mute speaker'}\n onPress={() => call.setSpeakerMuted(!call.speakerMuted)}\n style={control}\n >\n {call.speakerMuted ? (\n <VolumeXIcon color={colours.accent} />\n ) : (\n <Volume2Icon color={colours.accent} />\n )}\n </RoundButton>\n </View>\n )\n}\n\nfunction PoweredBy({ color }: { color: string }) {\n return (\n <TouchableOpacity\n accessibilityLabel=\"Powered by Lorikeet\"\n accessibilityRole=\"link\"\n onPress={() => void Linking.openURL('https://lorikeetcx.ai')}\n style={styles.poweredBy}\n >\n <LorikeetLogoMark color={color} size={16} />\n <Text style={[styles.small, { color }]}>Powered by Lorikeet</Text>\n </TouchableOpacity>\n )\n}\n\n/** The web Voice Widget's call screen and copy, on `useLorikeetCall`. */\nexport function LorikeetCallView({\n customisations,\n getSession,\n onError,\n style,\n}: LorikeetCallViewProps) {\n const call = useLorikeetCall({ getSession, onError })\n const seconds = useCallSeconds(call.state)\n const colours = callColours(customisations?.theme)\n const status = statusLabel(call.state, seconds)\n const announcement = announcementFor(call)\n // Android reads a live region when its text changes. iOS has no live\n // regions, so each change is announced, queued behind the button press.\n const liveRegion = Platform.OS === 'android' ? 'polite' : undefined\n\n useEffect(() => {\n if (Platform.OS === 'ios' && announcement) {\n AccessibilityInfo.announceForAccessibilityWithOptions(announcement, {\n queue: true,\n })\n }\n }, [announcement])\n\n return (\n <SafeAreaView\n style={[styles.shell, { backgroundColor: colours.shell }, style]}\n >\n <View style={styles.ringed}>\n <View\n style={[\n StyleSheet.absoluteFill,\n styles.ring,\n { backgroundColor: colours.ring },\n ]}\n />\n <View\n style={[\n styles.card,\n { backgroundColor: colours.card, shadowColor: colours.accent },\n ]}\n >\n <View style={styles.identity}>\n <CallLogo logoUrl={customisations?.logoUrl} />\n <Text\n accessibilityRole=\"header\"\n numberOfLines={1}\n style={[styles.bold, { color: colours.text }]}\n >\n {customisations?.title || 'Lorikeet AI Agent'}\n </Text>\n {/*\n Mounted even when empty, because Android reads a live region when\n its text changes, not when it first appears. Otherwise \"Starting\n call\" would be silent.\n */}\n <Text\n // Hidden while empty, so VoiceOver does not stop on it.\n accessibilityElementsHidden={!status}\n // Not while the clock runs, or it would be read every second.\n accessibilityLiveRegion={\n call.state === 'active' ? 'none' : liveRegion\n }\n style={[styles.small, { color: colours.text }]}\n >\n {status}\n </Text>\n </View>\n\n <CallWave active={call.state === 'active'} color={colours.accent} />\n\n <View accessibilityLiveRegion={liveRegion} style={styles.message}>\n <CallMessage call={call} colours={colours} />\n </View>\n\n <View style={styles.footer}>\n <CallActions call={call} colours={colours} />\n {customisations?.useSubtleBranding ? null : (\n <PoweredBy color={colours.subtleText} />\n )}\n </View>\n </View>\n </View>\n </SafeAreaView>\n )\n}\n\nconst styles = StyleSheet.create({\n body: { fontSize: 14, lineHeight: 20, textAlign: 'center' },\n bold: {\n fontSize: 14,\n fontWeight: '600',\n lineHeight: 20,\n textAlign: 'center',\n },\n card: {\n alignItems: 'center',\n borderColor: 'rgba(255, 255, 255, 0.8)',\n borderRadius: 20,\n borderWidth: 1,\n elevation: 8,\n flex: 1,\n justifyContent: 'space-between',\n paddingHorizontal: 24,\n paddingVertical: 32,\n shadowOffset: { height: 18, width: 0 },\n shadowOpacity: 0.28,\n shadowRadius: 17,\n },\n controls: { alignItems: 'center', flexDirection: 'row', gap: 12 },\n disabled: { opacity: 0.5 },\n footer: { alignItems: 'center', gap: 16 },\n identity: { alignItems: 'center', gap: 8, maxWidth: 320 },\n logo: { height: 48, width: 48 },\n marker: { alignItems: 'center', flexDirection: 'row', gap: 8, width: '100%' },\n markerRule: { flex: 1, height: StyleSheet.hairlineWidth, opacity: 0.4 },\n message: {\n gap: 4,\n justifyContent: 'center',\n maxWidth: 320,\n minHeight: 96,\n width: '100%',\n },\n poweredBy: { alignItems: 'center', flexDirection: 'row', gap: 2 },\n ring: { borderRadius: 27, opacity: 0.8 },\n ringed: { flex: 1, padding: 7 },\n roundButton: {\n alignItems: 'center',\n borderRadius: 24,\n height: 48,\n justifyContent: 'center',\n width: 48,\n },\n shell: { flex: 1, padding: 16 },\n small: { fontSize: 12, lineHeight: 16, textAlign: 'center' },\n startButton: {\n borderRadius: 8,\n justifyContent: 'center',\n minHeight: 40,\n paddingHorizontal: 16,\n },\n})\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,14 +1,15 @@
1
1
  {
2
2
  "name": "@lorikeetai/react-native-sdk",
3
- "version": "2.1.0",
3
+ "version": "2.2.0-beta.2",
4
4
  "type": "module",
5
5
  "private": false,
6
- "description": "LorikeetCX React Native widget for embedding the chat widget via WebView",
6
+ "description": "LorikeetCX React Native SDK: the chat widget in a WebView, and in-app voice calls",
7
7
  "keywords": [
8
8
  "lorikeet",
9
9
  "react-native",
10
10
  "webview",
11
- "widget"
11
+ "widget",
12
+ "voice"
12
13
  ],
13
14
  "author": "LorikeetCX.ai",
14
15
  "license": "MIT",
@@ -27,20 +28,48 @@
27
28
  "react-native": "./dist/index.js",
28
29
  "import": "./dist/index.js",
29
30
  "require": "./dist/index.cjs"
31
+ },
32
+ "./voice": {
33
+ "react-native": "./dist/voice.js",
34
+ "import": {
35
+ "types": "./dist/voice.d.ts",
36
+ "default": "./dist/voice.js"
37
+ },
38
+ "require": {
39
+ "types": "./dist/voice.d.cts",
40
+ "default": "./dist/voice.cjs"
41
+ }
30
42
  }
31
43
  },
32
44
  "peerDependencies": {
45
+ "@livekit/react-native": "^3.1.0",
46
+ "@livekit/react-native-webrtc": "^150.1.0",
47
+ "livekit-client": "^2.20.0",
33
48
  "react": ">=18.0.0",
34
49
  "react-native": ">=0.70.0",
35
50
  "react-native-safe-area-context": ">=4.0.0 <6.0.0",
36
51
  "react-native-svg": ">=13.0.0 <16.0.0",
37
52
  "react-native-webview": ">=13.0.0 <14.0.0"
38
53
  },
54
+ "peerDependenciesMeta": {
55
+ "@livekit/react-native": {
56
+ "optional": true
57
+ },
58
+ "@livekit/react-native-webrtc": {
59
+ "optional": true
60
+ },
61
+ "livekit-client": {
62
+ "optional": true
63
+ }
64
+ },
39
65
  "devDependencies": {
66
+ "@livekit/react-native": "3.1.0",
67
+ "@livekit/react-native-webrtc": "150.1.1",
40
68
  "@testing-library/react": "^16.1.0",
41
69
  "@types/react": "^19.0.0",
42
70
  "@typescript/native-preview": "7.0.0-dev.20260707.2",
43
71
  "happy-dom": "^21.0.4",
72
+ "livekit-client": "^2.23.0",
44
73
  "react": "^19.1.0",
45
74
  "react-dom": "^19.1.0",
46
75
  "react-native": "0.81.4",