add shenyan-app RN project + PWA push/noise updates
This commit is contained in:
115
shenyan-app/src/App.tsx
Normal file
115
shenyan-app/src/App.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
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',
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user