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