Close active elems (react-query refactor) (#1926)

* Refactor closeAny and closeAllActiveElements

* Add close lightbox

* Switch to hooks

* Fixes
zio/stable
Paul Frazee 2023-11-16 08:18:59 -08:00 committed by GitHub
parent 0de8d40981
commit a84b2f9f2f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 101 additions and 66 deletions

View File

@ -1,18 +1,13 @@
import {useCallback} from 'react'
import {useAnalytics} from '#/lib/analytics/analytics'
import {useStores} from '#/state/index'
import {useSetDrawerOpen} from '#/state/shell/drawer-open'
import {useModalControls} from '#/state/modals'
import {useSessionApi, SessionAccount} from '#/state/session'
import * as Toast from '#/view/com/util/Toast'
import {useCloseAllActiveElements} from '#/state/util'
export function useAccountSwitcher() {
const {track} = useAnalytics()
const store = useStores()
const setDrawerOpen = useSetDrawerOpen()
const {closeModal} = useModalControls()
const {selectAccount, clearCurrentAccount} = useSessionApi()
const closeAllActiveElements = useCloseAllActiveElements()
const onPressSwitchAccount = useCallback(
async (acct: SessionAccount) => {
@ -20,23 +15,14 @@ export function useAccountSwitcher() {
try {
await selectAccount(acct)
setDrawerOpen(false)
closeModal()
store.shell.closeAllActiveElements()
closeAllActiveElements()
Toast.show(`Signed in as ${acct.handle}`)
} catch (e) {
Toast.show('Sorry! We need you to enter your password.')
clearCurrentAccount() // back user out to login
}
},
[
track,
store,
setDrawerOpen,
closeModal,
clearCurrentAccount,
selectAccount,
],
[track, clearCurrentAccount, selectAccount, closeAllActiveElements],
)
return {onPressSwitchAccount}

View File

@ -31,10 +31,10 @@ const LightboxContext = React.createContext<{
const LightboxControlContext = React.createContext<{
openLightbox: (lightbox: Lightbox) => void
closeLightbox: () => void
closeLightbox: () => boolean
}>({
openLightbox: () => {},
closeLightbox: () => {},
closeLightbox: () => false,
})
export function Provider({children}: React.PropsWithChildren<{}>) {
@ -50,8 +50,10 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
)
const closeLightbox = React.useCallback(() => {
let wasActive = !!activeLightbox
setActiveLightbox(null)
}, [setActiveLightbox])
return wasActive
}, [setActiveLightbox, activeLightbox])
const state = React.useMemo(
() => ({

View File

@ -213,10 +213,12 @@ const ModalContext = React.createContext<{
const ModalControlContext = React.createContext<{
openModal: (modal: Modal) => void
closeModal: () => void
closeModal: () => boolean
closeAllModals: () => void
}>({
openModal: () => {},
closeModal: () => {},
closeModal: () => false,
closeAllModals: () => {},
})
/**
@ -226,6 +228,13 @@ export let unstable__openModal: (modal: Modal) => void = () => {
throw new Error(`ModalContext is not initialized`)
}
/**
* @deprecated DO NOT USE THIS unless you have no other choice.
*/
export let unstable__closeModal: () => boolean = () => {
throw new Error(`ModalContext is not initialized`)
}
export function Provider({children}: React.PropsWithChildren<{}>) {
const [isModalActive, setIsModalActive] = React.useState(false)
const [activeModals, setActiveModals] = React.useState<Modal[]>([])
@ -238,17 +247,25 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
[setIsModalActive, setActiveModals],
)
unstable__openModal = openModal
const closeModal = React.useCallback(() => {
let totalActiveModals = 0
let wasActive = isModalActive
setActiveModals(activeModals => {
activeModals = activeModals.slice(0, -1)
totalActiveModals = activeModals.length
return activeModals
})
setIsModalActive(totalActiveModals > 0)
}, [setIsModalActive, setActiveModals])
return wasActive
}, [setIsModalActive, setActiveModals, isModalActive])
const closeAllModals = React.useCallback(() => {
setActiveModals([])
setIsModalActive(false)
}, [setActiveModals, setIsModalActive])
unstable__openModal = openModal
unstable__closeModal = closeModal
const state = React.useMemo(
() => ({
@ -262,8 +279,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
() => ({
openModal,
closeModal,
closeAllModals,
}),
[openModal, closeModal],
[openModal, closeModal, closeAllModals],
)
return (

View File

@ -21,19 +21,6 @@ export class ShellUiModel {
this.setupLoginModals()
}
/**
* returns true if something was closed
* (used by the android hardware back btn)
*/
closeAnyActiveElement(): boolean {
return false
}
/**
* used to clear out any modals, eg for a navigation
*/
closeAllActiveElements() {}
setupLoginModals() {
this.rootStore.onSessionReady(() => {
if (shouldRequestEmailConfirmation(this.rootStore.session)) {

View File

@ -34,13 +34,15 @@ export interface ComposerOpts {
type StateContext = ComposerOpts | undefined
type ControlsContext = {
openComposer: (opts: ComposerOpts) => void
closeComposer: () => void
closeComposer: () => boolean
}
const stateContext = React.createContext<StateContext>(undefined)
const controlsContext = React.createContext<ControlsContext>({
openComposer(_opts: ComposerOpts) {},
closeComposer() {},
closeComposer() {
return false
},
})
export function Provider({children}: React.PropsWithChildren<{}>) {
@ -51,11 +53,14 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
setState(opts)
},
closeComposer() {
let wasOpen = !!state
setState(undefined)
return wasOpen
},
}),
[setState],
[setState, state],
)
return (
<stateContext.Provider value={state}>
<controlsContext.Provider value={api}>

View File

@ -8,6 +8,7 @@ const setContext = React.createContext<SetContext>((_: boolean) => {})
export function Provider({children}: React.PropsWithChildren<{}>) {
const [state, setState] = React.useState(false)
return (
<stateContext.Provider value={state}>
<setContext.Provider value={setState}>{children}</setContext.Provider>

45
src/state/util.ts 100644
View File

@ -0,0 +1,45 @@
import {useCallback} from 'react'
import {useLightboxControls} from './lightbox'
import {useModalControls} from './modals'
import {useComposerControls} from './shell/composer'
import {useSetDrawerOpen} from './shell/drawer-open'
/**
* returns true if something was closed
* (used by the android hardware back btn)
*/
export function useCloseAnyActiveElement() {
const {closeLightbox} = useLightboxControls()
const {closeModal} = useModalControls()
const {closeComposer} = useComposerControls()
const setDrawerOpen = useSetDrawerOpen()
return useCallback(() => {
if (closeLightbox()) {
return true
}
if (closeModal()) {
return true
}
if (closeComposer()) {
return true
}
setDrawerOpen(false)
return false
}, [closeLightbox, closeModal, closeComposer, setDrawerOpen])
}
/**
* used to clear out any modals, eg for a navigation
*/
export function useCloseAllActiveElements() {
const {closeLightbox} = useLightboxControls()
const {closeAllModals} = useModalControls()
const {closeComposer} = useComposerControls()
const setDrawerOpen = useSetDrawerOpen()
return useCallback(() => {
closeLightbox()
closeAllModals()
closeComposer()
setDrawerOpen(false)
}, [closeLightbox, closeAllModals, closeComposer, setDrawerOpen])
}

View File

@ -1,5 +1,4 @@
import React from 'react'
import {observer} from 'mobx-react-lite'
import {StatusBar} from 'expo-status-bar'
import {
DimensionValue,
@ -11,7 +10,6 @@ import {
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {Drawer} from 'react-native-drawer-layout'
import {useNavigationState} from '@react-navigation/native'
import {useStores} from 'state/index'
import {ModalsContainer} from 'view/com/modals/Modal'
import {Lightbox} from 'view/com/lightbox/Lightbox'
import {ErrorBoundary} from 'view/com/util/ErrorBoundary'
@ -32,15 +30,13 @@ import {
useIsDrawerSwipeDisabled,
} from '#/state/shell'
import {isAndroid} from 'platform/detection'
import {useModalControls} from '#/state/modals'
import {useSession} from '#/state/session'
import {useCloseAnyActiveElement} from '#/state/util'
const ShellInner = observer(function ShellInnerImpl() {
const store = useStores()
function ShellInner() {
const isDrawerOpen = useIsDrawerOpen()
const isDrawerSwipeDisabled = useIsDrawerSwipeDisabled()
const setIsDrawerOpen = useSetDrawerOpen()
const {closeModal} = useModalControls()
useOTAUpdate() // this hook polls for OTA updates every few seconds
const winDim = useWindowDimensions()
const safeAreaInsets = useSafeAreaInsets()
@ -59,20 +55,19 @@ const ShellInner = observer(function ShellInnerImpl() {
)
const canGoBack = useNavigationState(state => !isStateAtTabRoot(state))
const {hasSession} = useSession()
const closeAnyActiveElement = useCloseAnyActiveElement()
React.useEffect(() => {
let listener = {remove() {}}
if (isAndroid) {
listener = BackHandler.addEventListener('hardwareBackPress', () => {
setIsDrawerOpen(false)
closeModal()
return store.shell.closeAnyActiveElement()
return closeAnyActiveElement()
})
}
return () => {
listener.remove()
}
}, [store, setIsDrawerOpen, closeModal])
}, [closeAnyActiveElement])
return (
<>
@ -94,9 +89,9 @@ const ShellInner = observer(function ShellInnerImpl() {
<Lightbox />
</>
)
})
}
export const Shell: React.FC = observer(function ShellImpl() {
export const Shell: React.FC = function ShellImpl() {
const pal = usePalette('default')
const theme = useTheme()
return (
@ -109,7 +104,7 @@ export const Shell: React.FC = observer(function ShellImpl() {
</View>
</SafeAreaProvider>
)
})
}
const styles = StyleSheet.create({
outerContainer: {

View File

@ -1,7 +1,6 @@
import React, {useEffect} from 'react'
import {observer} from 'mobx-react-lite'
import {View, StyleSheet, TouchableOpacity} from 'react-native'
import {useStores} from 'state/index'
import {DesktopLeftNav} from './desktop/LeftNav'
import {DesktopRightNav} from './desktop/RightNav'
import {ErrorBoundary} from '../com/util/ErrorBoundary'
@ -23,28 +22,25 @@ import {
useSetDrawerOpen,
useOnboardingState,
} from '#/state/shell'
import {useModalControls} from '#/state/modals'
import {useSession} from '#/state/session'
import {useCloseAllActiveElements} from '#/state/util'
const ShellInner = observer(function ShellInnerImpl() {
const store = useStores()
function ShellInner() {
const isDrawerOpen = useIsDrawerOpen()
const setDrawerOpen = useSetDrawerOpen()
const {closeModal} = useModalControls()
const onboardingState = useOnboardingState()
const {isDesktop, isMobile} = useWebMediaQueries()
const navigator = useNavigation<NavigationProp>()
const {hasSession} = useSession()
const closeAllActiveElements = useCloseAllActiveElements()
useAuxClick()
useEffect(() => {
navigator.addListener('state', () => {
setDrawerOpen(false)
closeModal()
store.shell.closeAnyActiveElement()
closeAllActiveElements()
})
}, [navigator, store.shell, setDrawerOpen, closeModal])
}, [navigator, closeAllActiveElements])
const showBottomBar = isMobile && !onboardingState.isActive
const showSideNavs = !isMobile && hasSession && !onboardingState.isActive
@ -78,7 +74,7 @@ const ShellInner = observer(function ShellInnerImpl() {
)}
</View>
)
})
}
export const Shell: React.FC = observer(function ShellImpl() {
const pageBg = useColorSchemeStyle(styles.bgLight, styles.bgDark)