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 {useLingui} from '@lingui/react'
import {Nux} from '#/state/queries/nuxs' 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 {Button, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import { import {
AnnouncementBadge, AnnouncementBadge,
useAnnouncementState, useAnnouncementState,
useForceClose,
} from '#/components/dialogs/BlockingAnnouncements/common' } from '#/components/dialogs/BlockingAnnouncements/common'
import {InlineLinkText} from '#/components/Link' import {InlineLinkText} from '#/components/Link'
import {Span, Text} from '#/components/Typography' import {Span, Text} from '#/components/Typography'
import {AnnouncementDialog, useAnnouncementDialogContext} from '#/components/dialogs/BlockingAnnouncements/AnnouncementDialog'
export function useLocalState() { export function useLocalState() {
return useAnnouncementState({ return useAnnouncementState({
@@ -24,12 +23,12 @@ export function useLocalState() {
export function Announcement() { export function Announcement() {
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {_} = useLingui()
const forceClose = useForceClose()
const {complete} = useLocalState() const {complete} = useLocalState()
const {close} = useAnnouncementDialogContext()
const handleClose = useCallback(() => { const handleClose = useCallback(() => {
forceClose(() => complete()) close()
}, [forceClose, complete]) }, [close, complete])
const linkStyle = [a.text_md] const linkStyle = [a.text_md]
const links = { const links = {
@@ -66,9 +65,7 @@ export function Announcement() {
} }
return ( return (
<Dialog.ScrollableInner <AnnouncementDialog label={_(msg`We're updating our policies`)}>
label={_(msg`Were updating our policies`)}
style={[web({maxWidth: 420})]}>
<View style={[a.align_start, a.gap_xl]}> <View style={[a.align_start, a.gap_xl]}>
<AnnouncementBadge /> <AnnouncementBadge />
@@ -136,6 +133,6 @@ export function Announcement() {
</Text> </Text>
</View> </View>
</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' import * as PolicyUpdate20250801 from '#/components/dialogs/BlockingAnnouncements/PolicyUpdate20250801'
export function BlockingAnnouncements() { export function BlockingAnnouncements() {
@@ -9,13 +9,11 @@ export function BlockingAnnouncements() {
* NUX state for local testing and debugging. * NUX state for local testing and debugging.
*/ */
if (policyUpdate20250801.completed) return null
return ( return (
<> <AnnouncementDialogOuter>
{!policyUpdate20250801.completed && (
<BlockingAnnouncementDialogOuter>
<PolicyUpdate20250801.Announcement /> <PolicyUpdate20250801.Announcement />
</BlockingAnnouncementDialogOuter> </AnnouncementDialogOuter>
)}
</>
) )
} }
+1 -1
View File
@@ -156,7 +156,6 @@ function ShellInner() {
<ModalsContainer /> <ModalsContainer />
<MutedWordsDialog /> <MutedWordsDialog />
<SigninDialog /> <SigninDialog />
<BlockingAnnouncements />
<EmailDialog /> <EmailDialog />
<AgeAssuranceRedirectDialog /> <AgeAssuranceRedirectDialog />
<InAppBrowserConsentDialog /> <InAppBrowserConsentDialog />
@@ -164,6 +163,7 @@ function ShellInner() {
<Lightbox /> <Lightbox />
<PortalOutlet /> <PortalOutlet />
<BottomSheetOutlet /> <BottomSheetOutlet />
<BlockingAnnouncements />
</> </>
) )
} }
+2 -1
View File
@@ -71,7 +71,6 @@ function ShellInner() {
<ModalsContainer /> <ModalsContainer />
<MutedWordsDialog /> <MutedWordsDialog />
<SigninDialog /> <SigninDialog />
<BlockingAnnouncements />
<EmailDialog /> <EmailDialog />
<AgeAssuranceRedirectDialog /> <AgeAssuranceRedirectDialog />
<LinkWarningDialog /> <LinkWarningDialog />
@@ -115,6 +114,8 @@ function ShellInner() {
</TouchableWithoutFeedback> </TouchableWithoutFeedback>
</> </>
)} )}
<BlockingAnnouncements />
</> </>
) )
} }