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,15 +28,13 @@ 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">
<Accordion selectionMode="multiple" selectedKeys={selected}>
{difficulties.map((data, i) => { {difficulties.map((data, i) => {
const rank = CHUNI_SCORE_RANKS[data.scoreRank!]; const rank = CHUNI_SCORE_RANKS[data.scoreRank!];
const badges = [ const badges = [
@ -46,16 +45,17 @@ export const ChuniMusicPlaylog = ({ music, playlog }: ChuniMusicPlaylogProps) =>
</> : rank} </> : rank}
</ChuniScoreBadge>, </ChuniScoreBadge>,
data.isSuccess ? <ChuniLampSuccessBadge key="2" className={badgeClass} success={data.isSuccess} /> : null, data.isSuccess ? <ChuniLampSuccessBadge key="2" className={badgeClass} success={data.isSuccess} /> : null,
<ChuniLampComboBadge key="3" className={badgeClass} {...data} /> (data.isFullCombo || data.isAllJustice) && <ChuniLampComboBadge key="3" className={badgeClass} {...data} />
].filter(x => x); ].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"
onClick={() => {
const key = i.toString();
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="flex items-center">
<div className="w-14 mr-2 p-0.5 bg-black"> <div className="w-14 mr-2 p-0.5 bg-black">
<ChuniLevelBadge className="w-full" music={data} /> <ChuniLevelBadge className="w-full" music={data} />
@ -64,28 +64,33 @@ export const ChuniMusicPlaylog = ({ music, playlog }: ChuniMusicPlaylogProps) =>
</div> </div>
{!data.playlog.length && <div className="text-right italic text-gray-500 flex-grow">No Play History</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.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"> {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()} <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"> {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()} <span className="font-semibold">Max Combo: </span>{data.maxComboCount.toLocaleString()}
</div> : null} </div> : null}
</div> </div>
{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}> {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' : ''}`}>
{badges} {badges}
</div> : null} </div> : null}
{data.playlog.length ? <Accordion selectedKeys={expanded[i]} onSelectionChange={k => setExpanded(e => ({ ...e, [i]: k as any }))}> </div>}>
<AccordionItem key="1" title="Play History"> <div className="flex flex-wrap gap-x-4 gap-y-2 mb-3 justify-center sm:justify-end max-sm:text-xs">
<span className="mr-auto max-sm:w-full text-center"><span className="font-semibold">Chart designer:</span> {data.chartDesigner}</span>
{!!data.tapJudgeCount && <span><span className="font-semibold">Tap:</span> {data.tapJudgeCount}</span>}
{!!data.flickJudgeCount && <span><span className="font-semibold">Flick:</span> {data.flickJudgeCount}</span>}
{!!data.holdJudgeCount && <span><span className="font-semibold">Hold:</span> {data.holdJudgeCount}</span>}
{!!data.slideJudgeCount && <span><span className="font-semibold">Slide:</span> {data.slideJudgeCount}</span>}
{!!data.airJudgeCount && <span><span className="font-semibold">Air:</span> {data.airJudgeCount}</span>}
</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
}
</div>)
})} })}
</Accordion>
</div>); </div>);
}; };