chuni: add max combo

This commit is contained in:
sk1982 2024-03-18 21:54:53 -04:00
parent fa1257ec72
commit d69d3a2a87
3 changed files with 34 additions and 20 deletions

View File

@ -23,6 +23,7 @@ export const getMusic = async (musicId?: number) => {
) )
.select(({ fn }) => [...CHUNI_MUSIC_PROPERTIES, .select(({ fn }) => [...CHUNI_MUSIC_PROPERTIES,
'score.isFullCombo', 'score.isAllJustice', 'score.isSuccess', 'score.scoreRank', 'score.scoreMax', 'score.isFullCombo', 'score.isAllJustice', 'score.isSuccess', 'score.scoreRank', 'score.scoreMax',
'score.maxComboCount',
fn<boolean>('NOT ISNULL', ['favorite.favId']).as('favorite'), fn<boolean>('NOT ISNULL', ['favorite.favId']).as('favorite'),
chuniRating()]) chuniRating()])
.where(({ selectFrom, eb, and, or }) => and([ .where(({ selectFrom, eb, and, or }) => and([

View File

@ -68,6 +68,9 @@ export const ChuniMusicPlaylog = ({ music, playlog }: ChuniMusicPlaylogProps) =>
{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">
<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">
<span className="font-semibold">Max Combo: </span>{data.maxComboCount.toLocaleString()}
</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 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} {badges}

View File

@ -1,12 +1,14 @@
'use client';
import { getPlaylog } from '@/actions/chuni/playlog'; import { getPlaylog } from '@/actions/chuni/playlog';
import { getJacketUrl } from '@/helpers/assets'; import { getJacketUrl } from '@/helpers/assets';
import Link from 'next/link'; import Link from 'next/link';
import { ChuniRating } from '@/components/chuni/rating'; import { ChuniRating } from '@/components/chuni/rating';
import { ChuniScoreBadge, ChuniLampSuccessBadge, getVariantFromRank, ChuniLampComboBadge } from '@/components/chuni/score-badge'; import { ChuniLampComboBadge, ChuniLampSuccessBadge, ChuniScoreBadge, getVariantFromRank } from '@/components/chuni/score-badge';
import { ChuniLevelBadge } from '@/components/chuni/level-badge'; import { ChuniLevelBadge } from '@/components/chuni/level-badge';
import { ChuniDifficultyContainer } from '@/components/chuni/difficulty-container'; import { ChuniDifficultyContainer } from '@/components/chuni/difficulty-container';
import { formatJst } from '@/helpers/format-jst'; import { formatJst } from '@/helpers/format-jst';
import { Ticker } from '@/components/ticker'; import { Ticker, TickerHoverProvider } from '@/components/ticker';
export type ChuniPlaylogCardProps = { export type ChuniPlaylogCardProps = {
playlog: Awaited<ReturnType<typeof getPlaylog>>['data'][number], playlog: Awaited<ReturnType<typeof getPlaylog>>['data'][number],
@ -23,48 +25,56 @@ const getChangeColor = (val: number) => {
if (val === 0) return 'text-gray-500'; if (val === 0) return 'text-gray-500';
if (val < 0) return 'text-red-500'; if (val < 0) return 'text-red-500';
return 'text-blue-500'; return 'text-blue-500';
} };
export const ChuniPlaylogCard = ({ playlog, className }: ChuniPlaylogCardProps) => { export const ChuniPlaylogCard = ({ playlog, className }: ChuniPlaylogCardProps) => {
return (<div className={`rounded-md bg-content1 relative flex flex-col p-2 pt-1 border border-black/25 ${className ?? ''}`}> return (<TickerHoverProvider>{setHover => <div onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
className={`rounded-md bg-content1 relative flex flex-col p-2 pt-1 border border-black/25 ${className ?? ''}`}>
<div className="flex"> <div className="flex">
<div className="flex-shrink-0 mr-2 mt-auto"> <div className="flex-shrink-0 mr-2 mt-auto">
<ChuniDifficultyContainer difficulty={playlog.chartId ?? 0} className="w-28 aspect-square relative p-1"> <ChuniDifficultyContainer difficulty={playlog.chartId ?? 0} className="w-28 aspect-square relative p-1">
<ChuniLevelBadge className="absolute -bottom-1.5 -right-1.5 w-12" music={playlog} /> <ChuniLevelBadge className="absolute -bottom-1.5 -right-1.5 w-12" music={playlog}/>
<img className="aspect-square w-full rounded overflow-hidden" src={getJacketUrl(`chuni/jacket/${playlog.jacketPath}`)} <img className="aspect-square w-full rounded overflow-hidden"
alt={playlog.title ?? ''} /> src={getJacketUrl(`chuni/jacket/${playlog.jacketPath}`)}
alt={playlog.title ?? ''}/>
</ChuniDifficultyContainer> </ChuniDifficultyContainer>
</div> </div>
<div className="flex flex-col leading-tight overflow-hidden text-nowrap w-full"> <div className="flex flex-col leading-tight overflow-hidden text-nowrap w-full">
<div className="text-xs text-right -mb-0.5 w-full">{ formatJst(playlog.userPlayDate!) }</div> <div className="text-xs text-right -mb-0.5 w-full">{formatJst(playlog.userPlayDate!)}</div>
<Link href={`/chuni/music/${playlog.songId}`} lang="ja" className="hover:text-secondary transition mb-2 font-semibold"> <Link href={`/chuni/music/${playlog.songId}`} lang="ja"
<Ticker hoverOnly noDelay><span className="underline">{ playlog.title }</span></Ticker> className="hover:text-secondary transition mb-1 font-semibold">
<Ticker hoverOnly noDelay><span className="underline">{playlog.title}</span></Ticker>
</Link> </Link>
<Ticker className="text-sm mb-2">{ playlog.artist }</Ticker> <Ticker hoverOnly noDelay className="text-sm mb-1">{playlog.artist}</Ticker>
<span lang="ja" className="text-sm mb-2">{ playlog.genre }</span> <span lang="ja" className="text-sm">{playlog.genre}</span>
<div className="text-sm flex items-center"> <div className="text-sm flex items-center">
Rating:&nbsp;<ChuniRating className="text-medium" rating={+playlog.rating * 100} /> Rating:&nbsp;<ChuniRating className="text-medium" rating={+playlog.rating * 100}/>
<span className={`text-xs ${getChangeColor(playlog.playerRatingChange)}`}>&nbsp;( <span className={`text-xs ${getChangeColor(playlog.playerRatingChange)}`}>&nbsp;(
{getChangeSign(playlog.playerRatingChange)} {getChangeSign(playlog.playerRatingChange)}
{(playlog.playerRatingChange / 100).toFixed(2)} {(playlog.playerRatingChange / 100).toFixed(2)}
)</span> )</span>
</div> </div>
<div className="text-xs">
Max Combo: {playlog.maxCombo?.toLocaleString()}
</div>
</div> </div>
</div> </div>
<div className="h-5 lg:h-[1.125rem] xl:h-6 2xl:h-[1.125rem] 4xl:h-6 5xl:h-[1.125rem] my-auto flex gap-0.5 overflow-hidden"> <div
className="h-5 lg:h-[1.125rem] xl:h-6 2xl:h-[1.125rem] 4xl:h-6 5xl:h-[1.125rem] my-auto flex gap-0.5 overflow-hidden">
<ChuniScoreBadge variant={getVariantFromRank(playlog.rank ?? 0)} className="h-full"> <ChuniScoreBadge variant={getVariantFromRank(playlog.rank ?? 0)} className="h-full">
{playlog.score?.toLocaleString()} {playlog.score?.toLocaleString()}
</ChuniScoreBadge> </ChuniScoreBadge>
{!!playlog.isClear && <ChuniLampSuccessBadge success={1} />} {!!playlog.isClear && <ChuniLampSuccessBadge success={1}/>}
<ChuniLampComboBadge {...playlog} /> <ChuniLampComboBadge {...playlog} />
{!!playlog.isNewRecord && <ChuniScoreBadge variant="gold" fontSize="sm">NEW RECORD</ChuniScoreBadge>} {!!playlog.isNewRecord && <ChuniScoreBadge variant="gold" fontSize="sm">NEW RECORD</ChuniScoreBadge>}
</div> </div>
<div className="flex flex-wrap text-xs justify-around drop-shadow-sm"> <div className="flex flex-wrap text-xs justify-around drop-shadow-sm">
<div className="mr-0.5 text-chuni-justice-critical">Justice Critical: { playlog.judgeHeaven }</div> <div className="mr-0.5 text-chuni-justice-critical">Justice Critical: {playlog.judgeHeaven}</div>
<div className="mr-0.5 text-chuni-justice">Justice: { playlog.judgeCritical }</div> <div className="mr-0.5 text-chuni-justice">Justice: {playlog.judgeCritical}</div>
<div className="mr-0.5 text-chuni-attack">Attack: { playlog.judgeAttack }</div> <div className="mr-0.5 text-chuni-attack">Attack: {playlog.judgeAttack}</div>
<div className="mr-0.5 text-chuni-miss">Miss: { playlog.judgeGuilty }</div> <div className="mr-0.5 text-chuni-miss">Miss: {playlog.judgeGuilty}</div>
</div> </div>
</div>); </div>}
</TickerHoverProvider>);
}; };