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 {useCallback} from 'react'
|
||||||
|
|
||||||
import {useAnalytics} from '#/lib/analytics/analytics'
|
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 {useSessionApi, SessionAccount} from '#/state/session'
|
||||||
import * as Toast from '#/view/com/util/Toast'
|
import * as Toast from '#/view/com/util/Toast'
|
||||||
|
import {useCloseAllActiveElements} from '#/state/util'
|
||||||
|
|
||||||
export function useAccountSwitcher() {
|
export function useAccountSwitcher() {
|
||||||
const {track} = useAnalytics()
|
const {track} = useAnalytics()
|
||||||
const store = useStores()
|
|
||||||
const setDrawerOpen = useSetDrawerOpen()
|
|
||||||
const {closeModal} = useModalControls()
|
|
||||||
const {selectAccount, clearCurrentAccount} = useSessionApi()
|
const {selectAccount, clearCurrentAccount} = useSessionApi()
|
||||||
|
const closeAllActiveElements = useCloseAllActiveElements()
|
||||||
|
|
||||||
const onPressSwitchAccount = useCallback(
|
const onPressSwitchAccount = useCallback(
|
||||||
async (acct: SessionAccount) => {
|
async (acct: SessionAccount) => {
|
||||||
|
@ -20,23 +15,14 @@ export function useAccountSwitcher() {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await selectAccount(acct)
|
await selectAccount(acct)
|
||||||
setDrawerOpen(false)
|
closeAllActiveElements()
|
||||||
closeModal()
|
|
||||||
store.shell.closeAllActiveElements()
|
|
||||||
Toast.show(`Signed in as ${acct.handle}`)
|
Toast.show(`Signed in as ${acct.handle}`)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Toast.show('Sorry! We need you to enter your password.')
|
Toast.show('Sorry! We need you to enter your password.')
|
||||||
clearCurrentAccount() // back user out to login
|
clearCurrentAccount() // back user out to login
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[
|
[track, clearCurrentAccount, selectAccount, closeAllActiveElements],
|
||||||
track,
|
|
||||||
store,
|
|
||||||
setDrawerOpen,
|
|
||||||
closeModal,
|
|
||||||
clearCurrentAccount,
|
|
||||||
selectAccount,
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return {onPressSwitchAccount}
|
return {onPressSwitchAccount}
|
||||||
|
|
|
@ -31,10 +31,10 @@ const LightboxContext = React.createContext<{
|
||||||
|
|
||||||
const LightboxControlContext = React.createContext<{
|
const LightboxControlContext = React.createContext<{
|
||||||
openLightbox: (lightbox: Lightbox) => void
|
openLightbox: (lightbox: Lightbox) => void
|
||||||
closeLightbox: () => void
|
closeLightbox: () => boolean
|
||||||
}>({
|
}>({
|
||||||
openLightbox: () => {},
|
openLightbox: () => {},
|
||||||
closeLightbox: () => {},
|
closeLightbox: () => false,
|
||||||
})
|
})
|
||||||
|
|
||||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
|
@ -50,8 +50,10 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
)
|
)
|
||||||
|
|
||||||
const closeLightbox = React.useCallback(() => {
|
const closeLightbox = React.useCallback(() => {
|
||||||
|
let wasActive = !!activeLightbox
|
||||||
setActiveLightbox(null)
|
setActiveLightbox(null)
|
||||||
}, [setActiveLightbox])
|
return wasActive
|
||||||
|
}, [setActiveLightbox, activeLightbox])
|
||||||
|
|
||||||
const state = React.useMemo(
|
const state = React.useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
|
|
@ -213,10 +213,12 @@ const ModalContext = React.createContext<{
|
||||||
|
|
||||||
const ModalControlContext = React.createContext<{
|
const ModalControlContext = React.createContext<{
|
||||||
openModal: (modal: Modal) => void
|
openModal: (modal: Modal) => void
|
||||||
closeModal: () => void
|
closeModal: () => boolean
|
||||||
|
closeAllModals: () => void
|
||||||
}>({
|
}>({
|
||||||
openModal: () => {},
|
openModal: () => {},
|
||||||
closeModal: () => {},
|
closeModal: () => false,
|
||||||
|
closeAllModals: () => {},
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -226,6 +228,13 @@ export let unstable__openModal: (modal: Modal) => void = () => {
|
||||||
throw new Error(`ModalContext is not initialized`)
|
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<{}>) {
|
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
const [isModalActive, setIsModalActive] = React.useState(false)
|
const [isModalActive, setIsModalActive] = React.useState(false)
|
||||||
const [activeModals, setActiveModals] = React.useState<Modal[]>([])
|
const [activeModals, setActiveModals] = React.useState<Modal[]>([])
|
||||||
|
@ -238,17 +247,25 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
[setIsModalActive, setActiveModals],
|
[setIsModalActive, setActiveModals],
|
||||||
)
|
)
|
||||||
|
|
||||||
unstable__openModal = openModal
|
|
||||||
|
|
||||||
const closeModal = React.useCallback(() => {
|
const closeModal = React.useCallback(() => {
|
||||||
let totalActiveModals = 0
|
let totalActiveModals = 0
|
||||||
|
let wasActive = isModalActive
|
||||||
setActiveModals(activeModals => {
|
setActiveModals(activeModals => {
|
||||||
activeModals = activeModals.slice(0, -1)
|
activeModals = activeModals.slice(0, -1)
|
||||||
totalActiveModals = activeModals.length
|
totalActiveModals = activeModals.length
|
||||||
return activeModals
|
return activeModals
|
||||||
})
|
})
|
||||||
setIsModalActive(totalActiveModals > 0)
|
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(
|
const state = React.useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
|
@ -262,8 +279,9 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
() => ({
|
() => ({
|
||||||
openModal,
|
openModal,
|
||||||
closeModal,
|
closeModal,
|
||||||
|
closeAllModals,
|
||||||
}),
|
}),
|
||||||
[openModal, closeModal],
|
[openModal, closeModal, closeAllModals],
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -21,19 +21,6 @@ export class ShellUiModel {
|
||||||
this.setupLoginModals()
|
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() {
|
setupLoginModals() {
|
||||||
this.rootStore.onSessionReady(() => {
|
this.rootStore.onSessionReady(() => {
|
||||||
if (shouldRequestEmailConfirmation(this.rootStore.session)) {
|
if (shouldRequestEmailConfirmation(this.rootStore.session)) {
|
||||||
|
|
|
@ -34,13 +34,15 @@ export interface ComposerOpts {
|
||||||
type StateContext = ComposerOpts | undefined
|
type StateContext = ComposerOpts | undefined
|
||||||
type ControlsContext = {
|
type ControlsContext = {
|
||||||
openComposer: (opts: ComposerOpts) => void
|
openComposer: (opts: ComposerOpts) => void
|
||||||
closeComposer: () => void
|
closeComposer: () => boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const stateContext = React.createContext<StateContext>(undefined)
|
const stateContext = React.createContext<StateContext>(undefined)
|
||||||
const controlsContext = React.createContext<ControlsContext>({
|
const controlsContext = React.createContext<ControlsContext>({
|
||||||
openComposer(_opts: ComposerOpts) {},
|
openComposer(_opts: ComposerOpts) {},
|
||||||
closeComposer() {},
|
closeComposer() {
|
||||||
|
return false
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
|
@ -51,11 +53,14 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
setState(opts)
|
setState(opts)
|
||||||
},
|
},
|
||||||
closeComposer() {
|
closeComposer() {
|
||||||
|
let wasOpen = !!state
|
||||||
setState(undefined)
|
setState(undefined)
|
||||||
|
return wasOpen
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
[setState],
|
[setState, state],
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<stateContext.Provider value={state}>
|
<stateContext.Provider value={state}>
|
||||||
<controlsContext.Provider value={api}>
|
<controlsContext.Provider value={api}>
|
||||||
|
|
|
@ -8,6 +8,7 @@ const setContext = React.createContext<SetContext>((_: boolean) => {})
|
||||||
|
|
||||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||||
const [state, setState] = React.useState(false)
|
const [state, setState] = React.useState(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<stateContext.Provider value={state}>
|
<stateContext.Provider value={state}>
|
||||||
<setContext.Provider value={setState}>{children}</setContext.Provider>
|
<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 React from 'react'
|
||||||
import {observer} from 'mobx-react-lite'
|
|
||||||
import {StatusBar} from 'expo-status-bar'
|
import {StatusBar} from 'expo-status-bar'
|
||||||
import {
|
import {
|
||||||
DimensionValue,
|
DimensionValue,
|
||||||
|
@ -11,7 +10,6 @@ import {
|
||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
import {Drawer} from 'react-native-drawer-layout'
|
import {Drawer} from 'react-native-drawer-layout'
|
||||||
import {useNavigationState} from '@react-navigation/native'
|
import {useNavigationState} from '@react-navigation/native'
|
||||||
import {useStores} from 'state/index'
|
|
||||||
import {ModalsContainer} from 'view/com/modals/Modal'
|
import {ModalsContainer} from 'view/com/modals/Modal'
|
||||||
import {Lightbox} from 'view/com/lightbox/Lightbox'
|
import {Lightbox} from 'view/com/lightbox/Lightbox'
|
||||||
import {ErrorBoundary} from 'view/com/util/ErrorBoundary'
|
import {ErrorBoundary} from 'view/com/util/ErrorBoundary'
|
||||||
|
@ -32,15 +30,13 @@ import {
|
||||||
useIsDrawerSwipeDisabled,
|
useIsDrawerSwipeDisabled,
|
||||||
} from '#/state/shell'
|
} from '#/state/shell'
|
||||||
import {isAndroid} from 'platform/detection'
|
import {isAndroid} from 'platform/detection'
|
||||||
import {useModalControls} from '#/state/modals'
|
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
|
import {useCloseAnyActiveElement} from '#/state/util'
|
||||||
|
|
||||||
const ShellInner = observer(function ShellInnerImpl() {
|
function ShellInner() {
|
||||||
const store = useStores()
|
|
||||||
const isDrawerOpen = useIsDrawerOpen()
|
const isDrawerOpen = useIsDrawerOpen()
|
||||||
const isDrawerSwipeDisabled = useIsDrawerSwipeDisabled()
|
const isDrawerSwipeDisabled = useIsDrawerSwipeDisabled()
|
||||||
const setIsDrawerOpen = useSetDrawerOpen()
|
const setIsDrawerOpen = useSetDrawerOpen()
|
||||||
const {closeModal} = useModalControls()
|
|
||||||
useOTAUpdate() // this hook polls for OTA updates every few seconds
|
useOTAUpdate() // this hook polls for OTA updates every few seconds
|
||||||
const winDim = useWindowDimensions()
|
const winDim = useWindowDimensions()
|
||||||
const safeAreaInsets = useSafeAreaInsets()
|
const safeAreaInsets = useSafeAreaInsets()
|
||||||
|
@ -59,20 +55,19 @@ const ShellInner = observer(function ShellInnerImpl() {
|
||||||
)
|
)
|
||||||
const canGoBack = useNavigationState(state => !isStateAtTabRoot(state))
|
const canGoBack = useNavigationState(state => !isStateAtTabRoot(state))
|
||||||
const {hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
|
const closeAnyActiveElement = useCloseAnyActiveElement()
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
let listener = {remove() {}}
|
let listener = {remove() {}}
|
||||||
if (isAndroid) {
|
if (isAndroid) {
|
||||||
listener = BackHandler.addEventListener('hardwareBackPress', () => {
|
listener = BackHandler.addEventListener('hardwareBackPress', () => {
|
||||||
setIsDrawerOpen(false)
|
return closeAnyActiveElement()
|
||||||
closeModal()
|
|
||||||
return store.shell.closeAnyActiveElement()
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return () => {
|
return () => {
|
||||||
listener.remove()
|
listener.remove()
|
||||||
}
|
}
|
||||||
}, [store, setIsDrawerOpen, closeModal])
|
}, [closeAnyActiveElement])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -94,9 +89,9 @@ const ShellInner = observer(function ShellInnerImpl() {
|
||||||
<Lightbox />
|
<Lightbox />
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
|
||||||
export const Shell: React.FC = observer(function ShellImpl() {
|
export const Shell: React.FC = function ShellImpl() {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
return (
|
return (
|
||||||
|
@ -109,7 +104,7 @@ export const Shell: React.FC = observer(function ShellImpl() {
|
||||||
</View>
|
</View>
|
||||||
</SafeAreaProvider>
|
</SafeAreaProvider>
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
outerContainer: {
|
outerContainer: {
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import React, {useEffect} from 'react'
|
import React, {useEffect} from 'react'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import {View, StyleSheet, TouchableOpacity} from 'react-native'
|
import {View, StyleSheet, TouchableOpacity} from 'react-native'
|
||||||
import {useStores} from 'state/index'
|
|
||||||
import {DesktopLeftNav} from './desktop/LeftNav'
|
import {DesktopLeftNav} from './desktop/LeftNav'
|
||||||
import {DesktopRightNav} from './desktop/RightNav'
|
import {DesktopRightNav} from './desktop/RightNav'
|
||||||
import {ErrorBoundary} from '../com/util/ErrorBoundary'
|
import {ErrorBoundary} from '../com/util/ErrorBoundary'
|
||||||
|
@ -23,28 +22,25 @@ import {
|
||||||
useSetDrawerOpen,
|
useSetDrawerOpen,
|
||||||
useOnboardingState,
|
useOnboardingState,
|
||||||
} from '#/state/shell'
|
} from '#/state/shell'
|
||||||
import {useModalControls} from '#/state/modals'
|
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
|
import {useCloseAllActiveElements} from '#/state/util'
|
||||||
|
|
||||||
const ShellInner = observer(function ShellInnerImpl() {
|
function ShellInner() {
|
||||||
const store = useStores()
|
|
||||||
const isDrawerOpen = useIsDrawerOpen()
|
const isDrawerOpen = useIsDrawerOpen()
|
||||||
const setDrawerOpen = useSetDrawerOpen()
|
const setDrawerOpen = useSetDrawerOpen()
|
||||||
const {closeModal} = useModalControls()
|
|
||||||
const onboardingState = useOnboardingState()
|
const onboardingState = useOnboardingState()
|
||||||
const {isDesktop, isMobile} = useWebMediaQueries()
|
const {isDesktop, isMobile} = useWebMediaQueries()
|
||||||
const navigator = useNavigation<NavigationProp>()
|
const navigator = useNavigation<NavigationProp>()
|
||||||
const {hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
|
const closeAllActiveElements = useCloseAllActiveElements()
|
||||||
|
|
||||||
useAuxClick()
|
useAuxClick()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
navigator.addListener('state', () => {
|
navigator.addListener('state', () => {
|
||||||
setDrawerOpen(false)
|
closeAllActiveElements()
|
||||||
closeModal()
|
|
||||||
store.shell.closeAnyActiveElement()
|
|
||||||
})
|
})
|
||||||
}, [navigator, store.shell, setDrawerOpen, closeModal])
|
}, [navigator, closeAllActiveElements])
|
||||||
|
|
||||||
const showBottomBar = isMobile && !onboardingState.isActive
|
const showBottomBar = isMobile && !onboardingState.isActive
|
||||||
const showSideNavs = !isMobile && hasSession && !onboardingState.isActive
|
const showSideNavs = !isMobile && hasSession && !onboardingState.isActive
|
||||||
|
@ -78,7 +74,7 @@ const ShellInner = observer(function ShellInnerImpl() {
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
})
|
}
|
||||||
|
|
||||||
export const Shell: React.FC = observer(function ShellImpl() {
|
export const Shell: React.FC = observer(function ShellImpl() {
|
||||||
const pageBg = useColorSchemeStyle(styles.bgLight, styles.bgDark)
|
const pageBg = useColorSchemeStyle(styles.bgLight, styles.bgDark)
|
||||||
|
|
Loading…
Reference in New Issue