about summary refs log tree commit diff
path: root/src/components/dialogs
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/dialogs')
-rw-r--r--src/components/dialogs/Context.tsx54
-rw-r--r--src/components/dialogs/InAppBrowserConsent.tsx111
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>
+  )
+}