Better tablet layout (#7656)
* better tablet layout * adjust left nav spacing * add right nav to pwi * clearer logic * fix a couple screens that don't need the tablet layout * fix horiz scroll bar * fix double trending * fix ts-ignore * fix labeller screen * don't offset things within dialogs * fix load latest button (and add scale animation) * center loader on home screen * adjust break points * adjust left nav spacing * fix load latest btn (again) * add lang select to right nav if left nav is minimal * fix double scrollbar on tiny screens * fix scrollbar * fix type errors
This commit is contained in:
@@ -26,3 +26,18 @@ export function useBreakpoints(): Record<Breakpoint, boolean> & {
|
|||||||
}
|
}
|
||||||
}, [gtPhone, gtMobile, gtTablet])
|
}, [gtPhone, gtMobile, gtTablet])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fine-tuned breakpoints for the shell layout
|
||||||
|
*/
|
||||||
|
export function useLayoutBreakpoints() {
|
||||||
|
const rightNavVisible = useMediaQuery({minWidth: 1075})
|
||||||
|
const centerColumnOffset = useMediaQuery({minWidth: 1075, maxWidth: 1300})
|
||||||
|
const leftNavMinimal = useMediaQuery({maxWidth: 1300})
|
||||||
|
|
||||||
|
return {
|
||||||
|
rightNavVisible,
|
||||||
|
centerColumnOffset,
|
||||||
|
leftNavMinimal,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export const Context = React.createContext<DialogContextProps>({
|
|||||||
nativeSnapPoint: BottomSheetSnapPoint.Hidden,
|
nativeSnapPoint: BottomSheetSnapPoint.Hidden,
|
||||||
disableDrag: false,
|
disableDrag: false,
|
||||||
setDisableDrag: () => {},
|
setDisableDrag: () => {},
|
||||||
|
isWithinDialog: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
export function useDialogContext() {
|
export function useDialogContext() {
|
||||||
|
|||||||
@@ -154,6 +154,7 @@ export function Outer({
|
|||||||
nativeSnapPoint: snapPoint,
|
nativeSnapPoint: snapPoint,
|
||||||
disableDrag,
|
disableDrag,
|
||||||
setDisableDrag,
|
setDisableDrag,
|
||||||
|
isWithinDialog: true,
|
||||||
}),
|
}),
|
||||||
[close, snapPoint, disableDrag, setDisableDrag],
|
[close, snapPoint, disableDrag, setDisableDrag],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ export function Outer({
|
|||||||
nativeSnapPoint: 0,
|
nativeSnapPoint: 0,
|
||||||
disableDrag: false,
|
disableDrag: false,
|
||||||
setDisableDrag: () => {},
|
setDisableDrag: () => {},
|
||||||
|
isWithinDialog: true,
|
||||||
}),
|
}),
|
||||||
[close],
|
[close],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ export type DialogContextProps = {
|
|||||||
nativeSnapPoint: BottomSheetSnapPoint
|
nativeSnapPoint: BottomSheetSnapPoint
|
||||||
disableDrag: boolean
|
disableDrag: boolean
|
||||||
setDisableDrag: React.Dispatch<React.SetStateAction<boolean>>
|
setDisableDrag: React.Dispatch<React.SetStateAction<boolean>>
|
||||||
|
// in the event that the hook is used outside of a dialog
|
||||||
|
isWithinDialog: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type DialogControlOpenOptions = {
|
export type DialogControlOpenOptions = {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
TextStyleProp,
|
TextStyleProp,
|
||||||
useBreakpoints,
|
useBreakpoints,
|
||||||
useGutters,
|
useGutters,
|
||||||
|
useLayoutBreakpoints,
|
||||||
useTheme,
|
useTheme,
|
||||||
web,
|
web,
|
||||||
} from '#/alf'
|
} from '#/alf'
|
||||||
@@ -23,6 +24,7 @@ import {Menu_Stroke2_Corner0_Rounded as Menu} from '#/components/icons/Menu'
|
|||||||
import {
|
import {
|
||||||
BUTTON_VISUAL_ALIGNMENT_OFFSET,
|
BUTTON_VISUAL_ALIGNMENT_OFFSET,
|
||||||
HEADER_SLOT_SIZE,
|
HEADER_SLOT_SIZE,
|
||||||
|
SCROLLBAR_OFFSET,
|
||||||
} from '#/components/Layout/const'
|
} from '#/components/Layout/const'
|
||||||
import {ScrollbarOffsetContext} from '#/components/Layout/context'
|
import {ScrollbarOffsetContext} from '#/components/Layout/context'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
@@ -42,6 +44,7 @@ export function Outer({
|
|||||||
const gutters = useGutters([0, 'base'])
|
const gutters = useGutters([0, 'base'])
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
const {isWithinOffsetView} = useContext(ScrollbarOffsetContext)
|
const {isWithinOffsetView} = useContext(ScrollbarOffsetContext)
|
||||||
|
const {centerColumnOffset} = useLayoutBreakpoints()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
@@ -60,7 +63,12 @@ export function Outer({
|
|||||||
}),
|
}),
|
||||||
t.atoms.border_contrast_low,
|
t.atoms.border_contrast_low,
|
||||||
gtMobile && [a.mx_auto, {maxWidth: 600}],
|
gtMobile && [a.mx_auto, {maxWidth: 600}],
|
||||||
!isWithinOffsetView && a.scrollbar_offset,
|
!isWithinOffsetView && {
|
||||||
|
transform: [
|
||||||
|
{translateX: centerColumnOffset ? -150 : 0},
|
||||||
|
{translateX: web(SCROLLBAR_OFFSET) ?? 0},
|
||||||
|
],
|
||||||
|
},
|
||||||
]}>
|
]}>
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -13,7 +13,15 @@ import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
|||||||
|
|
||||||
import {isWeb} from '#/platform/detection'
|
import {isWeb} from '#/platform/detection'
|
||||||
import {useShellLayout} from '#/state/shell/shell-layout'
|
import {useShellLayout} from '#/state/shell/shell-layout'
|
||||||
import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
|
import {
|
||||||
|
atoms as a,
|
||||||
|
useBreakpoints,
|
||||||
|
useLayoutBreakpoints,
|
||||||
|
useTheme,
|
||||||
|
web,
|
||||||
|
} from '#/alf'
|
||||||
|
import {useDialogContext} from '#/components/Dialog'
|
||||||
|
import {SCROLLBAR_OFFSET} from '#/components/Layout/const'
|
||||||
import {ScrollbarOffsetContext} from '#/components/Layout/context'
|
import {ScrollbarOffsetContext} from '#/components/Layout/context'
|
||||||
|
|
||||||
export * from '#/components/Layout/const'
|
export * from '#/components/Layout/const'
|
||||||
@@ -47,6 +55,7 @@ export const Screen = React.memo(function Screen({
|
|||||||
export type ContentProps = AnimatedScrollViewProps & {
|
export type ContentProps = AnimatedScrollViewProps & {
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
contentContainerStyle?: StyleProp<ViewStyle>
|
contentContainerStyle?: StyleProp<ViewStyle>
|
||||||
|
ignoreTabletLayoutOffset?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,6 +65,7 @@ export const Content = React.memo(function Content({
|
|||||||
children,
|
children,
|
||||||
style,
|
style,
|
||||||
contentContainerStyle,
|
contentContainerStyle,
|
||||||
|
ignoreTabletLayoutOffset,
|
||||||
...props
|
...props
|
||||||
}: ContentProps) {
|
}: ContentProps) {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
@@ -84,8 +94,10 @@ export const Content = React.memo(function Content({
|
|||||||
]}
|
]}
|
||||||
{...props}>
|
{...props}>
|
||||||
{isWeb ? (
|
{isWeb ? (
|
||||||
// @ts-ignore web only -esb
|
<Center ignoreTabletLayoutOffset={ignoreTabletLayoutOffset}>
|
||||||
<Center>{children}</Center>
|
{/* @ts-expect-error web only -esb */}
|
||||||
|
{children}
|
||||||
|
</Center>
|
||||||
) : (
|
) : (
|
||||||
children
|
children
|
||||||
)}
|
)}
|
||||||
@@ -138,10 +150,13 @@ export const KeyboardAwareContent = React.memo(function LayoutScrollView({
|
|||||||
export const Center = React.memo(function LayoutContent({
|
export const Center = React.memo(function LayoutContent({
|
||||||
children,
|
children,
|
||||||
style,
|
style,
|
||||||
|
ignoreTabletLayoutOffset,
|
||||||
...props
|
...props
|
||||||
}: ViewProps) {
|
}: ViewProps & {ignoreTabletLayoutOffset?: boolean}) {
|
||||||
const {isWithinOffsetView} = useContext(ScrollbarOffsetContext)
|
const {isWithinOffsetView} = useContext(ScrollbarOffsetContext)
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
|
const {centerColumnOffset} = useLayoutBreakpoints()
|
||||||
|
const {isWithinDialog} = useDialogContext()
|
||||||
const ctx = useMemo(() => ({isWithinOffsetView: true}), [])
|
const ctx = useMemo(() => ({isWithinOffsetView: true}), [])
|
||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
@@ -151,8 +166,20 @@ export const Center = React.memo(function LayoutContent({
|
|||||||
gtMobile && {
|
gtMobile && {
|
||||||
maxWidth: 600,
|
maxWidth: 600,
|
||||||
},
|
},
|
||||||
|
!isWithinOffsetView && {
|
||||||
|
transform: [
|
||||||
|
{
|
||||||
|
translateX:
|
||||||
|
centerColumnOffset &&
|
||||||
|
!ignoreTabletLayoutOffset &&
|
||||||
|
!isWithinDialog
|
||||||
|
? -150
|
||||||
|
: 0,
|
||||||
|
},
|
||||||
|
{translateX: web(SCROLLBAR_OFFSET) ?? 0},
|
||||||
|
],
|
||||||
|
},
|
||||||
style,
|
style,
|
||||||
!isWithinOffsetView && a.scrollbar_offset,
|
|
||||||
]}
|
]}
|
||||||
{...props}>
|
{...props}>
|
||||||
<ScrollbarOffsetContext.Provider value={ctx}>
|
<ScrollbarOffsetContext.Provider value={ctx}>
|
||||||
@@ -168,6 +195,7 @@ export const Center = React.memo(function LayoutContent({
|
|||||||
const WebCenterBorders = React.memo(function LayoutContent() {
|
const WebCenterBorders = React.memo(function LayoutContent() {
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
|
const {centerColumnOffset} = useLayoutBreakpoints()
|
||||||
return gtMobile ? (
|
return gtMobile ? (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
@@ -180,9 +208,8 @@ const WebCenterBorders = React.memo(function LayoutContent() {
|
|||||||
width: 602,
|
width: 602,
|
||||||
left: '50%',
|
left: '50%',
|
||||||
transform: [
|
transform: [
|
||||||
{
|
{translateX: '-50%'},
|
||||||
translateX: '-50%',
|
{translateX: centerColumnOffset ? -150 : 0},
|
||||||
},
|
|
||||||
...a.scrollbar_offset.transform,
|
...a.scrollbar_offset.transform,
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
|
|||||||
@@ -106,6 +106,7 @@ export function Deactivated() {
|
|||||||
return (
|
return (
|
||||||
<View style={[a.util_screen_outer, a.flex_1]}>
|
<View style={[a.util_screen_outer, a.flex_1]}>
|
||||||
<Layout.Content
|
<Layout.Content
|
||||||
|
ignoreTabletLayoutOffset
|
||||||
contentContainerStyle={[
|
contentContainerStyle={[
|
||||||
a.px_2xl,
|
a.px_2xl,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import {isLabelerSubscribed, lookupLabelValueDefinition} from '#/lib/moderation'
|
|||||||
import {useScrollHandlers} from '#/lib/ScrollContext'
|
import {useScrollHandlers} from '#/lib/ScrollContext'
|
||||||
import {isNative} from '#/platform/detection'
|
import {isNative} from '#/platform/detection'
|
||||||
import {ListRef} from '#/view/com/util/List'
|
import {ListRef} from '#/view/com/util/List'
|
||||||
import {ScrollView} from '#/view/com/util/Views'
|
|
||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {Divider} from '#/components/Divider'
|
import {Divider} from '#/components/Divider'
|
||||||
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
|
||||||
@@ -148,8 +147,8 @@ export function ProfileLabelsSectionInner({
|
|||||||
}, [labelerInfo, labelValues])
|
}, [labelerInfo, labelValues])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView
|
<Layout.Content
|
||||||
// @ts-ignore TODO fix this
|
// @ts-expect-error TODO fix this
|
||||||
ref={scrollElRef}
|
ref={scrollElRef}
|
||||||
scrollEventThrottle={1}
|
scrollEventThrottle={1}
|
||||||
contentContainerStyle={{
|
contentContainerStyle={{
|
||||||
@@ -228,9 +227,8 @@ export function ProfileLabelsSectionInner({
|
|||||||
})}
|
})}
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
<View style={{height: 100}} />
|
||||||
<View style={{height: 400}} />
|
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</Layout.Content>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ import {
|
|||||||
import {atoms as a, useTheme} from '#/alf'
|
import {atoms as a, useTheme} from '#/alf'
|
||||||
import {AppLanguageDropdown} from '#/components/AppLanguageDropdown'
|
import {AppLanguageDropdown} from '#/components/AppLanguageDropdown'
|
||||||
import {Button, ButtonText} from '#/components/Button'
|
import {Button, ButtonText} from '#/components/Button'
|
||||||
|
import * as Layout from '#/components/Layout'
|
||||||
import {InlineLinkText} from '#/components/Link'
|
import {InlineLinkText} from '#/components/Link'
|
||||||
import {Text} from '#/components/Typography'
|
import {Text} from '#/components/Typography'
|
||||||
import {CenteredView} from '../util/Views'
|
|
||||||
|
|
||||||
export const SplashScreen = ({
|
export const SplashScreen = ({
|
||||||
onDismiss,
|
onDismiss,
|
||||||
@@ -70,13 +70,13 @@ export const SplashScreen = ({
|
|||||||
</Pressable>
|
</Pressable>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<CenteredView style={[a.h_full, a.flex_1]}>
|
<Layout.Center style={[a.h_full, a.flex_1]} ignoreTabletLayoutOffset>
|
||||||
<View
|
<View
|
||||||
testID="noSessionView"
|
testID="noSessionView"
|
||||||
style={[
|
style={[
|
||||||
a.h_full,
|
a.h_full,
|
||||||
a.justify_center,
|
a.justify_center,
|
||||||
// @ts-ignore web only
|
// @ts-expect-error web only
|
||||||
{paddingBottom: '20vh'},
|
{paddingBottom: '20vh'},
|
||||||
isMobileWeb && a.pb_5xl,
|
isMobileWeb && a.pb_5xl,
|
||||||
t.atoms.border_contrast_medium,
|
t.atoms.border_contrast_medium,
|
||||||
@@ -135,7 +135,7 @@ export const SplashScreen = ({
|
|||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
</View>
|
</View>
|
||||||
<Footer />
|
<Footer />
|
||||||
</CenteredView>
|
</Layout.Center>
|
||||||
<AppClipOverlay
|
<AppClipOverlay
|
||||||
visible={showClipOverlay}
|
visible={showClipOverlay}
|
||||||
setIsVisible={setShowClipOverlay}
|
setIsVisible={setShowClipOverlay}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ import {List, ListRef} from '#/view/com/util/List'
|
|||||||
import {PostFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
import {PostFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||||
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
|
import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
|
||||||
import {VideoFeedSourceContext} from '#/screens/VideoFeed/types'
|
import {VideoFeedSourceContext} from '#/screens/VideoFeed/types'
|
||||||
import {useBreakpoints} from '#/alf'
|
import {useBreakpoints, useLayoutBreakpoints} from '#/alf'
|
||||||
import {ProgressGuide, SuggestedFollows} from '#/components/FeedInterstitials'
|
import {ProgressGuide, SuggestedFollows} from '#/components/FeedInterstitials'
|
||||||
import {
|
import {
|
||||||
PostFeedVideoGridRow,
|
PostFeedVideoGridRow,
|
||||||
@@ -197,7 +197,8 @@ let PostFeed = ({
|
|||||||
const checkForNewRef = React.useRef<(() => void) | null>(null)
|
const checkForNewRef = React.useRef<(() => void) | null>(null)
|
||||||
const lastFetchRef = React.useRef<number>(Date.now())
|
const lastFetchRef = React.useRef<number>(Date.now())
|
||||||
const [feedType, feedUriOrActorDid, feedTab] = feed.split('|')
|
const [feedType, feedUriOrActorDid, feedTab] = feed.split('|')
|
||||||
const {gtMobile, gtTablet} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
|
const {rightNavVisible} = useLayoutBreakpoints()
|
||||||
const areVideoFeedsEnabled = isNative
|
const areVideoFeedsEnabled = isNative
|
||||||
|
|
||||||
const feedCacheKey = feedParams?.feedCacheKey
|
const feedCacheKey = feedParams?.feedCacheKey
|
||||||
@@ -396,7 +397,7 @@ let PostFeed = ({
|
|||||||
key: 'interstitial-' + sliceIndex + '-' + lastFetchedAt,
|
key: 'interstitial-' + sliceIndex + '-' + lastFetchedAt,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (!gtTablet && !trendingDisabled) {
|
if (!rightNavVisible && !trendingDisabled) {
|
||||||
arr.push({
|
arr.push({
|
||||||
type: 'interstitialTrending',
|
type: 'interstitialTrending',
|
||||||
key:
|
key:
|
||||||
@@ -512,7 +513,7 @@ let PostFeed = ({
|
|||||||
showProgressIntersitial,
|
showProgressIntersitial,
|
||||||
trendingDisabled,
|
trendingDisabled,
|
||||||
trendingVideoDisabled,
|
trendingVideoDisabled,
|
||||||
gtTablet,
|
rightNavVisible,
|
||||||
gtMobile,
|
gtMobile,
|
||||||
isVideoFeed,
|
isVideoFeed,
|
||||||
areVideoFeedsEnabled,
|
areVideoFeedsEnabled,
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ import Animated from 'react-native-reanimated'
|
|||||||
import {usePalette} from '#/lib/hooks/usePalette'
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||||
import {addStyle} from '#/lib/styles'
|
import {addStyle} from '#/lib/styles'
|
||||||
|
import {useLayoutBreakpoints} from '#/alf'
|
||||||
|
import {useDialogContext} from '#/components/Dialog'
|
||||||
|
|
||||||
interface AddedProps {
|
interface AddedProps {
|
||||||
desktopFixedHeight?: boolean | number
|
desktopFixedHeight?: boolean | number
|
||||||
@@ -46,9 +48,14 @@ export const CenteredView = React.forwardRef(function CenteredView(
|
|||||||
) {
|
) {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {isMobile} = useWebMediaQueries()
|
const {isMobile} = useWebMediaQueries()
|
||||||
|
const {centerColumnOffset} = useLayoutBreakpoints()
|
||||||
|
const {isWithinDialog} = useDialogContext()
|
||||||
if (!isMobile) {
|
if (!isMobile) {
|
||||||
style = addStyle(style, styles.container)
|
style = addStyle(style, styles.container)
|
||||||
}
|
}
|
||||||
|
if (centerColumnOffset && !isWithinDialog) {
|
||||||
|
style = addStyle(style, styles.containerOffset)
|
||||||
|
}
|
||||||
if (topBorder) {
|
if (topBorder) {
|
||||||
style = addStyle(style, {
|
style = addStyle(style, {
|
||||||
borderTopWidth: 1,
|
borderTopWidth: 1,
|
||||||
@@ -71,12 +78,17 @@ export const FlatList_INTERNAL = React.forwardRef(function FlatListImpl<ItemT>(
|
|||||||
ref: React.Ref<FlatList<ItemT>>,
|
ref: React.Ref<FlatList<ItemT>>,
|
||||||
) {
|
) {
|
||||||
const {isMobile} = useWebMediaQueries()
|
const {isMobile} = useWebMediaQueries()
|
||||||
|
const {centerColumnOffset} = useLayoutBreakpoints()
|
||||||
|
const {isWithinDialog} = useDialogContext()
|
||||||
if (!isMobile) {
|
if (!isMobile) {
|
||||||
contentContainerStyle = addStyle(
|
contentContainerStyle = addStyle(
|
||||||
contentContainerStyle,
|
contentContainerStyle,
|
||||||
styles.containerScroll,
|
styles.containerScroll,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (centerColumnOffset && !isWithinDialog) {
|
||||||
|
style = addStyle(style, styles.containerOffset)
|
||||||
|
}
|
||||||
if (contentOffset && contentOffset?.y !== 0) {
|
if (contentOffset && contentOffset?.y !== 0) {
|
||||||
// NOTE
|
// NOTE
|
||||||
// we use paddingTop & contentOffset to space around the floating header
|
// we use paddingTop & contentOffset to space around the floating header
|
||||||
@@ -92,7 +104,7 @@ export const FlatList_INTERNAL = React.forwardRef(function FlatListImpl<ItemT>(
|
|||||||
}
|
}
|
||||||
if (desktopFixedHeight) {
|
if (desktopFixedHeight) {
|
||||||
if (typeof desktopFixedHeight === 'number') {
|
if (typeof desktopFixedHeight === 'number') {
|
||||||
// @ts-ignore Web only -prf
|
// @ts-expect-error Web only -prf
|
||||||
style = addStyle(style, {
|
style = addStyle(style, {
|
||||||
height: `calc(100vh - ${desktopFixedHeight}px)`,
|
height: `calc(100vh - ${desktopFixedHeight}px)`,
|
||||||
})
|
})
|
||||||
@@ -108,9 +120,9 @@ export const FlatList_INTERNAL = React.forwardRef(function FlatListImpl<ItemT>(
|
|||||||
// around this, we set data-stable-gutters which can then be
|
// around this, we set data-stable-gutters which can then be
|
||||||
// styled in our external CSS.
|
// styled in our external CSS.
|
||||||
// -prf
|
// -prf
|
||||||
// @ts-ignore web only -prf
|
// @ts-expect-error web only -prf
|
||||||
props.dataSet = props.dataSet || {}
|
props.dataSet = props.dataSet || {}
|
||||||
// @ts-ignore web only -prf
|
// @ts-expect-error web only -prf
|
||||||
props.dataSet.stableGutters = '1'
|
props.dataSet.stableGutters = '1'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -133,16 +145,22 @@ export const ScrollView = React.forwardRef(function ScrollViewImpl(
|
|||||||
ref: React.Ref<Animated.ScrollView>,
|
ref: React.Ref<Animated.ScrollView>,
|
||||||
) {
|
) {
|
||||||
const {isMobile} = useWebMediaQueries()
|
const {isMobile} = useWebMediaQueries()
|
||||||
|
const {centerColumnOffset} = useLayoutBreakpoints()
|
||||||
if (!isMobile) {
|
if (!isMobile) {
|
||||||
contentContainerStyle = addStyle(
|
contentContainerStyle = addStyle(
|
||||||
contentContainerStyle,
|
contentContainerStyle,
|
||||||
styles.containerScroll,
|
styles.containerScroll,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
if (centerColumnOffset) {
|
||||||
|
contentContainerStyle = addStyle(
|
||||||
|
contentContainerStyle,
|
||||||
|
styles.containerOffset,
|
||||||
|
)
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<Animated.ScrollView
|
<Animated.ScrollView
|
||||||
contentContainerStyle={[styles.contentContainer, contentContainerStyle]}
|
contentContainerStyle={[styles.contentContainer, contentContainerStyle]}
|
||||||
// @ts-ignore something is wrong with the reanimated types -prf
|
|
||||||
ref={ref}
|
ref={ref}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@@ -151,7 +169,7 @@ export const ScrollView = React.forwardRef(function ScrollViewImpl(
|
|||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
contentContainer: {
|
contentContainer: {
|
||||||
// @ts-ignore web only
|
// @ts-expect-error web only
|
||||||
minHeight: '100vh',
|
minHeight: '100vh',
|
||||||
},
|
},
|
||||||
container: {
|
container: {
|
||||||
@@ -160,6 +178,9 @@ const styles = StyleSheet.create({
|
|||||||
marginLeft: 'auto',
|
marginLeft: 'auto',
|
||||||
marginRight: 'auto',
|
marginRight: 'auto',
|
||||||
},
|
},
|
||||||
|
containerOffset: {
|
||||||
|
transform: [{translateX: -150}],
|
||||||
|
},
|
||||||
containerScroll: {
|
containerScroll: {
|
||||||
width: '100%',
|
width: '100%',
|
||||||
maxWidth: 600,
|
maxWidth: 600,
|
||||||
@@ -167,7 +188,7 @@ const styles = StyleSheet.create({
|
|||||||
marginRight: 'auto',
|
marginRight: 'auto',
|
||||||
},
|
},
|
||||||
fixedHeight: {
|
fixedHeight: {
|
||||||
// @ts-ignore web only
|
// @ts-expect-error web only
|
||||||
height: '100vh',
|
height: '100vh',
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import Animated from 'react-native-reanimated'
|
import Animated from 'react-native-reanimated'
|
||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||||
import {useMediaQuery} from 'react-responsive'
|
import {useMediaQuery} from 'react-responsive'
|
||||||
|
|
||||||
import {HITSLOP_20} from '#/lib/constants'
|
import {HITSLOP_20} from '#/lib/constants'
|
||||||
|
import {PressableScale} from '#/lib/custom-animations/PressableScale'
|
||||||
import {useMinimalShellFabTransform} from '#/lib/hooks/useMinimalShellTransform'
|
import {useMinimalShellFabTransform} from '#/lib/hooks/useMinimalShellTransform'
|
||||||
import {usePalette} from '#/lib/hooks/usePalette'
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||||
@@ -13,9 +14,7 @@ import {useGate} from '#/lib/statsig/statsig'
|
|||||||
import {colors} from '#/lib/styles'
|
import {colors} from '#/lib/styles'
|
||||||
import {isWeb} from '#/platform/detection'
|
import {isWeb} from '#/platform/detection'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
|
import {useLayoutBreakpoints} from '#/alf'
|
||||||
const AnimatedTouchableOpacity =
|
|
||||||
Animated.createAnimatedComponent(TouchableOpacity)
|
|
||||||
|
|
||||||
export function LoadLatestBtn({
|
export function LoadLatestBtn({
|
||||||
onPress,
|
onPress,
|
||||||
@@ -29,6 +28,7 @@ export function LoadLatestBtn({
|
|||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {hasSession} = useSession()
|
const {hasSession} = useSession()
|
||||||
const {isDesktop, isTablet, isMobile, isTabletOrMobile} = useWebMediaQueries()
|
const {isDesktop, isTablet, isMobile, isTabletOrMobile} = useWebMediaQueries()
|
||||||
|
const {centerColumnOffset} = useLayoutBreakpoints()
|
||||||
const fabMinimalShellTransform = useMinimalShellFabTransform()
|
const fabMinimalShellTransform = useMinimalShellFabTransform()
|
||||||
const insets = useSafeAreaInsets()
|
const insets = useSafeAreaInsets()
|
||||||
|
|
||||||
@@ -49,33 +49,37 @@ export function LoadLatestBtn({
|
|||||||
: {bottom: clamp(insets.bottom, 15, 60) + 15}
|
: {bottom: clamp(insets.bottom, 15, 60) + 15}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AnimatedTouchableOpacity
|
<Animated.View style={[showBottomBar && fabMinimalShellTransform]}>
|
||||||
style={[
|
<PressableScale
|
||||||
styles.loadLatest,
|
style={[
|
||||||
isDesktop &&
|
styles.loadLatest,
|
||||||
(isTallViewport
|
isDesktop &&
|
||||||
? styles.loadLatestOutOfLine
|
(isTallViewport
|
||||||
: styles.loadLatestInline),
|
? styles.loadLatestOutOfLine
|
||||||
isTablet && styles.loadLatestInline,
|
: styles.loadLatestInline),
|
||||||
pal.borderDark,
|
isTablet &&
|
||||||
pal.view,
|
(centerColumnOffset
|
||||||
bottomPosition,
|
? styles.loadLatestInlineOffset
|
||||||
showBottomBar && fabMinimalShellTransform,
|
: styles.loadLatestInline),
|
||||||
]}
|
pal.borderDark,
|
||||||
onPress={onPress}
|
pal.view,
|
||||||
hitSlop={HITSLOP_20}
|
bottomPosition,
|
||||||
accessibilityRole="button"
|
]}
|
||||||
accessibilityLabel={label}
|
onPress={onPress}
|
||||||
accessibilityHint="">
|
hitSlop={HITSLOP_20}
|
||||||
<FontAwesomeIcon icon="angle-up" color={pal.colors.text} size={19} />
|
accessibilityLabel={label}
|
||||||
{showIndicator && <View style={[styles.indicator, pal.borderDark]} />}
|
accessibilityHint=""
|
||||||
</AnimatedTouchableOpacity>
|
targetScale={0.9}>
|
||||||
|
<FontAwesomeIcon icon="angle-up" color={pal.colors.text} size={19} />
|
||||||
|
{showIndicator && <View style={[styles.indicator, pal.borderDark]} />}
|
||||||
|
</PressableScale>
|
||||||
|
</Animated.View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
loadLatest: {
|
loadLatest: {
|
||||||
// @ts-ignore 'fixed' is web only -prf
|
zIndex: 20,
|
||||||
position: isWeb ? 'fixed' : 'absolute',
|
position: isWeb ? 'fixed' : 'absolute',
|
||||||
left: 18,
|
left: 18,
|
||||||
borderWidth: StyleSheet.hairlineWidth,
|
borderWidth: StyleSheet.hairlineWidth,
|
||||||
@@ -87,11 +91,15 @@ const styles = StyleSheet.create({
|
|||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
},
|
},
|
||||||
loadLatestInline: {
|
loadLatestInline: {
|
||||||
// @ts-ignore web only
|
// @ts-expect-error web only
|
||||||
left: 'calc(50vw - 282px)',
|
left: 'calc(50vw - 282px)',
|
||||||
},
|
},
|
||||||
|
loadLatestInlineOffset: {
|
||||||
|
// @ts-expect-error web only
|
||||||
|
left: 'calc(50vw - 432px)',
|
||||||
|
},
|
||||||
loadLatestOutOfLine: {
|
loadLatestOutOfLine: {
|
||||||
// @ts-ignore web only
|
// @ts-expect-error web only
|
||||||
left: 'calc(50vw - 382px)',
|
left: 'calc(50vw - 382px)',
|
||||||
},
|
},
|
||||||
indicator: {
|
indicator: {
|
||||||
|
|||||||
@@ -81,8 +81,10 @@ export function HomeScreen(props: Props) {
|
|||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<Layout.Screen style={styles.loading}>
|
<Layout.Screen>
|
||||||
<ActivityIndicator size="large" />
|
<Layout.Center style={styles.loading}>
|
||||||
|
<ActivityIndicator size="large" />
|
||||||
|
</Layout.Center>
|
||||||
</Layout.Screen>
|
</Layout.Screen>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -150,11 +150,10 @@ function NativeStackNavigator({
|
|||||||
descriptors={newDescriptors}
|
descriptors={newDescriptors}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
{isWeb && showBottomBar && <BottomBarWeb />}
|
{isWeb && (
|
||||||
{isWeb && !showBottomBar && (
|
|
||||||
<>
|
<>
|
||||||
<DesktopLeftNav />
|
{showBottomBar ? <BottomBarWeb /> : <DesktopLeftNav />}
|
||||||
<DesktopRightNav routeName={activeRoute.name} />
|
{!isMobile && <DesktopRightNav routeName={activeRoute.name} />}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</NavigationContent>
|
</NavigationContent>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, View} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import {AppBskyActorDefs} from '@atproto/api'
|
import {AppBskyActorDefs} from '@atproto/api'
|
||||||
import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
|
|
||||||
import {msg, plural, Trans} from '@lingui/macro'
|
import {msg, plural, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {
|
import {
|
||||||
@@ -33,7 +32,7 @@ import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
|||||||
import {PressableWithHover} from '#/view/com/util/PressableWithHover'
|
import {PressableWithHover} from '#/view/com/util/PressableWithHover'
|
||||||
import {UserAvatar} from '#/view/com/util/UserAvatar'
|
import {UserAvatar} from '#/view/com/util/UserAvatar'
|
||||||
import {NavSignupCard} from '#/view/shell/NavSignupCard'
|
import {NavSignupCard} from '#/view/shell/NavSignupCard'
|
||||||
import {atoms as a, tokens, useBreakpoints, useTheme} from '#/alf'
|
import {atoms as a, tokens, useLayoutBreakpoints, useTheme} from '#/alf'
|
||||||
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
||||||
import {DialogControlProps} from '#/components/Dialog'
|
import {DialogControlProps} from '#/components/Dialog'
|
||||||
import {ArrowBoxLeft_Stroke2_Corner0_Rounded as LeaveIcon} from '#/components/icons/ArrowBoxLeft'
|
import {ArrowBoxLeft_Stroke2_Corner0_Rounded as LeaveIcon} from '#/components/icons/ArrowBoxLeft'
|
||||||
@@ -86,7 +85,7 @@ function ProfileCard() {
|
|||||||
})
|
})
|
||||||
const profiles = data?.profiles
|
const profiles = data?.profiles
|
||||||
const signOutPromptControl = Prompt.usePromptControl()
|
const signOutPromptControl = Prompt.usePromptControl()
|
||||||
const {gtTablet} = useBreakpoints()
|
const {leftNavMinimal} = useLayoutBreakpoints()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
|
|
||||||
@@ -101,7 +100,7 @@ function ProfileCard() {
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[a.my_md, gtTablet && [a.w_full, a.align_start]]}>
|
<View style={[a.my_md, !leftNavMinimal && [a.w_full, a.align_start]]}>
|
||||||
{!isLoading && profile ? (
|
{!isLoading && profile ? (
|
||||||
<Menu.Root>
|
<Menu.Root>
|
||||||
<Menu.Trigger label={_(msg`Switch accounts`)}>
|
<Menu.Trigger label={_(msg`Switch accounts`)}>
|
||||||
@@ -120,7 +119,7 @@ function ProfileCard() {
|
|||||||
a.align_center,
|
a.align_center,
|
||||||
a.flex_row,
|
a.flex_row,
|
||||||
{gap: 6},
|
{gap: 6},
|
||||||
gtTablet && [a.pl_lg, a.pr_md],
|
!leftNavMinimal && [a.pl_lg, a.pr_md],
|
||||||
]}>
|
]}>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
@@ -133,8 +132,8 @@ function ProfileCard() {
|
|||||||
a.z_10,
|
a.z_10,
|
||||||
active && {
|
active && {
|
||||||
transform: [
|
transform: [
|
||||||
{scale: gtTablet ? 2 / 3 : 0.8},
|
{scale: !leftNavMinimal ? 2 / 3 : 0.8},
|
||||||
{translateX: gtTablet ? -22 : 0},
|
{translateX: !leftNavMinimal ? -22 : 0},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
]}>
|
]}>
|
||||||
@@ -144,7 +143,7 @@ function ProfileCard() {
|
|||||||
type={profile?.associated?.labeler ? 'labeler' : 'user'}
|
type={profile?.associated?.labeler ? 'labeler' : 'user'}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
{gtTablet && (
|
{!leftNavMinimal && (
|
||||||
<>
|
<>
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
@@ -197,7 +196,7 @@ function ProfileCard() {
|
|||||||
<LoadingPlaceholder
|
<LoadingPlaceholder
|
||||||
width={size}
|
width={size}
|
||||||
height={size}
|
height={size}
|
||||||
style={[{borderRadius: size}, gtTablet && a.ml_lg]}
|
style={[{borderRadius: size}, !leftNavMinimal && a.ml_lg]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<Prompt.Basic
|
<Prompt.Basic
|
||||||
@@ -307,8 +306,7 @@ function NavItem({count, hasNew, href, icon, iconFilled, label}: NavItemProps) {
|
|||||||
const t = useTheme()
|
const t = useTheme()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {currentAccount} = useSession()
|
const {currentAccount} = useSession()
|
||||||
const {gtMobile, gtTablet} = useBreakpoints()
|
const {leftNavMinimal} = useLayoutBreakpoints()
|
||||||
const isTablet = gtMobile && !gtTablet
|
|
||||||
const [pathName] = React.useMemo(() => router.matchPath(href), [href])
|
const [pathName] = React.useMemo(() => router.matchPath(href), [href])
|
||||||
const currentRouteInfo = useNavigationState(state => {
|
const currentRouteInfo = useNavigationState(state => {
|
||||||
if (!state) {
|
if (!state) {
|
||||||
@@ -350,9 +348,8 @@ function NavItem({count, hasNew, href, icon, iconFilled, label}: NavItemProps) {
|
|||||||
a.transition_color,
|
a.transition_color,
|
||||||
]}
|
]}
|
||||||
hoverStyle={t.atoms.bg_contrast_25}
|
hoverStyle={t.atoms.bg_contrast_25}
|
||||||
// @ts-ignore the function signature differs on web -prf
|
// @ts-expect-error the function signature differs on web -prf
|
||||||
onPress={onPressWrapped}
|
onPress={onPressWrapped}
|
||||||
// @ts-ignore web only -prf
|
|
||||||
href={href}
|
href={href}
|
||||||
dataSet={{noUnderline: 1}}
|
dataSet={{noUnderline: 1}}
|
||||||
role="link"
|
role="link"
|
||||||
@@ -367,7 +364,7 @@ function NavItem({count, hasNew, href, icon, iconFilled, label}: NavItemProps) {
|
|||||||
width: 24,
|
width: 24,
|
||||||
height: 24,
|
height: 24,
|
||||||
},
|
},
|
||||||
isTablet && {
|
leftNavMinimal && {
|
||||||
width: 40,
|
width: 40,
|
||||||
height: 40,
|
height: 40,
|
||||||
},
|
},
|
||||||
@@ -407,7 +404,7 @@ function NavItem({count, hasNew, href, icon, iconFilled, label}: NavItemProps) {
|
|||||||
paddingVertical: 1,
|
paddingVertical: 1,
|
||||||
minWidth: 16,
|
minWidth: 16,
|
||||||
},
|
},
|
||||||
isTablet && [
|
leftNavMinimal && [
|
||||||
{
|
{
|
||||||
top: '10%',
|
top: '10%',
|
||||||
left: count.length === 1 ? 20 : 16,
|
left: count.length === 1 ? 20 : 16,
|
||||||
@@ -429,7 +426,7 @@ function NavItem({count, hasNew, href, icon, iconFilled, label}: NavItemProps) {
|
|||||||
right: -1,
|
right: -1,
|
||||||
top: -3,
|
top: -3,
|
||||||
},
|
},
|
||||||
isTablet && {
|
leftNavMinimal && {
|
||||||
right: 6,
|
right: 6,
|
||||||
top: 4,
|
top: 4,
|
||||||
},
|
},
|
||||||
@@ -437,7 +434,7 @@ function NavItem({count, hasNew, href, icon, iconFilled, label}: NavItemProps) {
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</View>
|
</View>
|
||||||
{gtTablet && (
|
{!leftNavMinimal && (
|
||||||
<Text style={[a.text_xl, isCurrent ? a.font_heavy : a.font_normal]}>
|
<Text style={[a.text_xl, isCurrent ? a.font_heavy : a.font_normal]}>
|
||||||
{label}
|
{label}
|
||||||
</Text>
|
</Text>
|
||||||
@@ -451,7 +448,7 @@ function ComposeBtn() {
|
|||||||
const {getState} = useNavigation()
|
const {getState} = useNavigation()
|
||||||
const {openComposer} = useComposerControls()
|
const {openComposer} = useComposerControls()
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {isTablet} = useWebMediaQueries()
|
const {leftNavMinimal} = useLayoutBreakpoints()
|
||||||
const [isFetchingHandle, setIsFetchingHandle] = React.useState(false)
|
const [isFetchingHandle, setIsFetchingHandle] = React.useState(false)
|
||||||
const fetchHandle = useFetchHandle()
|
const fetchHandle = useFetchHandle()
|
||||||
|
|
||||||
@@ -491,9 +488,10 @@ function ComposeBtn() {
|
|||||||
const onPressCompose = async () =>
|
const onPressCompose = async () =>
|
||||||
openComposer({mention: await getProfileHandle()})
|
openComposer({mention: await getProfileHandle()})
|
||||||
|
|
||||||
if (isTablet) {
|
if (leftNavMinimal) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[a.flex_row, a.pl_md, a.pt_xl]}>
|
<View style={[a.flex_row, a.pl_md, a.pt_xl]}>
|
||||||
<Button
|
<Button
|
||||||
@@ -541,7 +539,8 @@ export function DesktopLeftNav() {
|
|||||||
const {hasSession, currentAccount} = useSession()
|
const {hasSession, currentAccount} = useSession()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const {_} = useLingui()
|
const {_} = useLingui()
|
||||||
const {isDesktop, isTablet} = useWebMediaQueries()
|
const {isDesktop} = useWebMediaQueries()
|
||||||
|
const {leftNavMinimal, centerColumnOffset} = useLayoutBreakpoints()
|
||||||
const numUnreadNotifications = useUnreadNotifications()
|
const numUnreadNotifications = useUnreadNotifications()
|
||||||
const hasHomeBadge = useHomeBadge()
|
const hasHomeBadge = useHomeBadge()
|
||||||
const gate = useGate()
|
const gate = useGate()
|
||||||
@@ -556,8 +555,14 @@ export function DesktopLeftNav() {
|
|||||||
style={[
|
style={[
|
||||||
a.px_xl,
|
a.px_xl,
|
||||||
styles.leftNav,
|
styles.leftNav,
|
||||||
isTablet && styles.leftNavTablet,
|
leftNavMinimal && styles.leftNavMinimal,
|
||||||
pal.border,
|
{
|
||||||
|
transform: [
|
||||||
|
{translateX: centerColumnOffset ? -450 : -300},
|
||||||
|
{translateX: '-100%'},
|
||||||
|
...a.scrollbar_offset.transform,
|
||||||
|
],
|
||||||
|
},
|
||||||
]}>
|
]}>
|
||||||
{hasSession ? (
|
{hasSession ? (
|
||||||
<ProfileCard />
|
<ProfileCard />
|
||||||
@@ -630,14 +635,14 @@ export function DesktopLeftNav() {
|
|||||||
href="/feeds"
|
href="/feeds"
|
||||||
icon={
|
icon={
|
||||||
<Hashtag
|
<Hashtag
|
||||||
style={pal.text as FontAwesomeIconStyle}
|
style={pal.text}
|
||||||
aria-hidden={true}
|
aria-hidden={true}
|
||||||
width={NAV_ICON_WIDTH}
|
width={NAV_ICON_WIDTH}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
iconFilled={
|
iconFilled={
|
||||||
<HashtagFilled
|
<HashtagFilled
|
||||||
style={pal.text as FontAwesomeIconStyle}
|
style={pal.text}
|
||||||
aria-hidden={true}
|
aria-hidden={true}
|
||||||
width={NAV_ICON_WIDTH}
|
width={NAV_ICON_WIDTH}
|
||||||
/>
|
/>
|
||||||
@@ -708,36 +713,25 @@ export function DesktopLeftNav() {
|
|||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
leftNav: {
|
leftNav: {
|
||||||
// @ts-ignore web only
|
|
||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
top: 10,
|
top: 0,
|
||||||
// @ts-ignore web only
|
paddingTop: 10,
|
||||||
|
paddingBottom: 10,
|
||||||
left: '50%',
|
left: '50%',
|
||||||
transform: [
|
|
||||||
{
|
|
||||||
translateX: -300,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
translateX: '-100%',
|
|
||||||
},
|
|
||||||
...a.scrollbar_offset.transform,
|
|
||||||
],
|
|
||||||
width: 240,
|
width: 240,
|
||||||
// @ts-ignore web only
|
// @ts-expect-error web only
|
||||||
maxHeight: 'calc(100vh - 10px)',
|
maxHeight: '100vh',
|
||||||
overflowY: 'auto',
|
overflowY: 'auto',
|
||||||
},
|
},
|
||||||
leftNavTablet: {
|
leftNavMinimal: {
|
||||||
top: 0,
|
paddingTop: 0,
|
||||||
left: 0,
|
paddingBottom: 0,
|
||||||
right: 'auto',
|
|
||||||
borderRightWidth: 1,
|
|
||||||
height: '100%',
|
|
||||||
width: 76,
|
|
||||||
paddingLeft: 0,
|
paddingLeft: 0,
|
||||||
paddingRight: 0,
|
paddingRight: 0,
|
||||||
|
height: '100%',
|
||||||
|
width: 86,
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
transform: [],
|
overflowX: 'hidden',
|
||||||
},
|
},
|
||||||
backBtn: {
|
backBtn: {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
|
|||||||
@@ -1,17 +1,23 @@
|
|||||||
import React from 'react'
|
import {useEffect, useState} from 'react'
|
||||||
import {View} from 'react-native'
|
import {View} from 'react-native'
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {useNavigation} from '@react-navigation/core'
|
import {useNavigation} from '@react-navigation/core'
|
||||||
|
|
||||||
import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants'
|
import {FEEDBACK_FORM_URL, HELP_DESK_URL} from '#/lib/constants'
|
||||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
|
||||||
import {useKawaiiMode} from '#/state/preferences/kawaii'
|
import {useKawaiiMode} from '#/state/preferences/kawaii'
|
||||||
import {useSession} from '#/state/session'
|
import {useSession} from '#/state/session'
|
||||||
import {DesktopFeeds} from '#/view/shell/desktop/Feeds'
|
import {DesktopFeeds} from '#/view/shell/desktop/Feeds'
|
||||||
import {DesktopSearch} from '#/view/shell/desktop/Search'
|
import {DesktopSearch} from '#/view/shell/desktop/Search'
|
||||||
import {SidebarTrendingTopics} from '#/view/shell/desktop/SidebarTrendingTopics'
|
import {SidebarTrendingTopics} from '#/view/shell/desktop/SidebarTrendingTopics'
|
||||||
import {atoms as a, useGutters, useTheme, web} from '#/alf'
|
import {
|
||||||
|
atoms as a,
|
||||||
|
useGutters,
|
||||||
|
useLayoutBreakpoints,
|
||||||
|
useTheme,
|
||||||
|
web,
|
||||||
|
} from '#/alf'
|
||||||
|
import {AppLanguageDropdown} from '#/components/AppLanguageDropdown'
|
||||||
import {Divider} from '#/components/Divider'
|
import {Divider} from '#/components/Divider'
|
||||||
import {InlineLinkText} from '#/components/Link'
|
import {InlineLinkText} from '#/components/Link'
|
||||||
import {ProgressGuideList} from '#/components/ProgressGuide/List'
|
import {ProgressGuideList} from '#/components/ProgressGuide/List'
|
||||||
@@ -19,16 +25,15 @@ import {Text} from '#/components/Typography'
|
|||||||
|
|
||||||
function useWebQueryParams() {
|
function useWebQueryParams() {
|
||||||
const navigation = useNavigation()
|
const navigation = useNavigation()
|
||||||
const [params, setParams] = React.useState<Record<string, string>>({})
|
const [params, setParams] = useState<Record<string, string>>({})
|
||||||
|
|
||||||
React.useEffect(() => {
|
useEffect(() => {
|
||||||
return navigation.addListener('state', e => {
|
return navigation.addListener('state', e => {
|
||||||
try {
|
try {
|
||||||
const {state} = e.data
|
const {state} = e.data
|
||||||
const lastRoute = state.routes[state.routes.length - 1]
|
const lastRoute = state.routes[state.routes.length - 1]
|
||||||
const {params} = lastRoute
|
setParams(lastRoute.params)
|
||||||
setParams(params)
|
} catch (err) {}
|
||||||
} catch (e) {}
|
|
||||||
})
|
})
|
||||||
}, [navigation, setParams])
|
}, [navigation, setParams])
|
||||||
|
|
||||||
@@ -45,9 +50,10 @@ export function DesktopRightNav({routeName}: {routeName: string}) {
|
|||||||
const webqueryParams = useWebQueryParams()
|
const webqueryParams = useWebQueryParams()
|
||||||
const searchQuery = webqueryParams?.q
|
const searchQuery = webqueryParams?.q
|
||||||
const showTrending = !isSearchScreen || (isSearchScreen && !!searchQuery)
|
const showTrending = !isSearchScreen || (isSearchScreen && !!searchQuery)
|
||||||
|
const {rightNavVisible, centerColumnOffset, leftNavMinimal} =
|
||||||
|
useLayoutBreakpoints()
|
||||||
|
|
||||||
const {isTablet} = useWebMediaQueries()
|
if (!rightNavVisible) {
|
||||||
if (isTablet) {
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,9 +66,7 @@ export function DesktopRightNav({routeName}: {routeName: string}) {
|
|||||||
position: 'fixed',
|
position: 'fixed',
|
||||||
left: '50%',
|
left: '50%',
|
||||||
transform: [
|
transform: [
|
||||||
{
|
{translateX: centerColumnOffset ? 150 : 300},
|
||||||
translateX: 300,
|
|
||||||
},
|
|
||||||
...a.scrollbar_offset.transform,
|
...a.scrollbar_offset.transform,
|
||||||
],
|
],
|
||||||
width: 300 + gutters.paddingLeft,
|
width: 300 + gutters.paddingLeft,
|
||||||
@@ -125,6 +129,12 @@ export function DesktopRightNav({routeName}: {routeName: string}) {
|
|||||||
</Trans>
|
</Trans>
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{!hasSession && leftNavMinimal && (
|
||||||
|
<View style={[a.w_full, {height: 32}]}>
|
||||||
|
<AppLanguageDropdown style={{marginTop: 0}} />
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user