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 session-alignment 271 lines 6.7 kB view raw
1import React, {useCallback, useEffect, useState} from 'react' 2import { 3 Image, 4 ImageStyle, 5 TouchableOpacity, 6 TouchableWithoutFeedback, 7 StyleSheet, 8 View, 9 Pressable, 10 ViewStyle, 11} from 'react-native' 12import { 13 FontAwesomeIcon, 14 FontAwesomeIconStyle, 15} from '@fortawesome/react-native-fontawesome' 16import {colors, s} from 'lib/styles' 17import ImageDefaultHeader from './ImageViewing/components/ImageDefaultHeader' 18import {Text} from '../util/text/Text' 19import {useLingui} from '@lingui/react' 20import {msg} from '@lingui/macro' 21import { 22 useLightbox, 23 useLightboxControls, 24 ImagesLightbox, 25 ProfileImageLightbox, 26} from '#/state/lightbox' 27import {useWebBodyScrollLock} from '#/lib/hooks/useWebBodyScrollLock' 28import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 29 30interface Img { 31 uri: string 32 alt?: string 33} 34 35export function Lightbox() { 36 const {activeLightbox} = useLightbox() 37 const {closeLightbox} = useLightboxControls() 38 const isActive = !!activeLightbox 39 useWebBodyScrollLock(isActive) 40 41 if (!isActive) { 42 return null 43 } 44 45 const initialIndex = 46 activeLightbox instanceof ImagesLightbox ? activeLightbox.index : 0 47 48 let imgs: Img[] | undefined 49 if (activeLightbox instanceof ProfileImageLightbox) { 50 const opts = activeLightbox 51 if (opts.profile.avatar) { 52 imgs = [{uri: opts.profile.avatar}] 53 } 54 } else if (activeLightbox instanceof ImagesLightbox) { 55 const opts = activeLightbox 56 imgs = opts.images 57 } 58 59 if (!imgs) { 60 return null 61 } 62 63 return ( 64 <LightboxInner 65 imgs={imgs} 66 initialIndex={initialIndex} 67 onClose={closeLightbox} 68 /> 69 ) 70} 71 72function LightboxInner({ 73 imgs, 74 initialIndex = 0, 75 onClose, 76}: { 77 imgs: Img[] 78 initialIndex: number 79 onClose: () => void 80}) { 81 const {_} = useLingui() 82 const [index, setIndex] = useState<number>(initialIndex) 83 const [isAltExpanded, setAltExpanded] = useState(false) 84 85 const canGoLeft = index >= 1 86 const canGoRight = index < imgs.length - 1 87 const onPressLeft = useCallback(() => { 88 if (canGoLeft) { 89 setIndex(index - 1) 90 } 91 }, [index, canGoLeft]) 92 const onPressRight = useCallback(() => { 93 if (canGoRight) { 94 setIndex(index + 1) 95 } 96 }, [index, canGoRight]) 97 98 const onKeyDown = useCallback( 99 (e: KeyboardEvent) => { 100 if (e.key === 'Escape') { 101 onClose() 102 } else if (e.key === 'ArrowLeft') { 103 onPressLeft() 104 } else if (e.key === 'ArrowRight') { 105 onPressRight() 106 } 107 }, 108 [onClose, onPressLeft, onPressRight], 109 ) 110 111 useEffect(() => { 112 window.addEventListener('keydown', onKeyDown) 113 return () => window.removeEventListener('keydown', onKeyDown) 114 }, [onKeyDown]) 115 116 const {isTabletOrDesktop} = useWebMediaQueries() 117 const btnStyle = React.useMemo(() => { 118 return isTabletOrDesktop ? styles.btnTablet : styles.btnMobile 119 }, [isTabletOrDesktop]) 120 const iconSize = React.useMemo(() => { 121 return isTabletOrDesktop ? 32 : 24 122 }, [isTabletOrDesktop]) 123 124 return ( 125 <View style={styles.mask}> 126 <TouchableWithoutFeedback 127 onPress={onClose} 128 accessibilityRole="button" 129 accessibilityLabel={_(msg`Close image viewer`)} 130 accessibilityHint={_(msg`Exits image view`)} 131 onAccessibilityEscape={onClose}> 132 <View style={styles.imageCenterer}> 133 <Image 134 accessibilityIgnoresInvertColors 135 source={imgs[index]} 136 style={styles.image as ImageStyle} 137 accessibilityLabel={imgs[index].alt} 138 accessibilityHint="" 139 /> 140 {canGoLeft && ( 141 <TouchableOpacity 142 onPress={onPressLeft} 143 style={[ 144 styles.btn, 145 btnStyle, 146 styles.leftBtn, 147 styles.blurredBackground, 148 ]} 149 accessibilityRole="button" 150 accessibilityLabel={_(msg`Previous image`)} 151 accessibilityHint=""> 152 <FontAwesomeIcon 153 icon="angle-left" 154 style={styles.icon as FontAwesomeIconStyle} 155 size={iconSize} 156 /> 157 </TouchableOpacity> 158 )} 159 {canGoRight && ( 160 <TouchableOpacity 161 onPress={onPressRight} 162 style={[ 163 styles.btn, 164 btnStyle, 165 styles.rightBtn, 166 styles.blurredBackground, 167 ]} 168 accessibilityRole="button" 169 accessibilityLabel={_(msg`Next image`)} 170 accessibilityHint=""> 171 <FontAwesomeIcon 172 icon="angle-right" 173 style={styles.icon as FontAwesomeIconStyle} 174 size={iconSize} 175 /> 176 </TouchableOpacity> 177 )} 178 </View> 179 </TouchableWithoutFeedback> 180 {imgs[index].alt ? ( 181 <View style={styles.footer}> 182 <Pressable 183 accessibilityLabel={_(msg`Expand alt text`)} 184 accessibilityHint={_( 185 msg`If alt text is long, toggles alt text expanded state`, 186 )} 187 onPress={() => { 188 setAltExpanded(!isAltExpanded) 189 }}> 190 <Text 191 style={s.white} 192 numberOfLines={isAltExpanded ? 0 : 3} 193 ellipsizeMode="tail"> 194 {imgs[index].alt} 195 </Text> 196 </Pressable> 197 </View> 198 ) : null} 199 <View style={styles.closeBtn}> 200 <ImageDefaultHeader onRequestClose={onClose} /> 201 </View> 202 </View> 203 ) 204} 205 206const styles = StyleSheet.create({ 207 mask: { 208 // @ts-ignore 209 position: 'fixed', 210 top: 0, 211 left: 0, 212 width: '100%', 213 height: '100%', 214 backgroundColor: '#000c', 215 }, 216 imageCenterer: { 217 flex: 1, 218 alignItems: 'center', 219 justifyContent: 'center', 220 }, 221 image: { 222 width: '100%', 223 height: '100%', 224 resizeMode: 'contain', 225 }, 226 icon: { 227 color: colors.white, 228 }, 229 closeBtn: { 230 position: 'absolute', 231 top: 10, 232 right: 10, 233 }, 234 btn: { 235 position: 'absolute', 236 backgroundColor: '#00000077', 237 justifyContent: 'center', 238 alignItems: 'center', 239 }, 240 btnTablet: { 241 width: 50, 242 height: 50, 243 borderRadius: 25, 244 left: 30, 245 right: 30, 246 }, 247 btnMobile: { 248 width: 44, 249 height: 44, 250 borderRadius: 22, 251 left: 20, 252 right: 20, 253 }, 254 leftBtn: { 255 right: 'auto', 256 top: '50%', 257 }, 258 rightBtn: { 259 left: 'auto', 260 top: '50%', 261 }, 262 footer: { 263 paddingHorizontal: 32, 264 paddingVertical: 24, 265 backgroundColor: colors.black, 266 }, 267 blurredBackground: { 268 backdropFilter: 'blur(10px)', 269 WebkitBackdropFilter: 'blur(10px)', 270 } as ViewStyle, 271})