feat: 添加聊天会话页面,支持会话ID和标题显示,优化会话列表和搜索功能

This commit is contained in:
2026-03-09 08:25:51 +07:00
parent 212b3fa8de
commit db4ee293d2
7 changed files with 225 additions and 31 deletions

View File

@@ -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",
},
});

View 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,
},
});