From 7e041e595e56fb2f63f2cf379b2dcb06f31e6f45 Mon Sep 17 00:00:00 2001 From: root Date: Sat, 28 Jun 2025 14:46:57 +0000 Subject: [PATCH 1/3] Fixed the logic for deciding which comments are eligible for bounty awards. Tested with a variety of cases involving comments that were previously awarded bounties and have direct tips. --- components/Comment/AwardBountyModal.tsx | 99 +++++++++++++++++++------ 1 file changed, 78 insertions(+), 21 deletions(-) diff --git a/components/Comment/AwardBountyModal.tsx b/components/Comment/AwardBountyModal.tsx index 77c359299..51a3dbd28 100644 --- a/components/Comment/AwardBountyModal.tsx +++ b/components/Comment/AwardBountyModal.tsx @@ -338,19 +338,41 @@ const FilteredCommentFeed = ({ try { const CommentService = (await import('@/services/comment.service')).CommentService; - const response = await CommentService.fetchComments({ - documentId, - contentType, - sort: 'TOP', - pageSize: 100, - }); + + // Fetch both BOUNTY and REVIEW comments to get all eligible candidates, then combine them. + // we're using Promise.all to fetch both types parallelly. + const [bountyResponse, reviewResponse] = await Promise.all([ + CommentService.fetchComments({ + documentId, + contentType, + sort: 'TOP', + pageSize: 100, + filter: 'BOUNTY', + }), + CommentService.fetchComments({ + documentId, + contentType, + sort: 'TOP', + pageSize: 100, + filter: 'REVIEW', + }), + ]); if (isMounted) { - if (response && response.comments) { - setAllComments(response.comments); - } else { - setAllComments([]); - } + const bountyComments = bountyResponse?.comments || []; + const reviewComments = reviewResponse?.comments || []; + + // Deduplicate comments by ID to avoid duplicate candidates + // Added this as a defensive approach, + // for instance comments from the bounties tab may reappear on the reviews tab + // and vice versa. + + const combinedComments = [...bountyComments, ...reviewComments]; + const uniqueComments = combinedComments.filter( + (comment, index, array) => array.findIndex((c) => c.id === comment.id) === index + ); + + setAllComments(uniqueComments); setError(null); } } catch (err) { @@ -374,16 +396,44 @@ const FilteredCommentFeed = ({ }; }, [documentId, contentType, stableOnLoadingChange]); - // Filter out comments that have bounties and flatten the comment tree - const commentsWithoutBounties = useMemo(() => { + // Filter comments to find eligible candidates for bounty awards + // This was previously called commentsWithoutBounties + const eligibleComments = useMemo(() => { // Helper function to flatten a comment tree const flattenComments = (comments: Comment[]): Comment[] => { return comments.reduce((acc, comment) => { - // Check if the comment is eligible - const isBountyComment = comment.commentType === 'BOUNTY' || hasBounties(comment); + // ELIGIBILITY LOGIC: Check if comment is eligible for award + // is EligibleForAward was previously isBountyComment, renamed for clarity and consistency of terms used here + const isEligibleForAward = (comment: Comment): boolean => { + // DECISION NEEDED, REVIEW COMMENT AWARD OPTIONS: + // OPTION 1: Allow Double-Award of REVIEW comments even if they already have received past bounties award OR direct tips + // if (comment.commentType === 'REVIEW') { + // return true; + // } + + // OPTION 2: Exclude REVIEW comments that already have any awards. We ONLY allow REVIEW comments without existing bounties OR tips + // if (comment.commentType === 'REVIEW') { + // const hasAwardItems = hasBounties(comment) || (comment?.tips?.length || 0) > 0 || (comment?.awardedBountyAmount || 0) > 0; + // return !hasAwardItems; + // } + + // OPTION 3 BEST?: Exclude REVIEW comments that have previous bounty awards (awardedBountyAmount), + // but INCLUDE those that only received direct tips from elsewhere + // Note: awarded bounties appear as tips in the UI. (see components/Feed/FeedItemActions.tsx) + // so direct tips are tips that are not originated from awarded bounties + if (comment.commentType === 'REVIEW') { + const hasDirectTipsOnly = + (comment?.tips?.length || 0) > 0 && !(comment?.awardedBountyAmount || 0); + const hasNoPreviousAwards = + !hasBounties(comment) && !(comment?.awardedBountyAmount || 0); + return hasDirectTipsOnly || hasNoPreviousAwards; + } + + return false; + }; // Add eligible comments to the result - if (!isBountyComment) { + if (isEligibleForAward(comment)) { acc.push(comment); } @@ -403,14 +453,14 @@ const FilteredCommentFeed = ({ // Notify parent component about eligible comments when they change useEffect(() => { if (!isLoading) { - if (commentsWithoutBounties.length > 0) { - const eligibleIds = commentsWithoutBounties.map((comment) => comment.id); + if (eligibleComments.length > 0) { + const eligibleIds = eligibleComments.map((comment) => comment.id); stableOnSetEligibleComments(eligibleIds); } else { stableOnSetEligibleComments([]); } } - }, [commentsWithoutBounties, isLoading, stableOnSetEligibleComments]); + }, [eligibleComments, isLoading, stableOnSetEligibleComments]); if (isLoading) { return
Loading comments...
; @@ -420,7 +470,7 @@ const FilteredCommentFeed = ({ return
{error}
; } - if (commentsWithoutBounties.length === 0) { + if (eligibleComments.length === 0) { return (

@@ -433,7 +483,7 @@ const FilteredCommentFeed = ({ return (

- {commentsWithoutBounties.map((comment) => { + {eligibleComments.map((comment) => { const commentId = comment.id; const awardAmount = awardAmounts[commentId] || 0; const selectedPercentage = selectedPercentages[commentId] || 0; @@ -577,6 +627,13 @@ export const AwardBountyModal = ({ ); } + // DEBUG: Log what we're trying to award + console.log('Attempting to award bounty:', { + bountyId: activeBounty.id, + awards, + totalAmount: awards.reduce((sum, award) => sum + award.amount, 0), + }); + await BountyService.awardBounty(activeBounty.id, awards); toast.success('Bounty awards submitted successfully'); From eaac245e699bb4dda588708214c141db12b0772c Mon Sep 17 00:00:00 2001 From: root Date: Sat, 28 Jun 2025 15:04:09 +0000 Subject: [PATCH 2/3] Removing debug comments used for checking award status during dev --- components/Comment/AwardBountyModal.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/components/Comment/AwardBountyModal.tsx b/components/Comment/AwardBountyModal.tsx index 51a3dbd28..a6cbf84b1 100644 --- a/components/Comment/AwardBountyModal.tsx +++ b/components/Comment/AwardBountyModal.tsx @@ -627,13 +627,6 @@ export const AwardBountyModal = ({ ); } - // DEBUG: Log what we're trying to award - console.log('Attempting to award bounty:', { - bountyId: activeBounty.id, - awards, - totalAmount: awards.reduce((sum, award) => sum + award.amount, 0), - }); - await BountyService.awardBounty(activeBounty.id, awards); toast.success('Bounty awards submitted successfully'); From 89e1fd46a287c4cbffd30487d7a28b304c8805cd Mon Sep 17 00:00:00 2001 From: root Date: Mon, 14 Jul 2025 03:49:53 +0000 Subject: [PATCH 3/3] decided to choose option1 with lease restriction on bounty awards --- components/Comment/AwardBountyModal.tsx | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/components/Comment/AwardBountyModal.tsx b/components/Comment/AwardBountyModal.tsx index a6cbf84b1..7b8175827 100644 --- a/components/Comment/AwardBountyModal.tsx +++ b/components/Comment/AwardBountyModal.tsx @@ -405,11 +405,11 @@ const FilteredCommentFeed = ({ // ELIGIBILITY LOGIC: Check if comment is eligible for award // is EligibleForAward was previously isBountyComment, renamed for clarity and consistency of terms used here const isEligibleForAward = (comment: Comment): boolean => { - // DECISION NEEDED, REVIEW COMMENT AWARD OPTIONS: + // AWARD OPTIONS: // OPTION 1: Allow Double-Award of REVIEW comments even if they already have received past bounties award OR direct tips - // if (comment.commentType === 'REVIEW') { - // return true; - // } + if (comment.commentType === 'REVIEW') { + return true; + } // OPTION 2: Exclude REVIEW comments that already have any awards. We ONLY allow REVIEW comments without existing bounties OR tips // if (comment.commentType === 'REVIEW') { @@ -421,13 +421,13 @@ const FilteredCommentFeed = ({ // but INCLUDE those that only received direct tips from elsewhere // Note: awarded bounties appear as tips in the UI. (see components/Feed/FeedItemActions.tsx) // so direct tips are tips that are not originated from awarded bounties - if (comment.commentType === 'REVIEW') { - const hasDirectTipsOnly = - (comment?.tips?.length || 0) > 0 && !(comment?.awardedBountyAmount || 0); - const hasNoPreviousAwards = - !hasBounties(comment) && !(comment?.awardedBountyAmount || 0); - return hasDirectTipsOnly || hasNoPreviousAwards; - } + // if (comment.commentType === 'REVIEW') { + // const hasDirectTipsOnly = + // (comment?.tips?.length || 0) > 0 && !(comment?.awardedBountyAmount || 0); + // const hasNoPreviousAwards = + // !hasBounties(comment) && !(comment?.awardedBountyAmount || 0); + // return hasDirectTipsOnly || hasNoPreviousAwards; + // } return false; };