mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

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

at tooltip 210 lines 6.2 kB view raw
1import React, {useRef} from 'react' 2import {Pressable, type StyleProp, View, type ViewStyle} from 'react-native' 3import {type AppBskyEmbedVideo} from '@atproto/api' 4import {BlueskyVideoView} from '@haileyok/bluesky-video' 5import {msg} from '@lingui/macro' 6import {useLingui} from '@lingui/react' 7 8import {HITSLOP_30} from '#/lib/constants' 9import {useAutoplayDisabled} from '#/state/preferences' 10import {atoms as a, useTheme} from '#/alf' 11import {useIsWithinMessage} from '#/components/dms/MessageContext' 12import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute' 13import {Pause_Filled_Corner0_Rounded as PauseIcon} from '#/components/icons/Pause' 14import {Play_Filled_Corner0_Rounded as PlayIcon} from '#/components/icons/Play' 15import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker' 16import {MediaInsetBorder} from '#/components/MediaInsetBorder' 17import {useVideoMuteState} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' 18import {TimeIndicator} from './TimeIndicator' 19 20export const VideoEmbedInnerNative = React.forwardRef( 21 function VideoEmbedInnerNative( 22 { 23 embed, 24 setStatus, 25 setIsLoading, 26 setIsActive, 27 }: { 28 embed: AppBskyEmbedVideo.View 29 setStatus: (status: 'playing' | 'paused') => void 30 setIsLoading: (isLoading: boolean) => void 31 setIsActive: (isActive: boolean) => void 32 }, 33 ref: React.Ref<{togglePlayback: () => void}>, 34 ) { 35 const {_} = useLingui() 36 const videoRef = useRef<BlueskyVideoView>(null) 37 const autoplayDisabled = useAutoplayDisabled() 38 const isWithinMessage = useIsWithinMessage() 39 const [muted, setMuted] = useVideoMuteState() 40 41 const [isPlaying, setIsPlaying] = React.useState(false) 42 const [timeRemaining, setTimeRemaining] = React.useState(0) 43 const [error, setError] = React.useState<string>() 44 45 React.useImperativeHandle(ref, () => ({ 46 togglePlayback: () => { 47 videoRef.current?.togglePlayback() 48 }, 49 })) 50 51 if (error) { 52 throw new Error(error) 53 } 54 55 return ( 56 <View style={[a.flex_1, a.relative]}> 57 <BlueskyVideoView 58 url={embed.playlist} 59 autoplay={!autoplayDisabled && !isWithinMessage} 60 beginMuted={autoplayDisabled ? false : muted} 61 style={[a.rounded_sm]} 62 onActiveChange={e => { 63 setIsActive(e.nativeEvent.isActive) 64 }} 65 onLoadingChange={e => { 66 setIsLoading(e.nativeEvent.isLoading) 67 }} 68 onMutedChange={e => { 69 setMuted(e.nativeEvent.isMuted) 70 }} 71 onStatusChange={e => { 72 setStatus(e.nativeEvent.status) 73 setIsPlaying(e.nativeEvent.status === 'playing') 74 }} 75 onTimeRemainingChange={e => { 76 setTimeRemaining(e.nativeEvent.timeRemaining) 77 }} 78 onError={e => { 79 setError(e.nativeEvent.error) 80 }} 81 ref={videoRef} 82 accessibilityLabel={ 83 embed.alt ? _(msg`Video: ${embed.alt}`) : _(msg`Video`) 84 } 85 accessibilityHint="" 86 /> 87 <VideoControls 88 enterFullscreen={() => { 89 videoRef.current?.enterFullscreen(true) 90 }} 91 toggleMuted={() => { 92 videoRef.current?.toggleMuted() 93 }} 94 togglePlayback={() => { 95 videoRef.current?.togglePlayback() 96 }} 97 isPlaying={isPlaying} 98 timeRemaining={timeRemaining} 99 /> 100 <MediaInsetBorder /> 101 </View> 102 ) 103 }, 104) 105 106function VideoControls({ 107 enterFullscreen, 108 toggleMuted, 109 togglePlayback, 110 timeRemaining, 111 isPlaying, 112}: { 113 enterFullscreen: () => void 114 toggleMuted: () => void 115 togglePlayback: () => void 116 timeRemaining: number 117 isPlaying: boolean 118}) { 119 const {_} = useLingui() 120 const t = useTheme() 121 const [muted] = useVideoMuteState() 122 123 // show countdown when: 124 // 1. timeRemaining is a number - was seeing NaNs 125 // 2. duration is greater than 0 - means metadata has loaded 126 // 3. we're less than 5 second into the video 127 const showTime = !isNaN(timeRemaining) 128 129 return ( 130 <View style={[a.absolute, a.inset_0]}> 131 <Pressable 132 onPress={enterFullscreen} 133 style={a.flex_1} 134 accessibilityLabel={_(msg`Video`)} 135 accessibilityHint={_(msg`Enters full screen`)} 136 accessibilityRole="button" 137 /> 138 <ControlButton 139 onPress={togglePlayback} 140 label={isPlaying ? _(msg`Pause`) : _(msg`Play`)} 141 accessibilityHint={_(msg`Plays or pauses the video`)} 142 style={{left: 6}}> 143 {isPlaying ? ( 144 <PauseIcon width={13} fill={t.palette.white} /> 145 ) : ( 146 <PlayIcon width={13} fill={t.palette.white} /> 147 )} 148 </ControlButton> 149 {showTime && <TimeIndicator time={timeRemaining} style={{left: 33}} />} 150 151 <ControlButton 152 onPress={toggleMuted} 153 label={ 154 muted 155 ? _(msg({message: `Unmute`, context: 'video'})) 156 : _(msg({message: `Mute`, context: 'video'})) 157 } 158 accessibilityHint={_(msg`Toggles the sound`)} 159 style={{right: 6}}> 160 {muted ? ( 161 <MuteIcon width={13} fill={t.palette.white} /> 162 ) : ( 163 <UnmuteIcon width={13} fill={t.palette.white} /> 164 )} 165 </ControlButton> 166 </View> 167 ) 168} 169 170function ControlButton({ 171 onPress, 172 children, 173 label, 174 accessibilityHint, 175 style, 176}: { 177 onPress: () => void 178 children: React.ReactNode 179 label: string 180 accessibilityHint: string 181 style?: StyleProp<ViewStyle> 182}) { 183 return ( 184 <View 185 style={[ 186 a.absolute, 187 a.rounded_full, 188 a.justify_center, 189 { 190 backgroundColor: 'rgba(0, 0, 0, 0.5)', 191 paddingHorizontal: 4, 192 paddingVertical: 4, 193 bottom: 6, 194 minHeight: 21, 195 minWidth: 21, 196 }, 197 style, 198 ]}> 199 <Pressable 200 onPress={onPress} 201 style={a.flex_1} 202 accessibilityLabel={label} 203 accessibilityHint={accessibilityHint} 204 accessibilityRole="button" 205 hitSlop={HITSLOP_30}> 206 {children} 207 </Pressable> 208 </View> 209 ) 210}