Bluesky app fork with some witchin' additions 馃挮 witchsky.app
bluesky fork client
at main 112 lines 3.4 kB view raw
1import {useCallback} from 'react' 2import {View} from 'react-native' 3import Animated, {FadeIn, FadeOut} from 'react-native-reanimated' 4import {msg} from '@lingui/core/macro' 5import {useLingui} from '@lingui/react' 6 7import {atoms as a} from '#/alf' 8import {Mute_Stroke2_Corner0_Rounded as MuteIcon} from '#/components/icons/Mute' 9import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as UnmuteIcon} from '#/components/icons/Speaker' 10import {useVideoVolumeState} from '#/components/Post/Embed/VideoEmbed/VideoVolumeContext' 11import {IS_WEB_SAFARI, IS_WEB_TOUCH_DEVICE} from '#/env' 12import {ControlButton} from './ControlButton' 13 14export function VolumeControl({ 15 muted, 16 changeMuted, 17 hovered, 18 onHover, 19 onEndHover, 20 drawFocus, 21}: { 22 muted: boolean 23 changeMuted: (muted: boolean | ((prev: boolean) => boolean)) => void 24 hovered: boolean 25 onHover: () => void 26 onEndHover: () => void 27 drawFocus: () => void 28}) { 29 const {_} = useLingui() 30 const [volume, setVolume] = useVideoVolumeState() 31 32 const onVolumeChange = useCallback( 33 (evt: React.ChangeEvent<HTMLInputElement>) => { 34 drawFocus() 35 const vol = sliderVolumeToVideoVolume(Number(evt.target.value)) 36 setVolume(vol) 37 changeMuted(vol === 0) 38 }, 39 [setVolume, drawFocus, changeMuted], 40 ) 41 42 const sliderVolume = muted ? 0 : videoVolumeToSliderVolume(volume) 43 44 const isZeroVolume = volume === 0 45 const onPressMute = useCallback(() => { 46 drawFocus() 47 if (isZeroVolume) { 48 setVolume(1) 49 changeMuted(false) 50 } else { 51 changeMuted(prevMuted => !prevMuted) 52 } 53 }, [drawFocus, setVolume, isZeroVolume, changeMuted]) 54 55 return ( 56 <View 57 onPointerEnter={onHover} 58 onPointerLeave={onEndHover} 59 style={[a.relative]}> 60 {hovered && !IS_WEB_TOUCH_DEVICE && ( 61 <Animated.View 62 entering={FadeIn.duration(100)} 63 exiting={FadeOut.duration(100)} 64 style={[a.absolute, a.w_full, {height: 100, bottom: '100%'}]}> 65 <View 66 style={[ 67 a.flex_1, 68 a.mb_xs, 69 a.px_2xs, 70 a.py_xs, 71 {backgroundColor: 'rgba(0, 0, 0, 0.6)'}, 72 a.rounded_xs, 73 a.align_center, 74 ]}> 75 <input 76 type="range" 77 min={0} 78 max={100} 79 value={sliderVolume} 80 aria-label={_(msg`Volume`)} 81 style={ 82 // Ridiculous safari hack for old version of safari. Fixed in sonoma beta -h 83 IS_WEB_SAFARI 84 ? {height: 92, minHeight: '100%'} 85 : {height: '100%'} 86 } 87 onChange={onVolumeChange} 88 // @ts-expect-error for old versions of firefox, and then re-using it for targeting the CSS -sfn 89 orient="vertical" 90 /> 91 </View> 92 </Animated.View> 93 )} 94 <ControlButton 95 active={muted || volume === 0} 96 activeLabel={_(msg({message: `Unmute`, context: 'video'}))} 97 inactiveLabel={_(msg({message: `Mute`, context: 'video'}))} 98 activeIcon={MuteIcon} 99 inactiveIcon={UnmuteIcon} 100 onPress={onPressMute} 101 /> 102 </View> 103 ) 104} 105 106function sliderVolumeToVideoVolume(value: number) { 107 return Math.pow(value / 100, 4) 108} 109 110function videoVolumeToSliderVolume(value: number) { 111 return Math.round(Math.pow(value, 1 / 4) * 100) 112}