Clean up state, remove Portal edits
This commit is contained in:
@@ -73,7 +73,7 @@ export function createPortalGroup() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return {Provider, Outlet, Portal, Context}
|
return {Provider, Outlet, Portal}
|
||||||
}
|
}
|
||||||
|
|
||||||
const DefaultPortal = createPortalGroup()
|
const DefaultPortal = createPortalGroup()
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import {View} from 'react-native'
|
||||||
|
import {Trans} from '@lingui/macro'
|
||||||
|
|
||||||
|
import {Logo} from '#/view/icons/Logo'
|
||||||
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
|
export function AnnouncementBadge() {
|
||||||
|
const t = useTheme()
|
||||||
|
return (
|
||||||
|
<View style={[a.align_start]}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
a.pl_md,
|
||||||
|
a.pr_lg,
|
||||||
|
a.py_sm,
|
||||||
|
a.rounded_full,
|
||||||
|
a.flex_row,
|
||||||
|
a.align_center,
|
||||||
|
a.gap_xs,
|
||||||
|
{
|
||||||
|
backgroundColor: t.palette.primary_25,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<Logo fill={t.palette.primary_600} width={14} />
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
a.font_bold,
|
||||||
|
{
|
||||||
|
color: t.palette.primary_600,
|
||||||
|
},
|
||||||
|
]}>
|
||||||
|
<Trans>Announcement</Trans>
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,10 +1,4 @@
|
|||||||
import {
|
import {type ReactNode} from 'react'
|
||||||
createContext,
|
|
||||||
type ReactNode,
|
|
||||||
useContext,
|
|
||||||
useMemo,
|
|
||||||
useState,
|
|
||||||
} from 'react'
|
|
||||||
import {ScrollView, View} from 'react-native'
|
import {ScrollView, View} from 'react-native'
|
||||||
import {
|
import {
|
||||||
useSafeAreaFrame,
|
useSafeAreaFrame,
|
||||||
@@ -21,29 +15,6 @@ import {LockScroll} from '#/components/LockScroll'
|
|||||||
|
|
||||||
const GUTTER = 24
|
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({
|
export function AnnouncementDialog({
|
||||||
children,
|
children,
|
||||||
label,
|
label,
|
||||||
|
|||||||
@@ -4,37 +4,23 @@ import {msg, Trans} from '@lingui/macro'
|
|||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
|
||||||
import {isAndroid} from '#/platform/detection'
|
import {isAndroid} from '#/platform/detection'
|
||||||
import {Nux} from '#/state/queries/nuxs'
|
import {useA11y} from '#/state/a11y'
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {Button, ButtonText} from '#/components/Button'
|
import {Button, ButtonText} from '#/components/Button'
|
||||||
import {
|
import {AnnouncementBadge} from '#/components/dialogs/BlockingAnnouncements/AnnouncementBadge'
|
||||||
AnnouncementBadge,
|
import {AnnouncementDialog} from '#/components/dialogs/BlockingAnnouncements/AnnouncementDialog'
|
||||||
useAnnouncementState,
|
import {type AnnouncementState} from '#/components/dialogs/BlockingAnnouncements/useAnnouncementState'
|
||||||
} from '#/components/dialogs/BlockingAnnouncements/common'
|
|
||||||
import {InlineLinkText, Link} from '#/components/Link'
|
import {InlineLinkText, Link} from '#/components/Link'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {
|
|
||||||
AnnouncementDialog,
|
|
||||||
useAnnouncementDialogContext,
|
|
||||||
} from '#/components/dialogs/BlockingAnnouncements/AnnouncementDialog'
|
|
||||||
import {useA11y} from '#/state/a11y'
|
|
||||||
|
|
||||||
export function useLocalState() {
|
export function Announcement({state}: {state: AnnouncementState}) {
|
||||||
return useAnnouncementState({
|
|
||||||
id: Nux.BlockingAnnouncementPolicyUpdate20250801,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function Announcement() {
|
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {complete} = useLocalState()
|
|
||||||
const {close} = useAnnouncementDialogContext()
|
|
||||||
const {screenReaderEnabled} = useA11y()
|
const {screenReaderEnabled} = useA11y()
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
close()
|
state.complete()
|
||||||
}, [close, complete])
|
}, [state])
|
||||||
|
|
||||||
const linkStyle = [a.text_md]
|
const linkStyle = [a.text_md]
|
||||||
const links = {
|
const links = {
|
||||||
|
|||||||
@@ -1,94 +0,0 @@
|
|||||||
import {type ReactNode, useCallback, useMemo} from 'react'
|
|
||||||
import {View} from 'react-native'
|
|
||||||
import {Trans} from '@lingui/macro'
|
|
||||||
|
|
||||||
import {type Nux, useNux, useSaveNux} from '#/state/queries/nuxs'
|
|
||||||
import {Logo} from '#/view/icons/Logo'
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
|
||||||
import * as Dialog from '#/components/Dialog'
|
|
||||||
import {useGlobalDialogsControlContext} from '#/components/dialogs/Context'
|
|
||||||
import {Text} from '#/components/Typography'
|
|
||||||
|
|
||||||
export function useForceClose() {
|
|
||||||
const {close} = Dialog.useDialogContext()
|
|
||||||
return useCallback(
|
|
||||||
(cb?: () => void) => {
|
|
||||||
close(cb, true)
|
|
||||||
},
|
|
||||||
[close],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useAnnouncementState({id}: {id: Nux}) {
|
|
||||||
const nux = useNux(id)
|
|
||||||
const {mutate: save, variables} = useSaveNux()
|
|
||||||
const optimisticallyCompleted = !!variables?.completed
|
|
||||||
return useMemo(
|
|
||||||
() => ({
|
|
||||||
/**
|
|
||||||
* Until data has loaded, assumed completed
|
|
||||||
*/
|
|
||||||
completed:
|
|
||||||
optimisticallyCompleted || nux.status === 'ready'
|
|
||||||
? nux.nux?.completed === true
|
|
||||||
: true,
|
|
||||||
complete() {
|
|
||||||
save({
|
|
||||||
id,
|
|
||||||
completed: true,
|
|
||||||
data: undefined,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
[id, nux, save, optimisticallyCompleted],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function BlockingAnnouncementDialogOuter({
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
children: ReactNode
|
|
||||||
}) {
|
|
||||||
const {signinDialogControl: control} = useGlobalDialogsControlContext()
|
|
||||||
|
|
||||||
Dialog.useAutoOpen(control, 1e3)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog.Outer preventDismiss control={control}>
|
|
||||||
<Dialog.Handle />
|
|
||||||
{children}
|
|
||||||
</Dialog.Outer>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function AnnouncementBadge() {
|
|
||||||
const t = useTheme()
|
|
||||||
return (
|
|
||||||
<View style={[a.align_start]}>
|
|
||||||
<View
|
|
||||||
style={[
|
|
||||||
a.pl_md,
|
|
||||||
a.pr_lg,
|
|
||||||
a.py_sm,
|
|
||||||
a.rounded_full,
|
|
||||||
a.flex_row,
|
|
||||||
a.align_center,
|
|
||||||
a.gap_xs,
|
|
||||||
{
|
|
||||||
backgroundColor: t.palette.primary_25,
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
<Logo fill={t.palette.primary_600} width={14} />
|
|
||||||
<Text
|
|
||||||
style={[
|
|
||||||
a.font_bold,
|
|
||||||
{
|
|
||||||
color: t.palette.primary_600,
|
|
||||||
},
|
|
||||||
]}>
|
|
||||||
<Trans>Announcement</Trans>
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1,38 +1,34 @@
|
|||||||
import {useContext} from 'react'
|
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
|
|
||||||
|
import {Nux} from '#/state/queries/nuxs'
|
||||||
import {atoms as a} from '#/alf'
|
import {atoms as a} from '#/alf'
|
||||||
import {AnnouncementDialogOuter} from '#/components/dialogs/BlockingAnnouncements/AnnouncementDialog'
|
|
||||||
import * as PolicyUpdate20250801 from '#/components/dialogs/BlockingAnnouncements/PolicyUpdate20250801'
|
import * as PolicyUpdate20250801 from '#/components/dialogs/BlockingAnnouncements/PolicyUpdate20250801'
|
||||||
|
import {useAnnouncementState} from '#/components/dialogs/BlockingAnnouncements/useAnnouncementState'
|
||||||
import {createPortalGroup} from '#/components/Portal'
|
import {createPortalGroup} from '#/components/Portal'
|
||||||
|
|
||||||
const portalGroup = createPortalGroup()
|
const portalGroup = createPortalGroup()
|
||||||
|
|
||||||
export const Provider = portalGroup.Provider
|
export const Provider = portalGroup.Provider
|
||||||
export const Portal = portalGroup.Portal
|
export const Portal = portalGroup.Portal
|
||||||
|
export const Outlet = portalGroup.Outlet
|
||||||
export function Outlet() {
|
|
||||||
const {outlet} = useContext(portalGroup.Context)
|
|
||||||
return outlet ? (
|
|
||||||
<View style={[a.fixed, a.inset_0, {zIndex: 9999}]}>{outlet}</View>
|
|
||||||
) : null
|
|
||||||
}
|
|
||||||
|
|
||||||
export function BlockingAnnouncements() {
|
export function BlockingAnnouncements() {
|
||||||
const policyUpdate20250801 = PolicyUpdate20250801.useLocalState()
|
const state = useAnnouncementState({
|
||||||
|
id: Nux.BlockingAnnouncementPolicyUpdate20250801,
|
||||||
|
})
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* See `window.clearNux` example in `/state/queries/nuxs` for a way to clear
|
* See `window.clearNux` example in `/state/queries/nuxs` for a way to clear
|
||||||
* NUX state for local testing and debugging.
|
* NUX state for local testing and debugging.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (policyUpdate20250801.completed) return null
|
if (state.completed) return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Portal>
|
<Portal>
|
||||||
<AnnouncementDialogOuter>
|
<View style={[a.fixed, a.inset_0, {zIndex: 9999}]}>
|
||||||
<PolicyUpdate20250801.Announcement />
|
<PolicyUpdate20250801.Announcement state={state} />
|
||||||
</AnnouncementDialogOuter>
|
</View>
|
||||||
</Portal>
|
</Portal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
import {useMemo} from 'react'
|
||||||
|
|
||||||
|
import {type Nux, useNux, useSaveNux} from '#/state/queries/nuxs'
|
||||||
|
|
||||||
|
export type AnnouncementState = {
|
||||||
|
completed: boolean
|
||||||
|
complete: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useAnnouncementState({id}: {id: Nux}) {
|
||||||
|
const nux = useNux(id)
|
||||||
|
const {mutate: save, variables} = useSaveNux()
|
||||||
|
return useMemo(() => {
|
||||||
|
/**
|
||||||
|
* Until data has loaded, assumed completed
|
||||||
|
*/
|
||||||
|
let completed = nux.status === 'ready' ? nux.nux?.completed === true : true
|
||||||
|
|
||||||
|
if (variables?.completed) {
|
||||||
|
completed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
completed,
|
||||||
|
complete() {
|
||||||
|
save({
|
||||||
|
id,
|
||||||
|
completed: true,
|
||||||
|
data: undefined,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}, [id, nux, save, variables])
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user