diff --git a/app/index.tsx b/app/index.tsx index 68b2e86..305f5dc 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useCallback, useRef } from "react"; +import React, { useEffect, useCallback, useRef, useState } from "react"; import { View, StyleSheet, ActivityIndicator, FlatList, Pressable, Dimensions } from "react-native"; import { ThemedView } from "@/components/ThemedView"; import { ThemedText } from "@/components/ThemedText"; @@ -19,6 +19,7 @@ export default function HomeScreen() { const router = useRouter(); const colorScheme = "dark"; const flatListRef = useRef(null); + const [selectedTag, setSelectedTag] = useState(null); const { categories, @@ -42,14 +43,38 @@ export default function HomeScreen() { ); useEffect(() => { - fetchInitialData(); - flatListRef.current?.scrollToOffset({ animated: false, offset: 0 }); + if (selectedCategory && !selectedCategory.tags) { + fetchInitialData(); + flatListRef.current?.scrollToOffset({ animated: false, offset: 0 }); + } else if (selectedCategory?.tags && !selectedCategory.tag) { + // Category with tags selected, but no specific tag yet. Select the first one. + const defaultTag = selectedCategory.tags[0]; + setSelectedTag(defaultTag); + selectCategory({ ...selectedCategory, tag: defaultTag }); + } }, [selectedCategory, fetchInitialData]); + useEffect(() => { + if (selectedCategory && selectedCategory.tag) { + fetchInitialData(); + flatListRef.current?.scrollToOffset({ animated: false, offset: 0 }); + } + }, [selectedCategory?.tag]); + const handleCategorySelect = (category: Category) => { + setSelectedTag(null); selectCategory(category); }; + const handleTagSelect = (tag: string) => { + setSelectedTag(tag); + if (selectedCategory) { + // Create a new category object with the selected tag + const categoryWithTag = { ...selectedCategory, tag: tag }; + selectCategory(categoryWithTag); + } + }; + const renderCategory = ({ item }: { item: Category }) => { const isSelected = selectedCategory?.title === item.title; return ( @@ -119,6 +144,33 @@ export default function HomeScreen() { /> + {/* Sub-category Tags */} + {selectedCategory && selectedCategory.tags && ( + + { + const isSelected = selectedTag === item; + return ( + handleTagSelect(item)} + isSelected={isSelected} + style={styles.categoryButton} + textStyle={styles.categoryText} + variant="ghost" + /> + ); + }} + keyExtractor={(item) => item} + horizontal + showsHorizontalScrollIndicator={false} + contentContainerStyle={styles.categoryListContent} + /> + + )} + {/* 内容网格 */} {loading ? ( @@ -143,7 +195,7 @@ export default function HomeScreen() { ListFooterComponent={renderFooter} ListEmptyComponent={ - 该分类下暂无内容 + {selectedCategory?.tags ? "请选择一个子分类" : "该分类下暂无内容"} } /> diff --git a/app/play.tsx b/app/play.tsx index 655068b..437a35a 100644 --- a/app/play.tsx +++ b/app/play.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useRef } from "react"; -import { View, StyleSheet, TouchableOpacity, ActivityIndicator, BackHandler } from "react-native"; +import { StyleSheet, TouchableOpacity, ActivityIndicator, BackHandler } from "react-native"; import { useLocalSearchParams, useRouter } from "expo-router"; import { Video, ResizeMode } from "expo-av"; import { useKeepAwake } from "expo-keep-awake"; @@ -37,9 +37,6 @@ export default function PlayScreen() { introEndTime, setVideoRef, loadVideo, - playEpisode, - togglePlayPause, - seek, handlePlaybackStatusUpdate, setShowControls, setShowEpisodeModal, @@ -100,6 +97,8 @@ export default function PlayScreen() { ref={videoRef} style={styles.videoPlayer} source={{ uri: currentEpisode?.url }} + usePoster + posterSource={{ uri: detail?.videoInfo.cover ?? "" }} resizeMode={ResizeMode.CONTAIN} onPlaybackStatusUpdate={handlePlaybackStatusUpdate} onLoad={() => { diff --git a/package.json b/package.json index c5210eb..6749f8b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "OrionTV", "private": true, "main": "expo-router/entry", - "version": "1.1.0", + "version": "1.1.1", "scripts": { "start": "EXPO_USE_METRO_WORKSPACE_ROOT=1 expo start", "start-tv": "EXPO_TV=1 EXPO_USE_METRO_WORKSPACE_ROOT=1 expo start", diff --git a/stores/homeStore.ts b/stores/homeStore.ts index 35e3fb3..485e61d 100644 --- a/stores/homeStore.ts +++ b/stores/homeStore.ts @@ -21,19 +21,16 @@ export interface Category { title: string; type?: 'movie' | 'tv' | 'record'; tag?: string; + tags?: string[]; } const initialCategories: Category[] = [ { title: '最近播放', type: 'record' }, { title: '热门剧集', type: 'tv', tag: '热门' }, + { title: '电视剧', type: 'tv', tags: [ '国产剧', '美剧', '英剧', '韩剧', '日剧', '港剧', '日本动画', '动画'] }, + { title: '电影', type: 'movie', tags: ['热门', '最新', '经典', '豆瓣高分', '冷门佳片', '华语', '欧美', '韩国', '日本', '动作', '喜剧', '爱情', '科幻', '悬疑', '恐怖'] }, { title: '综艺', type: 'tv', tag: '综艺' }, - { title: '热门电影', type: 'movie', tag: '热门' }, { title: '豆瓣 Top250', type: 'movie', tag: 'top250' }, - { title: '儿童', type: 'movie', tag: '少儿' }, - { title: '美剧', type: 'tv', tag: '美剧' }, - { title: '韩剧', type: 'tv', tag: '韩剧' }, - { title: '日剧', type: 'tv', tag: '日剧' }, - { title: '日漫', type: 'tv', tag: '日本动画' }, ]; interface HomeState { @@ -102,6 +99,9 @@ const useHomeStore = create((set, get) => ({ hasMore: true, })); } + } else if (selectedCategory.tags) { + // It's a container category, do not load content, but clear current content + set({ contentData: [], hasMore: false }); } else { set({ hasMore: false }); } @@ -117,8 +117,12 @@ const useHomeStore = create((set, get) => ({ }, selectCategory: (category: Category) => { - set({ selectedCategory: category }); - get().fetchInitialData(); + const currentCategory = get().selectedCategory; + // Only fetch new data if the category or tag actually changes + if (currentCategory.title !== category.title || currentCategory.tag !== category.tag) { + set({ selectedCategory: category, contentData: [], pageStart: 0, hasMore: true, error: null }); + get().fetchInitialData(); + } }, refreshPlayRecords: async () => {