Compare commits

...

33 Commits

Author SHA1 Message Date
c6be061130 f 2025-08-09 14:58:54 +08:00
60152a64f0 fix 2025-08-09 14:50:38 +08:00
9de8c3b5c7 f 2025-08-09 13:25:56 +08:00
4c4360cefc test 2025-08-09 13:13:18 +08:00
76f2e6ed48 s 2025-08-09 11:43:36 +08:00
384e607fe1 c 2025-08-09 11:33:16 +08:00
a7b6aeeb31 f 2025-08-09 11:12:42 +08:00
4e755b8f10 f 2025-08-09 11:04:16 +08:00
797414e78b f 2025-08-09 10:52:17 +08:00
2ff82495ac fix: Image 2025-08-09 10:43:18 +08:00
0482f23d97 f 2025-08-09 10:28:23 +08:00
85d9b823de 注释askhello 2025-08-09 10:12:28 +08:00
027f7b1672 fix 2025-08-08 14:29:43 +08:00
8f0cb0ada2 f 2025-08-08 14:13:43 +08:00
1891f5c359 fix 2025-08-07 19:55:46 +08:00
f8bd3b13be fix 2025-08-07 19:50:35 +08:00
1a28d8becd fix: hello回 2025-08-07 19:48:46 +08:00
3f2b849db2 fix: 恢复聊天页面 2025-08-07 19:42:49 +08:00
995f5ad981 f 2025-08-07 19:37:15 +08:00
9341a1560f f 2025-08-07 19:36:26 +08:00
f2b09cb013 f 2025-08-07 19:29:53 +08:00
827bf7b164 fix 2025-08-07 19:22:55 +08:00
fd5ea7f318 chore: 修复一下导航布局 2025-08-07 19:14:46 +08:00
162f3b91e4 fix: 注释掉ask页面的prefetch 2025-08-07 19:08:39 +08:00
b1031cf2b6 fix: 修复一下 2025-08-07 19:04:46 +08:00
45a3660ab8 fix: 试一下navigate 2025-08-07 19:02:17 +08:00
ce50710818 fix: 试一下replace 2025-08-07 19:00:45 +08:00
448e8dfb53 fix 2025-08-07 18:48:41 +08:00
d59378c2da fix: router 2025-08-07 18:43:33 +08:00
15fc8f3ad4 fix 2025-08-07 18:42:54 +08:00
da0b949ca4 fix: 连一下ws 2025-08-07 18:41:49 +08:00
da00968586 fix: ENV 2025-08-07 18:31:43 +08:00
193084fb62 fix: 移除ask页面的ws调用 2025-08-07 17:19:15 +08:00
10 changed files with 186 additions and 233 deletions

View File

@ -1,4 +1,3 @@
import { HapticTab } from '@/components/HapticTab';
import AskNavbar from '@/components/layout/ask'; import AskNavbar from '@/components/layout/ask';
import { TabBarIcon } from '@/components/navigation/TabBarIcon'; import { TabBarIcon } from '@/components/navigation/TabBarIcon';
import { requestNotificationPermission } from '@/components/owner/utils'; import { requestNotificationPermission } from '@/components/owner/utils';
@ -188,28 +187,13 @@ export default function TabLayout() {
return ( return (
<> <>
<Tabs <Tabs
screenOptions={{ tabBar={props => <AskNavbar {...props} wsStatus={wsStatus} />}
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
headerShown: false,
tabBarButton: HapticTab,
tabBarBackground: TabBarBackground,
tabBarStyle: Platform.select({
ios: {
// Use a transparent background on iOS to show the blur effect
position: 'absolute',
},
default: {},
}),
}}
> >
{/* 落地页 */} {/* 落地页 */}
<Tabs.Screen <Tabs.Screen
name="index" name="index"
options={{ options={{
title: 'Memo', href: null,
tabBarButton: () => null, // 隐藏底部标签栏
headerShown: false, // 隐藏导航栏
tabBarStyle: { display: 'none' } // 确保在标签栏中不显示
}} }}
/> />
{/* 登录 */} {/* 登录 */}
@ -260,10 +244,7 @@ export default function TabLayout() {
<Tabs.Screen <Tabs.Screen
name="ask" name="ask"
options={{ options={{
title: 'ask', href: null,
tabBarButton: () => null, // 隐藏底部标签栏
headerShown: false, // 隐藏导航栏
tabBarStyle: { display: 'none' }, // 确保在标签栏中不显示
...TransitionPresets.ShiftTransition, ...TransitionPresets.ShiftTransition,
}} }}
/> />
@ -271,10 +252,7 @@ export default function TabLayout() {
<Tabs.Screen <Tabs.Screen
name="memo-list" name="memo-list"
options={{ options={{
title: 'memo-list', href: null,
tabBarButton: () => null, // 隐藏底部标签栏
headerShown: false, // 隐藏导航栏
tabBarStyle: { display: 'none' }, // 确保在标签栏中不显示
...TransitionPresets.ShiftTransition, ...TransitionPresets.ShiftTransition,
}} }}
/> />
@ -282,10 +260,7 @@ export default function TabLayout() {
<Tabs.Screen <Tabs.Screen
name="owner" name="owner"
options={{ options={{
title: 'owner', href: null,
tabBarButton: () => null, // 隐藏底部标签栏
headerShown: false, // 隐藏导航栏
tabBarStyle: { display: 'none' }, // 确保在标签栏中不显示
...TransitionPresets.ShiftTransition, ...TransitionPresets.ShiftTransition,
}} }}
/> />
@ -377,7 +352,6 @@ export default function TabLayout() {
}} }}
/> />
</Tabs > </Tabs >
<AskNavbar wsStatus={wsStatus} />
</> </>
); );
} }

