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

Configure Feed

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

at samuel/exp-cli 285 lines 7.8 kB view raw
1import React, {useImperativeHandle} from 'react' 2import { 3 FlatList, 4 type FlatListProps, 5 type StyleProp, 6 TouchableWithoutFeedback, 7 View, 8 type ViewStyle, 9} from 'react-native' 10import {msg} from '@lingui/macro' 11import {useLingui} from '@lingui/react' 12import {DismissableLayer, FocusGuards, FocusScope} from 'radix-ui/internal' 13import {RemoveScrollBar} from 'react-remove-scroll-bar' 14 15import {logger} from '#/logger' 16import {useA11y} from '#/state/a11y' 17import {useDialogStateControlContext} from '#/state/dialogs' 18import {atoms as a, flatten, useBreakpoints, useTheme, web} from '#/alf' 19import {Button, ButtonIcon} from '#/components/Button' 20import {Context} from '#/components/Dialog/context' 21import { 22 type DialogControlProps, 23 type DialogInnerProps, 24 type DialogOuterProps, 25} from '#/components/Dialog/types' 26import {TimesLarge_Stroke2_Corner0_Rounded as X} from '#/components/icons/Times' 27import {Portal} from '#/components/Portal' 28 29export {useDialogContext, useDialogControl} from '#/components/Dialog/context' 30export * from '#/components/Dialog/shared' 31export * from '#/components/Dialog/types' 32export * from '#/components/Dialog/utils' 33export {Input} from '#/components/forms/TextField' 34 35const stopPropagation = (e: any) => e.stopPropagation() 36const preventDefault = (e: any) => e.preventDefault() 37 38export function Outer({ 39 children, 40 control, 41 onClose, 42 webOptions, 43}: React.PropsWithChildren<DialogOuterProps>) { 44 const {_} = useLingui() 45 const {gtMobile} = useBreakpoints() 46 const [isOpen, setIsOpen] = React.useState(false) 47 const {setDialogIsOpen} = useDialogStateControlContext() 48 49 const open = React.useCallback(() => { 50 setDialogIsOpen(control.id, true) 51 setIsOpen(true) 52 }, [setIsOpen, setDialogIsOpen, control.id]) 53 54 const close = React.useCallback<DialogControlProps['close']>( 55 cb => { 56 setDialogIsOpen(control.id, false) 57 setIsOpen(false) 58 59 try { 60 if (cb && typeof cb === 'function') { 61 // This timeout ensures that the callback runs at the same time as it would on native. I.e. 62 // console.log('Step 1') -> close(() => console.log('Step 3')) -> console.log('Step 2') 63 // This should always output 'Step 1', 'Step 2', 'Step 3', but without the timeout it would output 64 // 'Step 1', 'Step 3', 'Step 2'. 65 setTimeout(cb) 66 } 67 } catch (e: any) { 68 logger.error(`Dialog closeCallback failed`, { 69 message: e.message, 70 }) 71 } 72 73 onClose?.() 74 }, 75 [control.id, onClose, setDialogIsOpen], 76 ) 77 78 const handleBackgroundPress = React.useCallback(async () => { 79 close() 80 }, [close]) 81 82 useImperativeHandle( 83 control.ref, 84 () => ({ 85 open, 86 close, 87 }), 88 [close, open], 89 ) 90 91 const context = React.useMemo( 92 () => ({ 93 close, 94 isNativeDialog: false, 95 nativeSnapPoint: 0, 96 disableDrag: false, 97 setDisableDrag: () => {}, 98 isWithinDialog: true, 99 }), 100 [close], 101 ) 102 103 return ( 104 <> 105 {isOpen && ( 106 <Portal> 107 <Context.Provider value={context}> 108 <RemoveScrollBar /> 109 <TouchableWithoutFeedback 110 accessibilityHint={undefined} 111 accessibilityLabel={_(msg`Close active dialog`)} 112 onPress={handleBackgroundPress}> 113 <View 114 style={[ 115 web(a.fixed), 116 a.inset_0, 117 a.z_10, 118 a.px_xl, 119 webOptions?.alignCenter ? a.justify_center : undefined, 120 a.align_center, 121 { 122 overflowY: 'auto', 123 paddingVertical: gtMobile ? '10vh' : a.pt_xl.paddingTop, 124 }, 125 ]}> 126 <Backdrop /> 127 {/** 128 * This is needed to prevent centered dialogs from overflowing 129 * above the screen, and provides a "natural" centering so that 130 * stacked dialogs appear relatively aligned. 131 */} 132 <View 133 style={[ 134 a.w_full, 135 a.z_20, 136 a.align_center, 137 web({minHeight: '60vh', position: 'static'}), 138 ]}> 139 {children} 140 </View> 141 </View> 142 </TouchableWithoutFeedback> 143 </Context.Provider> 144 </Portal> 145 )} 146 </> 147 ) 148} 149 150export function Inner({ 151 children, 152 style, 153 label, 154 accessibilityLabelledBy, 155 accessibilityDescribedBy, 156 header, 157 contentContainerStyle, 158}: DialogInnerProps) { 159 const t = useTheme() 160 const {close} = React.useContext(Context) 161 const {gtMobile} = useBreakpoints() 162 const {reduceMotionEnabled} = useA11y() 163 FocusGuards.useFocusGuards() 164 return ( 165 <FocusScope.FocusScope loop asChild trapped> 166 <View 167 role="dialog" 168 aria-role="dialog" 169 aria-label={label} 170 aria-labelledby={accessibilityLabelledBy} 171 aria-describedby={accessibilityDescribedBy} 172 // @ts-expect-error web only -prf 173 onClick={stopPropagation} 174 onStartShouldSetResponder={_ => true} 175 onTouchEnd={stopPropagation} 176 style={flatten([ 177 a.relative, 178 a.rounded_md, 179 a.w_full, 180 a.border, 181 t.atoms.bg, 182 { 183 maxWidth: 600, 184 borderColor: t.palette.contrast_200, 185 shadowColor: t.palette.black, 186 shadowOpacity: t.name === 'light' ? 0.1 : 0.4, 187 shadowRadius: 30, 188 }, 189 !reduceMotionEnabled && a.zoom_fade_in, 190 style, 191 ])}> 192 <DismissableLayer.DismissableLayer 193 onInteractOutside={preventDefault} 194 onFocusOutside={preventDefault} 195 onDismiss={close} 196 style={{display: 'flex', flexDirection: 'column'}}> 197 {header} 198 <View style={[gtMobile ? a.p_2xl : a.p_xl, contentContainerStyle]}> 199 {children} 200 </View> 201 </DismissableLayer.DismissableLayer> 202 </View> 203 </FocusScope.FocusScope> 204 ) 205} 206 207export const ScrollableInner = Inner 208 209export const InnerFlatList = React.forwardRef< 210 FlatList, 211 FlatListProps<any> & {label: string} & { 212 webInnerStyle?: StyleProp<ViewStyle> 213 webInnerContentContainerStyle?: StyleProp<ViewStyle> 214 } 215>(function InnerFlatList( 216 {label, style, webInnerStyle, webInnerContentContainerStyle, ...props}, 217 ref, 218) { 219 const {gtMobile} = useBreakpoints() 220 return ( 221 <Inner 222 label={label} 223 style={[ 224 a.overflow_hidden, 225 a.px_0, 226 // 100 minus 10vh of paddingVertical 227 web({maxHeight: '80vh'}), 228 webInnerStyle, 229 ]} 230 contentContainerStyle={[a.px_0, webInnerContentContainerStyle]}> 231 <FlatList 232 ref={ref} 233 style={[gtMobile ? a.px_2xl : a.px_xl, flatten(style)]} 234 {...props} 235 /> 236 </Inner> 237 ) 238}) 239 240export function Close() { 241 const {_} = useLingui() 242 const {close} = React.useContext(Context) 243 return ( 244 <View 245 style={[ 246 a.absolute, 247 a.z_10, 248 { 249 top: a.pt_md.paddingTop, 250 right: a.pr_md.paddingRight, 251 }, 252 ]}> 253 <Button 254 size="small" 255 variant="ghost" 256 color="secondary" 257 shape="round" 258 onPress={() => close()} 259 label={_(msg`Close active dialog`)}> 260 <ButtonIcon icon={X} size="md" /> 261 </Button> 262 </View> 263 ) 264} 265 266export function Handle() { 267 return null 268} 269 270function Backdrop() { 271 const t = useTheme() 272 const {reduceMotionEnabled} = useA11y() 273 return ( 274 <View style={{opacity: 0.8}}> 275 <View 276 style={[ 277 a.fixed, 278 a.inset_0, 279 {backgroundColor: t.palette.black}, 280 !reduceMotionEnabled && a.fade_in, 281 ]} 282 /> 283 </View> 284 ) 285}