fix: BigVideoCard stale closure, visibility guard, mediaContainer positioning

This commit is contained in:
Developer
2026-03-11 14:32:40 +08:00
parent dfa8fc6dc8
commit d09c1d7346

View File

@@ -2,7 +2,7 @@
import React, { useEffect, useRef, useState } from 'react';
import {
View, Text, Image, TouchableOpacity, StyleSheet,
Dimensions, Animated,
useWindowDimensions, Animated,
} from 'react-native';
import Video from 'react-native-video';
import { Ionicons } from '@expo/vector-icons';
@@ -12,9 +12,6 @@ import { proxyImageUrl } from '../utils/imageUrl';
import { formatCount, formatDuration } from '../utils/format';
import type { VideoItem } from '../services/types';
const { width: SCREEN_W } = Dimensions.get('window');
const THUMB_H = SCREEN_W * 0.5625; // 16:9
const HEADERS = {
Referer: 'https://www.bilibili.com',
'User-Agent':
@@ -28,11 +25,22 @@ interface Props {
}
export function BigVideoCard({ item, isVisible, onPress }: Props) {
const { width: SCREEN_W } = useWindowDimensions();
const THUMB_H = SCREEN_W * 0.5625;
const [videoUrl, setVideoUrl] = useState<string | undefined>();
const [isDash, setIsDash] = useState(false);
const [paused, setPaused] = useState(true);
const thumbOpacity = useRef(new Animated.Value(1)).current;
// Reset video state when the item changes
useEffect(() => {
setVideoUrl(undefined);
setIsDash(false);
setPaused(true);
thumbOpacity.setValue(1);
}, [item.bvid]);
// Fetch play URL when visible for the first time
useEffect(() => {
if (!isVisible || videoUrl) return;
@@ -64,7 +72,7 @@ export function BigVideoCard({ item, isVisible, onPress }: Props) {
console.warn('BigVideoCard: failed to load play URL', e);
}
})();
}, [isVisible]);
}, [isVisible, item.bvid]);
// Pause/resume when visibility changes
useEffect(() => {
@@ -77,6 +85,7 @@ export function BigVideoCard({ item, isVisible, onPress }: Props) {
}, [isVisible, videoUrl]);
const handleVideoReady = () => {
if (!isVisible) return;
setPaused(false);
Animated.timing(thumbOpacity, {
toValue: 0,
@@ -88,12 +97,12 @@ export function BigVideoCard({ item, isVisible, onPress }: Props) {
return (
<TouchableOpacity style={styles.card} onPress={onPress} activeOpacity={0.9}>
{/* Media area */}
<View style={styles.mediaContainer}>
<View style={{ width: SCREEN_W - 8, height: THUMB_H, position: 'relative' }}>
{/* Thumbnail */}
<Animated.View style={[StyleSheet.absoluteFill, { opacity: thumbOpacity }]}>
<Image
source={{ uri: proxyImageUrl(item.pic) }}
style={styles.thumb}
style={{ width: SCREEN_W - 8, height: THUMB_H }}
resizeMode="cover"
/>
</Animated.View>
@@ -143,14 +152,6 @@ const styles = StyleSheet.create({
borderRadius: 6,
overflow: 'hidden',
},
mediaContainer: {
width: SCREEN_W - 8,
height: THUMB_H,
},
thumb: {
width: SCREEN_W - 8,
height: THUMB_H,
},
durationBadge: {
position: 'absolute',
bottom: 4,