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

@@ -19,7 +19,7 @@ export function ConversationHeader({
onPress={onPressCreate}
style={({ pressed }) => [styles.addButton, pressed && styles.addButtonPressed]}
>
<MaterialIcons color="#666666" name="add-circle-outline" size={36} />
<MaterialIcons color="#666666" name="add-circle-outline" size={30} />
</Pressable>
</View>
);

View File

@@ -1,5 +1,5 @@
import { FlashList } from "@shopify/flash-list";
import { StyleSheet, Text, View } from "react-native";
import { RefreshControl, StyleSheet, Text, View } from "react-native";
import { ConversationListItem } from "@/features/im/components/conversation-list-item";
import type { ConversationItem } from "@/features/im/types/conversation";
@@ -7,40 +7,45 @@ import type { ConversationItem } from "@/features/im/types/conversation";
type ConversationListProps = {
data: ConversationItem[];
isLoading?: boolean;
isRefreshing?: boolean;
onRefresh?: () => void;
onPressItem?: (item: ConversationItem) => void;
};
export function ConversationList({
data,
isLoading = false,
isRefreshing = false,
onRefresh,
onPressItem,
}: ConversationListProps) {
if (isLoading) {
return (
<View style={styles.emptyContainer}>
<Text style={styles.emptyTitle}>...</Text>
</View>
);
}
if (data.length === 0) {
return (
<View style={styles.emptyContainer}>
<Text style={styles.emptyTitle}></Text>
<Text style={styles.emptyHint}></Text>
</View>
);
}
return (
<FlashList
alwaysBounceVertical
bounces
contentInsetAdjustmentBehavior="automatic"
contentContainerStyle={styles.contentContainer}
data={data}
keyExtractor={(item) => item.id}
ListEmptyComponent={
<View style={styles.emptyContainer}>
<Text style={styles.emptyTitle}>{isLoading ? "会话加载中..." : "暂无会话"}</Text>
{!isLoading && <Text style={styles.emptyHint}></Text>}
</View>
}
onRefresh={onRefresh}
refreshControl={
<RefreshControl
colors={["#00D48C"]}
onRefresh={onRefresh}
refreshing={isRefreshing}
tintColor="#00D48C"
/>
}
renderItem={({ item }) => (
<ConversationListItem item={item} onPress={onPressItem} />
)}
refreshing={isRefreshing}
showsVerticalScrollIndicator={false}
/>
);
@@ -48,6 +53,7 @@ export function ConversationList({
const styles = StyleSheet.create({
contentContainer: {
flexGrow: 1,
paddingBottom: 28,
paddingTop: 16,
},

View File

@@ -1,5 +1,6 @@
import MaterialIcons from "@expo/vector-icons/MaterialIcons";
import { StyleSheet, TextInput, View } from "react-native";
import { useRef, useState } from "react";
import { Animated, Easing, StyleSheet, TextInput } from "react-native";
type ConversationSearchBarProps = {
value: string;
@@ -10,17 +11,52 @@ export function ConversationSearchBar({
value,
onChangeText,
}: ConversationSearchBarProps) {
const [isFocused, setIsFocused] = useState(false);
const focusAnim = useRef(new Animated.Value(0)).current;
const animateFocus = (toValue: 0 | 1) => {
Animated.timing(focusAnim, {
toValue,
duration: 140,
easing: Easing.out(Easing.quad),
useNativeDriver: true,
}).start();
};
return (
<View style={styles.wrapper}>
<Animated.View
style={[
styles.wrapper,
isFocused && styles.wrapperFocused,
{
transform: [
{
scale: focusAnim.interpolate({
inputRange: [0, 1],
outputRange: [1, 1.01],
}),
},
],
},
]}
>
<MaterialIcons color="#A2A2A7" name="search" size={28} />
<TextInput
onBlur={() => {
setIsFocused(false);
animateFocus(0);
}}
onChangeText={onChangeText}
onFocus={() => {
setIsFocused(true);
animateFocus(1);
}}
placeholder="搜索联系人备注/昵称/ID"
placeholderTextColor="#B8B8BC"
style={styles.input}
value={value}
/>
</View>
</Animated.View>
);
}
@@ -28,7 +64,9 @@ const styles = StyleSheet.create({
wrapper: {
alignItems: "center",
backgroundColor: "#F0F0F2",
borderColor: "transparent",
borderCurve: "continuous",
borderWidth: 1,
borderRadius: 20,
flexDirection: "row",
gap: 8,
@@ -36,6 +74,10 @@ const styles = StyleSheet.create({
marginHorizontal: 20,
paddingHorizontal: 16,
},
wrapperFocused: {
backgroundColor: "#F4FAF8",
borderColor: "#BEEEDB",
},
input: {
color: "#2A2A2A",
flex: 1,