diff --git a/src/components/FeedCard.tsx b/src/components/FeedCard.tsx
index c5e6200527..8a12d7559f 100644
--- a/src/components/FeedCard.tsx
+++ b/src/components/FeedCard.tsx
@@ -6,7 +6,7 @@ import {
AtUri,
RichText as RichTextApi,
} from '@atproto/api'
-import {msg, Plural, Trans} from '@lingui/macro'
+import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useQueryClient} from '@tanstack/react-query'
@@ -17,8 +17,10 @@ import {
useAddSavedFeedsMutation,
usePreferencesQuery,
useRemoveFeedMutation,
+ useUpdateSavedFeedsMutation,
} from '#/state/queries/preferences'
import {useSession} from '#/state/session'
+import {formatCount} from '#/view/com/util/numeric/format'
import * as Toast from '#/view/com/util/Toast'
import {UserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, useTheme} from '#/alf'
@@ -28,6 +30,7 @@ import {
type ButtonProps,
ButtonText,
} from '#/components/Button'
+import {Heart2_Filled_Stroke2_Corner0_Rounded as HeartIcon} from '#/components/icons/Heart2'
import {Pin_Stroke2_Corner0_Rounded as PinIcon} from '#/components/icons/Pin'
import {Link as InternalLink, type LinkProps} from '#/components/Link'
import {Loader} from '#/components/Loader'
@@ -134,9 +137,9 @@ export function TitleAndByline({
{creator && (
- Feed by {sanitizeHandle(creator.handle, '@')}
+ By {sanitizeHandle(creator.handle, '@')}
)}
@@ -213,12 +216,14 @@ export function DescriptionPlaceholder() {
export function Likes({count}: {count: number}) {
const t = useTheme()
+ const {i18n} = useLingui()
return (
-
-
- Liked by
-
-
+
+
+
+ {formatCount(i18n, count)}
+
+
)
}
@@ -252,6 +257,8 @@ function SaveButtonInner({
useAddSavedFeedsMutation()
const {isPending: isRemovePending, mutateAsync: removeFeed} =
useRemoveFeedMutation()
+ const {isPending: isUpdatePending, mutateAsync: updateSavedFeeds} =
+ useUpdateSavedFeedsMutation()
const uri = view.uri
const type = view.uri.includes('app.bsky.feed.generator') ? 'feed' : 'list'
@@ -259,23 +266,26 @@ function SaveButtonInner({
const savedFeedConfig = React.useMemo(() => {
return preferences?.savedFeeds?.find(feed => feed.value === uri)
}, [preferences?.savedFeeds, uri])
+ const isPinned = savedFeedConfig?.pinned ?? false
const removePromptControl = Prompt.usePromptControl()
- const isPending = isAddSavedFeedPending || isRemovePending
+ const isPending = isAddSavedFeedPending || isRemovePending || isUpdatePending
- const toggleSave = React.useCallback(
+ const onPinFeed = React.useCallback(
async (e: GestureResponderEvent) => {
e.preventDefault()
e.stopPropagation()
try {
if (savedFeedConfig) {
- await removeFeed(savedFeedConfig)
+ // Feed is saved but not pinned, update it to be pinned
+ await updateSavedFeeds([{...savedFeedConfig, pinned: true}])
} else {
+ // Feed is not saved, save it with pinned=true
await saveFeeds([
{
type,
value: uri,
- pinned: pin || false,
+ pinned: true,
},
])
}
@@ -285,10 +295,22 @@ function SaveButtonInner({
Toast.show(_(msg`Failed to update feeds`), 'xmark')
}
},
- [_, pin, saveFeeds, removeFeed, uri, savedFeedConfig, type],
+ [_, pin, saveFeeds, updateSavedFeeds, uri, savedFeedConfig, type],
)
- const onPrompRemoveFeed = React.useCallback(
+ const onRemoveFeed = React.useCallback(async () => {
+ try {
+ if (savedFeedConfig) {
+ await removeFeed(savedFeedConfig)
+ }
+ Toast.show(_(msg({message: 'Feeds updated!', context: 'toast'})))
+ } catch (err: any) {
+ logger.error(err, {message: `FeedCard: failed to remove feed`})
+ Toast.show(_(msg`Failed to update feeds`), 'xmark')
+ }
+ }, [_, removeFeed, savedFeedConfig])
+
+ const onPromptRemoveFeed = React.useCallback(
async (e: GestureResponderEvent) => {
e.preventDefault()
e.stopPropagation()
@@ -302,13 +324,17 @@ function SaveButtonInner({
<>