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