Bluesky app fork with some witchin' additions 💫

Make profile banners clickable (#4)

authored by

Yhvr and committed by
GitHub
686628c5 2a4eb5c9

+67 -15
+67 -15
src/view/com/util/UserBanner.tsx
··· 1 import React from 'react' 2 - import {Pressable, StyleSheet, View} from 'react-native' 3 - import {Image as RNImage} from 'react-native-image-crop-picker' 4 import {Image} from 'expo-image' 5 - import {ModerationUI} from '@atproto/api' 6 import {msg, Trans} from '@lingui/macro' 7 import {useLingui} from '@lingui/react' 8 9 import {usePalette} from '#/lib/hooks/usePalette' 10 import { 11 useCameraPermission, ··· 15 import {useTheme} from '#/lib/ThemeContext' 16 import {logger} from '#/logger' 17 import {isAndroid, isNative} from '#/platform/detection' 18 import {EventStopper} from '#/view/com/util/EventStopper' 19 import {tokens, useTheme as useAlfTheme} from '#/alf' 20 import {useSheetWrapper} from '#/components/Dialog/sheet-wrapper' ··· 45 const {requestCameraAccessIfNeeded} = useCameraPermission() 46 const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission() 47 const sheetWrapper = useSheetWrapper() 48 49 const onOpenCamera = React.useCallback(async () => { 50 if (!(await requestCameraAccessIfNeeded())) { ··· 88 onSelectNewBanner?.(null) 89 }, [onSelectNewBanner]) 90 91 // setUserBanner is only passed as prop on the EditProfile component 92 return onSelectNewBanner ? ( 93 <EventStopper onKeyDown={true}> ··· 164 </EventStopper> 165 ) : banner && 166 !((moderation?.blur && isAndroid) /* android crashes with blur */) ? ( 167 - <Image 168 - testID="userBannerImage" 169 - style={[ 170 - styles.bannerImage, 171 - {backgroundColor: theme.palette.default.backgroundLight}, 172 - ]} 173 - contentFit="cover" 174 - source={{uri: banner}} 175 - blurRadius={moderation?.blur ? 100 : 0} 176 - accessible={true} 177 - accessibilityIgnoresInvertColors 178 - /> 179 ) : ( 180 <View 181 testID="userBannerFallback"
··· 1 import React from 'react' 2 + import { 3 + Pressable, 4 + StyleSheet, 5 + TouchableWithoutFeedback, 6 + View, 7 + } from 'react-native' 8 + import {type Image as RNImage} from 'react-native-image-crop-picker' 9 + import { 10 + type MeasuredDimensions, 11 + runOnJS, 12 + runOnUI, 13 + } from 'react-native-reanimated' 14 import {Image} from 'expo-image' 15 + import {type ModerationUI} from '@atproto/api' 16 import {msg, Trans} from '@lingui/macro' 17 import {useLingui} from '@lingui/react' 18 19 + import {measureHandle, useHandleRef} from '#/lib/hooks/useHandleRef' 20 import {usePalette} from '#/lib/hooks/usePalette' 21 import { 22 useCameraPermission, ··· 26 import {useTheme} from '#/lib/ThemeContext' 27 import {logger} from '#/logger' 28 import {isAndroid, isNative} from '#/platform/detection' 29 + import {useLightboxControls} from '#/state/lightbox' 30 import {EventStopper} from '#/view/com/util/EventStopper' 31 import {tokens, useTheme as useAlfTheme} from '#/alf' 32 import {useSheetWrapper} from '#/components/Dialog/sheet-wrapper' ··· 57 const {requestCameraAccessIfNeeded} = useCameraPermission() 58 const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission() 59 const sheetWrapper = useSheetWrapper() 60 + const {openLightbox} = useLightboxControls() 61 + 62 + const bannerRef = useHandleRef() 63 64 const onOpenCamera = React.useCallback(async () => { 65 if (!(await requestCameraAccessIfNeeded())) { ··· 103 onSelectNewBanner?.(null) 104 }, [onSelectNewBanner]) 105 106 + const _openLightbox = React.useCallback( 107 + (uri: string, thumbRect: MeasuredDimensions | null) => { 108 + openLightbox({ 109 + images: [ 110 + { 111 + uri, 112 + thumbUri: uri, 113 + thumbRect, 114 + dimensions: thumbRect, 115 + thumbDimensions: null, 116 + type: 'image', 117 + }, 118 + ], 119 + index: 0, 120 + }) 121 + }, 122 + [openLightbox], 123 + ) 124 + 125 + const onPressBanner = React.useCallback(() => { 126 + if (banner && !(moderation?.blur && moderation?.noOverride)) { 127 + const bannerHandle = bannerRef.current 128 + runOnUI(() => { 129 + 'worklet' 130 + const rect = measureHandle(bannerHandle) 131 + runOnJS(_openLightbox)(banner, rect) 132 + })() 133 + } 134 + }, [banner, moderation, _openLightbox, bannerRef]) 135 + 136 // setUserBanner is only passed as prop on the EditProfile component 137 return onSelectNewBanner ? ( 138 <EventStopper onKeyDown={true}> ··· 209 </EventStopper> 210 ) : banner && 211 !((moderation?.blur && isAndroid) /* android crashes with blur */) ? ( 212 + <TouchableWithoutFeedback 213 + testID="profileHeaderAviButton" 214 + onPress={onPressBanner} 215 + accessibilityRole="image" 216 + accessibilityLabel={_(msg`View profile banner`)} 217 + accessibilityHint=""> 218 + <Image 219 + testID="userBannerImage" 220 + style={[ 221 + styles.bannerImage, 222 + {backgroundColor: theme.palette.default.backgroundLight}, 223 + ]} 224 + contentFit="cover" 225 + source={{uri: banner}} 226 + blurRadius={moderation?.blur ? 100 : 0} 227 + accessible={true} 228 + accessibilityIgnoresInvertColors 229 + /> 230 + </TouchableWithoutFeedback> 231 ) : ( 232 <View 233 testID="userBannerFallback"