This commit is contained in:
Azalea 2024-02-16 01:46:11 -05:00
parent 6afcb364d1
commit 643e0e0c1f
5 changed files with 271 additions and 273 deletions

View File

@ -1,5 +1,5 @@
export interface TrendEntry { export interface TrendEntry {
date: string date: string
rating: number rating: number
plays: number plays: number
} }

View File

@ -1,51 +1,51 @@
import {aqua_host, data_host} from "./config"; import { aqua_host, data_host } from './config'
import type {TrendEntry} from "./generalTypes"; import type { TrendEntry } from './generalTypes'
import type {MaimaiUserSummaryEntry} from "./maimaiTypes"; import type { MaimaiUserSummaryEntry } from './maimaiTypes'
const multTable = [ const multTable = [
[100.5, 22.4, "SSSp"], [ 100.5, 22.4, 'SSSp' ],
[100, 21.6, "SSS"], [ 100, 21.6, 'SSS' ],
[99.5, 21.1, "SSp"], [ 99.5, 21.1, 'SSp' ],
[99, 20.8, "SS"], [ 99, 20.8, 'SS' ],
[98, 20.3, "Sp"], [ 98, 20.3, 'Sp' ],
[97, 20, "S"], [ 97, 20, 'S' ],
[94, 16.8, "AAA"], [ 94, 16.8, 'AAA' ],
[90, 15.2, "AA"], [ 90, 15.2, 'AA' ],
[80, 13.6, "A"] [ 80, 13.6, 'A' ]
] ]
export function getMult(achievement: number) { export function getMult(achievement: number) {
achievement /= 10000 achievement /= 10000
for (let i = 0; i < multTable.length; i++) { for (let i = 0; i < multTable.length; i++) {
if (achievement >= (multTable[i][0] as number)) return multTable[i] if (achievement >= (multTable[i][0] as number)) return multTable[i]
} }
return [0, 0, 0] return [ 0, 0, 0 ]
} }
export async function getMaimai(endpoint: string, params: any) { export async function getMaimai(endpoint: string, params: any) {
return await fetch(`${aqua_host}/Maimai2Servlet/${endpoint}`, { return await fetch(`${aqua_host}/Maimai2Servlet/${endpoint}`, {
method: "POST", method: 'POST',
body: JSON.stringify(params) body: JSON.stringify(params)
}).then(res => res.json()) }).then(res => res.json())
} }
export async function getMaimaiAllMusic(): Promise<{ [key: string]: any }> { export async function getMaimaiAllMusic(): Promise<{ [key: string]: any }> {
return fetch(`${data_host}/maimai/meta/00/all-music.json`).then(it => it.json()) return fetch(`${data_host}/maimai/meta/00/all-music.json`).then(it => it.json())
} }
export async function getMaimaiApi(endpoint: string, params: any) { export async function getMaimaiApi(endpoint: string, params: any) {
let url = new URL(`${aqua_host}/api/game/maimai2new/${endpoint}`) const url = new URL(`${aqua_host}/api/game/maimai2new/${endpoint}`)
Object.keys(params).forEach(key => url.searchParams.append(key, params[key])) Object.keys(params).forEach(key => url.searchParams.append(key, params[key]))
return await fetch(url).then(res => res.json()) return await fetch(url).then(res => res.json())
} }
export async function getMaimaiTrend(userId: number): Promise<TrendEntry[]> { export async function getMaimaiTrend(userId: number): Promise<TrendEntry[]> {
return await getMaimaiApi("trend", {userId}) return await getMaimaiApi('trend', { userId })
} }
export async function getMaimaiUser(userId: number): Promise<MaimaiUserSummaryEntry> { export async function getMaimaiUser(userId: number): Promise<MaimaiUserSummaryEntry> {
return await getMaimaiApi("user-summary", {userId}) return await getMaimaiApi('user-summary', { userId })
} }

View File

