183 lines
5.8 KiB
TypeScript
183 lines
5.8 KiB
TypeScript
import { Tabs } from 'expo-router'
|
||
import React, { useEffect } from 'react'
|
||
import { StyleSheet, View, Text, Platform } from 'react-native'
|
||
import { LinearGradient } from 'expo-linear-gradient'
|
||
import { useTranslation } from 'react-i18next'
|
||
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||
|
||
import { HomeIcon, MessageIcon, MyIcon, VideoIcon } from '@/components/icon'
|
||
import { useMessageUnreadCount } from '@/hooks/use-message-unread-count'
|
||
import { useAnnouncementUnreadCount } from '@/hooks/use-announcement-unread-count'
|
||
|
||
const TAB_BAR_HEIGHT = 80
|
||
|
||
export default function TabLayout() {
|
||
const { t } = useTranslation()
|
||
const insets = useSafeAreaInsets()
|
||
// Android 不需要额外的底部安全区域,iOS 和 Web 需要
|
||
const bottomInset = Platform.OS === 'android' ? 0 : insets.bottom
|
||
|
||
// 获取未读消息数和公告数
|
||
const { data: messageData, refetch: refetchMessages } = useMessageUnreadCount()
|
||
const { data: announcementData, refetch: refetchAnnouncements } = useAnnouncementUnreadCount()
|
||
|
||
// 计算总未读数
|
||
const totalUnreadCount = (messageData?.total || 0) + (announcementData?.count || 0)
|
||
|
||
// 组件挂载时获取未读数
|
||
useEffect(() => {
|
||
refetchMessages()
|
||
refetchAnnouncements()
|
||
}, [])
|
||
|
||
return (
|
||
<Tabs
|
||
screenOptions={{
|
||
tabBarActiveTintColor: '#FFFFFF',
|
||
tabBarInactiveTintColor: '#FFFFFF',
|
||
headerShown: false,
|
||
tabBarHideOnKeyboard: true,
|
||
tabBarShowLabel: true,
|
||
tabBarStyle: {
|
||
position: 'absolute',
|
||
backgroundColor: '#1C1E22',
|
||
height: TAB_BAR_HEIGHT + bottomInset,
|
||
paddingTop: 4,
|
||
paddingBottom: bottomInset,
|
||
paddingLeft: 4,
|
||
paddingRight: 4,
|
||
borderTopWidth: 0,
|
||
elevation: 0,
|
||
shadowOpacity: 0,
|
||
borderTopColor: 'transparent',
|
||
},
|
||
tabBarLabelStyle: {
|
||
color: '#FFFFFF',
|
||
fontSize: 10,
|
||
fontWeight: '400',
|
||
},
|
||
tabBarBackground: () => (
|
||
<View style={{ flex: 1, backgroundColor: '#1C1E22' }} />
|
||
),
|
||
}}
|
||
>
|
||
<Tabs.Screen
|
||
name="index"
|
||
options={{
|
||
title: t('tabs.home'),
|
||
tabBarLabel: ({ focused }: { focused: boolean }) => (focused ? null : <Text style={styles.tabLabel}>{t('tabs.home')}</Text>),
|
||
tabBarIcon: ({ focused }: { focused: boolean }) => {
|
||
if (focused) {
|
||
return (
|
||
<LinearGradient colors={['#9966FF', '#FF6699', '#FF9966']} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={styles.iconContainer}>
|
||
<HomeIcon />
|
||
</LinearGradient>
|
||
)
|
||
}
|
||
return (
|
||
<View style={{ opacity: 0.6 }}>
|
||
<HomeIcon />
|
||
</View>
|
||
)
|
||
},
|
||
}}
|
||
/>
|
||
<Tabs.Screen
|
||
name="video"
|
||
options={{
|
||
title: t('tabs.video'),
|
||
tabBarLabel: ({ focused }: { focused: boolean }) => (focused ? null : <Text style={styles.tabLabel}>{t('tabs.video')}</Text>),
|
||
tabBarIcon: ({ focused }: { focused: boolean }) => {
|
||
if (focused) {
|
||
return (
|
||
<LinearGradient colors={['#9966FF', '#FF6699', '#FF9966']} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={styles.iconContainer}>
|
||
<VideoIcon />
|
||
</LinearGradient>
|
||
)
|
||
}
|
||
return (
|
||
<View style={{ opacity: 0.6 }}>
|
||
<VideoIcon />
|
||
</View>
|
||
)
|
||
},
|
||
}}
|
||
/>
|
||
<Tabs.Screen
|
||
name="message"
|
||
options={{
|
||
title: t('tabs.message'),
|
||
tabBarLabel: ({ focused }: { focused: boolean }) => (focused ? null : <Text style={styles.tabLabel}>{t('tabs.message')}</Text>),
|
||
tabBarIcon: ({ focused }: { focused: boolean }) => {
|
||
if (focused) {
|
||
return (
|
||
<View>
|
||
<LinearGradient colors={['#9966FF', '#FF6699', '#FF9966']} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={styles.iconContainer}>
|
||
<MessageIcon />
|
||
</LinearGradient>
|
||
{totalUnreadCount > 0 && (
|
||
<View testID="message-unread-badge" style={styles.unreadBadge} />
|
||
)}
|
||
</View>
|
||
)
|
||
}
|
||
return (
|
||
<View style={{ opacity: focused ? 1 : 0.6 }}>
|
||
<MessageIcon />
|
||
{totalUnreadCount > 0 && (
|
||
<View testID="message-unread-badge" style={styles.unreadBadge} />
|
||
)}
|
||
</View>
|
||
)
|
||
},
|
||
}}
|
||
/>
|
||
<Tabs.Screen
|
||
name="my"
|
||
options={{
|
||
title: t('tabs.my'),
|
||
tabBarLabel: ({ focused }: { focused: boolean }) => (focused ? null : <Text style={styles.tabLabel}>{t('tabs.my')}</Text>),
|
||
tabBarIcon: ({ focused }: { focused: boolean }) => {
|
||
if (focused) {
|
||
return (
|
||
<LinearGradient colors={['#9966FF', '#FF6699', '#FF9966']} start={{ x: 0, y: 0 }} end={{ x: 1, y: 0 }} style={styles.iconContainer}>
|
||
<MyIcon />
|
||
</LinearGradient>
|
||
)
|
||
}
|
||
return (
|
||
<View style={{ opacity: focused ? 1 : 0.6 }}>
|
||
<MyIcon />
|
||
</View>
|
||
)
|
||
},
|
||
}}
|
||
/>
|
||
</Tabs>
|
||
)
|
||
}
|
||
|
||
const styles = StyleSheet.create({
|
||
iconContainer: {
|
||
width: 87,
|
||
height: 41,
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
borderRadius: 100,
|
||
},
|
||
tabLabel: {
|
||
fontSize: 10,
|
||
color: '#FFFFFF',
|
||
textAlign: 'center',
|
||
},
|
||
unreadBadge: {
|
||
position: 'absolute',
|
||
top: 0,
|
||
right: 0,
|
||
width: 8,
|
||
height: 8,
|
||
borderRadius: 4,
|
||
backgroundColor: '#00FF66',
|
||
},
|
||
})
|