Let specified facets take priority in feed descriptions (#10740)

This commit is contained in:
Samuel Newman
2026-06-06 00:13:26 +03:00
committed by GitHub
parent fb8316fd85
commit a5338090cb
3 changed files with 50 additions and 55 deletions
-3
View File
@@ -1346,9 +1346,6 @@
}
},
"src/screens/Profile/components/ProfileFeedHeader.tsx": {
"@typescript-eslint/no-floating-promises": {
"count": 1
},
"@typescript-eslint/no-misused-promises": {
"count": 5
}
@@ -1,9 +1,7 @@
import {useCallback, useMemo, useState} from 'react'
import {View} from 'react-native'
import {AtUri} from '@atproto/api'
import {msg} from '@lingui/core/macro'
import {useLingui} from '@lingui/react'
import {Plural, Trans} from '@lingui/react/macro'
import {Plural, Trans, useLingui} from '@lingui/react/macro'
import {useHaptics} from '#/lib/haptics'
import {makeCustomFeedLink, makeProfileLink} from '#/lib/routes/links'
@@ -26,7 +24,6 @@ import {atoms as a, useBreakpoints, useTheme, web} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import * as Dialog from '#/components/Dialog'
import {Divider} from '#/components/Divider'
import {useRichText} from '#/components/hooks/useRichText'
import {ArrowOutOfBoxModified_Stroke2_Corner2_Rounded as Share} from '#/components/icons/ArrowOutOfBox'
import {CircleInfo_Stroke2_Corner0_Rounded as CircleInfo} from '#/components/icons/CircleInfo'
import {DotGrid3x1_Stroke2_Corner0_Rounded as Ellipsis} from '#/components/icons/DotGrid'
@@ -86,7 +83,7 @@ export function ProfileFeedHeaderSkeleton() {
export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
const t = useTheme()
const {_, i18n} = useLingui()
const {t: l, i18n} = useLingui()
const ax = useAnalytics()
const {hasSession} = useSession()
const {gtMobile} = useBreakpoints()
@@ -121,7 +118,7 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
if (savedFeedConfig) {
await removeFeed(savedFeedConfig)
Toast.show(_(msg`Removed from your feeds`))
Toast.show(l`Removed from your feeds`)
ax.metric('feed:unsave', {feedUrl: info.uri})
} else {
await addSavedFeeds([
@@ -131,14 +128,12 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
pinned: false,
},
])
Toast.show(_(msg`Saved to your feeds`))
Toast.show(l`Saved to your feeds`)
ax.metric('feed:save', {feedUrl: info.uri})
}
} catch (err) {
Toast.show(
_(
msg`There was an issue updating your feeds, please check your internet connection and try again.`,
),
l`There was an issue updating your feeds, please check your internet connection and try again.`,
{
type: 'error',
},
@@ -161,10 +156,10 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
])
if (pinned) {
Toast.show(_(msg`Pinned ${info.displayName} to Home`))
Toast.show(l`Pinned ${info.displayName} to Home`)
ax.metric('feed:pin', {feedUrl: info.uri})
} else {
Toast.show(_(msg`Unpinned ${info.displayName} from Home`))
Toast.show(l`Unpinned ${info.displayName} from Home`)
ax.metric('feed:unpin', {feedUrl: info.uri})
}
} else {
@@ -175,11 +170,11 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
pinned: true,
},
])
Toast.show(_(msg`Pinned ${info.displayName} to Home`))
Toast.show(l`Pinned ${info.displayName} to Home`)
ax.metric('feed:pin', {feedUrl: info.uri})
}
} catch (e) {
Toast.show(_(msg`There was an issue contacting the server`), {
Toast.show(l`There was an issue contacting the server`, {
type: 'error',
})
logger.error('Failed to toggle pinned feed', {message: e})
@@ -194,7 +189,7 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
<Layout.Header.BackButton />
<Layout.Header.Content align="left">
<Button
label={_(msg`Open feed info screen`)}
label={l`Open feed info screen`}
style={[
a.justify_start,
{
@@ -295,12 +290,12 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
<Layout.Header.Slot>
{isPinned ? (
<Menu.Root>
<Menu.Trigger label={_(msg`Open feed options menu`)}>
<Menu.Trigger label={l`Open feed options menu`}>
{({props}) => {
return (
<Button
{...props}
label={_(msg`Open feed options menu`)}
label={l`Open feed options menu`}
size="small"
variant="ghost"
shape="square"
@@ -314,23 +309,21 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
<Menu.Outer>
<Menu.Item
disabled={isFeedStateChangePending}
label={_(msg`Unpin from home`)}
label={l`Unpin from home`}
onPress={onTogglePinned}>
<Menu.ItemText>{_(msg`Unpin from home`)}</Menu.ItemText>
<Menu.ItemText>{l`Unpin from home`}</Menu.ItemText>
<Menu.ItemIcon icon={X} position="right" />
</Menu.Item>
<Menu.Item
disabled={isFeedStateChangePending}
label={
isSaved
? _(msg`Remove from my feeds`)
: _(msg`Save to my feeds`)
isSaved ? l`Remove from my feeds` : l`Save to my feeds`
}
onPress={onToggleSaved}>
<Menu.ItemText>
{isSaved
? _(msg`Remove from my feeds`)
: _(msg`Save to my feeds`)}
? l`Remove from my feeds`
: l`Save to my feeds`}
</Menu.ItemText>
<Menu.ItemIcon
icon={isSaved ? Trash : Plus}
@@ -341,7 +334,7 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
</Menu.Root>
) : (
<Button
label={_(msg`Pin to Home`)}
label={l`Pin to Home`}
size="small"
variant="ghost"
shape="square"
@@ -354,11 +347,10 @@ export function ProfileFeedHeader({info}: {info: FeedSourceFeedInfo}) {
)}
</Layout.Header.Outer>
</Layout.Center>
<Dialog.Outer control={infoControl}>
<Dialog.Handle />
<Dialog.ScrollableInner
label={_(msg`Feed menu`)}
label={l`Feed menu`}
style={[gtMobile ? {width: 'auto', minWidth: 450} : a.w_full]}>
<DialogInner
info={info}
@@ -393,13 +385,12 @@ function DialogInner({
isFeedStateChangePending: boolean
}) {
const t = useTheme()
const {_} = useLingui()
const {t: l} = useLingui()
const ax = useAnalytics()
const {hasSession} = useSession()
const playHaptic = useHaptics()
const control = Dialog.useDialogContext()
const reportDialogControl = useReportDialogControl()
const [rt] = useRichText(info.description.text)
const {mutateAsync: likeFeed, isPending: isLikePending} = useLikeMutation()
const {mutateAsync: unlikeFeed, isPending: isUnlikePending} =
useUnlikeMutation()
@@ -422,9 +413,7 @@ function DialogInner({
}
} catch (err) {
Toast.show(
_(
msg`There was an issue contacting the server, please check your internet connection and try again.`,
),
l`There was an issue contacting the server, please check your internet connection and try again.`,
{
type: 'error',
},
@@ -436,9 +425,9 @@ function DialogInner({
const onPressShare = useCallback(() => {
playHaptic()
const url = toShareUrl(info.route.href)
shareUrl(url)
void shareUrl(url)
ax.metric('feed:share', {feedUrl: info.uri})
}, [info, playHaptic])
}, [ax, info, playHaptic])
const onPressReport = useCallback(() => {
reportDialogControl.open()
@@ -462,7 +451,7 @@ function DialogInner({
<Trans>
By{' '}
<InlineLinkText
label={_(msg`View ${info.creatorHandle}'s profile`)}
label={l`View ${info.creatorHandle}'s profile`}
to={makeProfileLink({
did: info.creatorDid,
handle: info.creatorHandle,
@@ -477,7 +466,7 @@ function DialogInner({
</View>
<Button
label={_(msg`Share this feed`)}
label={l`Share this feed`}
size="small"
variant="ghost"
color="secondary"
@@ -486,13 +475,11 @@ function DialogInner({
<ButtonIcon icon={Share} size="lg" />
</Button>
</View>
<RichText value={rt} style={[a.text_md]} />
<RichText value={info.description} style={[a.text_md]} />
<View style={[a.flex_row, a.gap_sm, a.align_center]}>
{typeof likeCount === 'number' && (
<InlineLinkText
label={_(msg`View users who like this feed`)}
label={l`View users who like this feed`}
to={makeCustomFeedLink(info.creatorDid, feedRkey, 'liked-by')}
style={[a.underline, t.atoms.text_contrast_medium]}
onPress={() => control.close()}>
@@ -502,13 +489,12 @@ function DialogInner({
</InlineLinkText>
)}
</View>
{hasSession && (
<>
<View style={[a.flex_row, a.gap_sm, a.align_center, a.pt_sm]}>
<Button
disabled={isLikePending || isUnlikePending}
label={_(msg`Like this feed`)}
label={l`Like this feed`}
size="small"
color="secondary"
onPress={onToggleLiked}
@@ -525,7 +511,7 @@ function DialogInner({
</Button>
<Button
disabled={isFeedStateChangePending}
label={isPinned ? _(msg`Unpin feed`) : _(msg`Pin feed`)}
label={isPinned ? l`Unpin feed` : l`Pin feed`}
size="small"
color={isPinned ? 'secondary' : 'primary'}
onPress={onTogglePinned}
@@ -547,7 +533,7 @@ function DialogInner({
</Text>
<Button
label={_(msg`Report feed`)}
label={l`Report feed`}
size="small"
variant="solid"
color="secondary"
+20 -8
View File
@@ -101,6 +101,15 @@ export function hydrateFeedGenerator(
const href = `/profile/${urip.hostname}/${collection}/${urip.rkey}`
const route = router.matchPath(href)
const description = new RichText({
text: view.description || '',
facets: (view.descriptionFacets || [])?.slice(),
})
if (!view.descriptionFacets) {
description.detectFacetsWithoutResolution()
}
return {
type: 'feed',
view,
@@ -116,10 +125,7 @@ export function hydrateFeedGenerator(
displayName: view.displayName
? sanitizeDisplayName(view.displayName)
: t`Feed by ${sanitizeHandle(view.creator.handle, '@')}`,
description: new RichText({
text: view.description || '',
facets: (view.descriptionFacets || [])?.slice(),
}),
description,
creatorDid: view.creator.did,
creatorHandle: view.creator.handle,
likeCount: view.likeCount,
@@ -136,6 +142,15 @@ export function hydrateList(view: AppBskyGraphDefs.ListView): FeedSourceInfo {
const href = `/profile/${urip.hostname}/${collection}/${urip.rkey}`
const route = router.matchPath(href)
const description = new RichText({
text: view.description || '',
facets: (view.descriptionFacets || [])?.slice(),
})
if (!view.descriptionFacets) {
description.detectFacetsWithoutResolution()
}
return {
type: 'list',
view,
@@ -148,10 +163,7 @@ export function hydrateList(view: AppBskyGraphDefs.ListView): FeedSourceInfo {
},
cid: view.cid,
avatar: view.avatar,
description: new RichText({
text: view.description || '',
facets: (view.descriptionFacets || [])?.slice(),
}),
description,
creatorDid: view.creator.did,
creatorHandle: view.creator.handle,
displayName: view.name