Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add indicator of time remaining (#5000)

authored by

Samuel Newman and committed by
GitHub
b69c40da 9b534b96

+58 -35
+48
src/view/com/util/post-embeds/VideoEmbedInner/TimeIndicator.tsx
··· 1 + import React from 'react' 2 + import Animated, {FadeInDown, FadeOutDown} from 'react-native-reanimated' 3 + 4 + import {atoms as a, native, useTheme} from '#/alf' 5 + import {Text} from '#/components/Typography' 6 + 7 + /** 8 + * Absolutely positioned time indicator showing how many seconds are remaining 9 + * Time is in seconds 10 + */ 11 + export function TimeIndicator({time}: {time: number}) { 12 + const t = useTheme() 13 + 14 + if (isNaN(time)) { 15 + return null 16 + } 17 + 18 + const minutes = Math.floor(time / 60) 19 + const seconds = String(time % 60).padStart(2, '0') 20 + 21 + return ( 22 + <Animated.View 23 + entering={native(FadeInDown.duration(300))} 24 + exiting={native(FadeOutDown.duration(500))} 25 + style={[ 26 + { 27 + backgroundColor: 'rgba(0, 0, 0, 0.5)', 28 + borderRadius: 6, 29 + paddingHorizontal: 6, 30 + paddingVertical: 3, 31 + position: 'absolute', 32 + left: 5, 33 + bottom: 5, 34 + minHeight: 20, 35 + justifyContent: 'center', 36 + }, 37 + ]}> 38 + <Text 39 + style={[ 40 + {color: t.palette.white, fontSize: 12}, 41 + a.font_bold, 42 + {lineHeight: 1.25}, 43 + ]}> 44 + {minutes}:{seconds} 45 + </Text> 46 + </Animated.View> 47 + ) 48 + }
+6 -35
src/view/com/util/post-embeds/VideoEmbedInner/VideoEmbedInnerNative.tsx
··· 1 1 import React, {useCallback, useEffect, useRef, useState} from 'react' 2 2 import {Pressable, View} from 'react-native' 3 - import Animated, {FadeInDown, FadeOutDown} from 'react-native-reanimated' 3 + import Animated, {FadeInDown} from 'react-native-reanimated' 4 4 import {VideoPlayer, VideoView} from 'expo-video' 5 5 import {msg} from '@lingui/macro' 6 6 import {useLingui} from '@lingui/react' ··· 10 10 import {useAppState} from '#/lib/hooks/useAppState' 11 11 import {logger} from '#/logger' 12 12 import {useVideoPlayer} from '#/view/com/util/post-embeds/VideoPlayerContext' 13 - import {android, atoms as a, useTheme} from '#/alf' 13 + import {atoms as a, useTheme} from '#/alf' 14 14 import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute' 15 15 import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker' 16 - import {Text} from '#/components/Typography' 17 16 import { 18 17 AudioCategory, 19 18 PlatformInfo, 20 19 } from '../../../../../../modules/expo-bluesky-swiss-army' 20 + import {TimeIndicator} from './TimeIndicator' 21 21 22 22 export function VideoEmbedInnerNative() { 23 23 const player = useVideoPlayer() ··· 86 86 Math.floor(player.currentTime), 87 87 ) 88 88 89 - const timeRemaining = duration - currentTime 90 - const minutes = Math.floor(timeRemaining / 60) 91 - const seconds = String(timeRemaining % 60).padStart(2, '0') 92 - 93 89 useEffect(() => { 94 90 const interval = setInterval(() => { 95 91 // duration gets reset to 0 on loop ··· 143 139 // 1. timeRemaining is a number - was seeing NaNs 144 140 // 2. duration is greater than 0 - means metadata has loaded 145 141 // 3. we're less than 5 second into the video 142 + const timeRemaining = duration - currentTime 146 143 const showTime = !isNaN(timeRemaining) && duration > 0 && currentTime <= 5 147 144 148 145 return ( 149 146 <View style={[a.absolute, a.inset_0]}> 150 - {showTime && ( 151 - <Animated.View 152 - entering={FadeInDown.duration(300)} 153 - exiting={FadeOutDown.duration(500)} 154 - style={[ 155 - { 156 - backgroundColor: 'rgba(0, 0, 0, 0.75)', 157 - borderRadius: 6, 158 - paddingHorizontal: 6, 159 - paddingVertical: 3, 160 - position: 'absolute', 161 - left: 5, 162 - bottom: 5, 163 - minHeight: 20, 164 - justifyContent: 'center', 165 - }, 166 - ]}> 167 - <Text 168 - style={[ 169 - {color: t.palette.white, fontSize: 12}, 170 - a.font_bold, 171 - android({lineHeight: 1.25}), 172 - ]}> 173 - {minutes}:{seconds} 174 - </Text> 175 - </Animated.View> 176 - )} 147 + {showTime && <TimeIndicator time={timeRemaining} />} 177 148 <Pressable 178 149 onPress={onPressFullscreen} 179 150 style={a.flex_1} ··· 185 156 <Animated.View 186 157 entering={FadeInDown.duration(300)} 187 158 style={{ 188 - backgroundColor: 'rgba(0, 0, 0, 0.75)', 159 + backgroundColor: 'rgba(0, 0, 0, 0.5)', 189 160 borderRadius: 6, 190 161 paddingHorizontal: 6, 191 162 paddingVertical: 3,
+4
src/view/com/util/post-embeds/VideoEmbedInner/VideoWebControls.tsx
··· 36 36 import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker' 37 37 import {Loader} from '#/components/Loader' 38 38 import {Text} from '#/components/Typography' 39 + import {TimeIndicator} from './TimeIndicator' 39 40 40 41 export function Controls({ 41 42 videoRef, ··· 252 253 style={a.flex_1} 253 254 onPress={onPressEmptySpace} 254 255 /> 256 + {active && !showControls && !focused && ( 257 + <TimeIndicator time={Math.floor(duration - currentTime)} /> 258 + )} 255 259 <View 256 260 style={[ 257 261 a.flex_shrink_0,