View File

@ -1,12 +1,9 @@
import ReturnArrow from "@/assets/icons/svg/returnArrow.svg"; import ReturnArrow from "@/assets/icons/svg/returnArrow.svg";
import Chat from "@/components/ask/chat";
import AskHello from "@/components/ask/hello"; import AskHello from "@/components/ask/hello";
import SendMessage from "@/components/ask/send";
import { ThemedText } from "@/components/ThemedText"; import { ThemedText } from "@/components/ThemedText";
import { fetchApi } from "@/lib/server-api-util"; import { fetchApi } from "@/lib/server-api-util";
import { getWebSocketErrorMessage, webSocketManager, WsMessage } from "@/lib/websocket-util"; import { Message } from "@/types/ask";
import { Assistant, Message } from "@/types/ask"; import { useFocusEffect, useLocalSearchParams, useRouter } from "expo-router";
import { router, useFocusEffect, useLocalSearchParams } from "expo-router";
import { useCallback, useEffect, useRef, useState } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { import {
@ -25,6 +22,7 @@ import { runOnJS } from 'react-native-reanimated';
import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useSafeAreaInsets } from "react-native-safe-area-context";
export default function AskScreen() { export default function AskScreen() {
const router = useRouter();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const chatListRef = useRef<FlatList>(null); const chatListRef = useRef<FlatList>(null);
@ -99,88 +97,88 @@ export default function AskScreen() {
useFocusEffect( useFocusEffect(
useCallback(() => { useCallback(() => {
webSocketManager.connect(); // webSocketManager.connect();
const handleChatStream = (message: WsMessage) => { // const handleChatStream = (message: WsMessage) => {
if (message.type === 'ChatStream') { // if (message.type === 'ChatStream') {
setUserMessages(prevMessages => { // setUserMessages(prevMessages => {
const newMessages = [...prevMessages]; // const newMessages = [...prevMessages];
const lastMessage = newMessages[newMessages.length - 1]; // const lastMessage = newMessages[newMessages.length - 1];
if (lastMessage && lastMessage.role === Assistant) { // if (lastMessage && lastMessage.role === Assistant) {
if (typeof lastMessage.content === 'string') { // if (typeof lastMessage.content === 'string') {
if (lastMessage.content === 'keepSearchIng') { // if (lastMessage.content === 'keepSearchIng') {
// 第一次收到流式消息,替换占位符 // // 第一次收到流式消息,替换占位符
lastMessage.content = message.chunk; // lastMessage.content = message.chunk;
} else { // } else {
// 持续追加流式消息 // // 持续追加流式消息
lastMessage.content += message.chunk; // lastMessage.content += message.chunk;
} // }
} else { // } else {
// 如果 content 是数组,则更新第一个 text 部分 // // 如果 content 是数组,则更新第一个 text 部分
const textPart = lastMessage.content.find(p => p.type === 'text'); // const textPart = lastMessage.content.find(p => p.type === 'text');
if (textPart) { // if (textPart) {
textPart.text = (textPart.text || '') + message.chunk; // textPart.text = (textPart.text || '') + message.chunk;
} // }
} // }
} // }
return newMessages; // return newMessages;
}); // });
} // }
}; // };
const handleChatStreamEnd = (message: WsMessage) => { // const handleChatStreamEnd = (message: WsMessage) => {
if (message.type === 'ChatStreamEnd') { // if (message.type === 'ChatStreamEnd') {
setUserMessages(prevMessages => { // setUserMessages(prevMessages => {
const newMessages = [...prevMessages]; // const newMessages = [...prevMessages];
const lastMessage = newMessages[newMessages.length - 1]; // const lastMessage = newMessages[newMessages.length - 1];
if (lastMessage && lastMessage.role === Assistant) { // if (lastMessage && lastMessage.role === Assistant) {
// 使用最终消息替换流式消息,确保 message.message 存在 // // 使用最终消息替换流式消息,确保 message.message 存在
if (message.message) { // if (message.message) {
newMessages[newMessages.length - 1] = message.message as Message; // newMessages[newMessages.length - 1] = message.message as Message;
} else { // } else {
// 如果最终消息为空,则移除 'keepSearchIng' 占位符 // // 如果最终消息为空,则移除 'keepSearchIng' 占位符
return prevMessages.filter(m => !(typeof m.content === 'string' && m.content === 'keepSearchIng')); // return prevMessages.filter(m => !(typeof m.content === 'string' && m.content === 'keepSearchIng'));
} // }
} // }
return newMessages; // return newMessages;
}); // });
} // }
}; // };
const handleError = (message: WsMessage) => { // const handleError = (message: WsMessage) => {
if (message.type === 'Error') { // if (message.type === 'Error') {
console.log(`WebSocket Error: ${message.code} - ${message.message}`); // console.log(`WebSocket Error: ${message.code} - ${message.message}`);
// 可以在这里添加错误提示,例如替换最后一条消息为错误信息 // // 可以在这里添加错误提示,例如替换最后一条消息为错误信息
setUserMessages(prev => { // setUserMessages(prev => {
// 创建新的数组和新的消息对象 // // 创建新的数组和新的消息对象
return prev.map((msg, index) => { // return prev.map((msg, index) => {
if (index === prev.length - 1 && // if (index === prev.length - 1 &&
typeof msg.content === 'string' && // typeof msg.content === 'string' &&
msg.content === 'keepSearchIng') { // msg.content === 'keepSearchIng') {
// 返回新的消息对象 // // 返回新的消息对象
return { // return {
...msg, // ...msg,
content: getWebSocketErrorMessage(message.code, t) // content: getWebSocketErrorMessage(message.code, t)
}; // };
} // }
return msg; // return msg;
}); // });
}); // });
} // }
}; // };
webSocketManager.subscribe('ChatStream', handleChatStream); // webSocketManager.subscribe('ChatStream', handleChatStream);
webSocketManager.subscribe('ChatStreamEnd', handleChatStreamEnd); // webSocketManager.subscribe('ChatStreamEnd', handleChatStreamEnd);
webSocketManager.subscribe('Error', handleError); // webSocketManager.subscribe('Error', handleError);
return () => { // return () => {
webSocketManager.unsubscribe('ChatStream', handleChatStream); // webSocketManager.unsubscribe('ChatStream', handleChatStream);
webSocketManager.unsubscribe('ChatStreamEnd', handleChatStreamEnd); // webSocketManager.unsubscribe('ChatStreamEnd', handleChatStreamEnd);
webSocketManager.unsubscribe('Error', handleError); // webSocketManager.unsubscribe('Error', handleError);
// 可以在这里选择断开连接,或者保持连接以加快下次进入页面的速度 // // 可以在这里选择断开连接,或者保持连接以加快下次进入页面的速度
// webSocketManager.disconnect(); // // webSocketManager.disconnect();
}; // };
}, []) }, [])
); );
@ -196,7 +194,7 @@ export default function AskScreen() {
setIsHello(true); setIsHello(true);
setConversationId(null); setConversationId(null);
} }
}, [sessionId, newSession]); }, [sessionId, newSession])
useEffect(() => { useEffect(() => {
if (isHello) { if (isHello) {
@ -234,23 +232,6 @@ export default function AskScreen() {
} }
}, [isHello, fadeAnim, fadeAnimChat]); }, [isHello, fadeAnim, fadeAnimChat]);
useEffect(() => {
if (!isHello) {
// 不再自动关闭键盘,让用户手动控制
// 这里可以添加其他需要在隐藏hello界面时执行的逻辑
scrollToEnd(false);
}
}, [isHello]);
useFocusEffect(
useCallback(() => {
if (!sessionId) {
setIsHello(true);
setUserMessages([])
}
}, [sessionId])
);
return ( return (
<GestureDetector gesture={gesture}> <GestureDetector gesture={gesture}>
<View style={[styles.container, { paddingTop: insets.top, paddingBottom: insets.bottom }]}> <View style={[styles.container, { paddingTop: insets.top, paddingBottom: insets.bottom }]}>
@ -268,7 +249,9 @@ export default function AskScreen() {
console.log('失去焦点失败:', error); console.log('失去焦点失败:', error);
} }
Keyboard.dismiss(); Keyboard.dismiss();
router.push('/memo-list'); setTimeout(() => {
router.replace('/memo-list');
}, 100);
}} }}
> >
<ReturnArrow /> <ReturnArrow />
@ -303,17 +286,17 @@ export default function AskScreen() {
} }
]} ]}
> >
<Chat {/* <Chat
ref={chatListRef} ref={chatListRef}
userMessages={userMessages} userMessages={userMessages}
sessionId={sessionId} sessionId={sessionId}
setSelectedImages={setSelectedImages} setSelectedImages={setSelectedImages}
selectedImages={selectedImages} selectedImages={selectedImages}
style={styles.chatContainer} style={styles.chatContainer}
contentContainerStyle={styles.chatContentContainer} contentContainerStyle={styles.chatContentContainer}
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
onContentSizeChange={() => scrollToEnd()} onContentSizeChange={() => scrollToEnd()}
/> /> */}
</Animated.View> </Animated.View>
</View> </View>
@ -322,18 +305,18 @@ export default function AskScreen() {
behavior={Platform.OS === "ios" ? "padding" : "height"} behavior={Platform.OS === "ios" ? "padding" : "height"}
keyboardVerticalOffset={0} > keyboardVerticalOffset={0} >
<View style={styles.inputContainer} key={conversationId}> <View style={styles.inputContainer} key={conversationId}>
<SendMessage {/* <SendMessage
setIsHello={setIsHello} setIsHello={setIsHello}
conversationId={conversationId} conversationId={conversationId}
setConversationId={setConversationId} setConversationId={setConversationId}
setUserMessages={setUserMessages} setUserMessages={setUserMessages}
selectedImages={selectedImages} selectedImages={selectedImages}
setSelectedImages={setSelectedImages} setSelectedImages={setSelectedImages}
/> /> */}
</View> </View>
</KeyboardAvoidingView> </KeyboardAvoidingView>
</View > </View >
</GestureDetector> </GestureDetector >
); );
} }

