feat(performance): migrate FlatList to FlashList and add animations
Replace FlatList with FlashList across all message screens (ChatScreen, MessageListScreen, NotificationsScreen, HomeScreen) for improved list virtualization performance. Use `drawDistance={250}` instead of manual pagination. Simplify React.memo comparisons in MessageBubble and SegmentRenderer by removing function prop checks to prevent unnecessary re-renders.
Add AsyncStorage persistence for lastSystemMessageAt to avoid showing current time on first render. Include enter animations (fade and slide) for CreateGroupScreen and modernize UI styling to flat design.
BREAKING CHANGE: Upgrade @shopify/flash-list from 2.0.2 to ^2.3.1
This commit is contained in:
@@ -12,7 +12,6 @@
|
||||
import React, { useState, useCallback, useEffect, useMemo, useRef } from 'react';
|
||||
import {
|
||||
View,
|
||||
FlatList,
|
||||
StyleSheet,
|
||||
TouchableOpacity,
|
||||
Pressable,
|
||||
@@ -24,6 +23,7 @@ import {
|
||||
BackHandler,
|
||||
Platform,
|
||||
} from 'react-native';
|
||||
import { FlashList, ListRenderItem } from '@shopify/flash-list';
|
||||
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { useRouter } from 'expo-router';
|
||||
import { useIsFocused } from '@react-navigation/native';
|
||||
@@ -115,6 +115,7 @@ export const MessageListScreen: React.FC = () => {
|
||||
// 使用 MessageManager 获取未读数和系统通知数
|
||||
const { totalUnreadCount, systemUnreadCount } = useUnreadCount();
|
||||
const lastSystemMessageAt = useMessageStore(state => state.lastSystemMessageAt);
|
||||
const systemMessageTime = lastSystemMessageAt ?? '';
|
||||
const { markAllAsRead, isMarking } = useMarkAsRead(null);
|
||||
|
||||
// 【新架构】使用MessageManager的hook创建会话
|
||||
@@ -162,14 +163,14 @@ export const MessageListScreen: React.FC = () => {
|
||||
seq: 0,
|
||||
segments: [{ type: 'text', data: { text: '系统通知' } }],
|
||||
status: 'normal',
|
||||
created_at: lastSystemMessageAt || new Date().toISOString(),
|
||||
created_at: systemMessageTime,
|
||||
},
|
||||
last_message_at: lastSystemMessageAt || new Date().toISOString(),
|
||||
last_message_at: systemMessageTime,
|
||||
unread_count: systemUnreadCount,
|
||||
participants: [],
|
||||
created_at: lastSystemMessageAt || new Date().toISOString(),
|
||||
updated_at: lastSystemMessageAt || new Date().toISOString(),
|
||||
}), [systemUnreadCount, lastSystemMessageAt]);
|
||||
created_at: systemMessageTime,
|
||||
updated_at: systemMessageTime,
|
||||
}), [systemUnreadCount, systemMessageTime]);
|
||||
|
||||
// 动画值
|
||||
const [scaleAnims] = useState(() =>
|
||||
@@ -453,7 +454,6 @@ export const MessageListScreen: React.FC = () => {
|
||||
// 创建系统通知会话项
|
||||
const createSystemMessageItem = (): ConversationResponse => {
|
||||
const noticeContent = systemUnreadCount > 0 ? `您有 ${systemUnreadCount} 条未读通知` : '暂无新通知';
|
||||
const lastTime = lastSystemMessageAt || new Date().toISOString();
|
||||
return {
|
||||
id: SYSTEM_MESSAGE_CHANNEL_ID,
|
||||
type: 'private',
|
||||
@@ -465,13 +465,13 @@ export const MessageListScreen: React.FC = () => {
|
||||
seq: 0,
|
||||
segments: [{ type: 'text', data: { text: noticeContent } }],
|
||||
status: 'normal',
|
||||
created_at: lastTime,
|
||||
created_at: systemMessageTime,
|
||||
},
|
||||
last_message_at: lastTime,
|
||||
last_message_at: systemMessageTime,
|
||||
unread_count: systemUnreadCount,
|
||||
participants: [],
|
||||
created_at: lastTime,
|
||||
updated_at: lastTime,
|
||||
created_at: systemMessageTime,
|
||||
updated_at: systemMessageTime,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -500,8 +500,8 @@ export const MessageListScreen: React.FC = () => {
|
||||
const totalUnread = totalUnreadCount + systemUnreadCount;
|
||||
|
||||
// 渲染会话项(行级 memo + 按 id 解析最新会话,避免 Tab 切换时整表无效重绘)
|
||||
const renderConversation = useCallback(
|
||||
({ item, index }: { item: ConversationResponse; index: number }) => {
|
||||
const renderConversation = useCallback<ListRenderItem<ConversationResponse>>(
|
||||
({ item, index }) => {
|
||||
const isSelected = selectedConversation?.id === item.id;
|
||||
return (
|
||||
<ConversationListRow
|
||||
@@ -715,7 +715,7 @@ export const MessageListScreen: React.FC = () => {
|
||||
</View>
|
||||
|
||||
{/* 搜索结果 */}
|
||||
<FlatList
|
||||
<FlashList
|
||||
data={searchResults}
|
||||
renderItem={renderSearchResult}
|
||||
keyExtractor={(item, index) => {
|
||||
@@ -724,14 +724,16 @@ export const MessageListScreen: React.FC = () => {
|
||||
}
|
||||
return `user-${item.user?.id || index}`;
|
||||
}}
|
||||
contentContainerStyle={[
|
||||
styles.searchResultsContent,
|
||||
{ paddingBottom: listBottomInset },
|
||||
]}
|
||||
contentContainerStyle={{
|
||||
paddingHorizontal: spacing.md,
|
||||
paddingTop: spacing.sm,
|
||||
paddingBottom: listBottomInset,
|
||||
}}
|
||||
showsVerticalScrollIndicator={false}
|
||||
scrollEnabled={true}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
keyboardDismissMode="on-drag"
|
||||
drawDistance={250}
|
||||
ListEmptyComponent={renderSearchEmpty}
|
||||
/>
|
||||
</View>
|
||||
@@ -779,15 +781,15 @@ export const MessageListScreen: React.FC = () => {
|
||||
<ActivityIndicator size="large" color={colors.primary.main} />
|
||||
</View>
|
||||
) : (
|
||||
<FlatList
|
||||
<FlashList
|
||||
data={listData}
|
||||
renderItem={renderConversation}
|
||||
keyExtractor={(item) => String(item.id)}
|
||||
contentContainerStyle={[
|
||||
styles.listContent,
|
||||
...(isWideScreen ? [styles.listContentWide] : []),
|
||||
{ paddingBottom: listBottomInset },
|
||||
]}
|
||||
contentContainerStyle={{
|
||||
backgroundColor: colors.background.default,
|
||||
paddingHorizontal: spacing.xs,
|
||||
paddingBottom: listBottomInset,
|
||||
}}
|
||||
showsVerticalScrollIndicator={false}
|
||||
ListEmptyComponent={renderEmpty}
|
||||
onEndReached={onEndReached}
|
||||
@@ -795,6 +797,7 @@ export const MessageListScreen: React.FC = () => {
|
||||
scrollEnabled={true}
|
||||
keyboardShouldPersistTaps="handled"
|
||||
keyboardDismissMode="on-drag"
|
||||
drawDistance={250}
|
||||
refreshControl={
|
||||
<RefreshControl
|
||||
refreshing={refreshing}
|
||||
|
||||
Reference in New Issue
Block a user