add indicator of time remaining (#5000)

zio/stable
Samuel Newman 2024-08-27 22:15:59 +01:00 committed by GitHub
parent 9b534b968d
commit b69c40da33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 58 additions and 35 deletions

View File

@ -0,0 +1,48 @@
import React from 'react'
import Animated, {FadeInDown, FadeOutDown} from 'react-native-reanimated'
import {atoms as a, native, useTheme} from '#/alf'
import {Text} from '#/components/Typography'
/**
* Absolutely positioned time indicator showing how many seconds are remaining
* Time is in seconds
*/
export function TimeIndicator({time}: {time: number}) {
const t = useTheme()
if (isNaN(time)) {
return null
}
const minutes = Math.floor(time / 60)
const seconds = String(time % 60).padStart(2, '0')
return (
<Animated.View
entering={native(FadeInDown.duration(300))}
exiting={native(FadeOutDown.duration(500))}
style={[
{
backgroundColor: 'rgba(0, 0, 0, 0.5)',
borderRadius: 6,
paddingHorizontal: 6,
paddingVertical: 3,
position: 'absolute',
left: 5,
bottom: 5,
minHeight: 20,
justifyContent: 'center',
},
]}>
<Text
style={[
{color: t.palette.white, fontSize: 12},
a.font_bold,
{lineHeight: 1.25},
]}>
{minutes}:{seconds}
</Text>
</Animated.View>
)
}

View File

@ -1,6 +1,6 @@
import React, {useCallback, useEffect, useRef, useState} from 'react' import React, {useCallback, useEffect, useRef, useState} from 'react'
import {Pressable, View} from 'react-native' import {Pressable, View} from 'react-native'
import Animated, {FadeInDown, FadeOutDown} from 'react-native-reanimated' import Animated, {FadeInDown} from 'react-native-reanimated'
import {VideoPlayer, VideoView} from 'expo-video' import {VideoPlayer, VideoView} from 'expo-video'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
@ -10,14 +10,14 @@ import {HITSLOP_30} from '#/lib/constants'
import {useAppState} from '#/lib/hooks/useAppState' import {useAppState} from '#/lib/hooks/useAppState'
import {logger} from '#/logger' import {logger} from '#/logger'
import {useVideoPlayer} from '#/view/com/util/post-embeds/VideoPlayerContext' import {useVideoPlayer} from '#/view/com/util/post-embeds/VideoPlayerContext'
import {android, atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute' import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute'
import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker' import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker'
import {Text} from '#/components/Typography'
import { import {
AudioCategory, AudioCategory,
PlatformInfo, PlatformInfo,
} from '../../../../../../modules/expo-bluesky-swiss-army' } from '../../../../../../modules/expo-bluesky-swiss-army'
import {TimeIndicator} from './TimeIndicator'
export function VideoEmbedInnerNative() { export function VideoEmbedInnerNative() {
const player = useVideoPlayer() const player = useVideoPlayer()
@ -86,10 +86,6 @@ function Controls({
Math.floor(player.currentTime), Math.floor(player.currentTime),
) )
const timeRemaining = duration - currentTime
const minutes = Math.floor(timeRemaining / 60)
const seconds = String(timeRemaining % 60).padStart(2, '0')
useEffect(() => { useEffect(() => {
const interval = setInterval(() => { const interval = setInterval(() => {
// duration gets reset to 0 on loop // duration gets reset to 0 on loop
@ -143,37 +139,12 @@ function Controls({
// 1. timeRemaining is a number - was seeing NaNs // 1. timeRemaining is a number - was seeing NaNs
// 2. duration is greater than 0 - means metadata has loaded // 2. duration is greater than 0 - means metadata has loaded
// 3. we're less than 5 second into the video // 3. we're less than 5 second into the video
const timeRemaining = duration - currentTime
const showTime = !isNaN(timeRemaining) && duration > 0 && currentTime <= 5 const showTime = !isNaN(timeRemaining) && duration > 0 && currentTime <= 5
return ( return (
<View style={[a.absolute, a.inset_0]}> <View style={[a.absolute, a.inset_0]}>
{showTime && ( {showTime && <TimeIndicator time={timeRemaining} />}
<Animated.View
entering={FadeInDown.duration(300)}
exiting={FadeOutDown.duration(500)}
style={[
{
backgroundColor: 'rgba(0, 0, 0, 0.75)',
borderRadius: 6,
paddingHorizontal: 6,
paddingVertical: 3,
position: 'absolute',
left: 5,
bottom: 5,
minHeight: 20,
justifyContent: 'center',
},
]}>
<Text
style={[
{color: t.palette.white, fontSize: 12},
a.font_bold,
android({lineHeight: 1.25}),
]}>
{minutes}:{seconds}
</Text>
</Animated.View>
)}
<Pressable <Pressable
onPress={onPressFullscreen} onPress={onPressFullscreen}
style={a.flex_1} style={a.flex_1}
@ -185,7 +156,7 @@ function Controls({
<Animated.View <Animated.View
entering={FadeInDown.duration(300)} entering={FadeInDown.duration(300)}
style={{ style={{
backgroundColor: 'rgba(0, 0, 0, 0.75)', backgroundColor: 'rgba(0, 0, 0, 0.5)',
borderRadius: 6, borderRadius: 6,
paddingHorizontal: 6, paddingHorizontal: 6,
paddingVertical: 3, paddingVertical: 3,

View File

@ -36,6 +36,7 @@ import {Play_Filled_Corner0_Rounded as PlayIcon} from '#/components/icons/Play'
import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker' import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {TimeIndicator} from './TimeIndicator'
export function Controls({ export function Controls({
videoRef, videoRef,
@ -252,6 +253,9 @@ export function Controls({
style={a.flex_1} style={a.flex_1}
onPress={onPressEmptySpace} onPress={onPressEmptySpace}
/> />
{active && !showControls && !focused && (
<TimeIndicator time={Math.floor(duration - currentTime)} />
)}
<View <View
style={[ style={[
a.flex_shrink_0, a.flex_shrink_0,