diff --git a/src/screens/Feeds/VibeScreen.tsx b/src/screens/Feeds/VibeScreen.tsx index b80901be38..4b9be8f3f1 100644 --- a/src/screens/Feeds/VibeScreen.tsx +++ b/src/screens/Feeds/VibeScreen.tsx @@ -1,4 +1,4 @@ -import {useCallback, useEffect, useMemo, useState} from 'react' +import {useCallback, useMemo, useState} from 'react' import { LayoutAnimation, ListRenderItem, @@ -10,7 +10,6 @@ import { import {Gesture, GestureDetector} from 'react-native-gesture-handler' import {runOnJS} from 'react-native-reanimated' import {SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context' -import {useEvent} from 'expo' import {LinearGradient} from 'expo-linear-gradient' import {useVideoPlayer, VideoPlayer, VideoView} from 'expo-video' import { @@ -101,6 +100,10 @@ function YoloFeed() { fetchNextPage, } = usePostFeedQuery(`feedgen|${VIBES_FEED_URI}`) + const [currentSources, setCurrentSources] = useState< + [string | null, string | null, string | null] + >([null, null, null]) + const player1 = useVideoPlayer('', p => { p.loop = true }) @@ -125,27 +128,89 @@ function YoloFeed() { } const player = [player1, player2, player3][index % 3] + const currentSource = currentSources[index % 3] return ( ) }, - [player1, player2, player3, currentIndex, isFocused], + [player1, player2, player3, currentIndex, isFocused, currentSources], ) + const updateVideoState = useNonReactiveCallback((index: number) => { + if (!videos) return + setCurrentIndex(index) + setCurrentSources(oldSources => { + const currentSources = [...oldSources] as [ + string | null, + string | null, + string | null, + ] + + const prevEmbed = videos[index - 1]?.post.embed + const prevVideo = + prevEmbed && AppBskyEmbedVideo.isView(prevEmbed) + ? prevEmbed.playlist + : null + const currEmbed = videos[index]?.post.embed + const currVideo = + currEmbed && AppBskyEmbedVideo.isView(currEmbed) + ? currEmbed.playlist + : null + const nextEmbed = videos[index + 1]?.post.embed + const nextVideo = + nextEmbed && AppBskyEmbedVideo.isView(nextEmbed) + ? nextEmbed.playlist + : null + + const prevPlayer = [player1, player2, player3][(index + 2) % 3] + const prevPlayerCurrentSource = currentSources[(index + 2) % 3] + const currPlayer = [player1, player2, player3][index % 3] + const currPlayerCurrentSource = currentSources[index % 3] + const nextPlayer = [player1, player2, player3][(index + 1) % 3] + const nextPlayerCurrentSource = currentSources[(index + 1) % 3] + + if (prevVideo && prevVideo !== prevPlayerCurrentSource) { + prevPlayer.replace(prevVideo) + currentSources[index + (2 % 3)] = prevVideo + } + prevPlayer.pause() + + if (currVideo) { + if (currVideo !== currPlayerCurrentSource) { + currPlayer.replace(currVideo) + currentSources[index % 3] = currVideo + } + currPlayer.play() + } + + if (nextVideo && nextVideo !== nextPlayerCurrentSource) { + nextPlayer.replace(nextVideo) + currentSources[(index + 1) % 3] = nextVideo + } + nextPlayer.pause() + + return currentSources + }) + }) + const onViewableItemsChanged = useCallback( ({viewableItems}: {viewableItems: ViewToken[]; changed: ViewToken[]}) => { if (viewableItems[0] && viewableItems[0].index !== null) { - setCurrentIndex(viewableItems[0].index) + updateVideoState(viewableItems[0].index) } }, - [], + [updateVideoState], ) return ( @@ -170,7 +235,7 @@ function YoloFeed() { }} showsVerticalScrollIndicator={false} onViewableItemsChanged={onViewableItemsChanged} - viewabilityConfig={{itemVisiblePercentThreshold: 75}} + viewabilityConfig={{itemVisiblePercentThreshold: 95}} /> ) } @@ -183,7 +248,6 @@ function VibeItem({ player, post, embed, - active, loaded, }: { player: VideoPlayer @@ -194,42 +258,6 @@ function VibeItem({ }) { const {height, width} = useWindowDimensions() const insets = useSafeAreaInsets() - const source = embed.playlist - const sourceChangeEvent = useEvent(player, 'sourceChange') as { - // incorrect types - source: {uri?: string} - oldSource: {uri?: string} - } - - // for initial video - useEffect will handle the typical case where - // videos have a chance to preload - const maybePlay = useNonReactiveCallback(() => { - if (active && !player.playing) { - player.play() - } - }) - - useEffect(() => { - if (loaded && sourceChangeEvent?.source?.uri !== source) { - player.replace(source) - // play next tick - const timeout = setTimeout(() => { - maybePlay() - }, 0) - return () => { - clearTimeout(timeout) - } - } - }, [sourceChangeEvent?.source?.uri, loaded, source, player, maybePlay]) - - useEffect(() => { - if (active) { - player.play() - } else { - // should be a cleanup function, but that causes a crash - player.pause() - } - }, [active, player]) const screenAspectRatio = (width - insets.left - insets.right) / (height - insets.bottom) @@ -387,10 +415,12 @@ function ExpandableRichTextView({ { - LayoutAnimation.configureNext({ - duration: 500, - update: {type: 'spring', springDamping: 0.6}, - }) + if (contentHeight !== 0) { + LayoutAnimation.configureNext({ + duration: 500, + update: {type: 'spring', springDamping: 0.6}, + }) + } setContentHeight(h) }} style={{height: Math.min(contentHeight, screenHeight * 0.5)}}