add indicator of time remaining (#5000)
parent
9b534b968d
commit
b69c40da33
|
@ -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>
|
||||||
|
)
|
||||||
|
}
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue