From e07a1274b21b04f12a41e3abae27293712c6653b Mon Sep 17 00:00:00 2001 From: Dylan Jeffers Date: Thu, 2 Apr 2026 17:38:56 -0700 Subject: [PATCH] Wire up text-post heart reactions using existing comment reaction infrastructure The heart button on text posts was non-functional. This connects it to the existing useReactToComment hook with entityType 'FanClub', enabling optimistic UI updates and proper SDK/blockchain writes on both web and mobile. Co-Authored-By: Claude Opus 4.6 --- .../tan-query/comments/useReactToComment.ts | 8 ++-- .../components/TextPostCard.tsx | 46 +++++++++++++++++-- .../components/TextPostCard.tsx | 16 +++++++ 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/packages/common/src/api/tan-query/comments/useReactToComment.ts b/packages/common/src/api/tan-query/comments/useReactToComment.ts index 92862a5cd1b..05397362365 100644 --- a/packages/common/src/api/tan-query/comments/useReactToComment.ts +++ b/packages/common/src/api/tan-query/comments/useReactToComment.ts @@ -16,6 +16,7 @@ export type ReactToCommentArgs = { currentSort: any trackId: ID isEntityOwner?: boolean + entityType?: 'Track' | 'FanClub' } export const useReactToComment = () => { @@ -27,20 +28,21 @@ export const useReactToComment = () => { userId, commentId, isLiked, - trackId + trackId, + entityType = 'Track' }: ReactToCommentArgs) => { const sdk = await audiusSdk() if (isLiked) { await sdk.comments.reactToComment({ userId: Id.parse(userId)!, commentId: Id.parse(commentId)!, - metadata: { entityId: trackId, entityType: 'Track' } + metadata: { entityId: trackId, entityType } }) } else { await sdk.comments.unreactToComment({ userId: Id.parse(userId)!, commentId: Id.parse(commentId)!, - metadata: { entityId: trackId, entityType: 'Track' } + metadata: { entityId: trackId, entityType } }) } }, diff --git a/packages/mobile/src/screens/coin-details-screen/components/TextPostCard.tsx b/packages/mobile/src/screens/coin-details-screen/components/TextPostCard.tsx index dbcfe5b5a3b..d40bac86c5e 100644 --- a/packages/mobile/src/screens/coin-details-screen/components/TextPostCard.tsx +++ b/packages/mobile/src/screens/coin-details-screen/components/TextPostCard.tsx @@ -1,6 +1,11 @@ import { useCallback } from 'react' -import { useComment } from '@audius/common/api' +import { + useComment, + useCurrentUserId, + useReactToComment, + useArtistCoin +} from '@audius/common/api' import type { ID } from '@audius/common/models' import { getLargestTimeUnitText } from '@audius/common/utils' @@ -13,6 +18,7 @@ import { Text } from '@audius/harmony-native' import { ProfilePicture } from 'app/components/core' +import { FavoriteButton } from 'app/components/favorite-button' import { UserLink } from 'app/components/user-link' import { useDrawer } from 'app/hooks/useDrawer' @@ -28,8 +34,26 @@ type TextPostCardProps = { export const TextPostCard = ({ commentId, mint }: TextPostCardProps) => { const { data: comment, isPending } = useComment(commentId) + const { data: currentUserId } = useCurrentUserId() + const { data: coin } = useArtistCoin(mint) + const { mutate: reactToComment } = useReactToComment() const { onOpen: openLockedTextPostDrawer } = useDrawer('LockedTextPost') + const isCoinOwner = currentUserId != null && coin?.ownerId === currentUserId + + const handleReact = useCallback(() => { + if (!currentUserId || !comment) return + reactToComment({ + commentId, + userId: currentUserId, + isLiked: !comment.isCurrentUserReacted, + currentSort: 'newest', + trackId: comment.entityId, + isEntityOwner: isCoinOwner, + entityType: 'FanClub' + }) + }, [currentUserId, comment, commentId, reactToComment, isCoinOwner]) + const handleUnlock = useCallback(() => { openLockedTextPostDrawer({ mint }) }, [openLockedTextPostDrawer, mint]) @@ -103,9 +127,23 @@ export const TextPostCard = ({ commentId, mint }: TextPostCardProps) => { ) : ( - - {comment.message} - + <> + + {comment.message} + + + + {comment.reactCount > 0 ? ( + + {comment.reactCount} + + ) : null} + + )} ) diff --git a/packages/web/src/pages/fan-club-detail-page/components/TextPostCard.tsx b/packages/web/src/pages/fan-club-detail-page/components/TextPostCard.tsx index e84dc0bfad4..2ccfaccf89d 100644 --- a/packages/web/src/pages/fan-club-detail-page/components/TextPostCard.tsx +++ b/packages/web/src/pages/fan-club-detail-page/components/TextPostCard.tsx @@ -5,6 +5,7 @@ import { useCurrentUserId, useEditComment, useDeleteTextPost, + useReactToComment, useArtistCoin } from '@audius/common/api' import { ID } from '@audius/common/models' @@ -72,6 +73,7 @@ export const TextPostCard = ({ commentId, mint }: TextPostCardProps) => { const { data: coin } = useArtistCoin(mint) const { mutate: editComment } = useEditComment() const { mutate: deleteTextPost } = useDeleteTextPost() + const { mutate: reactToComment } = useReactToComment() const [isEditing, setIsEditing] = useState(false) const [editMessageId, setEditMessageId] = useState(0) @@ -82,6 +84,19 @@ export const TextPostCard = ({ commentId, mint }: TextPostCardProps) => { const isCoinOwner = currentUserId != null && coin?.ownerId === currentUserId const canModify = isOwner || isCoinOwner + const handleReact = useCallback(() => { + if (!currentUserId || !comment) return + reactToComment({ + commentId, + userId: currentUserId, + isLiked: !comment.isCurrentUserReacted, + currentSort: 'newest', + trackId: comment.entityId, + isEntityOwner: isCoinOwner, + entityType: 'FanClub' + }) + }, [currentUserId, comment, commentId, reactToComment, isCoinOwner]) + const handleEdit = useCallback(() => { setEditMessageId((prev) => prev + 1) setIsEditing(true) @@ -254,6 +269,7 @@ export const TextPostCard = ({ commentId, mint }: TextPostCardProps) => { color={comment.isCurrentUserReacted ? 'active' : 'subdued'} aria-label='Heart post' size='s' + onClick={handleReact} /> {comment.reactCount > 0 ? (