View File

@ -225,7 +225,7 @@ export default function HomeScreen() {
useEffect(() => { useEffect(() => {
setIsLoading(true); setIsLoading(true);
checkAuthStatus(router, () => { checkAuthStatus(router, () => {
router.replace('/ask') router.replace('/memo-list')
}, false).then(() => { }, false).then(() => {
setIsLoading(false); setIsLoading(false);
}).catch(() => { }).catch(() => {

View File

@ -14,7 +14,7 @@ import SkeletonItem from '@/components/memo/SkeletonItem';
// 类型定义 // 类型定义
import { useUploadManager } from '@/hooks/useUploadManager'; import { useUploadManager } from '@/hooks/useUploadManager';
import { getCachedData, prefetchChatDetail, prefetchChats } from '@/lib/prefetch'; import { getCachedData, prefetchChatDetail } from '@/lib/prefetch';
import { fetchApi } from '@/lib/server-api-util'; import { fetchApi } from '@/lib/server-api-util';
import { Chat, getMessageText } from '@/types/ask'; import { Chat, getMessageText } from '@/types/ask';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
@ -105,18 +105,11 @@ const MemoList = () => {
const initialize = async () => { const initialize = async () => {
try { try {
// 并行预加载资源和数据 // 并行预加载资源和数据
await Promise.all([ await Promise.all([
preloadAssets(), preloadAssets(),
prefetchChats().then((data) => { fetchHistoryList()
if (isActive && data) {
setHistoryList(data as Chat[]);
}
}),
]); ]);
// 主数据加载
await fetchHistoryList();
} catch (error) { } catch (error) {
console.error('初始化失败:', error); console.error('初始化失败:', error);
} finally { } finally {

View File

@ -1,9 +1,9 @@
import { ThemedText } from "@/components/ThemedText"; import { ThemedText } from "@/components/ThemedText";
import { webSocketManager } from "@/lib/websocket-util"; import { webSocketManager } from "@/lib/websocket-util";
import { Message } from "@/types/ask"; import { Message } from "@/types/ask";
import { Dispatch, SetStateAction } from "react"; import { Dispatch, SetStateAction, useCallback, useRef } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Dimensions, Image, ScrollView, StyleSheet, TouchableOpacity, View } from 'react-native'; import { Image, ScrollView, StyleSheet, TouchableOpacity, useWindowDimensions, View } from 'react-native';
import { createNewConversation } from "./utils"; import { createNewConversation } from "./utils";
interface AskHelloProps { interface AskHelloProps {
@ -13,39 +13,63 @@ interface AskHelloProps {
} }
export default function AskHello({ setUserMessages, setConversationId, setIsHello }: AskHelloProps) { export default function AskHello({ setUserMessages, setConversationId, setIsHello }: AskHelloProps) {
const { t } = useTranslation(); const { t } = useTranslation();
const width = Dimensions.get('window').width; const { width, height } = useWindowDimensions();
const height = Dimensions.get('window').height;
const handleCase = async (text: string) => { const inFlightRef = useRef(false);
setIsHello(false);
setUserMessages([ const handleCase = useCallback(async (text: string) => {
{ if (inFlightRef.current) return;
id: Math.random().toString(36).substring(2, 9), inFlightRef.current = true;
content: text, try {
role: 'user', // UI
timestamp: new Date().toISOString() setIsHello(false);
}, setUserMessages([
{ {
id: Math.random().toString(36).substring(2, 9), id: Math.random().toString(36).substring(2, 9),
content: "keepSearchIng", content: text,
role: 'assistant', role: 'user',
timestamp: new Date().toISOString() timestamp: new Date().toISOString()
},
{
id: Math.random().toString(36).substring(2, 9),
content: "keepSearchIng",
role: 'assistant',
timestamp: new Date().toISOString()
}
]);
const sessionId = await createNewConversation(text);
if (!sessionId) {
console.error("Failed to create a new conversation.");
setUserMessages(prev => prev.filter(item => item.content !== 'keepSearchIng'));
return;
} }
]);
const sessionId = await createNewConversation(text);
if (sessionId) {
setConversationId(sessionId); setConversationId(sessionId);
webSocketManager.send({ try {
type: 'Chat', if (webSocketManager && typeof (webSocketManager as any).send === 'function') {
session_id: sessionId, (webSocketManager as any).send({
message: text type: 'Chat',
}); session_id: sessionId,
} else { message: text
console.error("Failed to create a new conversation."); });
} else {
throw new Error('WebSocket manager is not ready');
}
} catch (wsErr) {
console.error('WebSocket send failed:', wsErr);
setUserMessages(prev => prev.filter(item => item.content !== 'keepSearchIng'));
}
} catch (err) {
console.error('handleCase failed:', err);
setUserMessages(prev => prev.filter(item => item.content !== 'keepSearchIng')); setUserMessages(prev => prev.filter(item => item.content !== 'keepSearchIng'));
} finally {
inFlightRef.current = false;
} }
} }, [setConversationId, setIsHello, setUserMessages]);
return ( return (
<View className="flex-1 bg-white w-full"> <View className="flex-1 bg-white w-full">
<ScrollView <ScrollView
@ -96,7 +120,7 @@ export default function AskHello({ setUserMessages, setConversationId, setIsHell
</View> </View>
</View> </View>
</ScrollView> </ScrollView>
</View> </View >
); );
} }
@ -106,7 +130,6 @@ const styles = StyleSheet.create({
flexDirection: 'row', flexDirection: 'row',
flexWrap: 'wrap', flexWrap: 'wrap',
justifyContent: 'center', justifyContent: 'center',
gap: 8,
width: '100%', width: '100%',
marginTop: 16 marginTop: 16
}, },
@ -115,6 +138,8 @@ const styles = StyleSheet.create({
borderColor: "#AC7E35", borderColor: "#AC7E35",
borderRadius: 10, borderRadius: 10,
paddingHorizontal: 8, paddingHorizontal: 8,
marginHorizontal: 4,
marginVertical: 4,
width: 'auto', width: 'auto',
fontSize: 14, fontSize: 14,
color: "#4C320C" color: "#4C320C"

View File

@ -3,7 +3,6 @@ import { Message } from "@/types/ask";
import * as FileSystem from 'expo-file-system'; import * as FileSystem from 'expo-file-system';
import * as MediaLibrary from 'expo-media-library'; import * as MediaLibrary from 'expo-media-library';
import { TFunction } from "i18next"; import { TFunction } from "i18next";
import { useCallback } from "react";
import { Alert } from 'react-native'; import { Alert } from 'react-native';
// 实现一个函数,从两个数组中轮流插入新数组 // 实现一个函数,从两个数组中轮流插入新数组
@ -19,12 +18,12 @@ export const mergeArrays = (arr1: any[], arr2: any[]) => {
// 创建新对话并获取消息 // 创建新对话并获取消息
export const createNewConversation = useCallback(async (user_text: string) => { export const createNewConversation = async (user_text: string) => {
const data = await fetchApi<string>("/chat/new", { const data = await fetchApi<string>("/chat/new", {
method: "POST", method: "POST",
}); });
return data return data
}, []); };
// 获取对话信息 // 获取对话信息
export const getConversation = async ({ export const getConversation = async ({

View File

@ -3,8 +3,9 @@ import ChatNotInSvg from "@/assets/icons/svg/chatNotIn.svg";
import PersonInSvg from "@/assets/icons/svg/personIn.svg"; import PersonInSvg from "@/assets/icons/svg/personIn.svg";
import PersonNotInSvg from "@/assets/icons/svg/personNotIn.svg"; import PersonNotInSvg from "@/assets/icons/svg/personNotIn.svg";
import { WebSocketStatus } from "@/lib/websocket-util"; import { WebSocketStatus } from "@/lib/websocket-util";
import { router, usePathname } from "expo-router"; import { BottomTabBarProps } from "@react-navigation/bottom-tabs";
import React, { useCallback, useEffect, useMemo } from 'react'; import { router } from "expo-router";
import React, { useMemo } from 'react';
import { Dimensions, Image, StyleSheet, TouchableOpacity, View } from 'react-native'; import { Dimensions, Image, StyleSheet, TouchableOpacity, View } from 'react-native';
import Svg, { Circle, Ellipse, G, Mask, Path, Rect } from "react-native-svg"; import Svg, { Circle, Ellipse, G, Mask, Path, Rect } from "react-native-svg";
@ -42,14 +43,15 @@ const CenterButtonSvg = React.memo(() => (
</Svg> </Svg>
)); ));
interface AskNavbarProps { type AskNavbarProps = BottomTabBarProps & {
wsStatus: WebSocketStatus; wsStatus: WebSocketStatus;
} };
const AskNavbar = ({ wsStatus }: AskNavbarProps) => { const AskNavbar = ({ state, descriptors, navigation, wsStatus }: AskNavbarProps) => {
// 获取设备尺寸 // 获取设备尺寸
const { width } = useMemo(() => Dimensions.get('window'), []); const { width } = useMemo(() => Dimensions.get('window'), []);
const pathname = usePathname(); const { routes, index } = state;
const currentRouteName = routes[index].name;
const statusColor = useMemo(() => { const statusColor = useMemo(() => {
switch (wsStatus) { switch (wsStatus) {
@ -64,34 +66,6 @@ const AskNavbar = ({ wsStatus }: AskNavbarProps) => {
} }
}, [wsStatus]); }, [wsStatus]);
// 预加载目标页面
useEffect(() => {
const preloadPages = async () => {
try {
await Promise.all([
router.prefetch('/memo-list'),
router.prefetch('/ask'),
router.prefetch('/owner')
]);
} catch (error) {
console.warn('预加载页面失败:', error);
}
};
preloadPages();
}, []);
// 使用 useCallback 缓存导航函数
const navigateTo = useCallback((route: string) => {
if (route === '/ask') {
router.push({
pathname: '/ask',
params: { newSession: "true" }
});
} else {
router.push(route as any);
}
}, []);
// 使用 useMemo 缓存样式对象 // 使用 useMemo 缓存样式对象
const styles = useMemo(() => StyleSheet.create({ const styles = useMemo(() => StyleSheet.create({
container: { container: {
@ -156,7 +130,7 @@ const AskNavbar = ({ wsStatus }: AskNavbarProps) => {
}), [width, statusColor]); }), [width, statusColor]);
// 如果当前路径是ask页面则不渲染导航栏 // 如果当前路径是ask页面则不渲染导航栏
if (pathname != '/memo-list' && pathname != '/owner') { if (currentRouteName !== 'memo-list' && currentRouteName !== 'owner') {
return null; return null;
} }
@ -165,18 +139,18 @@ const AskNavbar = ({ wsStatus }: AskNavbarProps) => {
<Image source={require('@/assets/images/png/owner/ask.png')} style={{ width: width * 1.18, height: 100, resizeMode: 'cover', marginLeft: -width * 0.07 }} /> <Image source={require('@/assets/images/png/owner/ask.png')} style={{ width: width * 1.18, height: 100, resizeMode: 'cover', marginLeft: -width * 0.07 }} />
<View style={styles.navContainer}> <View style={styles.navContainer}>
<TouchableOpacity <TouchableOpacity
onPress={() => navigateTo('/memo-list')} onPress={() => navigation.navigate('memo-list')}
style={[styles.navButton, { alignItems: "flex-start", paddingLeft: 16 }]} style={[styles.navButton, { alignItems: "flex-start", paddingLeft: 16 }]}
> >
<TabIcon <TabIcon
isActive={pathname === "/memo-list"} isActive={currentRouteName === "memo-list"}
ActiveIcon={ChatInSvg} ActiveIcon={ChatInSvg}
InactiveIcon={ChatNotInSvg} InactiveIcon={ChatNotInSvg}
/> />
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity <TouchableOpacity
onPress={() => navigateTo('/ask')} onPress={() => router.push({ pathname: '/ask', params: { newSession: "true" } })}
style={styles.centerButton} style={styles.centerButton}
> >
<View style={styles.statusIndicator} /> <View style={styles.statusIndicator} />
@ -184,11 +158,11 @@ const AskNavbar = ({ wsStatus }: AskNavbarProps) => {
</TouchableOpacity> </TouchableOpacity>
<TouchableOpacity <TouchableOpacity
onPress={() => navigateTo('/owner')} onPress={() => navigation.navigate('owner')}
style={styles.navButton} style={styles.navButton}
> >
<TabIcon <TabIcon
isActive={pathname === "/owner"} isActive={currentRouteName === "owner"}
ActiveIcon={PersonInSvg} ActiveIcon={PersonInSvg}
InactiveIcon={PersonNotInSvg} InactiveIcon={PersonNotInSvg}
/> />

View File

@ -1,5 +1,10 @@
import React from 'react';
import { View } from 'react-native';
// This is a shim for web and Android where the tab bar is generally opaque. // This is a shim for web and Android where the tab bar is generally opaque.
export default undefined; export default function TabBarBackground() {
return <View style={{ flex: 1, backgroundColor: 'transparent' }} />;
}
export function useBottomTabOverflow() { export function useBottomTabOverflow() {
return 0; return 0;

View File

@ -25,7 +25,7 @@ export interface PagedResult<T> {
// 获取.env文件中的变量 // 获取.env文件中的变量
export const API_ENDPOINT = Constants.expoConfig?.extra?.API_ENDPOINT || "http://192.168.31.16:31646/api"; export const API_ENDPOINT = process.env.EXPO_PUBLIC_API_ENDPOINT || Constants.expoConfig?.extra?.API_ENDPOINT;
// 更新 access_token 的逻辑 - 用于React组件中 // 更新 access_token 的逻辑 - 用于React组件中

View File

@ -4,7 +4,7 @@ import { TFunction } from 'i18next';
import { Platform } from 'react-native'; import { Platform } from 'react-native';
// 从环境变量或默认值中定义 WebSocket 端点 // 从环境变量或默认值中定义 WebSocket 端点
export const WEBSOCKET_ENDPOINT = Constants.expoConfig?.extra?.WEBSOCKET_ENDPOINT || "ws://192.168.31.16:31646/ws/chat"; export const WEBSOCKET_ENDPOINT = process.env.EXPO_PUBLIC_WEBSOCKET_ENDPOINT || Constants.expoConfig?.extra?.WEBSOCKET_ENDPOINT;
export type WebSocketStatus = 'connecting' | 'connected' | 'disconnected' | 'reconnecting'; export type WebSocketStatus = 'connecting' | 'connected' | 'disconnected' | 'reconnecting';