make scrollview automatically adapt to footer height

This commit is contained in:
Samuel Newman
2025-06-19 22:46:12 +03:00
parent 3c92714e4e
commit 3ecfe20c28
11 changed files with 195 additions and 162 deletions
+30 -31
View File
@@ -204,38 +204,37 @@ function App() {
* that is set up in the InnerApp component above. * that is set up in the InnerApp component above.
*/ */
return ( return (
<GeolocationProvider> <SafeAreaProvider initialMetrics={initialWindowMetrics}>
<A11yProvider> <GeolocationProvider>
<KeyboardControllerProvider> <A11yProvider>
<SessionProvider> <KeyboardControllerProvider>
<PrefsStateProvider> <SessionProvider>
<I18nProvider> <PrefsStateProvider>
<ShellStateProvider> <I18nProvider>
<InvitesStateProvider> <ShellStateProvider>
<ModalStateProvider> <InvitesStateProvider>
<DialogStateProvider> <ModalStateProvider>
<LightboxStateProvider> <DialogStateProvider>
<PortalProvider> <LightboxStateProvider>
<BottomSheetProvider> <PortalProvider>
<StarterPackProvider> <BottomSheetProvider>
<SafeAreaProvider <StarterPackProvider>
initialMetrics={initialWindowMetrics}>
<InnerApp /> <InnerApp />
</SafeAreaProvider> </StarterPackProvider>
</StarterPackProvider> </BottomSheetProvider>
</BottomSheetProvider> </PortalProvider>
</PortalProvider> </LightboxStateProvider>
</LightboxStateProvider> </DialogStateProvider>
</DialogStateProvider> </ModalStateProvider>
</ModalStateProvider> </InvitesStateProvider>
</InvitesStateProvider> </ShellStateProvider>
</ShellStateProvider> </I18nProvider>
</I18nProvider> </PrefsStateProvider>
</PrefsStateProvider> </SessionProvider>
</SessionProvider> </KeyboardControllerProvider>
</KeyboardControllerProvider> </A11yProvider>
</A11yProvider> </GeolocationProvider>
</GeolocationProvider> </SafeAreaProvider>
) )
} }
+41 -38
View File
@@ -4,7 +4,10 @@ import './style.css'
import React, {useEffect, useState} from 'react' import React, {useEffect, useState} from 'react'
import {RootSiblingParent} from 'react-native-root-siblings' import {RootSiblingParent} from 'react-native-root-siblings'
import {SafeAreaProvider} from 'react-native-safe-area-context' import {
initialWindowMetrics,
SafeAreaProvider,
} from 'react-native-safe-area-context'
import {msg} from '@lingui/macro' import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
import * as Sentry from '@sentry/react-native' import * as Sentry from '@sentry/react-native'
@@ -129,18 +132,16 @@ function InnerApp() {
<UnreadNotifsProvider> <UnreadNotifsProvider>
<BackgroundNotificationPreferencesProvider> <BackgroundNotificationPreferencesProvider>
<MutedThreadsProvider> <MutedThreadsProvider>
<SafeAreaProvider> <ProgressGuideProvider>
<ProgressGuideProvider> <ServiceConfigProvider>
<ServiceConfigProvider> <HideBottomBarBorderProvider>
<HideBottomBarBorderProvider> <IntentDialogProvider>
<IntentDialogProvider> <Shell />
<Shell /> <NuxDialogs />
<NuxDialogs /> </IntentDialogProvider>
</IntentDialogProvider> </HideBottomBarBorderProvider>
</HideBottomBarBorderProvider> </ServiceConfigProvider>
</ServiceConfigProvider> </ProgressGuideProvider>
</ProgressGuideProvider>
</SafeAreaProvider>
</MutedThreadsProvider> </MutedThreadsProvider>
</BackgroundNotificationPreferencesProvider> </BackgroundNotificationPreferencesProvider>
</UnreadNotifsProvider> </UnreadNotifsProvider>
@@ -183,31 +184,33 @@ function App() {
* that is set up in the InnerApp component above. * that is set up in the InnerApp component above.
*/ */
return ( return (
<GeolocationProvider> <SafeAreaProvider initialMetrics={initialWindowMetrics}>
<A11yProvider> <GeolocationProvider>
<SessionProvider> <A11yProvider>
<PrefsStateProvider> <SessionProvider>
<I18nProvider> <PrefsStateProvider>
<ShellStateProvider> <I18nProvider>
<InvitesStateProvider> <ShellStateProvider>
<ModalStateProvider> <InvitesStateProvider>
<DialogStateProvider> <ModalStateProvider>
<LightboxStateProvider> <DialogStateProvider>
<PortalProvider> <LightboxStateProvider>
<StarterPackProvider> <PortalProvider>
<InnerApp /> <StarterPackProvider>
</StarterPackProvider> <InnerApp />
</PortalProvider> </StarterPackProvider>
</LightboxStateProvider> </PortalProvider>
</DialogStateProvider> </LightboxStateProvider>
</ModalStateProvider> </DialogStateProvider>
</InvitesStateProvider> </ModalStateProvider>
</ShellStateProvider> </InvitesStateProvider>
</I18nProvider> </ShellStateProvider>
</PrefsStateProvider> </I18nProvider>
</SessionProvider> </PrefsStateProvider>
</A11yProvider> </SessionProvider>
</GeolocationProvider> </A11yProvider>
</GeolocationProvider>
</SafeAreaProvider>
) )
} }
+26 -29
View File
@@ -1,20 +1,25 @@
import {forwardRef, memo, useContext, useMemo} from 'react' import {forwardRef, memo, useContext, useMemo} from 'react'
import {StyleSheet, View, type ViewProps, type ViewStyle} from 'react-native' import {
ScrollView,
type ScrollViewProps,
StyleSheet,
View,
type ViewProps,
type ViewStyle,
} from 'react-native'
import {type StyleProp} from 'react-native' import {type StyleProp} from 'react-native'
import { import {
KeyboardAwareScrollView, KeyboardAwareScrollView,
type KeyboardAwareScrollViewProps, type KeyboardAwareScrollViewProps,
} from 'react-native-keyboard-controller' } from 'react-native-keyboard-controller'
import Animated, {
type AnimatedScrollViewProps,
useAnimatedProps,
} from 'react-native-reanimated'
import {useSafeAreaInsets} from 'react-native-safe-area-context' import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {isWeb} from '#/platform/detection' import {isIOS, isWeb} from '#/platform/detection'
import {useShellLayout} from '#/state/shell/shell-layout' import {useShellLayout} from '#/state/shell/shell-layout'
import { import {
android,
atoms as a, atoms as a,
ios,
useBreakpoints, useBreakpoints,
useLayoutBreakpoints, useLayoutBreakpoints,
useTheme, useTheme,
@@ -52,9 +57,7 @@ export const Screen = memo(function Screen({
) )
}) })
export type ContentProps = AnimatedScrollViewProps & { export type ContentProps = ScrollViewProps & {
style?: StyleProp<ViewStyle>
contentContainerStyle?: StyleProp<ViewStyle>
ignoreTabletLayoutOffset?: boolean ignoreTabletLayoutOffset?: boolean
} }
@@ -62,7 +65,7 @@ export type ContentProps = AnimatedScrollViewProps & {
* Default scroll view for simple pages * Default scroll view for simple pages
*/ */
export const Content = memo( export const Content = memo(
forwardRef<Animated.ScrollView, ContentProps>(function Content( forwardRef<ScrollView, ContentProps>(function Content(
{ {
children, children,
style, style,
@@ -74,39 +77,33 @@ export const Content = memo(
) { ) {
const t = useTheme() const t = useTheme()
const {footerHeight} = useShellLayout() const {footerHeight} = useShellLayout()
const animatedProps = useAnimatedProps(() => {
return {
scrollIndicatorInsets: {
bottom: footerHeight.get(),
top: 0,
right: 1,
},
} satisfies AnimatedScrollViewProps
})
return ( return (
<Animated.ScrollView <ScrollView
ref={ref} ref={ref}
id="content" id="content"
automaticallyAdjustsScrollIndicatorInsets={false} automaticallyAdjustsScrollIndicatorInsets={false}
scrollIndicatorInsets={{
bottom: footerHeight,
top: 0,
right: 1,
}}
indicatorStyle={t.scheme === 'dark' ? 'white' : 'black'} indicatorStyle={t.scheme === 'dark' ? 'white' : 'black'}
// sets the scroll inset to the height of the footer
animatedProps={animatedProps}
style={[scrollViewStyles.common, style]} style={[scrollViewStyles.common, style]}
contentInset={ios({top: 0, left: 0, bottom: footerHeight, right: 0})}
contentContainerStyle={[ contentContainerStyle={[
scrollViewStyles.contentContainer, !isIOS && {paddingBottom: footerHeight},
contentContainerStyle, contentContainerStyle,
]} ]}
{...props}> {...props}>
{isWeb ? ( {isWeb ? (
<Center ignoreTabletLayoutOffset={ignoreTabletLayoutOffset}> <Center ignoreTabletLayoutOffset={ignoreTabletLayoutOffset}>
{/* @ts-expect-error web only -esb */}
{children} {children}
</Center> </Center>
) : ( ) : (
children children
)} )}
</Animated.ScrollView> </ScrollView>
) )
}), }),
) )
@@ -115,9 +112,6 @@ const scrollViewStyles = StyleSheet.create({
common: { common: {
width: '100%', width: '100%',
}, },
contentContainer: {
paddingBottom: 100,
},
}) })
export type KeyboardAwareContentProps = KeyboardAwareScrollViewProps & { export type KeyboardAwareContentProps = KeyboardAwareScrollViewProps & {
@@ -136,11 +130,14 @@ export const KeyboardAwareContent = memo(function LayoutKeyboardAwareContent({
contentContainerStyle, contentContainerStyle,
...props ...props
}: KeyboardAwareContentProps) { }: KeyboardAwareContentProps) {
const {footerHeight} = useShellLayout()
return ( return (
<KeyboardAwareScrollView <KeyboardAwareScrollView
style={[scrollViewStyles.common, style]} style={[scrollViewStyles.common, style]}
contentInset={ios({top: 0, left: 0, bottom: footerHeight, right: 0})}
contentContainerStyle={[ contentContainerStyle={[
scrollViewStyles.contentContainer, android({paddingBottom: footerHeight}),
web({paddingBottom: footerHeight}),
contentContainerStyle, contentContainerStyle,
]} ]}
keyboardShouldPersistTaps="handled" keyboardShouldPersistTaps="handled"
+1 -5
View File
@@ -40,11 +40,7 @@ export function useMinimalShellFooterTransform() {
opacity: Math.pow(1 - footerModeValue, 2), opacity: Math.pow(1 - footerModeValue, 2),
transform: [ transform: [
{ {
translateY: interpolate( translateY: interpolate(footerModeValue, [0, 1], [0, footerHeight]),
footerModeValue,
[0, 1],
[0, footerHeight.get()],
),
}, },
], ],
} }
@@ -279,14 +279,14 @@ export function MessagesList({
onMove: e => { onMove: e => {
'worklet' 'worklet'
keyboardHeight.set(e.height) keyboardHeight.set(e.height)
if (e.height > footerHeight.get()) { if (e.height > footerHeight) {
scrollTo(flatListRef, 0, 1e7, false) scrollTo(flatListRef, 0, 1e7, false)
} }
}, },
onEnd: e => { onEnd: e => {
'worklet' 'worklet'
keyboardHeight.set(e.height) keyboardHeight.set(e.height)
if (e.height > footerHeight.get()) { if (e.height > footerHeight) {
scrollTo(flatListRef, 0, 1e7, false) scrollTo(flatListRef, 0, 1e7, false)
} }
keyboardIsOpening.set(false) keyboardIsOpening.set(false)
@@ -296,13 +296,11 @@ export function MessagesList({
) )
const animatedListStyle = useAnimatedStyle(() => ({ const animatedListStyle = useAnimatedStyle(() => ({
marginBottom: Math.max(keyboardHeight.get(), footerHeight.get()), marginBottom: Math.max(keyboardHeight.get(), footerHeight),
})) }))
const animatedStickyViewStyle = useAnimatedStyle(() => ({ const animatedStickyViewStyle = useAnimatedStyle(() => ({
transform: [ transform: [{translateY: -Math.max(keyboardHeight.get(), footerHeight)}],
{translateY: -Math.max(keyboardHeight.get(), footerHeight.get())},
],
})) }))
// -- Message sending // -- Message sending
@@ -446,6 +444,8 @@ export function MessagesList({
ListHeaderComponent={ ListHeaderComponent={
<MaybeLoader isLoading={convoState.isFetchingHistory} /> <MaybeLoader isLoading={convoState.isFetchingHistory} />
} }
contentInset={{bottom: 0}}
contentContainerStyle={{paddingBottom: 0}}
/> />
</ScrollProvider> </ScrollProvider>
<Animated.View style={animatedStickyViewStyle}> <Animated.View style={animatedStickyViewStyle}>
+23 -13
View File
@@ -1,6 +1,5 @@
import {useCallback, useMemo, useRef, useState} from 'react' import {useCallback, useMemo, useRef, useState} from 'react'
import {useWindowDimensions, View} from 'react-native' import {type LayoutChangeEvent, useWindowDimensions, View} from 'react-native'
import Animated, {useAnimatedStyle} from 'react-native-reanimated'
import {Trans} from '@lingui/macro' import {Trans} from '@lingui/macro'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender' import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
@@ -50,6 +49,8 @@ export function PostThread({uri}: {uri: string}) {
const anchorPostSource = useUnstablePostSource(uri) const anchorPostSource = useUnstablePostSource(uri)
const feedFeedback = useFeedFeedback(anchorPostSource?.feed, hasSession) const feedFeedback = useFeedFeedback(anchorPostSource?.feed, hasSession)
const [promptHeight, setPromptHeight] = useState(43)
/* /*
* One query to rule them all * One query to rule them all
*/ */
@@ -485,6 +486,7 @@ export function PostThread({uri}: {uri: string}) {
) )
const defaultListFooterHeight = hasParents ? windowHeight - 200 : undefined const defaultListFooterHeight = hasParents ? windowHeight - 200 : undefined
const hasPrompt = !gtMobile && canReply && hasSession
return ( return (
<> <>
@@ -553,29 +555,37 @@ export function PostThread({uri}: {uri: string}) {
initialNumToRender={initialNumToRender} initialNumToRender={initialNumToRender}
windowSize={11} windowSize={11}
sideBorders={false} sideBorders={false}
footerExtensionHeight={hasPrompt ? promptHeight : 0}
/> />
)} )}
{!gtMobile && canReply && hasSession && ( {hasPrompt && (
<MobileComposePrompt onPressReply={onReplyToAnchor} /> <MobileComposePrompt
onPressReply={onReplyToAnchor}
onLayout={evt =>
setPromptHeight(Math.round(evt.nativeEvent.layout.height))
}
/>
)} )}
</> </>
) )
} }
function MobileComposePrompt({onPressReply}: {onPressReply: () => unknown}) { function MobileComposePrompt({
onPressReply,
onLayout,
}: {
onPressReply: () => unknown
onLayout?: (event: LayoutChangeEvent) => void
}) {
const {footerHeight} = useShellLayout() const {footerHeight} = useShellLayout()
const animatedStyle = useAnimatedStyle(() => {
return {
bottom: footerHeight.get(),
}
})
return ( return (
<Animated.View style={[a.fixed, a.left_0, a.right_0, animatedStyle]}> <View
style={[a.fixed, a.left_0, a.right_0, {bottom: footerHeight}]}
onLayout={onLayout}>
<PostThreadComposePrompt onPressCompose={onPressReply} /> <PostThreadComposePrompt onPressCompose={onPressReply} />
</Animated.View> </View>
) )
} }
+42 -24
View File
@@ -1,12 +1,18 @@
import React from 'react' import {createContext, useContext, useMemo, useState} from 'react'
import {SharedValue, useSharedValue} from 'react-native-reanimated' import {type SharedValue, useSharedValue} from 'react-native-reanimated'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
type StateContext = { import {clamp} from '#/lib/numbers'
import {isWeb} from '#/platform/detection'
import {atoms as a, platform, useBreakpoints} from '#/alf'
type LayoutContextValue = {
headerHeight: SharedValue<number> headerHeight: SharedValue<number>
footerHeight: SharedValue<number> footerHeight: number
setFooterHeight: (height: number) => void
} }
const stateContext = React.createContext<StateContext>({ const LayoutContext = createContext<LayoutContextValue>({
headerHeight: { headerHeight: {
value: 0, value: 0,
addListener() {}, addListener() {},
@@ -17,33 +23,45 @@ const stateContext = React.createContext<StateContext>({
}, },
set() {}, set() {},
}, },
footerHeight: { footerHeight: 0,
value: 0, setFooterHeight: () => {},
addListener() {},
removeListener() {},
modify() {},
get() {
return 0
},
set() {},
},
}) })
export function Provider({children}: React.PropsWithChildren<{}>) { export function Provider({children}: React.PropsWithChildren<{}>) {
const headerHeight = useSharedValue(0) const headerHeight = useSharedValue(0)
const footerHeight = useSharedValue(0) const insets = useSafeAreaInsets()
const {gtMobile} = useBreakpoints()
const value = React.useMemo( const [footerHeight, setFooterHeight] = useState(() =>
() => ({ platform({
headerHeight, // try and precisely guess the footer height, then round it to 4 decimal places
footerHeight, // to remove floating point imprecision. if we can guess it exactly,
// we get to skip a rerender
native: round4dp(
47 + a.border.borderWidth + clamp(insets.bottom, 15, 60),
),
web: 58,
default: 0,
}), }),
[headerHeight, footerHeight],
) )
return <stateContext.Provider value={value}>{children}</stateContext.Provider> const value = useMemo(
() => ({
headerHeight,
footerHeight: isWeb && gtMobile ? 0 : footerHeight,
setFooterHeight: (height: number) => setFooterHeight(round4dp(height)),
}),
[headerHeight, footerHeight, setFooterHeight, gtMobile],
)
return (
<LayoutContext.Provider value={value}>{children}</LayoutContext.Provider>
)
} }
export function useShellLayout() { export function useShellLayout() {
return React.useContext(stateContext) return useContext(LayoutContext)
}
function round4dp(value: number) {
return Math.round(value * 10000) / 10000
} }
+3 -8
View File
@@ -1,6 +1,6 @@
import React, {memo, useRef, useState} from 'react' import React, {memo, useRef, useState} from 'react'
import {useWindowDimensions, View} from 'react-native' import {useWindowDimensions, View} from 'react-native'
import {runOnJS, useAnimatedStyle} from 'react-native-reanimated' import {runOnJS} from 'react-native-reanimated'
import Animated from 'react-native-reanimated' import Animated from 'react-native-reanimated'
import { import {
AppBskyFeedDefs, AppBskyFeedDefs,
@@ -735,14 +735,9 @@ ThreadMenu = memo(ThreadMenu)
function MobileComposePrompt({onPressReply}: {onPressReply: () => unknown}) { function MobileComposePrompt({onPressReply}: {onPressReply: () => unknown}) {
const {footerHeight} = useShellLayout() const {footerHeight} = useShellLayout()
const animatedStyle = useAnimatedStyle(() => {
return {
bottom: footerHeight.get(),
}
})
return ( return (
<Animated.View style={[a.fixed, a.left_0, a.right_0, animatedStyle]}> <Animated.View
style={[a.fixed, a.left_0, a.right_0, {bottom: footerHeight}]}>
<PostThreadComposePrompt onPressCompose={onPressReply} /> <PostThreadComposePrompt onPressCompose={onPressReply} />
</Animated.View> </Animated.View>
) )
+19 -2
View File
@@ -11,9 +11,10 @@ import {useAnimatedScrollHandler} from '#/lib/hooks/useAnimatedScrollHandler_FIX
import {useDedupe} from '#/lib/hooks/useDedupe' import {useDedupe} from '#/lib/hooks/useDedupe'
import {useScrollHandlers} from '#/lib/ScrollContext' import {useScrollHandlers} from '#/lib/ScrollContext'
import {addStyle} from '#/lib/styles' import {addStyle} from '#/lib/styles'
import {isIOS} from '#/platform/detection' import {isAndroid, isIOS} from '#/platform/detection'
import {useLightbox} from '#/state/lightbox' import {useLightbox} from '#/state/lightbox'
import {useTheme} from '#/alf' import {useShellLayout} from '#/state/shell/shell-layout'
import {ios, useTheme} from '#/alf'
import {FlatList_INTERNAL} from './Views' import {FlatList_INTERNAL} from './Views'
export type ListMethods = FlatList_INTERNAL export type ListMethods = FlatList_INTERNAL
@@ -38,6 +39,7 @@ export type ListProps<ItemT = any> = Omit<
disableFullWindowScroll?: boolean disableFullWindowScroll?: boolean
sideBorders?: boolean sideBorders?: boolean
progressViewOffset?: number progressViewOffset?: number
footerExtensionHeight?: number
} }
export type ListRef = React.MutableRefObject<FlatList_INTERNAL | null> export type ListRef = React.MutableRefObject<FlatList_INTERNAL | null>
@@ -54,6 +56,8 @@ let List = React.forwardRef<ListMethods, ListProps>(
style, style,
progressViewOffset, progressViewOffset,
automaticallyAdjustsScrollIndicatorInsets = false, automaticallyAdjustsScrollIndicatorInsets = false,
contentContainerStyle,
footerExtensionHeight = 0,
...props ...props
}, },
ref, ref,
@@ -62,6 +66,7 @@ let List = React.forwardRef<ListMethods, ListProps>(
const t = useTheme() const t = useTheme()
const dedupe = useDedupe(400) const dedupe = useDedupe(400)
const {activeLightbox} = useLightbox() const {activeLightbox} = useLightbox()
const {footerHeight} = useShellLayout()
function handleScrolledDownChange(didScrollDown: boolean) { function handleScrolledDownChange(didScrollDown: boolean) {
onScrolledDownChange?.(didScrollDown) onScrolledDownChange?.(didScrollDown)
@@ -156,14 +161,26 @@ let List = React.forwardRef<ListMethods, ListProps>(
onViewableItemsChanged={onViewableItemsChanged} onViewableItemsChanged={onViewableItemsChanged}
viewabilityConfig={viewabilityConfig} viewabilityConfig={viewabilityConfig}
{...props} {...props}
contentContainerStyle={[
isAndroid && {paddingBottom: footerHeight + footerExtensionHeight},
contentContainerStyle,
]}
automaticallyAdjustsScrollIndicatorInsets={ automaticallyAdjustsScrollIndicatorInsets={
automaticallyAdjustsScrollIndicatorInsets automaticallyAdjustsScrollIndicatorInsets
} }
scrollIndicatorInsets={{ scrollIndicatorInsets={{
top: headerOffset, top: headerOffset,
right: 1, right: 1,
bottom: footerHeight + footerExtensionHeight,
...props.scrollIndicatorInsets, ...props.scrollIndicatorInsets,
}} }}
contentInset={ios({
top: 0,
left: 0,
right: 0,
bottom: footerHeight + footerExtensionHeight,
...props.contentInset,
})}
indicatorStyle={t.scheme === 'dark' ? 'white' : 'black'} indicatorStyle={t.scheme === 'dark' ? 'white' : 'black'}
contentOffset={contentOffset} contentOffset={contentOffset}
refreshControl={refreshControl} refreshControl={refreshControl}
+2 -4
View File
@@ -60,7 +60,7 @@ export function BottomBar({navigation}: BottomTabBarProps) {
const pal = usePalette('default') const pal = usePalette('default')
const {_} = useLingui() const {_} = useLingui()
const safeAreaInsets = useSafeAreaInsets() const safeAreaInsets = useSafeAreaInsets()
const {footerHeight} = useShellLayout() const {setFooterHeight} = useShellLayout()
const {isAtHome, isAtSearch, isAtNotifications, isAtMyProfile, isAtMessages} = const {isAtHome, isAtSearch, isAtNotifications, isAtMyProfile, isAtMessages} =
useNavigationTabState() useNavigationTabState()
const numUnreadNotifications = useUnreadNotifications() const numUnreadNotifications = useUnreadNotifications()
@@ -152,9 +152,7 @@ export function BottomBar({navigation}: BottomTabBarProps) {
{paddingBottom: clamp(safeAreaInsets.bottom, 15, 60)}, {paddingBottom: clamp(safeAreaInsets.bottom, 15, 60)},
footerMinimalShellTransform, footerMinimalShellTransform,
]} ]}
onLayout={e => { onLayout={evt => setFooterHeight(evt.nativeEvent.layout.height)}>
footerHeight.set(e.nativeEvent.layout.height)
}}>
{hasSession ? ( {hasSession ? (
<> <>
<Btn <Btn
+2 -2
View File
@@ -51,7 +51,7 @@ export function BottomBarWeb() {
const footerMinimalShellTransform = useMinimalShellFooterTransform() const footerMinimalShellTransform = useMinimalShellFooterTransform()
const {requestSwitchToAccount} = useLoggedOutViewControls() const {requestSwitchToAccount} = useLoggedOutViewControls()
const closeAllActiveElements = useCloseAllActiveElements() const closeAllActiveElements = useCloseAllActiveElements()
const {footerHeight} = useShellLayout() const {setFooterHeight} = useShellLayout()
const hideBorder = useHideBottomBarBorder() const hideBorder = useHideBottomBarBorder()
const iconWidth = 26 const iconWidth = 26
@@ -83,7 +83,7 @@ export function BottomBarWeb() {
: t.atoms.border_contrast_low, : t.atoms.border_contrast_low,
footerMinimalShellTransform, footerMinimalShellTransform,
]} ]}
onLayout={event => footerHeight.set(event.nativeEvent.layout.height)}> onLayout={evt => setFooterHeight(evt.nativeEvent.layout.height)}>
{hasSession ? ( {hasSession ? (
<> <>
<NavItem <NavItem