From 011da8f827b764fc47be50f5c628f1298c13f4d8 Mon Sep 17 00:00:00 2001 From: truelyeth Date: Thu, 4 Sep 2025 01:59:56 +0300 Subject: [PATCH] Add foundation award badge feature --- components/Feed/items/FeedItemComment.tsx | 12 ++++++++- components/ui/FoundationAwardBadge.tsx | 15 +++++++++++ types/comment.ts | 31 ++++++++++++++++------- types/feed.ts | 24 +++++++++++++++++- 4 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 components/ui/FoundationAwardBadge.tsx diff --git a/components/Feed/items/FeedItemComment.tsx b/components/Feed/items/FeedItemComment.tsx index 98b1705c7..c667903e3 100644 --- a/components/Feed/items/FeedItemComment.tsx +++ b/components/Feed/items/FeedItemComment.tsx @@ -13,6 +13,7 @@ import { RelatedWorkCard } from '@/components/Paper/RelatedWorkCard'; import { Avatar } from '@/components/ui/Avatar'; import { LegacyCommentBanner } from '@/components/LegacyCommentBanner'; import { BaseFeedItem } from '@/components/Feed/BaseFeedItem'; +import { FoundationAwardBadge } from '@/components/ui/FoundationAwardBadge'; // Define the recursive rendering component for parent comments const RenderParentComment: FC<{ comment: ParentCommentPreview; level: number }> = ({ @@ -149,7 +150,10 @@ export const FeedItemComment: FC = ({ {isReview && (
- +
+ + {comment.awardedBountySolution?.isFoundationAwarded && } +
)} @@ -160,6 +164,12 @@ export const FeedItemComment: FC = ({ )} + {!isReview && comment.awardedBountySolution?.isFoundationAwarded && ( +
+ +
+ )} +
( + + + + Awarded + + +); \ No newline at end of file diff --git a/types/comment.ts b/types/comment.ts index fb399b38f..002dc6370 100644 --- a/types/comment.ts +++ b/types/comment.ts @@ -41,6 +41,13 @@ export interface Thread { raw: any; } +export interface AwardedBountySolution { + id: number; + awardedAmount: number; + awardedBy?: User; + isFoundationAwarded: boolean; +} + export interface Comment { id: number; content: any; @@ -56,6 +63,7 @@ export interface Comment { commentType: CommentType; bountyAmount?: number; awardedBountyAmount?: number; + awardedBountySolution?: AwardedBountySolution; expirationDate?: string; isPublic?: boolean; isRemoved?: boolean; @@ -65,6 +73,8 @@ export interface Comment { tips?: Tip[]; thread: Thread; userVote?: UserVoteType; + cachedAcademicScore?: number; + scoreLastCalculated?: string; metadata?: { isVoteUpdate?: boolean; [key: string]: any; @@ -87,12 +97,9 @@ export const transformContent = (raw: any): string => { }; export const transformComment = (raw: any): Comment => { - // Transform user_vote from API - // It can be either an object with vote_type or a direct numeric value let userVote: UserVoteType | undefined; if (raw.user_vote) { - // If user_vote is an object with vote_type property if (typeof raw.user_vote === 'object' && raw.user_vote.vote_type !== undefined) { const voteType = raw.user_vote.vote_type; if (voteType === 1) { @@ -103,7 +110,6 @@ export const transformComment = (raw: any): Comment => { userVote = 'NEUTRAL'; } } - // If user_vote is a direct numeric value (for backward compatibility) else if (typeof raw.user_vote === 'number') { if (raw.user_vote === 1) { userVote = 'UPVOTE'; @@ -115,15 +121,19 @@ export const transformComment = (raw: any): Comment => { } } - // Group bounties with their contributions const bounties = groupBountiesWithContributions(raw.bounties || []); - - // Transform tips const tips = (raw.purchases || []).map(transformTip); - - // Determine the comment type - if it has bounties, it should be a BOUNTY type const commentType = raw.comment_type || (bounties.length > 0 ? 'BOUNTY' : 'GENERIC_COMMENT'); + const awardedBountySolution = raw.awarded_bounty_solution + ? { + id: raw.awarded_bounty_solution.id, + awardedAmount: raw.awarded_bounty_solution.awarded_amount, + awardedBy: raw.awarded_bounty_solution.awarded_by, + isFoundationAwarded: raw.awarded_bounty_solution.is_foundation_awarded || false, + } + : undefined; + const result = { id: raw.id, content: raw.comment_content_json || raw.comment_content, @@ -140,6 +150,7 @@ export const transformComment = (raw: any): Comment => { commentType, bountyAmount: raw.amount, awardedBountyAmount: raw.awarded_bounty_amount, + awardedBountySolution, expirationDate: raw.expiration_date, isPublic: raw.is_public, isRemoved: raw.is_removed, @@ -148,6 +159,8 @@ export const transformComment = (raw: any): Comment => { tips, thread: transformThread(raw.thread), userVote, + cachedAcademicScore: raw.cached_academic_score, + scoreLastCalculated: raw.score_last_calculated, raw, metadata: raw.metadata, }; diff --git a/types/feed.ts b/types/feed.ts index acfc9aced..e82e7e514 100644 --- a/types/feed.ts +++ b/types/feed.ts @@ -4,7 +4,7 @@ import { Topic, transformTopic } from './topic'; import { createTransformer, BaseTransformed } from './transformer'; import { Work, transformPaper, transformPost, FundingRequest, ContentType } from './work'; import { Bounty, transformBounty } from './bounty'; -import { Comment, CommentType, ContentFormat, transformComment } from './comment'; +import { Comment, CommentType, ContentFormat, transformComment, AwardedBountySolution } from './comment'; import { Fundraise, transformFundraise } from './funding'; import { Journal } from './journal'; import { UserVoteType } from './reaction'; @@ -113,6 +113,11 @@ export interface FeedBountyContent extends BaseFeedContent { contentFormat: ContentFormat; commentType: CommentType; id: number; + awardedBountySolution?: { + id: number; + awardedAmount: number; + isFoundationAwarded: boolean; + }; }; } @@ -126,6 +131,7 @@ export interface FeedCommentContent extends BaseFeedContent { commentType: CommentType; score: number; reviewScore?: number; + awardedBountySolution?: AwardedBountySolution; thread?: { id: number; threadType: string; @@ -499,6 +505,7 @@ export const transformFeedEntry = (feedEntry: RawApiFeedEntry): FeedEntry => { created_location: '', unified_document_id: content_object.unified_document_id, bounty_amount: content_object.bounty_amount, + awarded_bounty_solution: content_object.awarded_bounty_solution, }; // Check if the comment is associated with a paper or post for related work @@ -527,6 +534,7 @@ export const transformFeedEntry = (feedEntry: RawApiFeedEntry): FeedEntry => { commentType: content_object.comment_type as CommentType, score: transformedComment.score || 0, reviewScore: transformedComment.reviewScore || 0, + awardedBountySolution: transformedComment.awardedBountySolution, thread: content_object.thread_id ? { id: content_object.thread_id, @@ -813,6 +821,13 @@ export const transformCommentToFeedItem = ( objectId: comment.thread.objectId, } : undefined, + awardedBountySolution: comment.awardedBountySolution + ? { + id: comment.awardedBountySolution.id, + awardedAmount: comment.awardedBountySolution.awardedAmount, + isFoundationAwarded: comment.awardedBountySolution.isFoundationAwarded, + } + : undefined, }, relatedDocumentId: comment.thread?.objectId, relatedDocumentContentType: contentType, @@ -879,6 +894,13 @@ export const transformBountyCommentToFeedItem = ( content: comment.content, contentFormat: comment.contentFormat || 'QUILL_EDITOR', commentType: comment.commentType || 'BOUNTY', + awardedBountySolution: comment.awardedBountySolution + ? { + id: comment.awardedBountySolution.id, + awardedAmount: comment.awardedBountySolution.awardedAmount, + isFoundationAwarded: comment.awardedBountySolution.isFoundationAwarded, + } + : undefined, }, };