Modernize in-app browser consent dialog (#8191)
* add stateful dialog control hook * add new alf'd consent * make secondary_inverted buttons clearer * contingency for opening a link from another dialog * rm old modal * Differentiate buttons more --------- Co-authored-by: Eric Bailey <git@esb.lol>
This commit is contained in:
+12
-12
@@ -1,23 +1,23 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {
|
import {
|
||||||
AccessibilityProps,
|
type AccessibilityProps,
|
||||||
GestureResponderEvent,
|
type GestureResponderEvent,
|
||||||
MouseEvent,
|
type MouseEvent,
|
||||||
NativeSyntheticEvent,
|
type NativeSyntheticEvent,
|
||||||
Pressable,
|
Pressable,
|
||||||
PressableProps,
|
type PressableProps,
|
||||||
StyleProp,
|
type StyleProp,
|
||||||
StyleSheet,
|
StyleSheet,
|
||||||
TargetedEvent,
|
type TargetedEvent,
|
||||||
TextProps,
|
type TextProps,
|
||||||
TextStyle,
|
type TextStyle,
|
||||||
View,
|
View,
|
||||||
ViewStyle,
|
type ViewStyle,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import {LinearGradient} from 'expo-linear-gradient'
|
import {LinearGradient} from 'expo-linear-gradient'
|
||||||
|
|
||||||
import {atoms as a, flatten, select, tokens, useTheme} from '#/alf'
|
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'
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
export type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'gradient'
|
export type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'gradient'
|
||||||
@@ -597,7 +597,7 @@ export function useSharedButtonTextStyles() {
|
|||||||
if (variant === 'solid' || variant === 'gradient') {
|
if (variant === 'solid' || variant === 'gradient') {
|
||||||
if (!disabled) {
|
if (!disabled) {
|
||||||
baseStyles.push({
|
baseStyles.push({
|
||||||
color: t.palette.contrast_100,
|
color: t.palette.contrast_50,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
baseStyles.push({
|
baseStyles.push({
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import React from 'react'
|
|||||||
|
|
||||||
import {useDialogStateContext} from '#/state/dialogs'
|
import {useDialogStateContext} from '#/state/dialogs'
|
||||||
import {
|
import {
|
||||||
DialogContextProps,
|
type DialogContextProps,
|
||||||
DialogControlRefProps,
|
type DialogControlRefProps,
|
||||||
DialogOuterProps,
|
type DialogOuterProps,
|
||||||
} from '#/components/Dialog/types'
|
} from '#/components/Dialog/types'
|
||||||
import {BottomSheetSnapPoint} from '../../../modules/bottom-sheet/src/BottomSheet.types'
|
import {BottomSheetSnapPoint} from '../../../modules/bottom-sheet/src/BottomSheet.types'
|
||||||
|
|
||||||
|
|||||||
@@ -1,32 +1,66 @@
|
|||||||
import React from 'react'
|
import {createContext, useContext, useMemo, useState} from 'react'
|
||||||
|
|
||||||
import * as Dialog from '#/components/Dialog'
|
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 = {
|
type ControlsContext = {
|
||||||
mutedWordsDialogControl: Control
|
mutedWordsDialogControl: Control
|
||||||
signinDialogControl: Control
|
signinDialogControl: Control
|
||||||
|
inAppBrowserConsentControl: StatefulControl<string>
|
||||||
}
|
}
|
||||||
|
|
||||||
const ControlsContext = React.createContext({
|
const ControlsContext = createContext<ControlsContext | null>(null)
|
||||||
mutedWordsDialogControl: {} as Control,
|
|
||||||
signinDialogControl: {} as Control,
|
|
||||||
})
|
|
||||||
|
|
||||||
export function useGlobalDialogsControlContext() {
|
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<{}>) {
|
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
const mutedWordsDialogControl = Dialog.useDialogControl()
|
const mutedWordsDialogControl = Dialog.useDialogControl()
|
||||||
const signinDialogControl = Dialog.useDialogControl()
|
const signinDialogControl = Dialog.useDialogControl()
|
||||||
const ctx = React.useMemo<ControlsContext>(
|
const inAppBrowserConsentControl = useStatefulDialogControl<string>()
|
||||||
() => ({mutedWordsDialogControl, signinDialogControl}),
|
|
||||||
[mutedWordsDialogControl, signinDialogControl],
|
const ctx = useMemo<ControlsContext>(
|
||||||
|
() => ({
|
||||||
|
mutedWordsDialogControl,
|
||||||
|
signinDialogControl,
|
||||||
|
inAppBrowserConsentControl,
|
||||||
|
}),
|
||||||
|
[mutedWordsDialogControl, signinDialogControl, inAppBrowserConsentControl],
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ControlsContext.Provider value={ctx}>{children}</ControlsContext.Provider>
|
<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],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -12,16 +12,18 @@ import {
|
|||||||
toNiceDomain,
|
toNiceDomain,
|
||||||
} from '#/lib/strings/url-helpers'
|
} from '#/lib/strings/url-helpers'
|
||||||
import {isNative} from '#/platform/detection'
|
import {isNative} from '#/platform/detection'
|
||||||
import {useModalControls} from '#/state/modals'
|
|
||||||
import {useInAppBrowser} from '#/state/preferences/in-app-browser'
|
import {useInAppBrowser} from '#/state/preferences/in-app-browser'
|
||||||
import {useTheme} from '#/alf'
|
import {useTheme} from '#/alf'
|
||||||
|
import {useDialogContext} from '#/components/Dialog'
|
||||||
import {useSheetWrapper} from '#/components/Dialog/sheet-wrapper'
|
import {useSheetWrapper} from '#/components/Dialog/sheet-wrapper'
|
||||||
|
import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
|
||||||
|
|
||||||
export function useOpenLink() {
|
export function useOpenLink() {
|
||||||
const {openModal} = useModalControls()
|
|
||||||
const enabled = useInAppBrowser()
|
const enabled = useInAppBrowser()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const sheetWrapper = useSheetWrapper()
|
const sheetWrapper = useSheetWrapper()
|
||||||
|
const dialogContext = useDialogContext()
|
||||||
|
const {inAppBrowserConsentControl} = useGlobalDialogsControlContext()
|
||||||
|
|
||||||
const openLink = useCallback(
|
const openLink = useCallback(
|
||||||
async (url: string, override?: boolean, shouldProxy?: boolean) => {
|
async (url: string, override?: boolean, shouldProxy?: boolean) => {
|
||||||
@@ -42,10 +44,17 @@ export function useOpenLink() {
|
|||||||
|
|
||||||
if (isNative && !url.startsWith('mailto:')) {
|
if (isNative && !url.startsWith('mailto:')) {
|
||||||
if (override === undefined && enabled === undefined) {
|
if (override === undefined && enabled === undefined) {
|
||||||
openModal({
|
// consent dialog is a global dialog, and while it's possible to nest dialogs,
|
||||||
name: 'in-app-browser-consent',
|
// the actual components need to be nested. sibling dialogs on iOS are not supported.
|
||||||
href: url,
|
// thus, check if we're in a dialog, and if so, close the existing dialog before opening the
|
||||||
|
// consent dialog -sfn
|
||||||
|
if (dialogContext.isWithinDialog) {
|
||||||
|
dialogContext.close(() => {
|
||||||
|
inAppBrowserConsentControl.open(url)
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
inAppBrowserConsentControl.open(url)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
} else if (override ?? enabled) {
|
} else if (override ?? enabled) {
|
||||||
await sheetWrapper(
|
await sheetWrapper(
|
||||||
@@ -62,7 +71,7 @@ export function useOpenLink() {
|
|||||||
}
|
}
|
||||||
Linking.openURL(url)
|
Linking.openURL(url)
|
||||||
},
|
},
|
||||||
[enabled, openModal, t, sheetWrapper],
|
[enabled, inAppBrowserConsentControl, t, sheetWrapper, dialogContext],
|
||||||
)
|
)
|
||||||
|
|
||||||
return openLink
|
return openLink
|
||||||
|
|||||||
@@ -66,11 +66,6 @@ export interface LinkWarningModal {
|
|||||||
share?: boolean
|
share?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface InAppBrowserConsentModal {
|
|
||||||
name: 'in-app-browser-consent'
|
|
||||||
href: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Modal =
|
export type Modal =
|
||||||
// Account
|
// Account
|
||||||
| DeleteAccountModal
|
| DeleteAccountModal
|
||||||
@@ -96,7 +91,6 @@ export type Modal =
|
|||||||
|
|
||||||
// Generic
|
// Generic
|
||||||
| LinkWarningModal
|
| LinkWarningModal
|
||||||
| InAppBrowserConsentModal
|
|
||||||
|
|
||||||
const ModalContext = React.createContext<{
|
const ModalContext = React.createContext<{
|
||||||
isModalActive: boolean
|
isModalActive: boolean
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
import React from 'react'
|
|
||||||
import {StyleSheet, View} from 'react-native'
|
|
||||||
import {msg, Trans} from '@lingui/macro'
|
|
||||||
import {useLingui} from '@lingui/react'
|
|
||||||
|
|
||||||
import {useOpenLink} from '#/lib/hooks/useOpenLink'
|
|
||||||
import {usePalette} from '#/lib/hooks/usePalette'
|
|
||||||
import {s} from '#/lib/styles'
|
|
||||||
import {useModalControls} from '#/state/modals'
|
|
||||||
import {useSetInAppBrowser} from '#/state/preferences/in-app-browser'
|
|
||||||
import {ScrollView} from '#/view/com/modals/util'
|
|
||||||
import {Button} from '#/view/com/util/forms/Button'
|
|
||||||
import {Text} from '#/view/com/util/text/Text'
|
|
||||||
|
|
||||||
export const snapPoints = [350]
|
|
||||||
|
|
||||||
export function Component({href}: {href: string}) {
|
|
||||||
const pal = usePalette('default')
|
|
||||||
const {closeModal} = useModalControls()
|
|
||||||
const {_} = useLingui()
|
|
||||||
const setInAppBrowser = useSetInAppBrowser()
|
|
||||||
const openLink = useOpenLink()
|
|
||||||
|
|
||||||
const onUseIAB = React.useCallback(() => {
|
|
||||||
setInAppBrowser(true)
|
|
||||||
closeModal()
|
|
||||||
openLink(href, true)
|
|
||||||
}, [closeModal, setInAppBrowser, href, openLink])
|
|
||||||
|
|
||||||
const onUseLinking = React.useCallback(() => {
|
|
||||||
setInAppBrowser(false)
|
|
||||||
closeModal()
|
|
||||||
openLink(href, false)
|
|
||||||
}, [closeModal, setInAppBrowser, href, openLink])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ScrollView
|
|
||||||
testID="inAppBrowserConsentModal"
|
|
||||||
style={[s.flex1, pal.view, {paddingHorizontal: 20, paddingTop: 10}]}>
|
|
||||||
<Text style={[pal.text, styles.title]}>
|
|
||||||
<Trans>How should we open this link?</Trans>
|
|
||||||
</Text>
|
|
||||||
<Text style={pal.text}>
|
|
||||||
<Trans>
|
|
||||||
Your choice will be saved, but can be changed later in settings.
|
|
||||||
</Trans>
|
|
||||||
</Text>
|
|
||||||
<View style={[styles.btnContainer]}>
|
|
||||||
<Button
|
|
||||||
testID="confirmBtn"
|
|
||||||
type="inverted"
|
|
||||||
onPress={onUseIAB}
|
|
||||||
accessibilityLabel={_(msg`Use in-app browser`)}
|
|
||||||
accessibilityHint=""
|
|
||||||
label={_(msg`Use in-app browser`)}
|
|
||||||
labelContainerStyle={{justifyContent: 'center', padding: 8}}
|
|
||||||
labelStyle={[s.f18]}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
testID="confirmBtn"
|
|
||||||
type="inverted"
|
|
||||||
onPress={onUseLinking}
|
|
||||||
accessibilityLabel={_(msg`Use my default browser`)}
|
|
||||||
accessibilityHint=""
|
|
||||||
label={_(msg`Use my default browser`)}
|
|
||||||
labelContainerStyle={{justifyContent: 'center', padding: 8}}
|
|
||||||
labelStyle={[s.f18]}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
testID="cancelBtn"
|
|
||||||
type="default"
|
|
||||||
onPress={() => {
|
|
||||||
closeModal()
|
|
||||||
}}
|
|
||||||
accessibilityLabel={_(msg`Cancel`)}
|
|
||||||
accessibilityHint=""
|
|
||||||
label={_(msg`Cancel`)}
|
|
||||||
labelContainerStyle={{justifyContent: 'center', padding: 8}}
|
|
||||||
labelStyle={[s.f18]}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
</ScrollView>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
title: {
|
|
||||||
textAlign: 'center',
|
|
||||||
fontWeight: '600',
|
|
||||||
fontSize: 24,
|
|
||||||
marginBottom: 12,
|
|
||||||
},
|
|
||||||
btnContainer: {
|
|
||||||
marginTop: 20,
|
|
||||||
flexDirection: 'column',
|
|
||||||
justifyContent: 'center',
|
|
||||||
rowGap: 10,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -11,7 +11,6 @@ import * as ChangePasswordModal from './ChangePassword'
|
|||||||
import * as CreateOrEditListModal from './CreateOrEditList'
|
import * as CreateOrEditListModal from './CreateOrEditList'
|
||||||
import * as DeleteAccountModal from './DeleteAccount'
|
import * as DeleteAccountModal from './DeleteAccount'
|
||||||
import * as EditProfileModal from './EditProfile'
|
import * as EditProfileModal from './EditProfile'
|
||||||
import * as InAppBrowserConsentModal from './InAppBrowserConsent'
|
|
||||||
import * as InviteCodesModal from './InviteCodes'
|
import * as InviteCodesModal from './InviteCodes'
|
||||||
import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguagesSettings'
|
import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguagesSettings'
|
||||||
import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettings'
|
import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettings'
|
||||||
@@ -76,9 +75,6 @@ export function ModalsContainer() {
|
|||||||
} else if (activeModal?.name === 'link-warning') {
|
} else if (activeModal?.name === 'link-warning') {
|
||||||
snapPoints = LinkWarningModal.snapPoints
|
snapPoints = LinkWarningModal.snapPoints
|
||||||
element = <LinkWarningModal.Component {...activeModal} />
|
element = <LinkWarningModal.Component {...activeModal} />
|
||||||
} else if (activeModal?.name === 'in-app-browser-consent') {
|
|
||||||
snapPoints = InAppBrowserConsentModal.snapPoints
|
|
||||||
element = <InAppBrowserConsentModal.Component {...activeModal} />
|
|
||||||
} else {
|
} else {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {ModalsContainer} from '#/view/com/modals/Modal'
|
|||||||
import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
|
import {ErrorBoundary} from '#/view/com/util/ErrorBoundary'
|
||||||
import {atoms as a, select, useTheme} from '#/alf'
|
import {atoms as a, select, useTheme} from '#/alf'
|
||||||
import {setSystemUITheme} from '#/alf/util/systemUI'
|
import {setSystemUITheme} from '#/alf/util/systemUI'
|
||||||
|
import {InAppBrowserConsentDialog} from '#/components/dialogs/InAppBrowserConsent'
|
||||||
import {MutedWordsDialog} from '#/components/dialogs/MutedWords'
|
import {MutedWordsDialog} from '#/components/dialogs/MutedWords'
|
||||||
import {SigninDialog} from '#/components/dialogs/Signin'
|
import {SigninDialog} from '#/components/dialogs/Signin'
|
||||||
import {Outlet as PortalOutlet} from '#/components/Portal'
|
import {Outlet as PortalOutlet} from '#/components/Portal'
|
||||||
@@ -151,6 +152,7 @@ function ShellInner() {
|
|||||||
<ModalsContainer />
|
<ModalsContainer />
|
||||||
<MutedWordsDialog />
|
<MutedWordsDialog />
|
||||||
<SigninDialog />
|
<SigninDialog />
|
||||||
|
<InAppBrowserConsentDialog />
|
||||||
<Lightbox />
|
<Lightbox />
|
||||||
<PortalOutlet />
|
<PortalOutlet />
|
||||||
<BottomSheetOutlet />
|
<BottomSheetOutlet />
|
||||||
|
|||||||
Reference in New Issue
Block a user