Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion components/Feed/items/FeedItemComment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 }> = ({
Expand Down Expand Up @@ -149,7 +150,10 @@ export const FeedItemComment: FC<FeedItemCommentProps> = ({
<BaseFeedItem entry={entry} href={commentPageUrl} showHeader={false} showActions={false}>
{isReview && (
<div className="flex justify-between items-center mb-3">
<ContentTypeBadge type="review" />
<div className="flex items-center gap-2">
<ContentTypeBadge type="review" />
{comment.awardedBountySolution?.isFoundationAwarded && <FoundationAwardBadge />}
</div>
</div>
)}

Expand All @@ -160,6 +164,12 @@ export const FeedItemComment: FC<FeedItemCommentProps> = ({
</div>
)}

{!isReview && comment.awardedBountySolution?.isFoundationAwarded && (
<div className="mb-3">
<FoundationAwardBadge />
</div>
)}

<div className="text-gray-600 mb-4">
<CommentReadOnly
content={comment.content}
Expand Down
15 changes: 15 additions & 0 deletions components/ui/FoundationAwardBadge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Badge } from '@/components/ui/Badge';
import { Tooltip } from '@/components/ui/Tooltip';
import { ResearchCoinIcon } from '@/components/ui/icons/ResearchCoinIcon';

export const FoundationAwardBadge = () => (
<Tooltip content="Verified and approved by the ResearchHub Editor Team">
<Badge
variant="primary"
className="bg-purple-100 text-purple-700 border-purple-300 gap-1.5 py-1"
>
<ResearchCoinIcon size={16} outlined color="currentColor" />
<span>Awarded</span>
</Badge>
</Tooltip>
);
31 changes: 22 additions & 9 deletions types/comment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -56,6 +63,7 @@ export interface Comment {
commentType: CommentType;
bountyAmount?: number;
awardedBountyAmount?: number;
awardedBountySolution?: AwardedBountySolution;
expirationDate?: string;
isPublic?: boolean;
isRemoved?: boolean;
Expand All @@ -65,6 +73,8 @@ export interface Comment {
tips?: Tip[];
thread: Thread;
userVote?: UserVoteType;
cachedAcademicScore?: number;
scoreLastCalculated?: string;
metadata?: {
isVoteUpdate?: boolean;
[key: string]: any;
Expand All @@ -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) {
Expand All @@ -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';
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
};
Expand Down
24 changes: 23 additions & 1 deletion types/feed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -113,6 +113,11 @@ export interface FeedBountyContent extends BaseFeedContent {
contentFormat: ContentFormat;
commentType: CommentType;
id: number;
awardedBountySolution?: {
id: number;
awardedAmount: number;
isFoundationAwarded: boolean;
};
};
}

Expand All @@ -126,6 +131,7 @@ export interface FeedCommentContent extends BaseFeedContent {
commentType: CommentType;
score: number;
reviewScore?: number;
awardedBountySolution?: AwardedBountySolution;
thread?: {
id: number;
threadType: string;
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
},
};

Expand Down