mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 23:16:21 +00:00
Add badge leaderboard UI and badge rarity styling
This commit is contained in:
@@ -30,7 +30,6 @@ export const DraggableWindow: FC<DraggableWindowProps> = props => {
|
||||
const [isPositioned, setIsPositioned] = useState(false);
|
||||
const [dragHandler, setDragHandler] = useState<HTMLElement>(null);
|
||||
const elementRef = useRef<HTMLDivElement>();
|
||||
|
||||
const bringToTop = useCallback(() => {
|
||||
let zIndex = 400;
|
||||
for (const existingWindow of CURRENT_WINDOWS) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { BadgeImageReadyEvent, GetEventDispatcher, GetSessionDataManager, NitroSprite, TextureUtils } from '@nitrots/nitro-renderer';
|
||||
import { CSSProperties, FC, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { createPortal } from 'react-dom';
|
||||
import { GetConfigurationValue, LocalizeBadgeDescription, LocalizeBadgeName, LocalizeText } from '../../api';
|
||||
import { BadgeLeaderboardStat, ensureBadgeLeaderboardLoaded, getCachedBadgeRarityStat, GetConfigurationValue, LocalizeBadgeDescription, LocalizeBadgeName, LocalizeText } from '../../api';
|
||||
import { Base, BaseProps } from '../Base';
|
||||
|
||||
export interface LayoutBadgeImageViewProps extends BaseProps<HTMLDivElement>
|
||||
@@ -12,13 +12,25 @@ export interface LayoutBadgeImageViewProps extends BaseProps<HTMLDivElement>
|
||||
customTitle?: string;
|
||||
isGrayscale?: boolean;
|
||||
scale?: number;
|
||||
showRarityInfo?: boolean;
|
||||
highlightRarity?: boolean;
|
||||
}
|
||||
|
||||
const BADGE_RARITY_COLORS: Record<string, { glow: string; pillBackground: string; pillBorder: string; pillText: string }> = {
|
||||
common: { glow: 'rgba(148, 163, 184, 0.55)', pillBackground: 'rgba(71, 85, 105, 0.16)', pillBorder: 'rgba(100, 116, 139, 0.45)', pillText: '#475569' },
|
||||
rare: { glow: 'rgba(59, 130, 246, 0.7)', pillBackground: 'rgba(59, 130, 246, 0.12)', pillBorder: 'rgba(37, 99, 235, 0.38)', pillText: '#1d4ed8' },
|
||||
epic: { glow: 'rgba(168, 85, 247, 0.72)', pillBackground: 'rgba(168, 85, 247, 0.14)', pillBorder: 'rgba(147, 51, 234, 0.4)', pillText: '#7e22ce' },
|
||||
legendary: { glow: 'rgba(249, 115, 22, 0.76)', pillBackground: 'rgba(249, 115, 22, 0.16)', pillBorder: 'rgba(234, 88, 12, 0.4)', pillText: '#c2410c' },
|
||||
mythical: { glow: 'rgba(236, 72, 153, 0.76)', pillBackground: 'rgba(236, 72, 153, 0.15)', pillBorder: 'rgba(219, 39, 119, 0.4)', pillText: '#be185d' },
|
||||
unique: { glow: 'rgba(34, 197, 94, 0.76)', pillBackground: 'rgba(34, 197, 94, 0.14)', pillBorder: 'rgba(22, 163, 74, 0.4)', pillText: '#15803d' }
|
||||
};
|
||||
|
||||
export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
|
||||
{
|
||||
const { badgeCode = null, isGroup = false, showInfo = false, customTitle = null, isGrayscale = false, scale = 1, classNames = [], style = {}, children = null, ...rest } = props;
|
||||
const { badgeCode = null, isGroup = false, showInfo = false, customTitle = null, isGrayscale = false, scale = 1, showRarityInfo = false, highlightRarity = false, classNames = [], style = {}, children = null, ...rest } = props;
|
||||
const [ imageElement, setImageElement ] = useState<HTMLImageElement>(null);
|
||||
const [ tooltipPosition, setTooltipPosition ] = useState<{ top: number; left: number } | null>(null);
|
||||
const [ badgeRarityStat, setBadgeRarityStat ] = useState<BadgeLeaderboardStat>(null);
|
||||
const badgeRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const tooltipsEnabled = showInfo && GetConfigurationValue<boolean>('badge.descriptions.enabled', true);
|
||||
@@ -73,10 +85,21 @@ export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
|
||||
}
|
||||
}
|
||||
|
||||
if(highlightRarity && badgeRarityStat)
|
||||
{
|
||||
const colors = BADGE_RARITY_COLORS[badgeRarityStat.rarity];
|
||||
|
||||
if(colors)
|
||||
{
|
||||
newStyle.borderRadius = 8;
|
||||
newStyle.boxShadow = `0 0 0 1px ${ colors.glow }, 0 0 14px ${ colors.glow }`;
|
||||
}
|
||||
}
|
||||
|
||||
if(Object.keys(style).length) newStyle = { ...newStyle, ...style };
|
||||
|
||||
return newStyle;
|
||||
}, [ badgeCode, isGroup, imageElement, scale, style ]);
|
||||
}, [ badgeCode, badgeRarityStat, highlightRarity, isGroup, imageElement, scale, style ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
@@ -136,6 +159,46 @@ export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
|
||||
return () => GetEventDispatcher().removeEventListener(BadgeImageReadyEvent.IMAGE_READY, onBadgeImageReadyEvent);
|
||||
}, [ badgeCode, isGroup ]);
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
if(isGroup || !badgeCode || (!showRarityInfo && !highlightRarity))
|
||||
{
|
||||
setBadgeRarityStat(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const cached = getCachedBadgeRarityStat(badgeCode);
|
||||
|
||||
if(cached)
|
||||
{
|
||||
setBadgeRarityStat(cached);
|
||||
return;
|
||||
}
|
||||
|
||||
let cancelled = false;
|
||||
|
||||
ensureBadgeLeaderboardLoaded()
|
||||
.then(() =>
|
||||
{
|
||||
if(cancelled) return;
|
||||
|
||||
setBadgeRarityStat(getCachedBadgeRarityStat(badgeCode));
|
||||
})
|
||||
.catch(() =>
|
||||
{
|
||||
if(cancelled) return;
|
||||
|
||||
setBadgeRarityStat(null);
|
||||
});
|
||||
|
||||
return () => { cancelled = true; };
|
||||
}, [ badgeCode, highlightRarity, isGroup, showRarityInfo ]);
|
||||
|
||||
const rarityColors = badgeRarityStat ? BADGE_RARITY_COLORS[badgeRarityStat.rarity] : null;
|
||||
const rarityLabel = badgeRarityStat ? LocalizeText(`badge.rarity.${ badgeRarityStat.rarity }`) : '';
|
||||
const rarityText = badgeRarityStat ? LocalizeText('badge.rarity.badge', [ 'rarity' ], [ rarityLabel ]) : '';
|
||||
const ownersText = badgeRarityStat ? LocalizeText('badge.owner_count', [ 'count' ], [ badgeRarityStat.ownerCount.toString() ]) : '';
|
||||
|
||||
return (
|
||||
<Base
|
||||
innerRef={ badgeRef }
|
||||
@@ -149,6 +212,19 @@ export const LayoutBadgeImageView: FC<LayoutBadgeImageViewProps> = props =>
|
||||
className="fixed z-[9999] pointer-events-none select-none w-[210px] rounded-[.25rem] bg-[#fff] text-black py-1 px-2 small"
|
||||
style={ { top: tooltipPosition.top, left: tooltipPosition.left } }>
|
||||
<div className="font-bold mb-1">{ isGroup ? customTitle : LocalizeBadgeName(badgeCode) }</div>
|
||||
{ showRarityInfo && badgeRarityStat &&
|
||||
<div className="flex flex-col gap-1 mb-1">
|
||||
<div
|
||||
className="inline-flex items-center self-start rounded-full px-2 py-[2px] text-[10px] font-bold uppercase tracking-[0.04em]"
|
||||
style={ {
|
||||
background: rarityColors.pillBackground,
|
||||
border: `1px solid ${ rarityColors.pillBorder }`,
|
||||
color: rarityColors.pillText
|
||||
} }>
|
||||
{ rarityText }
|
||||
</div>
|
||||
<div className="text-[10px] text-[#5f5f5f]">{ ownersText }</div>
|
||||
</div> }
|
||||
<div>{ isGroup ? LocalizeText('group.badgepopup.body') : LocalizeBadgeDescription(badgeCode) }</div>
|
||||
</div>,
|
||||
document.body
|
||||
|
||||
Reference in New Issue
Block a user