[Chat] Split view layout for web (#10258)
This commit is contained in:
@@ -15,10 +15,8 @@ import {useQueryClient} from '@tanstack/react-query'
|
||||
|
||||
import {DISCOVER_FEED_URI, VIDEO_FEED_URIS} from '#/lib/constants'
|
||||
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
|
||||
import {ComposeIcon2} from '#/lib/icons'
|
||||
import {getRootNavigation, getTabState, TabState} from '#/lib/routes/helpers'
|
||||
import {type AllNavigatorParams} from '#/lib/routes/types'
|
||||
import {s} from '#/lib/styles'
|
||||
import {listenSoftReset} from '#/state/events'
|
||||
import {FeedFeedbackProvider, useFeedFeedback} from '#/state/feed-feedback'
|
||||
import {useSetHomeBadge} from '#/state/home-badge'
|
||||
@@ -30,14 +28,16 @@ import {
|
||||
} from '#/state/queries/post-feed'
|
||||
import {truncateAndInvalidate} from '#/state/queries/util'
|
||||
import {useSession} from '#/state/session'
|
||||
import {PostFeed} from '#/view/com/posts/PostFeed'
|
||||
import {FAB} from '#/view/com/util/fab/FAB'
|
||||
import {type ListMethods} from '#/view/com/util/List'
|
||||
import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn'
|
||||
import {MainScrollProvider} from '#/view/com/util/MainScrollProvider'
|
||||
import {useTheme} from '#/alf'
|
||||
import {useHeaderOffset} from '#/components/hooks/useHeaderOffset'
|
||||
import {EditBig_Stroke2_Corner2_Rounded as EditBigIcon} from '#/components/icons/EditBig'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_NATIVE} from '#/env'
|
||||
import {PostFeed} from '../posts/PostFeed'
|
||||
import {FAB} from '../util/fab/FAB'
|
||||
import {type ListMethods} from '../util/List'
|
||||
import {LoadLatestBtn} from '../util/load-latest/LoadLatestBtn'
|
||||
import {MainScrollProvider} from '../util/MainScrollProvider'
|
||||
|
||||
const POLL_FREQ = 60e3 // 60sec
|
||||
|
||||
@@ -81,6 +81,7 @@ export function FeedPage({
|
||||
const _isVideoFeed = isBskyVideoFeed || feedIsVideoMode
|
||||
return IS_NATIVE && _isVideoFeed
|
||||
}, [feedInfo])
|
||||
const t = useTheme()
|
||||
|
||||
useEffect(() => {
|
||||
if (isPageFocused) {
|
||||
@@ -173,7 +174,7 @@ export function FeedPage({
|
||||
<FAB
|
||||
testID="composeFAB"
|
||||
onPress={onPressCompose}
|
||||
icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />}
|
||||
icon={<EditBigIcon size="lg" fill={t.palette.white} />}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={_(msg({message: `New post`, context: 'action'}))}
|
||||
accessibilityHint=""
|
||||
|
||||
@@ -2,45 +2,53 @@ import {isValidElement} from 'react'
|
||||
import {type StyleProp, type TextStyle, type ViewStyle} from 'react-native'
|
||||
import {View} from 'react-native'
|
||||
|
||||
import {usePalette} from '#/lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
|
||||
import {Button, type ButtonProps, ButtonText} from '#/components/Button'
|
||||
import {
|
||||
Button,
|
||||
ButtonIcon,
|
||||
type ButtonProps,
|
||||
ButtonText,
|
||||
} from '#/components/Button'
|
||||
import {EditBig_Stroke1_Corner0_Rounded as EditIcon} from '#/components/icons/EditBig'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
export type EmptyStateButtonProps = Omit<ButtonProps, 'children' | 'label'> & {
|
||||
label: string
|
||||
text: string
|
||||
icon?: React.ComponentProps<typeof ButtonIcon>['icon']
|
||||
}
|
||||
|
||||
export function EmptyState({
|
||||
testID,
|
||||
icon,
|
||||
iconSize = '3xl',
|
||||
iconColor,
|
||||
message,
|
||||
style,
|
||||
textStyle,
|
||||
button,
|
||||
}: {
|
||||
testID?: string
|
||||
icon?: React.ComponentType<any> | React.ReactElement
|
||||
iconSize?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl'
|
||||
icon?: React.ComponentType<any> | React.ReactElement | null
|
||||
iconSize?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl'
|
||||
iconColor?: string
|
||||
message: string
|
||||
style?: StyleProp<ViewStyle>
|
||||
textStyle?: StyleProp<TextStyle>
|
||||
button?: EmptyStateButtonProps
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {isTabletOrDesktop} = useWebMediaQueries()
|
||||
const t = useTheme()
|
||||
const {gtMobile} = useBreakpoints()
|
||||
const {gtMobile, gtTablet} = useBreakpoints()
|
||||
|
||||
const placeholderIcon = (
|
||||
<EditIcon size="2xl" fill={t.atoms.text_contrast_medium.color} />
|
||||
)
|
||||
|
||||
const renderIcon = () => {
|
||||
if (icon === null) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (!icon) {
|
||||
return placeholderIcon
|
||||
}
|
||||
@@ -57,8 +65,7 @@ export function EmptyState({
|
||||
return (
|
||||
<IconComponent
|
||||
size={iconSize}
|
||||
fill={t.atoms.text_contrast_medium.color}
|
||||
style={{color: t.atoms.text_contrast_low.color}}
|
||||
style={{color: iconColor ?? t.atoms.text_contrast_low.color}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -79,16 +86,14 @@ export function EmptyState({
|
||||
{height: 64, width: 64},
|
||||
isValidElement(icon)
|
||||
? a.bg_transparent
|
||||
: [isTabletOrDesktop && {marginTop: 50}],
|
||||
: [gtTablet && {marginTop: 50}],
|
||||
]}>
|
||||
{renderIcon()}
|
||||
</View>
|
||||
<Text
|
||||
style={[
|
||||
{
|
||||
color: pal.colors.textLight,
|
||||
maxWidth: gtMobile ? '40%' : '60%',
|
||||
},
|
||||
t.atoms.text_contrast_high,
|
||||
{maxWidth: gtMobile ? '40%' : '60%'},
|
||||
a.pt_xs,
|
||||
a.font_medium,
|
||||
a.text_md,
|
||||
@@ -101,8 +106,9 @@ export function EmptyState({
|
||||
{message}
|
||||
</Text>
|
||||
{button && (
|
||||
<View style={[a.flex_shrink, a.mt_xl, a.self_center, a.mb_5xl]}>
|
||||
<View style={[a.flex_shrink, a.mt_md, a.self_center, a.mb_5xl]}>
|
||||
<Button {...button}>
|
||||
{button.icon && <ButtonIcon icon={button.icon} />}
|
||||
<ButtonText>{button.text}</ButtonText>
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
@@ -22,6 +22,8 @@ import {batchedUpdates} from '#/lib/batchedUpdates'
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
import {useScrollHandlers} from '#/lib/ScrollContext'
|
||||
import {addStyle} from '#/lib/styles'
|
||||
import {useIsWithinSplitView} from '#/screens/Messages/components/splitView/context'
|
||||
import {useTheme, web} from '#/alf'
|
||||
import * as Layout from '#/components/Layout'
|
||||
|
||||
export type ListMethods = {
|
||||
@@ -60,7 +62,7 @@ function ListImpl<ItemT>(
|
||||
ListHeaderComponent,
|
||||
ListFooterComponent,
|
||||
ListEmptyComponent,
|
||||
disableFullWindowScroll,
|
||||
disableFullWindowScroll: disableFullWindowScrollProp,
|
||||
contentContainerStyle,
|
||||
data,
|
||||
desktopFixedHeight,
|
||||
@@ -83,6 +85,12 @@ function ListImpl<ItemT>(
|
||||
ref: React.Ref<ListMethods>,
|
||||
) {
|
||||
const contextScrollHandlers = useScrollHandlers()
|
||||
const {isWithinSplitView} = useIsWithinSplitView()
|
||||
const t = useTheme()
|
||||
|
||||
// automatically disable full window scroll when within split view
|
||||
const disableFullWindowScroll =
|
||||
disableFullWindowScrollProp ?? isWithinSplitView
|
||||
|
||||
const isEmpty = !data || data.length === 0
|
||||
|
||||
@@ -329,11 +337,15 @@ function ListImpl<ItemT>(
|
||||
<View
|
||||
{...props}
|
||||
style={[
|
||||
isWithinSplitView &&
|
||||
web({
|
||||
scrollbarWidth: 'thin',
|
||||
scrollbarColor: `${t.palette.contrast_100} transparent`,
|
||||
}),
|
||||
style,
|
||||
disableFullWindowScroll && {
|
||||
flex: 1,
|
||||
// @ts-expect-error web only
|
||||
'overflow-y': 'scroll',
|
||||
overflowY: 'scroll',
|
||||
},
|
||||
]}
|
||||
ref={nativeRef as unknown as React.RefObject<View>}>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import {View} from 'react-native'
|
||||
|
||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||
import {useBreakpoints} from '#/alf'
|
||||
import {FABInner, type FABProps} from './FABInner'
|
||||
|
||||
export const FAB = (_opts: FABProps) => {
|
||||
const {isDesktop} = useWebMediaQueries()
|
||||
export const FAB = (props: FABProps) => {
|
||||
const {gtMobile} = useBreakpoints()
|
||||
|
||||
if (!isDesktop) {
|
||||
return <FABInner {..._opts} />
|
||||
if (!gtMobile) {
|
||||
return <FABInner {...props} />
|
||||
}
|
||||
|
||||
return <View />
|
||||
|
||||
Reference in New Issue
Block a user