From 2508f955084c7add2ab9c5bf8ee674ab88150fe6 Mon Sep 17 00:00:00 2001 From: Samuel Newman Date: Wed, 18 Feb 2026 19:13:27 +0200 Subject: [PATCH] rip out modal system --- src/App.native.tsx | 31 ++++----- src/App.web.tsx | 21 +++--- src/components/Link.tsx | 5 -- src/state/modals/index.tsx | 105 ----------------------------- src/state/util.ts | 17 +---- src/view/com/composer/Composer.tsx | 6 +- src/view/com/modals/Modal.tsx | 91 ------------------------- src/view/com/modals/Modal.web.tsx | 101 --------------------------- src/view/com/modals/util.tsx | 4 -- src/view/com/modals/util.web.tsx | 1 - src/view/com/util/Link.tsx | 10 --- src/view/shell/Composer.web.tsx | 9 +-- src/view/shell/index.tsx | 2 - src/view/shell/index.web.tsx | 2 - 14 files changed, 28 insertions(+), 377 deletions(-) delete mode 100644 src/state/modals/index.tsx delete mode 100644 src/view/com/modals/Modal.tsx delete mode 100644 src/view/com/modals/Modal.web.tsx delete mode 100644 src/view/com/modals/util.tsx delete mode 100644 src/view/com/modals/util.web.tsx diff --git a/src/App.native.tsx b/src/App.native.tsx index 22dd39f9d7..d41f28bdb5 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -31,7 +31,6 @@ import {listenSessionDropped} from '#/state/events' import {GlobalGestureEventsProvider} from '#/state/global-gesture-events' import {Provider as HomeBadgeProvider} from '#/state/home-badge' import {MessagesProvider} from '#/state/messages' -import {Provider as ModalStateProvider} from '#/state/modals' import {init as initPersistedState} from '#/state/persisted' import {Provider as PrefsStateProvider} from '#/state/preferences' import {Provider as LabelDefsProvider} from '#/state/preferences/label-defs' @@ -241,22 +240,20 @@ function App() { - - - - - - - - - - - - - - - + + + + + + + + + + + + + diff --git a/src/App.web.tsx b/src/App.web.tsx index 6faf6f2218..e962728859 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -24,7 +24,6 @@ import {Provider as EmailVerificationProvider} from '#/state/email-verification' import {listenSessionDropped} from '#/state/events' import {Provider as HomeBadgeProvider} from '#/state/home-badge' import {MessagesProvider} from '#/state/messages' -import {Provider as ModalStateProvider} from '#/state/modals' import {init as initPersistedState} from '#/state/persisted' import {Provider as PrefsStateProvider} from '#/state/preferences' import {Provider as LabelDefsProvider} from '#/state/preferences/label-defs' @@ -220,17 +219,15 @@ function App() { - - - - - - - - - - - + + + + + + + + + diff --git a/src/components/Link.tsx b/src/components/Link.tsx index 258ba58ca4..0818b2a26f 100644 --- a/src/components/Link.tsx +++ b/src/components/Link.tsx @@ -26,7 +26,6 @@ import { isExternalUrl, linkRequiresWarning, } from '#/lib/strings/url-helpers' -import {useModalControls} from '#/state/modals' import {useInAppBrowser} from '#/state/preferences/in-app-browser' import {atoms as a, flatten, type TextStyleProp, useTheme, web} from '#/alf' import {Button, type ButtonProps} from '#/components/Button' @@ -140,7 +139,6 @@ export function useLink({ } const isExternal = isExternalUrl(href) - const {closeModal} = useModalControls() const {linkWarningDialogControl} = useGlobalDialogsControlContext() const openLink = useOpenLink() const groupChatJoinIntent = useGroupChatJoinIntent() @@ -188,8 +186,6 @@ export function useLink({ ) { void openLink(href) } else { - closeModal() // close any active modals - const [screen, params] = router.matchPath(href) as [ screen: keyof AllNavigatorParams, params?: RouteParams, @@ -242,7 +238,6 @@ export function useLink({ isExternal, href, openLink, - closeModal, action, navigation, overridePresentation, diff --git a/src/state/modals/index.tsx b/src/state/modals/index.tsx deleted file mode 100644 index d62ba2a7a0..0000000000 --- a/src/state/modals/index.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import {createContext, useContext, useEffect, useMemo, useState} from 'react' - -import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' -import {useHotkeysContext} from '#/lib/hotkeys' - -export interface ContentLanguagesSettingsModal { - name: 'content-languages-settings' -} - -/** - * @deprecated DO NOT ADD NEW MODALS - */ -export type Modal = - // Curation - ContentLanguagesSettingsModal - -const ModalContext = createContext<{ - isModalActive: boolean - activeModals: Modal[] -}>({ - isModalActive: false, - activeModals: [], -}) -ModalContext.displayName = 'ModalContext' - -const ModalControlContext = createContext<{ - openModal: (modal: Modal) => void - closeModal: () => boolean - closeAllModals: () => boolean -}>({ - openModal: () => {}, - closeModal: () => false, - closeAllModals: () => false, -}) -ModalControlContext.displayName = 'ModalControlContext' - -export function Provider({children}: React.PropsWithChildren<{}>) { - const [activeModals, setActiveModals] = useState([]) - const {disableScope, enableScope} = useHotkeysContext() - - useEffect(() => { - if (activeModals.length > 0) { - disableScope('global') - } else { - enableScope('global') - } - }, [activeModals.length, disableScope, enableScope]) - - const openModal = useNonReactiveCallback((modal: Modal) => { - setActiveModals(modals => [...modals, modal]) - }) - - const closeModal = useNonReactiveCallback(() => { - let wasActive = activeModals.length > 0 - setActiveModals(modals => { - return modals.slice(0, -1) - }) - return wasActive - }) - - const closeAllModals = useNonReactiveCallback(() => { - let wasActive = activeModals.length > 0 - setActiveModals([]) - return wasActive - }) - - const state = useMemo( - () => ({ - isModalActive: activeModals.length > 0, - activeModals, - }), - [activeModals], - ) - - const methods = useMemo( - () => ({ - openModal, - closeModal, - closeAllModals, - }), - [openModal, closeModal, closeAllModals], - ) - - return ( - - - {children} - - - ) -} - -/** - * @deprecated use the dialog system from `#/components/Dialog.tsx` - */ -export function useModals() { - return useContext(ModalContext) -} - -/** - * @deprecated use the dialog system from `#/components/Dialog.tsx` - */ -export function useModalControls() { - return useContext(ModalControlContext) -} diff --git a/src/state/util.ts b/src/state/util.ts index 239aab5aca..7924b103cf 100644 --- a/src/state/util.ts +++ b/src/state/util.ts @@ -2,7 +2,6 @@ import {useCallback} from 'react' import {useDialogStateControlContext} from '#/state/dialogs' import {useLightboxControls} from '#/components/Lightbox/state' -import {useModalControls} from './modals' import {useComposerControls} from './shell/composer' import {useSetDrawerOpen} from './shell/drawer-open' @@ -12,7 +11,6 @@ import {useSetDrawerOpen} from './shell/drawer-open' */ export function useCloseAnyActiveElement() { const {closeLightbox} = useLightboxControls() - const {closeModal} = useModalControls() const {closeComposer} = useComposerControls() const {closeAllDialogs} = useDialogStateControlContext() const setDrawerOpen = useSetDrawerOpen() @@ -20,9 +18,6 @@ export function useCloseAnyActiveElement() { if (closeLightbox()) { return true } - if (closeModal()) { - return true - } if (closeAllDialogs()) { return true } @@ -31,7 +26,7 @@ export function useCloseAnyActiveElement() { } setDrawerOpen(false) return false - }, [closeLightbox, closeModal, closeComposer, setDrawerOpen, closeAllDialogs]) + }, [closeLightbox, closeComposer, setDrawerOpen, closeAllDialogs]) } /** @@ -39,21 +34,13 @@ export function useCloseAnyActiveElement() { */ export function useCloseAllActiveElements() { const {closeLightbox} = useLightboxControls() - const {closeAllModals} = useModalControls() const {closeComposer} = useComposerControls() const {closeAllDialogs: closeAlfDialogs} = useDialogStateControlContext() const setDrawerOpen = useSetDrawerOpen() return useCallback(() => { closeLightbox() - closeAllModals() closeComposer() closeAlfDialogs() setDrawerOpen(false) - }, [ - closeLightbox, - closeAllModals, - closeComposer, - closeAlfDialogs, - setDrawerOpen, - ]) + }, [closeLightbox, closeComposer, closeAlfDialogs, setDrawerOpen]) } diff --git a/src/view/com/composer/Composer.tsx b/src/view/com/composer/Composer.tsx index 3ddaf41ed7..54932286be 100644 --- a/src/view/com/composer/Composer.tsx +++ b/src/view/com/composer/Composer.tsx @@ -86,7 +86,6 @@ import { createComposerImage, pasteImage, } from '#/state/gallery' -import {useModalControls} from '#/state/modals' import {useRequireAltTextEnabled} from '#/state/preferences' import { fromPostLanguages, @@ -283,7 +282,6 @@ export const ComposePost = ({ useSaveDraftMutation() const {mutate: cleanupPublishedDraft} = useCleanupPublishedDraftMutation() const {closeAllDialogs} = useDialogStateControlContext() - const {closeAllModals} = useModalControls() const {data: preferences} = usePreferencesQuery() const navigation = useNavigation() @@ -835,7 +833,7 @@ export const ComposePost = ({ const backHandler = BackHandler.addEventListener( 'hardwareBackPress', () => { - if (closeAllDialogs() || closeAllModals()) { + if (closeAllDialogs()) { return true } onPressCancel() @@ -845,7 +843,7 @@ export const ComposePost = ({ return () => { backHandler.remove() } - }, [onPressCancel, closeAllDialogs, closeAllModals]) + }, [onPressCancel, closeAllDialogs]) const missingAltError = useMemo(() => { if (!requireAltTextEnabled) { diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx deleted file mode 100644 index 96b04a1428..0000000000 --- a/src/view/com/modals/Modal.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import {Fragment, useEffect, useRef} from 'react' -import {StyleSheet} from 'react-native' -import {SafeAreaView} from 'react-native-safe-area-context' -import BottomSheet from '@discord/bottom-sheet/src' - -import {usePalette} from '#/lib/hooks/usePalette' -import {useModalControls, useModals} from '#/state/modals' -import {FullWindowOverlay} from '#/components/FullWindowOverlay' -import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop' - -const DEFAULT_SNAPPOINTS = ['90%'] -const HANDLE_HEIGHT = 24 - -export function ModalsContainer() { - const {isModalActive, activeModals} = useModals() - const {closeModal} = useModalControls() - const bottomSheetRef = useRef(null) - const pal = usePalette('default') - const activeModal = activeModals[activeModals.length - 1] - - const onBottomSheetChange = async (snapPoint: number) => { - if (snapPoint === -1) { - closeModal() - } - } - - const onClose = () => { - bottomSheetRef.current?.close() - closeModal() - } - - useEffect(() => { - if (isModalActive) { - bottomSheetRef.current?.snapToIndex(0) - } else { - bottomSheetRef.current?.close() - } - }, [isModalActive, bottomSheetRef, activeModal?.name]) - - let snapPoints: (string | number)[] = DEFAULT_SNAPPOINTS - let element - { - return null - } - - if (snapPoints[0] === 'fullscreen') { - return ( - - {element} - - ) - } - - const Container = activeModal ? FullWindowOverlay : Fragment - - return ( - - - {element} - - - ) -} - -const styles = StyleSheet.create({ - handle: { - borderTopLeftRadius: 10, - borderTopRightRadius: 10, - }, - fullscreenContainer: { - position: 'absolute', - top: 0, - left: 0, - bottom: 0, - right: 0, - }, -}) diff --git a/src/view/com/modals/Modal.web.tsx b/src/view/com/modals/Modal.web.tsx deleted file mode 100644 index a35c2b391b..0000000000 --- a/src/view/com/modals/Modal.web.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import {StyleSheet, TouchableWithoutFeedback, View} from 'react-native' -import Animated, {FadeIn, FadeOut} from 'react-native-reanimated' -import {RemoveScrollBar} from 'react-remove-scroll-bar' - -import {usePalette} from '#/lib/hooks/usePalette' -import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' -import {type Modal as ModalIface} from '#/state/modals' -import {useModalControls, useModals} from '#/state/modals' - -export function ModalsContainer() { - const {isModalActive, activeModals} = useModals() - - if (!isModalActive) { - return null - } - - return ( - <> - - {activeModals.map((modal, i) => ( - - ))} - - ) -} - -function Modal({modal: _modal}: {modal: ModalIface}) { - const {isModalActive} = useModals() - const {closeModal} = useModalControls() - const pal = usePalette('default') - const {isMobile} = useWebMediaQueries() - - if (!isModalActive) { - return null - } - - const onPressMask = () => { - closeModal() - } - const onInnerPress = () => { - // TODO: can we use prevent default? - // do nothing, we just want to stop it from bubbling - } - - let element - { - return null - } - - return ( - // eslint-disable-next-line react-native-a11y/has-valid-accessibility-descriptors - - - {/* eslint-disable-next-line react-native-a11y/has-valid-accessibility-descriptors */} - - - {element} - - - - - ) -} - -const styles = StyleSheet.create({ - mask: { - // @ts-ignore - position: 'fixed', - top: 0, - left: 0, - width: '100%', - height: '100%', - backgroundColor: '#000c', - alignItems: 'center', - justifyContent: 'center', - }, - container: { - width: 600, - // @ts-ignore web only - maxWidth: '100vw', - // @ts-ignore web only - maxHeight: '90vh', - paddingVertical: 20, - paddingHorizontal: 24, - borderRadius: 8, - borderWidth: 1, - }, - containerMobile: { - borderRadius: 0, - paddingHorizontal: 0, - }, -}) diff --git a/src/view/com/modals/util.tsx b/src/view/com/modals/util.tsx deleted file mode 100644 index c047a0523c..0000000000 --- a/src/view/com/modals/util.tsx +++ /dev/null @@ -1,4 +0,0 @@ -export { - BottomSheetScrollView as ScrollView, - BottomSheetTextInput as TextInput, -} from '@discord/bottom-sheet/src' diff --git a/src/view/com/modals/util.web.tsx b/src/view/com/modals/util.web.tsx deleted file mode 100644 index acd10c2a60..0000000000 --- a/src/view/com/modals/util.web.tsx +++ /dev/null @@ -1 +0,0 @@ -export {ScrollView, TextInput} from 'react-native' diff --git a/src/view/com/util/Link.tsx b/src/view/com/util/Link.tsx index 658a3ca3b3..b2647363ea 100644 --- a/src/view/com/util/Link.tsx +++ b/src/view/com/util/Link.tsx @@ -28,7 +28,6 @@ import { } from '#/lib/strings/url-helpers' import {type TypographyVariant} from '#/lib/ThemeContext' import {emitSoftReset} from '#/state/events' -import {useModalControls} from '#/state/modals' import {WebAuxClickWrapper} from '#/view/com/util/WebAuxClickWrapper' import {useTheme} from '#/alf' import {useGlobalDialogsControlContext} from '#/components/dialogs/Context' @@ -79,7 +78,6 @@ export const Link = memo(function Link({ ...props }: Props) { const t = useTheme() - const {closeModal} = useModalControls() const navigation = useNavigationDeduped() const anchorHref = asAnchor ? sanitizeUrl(href) : undefined const openLink = useOpenLink() @@ -90,7 +88,6 @@ export const Link = memo(function Link({ onBeforePress?.() if (typeof href === 'string') { return onPressInner( - closeModal, navigation, sanitizeUrl(href), navigationAction, @@ -101,7 +98,6 @@ export const Link = memo(function Link({ } }, [ - closeModal, navigation, navigationAction, href, @@ -205,7 +201,6 @@ export const TextLink = memo(function TextLink({ onBeforePress?: () => void } & TextProps) { const navigation = useNavigationDeduped() - const {closeModal} = useModalControls() const {linkWarningDialogControl} = useGlobalDialogsControlContext() const openLink = useOpenLink() const groupChatJoinIntent = useGroupChatJoinIntent() @@ -246,7 +241,6 @@ export const TextLink = memo(function TextLink({ return onPressProp() } return onPressInner( - closeModal, navigation, sanitizeUrl(href), navigationAction, @@ -258,7 +252,6 @@ export const TextLink = memo(function TextLink({ [ onBeforePress, onPressProp, - closeModal, navigation, href, text, @@ -383,7 +376,6 @@ const EXEMPT_PATHS = ['/robots.txt', '/security.txt', '/.well-known/'] // needed customizations // -prf function onPressInner( - closeModal = () => {}, navigation: DebouncedNavigationProp, href: string, navigationAction: 'push' | 'replace' | 'navigate' = 'push', @@ -429,8 +421,6 @@ function onPressInner( ) { openLink(href) } else { - closeModal() // close any active modals - const [routeName, params] = router.matchPath(href) if (navigationAction === 'push') { // @ts-ignore we're not able to type check on this one -prf diff --git a/src/view/shell/Composer.web.tsx b/src/view/shell/Composer.web.tsx index fc87660e14..d7d0e4ed84 100644 --- a/src/view/shell/Composer.web.tsx +++ b/src/view/shell/Composer.web.tsx @@ -3,7 +3,6 @@ import {DismissableLayer, FocusGuards, FocusScope} from 'radix-ui/internal' import {RemoveScrollBar} from 'react-remove-scroll-bar' import {useA11y} from '#/state/a11y' -import {useModals} from '#/state/modals' import {type ComposerOpts, useComposerState} from '#/state/shell/composer' import {ComposePost, useComposerCancelRef} from '#/view/com/composer/Composer' import {atoms as a, flatten, useBreakpoints, useTheme} from '#/alf' @@ -28,7 +27,6 @@ export function Composer() { function Inner({state}: {state: ComposerOpts}) { const ref = useComposerCancelRef() - const {isModalActive} = useModals() const t = useTheme() const {gtMobile} = useBreakpoints() const {reduceMotionEnabled} = useA11y() @@ -51,12 +49,7 @@ function Inner({state}: {state: ComposerOpts}) { ])} onFocusOutside={evt => evt.preventDefault()} onInteractOutside={evt => evt.preventDefault()} - onDismiss={() => { - // TEMP: remove when all modals are ALF'd -sfn - if (!isModalActive) { - ref.current?.onPressCancel() - } - }}> + onDismiss={() => ref.current?.onPressCancel()}> - diff --git a/src/view/shell/index.web.tsx b/src/view/shell/index.web.tsx index 62dec9bcf7..6a68530f05 100644 --- a/src/view/shell/index.web.tsx +++ b/src/view/shell/index.web.tsx @@ -10,7 +10,6 @@ import {type NavigationProp} from '#/lib/routes/types' import {useSession} from '#/state/session' import {useIsDrawerOpen, useSetDrawerOpen} from '#/state/shell' import {useCloseAllActiveElements} from '#/state/util' -import {ModalsContainer} from '#/view/com/modals/Modal' import {ErrorBoundary} from '#/view/com/util/ErrorBoundary' import {Deactivated} from '#/screens/Deactivated' import {Takendown} from '#/screens/Takendown' @@ -65,7 +64,6 @@ function ShellInner() { -