Files
mao1/shenyan-app/src/App.tsx

116 lines
3.3 KiB
TypeScript

import { useCallback, useEffect, useRef, useState } from 'react';
import { StatusBar, StyleSheet, View } from 'react-native';
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context';
import ChatArea from './components/ChatArea';
import InputArea from './components/InputArea';
import { fetchHistory, sendMessage, syncHistory } from './api';
import { loadMessages, mergeMessages, saveMessages } from './storage';
import type { Message } from './types';
export default function App() {
const [messages, setMessages] = useState<Message[]>([]);
const [loading, setLoading] = useState(false);
const [synced, setSynced] = useState(false);
const [refreshing, setRefreshing] = useState(false);
const nextId = useRef(1);
// 启动:先读本地缓存,再异步拉远端
useEffect(() => {
(async () => {
const local = await loadMessages();
if (local.length > 0) {
setMessages(local);
nextId.current = Math.max(...local.map(m => m.id)) + 1;
}
const remote = await fetchHistory();
if (remote.length > 0) {
const merged = mergeMessages(local, remote);
setMessages(merged);
nextId.current = Math.max(...merged.map(m => m.id)) + 1;
await saveMessages(merged);
} else if (local.length > 0) {
// 服务端没数据但本地有,把本地推上去
await syncHistory(local);
}
setSynced(true);
})();
}, []);
// synced 后,每次 messages 变化同步写本地 + 远端
useEffect(() => {
if (!synced || messages.length === 0) return;
saveMessages(messages);
syncHistory(messages);
}, [messages, synced]);
const handleSend = useCallback(
async (text: string) => {
const userMsg: Message = {
id: nextId.current++,
role: 'user',
text,
};
const withUser = [...messages, userMsg];
setMessages(withUser);
setLoading(true);
try {
const reply = await sendMessage(withUser);
const shenyanMsg: Message = {
id: nextId.current++,
role: 'shenyan',
text: reply,
};
setMessages(prev => [...prev, shenyanMsg]);
} catch {
const errMsg: Message = {
id: nextId.current++,
role: 'shenyan',
text: '连接失败',
};
setMessages(prev => [...prev, errMsg]);
} finally {
setLoading(false);
}
},
[messages],
);
const handleRefresh = useCallback(async () => {
setRefreshing(true);
const remote = await fetchHistory();
if (remote.length > 0) {
setMessages(prev => {
const merged = mergeMessages(prev, remote);
nextId.current = Math.max(...merged.map(m => m.id)) + 1;
return merged;
});
}
setRefreshing(false);
}, []);
return (
<SafeAreaProvider>
<StatusBar barStyle="light-content" backgroundColor="#000" />
<SafeAreaView style={styles.container} edges={['top']}>
<ChatArea
messages={messages}
onRefresh={handleRefresh}
refreshing={refreshing}
/>
<InputArea onSend={handleSend} disabled={loading} />
</SafeAreaView>
</SafeAreaProvider>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000',
},
});