@ -1,116 +1,116 @@
export interface Rating { export interface Rating {
musicId: number musicId: number
level: number level: number
achievement: number achievement: number
} }
export interface ParsedRating extends Rating { export interface ParsedRating extends Rating {
music: MaimaiMusic, music: MaimaiMusic,
calc: number, calc: number,
rank: string rank: string
} }
export interface MaimaiMusic { export interface MaimaiMusic {
name: string, name: string,
composer: string, composer: string,
bpm: number, bpm: number,
ver: number, ver: number,
note: { note: {
lv: number lv: number
designer: string designer: string
lv_id: number lv_id: number
notes: number notes: number
} }
} }
export interface MaimaiUserSummaryEntry { export interface MaimaiUserSummaryEntry {
name: string name: string
iconId: number iconId: number
serverRank: number serverRank: number
accuracy: number accuracy: number
rating: number rating: number
ratingHighest: number ratingHighest: number
ranks: { name: string, count: number }[] ranks: { name: string, count: number }[]
maxCombo: number maxCombo: number
fullCombo: number fullCombo: number
allPerfect: number allPerfect: number
totalDxScore: number totalDxScore: number
plays: number plays: number
totalPlayTime: number totalPlayTime: number
joined: string joined: string
lastSeen: string lastSeen: string
lastVersion: string lastVersion: string
best35: string best35: string
best15: string best15: string
recent: MaimaiUserPlaylog[] recent: MaimaiUserPlaylog[]
} }
export interface MaimaiUserPlaylog { export interface MaimaiUserPlaylog {
id: number; id: number;
musicId: number; musicId: number;
level: number; level: number;
userPlayDate: string; userPlayDate: string;
trackNo: number; trackNo: number;
vsRank: number; vsRank: number;
achievement: number; achievement: number;
deluxscore: number; deluxscore: number;
scoreRank: number; scoreRank: number;
maxCombo: number; maxCombo: number;
totalCombo: number; totalCombo: number;
maxSync: number; maxSync: number;
totalSync: number; totalSync: number;
tapCriticalPerfect: number; tapCriticalPerfect: number;
tapPerfect: number; tapPerfect: number;
tapGreat: number; tapGreat: number;
tapGood: number; tapGood: number;
tapMiss: number; tapMiss: number;
holdCriticalPerfect: number; holdCriticalPerfect: number;
holdPerfect: number; holdPerfect: number;
holdGreat: number; holdGreat: number;
holdGood: number; holdGood: number;
holdMiss: number; holdMiss: number;
slideCriticalPerfect: number; slideCriticalPerfect: number;
slidePerfect: number; slidePerfect: number;
slideGreat: number; slideGreat: number;
slideGood: number; slideGood: number;
slideMiss: number; slideMiss: number;
touchCriticalPerfect: number; touchCriticalPerfect: number;
touchPerfect: number; touchPerfect: number;
touchGreat: number; touchGreat: number;
touchGood: number; touchGood: number;
touchMiss: number; touchMiss: number;
breakCriticalPerfect: number; breakCriticalPerfect: number;
breakPerfect: number; breakPerfect: number;
breakGreat: number; breakGreat: number;
breakGood: number; breakGood: number;
breakMiss: number; breakMiss: number;
isTap: boolean; isTap: boolean;
isHold: boolean; isHold: boolean;
isSlide: boolean; isSlide: boolean;
isTouch: boolean; isTouch: boolean;
isBreak: boolean; isBreak: boolean;
isCriticalDisp: boolean; isCriticalDisp: boolean;
isFastLateDisp: boolean; isFastLateDisp: boolean;
fastCount: number; fastCount: number;
lateCount: number; lateCount: number;
isAchieveNewRecord: boolean; isAchieveNewRecord: boolean;
isDeluxscoreNewRecord: boolean; isDeluxscoreNewRecord: boolean;
comboStatus: number; comboStatus: number;
syncStatus: number; syncStatus: number;
isClear: boolean; isClear: boolean;
beforeRating: number; beforeRating: number;
afterRating: number; afterRating: number;
beforeGrade: number; beforeGrade: number;
afterGrade: number; afterGrade: number;
afterGradeRank: number; afterGradeRank: number;
beforeDeluxRating: number; beforeDeluxRating: number;
afterDeluxRating: number; afterDeluxRating: number;
isPlayTutorial: boolean; isPlayTutorial: boolean;
isEventMode: boolean; isEventMode: boolean;
isFreedomMode: boolean; isFreedomMode: boolean;
playMode: number; playMode: number;
isNewFree: boolean; isNewFree: boolean;
trialPlayAchievement: number; trialPlayAchievement: number;
extNum1: number; extNum1: number;
extNum2: number; extNum2: number;
} }

View File

