@adminide-stack/yantra-mobile 12.0.28-alpha.9 → 12.0.28-alpha.93

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.
Files changed (85) hide show
  1. package/lib/api/stt.js +54 -0
  2. package/lib/api/stt.js.map +1 -0
  3. package/lib/assets/icon.png +0 -0
  4. package/lib/components/CustomDrawer.js +479 -0
  5. package/lib/components/CustomDrawer.js.map +1 -0
  6. package/lib/components/GatewayConnector/GatewayConnector.js +18 -0
  7. package/lib/components/GatewayConnector/GatewayConnector.js.map +1 -0
  8. package/lib/components/GatewayToolbarButtonMobile.js +84 -0
  9. package/lib/components/GatewayToolbarButtonMobile.js.map +1 -0
  10. package/lib/components/NavigationHeader/NavigationHeader.js +214 -0
  11. package/lib/components/NavigationHeader/NavigationHeader.js.map +1 -0
  12. package/lib/components/ThinkingIndicator.js +55 -0
  13. package/lib/components/ThinkingIndicator.js.map +1 -0
  14. package/lib/components/YantraBrandLoader.js +94 -0
  15. package/lib/components/YantraBrandLoader.js.map +1 -0
  16. package/lib/compute.js +114 -5
  17. package/lib/compute.js.map +1 -1
  18. package/lib/config/constants.js +18 -0
  19. package/lib/config/constants.js.map +1 -0
  20. package/lib/config/env-config.js +75 -19
  21. package/lib/config/env-config.js.map +1 -1
  22. package/lib/contexts/CdecliConnectionContext.js +47 -0
  23. package/lib/contexts/CdecliConnectionContext.js.map +1 -0
  24. package/lib/contexts/GatewayContext.js +77 -0
  25. package/lib/contexts/GatewayContext.js.map +1 -0
  26. package/lib/features/audio-input/AudioRecorderPanel.js +220 -0
  27. package/lib/features/audio-input/AudioRecorderPanel.js.map +1 -0
  28. package/lib/features/audio-input/MicErrorBoundary.js +34 -0
  29. package/lib/features/audio-input/MicErrorBoundary.js.map +1 -0
  30. package/lib/features/audio-input/useAudioPermission.js +24 -0
  31. package/lib/features/audio-input/useAudioPermission.js.map +1 -0
  32. package/lib/graphql/agentGatewayDocuments.js +53 -0
  33. package/lib/graphql/agentGatewayDocuments.js.map +1 -0
  34. package/lib/hooks/useAccountDefaultSettings.js +38 -0
  35. package/lib/hooks/useAccountDefaultSettings.js.map +1 -0
  36. package/lib/hooks/useCdecliAutoConnect.js +244 -0
  37. package/lib/hooks/useCdecliAutoConnect.js.map +1 -0
  38. package/lib/hooks/useCdecliChannel.js +161 -0
  39. package/lib/hooks/useCdecliChannel.js.map +1 -0
  40. package/lib/hooks/useChatApi.js +386 -170
  41. package/lib/hooks/useChatApi.js.map +1 -1
  42. package/lib/hooks/useChatStream.js +179 -137
  43. package/lib/hooks/useChatStream.js.map +1 -1
  44. package/lib/hooks/useGatewayConnection.js +123 -0
  45. package/lib/hooks/useGatewayConnection.js.map +1 -0
  46. package/lib/hooks/useGatewayRegistry.js +28 -0
  47. package/lib/hooks/useGatewayRegistry.js.map +1 -0
  48. package/lib/hooks/usePrerequisiteIds.js +209 -0
  49. package/lib/hooks/usePrerequisiteIds.js.map +1 -0
  50. package/lib/hooks/useWorkspaceProvisioner.js +236 -0
  51. package/lib/hooks/useWorkspaceProvisioner.js.map +1 -0
  52. package/lib/index.js +1 -1
  53. package/lib/index.js.map +1 -1
  54. package/lib/routes.json +120 -5
  55. package/lib/screens/Chat/index.js +409 -0
  56. package/lib/screens/Chat/index.js.map +1 -0
  57. package/lib/screens/ChatHistory/index.js +56 -0
  58. package/lib/screens/ChatHistory/index.js.map +1 -0
  59. package/lib/screens/Home/HomeScreen.js +364 -144
  60. package/lib/screens/Home/HomeScreen.js.map +1 -1
  61. package/lib/screens/Home/components/ChatHistoryLanding.js +487 -0
  62. package/lib/screens/Home/components/ChatHistoryLanding.js.map +1 -0
  63. package/lib/screens/Home/components/DeepSearchModal.js +349 -0
  64. package/lib/screens/Home/components/DeepSearchModal.js.map +1 -0
  65. package/lib/screens/Home/deepSearchUtils.js +41 -0
  66. package/lib/screens/Home/deepSearchUtils.js.map +1 -0
  67. package/lib/screens/NewChat/index.js +43 -0
  68. package/lib/screens/NewChat/index.js.map +1 -0
  69. package/lib/services/agentSessionManager.js +451 -0
  70. package/lib/services/agentSessionManager.js.map +1 -0
  71. package/lib/services/gatewayApiKeyBridge.js +4 -0
  72. package/lib/services/gatewayApiKeyBridge.js.map +1 -0
  73. package/lib/services/gatewayClient.js +470 -0
  74. package/lib/services/gatewayClient.js.map +1 -0
  75. package/lib/theme/mobileTokens.js +18 -0
  76. package/lib/theme/mobileTokens.js.map +1 -0
  77. package/lib/utils/cdecodeUri.js +68 -0
  78. package/lib/utils/cdecodeUri.js.map +1 -0
  79. package/lib/utils/gatewaySelectionStorage.js +21 -0
  80. package/lib/utils/gatewaySelectionStorage.js.map +1 -0
  81. package/lib/utils/syncMobileOrgRouteContext.js +61 -0
  82. package/lib/utils/syncMobileOrgRouteContext.js.map +1 -0
  83. package/package.json +7 -3
  84. package/lib/api/chatApi.js +0 -102
  85. package/lib/api/chatApi.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"HomeScreen.js","sources":["../../../src/screens/Home/HomeScreen.tsx"],"sourcesContent":["import React, { FC, useState, useCallback, useEffect } from 'react';\nimport { Pressable, StyleSheet } from 'react-native';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { Box, InputToolBar, Text, getDefaultLeftItems, getDefaultRightItems } from '@admin-layout/gluestack-ui-mobile';\nimport { SafeAreaView } from 'react-native-safe-area-context';\nimport { useGetAccountChatSessionsQuery } from 'common/graphql';\nimport { v4 as uuidv4 } from 'uuid';\nimport { useNavigation } from '@react-navigation/native';\nimport { MessagesContainerUI } from '@messenger-box/platform-mobile';\nimport { useChatMutations } from '../../hooks/useChatApi';\nimport { useChatStream } from '../../hooks/useChatStream';\nimport type { ModeSubmission, RoutingMode } from './types';\n\nexport type { RoutingMode, ModeSubmission };\n\nexport interface HomeScreenProps {\n initialMode?: RoutingMode;\n onSubmit?: (submission: ModeSubmission) => void;\n onStop?: () => void;\n isLoading?: boolean;\n disabled?: boolean;\n}\n\nfunction HomeScreenContent({\n initialMode = 'chat',\n onSubmit: onSubmitProp,\n onStop,\n isLoading: isLoadingProp = false,\n disabled = false,\n}: HomeScreenProps) {\n const navigation = useNavigation();\n const [value, setValue] = useState('');\n const [activeMode, setActiveMode] = useState<RoutingMode>(initialMode);\n const [activeSessionId, setActiveSessionId] = useState<string | null>(null);\n\n const { createSession } = useChatMutations();\n const chat = useChatStream(activeSessionId);\n\n useGetAccountChatSessionsQuery({\n variables: { first: 25, includeArchived: false },\n fetchPolicy: 'cache-first',\n });\n\n const {\n messages,\n response,\n error: chatError,\n isLoading: chatLoading,\n isStreaming,\n hasMessages,\n sendMessage,\n cancel,\n } = chat;\n\n const trimmedQuery = value.trim();\n const hasQuery = trimmedQuery.length > 0;\n const canSubmit = hasQuery;\n const isLoading = isLoadingProp || chatLoading;\n const isResearchMode = activeMode === 'deep-search';\n const inputPlaceholder = isResearchMode ? 'Research anything...' : 'Ask anything...';\n\n // useEffect(() => {\n // navigation.setOptions({\n // headerLeft: () => null,\n // });\n // }, []);\n\n const handleModeSwitch = useCallback((mode: RoutingMode) => {\n setActiveMode(mode);\n }, []);\n\n const handleSubmit = useCallback(\n async (attachments?: ModeSubmission['attachments']) => {\n if (!canSubmit || isLoading || disabled) return;\n\n const submission: ModeSubmission = {\n mode: activeMode,\n query: trimmedQuery,\n attachments,\n };\n\n // Research (deep-search): delegate to parent when provided, like browser NewChatPage → session page\n if (activeMode === 'deep-search' && onSubmitProp) {\n onSubmitProp(submission);\n setValue('');\n return;\n }\n\n if (onSubmitProp && activeMode === 'chat') {\n onSubmitProp(submission);\n setValue('');\n return;\n }\n\n if (!activeSessionId) {\n const newSessionId = uuidv4();\n const title = activeMode === 'deep-search' ? 'Deep Search' : 'New Chat';\n try {\n await createSession({ id: newSessionId, title });\n setActiveSessionId(newSessionId);\n setValue('');\n sendMessage(trimmedQuery, undefined, newSessionId);\n } catch (err) {\n console.error('[HomeScreen] Failed to create session:', err);\n }\n } else {\n setValue('');\n sendMessage(trimmedQuery);\n }\n },\n [\n canSubmit,\n isLoading,\n disabled,\n activeMode,\n trimmedQuery,\n activeSessionId,\n onSubmitProp,\n createSession,\n sendMessage,\n ],\n );\n\n const handleNewChat = useCallback(() => {\n setActiveSessionId(null);\n setValue('');\n }, []);\n\n useEffect(() => {\n navigation.setOptions({\n headerRight: () => (\n <Pressable\n onPress={handleNewChat}\n style={({ pressed }) => [styles.newChatButton, pressed && styles.newChatButtonPressed]}\n accessibilityLabel=\"New chat\"\n >\n <MaterialCommunityIcons name=\"chat-plus-outline\" size={26} color=\"#000\" />\n </Pressable>\n ),\n });\n }, [navigation, handleNewChat]);\n\n const handleSendMessage = useCallback(\n async (text: string) => {\n if (!text.trim() || isLoading || disabled) return;\n const submission: ModeSubmission = { mode: activeMode, query: text.trim() };\n if (activeMode === 'deep-search' && onSubmitProp) {\n onSubmitProp(submission);\n return;\n }\n if (!activeSessionId) {\n const newSessionId = uuidv4();\n const title = activeMode === 'deep-search' ? 'Deep Search' : 'New Chat';\n try {\n await createSession({ id: newSessionId, title });\n setActiveSessionId(newSessionId);\n sendMessage(text.trim(), undefined, newSessionId);\n } catch (err) {\n console.error('[HomeScreen] Failed to create session:', err);\n }\n } else {\n sendMessage(text.trim());\n }\n },\n [isLoading, disabled, activeSessionId, activeMode, onSubmitProp, createSession, sendMessage],\n );\n\n const leftItems = getDefaultLeftItems({\n search: {\n active: activeMode === 'chat',\n onClick: () => handleModeSwitch('chat'),\n },\n zap: {\n active: activeMode === 'deep-search',\n onClick: () => handleModeSwitch('deep-search'),\n },\n lightbulb: { enabled: false },\n });\n\n const rightItems = getDefaultRightItems({\n tag: { enabled: false },\n chip: { enabled: false },\n camera: { onClick: () => console.log('camera') },\n image: { onClick: () => console.log('image') },\n attach: { onClick: () => console.log('attach') },\n });\n\n return (\n <SafeAreaView style={{ flex: 1, backgroundColor: '#fff' }}>\n <Box flex={1} width=\"100%\">\n {chatError && (\n <Box mb=\"$2\" mx=\"$4\" p=\"$3\" bg=\"$gray200\" borderRadius=\"$md\">\n <Text fontSize=\"$sm\" color=\"$gray900\">\n {chatError}\n </Text>\n </Box>\n )}\n {hasMessages || response ? (\n <Box flex={1} width=\"100%\" alignSelf=\"stretch\">\n <MessagesContainerUI\n mode=\"plan\"\n showBackButton={false}\n onBackPress={handleNewChat}\n compactTop={true}\n messagesContainerStyle={{ paddingHorizontal: 0, paddingTop: 0, margin: 0 }}\n listContentStyle={{ paddingTop: 0, margin: 0 }}\n messages={messages.map((msg, index) => ({\n id: `msg-${index}-${msg.role}`,\n role: msg.role,\n content: msg.content,\n metadata: msg.metadata,\n }))}\n streamingContent={response}\n currentUser={{ id: 'user' }}\n onSend={handleSendMessage}\n disabled={isLoading || disabled}\n isLoading={isLoading}\n onStop={isStreaming ? cancel : onStop}\n renderPlanInputToolbar={({ value, onChange, onSend, disabled: inputDisabled }) => (\n <InputToolBar\n inputConfig={{\n value,\n onChange: (e) => onChange(e.nativeEvent.text),\n placeholder: inputPlaceholder,\n disabled: inputDisabled,\n }}\n leftItems={leftItems}\n rightItems={rightItems}\n templateButton={null}\n templateModalConfig={null}\n micSendButton={{\n hasContent: value.trim().length > 0,\n onSend: () => onSend(value.trim()),\n onMic: () => console.log('mic'),\n disabled: inputDisabled,\n isLoading,\n onStop: isStreaming ? cancel : onStop,\n }}\n />\n )}\n renderBuildInputToolbar={() => null}\n />\n </Box>\n ) : (\n <Box flex={1} justifyContent=\"center\" alignItems=\"stretch\" p=\"$4\">\n <InputToolBar\n inputConfig={{\n value,\n onChange: (e) => setValue(e.nativeEvent.text),\n placeholder: inputPlaceholder,\n disabled: isLoading || disabled,\n }}\n leftItems={leftItems}\n rightItems={rightItems}\n templateButton={null}\n templateModalConfig={null}\n micSendButton={{\n hasContent: canSubmit,\n onSend: () => handleSubmit(),\n onMic: () => console.log('mic'),\n disabled: isLoading || disabled,\n isLoading,\n onStop,\n }}\n />\n </Box>\n )}\n </Box>\n </SafeAreaView>\n );\n}\n\nconst styles = StyleSheet.create({\n newChatButton: {\n padding: 6,\n marginRight: 4,\n alignItems: 'center',\n justifyContent: 'center',\n },\n newChatButtonPressed: {\n opacity: 0.6,\n },\n});\n\nconst HomeScreen: FC<HomeScreenProps> = (props) => <HomeScreenContent {...props} />;\n\nexport default HomeScreen;\n"],"names":["uuidv4","value"],"mappings":"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"}
