diff options
Diffstat (limited to 'src/components/dialogs')
-rw-r--r-- | src/components/dialogs/Context.tsx | 54 | ||||
-rw-r--r-- | src/components/dialogs/InAppBrowserConsent.tsx | 111 |
2 files changed, 155 insertions, 10 deletions
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> + ) +} |