fork
Configure Feed
Select the types of activity you want to include in your feed.
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.
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}