Fix various issues with screen dimension detection in external player (#2349)

* various dimension fixes

* use reactive hook for dimensions

* remove debug

* accurate dismissing of player
zio/stable
Hailey 2024-01-02 22:36:43 -08:00 committed by GitHub
parent db3bf784fb
commit d4bb64c1e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 45 additions and 19 deletions

View File

@ -1,14 +1,21 @@
import React from 'react' import React from 'react'
import { import {
ActivityIndicator, ActivityIndicator,
Dimensions,
GestureResponderEvent, GestureResponderEvent,
Pressable, Pressable,
StyleSheet, StyleSheet,
useWindowDimensions,
View, View,
} from 'react-native' } from 'react-native'
import Animated, {
measure,
runOnJS,
useAnimatedRef,
useFrameCallback,
} from 'react-native-reanimated'
import {Image} from 'expo-image' import {Image} from 'expo-image'
import {WebView} from 'react-native-webview' import {WebView} from 'react-native-webview'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import YoutubePlayer from 'react-native-youtube-iframe' import YoutubePlayer from 'react-native-youtube-iframe'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {EmbedPlayerParams, getPlayerHeight} from 'lib/strings/embed-player' import {EmbedPlayerParams, getPlayerHeight} from 'lib/strings/embed-player'
@ -116,6 +123,8 @@ export function ExternalPlayer({
params: EmbedPlayerParams params: EmbedPlayerParams
}) { }) {
const navigation = useNavigation<NavigationProp>() const navigation = useNavigation<NavigationProp>()
const insets = useSafeAreaInsets()
const windowDims = useWindowDimensions()
const [isPlayerActive, setPlayerActive] = React.useState(false) const [isPlayerActive, setPlayerActive] = React.useState(false)
const [isLoading, setIsLoading] = React.useState(true) const [isLoading, setIsLoading] = React.useState(true)
@ -124,34 +133,51 @@ export function ExternalPlayer({
height: 0, height: 0,
}) })
const viewRef = React.useRef<View>(null) const viewRef = useAnimatedRef()
const frameCallback = useFrameCallback(() => {
const measurement = measure(viewRef)
if (!measurement) return
const {height: winHeight, width: winWidth} = windowDims
// Get the proper screen height depending on what is going on
const realWinHeight = isNative // If it is native, we always want the larger number
? winHeight > winWidth
? winHeight
: winWidth
: winHeight // On web, we always want the actual screen height
const top = measurement.pageY
const bot = measurement.pageY + measurement.height
// We can use the same logic on all platforms against the screenHeight that we get above
const isVisible = top <= realWinHeight - insets.bottom && bot >= insets.top
if (!isVisible) {
runOnJS(setPlayerActive)(false)
}
}, false) // False here disables autostarting the callback
// watch for leaving the viewport due to scrolling // watch for leaving the viewport due to scrolling
React.useEffect(() => { React.useEffect(() => {
// We don't want to do anything if the player isn't active
if (!isPlayerActive) return
// Interval for scrolling works in most cases, However, for twitch embeds, if we navigate away from the screen the webview will // Interval for scrolling works in most cases, However, for twitch embeds, if we navigate away from the screen the webview will
// continue playing. We need to watch for the blur event // continue playing. We need to watch for the blur event
const unsubscribe = navigation.addListener('blur', () => { const unsubscribe = navigation.addListener('blur', () => {
setPlayerActive(false) setPlayerActive(false)
}) })
const interval = setInterval(() => { // Start watching for changes
viewRef.current?.measure((x, y, w, h, pageX, pageY) => { frameCallback.setActive(true)
const window = Dimensions.get('window')
const top = pageY
const bot = pageY + h
const isVisible = isNative
? top >= 0 && bot <= window.height
: !(top >= window.height || bot <= 0)
if (!isVisible) {
setPlayerActive(false)
}
})
}, 1e3)
return () => { return () => {
unsubscribe() unsubscribe()
clearInterval(interval) frameCallback.setActive(false)
} }
}, [viewRef, navigation]) }, [navigation, isPlayerActive, frameCallback])
// calculate height for the player and the screen size // calculate height for the player and the screen size
const height = React.useMemo( const height = React.useMemo(
@ -187,7 +213,7 @@ export function ExternalPlayer({
) )
return ( return (
<View <Animated.View
ref={viewRef} ref={viewRef}
style={{height}} style={{height}}
collapsable={false} collapsable={false}
@ -217,7 +243,7 @@ export function ExternalPlayer({
height={height} height={height}
onLoad={onLoad} onLoad={onLoad}
/> />
</View> </Animated.View>
) )
} }