Pinned feeds cards (#4526)
* Add lists support to FeedCard * Add useSavedFeeds query, similar to usePinnedFeedInfos * Integrate into Feeds screen * Fix alignment on mobile * Update usages * Add placeholder loading state * Handle no feeds state * Reuse previous data for placeholder * Staged loading * Improve staged loading * Use setQueryData approach to pre-caching * Add types for a little more safety * Fix precaching --------- Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
This commit is contained in:
parent
cb37647949
commit
4d6787009c
6 changed files with 447 additions and 233 deletions
|
@ -1,8 +1,6 @@
|
|||
import React from 'react'
|
||||
import {ActivityIndicator, type FlatList, StyleSheet, View} from 'react-native'
|
||||
import {AppBskyActorDefs, AppBskyFeedDefs} from '@atproto/api'
|
||||
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
|
||||
import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
|
||||
import {AppBskyFeedDefs} from '@atproto/api'
|
||||
import {msg, Trans} from '@lingui/macro'
|
||||
import {useLingui} from '@lingui/react'
|
||||
import {useFocusEffect} from '@react-navigation/native'
|
||||
|
@ -10,12 +8,11 @@ import debounce from 'lodash.debounce'
|
|||
|
||||
import {isNative, isWeb} from '#/platform/detection'
|
||||
import {
|
||||
getAvatarTypeFromUri,
|
||||
useFeedSourceInfoQuery,
|
||||
SavedFeedItem,
|
||||
useGetPopularFeedsQuery,
|
||||
useSavedFeeds,
|
||||
useSearchPopularFeedsMutation,
|
||||
} from '#/state/queries/feed'
|
||||
import {usePreferencesQuery} from '#/state/queries/preferences'
|
||||
import {useSession} from '#/state/session'
|
||||
import {useSetMinimalShellMode} from '#/state/shell'
|
||||
import {useComposerControls} from '#/state/shell/composer'
|
||||
|
@ -28,14 +25,10 @@ import {s} from 'lib/styles'
|
|||
import {ErrorMessage} from 'view/com/util/error/ErrorMessage'
|
||||
import {FAB} from 'view/com/util/fab/FAB'
|
||||
import {SearchInput} from 'view/com/util/forms/SearchInput'
|
||||
import {Link, TextLink} from 'view/com/util/Link'
|
||||
import {TextLink} from 'view/com/util/Link'
|
||||
import {List} from 'view/com/util/List'
|
||||
import {
|
||||
FeedFeedLoadingPlaceholder,
|
||||
LoadingPlaceholder,
|
||||
} from 'view/com/util/LoadingPlaceholder'
|
||||
import {FeedFeedLoadingPlaceholder} from 'view/com/util/LoadingPlaceholder'
|
||||
import {Text} from 'view/com/util/text/Text'
|
||||
import {UserAvatar} from 'view/com/util/UserAvatar'
|
||||
import {ViewHeader} from 'view/com/util/ViewHeader'
|
||||
import {NoFollowingFeed} from '#/screens/Feeds/NoFollowingFeed'
|
||||
import {NoSavedFeedsOfAnyType} from '#/screens/Feeds/NoSavedFeedsOfAnyType'
|
||||
|
@ -47,6 +40,7 @@ import {ListSparkle_Stroke2_Corner0_Rounded} from '#/components/icons/ListSparkl
|
|||
import hairlineWidth = StyleSheet.hairlineWidth
|
||||
import {Divider} from '#/components/Divider'
|
||||
import * as FeedCard from '#/components/FeedCard'
|
||||
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron'
|
||||
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Feeds'>
|
||||
|
||||
|
@ -61,9 +55,8 @@ type FlatlistSlice =
|
|||
key: string
|
||||
}
|
||||
| {
|
||||
type: 'savedFeedsLoading'
|
||||
type: 'savedFeedPlaceholder'
|
||||
key: string
|
||||
// pendingItems: number,
|
||||
}
|
||||
| {
|
||||
type: 'savedFeedNoResults'
|
||||
|
@ -72,8 +65,7 @@ type FlatlistSlice =
|
|||
| {
|
||||
type: 'savedFeed'
|
||||
key: string
|
||||
feedUri: string
|
||||
savedFeedConfig: AppBskyActorDefs.SavedFeed
|
||||
savedFeed: SavedFeedItem
|
||||
}
|
||||
| {
|
||||
type: 'savedFeedsLoadMore'
|
||||
|
@ -113,11 +105,11 @@ export function FeedsScreen(_props: Props) {
|
|||
const [query, setQuery] = React.useState('')
|
||||
const [isPTR, setIsPTR] = React.useState(false)
|
||||
const {
|
||||
data: preferences,
|
||||
isLoading: isPreferencesLoading,
|
||||
error: preferencesError,
|
||||
refetch: refetchPreferences,
|
||||
} = usePreferencesQuery()
|
||||
data: savedFeeds,
|
||||
isPlaceholderData: isSavedFeedsPlaceholder,
|
||||
error: savedFeedsError,
|
||||
refetch: refetchSavedFeeds,
|
||||
} = useSavedFeeds()
|
||||
const {
|
||||
data: popularFeeds,
|
||||
isFetching: isPopularFeedsFetching,
|
||||
|
@ -173,11 +165,11 @@ export function FeedsScreen(_props: Props) {
|
|||
const onPullToRefresh = React.useCallback(async () => {
|
||||
setIsPTR(true)
|
||||
await Promise.all([
|
||||
refetchPreferences().catch(_e => undefined),
|
||||
refetchSavedFeeds().catch(_e => undefined),
|
||||
refetchPopularFeeds().catch(_e => undefined),
|
||||
])
|
||||
setIsPTR(false)
|
||||
}, [setIsPTR, refetchPreferences, refetchPopularFeeds])
|
||||
}, [setIsPTR, refetchSavedFeeds, refetchPopularFeeds])
|
||||
const onEndReached = React.useCallback(() => {
|
||||
if (
|
||||
isPopularFeedsFetching ||
|
||||
|
@ -203,6 +195,11 @@ export function FeedsScreen(_props: Props) {
|
|||
|
||||
const items = React.useMemo(() => {
|
||||
let slices: FlatlistSlice[] = []
|
||||
const hasActualSavedCount =
|
||||
!isSavedFeedsPlaceholder ||
|
||||
(isSavedFeedsPlaceholder && (savedFeeds?.count || 0) > 0)
|
||||
const canShowDiscoverSection =
|
||||
!hasSession || (hasSession && hasActualSavedCount)
|
||||
|
||||
if (hasSession) {
|
||||
slices.push({
|
||||
|
@ -210,47 +207,63 @@ export function FeedsScreen(_props: Props) {
|
|||
type: 'savedFeedsHeader',
|
||||
})
|
||||
|
||||
if (preferencesError) {
|
||||
if (savedFeedsError) {
|
||||
slices.push({
|
||||
key: 'savedFeedsError',
|
||||
type: 'error',
|
||||
error: cleanError(preferencesError.toString()),
|
||||
error: cleanError(savedFeedsError.toString()),
|
||||
})
|
||||
} else {
|
||||
if (isPreferencesLoading || !preferences?.savedFeeds) {
|
||||
slices.push({
|
||||
key: 'savedFeedsLoading',
|
||||
type: 'savedFeedsLoading',
|
||||
// pendingItems: this.rootStore.preferences.savedFeeds.length || 3,
|
||||
})
|
||||
if (isSavedFeedsPlaceholder && !savedFeeds?.feeds.length) {
|
||||
/*
|
||||
* Initial render in placeholder state is 0 on a cold page load,
|
||||
* because preferences haven't loaded yet.
|
||||
*
|
||||
* In practice, `savedFeeds` is always defined, but we check for TS
|
||||
* and for safety.
|
||||
*
|
||||
* In both cases, we show 4 as the the loading state.
|
||||
*/
|
||||
const min = 8
|
||||
const count = savedFeeds
|
||||
? savedFeeds.count === 0
|
||||
? min
|
||||
: savedFeeds.count
|
||||
: min
|
||||
Array(count)
|
||||
.fill(0)
|
||||
.forEach((_, i) => {
|
||||
slices.push({
|
||||
key: 'savedFeedPlaceholder' + i,
|
||||
type: 'savedFeedPlaceholder',
|
||||
})
|
||||
})
|
||||
} else {
|
||||
if (preferences.savedFeeds?.length) {
|
||||
const noFollowingFeed = preferences.savedFeeds.every(
|
||||
if (savedFeeds?.feeds?.length) {
|
||||
const noFollowingFeed = savedFeeds.feeds.every(
|
||||
f => f.type !== 'timeline',
|
||||
)
|
||||
|
||||
slices = slices.concat(
|
||||
preferences.savedFeeds
|
||||
.filter(f => {
|
||||
return f.pinned
|
||||
savedFeeds.feeds
|
||||
.filter(s => {
|
||||
return s.config.pinned
|
||||
})
|
||||
.map(feed => ({
|
||||
key: `savedFeed:${feed.value}:${feed.id}`,
|
||||
.map(s => ({
|
||||
key: `savedFeed:${s.view?.uri}:${s.config.id}`,
|
||||
type: 'savedFeed',
|
||||
feedUri: feed.value,
|
||||
savedFeedConfig: feed,
|
||||
savedFeed: s,
|
||||
})),
|
||||
)
|
||||
slices = slices.concat(
|
||||
preferences.savedFeeds
|
||||
.filter(f => {
|
||||
return !f.pinned
|
||||
savedFeeds.feeds
|
||||
.filter(s => {
|
||||
return !s.config.pinned
|
||||
})
|
||||
.map(feed => ({
|
||||
key: `savedFeed:${feed.value}:${feed.id}`,
|
||||
.map(s => ({
|
||||
key: `savedFeed:${s.view?.uri}:${s.config.id}`,
|
||||
type: 'savedFeed',
|
||||
feedUri: feed.value,
|
||||
savedFeedConfig: feed,
|
||||
savedFeed: s,
|
||||
})),
|
||||
)
|
||||
|
||||
|
@ -270,59 +283,36 @@ export function FeedsScreen(_props: Props) {
|
|||
}
|
||||
}
|
||||
|
||||
slices.push({
|
||||
key: 'popularFeedsHeader',
|
||||
type: 'popularFeedsHeader',
|
||||
})
|
||||
|
||||
if (popularFeedsError || searchError) {
|
||||
if (!hasSession || (hasSession && canShowDiscoverSection)) {
|
||||
slices.push({
|
||||
key: 'popularFeedsError',
|
||||
type: 'error',
|
||||
error: cleanError(
|
||||
popularFeedsError?.toString() ?? searchError?.toString() ?? '',
|
||||
),
|
||||
key: 'popularFeedsHeader',
|
||||
type: 'popularFeedsHeader',
|
||||
})
|
||||
} else {
|
||||
if (isUserSearching) {
|
||||
if (isSearchPending || !searchResults) {
|
||||
slices.push({
|
||||
key: 'popularFeedsLoading',
|
||||
type: 'popularFeedsLoading',
|
||||
})
|
||||
} else {
|
||||
if (!searchResults || searchResults?.length === 0) {
|
||||
slices.push({
|
||||
key: 'popularFeedsNoResults',
|
||||
type: 'popularFeedsNoResults',
|
||||
})
|
||||
} else {
|
||||
slices = slices.concat(
|
||||
searchResults.map(feed => ({
|
||||
key: `popularFeed:${feed.uri}`,
|
||||
type: 'popularFeed',
|
||||
feedUri: feed.uri,
|
||||
feed,
|
||||
})),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (popularFeedsError || searchError) {
|
||||
slices.push({
|
||||
key: 'popularFeedsError',
|
||||
type: 'error',
|
||||
error: cleanError(
|
||||
popularFeedsError?.toString() ?? searchError?.toString() ?? '',
|
||||
),
|
||||
})
|
||||
} else {
|
||||
if (isPopularFeedsFetching && !popularFeeds?.pages) {
|
||||
slices.push({
|
||||
key: 'popularFeedsLoading',
|
||||
type: 'popularFeedsLoading',
|
||||
})
|
||||
} else {
|
||||
if (!popularFeeds?.pages) {
|
||||
if (isUserSearching) {
|
||||
if (isSearchPending || !searchResults) {
|
||||
slices.push({
|
||||
key: 'popularFeedsNoResults',
|
||||
type: 'popularFeedsNoResults',
|
||||
key: 'popularFeedsLoading',
|
||||
type: 'popularFeedsLoading',
|
||||
})
|
||||
} else {
|
||||
for (const page of popularFeeds.pages || []) {
|
||||
if (!searchResults || searchResults?.length === 0) {
|
||||
slices.push({
|
||||
key: 'popularFeedsNoResults',
|
||||
type: 'popularFeedsNoResults',
|
||||
})
|
||||
} else {
|
||||
slices = slices.concat(
|
||||
page.feeds.map(feed => ({
|
||||
searchResults.map(feed => ({
|
||||
key: `popularFeed:${feed.uri}`,
|
||||
type: 'popularFeed',
|
||||
feedUri: feed.uri,
|
||||
|
@ -330,12 +320,37 @@ export function FeedsScreen(_props: Props) {
|
|||
})),
|
||||
)
|
||||
}
|
||||
|
||||
if (isPopularFeedsFetchingNextPage) {
|
||||
}
|
||||
} else {
|
||||
if (isPopularFeedsFetching && !popularFeeds?.pages) {
|
||||
slices.push({
|
||||
key: 'popularFeedsLoading',
|
||||
type: 'popularFeedsLoading',
|
||||
})
|
||||
} else {
|
||||
if (!popularFeeds?.pages) {
|
||||
slices.push({
|
||||
key: 'popularFeedsLoadingMore',
|
||||
type: 'popularFeedsLoadingMore',
|
||||
key: 'popularFeedsNoResults',
|
||||
type: 'popularFeedsNoResults',
|
||||
})
|
||||
} else {
|
||||
for (const page of popularFeeds.pages || []) {
|
||||
slices = slices.concat(
|
||||
page.feeds.map(feed => ({
|
||||
key: `popularFeed:${feed.uri}`,
|
||||
type: 'popularFeed',
|
||||
feedUri: feed.uri,
|
||||
feed,
|
||||
})),
|
||||
)
|
||||
}
|
||||
|
||||
if (isPopularFeedsFetchingNextPage) {
|
||||
slices.push({
|
||||
key: 'popularFeedsLoadingMore',
|
||||
type: 'popularFeedsLoadingMore',
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -345,9 +360,9 @@ export function FeedsScreen(_props: Props) {
|
|||
return slices
|
||||
}, [
|
||||
hasSession,
|
||||
preferences,
|
||||
isPreferencesLoading,
|
||||
preferencesError,
|
||||
savedFeeds,
|
||||
isSavedFeedsPlaceholder,
|
||||
savedFeedsError,
|
||||
popularFeeds,
|
||||
isPopularFeedsFetching,
|
||||
popularFeedsError,
|
||||
|
@ -407,10 +422,7 @@ export function FeedsScreen(_props: Props) {
|
|||
({item}: {item: FlatlistSlice}) => {
|
||||
if (item.type === 'error') {
|
||||
return <ErrorMessage message={item.error} />
|
||||
} else if (
|
||||
item.type === 'popularFeedsLoadingMore' ||
|
||||
item.type === 'savedFeedsLoading'
|
||||
) {
|
||||
} else if (item.type === 'popularFeedsLoadingMore') {
|
||||
return (
|
||||
<View style={s.p10}>
|
||||
<ActivityIndicator size="large" />
|
||||
|
@ -459,8 +471,10 @@ export function FeedsScreen(_props: Props) {
|
|||
<NoSavedFeedsOfAnyType />
|
||||
</View>
|
||||
)
|
||||
} else if (item.type === 'savedFeedPlaceholder') {
|
||||
return <SavedFeedPlaceholder />
|
||||
} else if (item.type === 'savedFeed') {
|
||||
return <FeedOrFollowing savedFeedConfig={item.savedFeedConfig} />
|
||||
return <FeedOrFollowing savedFeed={item.savedFeed} />
|
||||
} else if (item.type === 'popularFeedsHeader') {
|
||||
return (
|
||||
<>
|
||||
|
@ -481,7 +495,7 @@ export function FeedsScreen(_props: Props) {
|
|||
} else if (item.type === 'popularFeed') {
|
||||
return (
|
||||
<View style={[a.px_lg, a.pt_lg, a.gap_lg]}>
|
||||
<FeedCard.Default feed={item.feed} />
|
||||
<FeedCard.Default type="feed" view={item.feed} />
|
||||
<Divider />
|
||||
</View>
|
||||
)
|
||||
|
@ -571,136 +585,103 @@ export function FeedsScreen(_props: Props) {
|
|||
)
|
||||
}
|
||||
|
||||
function FeedOrFollowing({
|
||||
savedFeedConfig: feed,
|
||||
}: {
|
||||
savedFeedConfig: AppBskyActorDefs.SavedFeed
|
||||
}) {
|
||||
return feed.type === 'timeline' ? (
|
||||
function FeedOrFollowing({savedFeed}: {savedFeed: SavedFeedItem}) {
|
||||
return savedFeed.type === 'timeline' ? (
|
||||
<FollowingFeed />
|
||||
) : (
|
||||
<SavedFeed savedFeedConfig={feed} />
|
||||
<SavedFeed savedFeed={savedFeed} />
|
||||
)
|
||||
}
|
||||
|
||||
function FollowingFeed() {
|
||||
const pal = usePalette('default')
|
||||
const t = useTheme()
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {_} = useLingui()
|
||||
return (
|
||||
<View
|
||||
testID={`saved-feed-timeline`}
|
||||
style={[
|
||||
pal.border,
|
||||
styles.savedFeed,
|
||||
isMobile && styles.savedFeedMobile,
|
||||
a.flex_1,
|
||||
a.px_lg,
|
||||
a.py_md,
|
||||
a.border_b,
|
||||
t.atoms.border_contrast_low,
|
||||
]}>
|
||||
<View
|
||||
style={[
|
||||
a.align_center,
|
||||
a.justify_center,
|
||||
{
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 3,
|
||||
backgroundColor: t.palette.primary_500,
|
||||
},
|
||||
]}>
|
||||
<FilterTimeline
|
||||
<FeedCard.Header>
|
||||
<View
|
||||
style={[
|
||||
a.align_center,
|
||||
a.justify_center,
|
||||
{
|
||||
width: 18,
|
||||
height: 18,
|
||||
width: 28,
|
||||
height: 28,
|
||||
borderRadius: 3,
|
||||
backgroundColor: t.palette.primary_500,
|
||||
},
|
||||
]}
|
||||
fill={t.palette.white}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
style={{flex: 1, flexDirection: 'row', gap: 8, alignItems: 'center'}}>
|
||||
<Text type="lg-medium" style={pal.text} numberOfLines={1}>
|
||||
<Trans>Following</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
]}>
|
||||
<FilterTimeline
|
||||
style={[
|
||||
{
|
||||
width: 18,
|
||||
height: 18,
|
||||
},
|
||||
]}
|
||||
fill={t.palette.white}
|
||||
/>
|
||||
</View>
|
||||
<FeedCard.TitleAndByline title={_(msg`Following`)} />
|
||||
</FeedCard.Header>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
function SavedFeed({
|
||||
savedFeedConfig: feed,
|
||||
savedFeed,
|
||||
}: {
|
||||
savedFeedConfig: AppBskyActorDefs.SavedFeed
|
||||
savedFeed: SavedFeedItem & {type: 'feed' | 'list'}
|
||||
}) {
|
||||
const pal = usePalette('default')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
const {data: info, error} = useFeedSourceInfoQuery({uri: feed.value})
|
||||
const typeAvatar = getAvatarTypeFromUri(feed.value)
|
||||
|
||||
if (!info)
|
||||
return (
|
||||
<SavedFeedLoadingPlaceholder
|
||||
key={`savedFeedLoadingPlaceholder:${feed.value}`}
|
||||
/>
|
||||
)
|
||||
const t = useTheme()
|
||||
const {view: feed} = savedFeed
|
||||
const displayName =
|
||||
savedFeed.type === 'feed' ? savedFeed.view.displayName : savedFeed.view.name
|
||||
|
||||
return (
|
||||
<Link
|
||||
testID={`saved-feed-${info.displayName}`}
|
||||
href={info.route.href}
|
||||
style={[pal.border, styles.savedFeed, isMobile && styles.savedFeedMobile]}
|
||||
hoverStyle={pal.viewLight}
|
||||
accessibilityLabel={info.displayName}
|
||||
accessibilityHint=""
|
||||
asAnchor
|
||||
anchorNoUnderline>
|
||||
{error ? (
|
||||
<FeedCard.Link testID={`saved-feed-${feed.displayName}`} feed={feed}>
|
||||
{({hovered, pressed}) => (
|
||||
<View
|
||||
style={{width: 28, flexDirection: 'row', justifyContent: 'center'}}>
|
||||
<FontAwesomeIcon
|
||||
icon="exclamation-circle"
|
||||
color={pal.colors.textLight}
|
||||
/>
|
||||
</View>
|
||||
) : (
|
||||
<UserAvatar type={typeAvatar} size={28} avatar={info.avatar} />
|
||||
)}
|
||||
<View
|
||||
style={{flex: 1, flexDirection: 'row', gap: 8, alignItems: 'center'}}>
|
||||
<Text type="lg-medium" style={pal.text} numberOfLines={1}>
|
||||
{info.displayName}
|
||||
</Text>
|
||||
{error ? (
|
||||
<View style={[styles.offlineSlug, pal.borderDark]}>
|
||||
<Text type="xs" style={pal.textLight}>
|
||||
<Trans>Feed offline</Trans>
|
||||
</Text>
|
||||
</View>
|
||||
) : null}
|
||||
</View>
|
||||
style={[
|
||||
a.flex_1,
|
||||
a.px_lg,
|
||||
a.py_md,
|
||||
a.border_b,
|
||||
t.atoms.border_contrast_low,
|
||||
(hovered || pressed) && t.atoms.bg_contrast_25,
|
||||
]}>
|
||||
<FeedCard.Header>
|
||||
<FeedCard.Avatar src={feed.avatar} size={28} />
|
||||
<FeedCard.TitleAndByline title={displayName} />
|
||||
|
||||
{isMobile && (
|
||||
<FontAwesomeIcon
|
||||
icon="chevron-right"
|
||||
size={14}
|
||||
style={pal.textLight as FontAwesomeIconStyle}
|
||||
/>
|
||||
<ChevronRight size="sm" fill={t.atoms.text_contrast_low.color} />
|
||||
</FeedCard.Header>
|
||||
</View>
|
||||
)}
|
||||
</Link>
|
||||
</FeedCard.Link>
|
||||
)
|
||||
}
|
||||
|
||||
function SavedFeedLoadingPlaceholder() {
|
||||
const pal = usePalette('default')
|
||||
const {isMobile} = useWebMediaQueries()
|
||||
function SavedFeedPlaceholder() {
|
||||
const t = useTheme()
|
||||
return (
|
||||
<View
|
||||
style={[
|
||||
pal.border,
|
||||
styles.savedFeed,
|
||||
isMobile && styles.savedFeedMobile,
|
||||
a.flex_1,
|
||||
a.px_lg,
|
||||
a.py_md,
|
||||
a.border_b,
|
||||
t.atoms.border_contrast_low,
|
||||
]}>
|
||||
<LoadingPlaceholder width={28} height={28} style={{borderRadius: 4}} />
|
||||
<LoadingPlaceholder width={140} height={12} />
|
||||
<FeedCard.Header>
|
||||
<FeedCard.AvatarPlaceholder size={28} />
|
||||
<FeedCard.TitleAndBylinePlaceholder />
|
||||
</FeedCard.Header>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -505,7 +505,7 @@ export function Explore() {
|
|||
a.px_lg,
|
||||
a.py_lg,
|
||||
]}>
|
||||
<FeedCard.Default feed={item.feed} />
|
||||
<FeedCard.Default type="feed" view={item.feed} />
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -306,7 +306,7 @@ let SearchScreenFeedsResults = ({
|
|||
a.px_lg,
|
||||
a.py_lg,
|
||||
]}>
|
||||
<FeedCard.Default feed={item} />
|
||||
<FeedCard.Default type="feed" view={item} />
|
||||
</View>
|
||||
)}
|
||||
keyExtractor={item => item.uri}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue