forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import React from 'react'
2
3import {type DialogControlRefProps} from '#/components/Dialog'
4import {Provider as GlobalDialogsProvider} from '#/components/dialogs/Context'
5import {IS_WEB} from '#/env'
6import {BottomSheetNativeComponent} from '../../../modules/bottom-sheet'
7
8interface IDialogContext {
9 /**
10 * The currently active `useDialogControl` hooks.
11 */
12 activeDialogs: React.MutableRefObject<
13 Map<string, React.MutableRefObject<DialogControlRefProps>>
14 >
15 /**
16 * The currently open dialogs, referenced by their IDs, generated from
17 * `useId`.
18 */
19 openDialogs: React.MutableRefObject<Set<string>>
20}
21
22interface IDialogControlContext {
23 closeAllDialogs(): boolean
24 setDialogIsOpen(id: string, isOpen: boolean): void
25 setFullyExpandedCount: React.Dispatch<React.SetStateAction<number>>
26}
27
28const DialogContext = React.createContext<IDialogContext>({} as IDialogContext)
29DialogContext.displayName = 'DialogContext'
30
31const DialogControlContext = React.createContext<IDialogControlContext>(
32 {} as IDialogControlContext,
33)
34DialogControlContext.displayName = 'DialogControlContext'
35
36/**
37 * The number of dialogs that are fully expanded. This is used to determine the background color of the status bar
38 * on iOS.
39 */
40const DialogFullyExpandedCountContext = React.createContext<number>(0)
41DialogFullyExpandedCountContext.displayName = 'DialogFullyExpandedCountContext'
42
43export function useDialogStateContext() {
44 return React.useContext(DialogContext)
45}
46
47export function useDialogStateControlContext() {
48 return React.useContext(DialogControlContext)
49}
50
51/** The number of dialogs that are fully expanded */
52export function useDialogFullyExpandedCountContext() {
53 return React.useContext(DialogFullyExpandedCountContext)
54}
55
56export function Provider({children}: React.PropsWithChildren<{}>) {
57 const [fullyExpandedCount, setFullyExpandedCount] = React.useState(0)
58
59 const activeDialogs = React.useRef<
60 Map<string, React.MutableRefObject<DialogControlRefProps>>
61 >(new Map())
62 const openDialogs = React.useRef<Set<string>>(new Set())
63
64 const closeAllDialogs = React.useCallback(() => {
65 if (IS_WEB) {
66 openDialogs.current.forEach(id => {
67 const dialog = activeDialogs.current.get(id)
68 if (dialog) dialog.current.close()
69 })
70
71 return openDialogs.current.size > 0
72 } else {
73 BottomSheetNativeComponent.dismissAll()
74 return false
75 }
76 }, [])
77
78 const setDialogIsOpen = React.useCallback((id: string, isOpen: boolean) => {
79 if (isOpen) {
80 openDialogs.current.add(id)
81 } else {
82 openDialogs.current.delete(id)
83 }
84 }, [])
85
86 const context = React.useMemo<IDialogContext>(
87 () => ({
88 activeDialogs,
89 openDialogs,
90 }),
91 [activeDialogs, openDialogs],
92 )
93 const controls = React.useMemo(
94 () => ({
95 closeAllDialogs,
96 setDialogIsOpen,
97 setFullyExpandedCount,
98 }),
99 [closeAllDialogs, setDialogIsOpen, setFullyExpandedCount],
100 )
101
102 return (
103 <DialogContext.Provider value={context}>
104 <DialogControlContext.Provider value={controls}>
105 <DialogFullyExpandedCountContext.Provider value={fullyExpandedCount}>
106 <GlobalDialogsProvider>{children}</GlobalDialogsProvider>
107 </DialogFullyExpandedCountContext.Provider>
108 </DialogControlContext.Provider>
109 </DialogContext.Provider>
110 )
111}
112Provider.displayName = 'DialogsProvider'