1
+ {"version":3,"file":"HomeScreen.js","sources":["../../../src/screens/Home/HomeScreen.tsx"],"sourcesContent":["import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n Keyboard,\n Platform,\n Pressable,\n StyleSheet,\n TouchableWithoutFeedback,\n View,\n useColorScheme,\n useWindowDimensions,\n} from 'react-native';\nimport { MaterialCommunityIcons } from '@expo/vector-icons';\nimport { Box, InputToolBar, Text, getDefaultLeftItems, getDefaultRightItems } from '@admin-layout/gluestack-ui-mobile';\nimport { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';\nimport { useNavigation, useRoute, CommonActions } from '@react-navigation/native';\nimport { useDispatch, useSelector } from 'react-redux';\nimport { v4 as uuidv4 } from 'uuid';\nimport { usePrerequisiteIds } from '../../hooks/usePrerequisiteIds';\nimport { syncMobileOrgRouteContext } from '../../utils/syncMobileOrgRouteContext';\nimport { useChatMutations } from '../../hooks/useChatApi';\nimport { AudioRecorderPanel } from '../../features/audio-input/AudioRecorderPanel';\nimport { MicErrorBoundary } from '../../features/audio-input/MicErrorBoundary';\nimport { requestMicPermission } from '../../features/audio-input/useAudioPermission';\nimport type { ModeSubmission, RoutingMode } from './types';\nimport { mobileTokens } from '../../theme/mobileTokens';\n\n/**\n * Build the channel `title` we persist on creation. This is the server-side mode\n * signal (web parity: `useYantraComposerChannelSubmit`): the backend inspects the\n * title prefix to route deep-search requests differently from plain chat. The\n * UI never displays this raw string — `buildSessionFromChannel` filters\n * `chat-channel-…` / `deep-search-…` titles out of the history label cascade\n * and derives the visible label from the user's first message instead.\n *\n * Previously we used the user's prompt directly as the title for friendlier\n * history rows, but that silently disabled the server's mode detection — every\n * \"deep-search\" submission landed as a plain chat. Restored to the web scheme.\n */\nfunction buildChannelTitle(mode: RoutingMode, channelId: string): string {\n return mode === 'deep-search' ? `deep-search-${channelId}` : `chat-channel-${channelId}`;\n}\n\nexport type { RoutingMode, ModeSubmission };\n\nexport interface HomeScreenProps {\n initialMode?: RoutingMode;\n onSubmit?: (submission: ModeSubmission) => void;\n onStop?: () => void;\n isLoading?: boolean;\n disabled?: boolean;\n}\n\ninterface EmptyStateSectionProps {\n isDark: boolean;\n secondaryTextColor: string;\n primaryTextColor: string;\n activeMode: RoutingMode;\n onModeSwitch: (mode: RoutingMode) => void;\n}\n\nconst EmptyStateSection = React.memo(function EmptyStateSection({\n isDark,\n secondaryTextColor,\n primaryTextColor,\n activeMode,\n onModeSwitch,\n}: EmptyStateSectionProps) {\n return (\n <Box flex={1} width=\"100%\">\n <View style={styles.emptyCenterTitleWrap}>\n <Text style={[styles.emptyEyebrow, { color: secondaryTextColor }]}>Choose your mode</Text>\n <View\n style={[\n styles.emptyModePillRow,\n {\n backgroundColor: isDark ? 'rgba(30, 41, 59, 0.9)' : '#eef2ff',\n borderColor: isDark ? 'rgba(148, 163, 184, 0.26)' : 'rgba(67, 56, 202, 0.15)',\n },\n ]}\n >\n <Pressable\n onPress={() => onModeSwitch('chat')}\n style={[\n styles.emptyModePill,\n activeMode === 'chat' && styles.emptyModePillActive,\n activeMode === 'chat' && { backgroundColor: isDark ? '#312e81' : '#4338ca' },\n ]}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Switch to chat mode\"\n >\n <MaterialCommunityIcons\n name=\"magnify\"\n size={14}\n color={activeMode === 'chat' ? '#ffffff' : secondaryTextColor}\n />\n <Text\n style={[\n styles.emptyModePillLabel,\n { color: activeMode === 'chat' ? '#ffffff' : secondaryTextColor },\n ]}\n >\n Chat\n </Text>\n </Pressable>\n <Pressable\n onPress={() => onModeSwitch('deep-search')}\n style={[\n styles.emptyModePill,\n activeMode === 'deep-search' && styles.emptyModePillActive,\n activeMode === 'deep-search' && { backgroundColor: isDark ? '#312e81' : '#4338ca' },\n ]}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Switch to deep-search mode\"\n >\n <MaterialCommunityIcons\n name=\"lightning-bolt-outline\"\n size={14}\n color={activeMode === 'deep-search' ? '#ffffff' : secondaryTextColor}\n />\n <Text\n style={[\n styles.emptyModePillLabel,\n { color: activeMode === 'deep-search' ? '#ffffff' : secondaryTextColor },\n ]}\n >\n Deep Search\n </Text>\n </Pressable>\n {/**\n * Build mode — third pill rendered as a peer of Chat and\n * Deep Search so the segmented control matches the web\n * composer's `search · zap · lightbulb` axis. Inert for\n * now: tapping logs to the console and intentionally does\n * NOT call `onModeSwitch`, so the indigo active fill never\n * lands on Build until the underlying workflow flow exists\n * (matches the same inert treatment the lightbulb gets in\n * the composer toolbar). When `'workflow'` joins\n * `RoutingMode`, swap the press handler to\n * `onModeSwitch('workflow')` and add the `activeMode`\n * styles in the same shape as the two above.\n */}\n <Pressable\n onPress={() => {\n // eslint-disable-next-line no-console\n console.log('build mode');\n }}\n style={styles.emptyModePill}\n accessibilityRole=\"button\"\n accessibilityLabel=\"Build mode (preview)\"\n >\n <MaterialCommunityIcons name=\"lightbulb-outline\" size={14} color={secondaryTextColor} />\n <Text style={[styles.emptyModePillLabel, { color: secondaryTextColor }]}>Build</Text>\n </Pressable>\n </View>\n <Text style={[styles.emptyCenterTitle, { color: primaryTextColor }]}>How do you want to start?</Text>\n <Text style={[styles.emptySubtitle, { color: secondaryTextColor }]}>\n Use Chat for quick help, Deep Search for source-backed answers, or Build to assemble workflows.\n </Text>\n </View>\n </Box>\n );\n});\n\nfunction HomeScreenContent({\n initialMode = 'chat',\n onSubmit: onSubmitProp,\n isLoading: isLoadingProp = false,\n disabled = false,\n}: HomeScreenProps) {\n const { width: screenWidth } = useWindowDimensions();\n const colorScheme = useColorScheme();\n const isDark = colorScheme === 'dark';\n const navigation = useNavigation<any>();\n const route = useRoute<any>();\n const dispatch = useDispatch();\n const syncedRouteOrgRef = useRef<string | null>(null);\n const mobileRoutePath = useSelector(\n (state: { router?: { location?: { currentRoute?: { path?: string } } } }) =>\n state.router?.location?.currentRoute?.path,\n );\n\n const [value, setValue] = useState('');\n const [activeMode, setActiveMode] = useState<RoutingMode>(initialMode);\n /**\n * `showAudioRecorder` swaps the InputToolBar for the inline voice recorder\n * panel — same pattern as the web composer\n * (`YantraSearchComposer.tsx`'s `showAudioRecorder` branch).\n */\n const [showAudioRecorder, setShowAudioRecorder] = useState(false);\n const [voiceError, setVoiceError] = useState<string | null>(null);\n\n const { createChannel } = useChatMutations();\n const {\n orgName: resolvedOrgName,\n projectId,\n accountUserId,\n loading: prerequisitesLoading,\n orgLoading,\n } = usePrerequisiteIds();\n const routeOrgName = ((route?.params?.orgName as string | undefined) ?? '').trim() || null;\n const effectiveOrgName = resolvedOrgName || routeOrgName || null;\n const effectiveProjectId = projectId || null;\n\n const trimmedQuery = value.trim();\n const hasQuery = trimmedQuery.length > 0;\n const canSubmit = hasQuery && Boolean(routeOrgName || effectiveOrgName);\n const isLoading = isLoadingProp;\n const isResearchMode = activeMode === 'deep-search';\n const inputPlaceholder = !effectiveOrgName\n ? 'Initializing workspace...'\n : isResearchMode\n ? 'Research anything...'\n : 'Ask anything...';\n\n const insets = useSafeAreaInsets();\n const [keyboardHeight, setKeyboardHeight] = useState(0);\n\n useEffect(() => {\n if (Platform.OS === 'ios') {\n const frameSub = Keyboard.addListener('keyboardWillChangeFrame', (event) =>\n setKeyboardHeight(event.endCoordinates?.height ?? 0),\n );\n const showSub = Keyboard.addListener('keyboardWillShow', (event) =>\n setKeyboardHeight(event.endCoordinates?.height ?? 0),\n );\n const hideSub = Keyboard.addListener('keyboardWillHide', () => setKeyboardHeight(0));\n return () => {\n frameSub.remove();\n showSub.remove();\n hideSub.remove();\n };\n }\n const showSub = Keyboard.addListener('keyboardDidShow', (event) =>\n setKeyboardHeight(event.endCoordinates?.height ?? 0),\n );\n const hideSub = Keyboard.addListener('keyboardDidHide', () => setKeyboardHeight(0));\n return () => {\n showSub.remove();\n hideSub.remove();\n };\n }, []);\n\n const handleModeSwitch = useCallback((mode: RoutingMode) => {\n setActiveMode(mode);\n }, []);\n\n /**\n * Mirrors the web `useYantraComposerChannelSubmit`: on submit we generate a client-side\n * channel id, fire-and-forget the createChannel mutation, then navigate immediately to\n * the Chat screen with the user's prompt. The Chat screen mounts, awaits the in-flight\n * creation just before saveMessages, and starts streaming the assistant response.\n */\n const navigateToChat = useCallback(\n (channelId: string, query: string, mode: RoutingMode, attachments?: ModeSubmission['attachments']) => {\n const params: Record<string, unknown> = {\n channelId,\n orgName: effectiveOrgName ?? routeOrgName ?? undefined,\n initialQuery: query,\n initialMode: mode,\n };\n if (attachments && attachments.length > 0) {\n params.initialAttachments = attachments;\n }\n navigation.dispatch(\n CommonActions.navigate({\n name: 'MainStack.Chat',\n params,\n }),\n );\n },\n [navigation, effectiveOrgName, routeOrgName],\n );\n\n const handleSubmit = useCallback(\n async (attachments?: ModeSubmission['attachments']) => {\n if (!canSubmit || isLoading || disabled) return;\n\n const submission: ModeSubmission = {\n mode: activeMode,\n query: trimmedQuery,\n attachments,\n };\n if (onSubmitProp) {\n onSubmitProp(submission);\n }\n\n if (!effectiveOrgName || !effectiveProjectId) {\n console.warn('[HomeScreen] orgName/projectId not ready — skipping submit.');\n return;\n }\n\n const requestedChannelId = uuidv4();\n /**\n * Title is the server's mode signal (`deep-search-…` vs `chat-channel-…`).\n * It's NOT the history label — the history list derives that from the\n * first user message via `buildSessionFromChannel`.\n */\n const title = buildChannelTitle(activeMode, requestedChannelId);\n\n const queryToForward = trimmedQuery;\n const attachmentsToForward = attachments;\n setValue('');\n\n void createChannel({\n id: requestedChannelId,\n title,\n isShared: false,\n sharedSlug: null,\n isArchived: false,\n isPinned: false,\n model: null,\n systemPrompt: null,\n orgName: effectiveOrgName,\n projectId: effectiveProjectId,\n } as any).catch((err) => {\n console.error('[HomeScreen] createChannel failed:', err);\n });\n\n navigateToChat(requestedChannelId, queryToForward, activeMode, attachmentsToForward);\n },\n [\n canSubmit,\n isLoading,\n disabled,\n activeMode,\n trimmedQuery,\n onSubmitProp,\n effectiveOrgName,\n effectiveProjectId,\n createChannel,\n navigateToChat,\n ],\n );\n\n const surfaceColor = isDark ? '#0f172a' : mobileTokens.color.surface;\n const primaryTextColor = isDark ? '#e5e7eb' : mobileTokens.color.text;\n const secondaryTextColor = isDark ? '#94a3b8' : mobileTokens.color.textMuted;\n const contentMaxWidth = Math.min(720, Math.max(360, screenWidth - 24));\n\n /**\n * After verify-user, Home mounts with empty `route.params.orgName`. Settings load via\n * `usePrerequisiteIds` (single useSettings). Merge-navigate does not re-fire LOCATION_CHANGE\n * on the same path; sync params + LOCATION_CHANGE + orgChangeAction like refresh does.\n */\n useEffect(() => {\n const currentRouteOrgName = (route?.params?.orgName as string | undefined)?.trim() || '';\n if (currentRouteOrgName) {\n syncedRouteOrgRef.current = currentRouteOrgName;\n return;\n }\n const orgToApply = resolvedOrgName;\n if (!orgToApply || orgLoading) {\n return;\n }\n if (syncedRouteOrgRef.current === orgToApply) {\n return;\n }\n\n syncedRouteOrgRef.current = orgToApply;\n syncMobileOrgRouteContext(dispatch, orgToApply, {\n userId: accountUserId,\n routePath: mobileRoutePath,\n });\n navigation.dispatch(\n CommonActions.navigate({\n name: route?.name ?? 'MainStack.Layout.Home',\n params: { ...(route?.params || {}), orgName: orgToApply },\n merge: true,\n }),\n );\n }, [\n dispatch,\n navigation,\n route?.name,\n route?.params,\n route?.params?.orgName,\n resolvedOrgName,\n accountUserId,\n mobileRoutePath,\n orgLoading,\n ]);\n\n const handleValueChange = useCallback((e: any) => {\n setValue(e.nativeEvent.text);\n }, []);\n\n /**\n * Mic affordance — web parity (`handleMicClick` in `YantraSearchComposer.tsx`).\n *\n * Only opens the recorder when the input is empty: if the user already has\n * a draft, the same button must act as Send (the `MicSendButton` in\n * `InputToolBar.tsx` already swaps its glyph from mic→send based on\n * `hasContent`, so this branch only fires when the icon is visibly a mic).\n *\n * Permission is requested HERE, before the panel mounts. The previous flow\n * flipped `showAudioRecorder=true` immediately, which mounted\n * `AudioRecorderPanel` and constructed the native `AVAudioRecorder` via\n * `useAudioRecorder()` during the panel's render phase — before iOS had\n * granted mic access. On SDK 53 + `newArchEnabled: true` + Hermes that\n * ordering hard-crashes TestFlight builds even though it works in Expo Go\n * (Expo Go pre-caches permission). Asking up front means iOS shows the\n * system alert first and the panel only mounts (and therefore the native\n * recorder is only allocated) once permission is granted.\n *\n * `expo-audio` is lazy-imported so a missing/broken native module surfaces\n * as a user-visible banner instead of a synchronous TurboModule crash.\n */\n const handleMicPress = useCallback(() => {\n if (hasQuery) return;\n setVoiceError(null);\n Keyboard.dismiss();\n void (async () => {\n const { granted, error } = await requestMicPermission();\n if (!granted) {\n setVoiceError(error || 'Microphone is not available.');\n return;\n }\n setShowAudioRecorder(true);\n })();\n }, [hasQuery]);\n\n const handleTranscriptionComplete = useCallback((text: string) => {\n const cleaned = (text ?? '').trim();\n setShowAudioRecorder(false);\n if (!cleaned) return;\n /*\n * Append on top of any existing draft (web `setQuery((prev) => …)`).\n * Single-space separator keeps the transcript readable when the user\n * dictates multiple times before sending.\n */\n setValue((prev) => (prev.trim().length > 0 ? `${prev.trim()} ${cleaned}` : cleaned));\n }, []);\n\n const handleRecorderCancel = useCallback(() => {\n setShowAudioRecorder(false);\n }, []);\n\n const handleRecorderError = useCallback((error: string) => {\n /*\n * Surface the failure to the user but don't trap them in the recorder —\n * `AudioRecorderPanel` already calls `onCancel` after an error, so we\n * just record the message here for the (lightweight) banner below.\n */\n setVoiceError(error);\n }, []);\n\n const leftItems = useMemo(\n () =>\n getDefaultLeftItems({\n search: {\n active: activeMode === 'chat',\n onClick: () => handleModeSwitch('chat'),\n },\n zap: {\n active: activeMode === 'deep-search',\n onClick: () => handleModeSwitch('deep-search'),\n },\n lightbulb: {\n onClick: () => {\n // eslint-disable-next-line no-console\n console.log('build mode');\n },\n },\n }),\n [activeMode, handleModeSwitch],\n );\n\n const rightItems = useMemo(\n () =>\n getDefaultRightItems({\n tag: { enabled: false },\n chip: { enabled: false },\n camera: { enabled: false },\n image: { enabled: false },\n attach: { enabled: false },\n }),\n [],\n );\n\n const inputConfig = useMemo(\n () => ({\n value,\n onChange: handleValueChange,\n placeholder: inputPlaceholder,\n disabled: isLoading || disabled,\n }),\n [value, handleValueChange, inputPlaceholder, isLoading, disabled],\n );\n\n const micSendButton = useMemo(\n () => ({\n hasContent: canSubmit,\n onSend: () => handleSubmit(),\n onMic: handleMicPress,\n disabled: isLoading || disabled,\n isLoading,\n }),\n [canSubmit, handleSubmit, handleMicPress, isLoading, disabled],\n );\n\n return (\n <SafeAreaView edges={['left', 'right', 'bottom']} style={{ flex: 1, backgroundColor: surfaceColor }}>\n <TouchableWithoutFeedback onPress={Keyboard.dismiss} accessible={false}>\n <Box flex={1} width=\"100%\" position=\"relative\">\n <EmptyStateSection\n isDark={isDark}\n secondaryTextColor={secondaryTextColor}\n primaryTextColor={primaryTextColor}\n activeMode={activeMode}\n onModeSwitch={handleModeSwitch}\n />\n <View\n style={[\n styles.bottomComposerWrap,\n {\n bottom: Math.max(0, keyboardHeight - insets.bottom),\n paddingBottom: Math.max(insets.bottom - 6, 2),\n },\n ]}\n >\n <View style={[styles.bottomComposerInner, { width: contentMaxWidth }]}>\n {voiceError ? (\n <View\n style={[\n styles.voiceErrorBanner,\n {\n backgroundColor: isDark ? '#2b1a1d' : '#fef2f2',\n borderColor: isDark ? '#7f1d1d' : '#fecaca',\n },\n ]}\n >\n <Text style={[styles.voiceErrorText, { color: isDark ? '#fecaca' : '#991b1b' }]}>\n {voiceError}\n </Text>\n </View>\n ) : null}\n {showAudioRecorder ? (\n /*\n * Render-time JS errors thrown by `useAudioRecorder`\n * (e.g. expo-audio native module missing, recorder\n * constructor throws in a release build) are caught\n * by the boundary and converted into a soft cancel\n * via the same handlers the panel itself uses on\n * runtime errors. Without this, a throw during the\n * panel's render phase kills the JS thread in\n * release / TestFlight.\n */\n <MicErrorBoundary onCancel={handleRecorderCancel} onError={handleRecorderError}>\n <AudioRecorderPanel\n isDark={isDark}\n onTranscriptionComplete={handleTranscriptionComplete}\n onCancel={handleRecorderCancel}\n onError={handleRecorderError}\n />\n </MicErrorBoundary>\n ) : (\n <InputToolBar\n inputConfig={inputConfig}\n leftItems={leftItems}\n rightItems={rightItems}\n templateButton={null}\n templateModalConfig={null}\n micSendButton={micSendButton}\n />\n )}\n </View>\n </View>\n </Box>\n </TouchableWithoutFeedback>\n </SafeAreaView>\n );\n}\n\nconst styles = StyleSheet.create({\n emptyCenterTitleWrap: {\n flex: 1,\n alignItems: 'center',\n justifyContent: 'flex-start',\n paddingHorizontal: 24,\n paddingTop: 88,\n paddingBottom: 52,\n maxWidth: 520,\n alignSelf: 'center',\n },\n emptyEyebrow: {\n fontSize: 12,\n fontWeight: '600',\n letterSpacing: 0.4,\n textTransform: 'uppercase',\n marginBottom: 10,\n },\n emptyCenterTitle: {\n marginTop: 2,\n fontSize: 30,\n fontWeight: '700',\n textAlign: 'center',\n letterSpacing: -0.5,\n },\n emptySubtitle: {\n marginTop: 12,\n fontSize: 14,\n lineHeight: 22,\n textAlign: 'center',\n paddingHorizontal: 16,\n maxWidth: 360,\n },\n bottomComposerWrap: {\n position: 'absolute',\n width: '100%',\n left: 0,\n right: 0,\n bottom: 0,\n alignItems: 'center',\n justifyContent: 'flex-end',\n shadowColor: '#0f172a',\n shadowOpacity: 0.08,\n shadowRadius: 8,\n shadowOffset: { width: 0, height: -2 },\n backgroundColor: 'transparent',\n elevation: 6,\n },\n bottomComposerInner: {\n paddingHorizontal: 14,\n paddingTop: 10,\n paddingBottom: 4,\n },\n voiceErrorBanner: {\n marginBottom: 8,\n paddingHorizontal: 12,\n paddingVertical: 8,\n borderRadius: 10,\n borderWidth: 1,\n },\n voiceErrorText: {\n fontSize: 12,\n fontWeight: '500',\n },\n emptyModePillRow: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 8,\n marginBottom: 22,\n borderRadius: 16,\n padding: 6,\n borderWidth: 1,\n shadowColor: '#312e81',\n shadowOpacity: 0.08,\n shadowRadius: 8,\n shadowOffset: { width: 0, height: 3 },\n elevation: 3,\n },\n emptyModePill: {\n flexDirection: 'row',\n alignItems: 'center',\n gap: 4,\n paddingHorizontal: 12,\n paddingVertical: 7,\n borderRadius: 11,\n },\n emptyModePillActive: {\n shadowColor: '#312e81',\n shadowOpacity: 0.18,\n shadowRadius: 6,\n shadowOffset: { width: 0, height: 2 },\n elevation: 2,\n },\n emptyModePillLabel: {\n fontSize: 12,\n fontWeight: '600',\n },\n});\n\nconst HomeScreen: FC<HomeScreenProps> = (props) => <HomeScreenContent {...props} />;\n\nexport default HomeScreen;\n"],"names":["EmptyStateSection","_a","_b","_c","showSub","hideSub","uuidv4"],"mappings":"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"}
@@ -0,0 +1,487 @@
1
+ import {jsx,jsxs}from'react/jsx-runtime';import React from'react';import {useWindowDimensions,useColorScheme,View,StyleSheet,Pressable,ActivityIndicator,SectionList,RefreshControl}from'react-native';import {Feather,MaterialCommunityIcons}from'@expo/vector-icons';import {Text}from'@admin-layout/gluestack-ui-mobile';import {useFocusEffect}from'@react-navigation/native';import {useChatHistorySessionsFromChannels,HISTORY_PAGE_SIZE,getHistoryChannelsQueryVariables,chatHistorySessionsFromChannels}from'../../../hooks/useChatApi.js';import {useApolloClient}from'@apollo/client/index.js';import {GetChannelsByUserWithLastMessageDocument}from'common/graphql';import {usePrerequisiteIds}from'../../../hooks/usePrerequisiteIds.js';import ThinkingIndicator from'../../../components/ThinkingIndicator.js';import {mobileTokens}from'../../../theme/mobileTokens.js';var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ function truncate(text, max = 96) {
21
+ const cleaned = text.replace(/\s+/g, " ").trim();
22
+ if (cleaned.length <= max) return cleaned;
23
+ return `${cleaned.slice(0, max).trim()}...`;
24
+ }
25
+ const WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
26
+ function relativeTime(ts, now) {
27
+ var _a;
28
+ const deltaMs = now - ts;
29
+ if (deltaMs < 6e4) return "now";
30
+ const minutes = Math.floor(deltaMs / 6e4);
31
+ if (minutes < 60) return `${minutes}m`;
32
+ const hours = Math.floor(minutes / 60);
33
+ if (hours < 24) return `${hours}h`;
34
+ const days = Math.floor(hours / 24);
35
+ if (days < 7) {
36
+ const d2 = new Date(ts);
37
+ return (_a = WEEKDAYS[d2.getDay()]) != null ? _a : `${days}d`;
38
+ }
39
+ const d = new Date(ts);
40
+ const sameYear = d.getFullYear() === new Date(now).getFullYear();
41
+ const m = d.getMonth() + 1;
42
+ const day = d.getDate();
43
+ return sameYear ? `${m}/${day}` : `${m}/${day}/${String(d.getFullYear()).slice(2)}`;
44
+ }
45
+ function bucketFor(ts, now) {
46
+ const a = new Date(ts);
47
+ const b = new Date(now);
48
+ const startOf = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
49
+ const today = startOf(b);
50
+ const yesterday = today - 24 * 60 * 60 * 1e3;
51
+ const weekAgo = today - 7 * 24 * 60 * 60 * 1e3;
52
+ const tsDay = startOf(a);
53
+ if (tsDay >= today) return "today";
54
+ if (tsDay >= yesterday) return "yesterday";
55
+ if (tsDay >= weekAgo) return "week";
56
+ return "earlier";
57
+ }
58
+ function buildSections(rows, now) {
59
+ const buckets = {
60
+ today: [],
61
+ yesterday: [],
62
+ week: [],
63
+ earlier: []
64
+ };
65
+ for (const row of rows) {
66
+ buckets[bucketFor(row.sortAt, now)].push(row);
67
+ }
68
+ const out = [];
69
+ if (buckets.today.length) out.push({
70
+ key: "today",
71
+ title: "Today",
72
+ data: buckets.today
73
+ });
74
+ if (buckets.yesterday.length) out.push({
75
+ key: "yesterday",
76
+ title: "Yesterday",
77
+ data: buckets.yesterday
78
+ });
79
+ if (buckets.week.length) out.push({
80
+ key: "week",
81
+ title: "This week",
82
+ data: buckets.week
83
+ });
84
+ if (buckets.earlier.length) out.push({
85
+ key: "earlier",
86
+ title: "Earlier",
87
+ data: buckets.earlier
88
+ });
89
+ return out;
90
+ }
91
+ function buildTheme(isDark) {
92
+ if (isDark) {
93
+ return {
94
+ bg: "#0b1220",
95
+ surface: mobileTokens.color.surface,
96
+ title: "#f1f5f9",
97
+ preview: "#94a3b8",
98
+ meta: "#9aa0b8",
99
+ muted: "#7a809a",
100
+ divider: "rgba(148, 163, 184, 0.12)",
101
+ sectionLabel: "#cbd5e1",
102
+ tileChatBg: "rgba(148, 163, 184, 0.16)",
103
+ tileChatIcon: "#e2e8f0",
104
+ tileSearchBg: "rgba(251, 191, 36, 0.16)",
105
+ tileSearchIcon: "#fde68a",
106
+ pressed: "rgba(148, 163, 184, 0.10)",
107
+ loadMoreText: "#e2e8f0",
108
+ loadMoreBorder: "rgba(148, 163, 184, 0.28)",
109
+ loadMoreBg: "rgba(148, 163, 184, 0.10)",
110
+ refreshTint: "#cbd5e1",
111
+ endDot: "#94a3b8",
112
+ emptyMarkBg: "rgba(148, 163, 184, 0.18)",
113
+ emptyMarkIcon: "#b4bccc"
114
+ };
115
+ }
116
+ return {
117
+ bg: mobileTokens.color.bg,
118
+ surface: mobileTokens.color.surface,
119
+ title: "#0b1424",
120
+ preview: "#525a73",
121
+ meta: "#7b8197",
122
+ muted: "#94a3b8",
123
+ divider: "rgba(15, 23, 42, 0.06)",
124
+ sectionLabel: "#374151",
125
+ tileChatBg: "rgba(15, 23, 42, 0.06)",
126
+ tileChatIcon: "#1f2937",
127
+ tileSearchBg: "rgba(217, 119, 6, 0.10)",
128
+ tileSearchIcon: "#b45309",
129
+ pressed: "rgba(15, 23, 42, 0.05)",
130
+ loadMoreText: "#1f2937",
131
+ loadMoreBorder: "rgba(15, 23, 42, 0.15)",
132
+ loadMoreBg: "rgba(15, 23, 42, 0.04)",
133
+ refreshTint: "#374151",
134
+ endDot: "#9ca3af",
135
+ emptyMarkBg: "rgba(15, 23, 42, 0.07)",
136
+ emptyMarkIcon: "#6b7280"
137
+ };
138
+ }
139
+ function ModeTile({
140
+ mode,
141
+ theme
142
+ }) {
143
+ const isSearch = mode === "deep-search";
144
+ return /* @__PURE__ */ jsx(View, { style: [styles.tile, {
145
+ backgroundColor: isSearch ? theme.tileSearchBg : theme.tileChatBg
146
+ }], children: isSearch ? /* @__PURE__ */ jsx(MaterialCommunityIcons, { name: "lightning-bolt-outline", size: 18, color: theme.tileSearchIcon }) : /* @__PURE__ */ jsx(Feather, { name: "message-circle", size: 18, color: theme.tileChatIcon }) });
147
+ }
148
+ function ChatHistoryRow({
149
+ session,
150
+ onSelectSession,
151
+ theme,
152
+ timeLabel
153
+ }) {
154
+ const titleColor = session.isPlaceholder ? theme.preview : theme.title;
155
+ const titleStyle = [styles.rowTitle, {
156
+ color: titleColor
157
+ }, session.isPlaceholder ? styles.rowTitlePlaceholder : null];
158
+ return /* @__PURE__ */ jsxs(Pressable, { onPress: () => onSelectSession(session.channelId, session.mode), style: ({
159
+ pressed
160
+ }) => [styles.row, pressed && {
161
+ backgroundColor: theme.pressed
162
+ }], accessibilityRole: "button", accessibilityLabel: session.previewForRender ? `Open conversation: ${session.titleForRender}. ${session.previewForRender}. ${timeLabel}` : `Open conversation: ${session.titleForRender}, ${timeLabel}`, children: [
163
+ /* @__PURE__ */ jsx(ModeTile, { mode: session.mode, theme }),
164
+ /* @__PURE__ */ jsxs(View, { style: styles.rowBody, children: [
165
+ /* @__PURE__ */ jsxs(View, { style: styles.rowHeader, children: [
166
+ /* @__PURE__ */ jsx(Text, { style: titleStyle, numberOfLines: 1, ellipsizeMode: "tail", children: session.titleForRender }),
167
+ /* @__PURE__ */ jsx(Text, { style: [styles.rowDate, {
168
+ color: theme.meta
169
+ }], numberOfLines: 1, children: timeLabel })
170
+ ] }),
171
+ session.previewForRender ? /* @__PURE__ */ jsx(Text, { style: [styles.rowPreview, {
172
+ color: theme.preview
173
+ }], numberOfLines: 1, ellipsizeMode: "tail", children: session.previewForRender }) : null
174
+ ] })
175
+ ] });
176
+ }
177
+ function ChatHistoryLanding({
178
+ onSelectSession
179
+ }) {
180
+ const {
181
+ width: screenWidth
182
+ } = useWindowDimensions();
183
+ const colorScheme = useColorScheme();
184
+ const isDark = colorScheme === "dark";
185
+ const theme = React.useMemo(() => buildTheme(isDark), [isDark]);
186
+ const apollo = useApolloClient();
187
+ const {
188
+ orgName,
189
+ loading: idsLoading
190
+ } = usePrerequisiteIds();
191
+ const {
192
+ sessions: liveSessions,
193
+ sessionsLoaded,
194
+ refetch,
195
+ sourceChannelCount
196
+ } = useChatHistorySessionsFromChannels(orgName, {
197
+ skip: !orgName
198
+ });
199
+ const [nowTs, setNowTs] = React.useState(() => Date.now());
200
+ const [refreshing, setRefreshing] = React.useState(false);
201
+ const [olderSessions, setOlderSessions] = React.useState([]);
202
+ const [hasMore, setHasMore] = React.useState(false);
203
+ const [loadingMore, setLoadingMore] = React.useState(false);
204
+ React.useEffect(() => {
205
+ if (olderSessions.length > 0) return;
206
+ setHasMore(sourceChannelCount >= HISTORY_PAGE_SIZE);
207
+ }, [sourceChannelCount, olderSessions.length]);
208
+ useFocusEffect(React.useCallback(() => {
209
+ if (!orgName) return;
210
+ void refetch(getHistoryChannelsQueryVariables(orgName)).catch(() => {
211
+ });
212
+ }, [orgName, refetch]));
213
+ const onRefresh = React.useCallback(async () => {
214
+ if (!orgName) return;
215
+ setRefreshing(true);
216
+ try {
217
+ await refetch(getHistoryChannelsQueryVariables(orgName));
218
+ setOlderSessions([]);
219
+ setNowTs(Date.now());
220
+ } catch (e) {
221
+ } finally {
222
+ setRefreshing(false);
223
+ }
224
+ }, [orgName, refetch]);
225
+ const onLoadMore = React.useCallback(async () => {
226
+ var _a, _b;
227
+ if (!orgName || loadingMore || !hasMore) return;
228
+ setLoadingMore(true);
229
+ try {
230
+ const nextSkip = liveSessions.length + olderSessions.length;
231
+ const result = await apollo.query({
232
+ query: GetChannelsByUserWithLastMessageDocument,
233
+ variables: getHistoryChannelsQueryVariables(orgName, {
234
+ skip: nextSkip,
235
+ limit: HISTORY_PAGE_SIZE
236
+ }),
237
+ fetchPolicy: "network-only"
238
+ });
239
+ const rawCount = ((_b = (_a = result.data) == null ? void 0 : _a.channelsByUser) != null ? _b : []).length;
240
+ const nextBatch = chatHistorySessionsFromChannels(result.data);
241
+ if (nextBatch.length === 0) {
242
+ setHasMore(false);
243
+ } else {
244
+ setOlderSessions((prev) => {
245
+ const map = /* @__PURE__ */ new Map();
246
+ for (const s of prev) map.set(s.channelId, s);
247
+ for (const s of nextBatch) map.set(s.channelId, s);
248
+ return Array.from(map.values());
249
+ });
250
+ setHasMore(rawCount >= HISTORY_PAGE_SIZE);
251
+ }
252
+ } catch (e) {
253
+ } finally {
254
+ setLoadingMore(false);
255
+ }
256
+ }, [apollo, hasMore, liveSessions.length, loadingMore, olderSessions, orgName]);
257
+ const sessions = React.useMemo(() => {
258
+ const map = /* @__PURE__ */ new Map();
259
+ for (const s of olderSessions) map.set(s.channelId, s);
260
+ for (const s of liveSessions) map.set(s.channelId, s);
261
+ return Array.from(map.values()).sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()).map((s) => __spreadProps(__spreadValues({}, s), {
262
+ id: s.channelId,
263
+ titleForRender: truncate(s.title, 64),
264
+ previewForRender: s.preview ? truncate(s.preview, 96) : "",
265
+ sortAt: s.updatedAt.getTime()
266
+ }));
267
+ }, [liveSessions, olderSessions]);
268
+ const sections = React.useMemo(() => buildSections(sessions, nowTs), [sessions, nowTs]);
269
+ const initialLoading = !sessionsLoaded || idsLoading || !orgName;
270
+ const contentMaxWidth = Math.min(720, Math.max(360, screenWidth - 24));
271
+ const containerStyle = {
272
+ maxWidth: contentMaxWidth,
273
+ alignSelf: "center",
274
+ width: "100%"
275
+ };
276
+ const listFooter = hasMore ? /* @__PURE__ */ jsx(View, { style: [styles.footer, containerStyle], children: /* @__PURE__ */ jsx(Pressable, { onPress: () => void onLoadMore(), disabled: loadingMore, style: ({
277
+ pressed
278
+ }) => [styles.loadMore, {
279
+ backgroundColor: theme.loadMoreBg,
280
+ borderColor: theme.loadMoreBorder
281
+ }, loadingMore && {
282
+ opacity: 0.7
283
+ }, pressed && !loadingMore && {
284
+ opacity: 0.8
285
+ }], accessibilityRole: "button", accessibilityLabel: "Load older conversations", children: loadingMore ? /* @__PURE__ */ jsx(ActivityIndicator, { size: "small", color: theme.loadMoreText }) : /* @__PURE__ */ jsx(Text, { style: [styles.loadMoreText, {
286
+ color: theme.loadMoreText
287
+ }], children: "Load older" }) }) }) : !hasMore && sessions.length > 0 ? /* @__PURE__ */ jsxs(View, { style: [styles.endHint, containerStyle], children: [
288
+ /* @__PURE__ */ jsx(View, { style: [styles.endHintDot, {
289
+ backgroundColor: theme.endDot,
290
+ opacity: 0.5
291
+ }] }),
292
+ /* @__PURE__ */ jsx(Text, { style: [styles.endHintText, {
293
+ color: theme.muted
294
+ }], children: "You're all caught up" })
295
+ ] }) : /* @__PURE__ */ jsx(View, { style: {
296
+ height: 24
297
+ } });
298
+ const renderItemSeparator = React.useCallback(() => /* @__PURE__ */ jsx(View, { style: [styles.divider, {
299
+ backgroundColor: theme.divider
300
+ }] }), [theme.divider]);
301
+ return /* @__PURE__ */ jsx(View, { style: [styles.container, {
302
+ backgroundColor: theme.bg
303
+ }], children: initialLoading ? (
304
+ /**
305
+ * Initial-fetch state mirrors the web composer's "Thinking…" pill
306
+ * (see `packages-modules/account/browser/src/pages/chat/ChatSessionPage.tsx`):
307
+ * a thin spinning arc + 12px muted caption, both tinted with
308
+ * `theme.muted` so the indicator reads as a quiet status row
309
+ * rather than a brand-stamped loading screen.
310
+ */
311
+ /* @__PURE__ */ jsx(View, { style: styles.emptyStateWrap, children: /* @__PURE__ */ jsx(ThinkingIndicator, { color: theme.muted }) })
312
+ ) : sessions.length === 0 ? /* @__PURE__ */ jsxs(View, { style: styles.emptyStateWrap, children: [
313
+ /* @__PURE__ */ jsx(View, { style: [styles.emptyMark, {
314
+ backgroundColor: theme.emptyMarkBg
315
+ }], children: /* @__PURE__ */ jsx(Feather, { name: "message-circle", size: 40, color: theme.emptyMarkIcon }) }),
316
+ /* @__PURE__ */ jsx(Text, { style: [styles.emptyTitle, {
317
+ color: theme.title
318
+ }], children: "No conversations yet" }),
319
+ /* @__PURE__ */ jsx(Text, { style: [styles.emptySubtitle, {
320
+ color: theme.muted
321
+ }], children: "Start your first chat to see your conversation history here." })
322
+ ] }) : /* @__PURE__ */ jsx(SectionList, { sections, keyExtractor: (item) => item.channelId, stickySectionHeadersEnabled: false, contentContainerStyle: [styles.listContent, containerStyle, {
323
+ paddingBottom: 32
324
+ }], ListFooterComponent: listFooter, ItemSeparatorComponent: renderItemSeparator, refreshControl: /* @__PURE__ */ jsx(RefreshControl, { refreshing, onRefresh: () => void onRefresh(), tintColor: theme.refreshTint, colors: [theme.refreshTint] }), renderSectionHeader: ({
325
+ section
326
+ }) => /* @__PURE__ */ jsx(View, { style: styles.sectionHeader, children: /* @__PURE__ */ jsx(Text, { style: [styles.sectionLabel, {
327
+ color: theme.sectionLabel
328
+ }], children: section.title }) }), renderItem: ({
329
+ item
330
+ }) => /* @__PURE__ */ jsx(ChatHistoryRow, { session: item, onSelectSession, theme, timeLabel: relativeTime(item.sortAt, nowTs) }), showsVerticalScrollIndicator: false }) });
331
+ }
332
+ const styles = StyleSheet.create({
333
+ container: {
334
+ flex: 1
335
+ },
336
+ listContent: {
337
+ paddingHorizontal: 20,
338
+ paddingTop: 4
339
+ },
340
+ /**
341
+ * Section header: sentence case, indigo-tinted, modest size. 28px top padding
342
+ * establishes rhythm between groups without dead air below the label.
343
+ */
344
+ sectionHeader: {
345
+ paddingTop: 28,
346
+ paddingBottom: 10,
347
+ paddingHorizontal: 2
348
+ },
349
+ sectionLabel: {
350
+ fontSize: 12,
351
+ fontWeight: "600",
352
+ letterSpacing: 0.2,
353
+ textTransform: "none"
354
+ },
355
+ /**
356
+ * Row: leading 40×40 mode tile, two-line body (title + preview), right-aligned
357
+ * date aligned to the title's baseline. 14px gap matches the tile-to-text
358
+ * rhythm in Manus / iOS Mail.
359
+ */
360
+ row: {
361
+ flexDirection: "row",
362
+ alignItems: "center",
363
+ paddingVertical: 12,
364
+ paddingHorizontal: 2,
365
+ gap: 14
366
+ },
367
+ tile: {
368
+ width: 40,
369
+ height: 40,
370
+ borderRadius: 12,
371
+ alignItems: "center",
372
+ justifyContent: "center"
373
+ },
374
+ rowBody: {
375
+ flex: 1,
376
+ minWidth: 0
377
+ },
378
+ rowHeader: {
379
+ flexDirection: "row",
380
+ alignItems: "baseline",
381
+ gap: 10
382
+ },
383
+ rowTitle: {
384
+ flex: 1,
385
+ fontSize: 16,
386
+ fontWeight: "600",
387
+ letterSpacing: -0.1,
388
+ lineHeight: 22
389
+ },
390
+ rowTitlePlaceholder: {
391
+ fontWeight: "500",
392
+ fontStyle: "italic"
393
+ },
394
+ rowDate: {
395
+ fontSize: 12.5,
396
+ fontWeight: "500",
397
+ fontVariant: ["tabular-nums"]
398
+ },
399
+ rowPreview: {
400
+ marginTop: 2,
401
+ fontSize: 13.5,
402
+ lineHeight: 19,
403
+ fontWeight: "400",
404
+ letterSpacing: -0.05
405
+ },
406
+ /**
407
+ * Indented hairline: starts past the tile column (40 + 14 = 54) so the rule
408
+ * aligns with the title's leading edge. Mail-style.
409
+ */
410
+ divider: {
411
+ height: StyleSheet.hairlineWidth,
412
+ marginLeft: 56
413
+ },
414
+ footer: {
415
+ paddingTop: 28,
416
+ paddingBottom: 8,
417
+ alignItems: "center"
418
+ },
419
+ /** Bordered ink-neutral pill. Label is the affordance, no leading icon, no chevron. */
420
+ loadMore: {
421
+ flexDirection: "row",
422
+ alignItems: "center",
423
+ justifyContent: "center",
424
+ paddingVertical: 10,
425
+ paddingHorizontal: 18,
426
+ borderRadius: 999,
427
+ borderWidth: 1,
428
+ minWidth: 132
429
+ },
430
+ loadMoreText: {
431
+ fontSize: 13.5,
432
+ fontWeight: "600",
433
+ letterSpacing: -0.05
434
+ },
435
+ endHint: {
436
+ flexDirection: "row",
437
+ alignItems: "center",
438
+ justifyContent: "center",
439
+ gap: 8,
440
+ paddingTop: 32,
441
+ paddingBottom: 12
442
+ },
443
+ endHintDot: {
444
+ width: 4,
445
+ height: 4,
446
+ borderRadius: 2
447
+ },
448
+ endHintText: {
449
+ fontSize: 12.5,
450
+ fontWeight: "500",
451
+ letterSpacing: 0.1
452
+ },
453
+ /**
454
+ * Empty state: stack a 64×64 indigo-tinted mark over title + subtitle.
455
+ * Generous vertical gap (16) between mark and title; tighter gap (6)
456
+ * between title and subtitle so the pair reads as one unit hanging off
457
+ * the mark, not three independent items.
458
+ */
459
+ emptyStateWrap: {
460
+ flex: 1,
461
+ minHeight: 420,
462
+ alignItems: "center",
463
+ justifyContent: "center",
464
+ paddingHorizontal: 32
465
+ },
466
+ emptyMark: {
467
+ width: 72,
468
+ height: 72,
469
+ borderRadius: 36,
470
+ alignItems: "center",
471
+ justifyContent: "center",
472
+ marginBottom: 18
473
+ },
474
+ emptyTitle: {
475
+ fontSize: 20,
476
+ fontWeight: "700",
477
+ letterSpacing: -0.3,
478
+ textAlign: "center",
479
+ marginBottom: 6
480
+ },
481
+ emptySubtitle: {
482
+ fontSize: 14,
483
+ lineHeight: 20,
484
+ textAlign: "center",
485
+ maxWidth: 340
486
+ }
487
+ });export{ChatHistoryLanding as default};//# sourceMappingURL=ChatHistoryLanding.js.map