diff options
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/Button.tsx | 24 | ||||
-rw-r--r-- | src/components/Dialog/context.ts | 6 | ||||
-rw-r--r-- | src/components/dialogs/Context.tsx | 54 | ||||
-rw-r--r-- | src/components/dialogs/InAppBrowserConsent.tsx | 111 |
4 files changed, 170 insertions, 25 deletions
diff --git a/src/components/Button.tsx b/src/components/Button.tsx index 123e6ee42..2d6ddc834 100644 --- a/src/components/Button.tsx +++ b/src/components/Button.tsx @@ -1,23 +1,23 @@ import React from 'react' import { - AccessibilityProps, - GestureResponderEvent, - MouseEvent, - NativeSyntheticEvent, + type AccessibilityProps, + type GestureResponderEvent, + type MouseEvent, + type NativeSyntheticEvent, Pressable, - PressableProps, - StyleProp, + type PressableProps, + type StyleProp, StyleSheet, - TargetedEvent, - TextProps, - TextStyle, + type TargetedEvent, + type TextProps, + type TextStyle, View, - ViewStyle, + type ViewStyle, } from 'react-native' import {LinearGradient} from 'expo-linear-gradient' import {atoms as a, flatten, select, tokens, useTheme} from '#/alf' -import {Props as SVGIconProps} from '#/components/icons/common' +import {type Props as SVGIconProps} from '#/components/icons/common' import {Text} from '#/components/Typography' export type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'gradient' @@ -597,7 +597,7 @@ export function useSharedButtonTextStyles() { if (variant === 'solid' || variant === 'gradient') { if (!disabled) { baseStyles.push({ - color: t.palette.contrast_100, + color: t.palette.contrast_50, }) } else { baseStyles.push({ diff --git a/src/components/Dialog/context.ts b/src/components/Dialog/context.ts index 331ff3f33..eb892403f 100644 --- a/src/components/Dialog/context.ts +++ b/src/components/Dialog/context.ts @@ -2,9 +2,9 @@ import React from 'react' import {useDialogStateContext} from '#/state/dialogs' import { - DialogContextProps, - DialogControlRefProps, - DialogOuterProps, + type DialogContextProps, + type DialogControlRefProps, + type DialogOuterProps, } from '#/components/Dialog/types' import {BottomSheetSnapPoint} from '../../../modules/bottom-sheet/src/BottomSheet.types' diff --git a/src/components/dialogs/Context.tsx b/src/components/dialogs/Context.tsx index c9dff9a99..fda904b8b 100644 --- a/src/components/dialogs/Context.tsx +++ b/src/components/dialogs/Context.tsx @@ -1,32 +1,66 @@ -import React from 'react' +import {createContext, useContext, useMemo, useState} from 'react' import * as Dialog from '#/components/Dialog' -type Control = Dialog.DialogOuterProps['control'] +type Control = Dialog.DialogControlProps + +export type StatefulControl<T> = { + control: Control + open: (value: T) => void + clear: () => void + value: T | undefined +} type ControlsContext = { mutedWordsDialogControl: Control signinDialogControl: Control + inAppBrowserConsentControl: StatefulControl<string> } -const ControlsContext = React.createContext({ - mutedWordsDialogControl: {} as Control, - signinDialogControl: {} as Control, -}) +const ControlsContext = createContext<ControlsContext | null>(null) export function useGlobalDialogsControlContext() { - return React.useContext(ControlsContext) + const ctx = useContext(ControlsContext) + if (!ctx) { + throw new Error( + 'useGlobalDialogsControlContext must be used within a Provider', + ) + } + return ctx } export function Provider({children}: React.PropsWithChildren<{}>) { const mutedWordsDialogControl = Dialog.useDialogControl() const signinDialogControl = Dialog.useDialogControl() - const ctx = React.useMemo<ControlsContext>( - () => ({mutedWordsDialogControl, signinDialogControl}), - [mutedWordsDialogControl, signinDialogControl], + const inAppBrowserConsentControl = useStatefulDialogControl<string>() + + const ctx = useMemo<ControlsContext>( + () => ({ + mutedWordsDialogControl, + signinDialogControl, + inAppBrowserConsentControl, + }), + [mutedWordsDialogControl, signinDialogControl, inAppBrowserConsentControl], ) return ( <ControlsContext.Provider value={ctx}>{children}</ControlsContext.Provider> ) } + +function useStatefulDialogControl<T>(initialValue?: T): StatefulControl<T> { + const [value, setValue] = useState(initialValue) + const control = Dialog.useDialogControl() + return useMemo( + () => ({ + control, + open: (v: T) => { + setValue(v) + control.open() + }, + clear: () => setValue(initialValue), + value, + }), + [control, value, initialValue], + ) +} diff --git a/src/components/dialogs/InAppBrowserConsent.tsx b/src/components/dialogs/InAppBrowserConsent.tsx new file mode 100644 index 000000000..4459c64db --- /dev/null +++ b/src/components/dialogs/InAppBrowserConsent.tsx @@ -0,0 +1,111 @@ +import {useCallback} from 'react' +import {View} from 'react-native' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {useOpenLink} from '#/lib/hooks/useOpenLink' +import {isWeb} from '#/platform/detection' +import {useSetInAppBrowser} from '#/state/preferences/in-app-browser' +import {atoms as a, useTheme} from '#/alf' +import {Button, ButtonIcon, ButtonText} from '#/components/Button' +import * as Dialog from '#/components/Dialog' +import {SquareArrowTopRight_Stroke2_Corner0_Rounded as External} from '#/components/icons/SquareArrowTopRight' +import {Text} from '#/components/Typography' +import {useGlobalDialogsControlContext} from './Context' + +export function InAppBrowserConsentDialog() { + const {inAppBrowserConsentControl} = useGlobalDialogsControlContext() + + if (isWeb) return null + + return ( + <Dialog.Outer + control={inAppBrowserConsentControl.control} + nativeOptions={{preventExpansion: true}} + onClose={inAppBrowserConsentControl.clear}> + <Dialog.Handle /> + <InAppBrowserConsentInner href={inAppBrowserConsentControl.value} /> + </Dialog.Outer> + ) +} + +function InAppBrowserConsentInner({href}: {href?: string}) { + const control = Dialog.useDialogContext() + const {_} = useLingui() + const t = useTheme() + const setInAppBrowser = useSetInAppBrowser() + const openLink = useOpenLink() + + const onUseIAB = useCallback(() => { + control.close(() => { + setInAppBrowser(true) + if (href) { + openLink(href, true) + } + }) + }, [control, setInAppBrowser, href, openLink]) + + const onUseLinking = useCallback(() => { + control.close(() => { + setInAppBrowser(false) + if (href) { + openLink(href, false) + } + }) + }, [control, setInAppBrowser, href, openLink]) + + const onCancel = useCallback(() => { + control.close() + }, [control]) + + return ( + <Dialog.ScrollableInner label={_(msg`How should we open this link?`)}> + <View style={[a.gap_2xl]}> + <View style={[a.gap_sm]}> + <Text style={[a.font_heavy, a.text_2xl]}> + <Trans>How should we open this link?</Trans> + </Text> + <Text style={[t.atoms.text_contrast_high, a.leading_snug, a.text_md]}> + <Trans> + Your choice will be remembered for future links. You can change it + at any time in settings. + </Trans> + </Text> + </View> + <View style={[a.gap_sm]}> + <Button + label={_(msg`Use in-app browser`)} + onPress={onUseIAB} + size="large" + variant="solid" + color="primary"> + <ButtonText> + <Trans>Use in-app browser</Trans> + </ButtonText> + </Button> + <Button + label={_(msg`Use my default browser`)} + onPress={onUseLinking} + size="large" + variant="solid" + color="secondary"> + <ButtonText> + <Trans>Use my default browser</Trans> + </ButtonText> + <ButtonIcon position="right" icon={External} /> + </Button> + <Button + label={_(msg`Cancel`)} + onPress={onCancel} + size="large" + variant="ghost" + color="secondary"> + <ButtonText> + <Trans>Cancel</Trans> + </ButtonText> + </Button> + </View> + </View> + </Dialog.ScrollableInner> + ) +} |