From b49cc0f3bd960ecb45c0d99c4162dcf7ef777177 Mon Sep 17 00:00:00 2001 From: lafay <2021211506@stu.hit.edu.cn> Date: Tue, 24 Mar 2026 15:39:28 +0800 Subject: [PATCH] refactor(TabsLayout, ImageGallery, ChatScreen, GroupInfoScreen, PrivateChatInfoScreen): enhance UI components and improve layout structure - Adjusted tab bar dimensions and margins in TabsLayout for better visual consistency. - Streamlined ImageGallery by removing loading states and optimizing image transition handling. - Updated ChatScreen to utilize SafeAreaView for improved layout on different devices. - Enhanced GroupInfoScreen and PrivateChatInfoScreen with a consistent page header layout, including back navigation. - Refactored ChatHeader to simplify structure and improve maintainability. --- app/(app)/(tabs)/_layout.tsx | 26 ++++--- src/components/common/ImageGallery.tsx | 59 ++-------------- src/screens/message/ChatScreen.tsx | 68 ++++++++++--------- src/screens/message/GroupInfoScreen.tsx | 39 ++++++++++- src/screens/message/PrivateChatInfoScreen.tsx | 32 ++++++++- .../components/ChatScreen/ChatHeader.tsx | 12 +--- 6 files changed, 126 insertions(+), 110 deletions(-) diff --git a/app/(app)/(tabs)/_layout.tsx b/app/(app)/(tabs)/_layout.tsx index afbc258..26024a9 100644 --- a/app/(app)/(tabs)/_layout.tsx +++ b/app/(app)/(tabs)/_layout.tsx @@ -7,9 +7,9 @@ import { colors, shadows } from '../../../src/theme'; import { BREAKPOINTS } from '../../../src/hooks/useResponsive'; import { useTotalUnreadCount } from '../../../src/stores'; -const TAB_BAR_HEIGHT = 64; -const TAB_BAR_MARGIN = 14; +const TAB_BAR_HEIGHT = 56; const TAB_BAR_FLOATING_MARGIN = 12; +const TAB_BAR_MARGIN = 24; export default function TabsLayout() { const { width } = useWindowDimensions(); @@ -27,19 +27,25 @@ export default function TabsLayout() { ? { display: 'none', height: 0, overflow: 'hidden' } : { position: 'absolute', - left: TAB_BAR_MARGIN, - right: TAB_BAR_MARGIN, + left: 0, + right: 0, + marginHorizontal: TAB_BAR_MARGIN, bottom: TAB_BAR_FLOATING_MARGIN + insets.bottom, height: TAB_BAR_HEIGHT, - borderRadius: 24, + borderRadius: 22, backgroundColor: colors.background.paper, ...shadows.lg, borderTopWidth: 0, elevation: 100, zIndex: 100, }, + tabBarItemStyle: { + borderRadius: 16, + marginHorizontal: 2, + paddingVertical: 1, + }, tabBarShowLabel: true, - tabBarLabelStyle: { fontSize: 12, fontWeight: '600', marginTop: -2 }, + tabBarLabelStyle: { fontSize: 11, fontWeight: '600', marginTop: -1 }, }} > ( ), @@ -63,7 +69,7 @@ export default function TabsLayout() { tabBarIcon: ({ color, focused }) => ( ), @@ -74,7 +80,7 @@ export default function TabsLayout() { options={{ title: '课表', tabBarIcon: ({ color, focused }) => ( - + ), }} /> @@ -85,7 +91,7 @@ export default function TabsLayout() { tabBarIcon: ({ color, focused }) => ( ), diff --git a/src/components/common/ImageGallery.tsx b/src/components/common/ImageGallery.tsx index 53f6451..cccd7ca 100644 --- a/src/components/common/ImageGallery.tsx +++ b/src/components/common/ImageGallery.tsx @@ -13,7 +13,6 @@ import { TouchableOpacity, Text, StatusBar, - ActivityIndicator, Alert, } from 'react-native'; import { Image as ExpoImage } from 'expo-image'; @@ -25,10 +24,8 @@ import { import Animated, { useSharedValue, useAnimatedStyle, - useDerivedValue, runOnJS, withTiming, - withSpring, } from 'react-native-reanimated'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { MaterialCommunityIcons } from '@expo/vector-icons'; @@ -91,7 +88,6 @@ export const ImageGallery: React.FC = ({ const [currentIndex, setCurrentIndex] = useState(initialIndex); currentIndexRef.current = currentIndex; const [showControls, setShowControls] = useState(true); - const [loading, setLoading] = useState(true); const [error, setError] = useState(false); const [saving, setSaving] = useState(false); const [saveToast, setSaveToast] = useState<'success' | 'error' | null>(null); @@ -132,7 +128,6 @@ export const ImageGallery: React.FC = ({ if (visible) { setCurrentIndex(initialIndex); setShowControls(true); - setLoading(true); setError(false); resetZoom(); StatusBar.setHidden(true, 'fade'); @@ -166,7 +161,6 @@ export const ImageGallery: React.FC = ({ if (clampedIndex === currentIndexRef.current) { return; } - setLoading(true); setError(false); setCurrentIndex(clampedIndex); onIndexChange?.(clampedIndex); @@ -266,24 +260,16 @@ export const ImageGallery: React.FC = ({ // 滑动切换图片相关状态 const swipeTranslateX = useSharedValue(0); - // 滑动切图完成后,让新图从目标方向进入,避免旧图先弹回导致前后图闪烁 useEffect(() => { - const direction = pendingSwipeDirectionRef.current; - if (direction == null) { - return; - } pendingSwipeDirectionRef.current = null; - swipeTranslateX.value = -direction * SCREEN_WIDTH; - swipeTranslateX.value = withTiming(0, { duration: 180 }); + swipeTranslateX.value = 0; }, [currentIndex, swipeTranslateX]); const switchWithDirection = useCallback( (targetIndex: number, direction: -1 | 1) => { - swipeTranslateX.value = withTiming(direction * SCREEN_WIDTH, { duration: 200 }, () => { - runOnJS(updateIndexFromSwipe)(targetIndex, direction); - }); + updateIndexFromSwipe(targetIndex, direction); }, - [swipeTranslateX, updateIndexFromSwipe] + [updateIndexFromSwipe] ); const goToPrev = useCallback(() => { @@ -310,16 +296,6 @@ export const ImageGallery: React.FC = ({ // 放大状态下:拖动图片 translateX.value = savedTranslateX.value + e.translationX; translateY.value = savedTranslateY.value + e.translationY; - } else if (validImages.length > 1) { - // 未放大且有多张图片:切换图片的跟随效果 - const isFirst = currentIndex === 0 && e.translationX > 0; - const isLast = currentIndex === validImages.length - 1 && e.translationX < 0; - if (isFirst || isLast) { - // 边界阻力效果 - swipeTranslateX.value = e.translationX * 0.3; - } else { - swipeTranslateX.value = e.translationX; - } } }) .onEnd((e) => { @@ -337,17 +313,10 @@ export const ImageGallery: React.FC = ({ if (shouldGoNext && currentIndex < validImages.length - 1) { // 向左滑动,显示下一张 - swipeTranslateX.value = withTiming(-SCREEN_WIDTH, { duration: 200 }, () => { - runOnJS(updateIndexFromSwipe)(currentIndex + 1, -1); - }); + runOnJS(updateIndexFromSwipe)(currentIndex + 1, -1); } else if (shouldGoPrev && currentIndex > 0) { // 向右滑动,显示上一张 - swipeTranslateX.value = withTiming(SCREEN_WIDTH, { duration: 200 }, () => { - runOnJS(updateIndexFromSwipe)(currentIndex - 1, 1); - }); - } else { - // 回弹到原位 - swipeTranslateX.value = withTiming(0, { duration: 200 }); + runOnJS(updateIndexFromSwipe)(currentIndex - 1, 1); } } }); @@ -411,7 +380,7 @@ export const ImageGallery: React.FC = ({ disabled={saving} > {saving ? ( - + ) : ( )} @@ -423,12 +392,6 @@ export const ImageGallery: React.FC = ({ {/* 图片显示区域 */} - {loading && ( - - - - )} - {error && ( @@ -447,13 +410,10 @@ export const ImageGallery: React.FC = ({ recyclingKey={currentImage.url} transition={null} allowDownscaling - onLoadStart={() => setLoading(true)} onLoad={() => { - setLoading(false); setError(false); }} onError={() => { - setLoading(false); setError(true); }} /> @@ -584,13 +544,6 @@ const styles = StyleSheet.create({ width: SCREEN_WIDTH, height: SCREEN_HEIGHT * 0.8, }, - loadingContainer: { - ...StyleSheet.absoluteFillObject, - justifyContent: 'center', - alignItems: 'center', - zIndex: 5, - backgroundColor: '#000', - }, errorContainer: { ...StyleSheet.absoluteFillObject, justifyContent: 'center', diff --git a/src/screens/message/ChatScreen.tsx b/src/screens/message/ChatScreen.tsx index 157a3f0..00a1c85 100644 --- a/src/screens/message/ChatScreen.tsx +++ b/src/screens/message/ChatScreen.tsx @@ -29,7 +29,7 @@ import { } from 'react-native'; import { useNavigation, useRouter } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; -import { useSafeAreaInsets } from 'react-native-safe-area-context'; +import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'; import { Text, ImageGallery, ImageGridItem } from '../../components/common'; import { colors } from '../../theme'; import * as hrefs from '../../navigation/hrefs'; @@ -364,12 +364,13 @@ export const ChatScreen: React.FC = () => { }, [handleMessageListContentSizeChange]); return ( - - + + + {/* 顶部栏 */} { /> {/* 群信息侧边栏面板 - 仅大屏幕显示 */} - { - // TODO: 实现置顶功能 - console.log('Toggle pin:', pinned); - }} - onLeaveGroup={() => { - // TODO: 实现退出群聊功能 - console.log('Leave group'); - }} - onInviteMembers={() => { - // TODO: 实现邀请新成员功能 - console.log('Invite members'); - }} - onViewAllMembers={() => { - // TODO: 实现查看全部成员功能 - console.log('View all members'); - }} - /> - + { + // TODO: 实现置顶功能 + console.log('Toggle pin:', pinned); + }} + onLeaveGroup={() => { + // TODO: 实现退出群聊功能 + console.log('Leave group'); + }} + onInviteMembers={() => { + // TODO: 实现邀请新成员功能 + console.log('Invite members'); + }} + onViewAllMembers={() => { + // TODO: 实现查看全部成员功能 + console.log('View all members'); + }} + /> + + ); }; diff --git a/src/screens/message/GroupInfoScreen.tsx b/src/screens/message/GroupInfoScreen.tsx index 7c7846d..99af5a7 100644 --- a/src/screens/message/GroupInfoScreen.tsx +++ b/src/screens/message/GroupInfoScreen.tsx @@ -44,6 +44,7 @@ import * as hrefs from '../../navigation/hrefs'; import { messageManager } from '../../stores/messageManager'; import { groupManager } from '../../stores/groupManager'; import MutualFollowSelectorModal from './components/MutualFollowSelectorModal'; +import { AppBackButton } from '../../components/common'; const { width: SCREEN_WIDTH } = Dimensions.get('window'); @@ -537,7 +538,12 @@ const GroupInfoScreen: React.FC = () => { if (loading) { return ( - + + + router.back()} /> + 群聊信息 + + ); @@ -545,7 +551,12 @@ const GroupInfoScreen: React.FC = () => { if (!group) { return ( - + + + router.back()} /> + 群聊信息 + + 群组信息不存在 @@ -556,7 +567,12 @@ const GroupInfoScreen: React.FC = () => { } return ( - + + + router.back()} /> + 群聊信息 + + { const router = useRouter(); @@ -309,7 +310,12 @@ const PrivateChatInfoScreen: React.FC = () => { if (loading) { return ( - + + + router.back()} /> + 私聊信息 + + ); @@ -322,7 +328,12 @@ const PrivateChatInfoScreen: React.FC = () => { }; return ( - + + + router.back()} /> + 私聊信息 + + = ({ }; return ( - + - {/* 大屏幕(>= 768px)时隐藏返回按钮 */} - {!isWideScreen ? ( - - ) : ( - - )} + = ({ )} - + ); };