diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 97612c9ecf..e1d5e76aaf 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -483,9 +483,21 @@ function navigate( params?: AllNavigatorParams[K], ) { if (navigationRef.isReady()) { - // @ts-ignore I dont know what would make typescript happy but I have a life -prf - navigationRef.navigate(name, params) + return Promise.race([ + new Promise(resolve => { + const handler = () => { + resolve() + navigationRef.removeListener('state', handler) + } + navigationRef.addListener('state', handler) + + // @ts-ignore I dont know what would make typescript happy but I have a life -prf + navigationRef.navigate(name, params) + }), + timeout(1e3), + ]) } + return Promise.resolve() } function resetToTab(tabName: 'HomeTab' | 'SearchTab' | 'NotificationsTab') { diff --git a/src/view/com/modals/Modal.tsx b/src/view/com/modals/Modal.tsx index 4f3f424a31..1fe1299d73 100644 --- a/src/view/com/modals/Modal.tsx +++ b/src/view/com/modals/Modal.tsx @@ -1,11 +1,12 @@ import React, {useRef, useEffect} from 'react' import {StyleSheet} from 'react-native' -import {SafeAreaView} from 'react-native-safe-area-context' +import {SafeAreaView, useSafeAreaInsets} from 'react-native-safe-area-context' import {observer} from 'mobx-react-lite' import BottomSheet from '@gorhom/bottom-sheet' import {useStores} from 'state/index' import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop' import {usePalette} from 'lib/hooks/usePalette' +import {timeout} from 'lib/async/timeout' import {navigate} from '../../../Navigation' import once from 'lodash.once' @@ -36,11 +37,13 @@ import * as SwitchAccountModal from './SwitchAccount' import * as LinkWarningModal from './LinkWarning' const DEFAULT_SNAPPOINTS = ['90%'] +const HANDLE_HEIGHT = 24 export const ModalsContainer = observer(function ModalsContainer() { const store = useStores() const bottomSheetRef = useRef(null) const pal = usePalette('default') + const safeAreaInsets = useSafeAreaInsets() const activeModal = store.shell.activeModals[store.shell.activeModals.length - 1] @@ -53,12 +56,16 @@ export const ModalsContainer = observer(function ModalsContainer() { navigateOnce('Profile', {name: activeModal.did}) } } - const onBottomSheetChange = (snapPoint: number) => { + const onBottomSheetChange = async (snapPoint: number) => { if (snapPoint === -1) { store.shell.closeModal() } else if (activeModal?.name === 'profile-preview' && snapPoint === 1) { - // ensure we navigate to Profile and close the modal - navigateOnce('Profile', {name: activeModal.did}) + await navigateOnce('Profile', {name: activeModal.did}) + // There is no particular callback for when the view has actually been presented. + // This delay gives us a decent chance the navigation has flushed *and* images have loaded. + // It's acceptable because the data is already being fetched + it usually takes longer anyway. + // TODO: Figure out why avatar/cover don't always show instantly from cache. + await timeout(200) store.shell.closeModal() } } @@ -75,6 +82,7 @@ export const ModalsContainer = observer(function ModalsContainer() { } }, [store.shell.isModalActive, bottomSheetRef, activeModal?.name]) + let needsSafeTopInset = false let snapPoints: (string | number)[] = DEFAULT_SNAPPOINTS let element if (activeModal?.name === 'confirm') { @@ -86,6 +94,7 @@ export const ModalsContainer = observer(function ModalsContainer() { } else if (activeModal?.name === 'profile-preview') { snapPoints = ProfilePreviewModal.snapPoints element = + needsSafeTopInset = true // Need to align with the target profile screen. } else if (activeModal?.name === 'server-input') { snapPoints = ServerInputModal.snapPoints element = @@ -164,10 +173,13 @@ export const ModalsContainer = observer(function ModalsContainer() { ) } + const topInset = needsSafeTopInset ? safeAreaInsets.top - HANDLE_HEIGHT : 0 return ( - + - + - +