fix video

This commit is contained in:
2025-09-30 15:56:11 +02:00
parent ccce5ce264
commit 93994f27b2
3 changed files with 404 additions and 88 deletions
+175 -12
View File
@@ -10,7 +10,6 @@ import PlaybackItem from "./components/PlaybackItem.web";
const Playbacks = () => {
const { height: windowHeight } = useWindowDimensions();
const [activeIndex, setActiveIndex] = useState(0);
const carouselRef = useRef(null);
const route = useRoute();
const focusProjectId =
route?.params?.projectId || route?.params?.focusId || null;
@@ -25,15 +24,132 @@ const Playbacks = () => {
batchSize: 6,
});
const activePlayback = playbacks?.[activeIndex] || null;
const activeVideoUrl = activePlayback?.playbackUrl || null;
const backgroundVideoRef = useRef(null);
const lastActiveIndexRef = useRef(0);
const backgroundSeekPendingRef = useRef(false);
const listRef = useRef(null);
useEffect(() => {
const total = playbacks?.length || 0;
if (total <= 0) {
if (activeIndex !== 0) {
lastActiveIndexRef.current = 0;
setActiveIndex(0);
}
return;
}
const maxIndex = total - 1;
if (activeIndex > maxIndex) {
lastActiveIndexRef.current = maxIndex;
setActiveIndex(maxIndex);
}
}, [playbacks?.length, activeIndex]);
const handleActiveIndexChange = useCallback(
(rawIndex) => {
if (Number.isNaN(rawIndex)) return;
const total = playbacks?.length || 0;
const maxIndex = Math.max(0, total - 1);
const clamped = Math.max(0, Math.min(rawIndex, maxIndex));
if (clamped === lastActiveIndexRef.current) return;
lastActiveIndexRef.current = clamped;
setActiveIndex((prev) => (prev === clamped ? prev : clamped));
if (total > 0 && clamped >= Math.max(0, total - 6)) {
loadMore?.();
}
},
[playbacks?.length, loadMore]
);
const syncBackgroundVideo = useCallback(({ currentTime, isPlaying }) => {
const bg = backgroundVideoRef.current;
if (!bg) return;
if (typeof currentTime === "number" && Number.isFinite(currentTime)) {
const applyTime = () => {
backgroundSeekPendingRef.current = false;
const diff = Math.abs((bg.currentTime || 0) - currentTime);
if (diff > 0.25) {
try {
bg.currentTime = currentTime;
} catch (_e) {}
}
};
if (bg.readyState >= 1) applyTime();
else if (!backgroundSeekPendingRef.current) {
backgroundSeekPendingRef.current = true;
const handler = () => applyTime();
bg.addEventListener("loadeddata", handler, { once: true });
}
}
if (isPlaying === true) {
if (bg.paused) {
bg.play().catch(() => {});
}
} else if (isPlaying === false) {
try {
if (!bg.paused) bg.pause();
} catch (_e) {}
}
}, []);
useEffect(() => {
const bg = backgroundVideoRef.current;
if (!bg) return;
if (!activeVideoUrl) {
backgroundSeekPendingRef.current = false;
try {
bg.pause();
} catch (_e) {}
bg.removeAttribute?.("src");
bg.load?.();
return;
}
backgroundSeekPendingRef.current = false;
const setSrcIfNeeded = () => {
const attrSrc = bg.getAttribute("src");
if (attrSrc !== activeVideoUrl) {
bg.setAttribute("src", activeVideoUrl);
try {
bg.load();
} catch (_e) {}
}
};
const startPlayback = () => {
bg.play().catch(() => {});
};
setSrcIfNeeded();
if (bg.readyState >= 2) {
startPlayback();
return;
}
const handleLoaded = () => {
startPlayback();
};
bg.addEventListener("loadeddata", handleLoaded, { once: true });
return () => {
bg.removeEventListener("loadeddata", handleLoaded);
};
}, [activeVideoUrl]);
const triedLoadMoreRef = useRef(0);
useEffect(() => {
if (!focusProjectId) return;
const idx = playbacks.findIndex((p) => p?.id === focusProjectId);
if (idx >= 0) {
lastActiveIndexRef.current = idx;
setActiveIndex(idx);
setTimeout(() => {
try {
carouselRef.current?.scrollTo?.({ index: idx, animated: false });
listRef.current?.scrollToIndex?.({ index: idx, animated: false });
} catch (_e) {}
}, 50);
} else if (loadMore && triedLoadMoreRef.current < 6) {
@@ -43,22 +159,27 @@ const Playbacks = () => {
}, [focusProjectId, playbacks, loadMore]);
// === FlatList (one real page per item) ===
const listRef = useRef(null);
// keep active index in sync with scroll
const onMomentumScrollEnd = useCallback(
(e) => {
try {
const y = e?.nativeEvent?.contentOffset?.y || 0;
const idx = Math.round(y / windowHeight);
if (!Number.isNaN(idx))
setActiveIndex(
Math.max(0, Math.min(idx, (playbacks?.length || 1) - 1))
);
if (idx >= (playbacks?.length || 0) - 6) loadMore?.();
handleActiveIndexChange(idx);
} catch (_e) {}
},
[windowHeight, playbacks?.length, loadMore]
[windowHeight, handleActiveIndexChange]
);
const onScroll = useCallback(
(e) => {
try {
const y = e?.nativeEvent?.contentOffset?.y || 0;
const idx = Math.round(y / windowHeight);
handleActiveIndexChange(idx);
} catch (_e) {}
},
[windowHeight, handleActiveIndexChange]
);
// programmatic jump to focus item
@@ -66,6 +187,7 @@ const Playbacks = () => {
if (!focusProjectId) return;
const idx = playbacks.findIndex((p) => p?.id === focusProjectId);
if (idx >= 0) {
lastActiveIndexRef.current = idx;
setActiveIndex(idx);
setTimeout(() => {
try {
@@ -85,8 +207,36 @@ const Playbacks = () => {
);
return (
<Page headerType="NONE" width="100%" containerStyle={{ padding: 0 }}>
<Page
headerType="NONE"
width="100%"
containerStyle={{ padding: 0 }}
>
<View style={styles.screen}>
{activeVideoUrl ? (
<View pointerEvents="none" style={styles.backgroundVideoWrapper}>
<video
ref={backgroundVideoRef}
key={activeVideoUrl}
src={activeVideoUrl}
autoPlay
muted
loop
playsInline
preload="auto"
style={{
position: "absolute",
inset: 0,
width: "100%",
height: "100%",
objectFit: "cover",
filter: "blur(28px) saturate(120%) brightness(55%)",
transform: "scale(1.1)",
}}
/>
<View style={styles.backgroundOverlay} />
</View>
) : null}
<FlatList
ref={listRef}
data={playbacks}
@@ -98,6 +248,7 @@ const Playbacks = () => {
userCache={userCache}
getUserByUid={getUserByUid}
isActive={isFocused && index === activeIndex}
onBackgroundSync={syncBackgroundVideo}
/>
</View>
)}
@@ -108,7 +259,8 @@ const Playbacks = () => {
initialNumToRender={3}
windowSize={5}
removeClippedSubviews
scrollEventThrottle={32}
onScroll={onScroll}
scrollEventThrottle={16}
/>
</View>
</Page>
@@ -121,5 +273,16 @@ const styles = StyleSheet.create({
screen: {
flex: 1,
overscrollBehavior: "none",
position: "relative",
},
backgroundVideoWrapper: {
...StyleSheet.absoluteFillObject,
overflow: "hidden",
zIndex: -1,
backgroundColor: "#060606",
},
backgroundOverlay: {
...StyleSheet.absoluteFillObject,
backgroundColor: "rgba(2, 2, 2, 0.35)",
},
});