Fix profile preview jump (#1693)

* Add top inset for profile preview to match target screen

* Avoid flicker by waiting for profile screen navigation

* Fix glimmer to align with the content

* A more reliable (but non-scientific) fix for the flash

* Lower the timeout
zio/stable
dan 2023-10-13 20:10:15 +01:00 committed by GitHub
parent d5ccbd76d5
commit f447eaa669
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 33 additions and 18 deletions

View File

@ -483,9 +483,21 @@ function navigate<K extends keyof AllNavigatorParams>(
params?: AllNavigatorParams[K], params?: AllNavigatorParams[K],
) { ) {
if (navigationRef.isReady()) { if (navigationRef.isReady()) {
return Promise.race([
new Promise<void>(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 // @ts-ignore I dont know what would make typescript happy but I have a life -prf
navigationRef.navigate(name, params) navigationRef.navigate(name, params)
}),
timeout(1e3),
])
} }
return Promise.resolve()
} }
function resetToTab(tabName: 'HomeTab' | 'SearchTab' | 'NotificationsTab') { function resetToTab(tabName: 'HomeTab' | 'SearchTab' | 'NotificationsTab') {

View File

@ -1,11 +1,12 @@
import React, {useRef, useEffect} from 'react' import React, {useRef, useEffect} from 'react'
import {StyleSheet} from 'react-native' 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 {observer} from 'mobx-react-lite'
import BottomSheet from '@gorhom/bottom-sheet' import BottomSheet from '@gorhom/bottom-sheet'
import {useStores} from 'state/index' import {useStores} from 'state/index'
import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop' import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop'
import {usePalette} from 'lib/hooks/usePalette' import {usePalette} from 'lib/hooks/usePalette'
import {timeout} from 'lib/async/timeout'
import {navigate} from '../../../Navigation' import {navigate} from '../../../Navigation'
import once from 'lodash.once' import once from 'lodash.once'
@ -36,11 +37,13 @@ import * as SwitchAccountModal from './SwitchAccount'
import * as LinkWarningModal from './LinkWarning' import * as LinkWarningModal from './LinkWarning'
const DEFAULT_SNAPPOINTS = ['90%'] const DEFAULT_SNAPPOINTS = ['90%']
const HANDLE_HEIGHT = 24
export const ModalsContainer = observer(function ModalsContainer() { export const ModalsContainer = observer(function ModalsContainer() {
const store = useStores() const store = useStores()
const bottomSheetRef = useRef<BottomSheet>(null) const bottomSheetRef = useRef<BottomSheet>(null)
const pal = usePalette('default') const pal = usePalette('default')
const safeAreaInsets = useSafeAreaInsets()
const activeModal = const activeModal =
store.shell.activeModals[store.shell.activeModals.length - 1] store.shell.activeModals[store.shell.activeModals.length - 1]
@ -53,12 +56,16 @@ export const ModalsContainer = observer(function ModalsContainer() {
navigateOnce('Profile', {name: activeModal.did}) navigateOnce('Profile', {name: activeModal.did})
} }
} }
const onBottomSheetChange = (snapPoint: number) => { const onBottomSheetChange = async (snapPoint: number) => {
if (snapPoint === -1) { if (snapPoint === -1) {
store.shell.closeModal() store.shell.closeModal()
} else if (activeModal?.name === 'profile-preview' && snapPoint === 1) { } else if (activeModal?.name === 'profile-preview' && snapPoint === 1) {
// ensure we navigate to Profile and close the modal await navigateOnce('Profile', {name: activeModal.did})
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() store.shell.closeModal()
} }
} }
@ -75,6 +82,7 @@ export const ModalsContainer = observer(function ModalsContainer() {
} }
}, [store.shell.isModalActive, bottomSheetRef, activeModal?.name]) }, [store.shell.isModalActive, bottomSheetRef, activeModal?.name])
let needsSafeTopInset = false
let snapPoints: (string | number)[] = DEFAULT_SNAPPOINTS let snapPoints: (string | number)[] = DEFAULT_SNAPPOINTS
let element let element
if (activeModal?.name === 'confirm') { if (activeModal?.name === 'confirm') {
@ -86,6 +94,7 @@ export const ModalsContainer = observer(function ModalsContainer() {
} else if (activeModal?.name === 'profile-preview') { } else if (activeModal?.name === 'profile-preview') {
snapPoints = ProfilePreviewModal.snapPoints snapPoints = ProfilePreviewModal.snapPoints
element = <ProfilePreviewModal.Component {...activeModal} /> element = <ProfilePreviewModal.Component {...activeModal} />
needsSafeTopInset = true // Need to align with the target profile screen.
} else if (activeModal?.name === 'server-input') { } else if (activeModal?.name === 'server-input') {
snapPoints = ServerInputModal.snapPoints snapPoints = ServerInputModal.snapPoints
element = <ServerInputModal.Component {...activeModal} /> element = <ServerInputModal.Component {...activeModal} />
@ -164,10 +173,13 @@ export const ModalsContainer = observer(function ModalsContainer() {
) )
} }
const topInset = needsSafeTopInset ? safeAreaInsets.top - HANDLE_HEIGHT : 0
return ( return (
<BottomSheet <BottomSheet
ref={bottomSheetRef} ref={bottomSheetRef}
snapPoints={snapPoints} snapPoints={snapPoints}
topInset={topInset}
handleHeight={HANDLE_HEIGHT}
index={store.shell.isModalActive ? 0 : -1} index={store.shell.isModalActive ? 0 : -1}
enablePanDownToClose enablePanDownToClose
android_keyboardInputMode="adjustResize" android_keyboardInputMode="adjustResize"

View File

@ -9,7 +9,6 @@ import {useAnalytics} from 'lib/analytics/analytics'
import {ProfileHeader} from '../profile/ProfileHeader' import {ProfileHeader} from '../profile/ProfileHeader'
import {InfoCircleIcon} from 'lib/icons' import {InfoCircleIcon} from 'lib/icons'
import {useNavigationState} from '@react-navigation/native' import {useNavigationState} from '@react-navigation/native'
import {isIOS} from 'platform/detection'
import {s} from 'lib/styles' import {s} from 'lib/styles'
export const snapPoints = [520, '100%'] export const snapPoints = [520, '100%']
@ -36,11 +35,7 @@ export const Component = observer(function ProfilePreviewImpl({
return ( return (
<View testID="profilePreview" style={[pal.view, s.flex1]}> <View testID="profilePreview" style={[pal.view, s.flex1]}>
<View <View style={[styles.headerWrapper]}>
style={[
styles.headerWrapper,
isLoading && isIOS && styles.headerPositionAdjust,
]}>
<ProfileHeader <ProfileHeader
view={model} view={model}
hideBackButton hideBackButton
@ -70,10 +65,6 @@ const styles = StyleSheet.create({
headerWrapper: { headerWrapper: {
height: 440, height: 440,
}, },
headerPositionAdjust: {
// HACK align the header for the profilescreen transition -prf
paddingTop: 23,
},
hintWrapper: { hintWrapper: {
height: 80, height: 80,
}, },

View File

@ -60,14 +60,14 @@ export const ProfileHeader = observer(function ProfileHeaderImpl({
if (!view || !view.hasLoaded) { if (!view || !view.hasLoaded) {
return ( return (
<View style={pal.view}> <View style={pal.view}>
<LoadingPlaceholder width="100%" height={120} /> <LoadingPlaceholder width="100%" height={153} />
<View <View
style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}> style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}>
<LoadingPlaceholder width={80} height={80} style={styles.br40} /> <LoadingPlaceholder width={80} height={80} style={styles.br40} />
</View> </View>
<View style={styles.content}> <View style={styles.content}>
<View style={[styles.buttonsLine]}> <View style={[styles.buttonsLine]}>
<LoadingPlaceholder width={100} height={31} style={styles.br50} /> <LoadingPlaceholder width={167} height={31} style={styles.br50} />
</View> </View>
<View> <View>
<Text type="title-2xl" style={[pal.text, styles.title]}> <Text type="title-2xl" style={[pal.text, styles.title]}>