Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

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

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