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 ruby-v 209 lines 6.3 kB view raw
1import React from 'react' 2import {Pressable, StyleSheet, View} from 'react-native' 3import {Image as RNImage} from 'react-native-image-crop-picker' 4import {Image} from 'expo-image' 5import {ModerationUI} from '@atproto/api' 6import {msg, Trans} from '@lingui/macro' 7import {useLingui} from '@lingui/react' 8 9import {usePalette} from '#/lib/hooks/usePalette' 10import { 11 useCameraPermission, 12 usePhotoLibraryPermission, 13} from '#/lib/hooks/usePermissions' 14import {colors} from '#/lib/styles' 15import {useTheme} from '#/lib/ThemeContext' 16import {logger} from '#/logger' 17import {isAndroid, isNative} from '#/platform/detection' 18import {EventStopper} from '#/view/com/util/EventStopper' 19import {tokens, useTheme as useAlfTheme} from '#/alf' 20import {useSheetWrapper} from '#/components/Dialog/sheet-wrapper' 21import { 22 Camera_Filled_Stroke2_Corner0_Rounded as CameraFilled, 23 Camera_Stroke2_Corner0_Rounded as Camera, 24} from '#/components/icons/Camera' 25import {StreamingLive_Stroke2_Corner0_Rounded as Library} from '#/components/icons/StreamingLive' 26import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash' 27import * as Menu from '#/components/Menu' 28import {openCamera, openCropper, openPicker} from '../../../lib/media/picker' 29 30export function UserBanner({ 31 type, 32 banner, 33 moderation, 34 onSelectNewBanner, 35}: { 36 type?: 'labeler' | 'default' 37 banner?: string | null 38 moderation?: ModerationUI 39 onSelectNewBanner?: (img: RNImage | null) => void 40}) { 41 const pal = usePalette('default') 42 const theme = useTheme() 43 const t = useAlfTheme() 44 const {_} = useLingui() 45 const {requestCameraAccessIfNeeded} = useCameraPermission() 46 const {requestPhotoAccessIfNeeded} = usePhotoLibraryPermission() 47 const sheetWrapper = useSheetWrapper() 48 49 const onOpenCamera = React.useCallback(async () => { 50 if (!(await requestCameraAccessIfNeeded())) { 51 return 52 } 53 onSelectNewBanner?.( 54 await openCamera({ 55 width: 3000, 56 height: 1000, 57 }), 58 ) 59 }, [onSelectNewBanner, requestCameraAccessIfNeeded]) 60 61 const onOpenLibrary = React.useCallback(async () => { 62 if (!(await requestPhotoAccessIfNeeded())) { 63 return 64 } 65 const items = await sheetWrapper(openPicker()) 66 if (!items[0]) { 67 return 68 } 69 70 try { 71 onSelectNewBanner?.( 72 await openCropper({ 73 mediaType: 'photo', 74 path: items[0].path, 75 width: 3000, 76 height: 1000, 77 webAspectRatio: 3, 78 }), 79 ) 80 } catch (e: any) { 81 if (!String(e).includes('Canceled')) { 82 logger.error('Failed to crop banner', {error: e}) 83 } 84 } 85 }, [onSelectNewBanner, requestPhotoAccessIfNeeded, sheetWrapper]) 86 87 const onRemoveBanner = React.useCallback(() => { 88 onSelectNewBanner?.(null) 89 }, [onSelectNewBanner]) 90 91 // setUserBanner is only passed as prop on the EditProfile component 92 return onSelectNewBanner ? ( 93 <EventStopper onKeyDown={true}> 94 <Menu.Root> 95 <Menu.Trigger label={_(msg`Edit avatar`)}> 96 {({props}) => ( 97 <Pressable {...props} testID="changeBannerBtn"> 98 {banner ? ( 99 <Image 100 testID="userBannerImage" 101 style={styles.bannerImage} 102 source={{uri: banner}} 103 accessible={true} 104 accessibilityIgnoresInvertColors 105 /> 106 ) : ( 107 <View 108 testID="userBannerFallback" 109 style={[styles.bannerImage, t.atoms.bg_contrast_25]} 110 /> 111 )} 112 <View style={[styles.editButtonContainer, pal.btn]}> 113 <CameraFilled height={14} width={14} style={t.atoms.text} /> 114 </View> 115 </Pressable> 116 )} 117 </Menu.Trigger> 118 <Menu.Outer showCancel> 119 <Menu.Group> 120 {isNative && ( 121 <Menu.Item 122 testID="changeBannerCameraBtn" 123 label={_(msg`Upload from Camera`)} 124 onPress={onOpenCamera}> 125 <Menu.ItemText> 126 <Trans>Upload from Camera</Trans> 127 </Menu.ItemText> 128 <Menu.ItemIcon icon={Camera} /> 129 </Menu.Item> 130 )} 131 132 <Menu.Item 133 testID="changeBannerLibraryBtn" 134 label={_(msg`Upload from Library`)} 135 onPress={onOpenLibrary}> 136 <Menu.ItemText> 137 {isNative ? ( 138 <Trans>Upload from Library</Trans> 139 ) : ( 140 <Trans>Upload from Files</Trans> 141 )} 142 </Menu.ItemText> 143 <Menu.ItemIcon icon={Library} /> 144 </Menu.Item> 145 </Menu.Group> 146 {!!banner && ( 147 <> 148 <Menu.Divider /> 149 <Menu.Group> 150 <Menu.Item 151 testID="changeBannerRemoveBtn" 152 label={_(`Remove Banner`)} 153 onPress={onRemoveBanner}> 154 <Menu.ItemText> 155 <Trans>Remove Banner</Trans> 156 </Menu.ItemText> 157 <Menu.ItemIcon icon={Trash} /> 158 </Menu.Item> 159 </Menu.Group> 160 </> 161 )} 162 </Menu.Outer> 163 </Menu.Root> 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 resizeMode="cover" 174 source={{uri: banner}} 175 blurRadius={moderation?.blur ? 100 : 0} 176 accessible={true} 177 accessibilityIgnoresInvertColors 178 /> 179 ) : ( 180 <View 181 testID="userBannerFallback" 182 style={[ 183 styles.bannerImage, 184 type === 'labeler' ? styles.labelerBanner : t.atoms.bg_contrast_25, 185 ]} 186 /> 187 ) 188} 189 190const styles = StyleSheet.create({ 191 editButtonContainer: { 192 position: 'absolute', 193 width: 24, 194 height: 24, 195 bottom: 8, 196 right: 24, 197 borderRadius: 12, 198 alignItems: 'center', 199 justifyContent: 'center', 200 backgroundColor: colors.gray5, 201 }, 202 bannerImage: { 203 width: '100%', 204 height: 150, 205 }, 206 labelerBanner: { 207 backgroundColor: tokens.color.temp_purple, 208 }, 209})