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