chuni: show chart credits and note counts on music detail

This commit is contained in:
sk1982 2024-04-13 04:53:51 -04:00
parent 9f126d84b1
commit 8a864c2cf7

View File

@ -19,7 +19,8 @@ type ChuniMusicPlaylogProps = {
export const ChuniMusicPlaylog = ({ music, playlog }: ChuniMusicPlaylogProps) => { export const ChuniMusicPlaylog = ({ music, playlog }: ChuniMusicPlaylogProps) => {
type Music = (typeof music)[number]; type Music = (typeof music)[number];
type Playlog = (typeof playlog)['data'][number]; type Playlog = (typeof playlog)['data'][number];
const defaultExpanded: Record<string, Set<string>> = {}
const [selected, setSelected] = useState(new Set<string>());
const difficulties: (Music & { playlog: Playlog[] })[] = []; const difficulties: (Music & { playlog: Playlog[] })[] = [];
music.forEach(m => { music.forEach(m => {
@ -27,65 +28,69 @@ export const ChuniMusicPlaylog = ({ music, playlog }: ChuniMusicPlaylogProps) =>
}); });
playlog.data.forEach(play => { playlog.data.forEach(play => {
defaultExpanded[play.chartId!] = new Set();
difficulties[play.chartId!].playlog.push(play); difficulties[play.chartId!].playlog.push(play);
}); });
const [expanded, setExpanded] = useState(defaultExpanded);
const badgeClass = 'h-6 sm:h-8'; const badgeClass = 'h-6 sm:h-8';
return (<div className="flex flex-col w-full px-2 sm:px-0"> return (<div className="flex flex-col w-full px-1 sm:px-0">
{difficulties.map((data, i) => { <Accordion selectionMode="multiple" selectedKeys={selected}>
const rank = CHUNI_SCORE_RANKS[data.scoreRank!]; {difficulties.map((data, i) => {
const badges = [ const rank = CHUNI_SCORE_RANKS[data.scoreRank!];
!!data.scoreRank && <ChuniScoreBadge variant={getVariantFromRank(data.scoreRank)} className={`${badgeClass} tracking-[0.05cqw]`} key="1"> const badges = [
{rank.endsWith('+') ? <> !!data.scoreRank && <ChuniScoreBadge variant={getVariantFromRank(data.scoreRank)} className={`${badgeClass} tracking-[0.05cqw]`} key="1">
{rank.slice(0, -1)} {rank.endsWith('+') ? <>
<div className="inline-block translate-y-[-15cqh]">+</div> {rank.slice(0, -1)}
</> : rank} <div className="inline-block translate-y-[-15cqh]">+</div>
</ChuniScoreBadge>, </> : rank}
data.isSuccess ? <ChuniLampSuccessBadge key="2" className={badgeClass} success={data.isSuccess} /> : null, </ChuniScoreBadge>,
<ChuniLampComboBadge key="3" className={badgeClass} {...data} /> data.isSuccess ? <ChuniLampSuccessBadge key="2" className={badgeClass} success={data.isSuccess} /> : null,
].filter(x => x); (data.isFullCombo || data.isAllJustice) && <ChuniLampComboBadge key="3" className={badgeClass} {...data} />
].filter(x => x);
const toggleExpanded = () => expanded[i] && setExpanded(e =>
({ ...e,
[i]: e[i].size ? new Set() : new Set(['1'])
}));
return (<div key={i} className="mb-2 border-b pb-2 border-gray-500 flex flex-row flex-wrap"> // <div key={i} className="mb-2 border-b pb-2 border-gray-500 flex flex-row flex-wrap items-center">
<div className={`flex items-center gap-2 flex-wrap w-full lg:w-auto lg:flex-grow ${data.playlog.length ? 'cursor-pointer' : ''}`} onClick={toggleExpanded}> return (<AccordionItem key={i.toString()} classNames={{ trigger: 'py-0 my-2' }} title={<div className="flex flex-row flex-wrap items-center gap-y-1.5"
<div className="flex items-center"> onClick={() => {
<div className="w-14 mr-2 p-0.5 bg-black"> const key = i.toString();
<ChuniLevelBadge className="w-full" music={data} /> setSelected(s => s.has(key) ? new Set([...s].filter(k => k !== key)) : new Set([...s, key]))
}}>
<div className={`flex items-center gap-2 flex-wrap lg:flex-grow ${data.playlog.length ? 'cursor-pointer w-full lg:w-auto' : 'flex-grow'}`}>
<div className="flex items-center">
<div className="w-14 mr-2 p-0.5 bg-black">
<ChuniLevelBadge className="w-full" music={data} />
</div>
<div className="text-xl font-semibold">{CHUNI_DIFFICULTIES[i]}</div>
</div> </div>
<div className="text-xl font-semibold">{CHUNI_DIFFICULTIES[i]}</div> {!data.playlog.length && <div className="text-right italic text-gray-500 flex-grow">No Play History</div>}
{data.rating ? <ChuniRating className="text-2xl text-right" rating={+data.rating * 100} /> : null}
{data.scoreMax ? <div className="ml-2 text-center flex-grow sm:flex-grow-0 max-sm:text-sm">
<span className="font-semibold">High Score: </span>{data.scoreMax.toLocaleString()}
</div> : null}
{data.maxComboCount ? <div className="ml-2 text-center flex-grow sm:flex-grow-0 max-sm:text-sm">
<span className="font-semibold">Max Combo: </span>{data.maxComboCount.toLocaleString()}
</div> : null}
</div> </div>
{!data.playlog.length && <div className="text-right italic text-gray-500 flex-grow">No Play History</div>} {badges.length ? <div className={`flex-grow items-center lg:flex-grow-0 ml-auto mr-auto sm:ml-0 lg:ml-auto lg:mr-0 flex gap-0.5 flex-wrap justify-center sm:justify-start ${data.playlog.length ? 'cursor-pointer' : ''}`}>
{data.rating ? <ChuniRating className="text-2xl text-right" rating={+data.rating * 100} /> : null} {badges}
{data.scoreMax ? <div className="ml-2 text-center flex-grow sm:flex-grow-0">
<span className="font-semibold">High Score: </span>{data.scoreMax.toLocaleString()}
</div> : null} </div> : null}
{data.maxComboCount ? <div className="ml-2 text-center flex-grow sm:flex-grow-0"> </div>}>
<span className="font-semibold">Max Combo: </span>{data.maxComboCount.toLocaleString()} <div className="flex flex-wrap gap-x-4 gap-y-2 mb-3 justify-center sm:justify-end max-sm:text-xs">
</div> : null} <span className="mr-auto max-sm:w-full text-center"><span className="font-semibold">Chart designer:</span> {data.chartDesigner}</span>
</div> {!!data.tapJudgeCount && <span><span className="font-semibold">Tap:</span> {data.tapJudgeCount}</span>}
{badges.length ? <div className={`flex-grow lg:flex-grow-0 ml-auto mr-auto sm:ml-0 lg:ml-auto lg:mr-0 mt-2 flex gap-0.5 flex-wrap justify-center sm:justify-start ${data.playlog.length ? 'cursor-pointer' : ''}`} onClick={toggleExpanded}> {!!data.flickJudgeCount && <span><span className="font-semibold">Flick:</span> {data.flickJudgeCount}</span>}
{badges} {!!data.holdJudgeCount && <span><span className="font-semibold">Hold:</span> {data.holdJudgeCount}</span>}
</div> : null} {!!data.slideJudgeCount && <span><span className="font-semibold">Slide:</span> {data.slideJudgeCount}</span>}
{data.playlog.length ? <Accordion selectedKeys={expanded[i]} onSelectionChange={k => setExpanded(e => ({ ...e, [i]: k as any }))}> {!!data.airJudgeCount && <span><span className="font-semibold">Air:</span> {data.airJudgeCount}</span>}
<AccordionItem key="1" title="Play History"> </div>
<div className="grid grid-cols-1 lg:grid-cols-2 2xl:grid-cols-3 4xl:grid-cols-4 5xl:grid-cols-5 6xl:grid-cols-6 gap-2"> <div className="grid grid-cols-1 lg:grid-cols-2 2xl:grid-cols-3 4xl:grid-cols-4 5xl:grid-cols-5 6xl:grid-cols-6 gap-2">
{data.playlog.map(p => <ChuniPlaylogCard key={p.id} {data.playlog.map(p => <ChuniPlaylogCard key={p.id}
showDetails showDetails
badgeClass="h-5 sm:h-6 md:h-5 lg:h-[1.125rem] 3xl:h-5" badgeClass="h-5 sm:h-6 md:h-5 lg:h-[1.125rem] 3xl:h-5 md:-mt-1"
playlog={p} className="h-64 md:h-52" />)} playlog={p} className="h-64 md:h-52" />)}
</div> </div>
</AccordionItem> </AccordionItem>);
</Accordion> : null })}
} </Accordion>
</div>)
})}
</div>); </div>);
}; };