Close active elems (react-query refactor) (#1926)
* Refactor closeAny and closeAllActiveElements * Add close lightbox * Switch to hooks * Fixeszio/stable
parent
0de8d40981
commit
a84b2f9f2f
|
@ -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}
|
||||
|
|
|
@ -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(
|
||||
() => ({
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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}>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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])
|
||||
}
|
|
@ -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: {
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue