@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.
- package/lib/api/stt.js +54 -0
- package/lib/api/stt.js.map +1 -0
- package/lib/assets/icon.png +0 -0
- package/lib/components/CustomDrawer.js +479 -0
- package/lib/components/CustomDrawer.js.map +1 -0
- package/lib/components/GatewayConnector/GatewayConnector.js +18 -0
- package/lib/components/GatewayConnector/GatewayConnector.js.map +1 -0
- package/lib/components/GatewayToolbarButtonMobile.js +84 -0
- package/lib/components/GatewayToolbarButtonMobile.js.map +1 -0
- package/lib/components/NavigationHeader/NavigationHeader.js +214 -0
- package/lib/components/NavigationHeader/NavigationHeader.js.map +1 -0
- package/lib/components/ThinkingIndicator.js +55 -0
- package/lib/components/ThinkingIndicator.js.map +1 -0
- package/lib/components/YantraBrandLoader.js +94 -0
- package/lib/components/YantraBrandLoader.js.map +1 -0
- package/lib/compute.js +114 -5
- package/lib/compute.js.map +1 -1
- package/lib/config/constants.js +18 -0
- package/lib/config/constants.js.map +1 -0
- package/lib/config/env-config.js +75 -19
- package/lib/config/env-config.js.map +1 -1
- package/lib/contexts/CdecliConnectionContext.js +47 -0
- package/lib/contexts/CdecliConnectionContext.js.map +1 -0
- package/lib/contexts/GatewayContext.js +77 -0
- package/lib/contexts/GatewayContext.js.map +1 -0
- package/lib/features/audio-input/AudioRecorderPanel.js +220 -0
- package/lib/features/audio-input/AudioRecorderPanel.js.map +1 -0
- package/lib/features/audio-input/MicErrorBoundary.js +34 -0
- package/lib/features/audio-input/MicErrorBoundary.js.map +1 -0
- package/lib/features/audio-input/useAudioPermission.js +24 -0
- package/lib/features/audio-input/useAudioPermission.js.map +1 -0
- package/lib/graphql/agentGatewayDocuments.js +53 -0
- package/lib/graphql/agentGatewayDocuments.js.map +1 -0
- package/lib/hooks/useAccountDefaultSettings.js +38 -0
- package/lib/hooks/useAccountDefaultSettings.js.map +1 -0
- package/lib/hooks/useCdecliAutoConnect.js +244 -0
- package/lib/hooks/useCdecliAutoConnect.js.map +1 -0
- package/lib/hooks/useCdecliChannel.js +161 -0
- package/lib/hooks/useCdecliChannel.js.map +1 -0
- package/lib/hooks/useChatApi.js +386 -170
- package/lib/hooks/useChatApi.js.map +1 -1
- package/lib/hooks/useChatStream.js +179 -137
- package/lib/hooks/useChatStream.js.map +1 -1
- package/lib/hooks/useGatewayConnection.js +123 -0
- package/lib/hooks/useGatewayConnection.js.map +1 -0
- package/lib/hooks/useGatewayRegistry.js +28 -0
- package/lib/hooks/useGatewayRegistry.js.map +1 -0
- package/lib/hooks/usePrerequisiteIds.js +209 -0
- package/lib/hooks/usePrerequisiteIds.js.map +1 -0
- package/lib/hooks/useWorkspaceProvisioner.js +236 -0
- package/lib/hooks/useWorkspaceProvisioner.js.map +1 -0
- package/lib/index.js +1 -1
- package/lib/index.js.map +1 -1
- package/lib/routes.json +120 -5
- package/lib/screens/Chat/index.js +409 -0
- package/lib/screens/Chat/index.js.map +1 -0
- package/lib/screens/ChatHistory/index.js +56 -0
- package/lib/screens/ChatHistory/index.js.map +1 -0
- package/lib/screens/Home/HomeScreen.js +364 -144
- package/lib/screens/Home/HomeScreen.js.map +1 -1
- package/lib/screens/Home/components/ChatHistoryLanding.js +487 -0
- package/lib/screens/Home/components/ChatHistoryLanding.js.map +1 -0
- package/lib/screens/Home/components/DeepSearchModal.js +349 -0
- package/lib/screens/Home/components/DeepSearchModal.js.map +1 -0
- package/lib/screens/Home/deepSearchUtils.js +41 -0
- package/lib/screens/Home/deepSearchUtils.js.map +1 -0
- package/lib/screens/NewChat/index.js +43 -0
- package/lib/screens/NewChat/index.js.map +1 -0
- package/lib/services/agentSessionManager.js +451 -0
- package/lib/services/agentSessionManager.js.map +1 -0
- package/lib/services/gatewayApiKeyBridge.js +4 -0
- package/lib/services/gatewayApiKeyBridge.js.map +1 -0
- package/lib/services/gatewayClient.js +470 -0
- package/lib/services/gatewayClient.js.map +1 -0
- package/lib/theme/mobileTokens.js +18 -0
- package/lib/theme/mobileTokens.js.map +1 -0
- package/lib/utils/cdecodeUri.js +68 -0
- package/lib/utils/cdecodeUri.js.map +1 -0
- package/lib/utils/gatewaySelectionStorage.js +21 -0
- package/lib/utils/gatewaySelectionStorage.js.map +1 -0
- package/lib/utils/syncMobileOrgRouteContext.js +61 -0
- package/lib/utils/syncMobileOrgRouteContext.js.map +1 -0
- package/package.json +7 -3
- package/lib/api/chatApi.js +0 -102
- 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
|