mirror of
https://github.com/zimplexing/OrionTV.git
synced 2026-02-10 17:44:44 +08:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
023fa591ec | ||
|
|
b401d535ce | ||
|
|
23647f7329 | ||
|
|
67275988bd | ||
|
|
f7ae93bd3d | ||
|
|
f124f7e1e2 | ||
|
|
9bcdeaa44d | ||
|
|
4c93736c5e |
@@ -1,5 +1,5 @@
|
||||
import React, { useEffect, useCallback, useRef, useState } from "react";
|
||||
import { View, StyleSheet, ActivityIndicator, FlatList, Pressable, Animated, StatusBar } from "react-native";
|
||||
import { View, StyleSheet, ActivityIndicator, FlatList, Pressable, Animated, StatusBar, Platform } from "react-native";
|
||||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||||
import { ThemedView } from "@/components/ThemedView";
|
||||
import { ThemedText } from "@/components/ThemedText";
|
||||
@@ -15,6 +15,7 @@ import { useResponsiveLayout } from "@/hooks/useResponsiveLayout";
|
||||
import { getCommonResponsiveStyles } from "@/utils/ResponsiveStyles";
|
||||
import ResponsiveNavigation from "@/components/navigation/ResponsiveNavigation";
|
||||
import { useApiConfig, getApiConfigErrorMessage } from "@/hooks/useApiConfig";
|
||||
import { Colors } from "@/constants/Colors";
|
||||
|
||||
const LOAD_MORE_THRESHOLD = 200;
|
||||
|
||||
@@ -166,7 +167,7 @@ export default function HomeScreen() {
|
||||
<View style={dynamicStyles.headerContainer}>
|
||||
<View style={{ flexDirection: "row", alignItems: "center" }}>
|
||||
<ThemedText style={dynamicStyles.headerTitle}>首页</ThemedText>
|
||||
<Pressable style={{ marginLeft: 20 }} onPress={() => router.push("/live")}>
|
||||
<Pressable android_ripple={Platform.isTV || deviceType !== 'tv'? { color: 'transparent' } : { color: Colors.dark.link }} style={{ marginLeft: 20 }} onPress={() => router.push("/live")}>
|
||||
{({ focused }) => (
|
||||
<ThemedText style={[dynamicStyles.headerTitle, { color: focused ? "white" : "grey" }]}>直播</ThemedText>
|
||||
)}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React, { forwardRef } from "react";
|
||||
import { Animated, Pressable, StyleSheet, StyleProp, ViewStyle, PressableProps, TextStyle, View } from "react-native";
|
||||
import { Animated, Pressable, StyleSheet, StyleProp, ViewStyle, PressableProps, TextStyle, View, Platform } from "react-native";
|
||||
import { ThemedText } from "./ThemedText";
|
||||
import { Colors } from "@/constants/Colors";
|
||||
import { useButtonAnimation } from "@/hooks/useAnimation";
|
||||
import { useResponsiveLayout } from "@/hooks/useResponsiveLayout";
|
||||
|
||||
interface StyledButtonProps extends PressableProps {
|
||||
children?: React.ReactNode;
|
||||
@@ -19,6 +20,7 @@ export const StyledButton = forwardRef<View, StyledButtonProps>(
|
||||
const colors = Colors[colorScheme];
|
||||
const [isFocused, setIsFocused] = React.useState(false);
|
||||
const animationStyle = useButtonAnimation(isFocused);
|
||||
const deviceType = useResponsiveLayout().deviceType;
|
||||
|
||||
const variantStyles = {
|
||||
default: StyleSheet.create({
|
||||
@@ -108,6 +110,7 @@ export const StyledButton = forwardRef<View, StyledButtonProps>(
|
||||
return (
|
||||
<Animated.View style={[animationStyle, style]}>
|
||||
<Pressable
|
||||
android_ripple={Platform.isTV || deviceType !== 'tv'? { color: 'transparent' } : { color: Colors.dark.link }}
|
||||
ref={ref}
|
||||
onFocus={() => setIsFocused(true)}
|
||||
onBlur={() => setIsFocused(false)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { useState, useEffect, useCallback, useRef, forwardRef } from "react";
|
||||
import { View, Text, Image, StyleSheet, TouchableOpacity, Alert, Animated } from "react-native";
|
||||
import { View, Text, Image, StyleSheet, Pressable, TouchableOpacity, Alert, Animated, Platform } from "react-native";
|
||||
import { useRouter } from "expo-router";
|
||||
import { Star, Play } from "lucide-react-native";
|
||||
import { PlayRecordManager } from "@/services/storage";
|
||||
@@ -7,6 +7,7 @@ import { API } from "@/services/api";
|
||||
import { ThemedText } from "@/components/ThemedText";
|
||||
import { Colors } from "@/constants/Colors";
|
||||
import Logger from '@/utils/Logger';
|
||||
import { useResponsiveLayout } from "@/hooks/useResponsiveLayout";
|
||||
|
||||
const logger = Logger.withTag('VideoCardTV');
|
||||
|
||||
@@ -54,6 +55,8 @@ const VideoCard = forwardRef<View, VideoCardProps>(
|
||||
|
||||
const scale = useRef(new Animated.Value(1)).current;
|
||||
|
||||
const deviceType = useResponsiveLayout().deviceType;
|
||||
|
||||
const animatedStyle = {
|
||||
transform: [{ scale }],
|
||||
};
|
||||
@@ -147,63 +150,126 @@ const VideoCard = forwardRef<View, VideoCardProps>(
|
||||
|
||||
return (
|
||||
<Animated.View style={[styles.wrapper, animatedStyle, { opacity: fadeAnim }]}>
|
||||
<TouchableOpacity
|
||||
onPress={handlePress}
|
||||
onLongPress={handleLongPress}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
style={styles.pressable}
|
||||
activeOpacity={1}
|
||||
delayLongPress={1000}
|
||||
>
|
||||
<View style={styles.card}>
|
||||
<Image source={{ uri: api.getImageProxyUrl(poster) }} style={styles.poster} />
|
||||
{isFocused && (
|
||||
<View style={styles.overlay}>
|
||||
{isContinueWatching && (
|
||||
<View style={styles.continueWatchingBadge}>
|
||||
<Play size={16} color="#ffffff" fill="#ffffff" />
|
||||
<ThemedText style={styles.continueWatchingText}>继续观看</ThemedText>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
{Platform.isTV || deviceType !== 'tv' ? (
|
||||
<TouchableOpacity
|
||||
onPress={handlePress}
|
||||
onLongPress={handleLongPress}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
style={styles.pressable}
|
||||
activeOpacity={1}
|
||||
delayLongPress={1000}
|
||||
>
|
||||
<View style={styles.card}>
|
||||
<Image source={{ uri: api.getImageProxyUrl(poster) }} style={styles.poster} />
|
||||
{isFocused && (
|
||||
<View style={styles.overlay}>
|
||||
{isContinueWatching && (
|
||||
<View style={styles.continueWatchingBadge}>
|
||||
<Play size={16} color="#ffffff" fill="#ffffff" />
|
||||
<ThemedText style={styles.continueWatchingText}>继续观看</ThemedText>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* 进度条 */}
|
||||
{isContinueWatching && (
|
||||
<View style={styles.progressContainer}>
|
||||
<View style={[styles.progressBar, { width: `${(progress || 0) * 100}%` }]} />
|
||||
</View>
|
||||
)}
|
||||
{/* 进度条 */}
|
||||
{isContinueWatching && (
|
||||
<View style={styles.progressContainer}>
|
||||
<View style={[styles.progressBar, { width: `${(progress || 0) * 100}%` }]} />
|
||||
</View>
|
||||
)}
|
||||
|
||||
{rate && (
|
||||
<View style={styles.ratingContainer}>
|
||||
<Star size={12} color="#FFD700" fill="#FFD700" />
|
||||
<ThemedText style={styles.ratingText}>{rate}</ThemedText>
|
||||
</View>
|
||||
)}
|
||||
{year && (
|
||||
<View style={styles.yearBadge}>
|
||||
<Text style={styles.badgeText}>{year}</Text>
|
||||
</View>
|
||||
)}
|
||||
{sourceName && (
|
||||
<View style={styles.sourceNameBadge}>
|
||||
<Text style={styles.badgeText}>{sourceName}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<View style={styles.infoContainer}>
|
||||
<ThemedText numberOfLines={1}>{title}</ThemedText>
|
||||
{isContinueWatching && (
|
||||
<View style={styles.infoRow}>
|
||||
<ThemedText style={styles.continueLabel}>
|
||||
第{episodeIndex}集 已观看 {Math.round((progress || 0) * 100)}%
|
||||
</ThemedText>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
) : (
|
||||
<Pressable
|
||||
android_ripple={{ color: Colors.dark.link }}
|
||||
onPress={handlePress}
|
||||
onLongPress={handleLongPress}
|
||||
onFocus={handleFocus}
|
||||
onBlur={handleBlur}
|
||||
style={styles.pressable}
|
||||
// activeOpacity={1}
|
||||
delayLongPress={1000}
|
||||
>
|
||||
<View style={styles.card}>
|
||||
<Image source={{ uri: api.getImageProxyUrl(poster) }} style={styles.poster} />
|
||||
{isFocused && (
|
||||
<View style={styles.overlay}>
|
||||
{isContinueWatching && (
|
||||
<View style={styles.continueWatchingBadge}>
|
||||
<Play size={16} color="#ffffff" fill="#ffffff" />
|
||||
<ThemedText style={styles.continueWatchingText}>继续观看</ThemedText>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
|
||||
{/* 进度条 */}
|
||||
{isContinueWatching && (
|
||||
<View style={styles.progressContainer}>
|
||||
<View style={[styles.progressBar, { width: `${(progress || 0) * 100}%` }]} />
|
||||
</View>
|
||||
)}
|
||||
|
||||
{rate && (
|
||||
<View style={styles.ratingContainer}>
|
||||
<Star size={12} color="#FFD700" fill="#FFD700" />
|
||||
<ThemedText style={styles.ratingText}>{rate}</ThemedText>
|
||||
</View>
|
||||
)}
|
||||
{year && (
|
||||
<View style={styles.yearBadge}>
|
||||
<Text style={styles.badgeText}>{year}</Text>
|
||||
</View>
|
||||
)}
|
||||
{sourceName && (
|
||||
<View style={styles.sourceNameBadge}>
|
||||
<Text style={styles.badgeText}>{sourceName}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<View style={styles.infoContainer}>
|
||||
<ThemedText numberOfLines={1}>{title}</ThemedText>
|
||||
{isContinueWatching && (
|
||||
<View style={styles.infoRow}>
|
||||
<ThemedText style={styles.continueLabel}>
|
||||
第{episodeIndex}集 已观看 {Math.round((progress || 0) * 100)}%
|
||||
</ThemedText>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</Pressable>
|
||||
|
||||
)}
|
||||
|
||||
{rate && (
|
||||
<View style={styles.ratingContainer}>
|
||||
<Star size={12} color="#FFD700" fill="#FFD700" />
|
||||
<ThemedText style={styles.ratingText}>{rate}</ThemedText>
|
||||
</View>
|
||||
)}
|
||||
{year && (
|
||||
<View style={styles.yearBadge}>
|
||||
<Text style={styles.badgeText}>{year}</Text>
|
||||
</View>
|
||||
)}
|
||||
{sourceName && (
|
||||
<View style={styles.sourceNameBadge}>
|
||||
<Text style={styles.badgeText}>{sourceName}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<View style={styles.infoContainer}>
|
||||
<ThemedText numberOfLines={1}>{title}</ThemedText>
|
||||
{isContinueWatching && (
|
||||
<View style={styles.infoRow}>
|
||||
<ThemedText style={styles.continueLabel}>
|
||||
第{episodeIndex}集 已观看 {Math.round((progress || 0) * 100)}%
|
||||
</ThemedText>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
@@ -221,9 +287,14 @@ const styles = StyleSheet.create({
|
||||
marginHorizontal: 8,
|
||||
},
|
||||
pressable: {
|
||||
width: CARD_WIDTH + 20,
|
||||
height: CARD_HEIGHT + 60,
|
||||
justifyContent: 'center',
|
||||
alignItems: "center",
|
||||
overflow: "visible",
|
||||
},
|
||||
card: {
|
||||
marginTop: 10,
|
||||
width: CARD_WIDTH,
|
||||
height: CARD_HEIGHT,
|
||||
borderRadius: 8,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "OrionTV",
|
||||
"private": true,
|
||||
"main": "expo-router/entry",
|
||||
"version": "1.3.7",
|
||||
"version": "1.3.8",
|
||||
"scripts": {
|
||||
"start": "EXPO_TV=1 EXPO_USE_METRO_WORKSPACE_ROOT=1 expo start",
|
||||
"android": "EXPO_TV=1 EXPO_USE_METRO_WORKSPACE_ROOT=1 expo run:android",
|
||||
|
||||
Reference in New Issue
Block a user