WIP custom dialog

This commit is contained in:
Eric Bailey
2025-08-01 12:54:39 -05:00
parent b57eef7050
commit c6a82c8e8a
5 changed files with 163 additions and 20 deletions
@@ -0,0 +1,147 @@
import {createContext, useMemo, useContext, useState, ReactNode} from 'react'
import {View, ScrollView, useWindowDimensions} from 'react-native'
// import {FocusScope} from 'radix-ui/internal'
// import {RemoveScrollBar} from 'react-remove-scroll-bar'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {LinearGradient} from 'expo-linear-gradient'
import {isNative} from '#/platform/detection'
import {atoms as a, useTheme, useBreakpoints, web, flatten} from '#/alf'
import {transparentifyColor} from '#/alf/util/colorGeneration'
import {useA11y} from '#/state/a11y'
const GUTTER = 24
export const Context = createContext({
close: () => {},
})
export function useAnnouncementDialogContext() {
return useContext(Context)
}
export function AnnouncementDialogOuter({children}: {children: ReactNode}) {
const [open, setOpen] = useState(true)
const ctx = useMemo(
() => ({
close() {
setOpen(false)
},
}),
[setOpen],
)
return <Context.Provider value={ctx}>{open && children}</Context.Provider>
}
export function AnnouncementDialog({
children,
label,
}: {
children: ReactNode
label: string
}) {
const t = useTheme()
const {gtPhone} = useBreakpoints()
const {reduceMotionEnabled} = useA11y()
const {height} = useWindowDimensions()
const insets = useSafeAreaInsets()
return (
<>
<View style={[a.fixed, a.inset_0, !reduceMotionEnabled && a.fade_in]}>
<LinearGradient
colors={[
transparentifyColor(t.atoms.bg.backgroundColor, 0),
t.atoms.bg.backgroundColor,
t.atoms.bg.backgroundColor,
]}
start={[0.5, 0]}
end={[0.5, 1]}
style={[a.absolute, a.inset_0]}
/>
</View>
{/* <RemoveScrollBar /> */}
<ScrollView
showsVerticalScrollIndicator={false}
style={[a.fixed, a.inset_0, a.z_10]}
contentContainerStyle={[
a.align_center,
gtPhone &&
web({
paddingHorizontal: GUTTER,
paddingVertical: '10vh',
}),
]}>
{/**
* This is needed to prevent centered dialogs from overflowing
* above the screen, and provides a "natural" centering so that
* stacked dialogs appear relatively aligned.
*/}
<View
style={[
a.w_full,
a.z_20,
a.align_center,
!gtPhone && a.justify_end,
{minHeight: height},
isNative && [
{
paddingBottom: Math.max(insets.bottom, a.p_2xl.padding),
},
],
]}>
{/* <FocusScope.FocusScope loop asChild trapped> */}
{!gtPhone && (
<View
style={[
a.flex_1,
a.w_full,
{
minHeight: Math.max(insets.top, a.p_2xl.padding),
},
]}>
<LinearGradient
colors={[
transparentifyColor(t.atoms.bg.backgroundColor, 0),
t.atoms.bg.backgroundColor,
]}
start={[0.5, 0]}
end={[0.5, 1]}
style={[a.absolute, a.inset_0]}
/>
</View>
)}
<View
role="dialog"
aria-role="dialog"
aria-label={label}
style={flatten([
a.relative,
a.w_full,
a.p_2xl,
t.atoms.bg,
!reduceMotionEnabled && a.zoom_fade_in,
gtPhone && [
a.rounded_md,
a.border,
t.atoms.shadow_lg,
t.atoms.border_contrast_low,
web({
maxWidth: 420,
}),
],
])}>
{children}
</View>
{/* </FocusScope.FocusScope> */}
</View>
</ScrollView>
</>
)
}
@@ -4,16 +4,15 @@ import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {Nux} from '#/state/queries/nuxs'
import {atoms as a, useTheme, web} from '#/alf'
import {atoms as a, useTheme} from '#/alf'
import {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {
AnnouncementBadge,
useAnnouncementState,
useForceClose,
} from '#/components/dialogs/BlockingAnnouncements/common'
import {InlineLinkText} from '#/components/Link'
import {Span, Text} from '#/components/Typography'
import {AnnouncementDialog, useAnnouncementDialogContext} from '#/components/dialogs/BlockingAnnouncements/AnnouncementDialog'
export function useLocalState() {
return useAnnouncementState({
@@ -24,12 +23,12 @@ export function useLocalState() {
export function Announcement() {
const t = useTheme()
const {_} = useLingui()
const forceClose = useForceClose()
const {complete} = useLocalState()
const {close} = useAnnouncementDialogContext()
const handleClose = useCallback(() => {
forceClose(() => complete())
}, [forceClose, complete])
close()
}, [close, complete])
const linkStyle = [a.text_md]
const links = {
@@ -66,9 +65,7 @@ export function Announcement() {
}
return (
<Dialog.ScrollableInner
label={_(msg`Were updating our policies`)}
style={[web({maxWidth: 420})]}>
<AnnouncementDialog label={_(msg`We're updating our policies`)}>
<View style={[a.align_start, a.gap_xl]}>
<AnnouncementBadge />
@@ -136,6 +133,6 @@ export function Announcement() {
</Text>
</View>
</View>
</Dialog.ScrollableInner>
</AnnouncementDialog>
)
}
@@ -1,4 +1,4 @@
import {BlockingAnnouncementDialogOuter} from '#/components/dialogs/BlockingAnnouncements/common'
import {AnnouncementDialogOuter} from '#/components/dialogs/BlockingAnnouncements/AnnouncementDialog'
import * as PolicyUpdate20250801 from '#/components/dialogs/BlockingAnnouncements/PolicyUpdate20250801'
export function BlockingAnnouncements() {
@@ -9,13 +9,11 @@ export function BlockingAnnouncements() {
* NUX state for local testing and debugging.
*/
if (policyUpdate20250801.completed) return null
return (
<>
{!policyUpdate20250801.completed && (
<BlockingAnnouncementDialogOuter>
<PolicyUpdate20250801.Announcement />
</BlockingAnnouncementDialogOuter>
)}
</>
<AnnouncementDialogOuter>
<PolicyUpdate20250801.Announcement />
</AnnouncementDialogOuter>
)
}
+1 -1
View File
@@ -156,7 +156,6 @@ function ShellInner() {
<ModalsContainer />
<MutedWordsDialog />
<SigninDialog />
<BlockingAnnouncements />
<EmailDialog />
<AgeAssuranceRedirectDialog />
<InAppBrowserConsentDialog />
@@ -164,6 +163,7 @@ function ShellInner() {
<Lightbox />
<PortalOutlet />
<BottomSheetOutlet />
<BlockingAnnouncements />
</>
)
}
+2 -1
View File
@@ -71,7 +71,6 @@ function ShellInner() {
<ModalsContainer />
<MutedWordsDialog />
<SigninDialog />
<BlockingAnnouncements />
<EmailDialog />
<AgeAssuranceRedirectDialog />
<LinkWarningDialog />
@@ -115,6 +114,8 @@ function ShellInner() {
</TouchableWithoutFeedback>
</>
)}
<BlockingAnnouncements />
</>
)
}