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
6 changes: 3 additions & 3 deletions components/Bounty/BountyInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ export const BountyInfo: FC<BountyInfoProps> = ({
};

// Get display amount (handles Foundation bounties with flat $150 USD)
const { amount: displayAmount } = useMemo(
const { amount: displayAmount, isFoundation } = useMemo(
() => getBountyDisplayAmount(bounty, exchangeRate, showUSD),
[bounty, exchangeRate, showUSD]
);
Expand Down Expand Up @@ -231,15 +231,15 @@ export const BountyInfo: FC<BountyInfoProps> = ({
variant="text"
size="md"
showText={true}
currency={showUSD ? 'USD' : 'RSC'}
currency={isFoundation ? 'USD' : showUSD ? 'USD' : 'RSC'}
className="p-0 gap-0"
textColor={isActive ? 'text-orange-700' : 'text-gray-600'}
fontWeight="font-bold"
showExchangeRate={false}
iconColor={isActive ? '#ea580c' : colors.gray[500]}
iconSize={18}
shorten
skipConversion={showUSD}
skipConversion={isFoundation || showUSD}
/>
</div>

Expand Down
8 changes: 5 additions & 3 deletions components/Bounty/BountyInfoSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,13 @@ export const BountyInfoSummary: FC<BountyInfoSummaryProps> = ({
);

// Calculate display amount (handles Foundation bounties with flat $150 USD)
const { amount: totalAmount } = useMemo(
const { amount: totalAmount, foundationBountyCount } = useMemo(
() => getTotalBountyDisplayAmount(openBounties, exchangeRate, showUSD),
[openBounties, exchangeRate, showUSD]
);

const isAllFoundation = foundationBountyCount > 0 && foundationBountyCount === openBounties.length;

// If no open bounties, don't render anything
if (openBounties.length === 0) {
return null;
Expand All @@ -60,14 +62,14 @@ export const BountyInfoSummary: FC<BountyInfoSummaryProps> = ({
variant="text"
size="xl"
showText={true}
currency={showUSD ? 'USD' : 'RSC'}
currency={isAllFoundation ? 'USD' : showUSD ? 'USD' : 'RSC'}
className="p-0 gap-0"
textColor="text-gray-700"
showExchangeRate={false}
iconColor={colors.gray[700]}
iconSize={24}
shorten
skipConversion={showUSD}
skipConversion={isAllFoundation || showUSD}
/>
</div>
</div>
Expand Down
7 changes: 6 additions & 1 deletion components/Bounty/BountyMetadataLine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ interface BountyMetadataLineProps {
* Useful when the caller has pre-calculated the amount (e.g., Foundation bounty flat fee).
*/
skipConversion?: boolean;
/**
* Optional currency override. If provided, uses this currency instead of the global preference.
*/
currency?: 'RSC' | 'USD';
}

export const BountyMetadataLine = ({
Expand All @@ -30,6 +34,7 @@ export const BountyMetadataLine = ({
showDeadline = true,
bountyStatus,
skipConversion = false,
currency,
}: BountyMetadataLineProps) => {
const { showUSD } = useCurrencyPreference();

Expand All @@ -54,7 +59,7 @@ export const BountyMetadataLine = ({
amount={amount}
size="sm"
variant={isOpen ? 'badge' : 'disabled'}
currency={showUSD ? 'USD' : 'RSC'}
currency={currency || (showUSD ? 'USD' : 'RSC')}
skipConversion={skipConversion}
/>
</div>
Expand Down
29 changes: 17 additions & 12 deletions components/Bounty/lib/bountyUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,10 +523,20 @@ const getCreatorUserId = (bounty: Bounty): number | undefined => {
* @returns True if the bounty was created by the Foundation account
*/
export const isFoundationBounty = (bounty: Bounty): boolean => {
if (!FOUNDATION_USER_ID) return false;

const creatorUserId = getCreatorUserId(bounty);
return creatorUserId === FOUNDATION_USER_ID;

if (FOUNDATION_USER_ID && creatorUserId === FOUNDATION_USER_ID) {
return true;
}

// Fallback check for Foundation account by name
const authorProfile = bounty.createdBy?.authorProfile;
const fullName = authorProfile?.fullName || bounty.createdBy?.fullName;
if (fullName?.toLowerCase() === 'researchhub foundation') {
return true;
}

return false;
};

/**
Expand Down Expand Up @@ -571,19 +581,14 @@ export const getBountyDisplayAmount = (
showUSD: boolean
): { amount: number; isFoundation: boolean } => {
const isFoundation = isFoundationBounty(bounty);
const rscAmount = parseFloat(bounty.totalAmount || bounty.amount || '0');

if (isFoundation) {
if (showUSD) {
// Show flat $150 USD for Foundation bounties
return { amount: FOUNDATION_BOUNTY_FLAT_USD, isFoundation: true };
}
// Show RSC equivalent of $150 USD for Foundation bounties
// exchangeRate is USD per RSC, so RSC = USD / exchangeRate
const rscEquivalent = exchangeRate > 0 ? FOUNDATION_BOUNTY_FLAT_USD / exchangeRate : rscAmount;
return { amount: Math.round(rscEquivalent), isFoundation: true };
// ALWAYS return flat $150 USD for Foundation bounties to avoid confusion
return { amount: FOUNDATION_BOUNTY_FLAT_USD, isFoundation: true };
}

const rscAmount = parseFloat(bounty.totalAmount || bounty.amount || '0');

if (showUSD) {
return { amount: Math.round(rscAmount * exchangeRate), isFoundation: false };
}
Expand Down
14 changes: 8 additions & 6 deletions components/Feed/FeedItemActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -398,12 +398,14 @@ export const FeedItemActions: FC<FeedItemActionsProps> = ({
const hasOpenBounties = openBounties.length > 0;

// Calculate total bounty amount for open bounties (handles Foundation bounties with flat $150 USD)
const { amount: totalBountyAmount } = getTotalBountyDisplayAmount(
const { amount: totalBountyAmount, foundationBountyCount } = getTotalBountyDisplayAmount(
openBounties,
exchangeRate,
showUSD
);

const isAllFoundation = foundationBountyCount > 0 && foundationBountyCount === openBounties.length;

// Use media queries to determine screen size
const isMobile = useMediaQuery('(max-width: 480px)');
const isTabletOrSmaller = useMediaQuery('(max-width: 768px)');
Expand Down Expand Up @@ -620,7 +622,7 @@ export const FeedItemActions: FC<FeedItemActionsProps> = ({
totalAmount={totalBountyAmount}
href={href}
showUSD={showUSD}
skipConversion={showUSD}
skipConversion={isAllFoundation || showUSD}
/>
}
position="top"
Expand All @@ -640,12 +642,12 @@ export const FeedItemActions: FC<FeedItemActionsProps> = ({
textColor="inherit"
iconColor="inherit"
iconSize={18}
currency={showUSD ? 'USD' : 'RSC'}
currency={isAllFoundation ? 'USD' : showUSD ? 'USD' : 'RSC'}
shorten={true}
showExchangeRate={false}
showIcon={true}
showText={false}
skipConversion={showUSD}
skipConversion={isAllFoundation || showUSD}
/>
}
showTooltip={false}
Expand All @@ -667,12 +669,12 @@ export const FeedItemActions: FC<FeedItemActionsProps> = ({
textColor="inherit"
iconColor="inherit"
iconSize={18}
currency={showUSD ? 'USD' : 'RSC'}
currency={isAllFoundation ? 'USD' : showUSD ? 'USD' : 'RSC'}
shorten={true}
showExchangeRate={false}
showIcon={true}
showText={false}
skipConversion={showUSD}
skipConversion={isAllFoundation || showUSD}
/>
}
showTooltip={false}
Expand Down
9 changes: 7 additions & 2 deletions components/Feed/items/FeedItemBountyComment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,11 @@ export const FeedItemBountyComment: FC<FeedItemBountyCommentProps> = ({
const hasSolutions = solutionsCount > 0;

// Calculate display amount (handles Foundation bounties with flat $150 USD)
const { amount: displayBountyAmount } = getBountyDisplayAmount(bounty, exchangeRate, showUSD);
const { amount: displayBountyAmount, isFoundation } = getBountyDisplayAmount(
bounty,
exchangeRate,
showUSD
);

// Always use generic action text without amount
const bountyActionText = 'created a bounty';
Expand Down Expand Up @@ -313,7 +317,8 @@ export const FeedItemBountyComment: FC<FeedItemBountyCommentProps> = ({
solutionsCount={solutionsCount}
bountyStatus={bounty.status}
showDeadline={showDeadline}
skipConversion={showUSD}
currency={isFoundation ? 'USD' : showUSD ? 'USD' : 'RSC'}
skipConversion={isFoundation || showUSD}
/>
</div>

Expand Down
12 changes: 7 additions & 5 deletions components/banners/EarningOpportunityBanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,13 @@ export const EarningOpportunityBanner = ({

// Calculate display amount (handles Foundation bounties with flat $150 USD)
const openBounties = useMemo(() => getOpenBounties(metadata.bounties || []), [metadata.bounties]);
const { amount: displayAmount } = useMemo(
const { amount: displayAmount, foundationBountyCount } = useMemo(
() => getTotalBountyDisplayAmount(openBounties, exchangeRate, showUSD),
[openBounties, exchangeRate, showUSD]
);

const isAllFoundation = foundationBountyCount > 0 && foundationBountyCount === openBounties.length;

// Check if we can display the bounty amount (exchange rate loaded if USD preferred)
const canDisplayAmount = !showUSD || (showUSD && !isExchangeRateLoading && exchangeRate > 0);

Expand Down Expand Up @@ -81,14 +83,14 @@ export const EarningOpportunityBanner = ({
amount={displayAmount}
variant="text"
size="sm"
currency={showUSD ? 'USD' : 'RSC'}
currency={isAllFoundation ? 'USD' : showUSD ? 'USD' : 'RSC'}
showExchangeRate={false}
showText={true}
showIcon={false}
textColor="text-orange-600"
fontWeight="font-semibold"
className="p-0 text-sm inline-flex"
skipConversion={showUSD}
skipConversion={isAllFoundation || showUSD}
/>
</>
)}
Expand Down Expand Up @@ -124,14 +126,14 @@ export const EarningOpportunityBanner = ({
amount={displayAmount}
variant="text"
size="sm"
currency={showUSD ? 'USD' : 'RSC'}
currency={isAllFoundation ? 'USD' : showUSD ? 'USD' : 'RSC'}
showExchangeRate={false}
showText={true}
showIcon={false}
textColor="text-orange-600"
fontWeight="font-semibold"
className="p-0 text-base inline-flex"
skipConversion={showUSD}
skipConversion={isAllFoundation || showUSD}
/>
</>
)}
Expand Down
7 changes: 5 additions & 2 deletions components/tooltips/BountyTooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ interface BountyTooltipProps {
showUSD?: boolean;
/** If true, the amount is already in the target currency and should not be converted */
skipConversion?: boolean;
/** If true, the bounty is from the foundation and should be displayed in USD */
isFoundation?: boolean;
}

export function BountyTooltip({
totalAmount,
href,
showUSD = false,
skipConversion = false,
isFoundation = false,
}: BountyTooltipProps) {
const handleClick = (e: React.MouseEvent) => {
e.stopPropagation();
Expand All @@ -38,12 +41,12 @@ export function BountyTooltip({
iconColor="#f97316"
iconSize={20}
fontWeight="font-bold"
currency={showUSD ? 'USD' : 'RSC'}
currency={isFoundation ? 'USD' : showUSD ? 'USD' : 'RSC'}
shorten={true}
showExchangeRate={false}
showIcon={true}
showText={false}
skipConversion={skipConversion}
skipConversion={isFoundation || skipConversion}
/>
</span>
</div>
Expand Down
5 changes: 4 additions & 1 deletion types/feed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -951,7 +951,8 @@ export const transformFeedEntry = (feedEntry: RawApiFeedEntry): FeedEntry => {
: undefined,
tips: [], // Default empty tips
awardedBountyAmount: (content as any)?.awardedBountyAmount,
isAwardedForFoundationBounty: (content as any)?.bounty_creator_id,
isAwardedForFoundationBounty:
(content as any)?.bounty_creator_id?.toString() === FOUNDATION_USER_ID?.toString(),
} as FeedEntry;
};

Expand Down Expand Up @@ -1113,6 +1114,8 @@ export const transformBountyCommentToFeedItem = (
userVote: comment.userVote,
tips: comment.tips,
awardedBountyAmount: comment.awardedBountyAmount,
isAwardedForFoundationBounty:
comment.bountyCreatorId?.toString() === FOUNDATION_USER_ID?.toString(),
};
};

Expand Down