feat: 添加聊天会话页面,支持会话ID和标题显示,优化会话列表和搜索功能
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
import { StatusBar } from "expo-status-bar";
|
||||
import { useMemo, useState } from "react";
|
||||
import { StyleSheet, View } from "react-native";
|
||||
import { Pressable, StyleSheet, Text, View } from "react-native";
|
||||
import { useRouter } from "expo-router";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
|
||||
import { ConversationHeader } from "@/features/im/components/conversation-header";
|
||||
import { ConversationList } from "@/features/im/components/conversation-list";
|
||||
import { ConversationSearchBar } from "@/features/im/components/conversation-search-bar";
|
||||
import { useDebouncedValue } from "@/features/im/hooks/use-debounced-value";
|
||||
import { useConversationList } from "@/features/im/hooks/use-conversation-list";
|
||||
import { useConversationSearch } from "@/features/im/hooks/use-conversation-search";
|
||||
import { mockConversations } from "@/features/im/mocks/mock-conversations";
|
||||
@@ -20,16 +22,18 @@ const SYSTEM_MESSAGE_ITEM: ConversationItem = {
|
||||
};
|
||||
|
||||
export default function IndexRoute() {
|
||||
const router = useRouter();
|
||||
const insets = useSafeAreaInsets();
|
||||
const [keyword, setKeyword] = useState("");
|
||||
const { items, isLoading, isLoggedIn } = useConversationList();
|
||||
const debouncedKeyword = useDebouncedValue(keyword, 180);
|
||||
const { items, isLoading, isRefreshing, isLoggedIn, error, reload } = useConversationList();
|
||||
|
||||
const sourceItems = isLoggedIn ? items : mockConversations;
|
||||
const conversationItems = useMemo(
|
||||
() => [SYSTEM_MESSAGE_ITEM, ...sourceItems.filter((item) => item.kind !== "system")],
|
||||
[sourceItems]
|
||||
);
|
||||
const filteredConversations = useConversationSearch(conversationItems, keyword);
|
||||
const filteredConversations = useConversationSearch(conversationItems, debouncedKeyword);
|
||||
const unreadTotal = useMemo(() => {
|
||||
const total = sourceItems.reduce((sum, item) => sum + (item.unreadCount ?? 0), 0);
|
||||
if (total <= 0) {
|
||||
@@ -48,7 +52,36 @@ export default function IndexRoute() {
|
||||
</View>
|
||||
|
||||
<View style={styles.listBlock}>
|
||||
<ConversationList data={filteredConversations} isLoading={isLoading && isLoggedIn} />
|
||||
{isLoggedIn && !!error && (
|
||||
<View style={styles.errorBanner}>
|
||||
<Text numberOfLines={1} style={styles.errorText}>
|
||||
{error}
|
||||
</Text>
|
||||
<Pressable onPress={reload} style={styles.errorAction}>
|
||||
<Text style={styles.errorActionText}>重试</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<ConversationList
|
||||
data={filteredConversations}
|
||||
isLoading={isLoading && isLoggedIn}
|
||||
isRefreshing={isRefreshing}
|
||||
onRefresh={reload}
|
||||
onPressItem={(item) => {
|
||||
if (item.kind === "system") {
|
||||
return;
|
||||
}
|
||||
|
||||
router.push({
|
||||
pathname: "/chat/[conversationId]",
|
||||
params: {
|
||||
conversationId: item.id,
|
||||
title: item.title,
|
||||
},
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
@@ -69,4 +102,29 @@ const styles = StyleSheet.create({
|
||||
borderTopWidth: StyleSheet.hairlineWidth,
|
||||
flex: 1,
|
||||
},
|
||||
errorBanner: {
|
||||
alignItems: "center",
|
||||
backgroundColor: "#F9FFFC",
|
||||
borderBottomColor: "#DCF6EB",
|
||||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
flexDirection: "row",
|
||||
gap: 8,
|
||||
justifyContent: "space-between",
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 10,
|
||||
},
|
||||
errorText: {
|
||||
color: "#3D4A45",
|
||||
flex: 1,
|
||||
fontSize: 13,
|
||||
},
|
||||
errorAction: {
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 2,
|
||||
},
|
||||
errorActionText: {
|
||||
color: "#00B97A",
|
||||
fontSize: 13,
|
||||
fontWeight: "600",
|
||||
},
|
||||
});
|
||||
48
app/chat/[conversationId].tsx
Normal file
48
app/chat/[conversationId].tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import { Stack, useLocalSearchParams } from "expo-router";
|
||||
import { StyleSheet, Text, View } from "react-native";
|
||||
|
||||
export default function ChatConversationRoute() {
|
||||
const params = useLocalSearchParams<{ conversationId?: string; title?: string }>();
|
||||
|
||||
return (
|
||||
<View style={styles.screen}>
|
||||
<Stack.Screen options={{ title: params.title || "会话" }} />
|
||||
<View style={styles.card}>
|
||||
<Text selectable style={styles.title}>
|
||||
聊天页骨架
|
||||
</Text>
|
||||
<Text selectable style={styles.subTitle}>
|
||||
会话 ID: {params.conversationId || "-"}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
screen: {
|
||||
alignItems: "center",
|
||||
backgroundColor: "#F7F7F8",
|
||||
flex: 1,
|
||||
justifyContent: "center",
|
||||
paddingHorizontal: 20,
|
||||
},
|
||||
card: {
|
||||
backgroundColor: "#FFFFFF",
|
||||
borderCurve: "continuous",
|
||||
borderRadius: 20,
|
||||
gap: 8,
|
||||
paddingHorizontal: 18,
|
||||
paddingVertical: 16,
|
||||
width: "100%",
|
||||
},
|
||||
title: {
|
||||
color: "#1F2328",
|
||||
fontSize: 18,
|
||||
fontWeight: "700",
|
||||
},
|
||||
subTitle: {
|
||||
color: "#6C737F",
|
||||
fontSize: 14,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user