mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React from 'react'
2import {type GestureResponderEvent, View} from 'react-native'
3import {msg} from '@lingui/macro'
4import {useLingui} from '@lingui/react'
5
6import {
7 atoms as a,
8 useBreakpoints,
9 useTheme,
10 type ViewStyleProp,
11 web,
12} from '#/alf'
13import {Button, type ButtonColor, ButtonText} from '#/components/Button'
14import * as Dialog from '#/components/Dialog'
15import {Text} from '#/components/Typography'
16import {type BottomSheetViewProps} from '../../modules/bottom-sheet'
17
18export {
19 type DialogControlProps as PromptControlProps,
20 useDialogControl as usePromptControl,
21} from '#/components/Dialog'
22
23const Context = React.createContext<{
24 titleId: string
25 descriptionId: string
26}>({
27 titleId: '',
28 descriptionId: '',
29})
30
31export function Outer({
32 children,
33 control,
34 testID,
35 nativeOptions,
36}: React.PropsWithChildren<{
37 control: Dialog.DialogControlProps
38 testID?: string
39 nativeOptions?: Omit<BottomSheetViewProps, 'children'>
40}>) {
41 const titleId = React.useId()
42 const descriptionId = React.useId()
43
44 const context = React.useMemo(
45 () => ({titleId, descriptionId}),
46 [titleId, descriptionId],
47 )
48
49 return (
50 <Dialog.Outer
51 control={control}
52 testID={testID}
53 webOptions={{alignCenter: true}}
54 nativeOptions={{preventExpansion: true, ...nativeOptions}}>
55 <Dialog.Handle />
56 <Context.Provider value={context}>
57 <Dialog.ScrollableInner
58 accessibilityLabelledBy={titleId}
59 accessibilityDescribedBy={descriptionId}
60 style={web({maxWidth: 400})}>
61 {children}
62 </Dialog.ScrollableInner>
63 </Context.Provider>
64 </Dialog.Outer>
65 )
66}
67
68export function TitleText({
69 children,
70 style,
71}: React.PropsWithChildren<ViewStyleProp>) {
72 const {titleId} = React.useContext(Context)
73 return (
74 <Text
75 nativeID={titleId}
76 style={[
77 a.flex_1,
78 a.text_2xl,
79 a.font_bold,
80 a.pb_sm,
81 a.leading_snug,
82 style,
83 ]}>
84 {children}
85 </Text>
86 )
87}
88
89export function DescriptionText({
90 children,
91 selectable,
92}: React.PropsWithChildren<{selectable?: boolean}>) {
93 const t = useTheme()
94 const {descriptionId} = React.useContext(Context)
95 return (
96 <Text
97 nativeID={descriptionId}
98 selectable={selectable}
99 style={[a.text_md, a.leading_snug, t.atoms.text_contrast_high, a.pb_lg]}>
100 {children}
101 </Text>
102 )
103}
104
105export function Actions({children}: React.PropsWithChildren<{}>) {
106 const {gtMobile} = useBreakpoints()
107
108 return (
109 <View
110 style={[
111 a.w_full,
112 a.gap_md,
113 a.justify_end,
114 gtMobile
115 ? [a.flex_row, a.flex_row_reverse, a.justify_start]
116 : [a.flex_col],
117 ]}>
118 {children}
119 </View>
120 )
121}
122
123export function Cancel({
124 cta,
125}: {
126 /**
127 * Optional i18n string. If undefined, it will default to "Cancel".
128 */
129 cta?: string
130}) {
131 const {_} = useLingui()
132 const {gtMobile} = useBreakpoints()
133 const {close} = Dialog.useDialogContext()
134 const onPress = React.useCallback(() => {
135 close()
136 }, [close])
137
138 return (
139 <Button
140 variant="solid"
141 color="secondary"
142 size={gtMobile ? 'small' : 'large'}
143 label={cta || _(msg`Cancel`)}
144 onPress={onPress}>
145 <ButtonText>{cta || _(msg`Cancel`)}</ButtonText>
146 </Button>
147 )
148}
149
150export function Action({
151 onPress,
152 color = 'primary',
153 cta,
154 testID,
155}: {
156 /**
157 * Callback to run when the action is pressed. The method is called _after_
158 * the dialog closes.
159 *
160 * Note: The dialog will close automatically when the action is pressed, you
161 * should NOT close the dialog as a side effect of this method.
162 */
163 onPress: (e: GestureResponderEvent) => void
164 color?: ButtonColor
165 /**
166 * Optional i18n string. If undefined, it will default to "Confirm".
167 */
168 cta?: string
169 testID?: string
170}) {
171 const {_} = useLingui()
172 const {gtMobile} = useBreakpoints()
173 const {close} = Dialog.useDialogContext()
174 const handleOnPress = React.useCallback(
175 (e: GestureResponderEvent) => {
176 close(() => onPress?.(e))
177 },
178 [close, onPress],
179 )
180
181 return (
182 <Button
183 variant="solid"
184 color={color}
185 size={gtMobile ? 'small' : 'large'}
186 label={cta || _(msg`Confirm`)}
187 onPress={handleOnPress}
188 testID={testID}>
189 <ButtonText>{cta || _(msg`Confirm`)}</ButtonText>
190 </Button>
191 )
192}
193
194export function Basic({
195 control,
196 title,
197 description,
198 cancelButtonCta,
199 confirmButtonCta,
200 onConfirm,
201 confirmButtonColor,
202 showCancel = true,
203}: React.PropsWithChildren<{
204 control: Dialog.DialogOuterProps['control']
205 title: string
206 description?: string
207 cancelButtonCta?: string
208 confirmButtonCta?: string
209 /**
210 * Callback to run when the Confirm button is pressed. The method is called
211 * _after_ the dialog closes.
212 *
213 * Note: The dialog will close automatically when the action is pressed, you
214 * should NOT close the dialog as a side effect of this method.
215 */
216 onConfirm: (e: GestureResponderEvent) => void
217 confirmButtonColor?: ButtonColor
218 showCancel?: boolean
219}>) {
220 return (
221 <Outer control={control} testID="confirmModal">
222 <TitleText>{title}</TitleText>
223 {description && <DescriptionText>{description}</DescriptionText>}
224 <Actions>
225 <Action
226 cta={confirmButtonCta}
227 onPress={onConfirm}
228 color={confirmButtonColor}
229 testID="confirmBtn"
230 />
231 {showCancel && <Cancel cta={cancelButtonCta} />}
232 </Actions>
233 </Outer>
234 )
235}