@ -1,101 +1,100 @@
import { import {
Chart as ChartJS, Chart as ChartJS,
Title, Title,
Tooltip, Tooltip,
Legend, Legend,
LineElement, LineElement,
LinearScale, LinearScale,
PointElement, PointElement,
CategoryScale, TimeScale, type ChartOptions, type LineOptions, CategoryScale, TimeScale, type ChartOptions, type LineOptions,
} from 'chart.js'; } from 'chart.js'
import moment from "moment/moment"; import moment from 'moment/moment'
// @ts-ignore // @ts-expect-error Cal-heatmap does not have proper types
import CalHeatmap from "cal-heatmap"; import CalHeatmap from 'cal-heatmap'
// @ts-ignore // @ts-expect-error Cal-heatmap does not have proper types
import CalTooltip from 'cal-heatmap/plugins/Tooltip'; import CalTooltip from 'cal-heatmap/plugins/Tooltip'
import type {Line} from "svelte-chartjs";
export function title(t: string) {
export function title(t: string) { document.title = `AquaNet - ${t}`
document.title = `AquaNet - ${t}` }
}
export function registerChart() {
export function registerChart() { ChartJS.register(
ChartJS.register( Title,
Title, Tooltip,
Tooltip, Legend,
Legend, LineElement,
LineElement, LinearScale,
LinearScale, PointElement,
PointElement, CategoryScale,
CategoryScale, TimeScale
TimeScale )
); }
}
export function renderCal(el: HTMLElement, d: {date: any, value: any}[]) {
export function renderCal(el: HTMLElement, d: {date: any, value: any}[]) { const cal = new CalHeatmap()
const cal = new CalHeatmap(); return cal.paint({
return cal.paint({ itemSelector: el,
itemSelector: el, domain: {
domain: { type: 'month',
type: 'month', label: { text: 'MMM', textAlign: 'start', position: 'top' },
label: { text: 'MMM', textAlign: 'start', position: 'top' }, },
}, subDomain: {
subDomain: { type: 'ghDay',
type: 'ghDay', radius: 2, width: 11, height: 11, gutter: 4
radius: 2, width: 11, height: 11, gutter: 4 },
}, range: 12,
range: 12, data: { source: d, x: 'date', y: 'value' },
data: {source: d, x: 'date', y: 'value'}, scale: {
scale: { color: {
color: { type: 'linear',
type: 'linear', range: [ '#14432a', '#4dd05a' ],
range: ['#14432a', '#4dd05a'], domain: [ 0, d.reduce((a, b) => Math.max(a, b.value), 0) ]
domain: [0, d.reduce((a, b) => Math.max(a, b.value), 0)] },
}, },
}, date: { start: moment().subtract(1, 'year').add(1, 'month').toDate() },
date: {start: moment().subtract(1, 'year').add(1, 'month').toDate()}, theme: 'dark',
theme: "dark", }, [
}, [ [ CalTooltip, { text: (_: Date, v: number, d: any) =>
[CalTooltip, {text: (_: Date, v: number, d: any) => `${v ?? 'No'} songs played on ${d.format('MMMM D, YYYY')}` }]
`${v ?? "No"} songs played on ${d.format('MMMM D, YYYY')}`}] ])
]); }
}
export const CHARTJS_OPT: ChartOptions<'line'> = {
export const CHARTJS_OPT: ChartOptions<"line"> = { responsive: true,
responsive: true, maintainAspectRatio: false,
maintainAspectRatio: false, // TODO: Show point on hover
// TODO: Show point on hover elements: {
elements: { point: {
point: { radius: 0
radius: 0 }
} },
}, scales: {
scales: { xAxis: {
xAxis: { type: 'time',
type: 'time', display: false
display: false },
}, y: {
y: { display: false,
display: false, }
} },
}, plugins: {
plugins: { legend: {
legend: { display: false
display: false },
}, tooltip: {
tooltip: { mode: 'index',
mode: "index", intersect: false
intersect: false }
} },
}, }
}
/**
/** * Usage: clazz({a: false, b: true}) -> "b"
* Usage: clazz({a: false, b: true}) -> "b" *
* * @param obj HashMap<string, boolean>
* @param obj HashMap<string, boolean> */
*/ export function clazz(obj: { [key: string]: boolean }) {
export function clazz(obj: { [key: string]: boolean }) { return Object.keys(obj).filter(k => obj[k]).join(' ')
return Object.keys(obj).filter(k => obj[k]).join(" ") }
}

View File

@ -1,7 +1,6 @@
import './app.sass' import './app.sass'
import App from './App.svelte' 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 export default app