162 lines
3.8 KiB
TypeScript
162 lines
3.8 KiB
TypeScript
import React from 'react'
|
|
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'
|
|
import { LinearGradient } from 'expo-linear-gradient'
|
|
|
|
// Types
|
|
export type MessageType = 'ACTIVITY' | 'SYSTEM' | 'BILLING' | undefined
|
|
|
|
export interface TabItem {
|
|
key: string
|
|
label: string
|
|
type: MessageType
|
|
}
|
|
|
|
export interface MessageTabBarProps {
|
|
activeTab: MessageType
|
|
onTabChange: (type: MessageType) => void
|
|
unreadCounts?: {
|
|
all?: number
|
|
activity?: number
|
|
system?: number
|
|
billing?: number
|
|
}
|
|
}
|
|
|
|
// Constants
|
|
export const TAB_ITEMS: TabItem[] = [
|
|
{ key: 'all', label: '全部', type: undefined },
|
|
{ key: 'activity', label: '互动', type: 'ACTIVITY' },
|
|
{ key: 'system', label: '系统', type: 'SYSTEM' },
|
|
{ key: 'billing', label: '账单', type: 'BILLING' },
|
|
]
|
|
|
|
// Helper function to format badge count
|
|
const formatBadgeCount = (count: number): string => {
|
|
if (count > 99) {
|
|
return '99+'
|
|
}
|
|
return String(count)
|
|
}
|
|
|
|
// Helper function to get unread count for a tab
|
|
const getUnreadCount = (
|
|
key: string,
|
|
unreadCounts?: MessageTabBarProps['unreadCounts']
|
|
): number | undefined => {
|
|
if (!unreadCounts) return undefined
|
|
switch (key) {
|
|
case 'all':
|
|
return unreadCounts.all
|
|
case 'activity':
|
|
return unreadCounts.activity
|
|
case 'system':
|
|
return unreadCounts.system
|
|
case 'billing':
|
|
return unreadCounts.billing
|
|
default:
|
|
return undefined
|
|
}
|
|
}
|
|
|
|
export const MessageTabBar: React.FC<MessageTabBarProps> = ({
|
|
activeTab,
|
|
onTabChange,
|
|
unreadCounts,
|
|
}) => {
|
|
return (
|
|
<View style={styles.container}>
|
|
{TAB_ITEMS.map((tab) => {
|
|
const isActive = activeTab === tab.type
|
|
const unreadCount = getUnreadCount(tab.key, unreadCounts)
|
|
const showBadge = unreadCount !== undefined && unreadCount > 0
|
|
|
|
return (
|
|
<TouchableOpacity
|
|
key={tab.key}
|
|
testID={`tab-${tab.key}`}
|
|
style={styles.tabItem}
|
|
onPress={() => onTabChange(tab.type)}
|
|
activeOpacity={0.7}
|
|
>
|
|
<View style={styles.tabContent}>
|
|
<Text
|
|
style={[
|
|
styles.tabLabel,
|
|
isActive ? styles.tabLabelActive : styles.tabLabelInactive,
|
|
]}
|
|
>
|
|
{tab.label}
|
|
</Text>
|
|
{showBadge && (
|
|
<View testID={`tab-${tab.key}-badge`} style={styles.badge}>
|
|
<Text style={styles.badgeText}>
|
|
{formatBadgeCount(unreadCount)}
|
|
</Text>
|
|
</View>
|
|
)}
|
|
</View>
|
|
{isActive && (
|
|
<LinearGradient
|
|
testID={`tab-${tab.key}-active-indicator`}
|
|
colors={['#FF9966', '#FF6699', '#9966FF']}
|
|
start={{ x: 0, y: 0 }}
|
|
end={{ x: 1, y: 0 }}
|
|
style={styles.activeIndicator}
|
|
/>
|
|
)}
|
|
</TouchableOpacity>
|
|
)
|
|
})}
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flexDirection: 'row',
|
|
backgroundColor: '#090A0B',
|
|
paddingHorizontal: 16,
|
|
paddingTop: 12,
|
|
},
|
|
tabItem: {
|
|
marginRight: 24,
|
|
paddingBottom: 8,
|
|
alignItems: 'center',
|
|
},
|
|
tabContent: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
},
|
|
tabLabel: {
|
|
fontSize: 16,
|
|
fontWeight: '500',
|
|
},
|
|
tabLabelActive: {
|
|
color: '#FFFFFF',
|
|
},
|
|
tabLabelInactive: {
|
|
color: '#8A8A8A',
|
|
},
|
|
activeIndicator: {
|
|
height: 2,
|
|
width: '100%',
|
|
marginTop: 6,
|
|
borderRadius: 1,
|
|
},
|
|
badge: {
|
|
backgroundColor: '#FF3B30',
|
|
borderRadius: 10,
|
|
minWidth: 18,
|
|
height: 18,
|
|
paddingHorizontal: 5,
|
|
marginLeft: 4,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
badgeText: {
|
|
color: '#FFFFFF',
|
|
fontSize: 11,
|
|
fontWeight: '600',
|
|
},
|
|
})
|