Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

* Refactor closeAny and closeAllActiveElements

* Add close lightbox

* Switch to hooks

* Fixes

authored by

Paul Frazee and committed by
GitHub
a84b2f9f 0de8d409

+101 -66
+4 -18
src/lib/hooks/useAccountSwitcher.ts
··· 1 1 import {useCallback} from 'react' 2 - 3 2 import {useAnalytics} from '#/lib/analytics/analytics' 4 - import {useStores} from '#/state/index' 5 - import {useSetDrawerOpen} from '#/state/shell/drawer-open' 6 - import {useModalControls} from '#/state/modals' 7 3 import {useSessionApi, SessionAccount} from '#/state/session' 8 4 import * as Toast from '#/view/com/util/Toast' 5 + import {useCloseAllActiveElements} from '#/state/util' 9 6 10 7 export function useAccountSwitcher() { 11 8 const {track} = useAnalytics() 12 - const store = useStores() 13 - const setDrawerOpen = useSetDrawerOpen() 14 - const {closeModal} = useModalControls() 15 9 const {selectAccount, clearCurrentAccount} = useSessionApi() 10 + const closeAllActiveElements = useCloseAllActiveElements() 16 11 17 12 const onPressSwitchAccount = useCallback( 18 13 async (acct: SessionAccount) => { ··· 20 15 21 16 try { 22 17 await selectAccount(acct) 23 - setDrawerOpen(false) 24 - closeModal() 25 - store.shell.closeAllActiveElements() 18 + closeAllActiveElements() 26 19 Toast.show(`Signed in as ${acct.handle}`) 27 20 } catch (e) { 28 21 Toast.show('Sorry! We need you to enter your password.') 29 22 clearCurrentAccount() // back user out to login 30 23 } 31 24 }, 32 - [ 33 - track, 34 - store, 35 - setDrawerOpen, 36 - closeModal, 37 - clearCurrentAccount, 38 - selectAccount, 39 - ], 25 + [track, clearCurrentAccount, selectAccount, closeAllActiveElements], 40 26 ) 41 27 42 28 return {onPressSwitchAccount}
+5 -3
src/state/lightbox.tsx
··· 31 31 32 32 const LightboxControlContext = React.createContext<{ 33 33 openLightbox: (lightbox: Lightbox) => void 34 - closeLightbox: () => void 34 + closeLightbox: () => boolean 35 35 }>({ 36 36 openLightbox: () => {}, 37 - closeLightbox: () => {}, 37 + closeLightbox: () => false, 38 38 }) 39 39 40 40 export function Provider({children}: React.PropsWithChildren<{}>) { ··· 50 50 ) 51 51 52 52 const closeLightbox = React.useCallback(() => { 53 + let wasActive = !!activeLightbox 53 54 setActiveLightbox(null) 54 - }, [setActiveLightbox]) 55 + return wasActive 56 + }, [setActiveLightbox, activeLightbox]) 55 57 56 58 const state = React.useMemo( 57 59 () => ({
+24 -6
src/state/modals/index.tsx
··· 213 213 214 214 const ModalControlContext = React.createContext<{ 215 215 openModal: (modal: Modal) => void 216 - closeModal: () => void 216 + closeModal: () => boolean 217 + closeAllModals: () => void 217 218 }>({ 218 219 openModal: () => {}, 219 - closeModal: () => {}, 220 + closeModal: () => false, 221 + closeAllModals: () => {}, 220 222 }) 221 223 222 224 /** ··· 226 228 throw new Error(`ModalContext is not initialized`) 227 229 } 228 230 231 + /** 232 + * @deprecated DO NOT USE THIS unless you have no other choice. 233 + */ 234 + export let unstable__closeModal: () => boolean = () => { 235 + throw new Error(`ModalContext is not initialized`) 236 + } 237 + 229 238 export function Provider({children}: React.PropsWithChildren<{}>) { 230 239 const [isModalActive, setIsModalActive] = React.useState(false) 231 240 const [activeModals, setActiveModals] = React.useState<Modal[]>([]) ··· 237 246 }, 238 247 [setIsModalActive, setActiveModals], 239 248 ) 240 - 241 - unstable__openModal = openModal 242 249 243 250 const closeModal = React.useCallback(() => { 244 251 let totalActiveModals = 0 252 + let wasActive = isModalActive 245 253 setActiveModals(activeModals => { 246 254 activeModals = activeModals.slice(0, -1) 247 255 totalActiveModals = activeModals.length 248 256 return activeModals 249 257 }) 250 258 setIsModalActive(totalActiveModals > 0) 251 - }, [setIsModalActive, setActiveModals]) 259 + return wasActive 260 + }, [setIsModalActive, setActiveModals, isModalActive]) 261 + 262 + const closeAllModals = React.useCallback(() => { 263 + setActiveModals([]) 264 + setIsModalActive(false) 265 + }, [setActiveModals, setIsModalActive]) 266 + 267 + unstable__openModal = openModal 268 + unstable__closeModal = closeModal 252 269 253 270 const state = React.useMemo( 254 271 () => ({ ··· 262 279 () => ({ 263 280 openModal, 264 281 closeModal, 282 + closeAllModals, 265 283 }), 266 - [openModal, closeModal], 284 + [openModal, closeModal, closeAllModals], 267 285 ) 268 286 269 287 return (
-13
src/state/models/ui/shell.ts
··· 21 21 this.setupLoginModals() 22 22 } 23 23 24 - /** 25 - * returns true if something was closed 26 - * (used by the android hardware back btn) 27 - */ 28 - closeAnyActiveElement(): boolean { 29 - return false 30 - } 31 - 32 - /** 33 - * used to clear out any modals, eg for a navigation 34 - */ 35 - closeAllActiveElements() {} 36 - 37 24 setupLoginModals() { 38 25 this.rootStore.onSessionReady(() => { 39 26 if (shouldRequestEmailConfirmation(this.rootStore.session)) {
+8 -3
src/state/shell/composer.tsx
··· 34 34 type StateContext = ComposerOpts | undefined 35 35 type ControlsContext = { 36 36 openComposer: (opts: ComposerOpts) => void 37 - closeComposer: () => void 37 + closeComposer: () => boolean 38 38 } 39 39 40 40 const stateContext = React.createContext<StateContext>(undefined) 41 41 const controlsContext = React.createContext<ControlsContext>({ 42 42 openComposer(_opts: ComposerOpts) {}, 43 - closeComposer() {}, 43 + closeComposer() { 44 + return false 45 + }, 44 46 }) 45 47 46 48 export function Provider({children}: React.PropsWithChildren<{}>) { ··· 51 53 setState(opts) 52 54 }, 53 55 closeComposer() { 56 + let wasOpen = !!state 54 57 setState(undefined) 58 + return wasOpen 55 59 }, 56 60 }), 57 - [setState], 61 + [setState, state], 58 62 ) 63 + 59 64 return ( 60 65 <stateContext.Provider value={state}> 61 66 <controlsContext.Provider value={api}>
+1
src/state/shell/drawer-open.tsx
··· 8 8 9 9 export function Provider({children}: React.PropsWithChildren<{}>) { 10 10 const [state, setState] = React.useState(false) 11 + 11 12 return ( 12 13 <stateContext.Provider value={state}> 13 14 <setContext.Provider value={setState}>{children}</setContext.Provider>
+45
src/state/util.ts
··· 1 + import {useCallback} from 'react' 2 + import {useLightboxControls} from './lightbox' 3 + import {useModalControls} from './modals' 4 + import {useComposerControls} from './shell/composer' 5 + import {useSetDrawerOpen} from './shell/drawer-open' 6 + 7 + /** 8 + * returns true if something was closed 9 + * (used by the android hardware back btn) 10 + */ 11 + export function useCloseAnyActiveElement() { 12 + const {closeLightbox} = useLightboxControls() 13 + const {closeModal} = useModalControls() 14 + const {closeComposer} = useComposerControls() 15 + const setDrawerOpen = useSetDrawerOpen() 16 + return useCallback(() => { 17 + if (closeLightbox()) { 18 + return true 19 + } 20 + if (closeModal()) { 21 + return true 22 + } 23 + if (closeComposer()) { 24 + return true 25 + } 26 + setDrawerOpen(false) 27 + return false 28 + }, [closeLightbox, closeModal, closeComposer, setDrawerOpen]) 29 + } 30 + 31 + /** 32 + * used to clear out any modals, eg for a navigation 33 + */ 34 + export function useCloseAllActiveElements() { 35 + const {closeLightbox} = useLightboxControls() 36 + const {closeAllModals} = useModalControls() 37 + const {closeComposer} = useComposerControls() 38 + const setDrawerOpen = useSetDrawerOpen() 39 + return useCallback(() => { 40 + closeLightbox() 41 + closeAllModals() 42 + closeComposer() 43 + setDrawerOpen(false) 44 + }, [closeLightbox, closeAllModals, closeComposer, setDrawerOpen]) 45 + }
+8 -13
src/view/shell/index.tsx
··· 1 1 import React from 'react' 2 - import {observer} from 'mobx-react-lite' 3 2 import {StatusBar} from 'expo-status-bar' 4 3 import { 5 4 DimensionValue, ··· 11 10 import {useSafeAreaInsets} from 'react-native-safe-area-context' 12 11 import {Drawer} from 'react-native-drawer-layout' 13 12 import {useNavigationState} from '@react-navigation/native' 14 - import {useStores} from 'state/index' 15 13 import {ModalsContainer} from 'view/com/modals/Modal' 16 14 import {Lightbox} from 'view/com/lightbox/Lightbox' 17 15 import {ErrorBoundary} from 'view/com/util/ErrorBoundary' ··· 32 30 useIsDrawerSwipeDisabled, 33 31 } from '#/state/shell' 34 32 import {isAndroid} from 'platform/detection' 35 - import {useModalControls} from '#/state/modals' 36 33 import {useSession} from '#/state/session' 34 + import {useCloseAnyActiveElement} from '#/state/util' 37 35 38 - const ShellInner = observer(function ShellInnerImpl() { 39 - const store = useStores() 36 + function ShellInner() { 40 37 const isDrawerOpen = useIsDrawerOpen() 41 38 const isDrawerSwipeDisabled = useIsDrawerSwipeDisabled() 42 39 const setIsDrawerOpen = useSetDrawerOpen() 43 - const {closeModal} = useModalControls() 44 40 useOTAUpdate() // this hook polls for OTA updates every few seconds 45 41 const winDim = useWindowDimensions() 46 42 const safeAreaInsets = useSafeAreaInsets() ··· 59 55 ) 60 56 const canGoBack = useNavigationState(state => !isStateAtTabRoot(state)) 61 57 const {hasSession} = useSession() 58 + const closeAnyActiveElement = useCloseAnyActiveElement() 62 59 63 60 React.useEffect(() => { 64 61 let listener = {remove() {}} 65 62 if (isAndroid) { 66 63 listener = BackHandler.addEventListener('hardwareBackPress', () => { 67 - setIsDrawerOpen(false) 68 - closeModal() 69 - return store.shell.closeAnyActiveElement() 64 + return closeAnyActiveElement() 70 65 }) 71 66 } 72 67 return () => { 73 68 listener.remove() 74 69 } 75 - }, [store, setIsDrawerOpen, closeModal]) 70 + }, [closeAnyActiveElement]) 76 71 77 72 return ( 78 73 <> ··· 94 89 <Lightbox /> 95 90 </> 96 91 ) 97 - }) 92 + } 98 93 99 - export const Shell: React.FC = observer(function ShellImpl() { 94 + export const Shell: React.FC = function ShellImpl() { 100 95 const pal = usePalette('default') 101 96 const theme = useTheme() 102 97 return ( ··· 109 104 </View> 110 105 </SafeAreaProvider> 111 106 ) 112 - }) 107 + } 113 108 114 109 const styles = StyleSheet.create({ 115 110 outerContainer: {
+6 -10
src/view/shell/index.web.tsx
··· 1 1 import React, {useEffect} from 'react' 2 2 import {observer} from 'mobx-react-lite' 3 3 import {View, StyleSheet, TouchableOpacity} from 'react-native' 4 - import {useStores} from 'state/index' 5 4 import {DesktopLeftNav} from './desktop/LeftNav' 6 5 import {DesktopRightNav} from './desktop/RightNav' 7 6 import {ErrorBoundary} from '../com/util/ErrorBoundary' ··· 23 22 useSetDrawerOpen, 24 23 useOnboardingState, 25 24 } from '#/state/shell' 26 - import {useModalControls} from '#/state/modals' 27 25 import {useSession} from '#/state/session' 26 + import {useCloseAllActiveElements} from '#/state/util' 28 27 29 - const ShellInner = observer(function ShellInnerImpl() { 30 - const store = useStores() 28 + function ShellInner() { 31 29 const isDrawerOpen = useIsDrawerOpen() 32 30 const setDrawerOpen = useSetDrawerOpen() 33 - const {closeModal} = useModalControls() 34 31 const onboardingState = useOnboardingState() 35 32 const {isDesktop, isMobile} = useWebMediaQueries() 36 33 const navigator = useNavigation<NavigationProp>() 37 34 const {hasSession} = useSession() 35 + const closeAllActiveElements = useCloseAllActiveElements() 38 36 39 37 useAuxClick() 40 38 41 39 useEffect(() => { 42 40 navigator.addListener('state', () => { 43 - setDrawerOpen(false) 44 - closeModal() 45 - store.shell.closeAnyActiveElement() 41 + closeAllActiveElements() 46 42 }) 47 - }, [navigator, store.shell, setDrawerOpen, closeModal]) 43 + }, [navigator, closeAllActiveElements]) 48 44 49 45 const showBottomBar = isMobile && !onboardingState.isActive 50 46 const showSideNavs = !isMobile && hasSession && !onboardingState.isActive ··· 78 74 )} 79 75 </View> 80 76 ) 81 - }) 77 + } 82 78 83 79 export const Shell: React.FC = observer(function ShellImpl() { 84 80 const pageBg = useColorSchemeStyle(styles.bgLight, styles.bgDark)