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 React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
||||||
import {StyleSheet, View, ScrollView, LayoutChangeEvent} from 'react-native'
|
import {LayoutChangeEvent, ScrollView, StyleSheet, View} from 'react-native'
|
||||||
import {Text} from '../util/text/Text'
|
|
||||||
import {PressableWithHover} from '../util/PressableWithHover'
|
import {isNative} from '#/platform/detection'
|
||||||
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 {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 {DraggableScrollView} from './DraggableScrollView'
|
||||||
import {isNative} from '#/platform/detection'
|
|
||||||
|
|
||||||
export interface TabBarProps {
|
export interface TabBarProps {
|
||||||
testID?: string
|
testID?: string
|
||||||
@@ -36,6 +41,7 @@ export function TabBar({
|
|||||||
() => ({borderBottomColor: indicatorColor || pal.colors.link}),
|
() => ({borderBottomColor: indicatorColor || pal.colors.link}),
|
||||||
[indicatorColor, pal],
|
[indicatorColor, pal],
|
||||||
)
|
)
|
||||||
|
const myFeedsControl = useDialogControl()
|
||||||
const {isDesktop, isTablet} = useWebMediaQueries()
|
const {isDesktop, isTablet} = useWebMediaQueries()
|
||||||
const styles = isDesktop || isTablet ? desktopStyles : mobileStyles
|
const styles = isDesktop || isTablet ? desktopStyles : mobileStyles
|
||||||
|
|
||||||
@@ -147,6 +153,14 @@ export function TabBar({
|
|||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</DraggableScrollView>
|
</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 style={[pal.border, styles.outerBottomBorder]} />
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
@@ -178,6 +192,10 @@ const desktopStyles = StyleSheet.create({
|
|||||||
bottom: -1,
|
bottom: -1,
|
||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
},
|
},
|
||||||
|
dropdownBtn: {
|
||||||
|
paddingTop: 14,
|
||||||
|
paddingHorizontal: 14,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const mobileStyles = StyleSheet.create({
|
const mobileStyles = StyleSheet.create({
|
||||||
@@ -205,4 +223,8 @@ const mobileStyles = StyleSheet.create({
|
|||||||
bottom: -1,
|
bottom: -1,
|
||||||
borderBottomWidth: 1,
|
borderBottomWidth: 1,
|
||||||
},
|
},
|
||||||
|
dropdownBtn: {
|
||||||
|
paddingTop: 12,
|
||||||
|
paddingHorizontal: 14,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export function ProfileMenuDialog({
|
|||||||
<Dialog.Outer control={control}>
|
<Dialog.Outer control={control}>
|
||||||
<Dialog.Handle />
|
<Dialog.Handle />
|
||||||
|
|
||||||
<Dialog.ScrollableInner label={_(msg`Switch Account`)}>
|
<Dialog.ScrollableInner label={_(msg`App Menu`)}>
|
||||||
<View style={[a.gap_sm]}>
|
<View style={[a.gap_sm]}>
|
||||||
<ProfileCard onPress={onPressProfile} />
|
<ProfileCard onPress={onPressProfile} />
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user