Add placeholder feeds dropdown to home
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
import React, {useCallback} from 'react'
|
||||
import {View} from 'react-native'
|
||||
import {AtUri} from '@atproto/api'
|
||||
import {msg} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useNavigation} from '@react-navigation/native'
|
||||
|
||||
import {cleanError} from '#/lib/strings/errors'
|
||||
import {useFeedSourceInfoQuery} from '#/state/queries/feed'
|
||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||
import {NavigationProp} from 'lib/routes/types'
|
||||
import {LoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
|
||||
import {atoms as a, useTheme} from '#/alf'
|
||||
import * as Dialog from '#/components/Dialog'
|
||||
import {router} from '#/routes'
|
||||
import {Button} from '../Button'
|
||||
import {Text} from '../Typography'
|
||||
|
||||
export function MyFeedsDialog({control}: {control: Dialog.DialogControlProps}) {
|
||||
const {_} = useLingui()
|
||||
const t = useTheme()
|
||||
const {data: preferences} = usePreferencesQuery()
|
||||
const navigation = useNavigation<NavigationProp>()
|
||||
|
||||
const onPressFeed = useCallback(
|
||||
(uri: string) => {
|
||||
control.close()
|
||||
|
||||
const urip = new AtUri(uri)
|
||||
const collection =
|
||||
urip.collection === 'app.bsky.feed.generator' ? 'feed' : 'lists'
|
||||
const href = `/profile/${urip.hostname}/${collection}/${urip.rkey}`
|
||||
const route = router.matchPath(href)
|
||||
// @ts-ignore This is correct -prf
|
||||
navigation.navigate(route[0], route[1])
|
||||
},
|
||||
[control, navigation],
|
||||
)
|
||||
const onPressEditFeeds = useCallback(() => {
|
||||
control.close()
|
||||
navigation.navigate('SavedFeeds')
|
||||
}, [control, navigation])
|
||||
|
||||
let feeds: string[] = []
|
||||
if (preferences && preferences?.feeds?.saved.length !== 0) {
|
||||
const {saved, pinned} = preferences.feeds
|
||||
feeds = feeds.concat(pinned)
|
||||
feeds = feeds.concat(saved.filter(uri => !pinned.includes(uri)))
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog.Outer control={control}>
|
||||
<Dialog.Handle />
|
||||
|
||||
<Dialog.ScrollableInner label={_(msg`Switch Account`)}>
|
||||
{feeds.map(feedUri => (
|
||||
<SavedFeed key={feedUri} feedUri={feedUri} onPress={onPressFeed} />
|
||||
))}
|
||||
<Button label="Edit feeds" onPress={onPressEditFeeds}>
|
||||
{() => (
|
||||
<View
|
||||
style={[
|
||||
a.border_b,
|
||||
t.atoms.border_contrast_low,
|
||||
a.px_md,
|
||||
a.py_md,
|
||||
a.flex_1,
|
||||
]}>
|
||||
<Text numberOfLines={1} style={[t.atoms.text, a.text_md]}>
|
||||
Edit my feeds
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</Button>
|
||||
</Dialog.ScrollableInner>
|
||||
</Dialog.Outer>
|
||||
)
|
||||
}
|
||||
|
||||
function SavedFeed({
|
||||
feedUri,
|
||||
onPress,
|
||||
}: {
|
||||
feedUri: string
|
||||
onPress: (uri: string) => void
|
||||
}) {
|
||||
const t = useTheme()
|
||||
const {data: info, error} = useFeedSourceInfoQuery({uri: feedUri})
|
||||
|
||||
if (!info && !error) {
|
||||
return <SavedFeedLoadingPlaceholder />
|
||||
}
|
||||
|
||||
return (
|
||||
<Button
|
||||
testID={`saved-feed-${info?.displayName}`}
|
||||
label={info ? info.displayName : 'Feed offline'}
|
||||
onPress={() => onPress(feedUri)}>
|
||||
{() => (
|
||||
<View
|
||||
style={[
|
||||
a.border_b,
|
||||
t.atoms.border_contrast_low,
|
||||
a.px_md,
|
||||
a.py_md,
|
||||
a.flex_1,
|
||||
]}>
|
||||
<Text numberOfLines={1} style={[t.atoms.text, a.text_md]}>
|
||||
{info ? info.displayName : cleanError(error)}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
function SavedFeedLoadingPlaceholder() {
|
||||
const t = useTheme()
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
a.flex_1,
|
||||
a.border_b,
|
||||
t.atoms.border_contrast_low,
|
||||
a.px_md,
|
||||
a.py_sm,
|
||||
]}>
|
||||
<LoadingPlaceholder width={140} height={12} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
import React, {useRef, useMemo, useEffect, useState, useCallback} from 'react'
|
||||
import {StyleSheet, View, ScrollView, LayoutChangeEvent} from 'react-native'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {PressableWithHover} from '../util/PressableWithHover'
|
||||
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
||||
import {LayoutChangeEvent, ScrollView, StyleSheet, View} from 'react-native'
|
||||
|
||||
import {isNative} from '#/platform/detection'
|
||||
import {usePalette} from 'lib/hooks/usePalette'
|
||||
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
|
||||
import {useDialogControl} from '#/components/Dialog'
|
||||
import {MyFeedsDialog} from '#/components/dialogs/MyFeeds'
|
||||
// import {ArrowTriangleBottom_Stroke2_Corner1_Rounded as ArrowTriangleBottom} from '#/components/icons/ArrowTriangle'
|
||||
import {ChevronBottom_Stroke2_Corner0_Rounded as ArrowTriangleBottom} from '#/components/icons/Chevron'
|
||||
import {PressableWithHover} from '../util/PressableWithHover'
|
||||
import {Text} from '../util/text/Text'
|
||||
import {DraggableScrollView} from './DraggableScrollView'
|
||||
import {isNative} from '#/platform/detection'
|
||||
|
||||
export interface TabBarProps {
|
||||
testID?: string
|
||||
@@ -36,6 +41,7 @@ export function TabBar({
|
||||
() => ({borderBottomColor: indicatorColor || pal.colors.link}),
|
||||
[indicatorColor, pal],
|
||||
)
|
||||
const myFeedsControl = useDialogControl()
|
||||
const {isDesktop, isTablet} = useWebMediaQueries()
|
||||
const styles = isDesktop || isTablet ? desktopStyles : mobileStyles
|
||||
|
||||
@@ -147,6 +153,14 @@ export function TabBar({
|
||||
)
|
||||
})}
|
||||
</DraggableScrollView>
|
||||
<PressableWithHover
|
||||
testID="feedsDropdownBtn"
|
||||
style={styles.dropdownBtn}
|
||||
hoverStyle={pal.viewLight}
|
||||
onPress={() => myFeedsControl.open()}>
|
||||
<ArrowTriangleBottom fill={pal.textLight.color} size="sm" />
|
||||
</PressableWithHover>
|
||||
<MyFeedsDialog control={myFeedsControl} />
|
||||
<View style={[pal.border, styles.outerBottomBorder]} />
|
||||
</View>
|
||||
)
|
||||
@@ -178,6 +192,10 @@ const desktopStyles = StyleSheet.create({
|
||||
bottom: -1,
|
||||
borderBottomWidth: 1,
|
||||
},
|
||||
dropdownBtn: {
|
||||
paddingTop: 14,
|
||||
paddingHorizontal: 14,
|
||||
},
|
||||
})
|
||||
|
||||
const mobileStyles = StyleSheet.create({
|
||||
@@ -205,4 +223,8 @@ const mobileStyles = StyleSheet.create({
|
||||
bottom: -1,
|
||||
borderBottomWidth: 1,
|
||||
},
|
||||
dropdownBtn: {
|
||||
paddingTop: 12,
|
||||
paddingHorizontal: 14,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -93,7 +93,7 @@ export function ProfileMenuDialog({
|
||||
<Dialog.Outer control={control}>
|
||||
<Dialog.Handle />
|
||||
|
||||
<Dialog.ScrollableInner label={_(msg`Switch Account`)}>
|
||||
<Dialog.ScrollableInner label={_(msg`App Menu`)}>
|
||||
<View style={[a.gap_sm]}>
|
||||
<ProfileCard onPress={onPressProfile} />
|
||||
|
||||
|
||||
Reference in New Issue
Block a user