230 lines
8.7 KiB
TypeScript
230 lines
8.7 KiB
TypeScript
import { useState, useEffect } from 'react'
|
|
import {
|
|
View,
|
|
Text,
|
|
StyleSheet,
|
|
Pressable,
|
|
StatusBar as RNStatusBar,
|
|
ActivityIndicator,
|
|
} from 'react-native'
|
|
import { StatusBar } from 'expo-status-bar'
|
|
import { LinearGradient } from 'expo-linear-gradient'
|
|
import { SafeAreaView } from 'react-native-safe-area-context'
|
|
import { useRouter, useLocalSearchParams } from 'expo-router'
|
|
import { useTranslation } from 'react-i18next'
|
|
|
|
import { TopArrowIcon } from '@/components/icon'
|
|
import GradientText from '@/components/GradientText'
|
|
import { useCategories } from '@/hooks/use-categories'
|
|
|
|
|
|
export default function ChannelsScreen() {
|
|
const { t } = useTranslation()
|
|
const router = useRouter()
|
|
const params = useLocalSearchParams()
|
|
const [isExpanded, setIsExpanded] = useState(true)
|
|
|
|
// 使用 useCategories hook 获取分类数据
|
|
const { load, data: categoriesData, loading: categoriesLoading, error: categoriesError } = useCategories()
|
|
|
|
// 从路由参数获取当前选中的分类 ID
|
|
const currentCategoryId = params.categoryId as string | undefined
|
|
|
|
useEffect(() => {
|
|
// 加载分类数据
|
|
load()
|
|
}, [])
|
|
|
|
// 使用接口返回的分类数据
|
|
const categories = categoriesData?.categories || []
|
|
|
|
// 如果没有分类数据,显示加载状态或空状态
|
|
const showLoading = categoriesLoading
|
|
const showEmptyState = !categoriesLoading && categories.length === 0
|
|
|
|
return (
|
|
<View style={styles.screen}>
|
|
<StatusBar style="light" />
|
|
<RNStatusBar barStyle="light-content" />
|
|
|
|
{/* 顶部容器 - 包含 header 和频道选择区域,带底部圆角 */}
|
|
<View style={styles.topContainer}>
|
|
<SafeAreaView style={styles.safeArea} edges={['top']}>
|
|
{/* 标题栏 */}
|
|
<View style={styles.header}>
|
|
<Text style={styles.headerTitle}>{t('channels.title')}</Text>
|
|
<Pressable
|
|
onPress={() => {
|
|
router.back()
|
|
}}
|
|
>
|
|
<TopArrowIcon />
|
|
</Pressable>
|
|
</View>
|
|
|
|
{/* 频道选择区域 */}
|
|
{isExpanded && (
|
|
<View style={styles.channelsSection}>
|
|
{showLoading ? (
|
|
<View style={styles.loadingContainer}>
|
|
<ActivityIndicator size="small" color="#FF6699" />
|
|
</View>
|
|
) : showEmptyState ? (
|
|
<View style={styles.emptyStateContainer}>
|
|
<Text style={styles.emptyStateText}>{t('channels.noCategories') || '暂无分类'}</Text>
|
|
<Pressable style={styles.retryButton} onPress={() => load()}>
|
|
<Text style={styles.retryButtonText}>{t('channels.retry') || '重新加载'}</Text>
|
|
</Pressable>
|
|
</View>
|
|
) : (
|
|
<View style={styles.channelsGrid}>
|
|
{categories.map((category) => {
|
|
const isSelected = currentCategoryId === category.id
|
|
return (
|
|
<Pressable
|
|
key={category.id}
|
|
onPress={() => {
|
|
// 选择分类后返回首页
|
|
router.push({
|
|
pathname: '/' as any,
|
|
params: { categoryId: category.id },
|
|
})
|
|
}}
|
|
style={[styles.channelButtonWrapper]}
|
|
>
|
|
{isSelected ? (
|
|
<LinearGradient
|
|
colors={['#FF9966', '#FF6699', '#9966FF']}
|
|
start={{ x: 0, y: 0 }}
|
|
end={{ x: 1, y: 0 }}
|
|
style={styles.channelButtonGradient}
|
|
>
|
|
<View style={styles.channelButtonDefault}>
|
|
<GradientText
|
|
colors={['#FF9966', '#FF6699', '#9966FF']}
|
|
start={{ x: 0, y: 0 }}
|
|
end={{ x: 1, y: 0 }}
|
|
style={styles.channelButtonText}
|
|
>
|
|
{category.name}
|
|
</GradientText>
|
|
</View>
|
|
</LinearGradient>
|
|
) : (
|
|
<View style={styles.channelButtonWrapperUnselected}>
|
|
<View style={styles.channelButtonDefault}>
|
|
<Text style={styles.channelButtonText}>
|
|
{category.name}
|
|
</Text>
|
|
</View>
|
|
</View>
|
|
)}
|
|
</Pressable>
|
|
)
|
|
})}
|
|
</View>
|
|
)}
|
|
</View>
|
|
)}
|
|
</SafeAreaView>
|
|
</View>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
screen: {
|
|
flex: 1,
|
|
backgroundColor: '#0A0A0A',
|
|
},
|
|
topContainer: {
|
|
backgroundColor: '#16181B',
|
|
borderBottomLeftRadius: 16,
|
|
borderBottomRightRadius: 16,
|
|
overflow: 'hidden',
|
|
},
|
|
safeArea: {
|
|
backgroundColor: '#16181B',
|
|
},
|
|
header: {
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
paddingHorizontal: 16,
|
|
paddingTop: 27,
|
|
paddingBottom: 12,
|
|
},
|
|
headerTitle: {
|
|
color: '#F5F5F5',
|
|
fontSize: 12,
|
|
fontWeight: '500',
|
|
},
|
|
searchText: {
|
|
color: '#ABABAB',
|
|
fontSize: 14,
|
|
fontWeight: '400',
|
|
},
|
|
channelsSection: {
|
|
paddingHorizontal: 12,
|
|
paddingBottom: 12,
|
|
},
|
|
channelsGrid: {
|
|
flexDirection: 'row',
|
|
flexWrap: 'wrap',
|
|
gap: 8,
|
|
},
|
|
channelButtonWrapper: {
|
|
// 按钮宽度根据内容自适应
|
|
},
|
|
channelButtonGradient: {
|
|
borderRadius: 100,
|
|
padding: 1,
|
|
},
|
|
channelButtonWrapperUnselected: {
|
|
borderRadius: 100,
|
|
padding: 1,
|
|
backgroundColor: 'transparent',
|
|
},
|
|
channelButtonDefault: {
|
|
backgroundColor: '#262A31',
|
|
borderRadius: 100,
|
|
paddingVertical: 8,
|
|
paddingHorizontal: 17,
|
|
borderWidth: 1,
|
|
borderColor: '#262A31',
|
|
},
|
|
channelButtonText: {
|
|
color: '#F5F5F5',
|
|
fontSize: 12,
|
|
fontWeight: '500',
|
|
textAlign: 'center',
|
|
},
|
|
loadingContainer: {
|
|
paddingVertical: 24,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
},
|
|
emptyStateContainer: {
|
|
paddingVertical: 40,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
gap: 16,
|
|
},
|
|
emptyStateText: {
|
|
color: '#ABABAB',
|
|
fontSize: 14,
|
|
},
|
|
retryButton: {
|
|
backgroundColor: '#FF6699',
|
|
paddingHorizontal: 24,
|
|
paddingVertical: 12,
|
|
borderRadius: 8,
|
|
},
|
|
retryButtonText: {
|
|
color: '#FFFFFF',
|
|
fontSize: 14,
|
|
fontWeight: '600',
|
|
},
|
|
})
|
|
|