[iPad support] Custom tab navigator for native tablet, with side nav (#3385)

* fix fab position

* show left nav

* custom tab navigator for tablets

* re-disable right nav on tablet

* fix unread count not being rounded

* use mobile subpage header on native tablet

* fix feed being hidden under header

* rename misleading variable name

* remove drawer on native tablet, as on web tablet

* fix prompt styles on tablet

* import isWeb (ts-ignore 😡)

* disable emoji picker on tablet

* prettier format
This commit is contained in:
Samuel Newman
2024-04-10 14:59:54 +01:00
committed by GitHub
parent d04d88d235
commit 842f0c96a3
18 changed files with 420 additions and 221 deletions
+1 -1
View File
@@ -484,7 +484,7 @@ export const ComposePost = observer(function ComposePost({
<OpenCameraBtn gallery={gallery} />
</>
) : null}
{!isMobile ? (
{!isMobile && !isNative ? (
<Pressable
onPress={onEmojiButtonPress}
accessibilityRole="button"
+3 -3
View File
@@ -7,7 +7,7 @@ import {useQueryClient} from '@tanstack/react-query'
import {getRootNavigation, getTabState, TabState} from '#/lib/routes/helpers'
import {logEvent, useGate} from '#/lib/statsig/statsig'
import {isNative} from '#/platform/detection'
import {isNative, isNativeTablet} from '#/platform/detection'
import {listenSoftReset} from '#/state/events'
import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed'
import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed'
@@ -155,9 +155,9 @@ export function FeedPage({
}
function useHeaderOffset() {
const {isDesktop, isTablet} = useWebMediaQueries()
const {isTabletOrDesktop} = useWebMediaQueries()
const {fontScale} = useWindowDimensions()
if (isDesktop || isTablet) {
if (isTabletOrDesktop && !isNativeTablet) {
return 0
}
const navBarHeight = 42
+23 -18
View File
@@ -11,7 +11,7 @@ import {useLingui} from '@lingui/react'
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
import {useSetDrawerOpen} from '#/state/shell/drawer-open'
import {useShellLayout} from '#/state/shell/shell-layout'
import {isWeb} from 'platform/detection'
import {isNativeTablet, isWeb} from 'platform/detection'
import {Logo} from '#/view/icons/Logo'
import {IS_DEV} from '#/env'
@@ -42,23 +42,28 @@ export function HomeHeaderLayoutMobile({
headerHeight.value = e.nativeEvent.layout.height
}}>
<View style={[pal.view, styles.topBar]}>
<View style={[pal.view, {width: 100}]}>
<TouchableOpacity
testID="viewHeaderDrawerBtn"
onPress={onPressAvi}
accessibilityRole="button"
accessibilityLabel={_(msg`Open navigation`)}
accessibilityHint={_(
msg`Access profile and other navigation links`,
)}
hitSlop={HITSLOP_10}>
<FontAwesomeIcon
icon="bars"
size={18}
color={pal.colors.textLight}
/>
</TouchableOpacity>
</View>
{!isNativeTablet ? (
<View style={[pal.view, {width: 100}]}>
<TouchableOpacity
testID="viewHeaderDrawerBtn"
onPress={onPressAvi}
accessibilityRole="button"
accessibilityLabel={_(msg`Open navigation`)}
accessibilityHint={_(
msg`Access profile and other navigation links`,
)}
hitSlop={HITSLOP_10}>
<FontAwesomeIcon
icon="bars"
size={18}
color={pal.colors.textLight}
/>
</TouchableOpacity>
</View>
) : (
// subtract width of LeftNav
<View style={{width: 100 - 77}} />
)}
<View>
<Logo width={30} />
</View>
@@ -13,7 +13,7 @@ import {sanitizeHandle} from 'lib/strings/handles'
import {makeProfileLink} from 'lib/routes/links'
import {NavigationProp} from 'lib/routes/types'
import {BACK_HITSLOP} from 'lib/constants'
import {isNative} from 'platform/detection'
import {isNative, isNativeTablet} from 'platform/detection'
import {useLightboxControls, ImagesLightbox} from '#/state/lightbox'
import {useLingui} from '@lingui/react'
import {Trans, msg} from '@lingui/macro'
@@ -71,9 +71,11 @@ export function ProfileSubpageHeader({
}
}, [openLightbox, avatar])
const isMobileLayout = isMobile || isNativeTablet
return (
<CenteredView style={pal.view}>
{isMobile && (
{isMobileLayout && (
<View
style={[
{
@@ -82,7 +84,7 @@ export function ProfileSubpageHeader({
borderBottomWidth: 1,
paddingTop: isNative ? 0 : 8,
paddingBottom: 8,
paddingHorizontal: isMobile ? 12 : 14,
paddingHorizontal: isMobileLayout ? 12 : 14,
},
pal.border,
]}>
@@ -119,7 +121,7 @@ export function ProfileSubpageHeader({
gap: 10,
paddingTop: 14,
paddingBottom: 6,
paddingHorizontal: isMobile ? 12 : 14,
paddingHorizontal: isMobileLayout ? 12 : 14,
}}>
<Pressable
testID="headerAviButton"
@@ -170,7 +172,7 @@ export function ProfileSubpageHeader({
</Text>
)}
</View>
{!isMobile && (
{!isMobileLayout && (
<View
style={{
flexDirection: 'row',
+3 -3
View File
@@ -14,7 +14,7 @@ import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {useAnalytics} from 'lib/analytics/analytics'
import {NavigationProp} from 'lib/routes/types'
import {useSetDrawerOpen} from '#/state/shell'
import {isWeb} from '#/platform/detection'
import {isNativeTablet, isWeb} from '#/platform/detection'
const BACK_HITSLOP = {left: 20, top: 20, right: 50, bottom: 20}
@@ -71,13 +71,13 @@ export function SimpleViewHeader({
icon="angle-left"
style={[styles.backIcon, pal.text]}
/>
) : (
) : !isNativeTablet ? (
<FontAwesomeIcon
size={18}
icon="bars"
style={[styles.backIcon, pal.textLight]}
/>
)}
) : null}
</TouchableOpacity>
) : null}
{children}
+2 -1
View File
@@ -1,7 +1,8 @@
import React from 'react'
import {View} from 'react-native'
import {FABInner, FABProps} from './FABInner'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {FABInner, FABProps} from './FABInner'
export const FAB = (_opts: FABProps) => {
const {isDesktop} = useWebMediaQueries()
+2 -2
View File
@@ -4,7 +4,7 @@ import Animated from 'react-native-reanimated'
import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {LinearGradient} from 'expo-linear-gradient'
import {isNativeTablet, isWeb} from '#/platform/detection'
import {isWeb} from '#/platform/detection'
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {clamp} from 'lib/numbers'
@@ -40,7 +40,7 @@ export function FABInner({testID, icon, ...props}: FABProps) {
styles.outer,
size,
tabletSpacing,
(isMobile || isNativeTablet) && fabMinimalShellTransform,
isMobile && fabMinimalShellTransform,
]}>
<LinearGradient
colors={[gradients.blueLight.start, gradients.blueLight.end]}
@@ -1,7 +1,8 @@
import React from 'react'
import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native'
import {usePalette} from 'lib/hooks/usePalette'
import {useColorSchemeStyle} from 'lib/hooks/useColorSchemeStyle'
import {usePalette} from 'lib/hooks/usePalette'
interface Props {
testID?: string
+16 -15
View File
@@ -4,15 +4,16 @@ import Animated from 'react-native-reanimated'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {useMediaQuery} from 'react-responsive'
import {HITSLOP_20} from 'lib/constants'
import {useMinimalShellMode} from 'lib/hooks/useMinimalShellMode'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {colors} from 'lib/styles'
import {HITSLOP_20} from '#/lib/constants'
import {useMinimalShellMode} from '#/lib/hooks/useMinimalShellMode'
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
import {colors} from '#/lib/styles'
import {isNativeTablet, isWeb} from '#/platform/detection'
import {useSession} from '#/state/session'
import {useTheme} from '#/alf'
const AnimatedTouchableOpacity =
Animated.createAnimatedComponent(TouchableOpacity)
import {isNativeTablet, isWeb} from 'platform/detection'
import {useSession} from 'state/session'
export function LoadLatestBtn({
onPress,
@@ -23,7 +24,7 @@ export function LoadLatestBtn({
label: string
showIndicator: boolean
}) {
const pal = usePalette('default')
const t = useTheme()
const {hasSession} = useSession()
const {isDesktop, isTablet, isMobile, isTabletOrMobile} = useWebMediaQueries()
const {fabMinimalShellTransform} = useMinimalShellMode()
@@ -33,9 +34,7 @@ export function LoadLatestBtn({
// Adjust height of the fab if we have a session only on mobile web. If we don't have a session, we want to adjust
// it on both tablet and mobile since we are showing the bottom bar (see createNativeStackNavigatorWithAuth)
const showBottomBar = hasSession
? isMobile || isNativeTablet
: isTabletOrMobile
const showBottomBar = hasSession ? isMobile : isTabletOrMobile
return (
<AnimatedTouchableOpacity
@@ -46,8 +45,8 @@ export function LoadLatestBtn({
? styles.loadLatestOutOfLine
: styles.loadLatestInline),
isTablet && !isNativeTablet && styles.loadLatestInline,
pal.borderDark,
pal.view,
t.atoms.border_contrast_high,
t.atoms.bg,
showBottomBar && fabMinimalShellTransform,
]}
onPress={onPress}
@@ -55,8 +54,10 @@ export function LoadLatestBtn({
accessibilityRole="button"
accessibilityLabel={label}
accessibilityHint="">
<FontAwesomeIcon icon="angle-up" color={pal.colors.text} size={19} />
{showIndicator && <View style={[styles.indicator, pal.borderDark]} />}
<FontAwesomeIcon icon="angle-up" color={t.atoms.text.color} size={19} />
{showIndicator && (
<View style={[styles.indicator, t.atoms.border_contrast_high]} />
)}
</AnimatedTouchableOpacity>
)
}