diff --git a/AquaNet/src/libs/generalTypes.ts b/AquaNet/src/libs/generalTypes.ts index 5a888975..26fe3f64 100644 --- a/AquaNet/src/libs/generalTypes.ts +++ b/AquaNet/src/libs/generalTypes.ts @@ -1,5 +1,5 @@ -export interface TrendEntry { - date: string - rating: number - plays: number +export interface TrendEntry { + date: string + rating: number + plays: number } \ No newline at end of file diff --git a/AquaNet/src/libs/maimai.ts b/AquaNet/src/libs/maimai.ts index 50564f37..0db3e463 100644 --- a/AquaNet/src/libs/maimai.ts +++ b/AquaNet/src/libs/maimai.ts @@ -1,51 +1,51 @@ -import {aqua_host, data_host} from "./config"; -import type {TrendEntry} from "./generalTypes"; -import type {MaimaiUserSummaryEntry} from "./maimaiTypes"; - - -const multTable = [ - [100.5, 22.4, "SSSp"], - [100, 21.6, "SSS"], - [99.5, 21.1, "SSp"], - [99, 20.8, "SS"], - [98, 20.3, "Sp"], - [97, 20, "S"], - [94, 16.8, "AAA"], - [90, 15.2, "AA"], - [80, 13.6, "A"] -] - - -export function getMult(achievement: number) { - achievement /= 10000 - for (let i = 0; i < multTable.length; i++) { - if (achievement >= (multTable[i][0] as number)) return multTable[i] - } - return [0, 0, 0] -} - - -export async function getMaimai(endpoint: string, params: any) { - return await fetch(`${aqua_host}/Maimai2Servlet/${endpoint}`, { - method: "POST", - body: JSON.stringify(params) - }).then(res => res.json()) -} - -export async function getMaimaiAllMusic(): Promise<{ [key: string]: any }> { - return fetch(`${data_host}/maimai/meta/00/all-music.json`).then(it => it.json()) -} - -export async function getMaimaiApi(endpoint: string, params: any) { - let url = new URL(`${aqua_host}/api/game/maimai2new/${endpoint}`) - Object.keys(params).forEach(key => url.searchParams.append(key, params[key])) - return await fetch(url).then(res => res.json()) -} - -export async function getMaimaiTrend(userId: number): Promise { - return await getMaimaiApi("trend", {userId}) -} - -export async function getMaimaiUser(userId: number): Promise { - return await getMaimaiApi("user-summary", {userId}) +import { aqua_host, data_host } from './config' +import type { TrendEntry } from './generalTypes' +import type { MaimaiUserSummaryEntry } from './maimaiTypes' + + +const multTable = [ + [ 100.5, 22.4, 'SSSp' ], + [ 100, 21.6, 'SSS' ], + [ 99.5, 21.1, 'SSp' ], + [ 99, 20.8, 'SS' ], + [ 98, 20.3, 'Sp' ], + [ 97, 20, 'S' ], + [ 94, 16.8, 'AAA' ], + [ 90, 15.2, 'AA' ], + [ 80, 13.6, 'A' ] +] + + +export function getMult(achievement: number) { + achievement /= 10000 + for (let i = 0; i < multTable.length; i++) { + if (achievement >= (multTable[i][0] as number)) return multTable[i] + } + return [ 0, 0, 0 ] +} + + +export async function getMaimai(endpoint: string, params: any) { + return await fetch(`${aqua_host}/Maimai2Servlet/${endpoint}`, { + method: 'POST', + body: JSON.stringify(params) + }).then(res => res.json()) +} + +export async function getMaimaiAllMusic(): Promise<{ [key: string]: any }> { + return fetch(`${data_host}/maimai/meta/00/all-music.json`).then(it => it.json()) +} + +export async function getMaimaiApi(endpoint: string, params: any) { + const url = new URL(`${aqua_host}/api/game/maimai2new/${endpoint}`) + Object.keys(params).forEach(key => url.searchParams.append(key, params[key])) + return await fetch(url).then(res => res.json()) +} + +export async function getMaimaiTrend(userId: number): Promise { + return await getMaimaiApi('trend', { userId }) +} + +export async function getMaimaiUser(userId: number): Promise { + return await getMaimaiApi('user-summary', { userId }) } \ No newline at end of file diff --git a/AquaNet/src/libs/maimaiTypes.ts b/AquaNet/src/libs/maimaiTypes.ts index 6906f88b..61e3539d 100644 --- a/AquaNet/src/libs/maimaiTypes.ts +++ b/AquaNet/src/libs/maimaiTypes.ts @@ -1,116 +1,116 @@ -export interface Rating { - musicId: number - level: number - achievement: number -} - -export interface ParsedRating extends Rating { - music: MaimaiMusic, - calc: number, - rank: string -} - -export interface MaimaiMusic { - name: string, - composer: string, - bpm: number, - ver: number, - note: { - lv: number - designer: string - lv_id: number - notes: number - } -} - -export interface MaimaiUserSummaryEntry { - name: string - iconId: number - serverRank: number - accuracy: number - rating: number - ratingHighest: number - ranks: { name: string, count: number }[] - maxCombo: number - fullCombo: number - allPerfect: number - totalDxScore: number - plays: number - totalPlayTime: number - joined: string - lastSeen: string - lastVersion: string - best35: string - best15: string - recent: MaimaiUserPlaylog[] -} - -export interface MaimaiUserPlaylog { - id: number; - musicId: number; - level: number; - userPlayDate: string; - trackNo: number; - vsRank: number; - achievement: number; - deluxscore: number; - scoreRank: number; - maxCombo: number; - totalCombo: number; - maxSync: number; - totalSync: number; - tapCriticalPerfect: number; - tapPerfect: number; - tapGreat: number; - tapGood: number; - tapMiss: number; - holdCriticalPerfect: number; - holdPerfect: number; - holdGreat: number; - holdGood: number; - holdMiss: number; - slideCriticalPerfect: number; - slidePerfect: number; - slideGreat: number; - slideGood: number; - slideMiss: number; - touchCriticalPerfect: number; - touchPerfect: number; - touchGreat: number; - touchGood: number; - touchMiss: number; - breakCriticalPerfect: number; - breakPerfect: number; - breakGreat: number; - breakGood: number; - breakMiss: number; - isTap: boolean; - isHold: boolean; - isSlide: boolean; - isTouch: boolean; - isBreak: boolean; - isCriticalDisp: boolean; - isFastLateDisp: boolean; - fastCount: number; - lateCount: number; - isAchieveNewRecord: boolean; - isDeluxscoreNewRecord: boolean; - comboStatus: number; - syncStatus: number; - isClear: boolean; - beforeRating: number; - afterRating: number; - beforeGrade: number; - afterGrade: number; - afterGradeRank: number; - beforeDeluxRating: number; - afterDeluxRating: number; - isPlayTutorial: boolean; - isEventMode: boolean; - isFreedomMode: boolean; - playMode: number; - isNewFree: boolean; - trialPlayAchievement: number; - extNum1: number; - extNum2: number; -} +export interface Rating { + musicId: number + level: number + achievement: number +} + +export interface ParsedRating extends Rating { + music: MaimaiMusic, + calc: number, + rank: string +} + +export interface MaimaiMusic { + name: string, + composer: string, + bpm: number, + ver: number, + note: { + lv: number + designer: string + lv_id: number + notes: number + } +} + +export interface MaimaiUserSummaryEntry { + name: string + iconId: number + serverRank: number + accuracy: number + rating: number + ratingHighest: number + ranks: { name: string, count: number }[] + maxCombo: number + fullCombo: number + allPerfect: number + totalDxScore: number + plays: number + totalPlayTime: number + joined: string + lastSeen: string + lastVersion: string + best35: string + best15: string + recent: MaimaiUserPlaylog[] +} + +export interface MaimaiUserPlaylog { + id: number; + musicId: number; + level: number; + userPlayDate: string; + trackNo: number; + vsRank: number; + achievement: number; + deluxscore: number; + scoreRank: number; + maxCombo: number; + totalCombo: number; + maxSync: number; + totalSync: number; + tapCriticalPerfect: number; + tapPerfect: number; + tapGreat: number; + tapGood: number; + tapMiss: number; + holdCriticalPerfect: number; + holdPerfect: number; + holdGreat: number; + holdGood: number; + holdMiss: number; + slideCriticalPerfect: number; + slidePerfect: number; + slideGreat: number; + slideGood: number; + slideMiss: number; + touchCriticalPerfect: number; + touchPerfect: number; + touchGreat: number; + touchGood: number; + touchMiss: number; + breakCriticalPerfect: number; + breakPerfect: number; + breakGreat: number; + breakGood: number; + breakMiss: number; + isTap: boolean; + isHold: boolean; + isSlide: boolean; + isTouch: boolean; + isBreak: boolean; + isCriticalDisp: boolean; + isFastLateDisp: boolean; + fastCount: number; + lateCount: number; + isAchieveNewRecord: boolean; + isDeluxscoreNewRecord: boolean; + comboStatus: number; + syncStatus: number; + isClear: boolean; + beforeRating: number; + afterRating: number; + beforeGrade: number; + afterGrade: number; + afterGradeRank: number; + beforeDeluxRating: number; + afterDeluxRating: number; + isPlayTutorial: boolean; + isEventMode: boolean; + isFreedomMode: boolean; + playMode: number; + isNewFree: boolean; + trialPlayAchievement: number; + extNum1: number; + extNum2: number; +} diff --git a/AquaNet/src/libs/ui.ts b/AquaNet/src/libs/ui.ts index 20850e7a..d59382b2 100644 --- a/AquaNet/src/libs/ui.ts +++ b/AquaNet/src/libs/ui.ts @@ -1,101 +1,100 @@ -import { - Chart as ChartJS, - Title, - Tooltip, - Legend, - LineElement, - LinearScale, - PointElement, - CategoryScale, TimeScale, type ChartOptions, type LineOptions, -} from 'chart.js'; -import moment from "moment/moment"; -// @ts-ignore -import CalHeatmap from "cal-heatmap"; -// @ts-ignore -import CalTooltip from 'cal-heatmap/plugins/Tooltip'; -import type {Line} from "svelte-chartjs"; - -export function title(t: string) { - document.title = `AquaNet - ${t}` -} - -export function registerChart() { - ChartJS.register( - Title, - Tooltip, - Legend, - LineElement, - LinearScale, - PointElement, - CategoryScale, - TimeScale - ); -} - -export function renderCal(el: HTMLElement, d: {date: any, value: any}[]) { - const cal = new CalHeatmap(); - return cal.paint({ - itemSelector: el, - domain: { - type: 'month', - label: { text: 'MMM', textAlign: 'start', position: 'top' }, - }, - subDomain: { - type: 'ghDay', - radius: 2, width: 11, height: 11, gutter: 4 - }, - range: 12, - data: {source: d, x: 'date', y: 'value'}, - scale: { - color: { - type: 'linear', - range: ['#14432a', '#4dd05a'], - domain: [0, d.reduce((a, b) => Math.max(a, b.value), 0)] - }, - }, - date: {start: moment().subtract(1, 'year').add(1, 'month').toDate()}, - theme: "dark", - }, [ - [CalTooltip, {text: (_: Date, v: number, d: any) => - `${v ?? "No"} songs played on ${d.format('MMMM D, YYYY')}`}] - ]); -} - - -export const CHARTJS_OPT: ChartOptions<"line"> = { - responsive: true, - maintainAspectRatio: false, - // TODO: Show point on hover - elements: { - point: { - radius: 0 - } - }, - scales: { - xAxis: { - type: 'time', - display: false - }, - y: { - display: false, - } - }, - plugins: { - legend: { - display: false - }, - tooltip: { - mode: "index", - intersect: false - } - }, -} - -/** - * Usage: clazz({a: false, b: true}) -> "b" - * - * @param obj HashMap - */ -export function clazz(obj: { [key: string]: boolean }) { - return Object.keys(obj).filter(k => obj[k]).join(" ") -} +import { + Chart as ChartJS, + Title, + Tooltip, + Legend, + LineElement, + LinearScale, + PointElement, + CategoryScale, TimeScale, type ChartOptions, type LineOptions, +} from 'chart.js' +import moment from 'moment/moment' +// @ts-expect-error Cal-heatmap does not have proper types +import CalHeatmap from 'cal-heatmap' +// @ts-expect-error Cal-heatmap does not have proper types +import CalTooltip from 'cal-heatmap/plugins/Tooltip' + +export function title(t: string) { + document.title = `AquaNet - ${t}` +} + +export function registerChart() { + ChartJS.register( + Title, + Tooltip, + Legend, + LineElement, + LinearScale, + PointElement, + CategoryScale, + TimeScale + ) +} + +export function renderCal(el: HTMLElement, d: {date: any, value: any}[]) { + const cal = new CalHeatmap() + return cal.paint({ + itemSelector: el, + domain: { + type: 'month', + label: { text: 'MMM', textAlign: 'start', position: 'top' }, + }, + subDomain: { + type: 'ghDay', + radius: 2, width: 11, height: 11, gutter: 4 + }, + range: 12, + data: { source: d, x: 'date', y: 'value' }, + scale: { + color: { + type: 'linear', + range: [ '#14432a', '#4dd05a' ], + domain: [ 0, d.reduce((a, b) => Math.max(a, b.value), 0) ] + }, + }, + date: { start: moment().subtract(1, 'year').add(1, 'month').toDate() }, + theme: 'dark', + }, [ + [ CalTooltip, { text: (_: Date, v: number, d: any) => + `${v ?? 'No'} songs played on ${d.format('MMMM D, YYYY')}` }] + ]) +} + + +export const CHARTJS_OPT: ChartOptions<'line'> = { + responsive: true, + maintainAspectRatio: false, + // TODO: Show point on hover + elements: { + point: { + radius: 0 + } + }, + scales: { + xAxis: { + type: 'time', + display: false + }, + y: { + display: false, + } + }, + plugins: { + legend: { + display: false + }, + tooltip: { + mode: 'index', + intersect: false + } + }, +} + +/** + * Usage: clazz({a: false, b: true}) -> "b" + * + * @param obj HashMap + */ +export function clazz(obj: { [key: string]: boolean }) { + return Object.keys(obj).filter(k => obj[k]).join(' ') +} diff --git a/AquaNet/src/main.ts b/AquaNet/src/main.ts index 41709477..255357a4 100644 --- a/AquaNet/src/main.ts +++ b/AquaNet/src/main.ts @@ -1,7 +1,6 @@ import './app.sass' import App from './App.svelte' -// @ts-ignore -const app = new App({target: document.getElementById('app')}) +const app = new App({ target: document.getElementById('app')! }) export default app \ No newline at end of file