diff --git a/.gitignore b/.gitignore
index 90f4ecf8..26fd3569 100644
--- a/.gitignore
+++ b/.gitignore
@@ -75,4 +75,7 @@ gradle-app.setting
### Gradle Patch ###
# Java heap dump
-*.hprof
\ No newline at end of file
+*.hprof
+
+### Docker ###
+/db/*
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 00000000..532651af
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,8 @@
+{
+ "trailingComma": "es5",
+ "semi": false,
+ "singleQuote": true,
+ "bracketSpacing": false,
+ "plugins": ["prettier-plugin-svelte"],
+ "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
+}
diff --git a/AquaMai/AquaMai.toml b/AquaMai/AquaMai.toml
index bdbe445e..24f0988a 100644
--- a/AquaMai/AquaMai.toml
+++ b/AquaMai/AquaMai.toml
@@ -11,6 +11,6 @@ TicketUnlock=true
# Skip the warning screen and logo shown after the POST sequence
SkipWarningScreen=true
# Single player: Show 1P only, at the center of the screen
-SinglePlayer=true
+SinglePlayer=false
# !!EXPERIMENTAL!! Skip from the card-scanning screen directly to music selection screen
-SkipToMusicSelection=false
\ No newline at end of file
+SkipToMusicSelection=true
\ No newline at end of file
diff --git a/AquaNet/README.md b/AquaNet/README.md
index d079bd1e..03ca07da 100644
--- a/AquaNet/README.md
+++ b/AquaNet/README.md
@@ -1,6 +1,6 @@
# AquaNet
-This is the codebase for the new frontend of AquaDX.
+This is the codebase for the new frontend of AquaDX.
This project is also heavily WIP, so more details will be added later on.
## Development
@@ -19,4 +19,3 @@ Finally, run:
yarn install
yarn dev
```
-
diff --git a/AquaNet/SVELTE.md b/AquaNet/SVELTE.md
index 94e85ede..74c5dfcf 100644
--- a/AquaNet/SVELTE.md
+++ b/AquaNet/SVELTE.md
@@ -1,35 +1,35 @@
-## Technical considerations
-
-**Why use this over SvelteKit?**
-
-- It brings its own routing solution which might not be preferable for some users.
-- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app.
-
-This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project.
-
-Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate.
-
-**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?**
-
-Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information.
-
-**Why include `.vscode/extensions.json`?**
-
-Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project.
-
-**Why enable `allowJs` in the TS template?**
-
-While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not prevent the use of JavaScript syntax in `.svelte` files. In addition, it would force `checkJs: false`, bringing the worst of both worlds: not being able to guarantee the entire codebase is TypeScript, and also having worse typechecking for the existing JavaScript. In addition, there are valid use cases in which a mixed codebase may be relevant.
-
-**Why is HMR not preserving my local component state?**
-
-HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr).
-
-If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR.
-
-```ts
-// store.ts
-// An extremely simple external store
-import { writable } from 'svelte/store'
-export default writable(0)
-```
\ No newline at end of file
+## Technical considerations
+
+**Why use this over SvelteKit?**
+
+- It brings its own routing solution which might not be preferable for some users.
+- It is first and foremost a framework that just happens to use Vite under the hood, not a Vite app.
+
+This template contains as little as possible to get started with Vite + TypeScript + Svelte, while taking into account the developer experience with regards to HMR and intellisense. It demonstrates capabilities on par with the other `create-vite` templates and is a good starting point for beginners dipping their toes into a Vite + Svelte project.
+
+Should you later need the extended capabilities and extensibility provided by SvelteKit, the template has been structured similarly to SvelteKit so that it is easy to migrate.
+
+**Why `global.d.ts` instead of `compilerOptions.types` inside `jsconfig.json` or `tsconfig.json`?**
+
+Setting `compilerOptions.types` shuts out all other types not explicitly listed in the configuration. Using triple-slash references keeps the default TypeScript setting of accepting type information from the entire workspace, while also adding `svelte` and `vite/client` type information.
+
+**Why include `.vscode/extensions.json`?**
+
+Other templates indirectly recommend extensions via the README, but this file allows VS Code to prompt the user to install the recommended extension upon opening the project.
+
+**Why enable `allowJs` in the TS template?**
+
+While `allowJs: false` would indeed prevent the use of `.js` files in the project, it does not prevent the use of JavaScript syntax in `.svelte` files. In addition, it would force `checkJs: false`, bringing the worst of both worlds: not being able to guarantee the entire codebase is TypeScript, and also having worse typechecking for the existing JavaScript. In addition, there are valid use cases in which a mixed codebase may be relevant.
+
+**Why is HMR not preserving my local component state?**
+
+HMR state preservation comes with a number of gotchas! It has been disabled by default in both `svelte-hmr` and `@sveltejs/vite-plugin-svelte` due to its often surprising behavior. You can read the details [here](https://github.com/rixo/svelte-hmr#svelte-hmr).
+
+If you have state that's important to retain within a component, consider creating an external store which would not be replaced by HMR.
+
+```ts
+// store.ts
+// An extremely simple external store
+import {writable} from 'svelte/store'
+export default writable(0)
+```
diff --git a/AquaNet/index.html b/AquaNet/index.html
index 8788bb8e..4a515450 100644
--- a/AquaNet/index.html
+++ b/AquaNet/index.html
@@ -6,15 +6,36 @@
AquaNet
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/AquaNet/package.json b/AquaNet/package.json
index 73b63bf5..aebdab09 100644
--- a/AquaNet/package.json
+++ b/AquaNet/package.json
@@ -7,13 +7,16 @@
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
- "check": "svelte-check --tsconfig ./tsconfig.json"
+ "check": "svelte-check --tsconfig ./tsconfig.json",
+ "format": "prettier --write ."
},
"devDependencies": {
"@iconify/svelte": "^3.1.6",
"@sveltejs/vite-plugin-svelte": "^3.0.1",
"@tsconfig/svelte": "^5.0.2",
"chartjs-adapter-moment": "^1.0.1",
+ "prettier": "^3.2.5",
+ "prettier-plugin-svelte": "^3.1.2",
"sass": "^1.70.0",
"svelte": "^4.2.10",
"svelte-check": "^3.6.4",
diff --git a/AquaNet/public/assets/icons/site.webmanifest b/AquaNet/public/assets/icons/site.webmanifest
index 85e0b459..5b1e299c 100644
--- a/AquaNet/public/assets/icons/site.webmanifest
+++ b/AquaNet/public/assets/icons/site.webmanifest
@@ -1,19 +1,19 @@
{
- "name": "",
- "short_name": "",
- "icons": [
- {
- "src": "/assets/icons/android-chrome-192x192.png",
- "sizes": "192x192",
- "type": "image/png"
- },
- {
- "src": "/assets/icons/android-chrome-512x512.png",
- "sizes": "512x512",
- "type": "image/png"
- }
- ],
- "theme_color": "#ffffff",
- "background_color": "#ffffff",
- "display": "standalone"
+ "name": "",
+ "short_name": "",
+ "icons": [
+ {
+ "src": "/assets/icons/android-chrome-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "/assets/icons/android-chrome-512x512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ],
+ "theme_color": "#ffffff",
+ "background_color": "#ffffff",
+ "display": "standalone"
}
diff --git a/AquaNet/src/App.svelte b/AquaNet/src/App.svelte
index 37e67b4a..1e5e756b 100644
--- a/AquaNet/src/App.svelte
+++ b/AquaNet/src/App.svelte
@@ -1,11 +1,11 @@
@@ -53,4 +53,4 @@
@media (max-width: $w-mobile)
justify-content: center
-
\ No newline at end of file
+
diff --git a/AquaNet/src/libs/generalTypes.ts b/AquaNet/src/libs/generalTypes.ts
index 5a888975..492e9849 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
-}
\ No newline at end of file
+export interface TrendEntry {
+ date: string
+ rating: number
+ plays: number
+}
diff --git a/AquaNet/src/libs/maimai.ts b/AquaNet/src/libs/maimai.ts
index 50564f37..b5c7aa7b 100644
--- a/AquaNet/src/libs/maimai.ts
+++ b/AquaNet/src/libs/maimai.ts
@@ -1,51 +1,54 @@
-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})
-}
\ No newline at end of file
+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})
+}
diff --git a/AquaNet/src/libs/maimaiTypes.ts b/AquaNet/src/libs/maimaiTypes.ts
index 39e67f1a..1995b750 100644
--- a/AquaNet/src/libs/maimaiTypes.ts
+++ b/AquaNet/src/libs/maimaiTypes.ts
@@ -1,115 +1,115 @@
-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;
- 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
+ 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..ca3ab678 100644
--- a/AquaNet/src/libs/ui.ts
+++ b/AquaNet/src/libs/ui.ts
@@ -1,101 +1,116 @@
-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-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(' ')
+}
diff --git a/AquaNet/src/main.ts b/AquaNet/src/main.ts
index 41709477..2aab6f79 100644
--- a/AquaNet/src/main.ts
+++ b/AquaNet/src/main.ts
@@ -4,4 +4,4 @@ import App from './App.svelte'
// @ts-ignore
const app = new App({target: document.getElementById('app')})
-export default app
\ No newline at end of file
+export default app
diff --git a/AquaNet/src/pages/Home.svelte b/AquaNet/src/pages/Home.svelte
index 90af2053..8a063d03 100644
--- a/AquaNet/src/pages/Home.svelte
+++ b/AquaNet/src/pages/Home.svelte
@@ -1,83 +1,83 @@
-
- AquaNet
-
- Login
- Sign Up
-
-
-
-
-
-
\ No newline at end of file
+
+ AquaNet
+
+ Login
+ Sign Up
+
+
+
+
+
+
diff --git a/AquaNet/src/pages/MaimaiRating.svelte b/AquaNet/src/pages/MaimaiRating.svelte
index 9de77d91..aa3b6438 100644
--- a/AquaNet/src/pages/MaimaiRating.svelte
+++ b/AquaNet/src/pages/MaimaiRating.svelte
@@ -1,199 +1,219 @@
-
-
-
-
- {#if parsedRatings}
- {#each [{title: "Old", data: parsedRatings.old}, {title: "New", data: parsedRatings.new}] as section}
- {section.title}
-
- {#each section.data as rating}
-
-
-
-
-
{rating.music.name}
-
- {(rating.achievement / 10000).toFixed(2)}%
-
-
-
{rating.calc.toFixed(1)}
-
-
-
{rating.music.note.lv}
-
- {/each}
-
- {/each}
- {/if}
-
-
-
\ No newline at end of file
+
+
+
+
+ {#if parsedRatings}
+ {#each [{title: 'Old', data: parsedRatings.old}, {title: 'New', data: parsedRatings.new}] as section}
+ {section.title}
+
+ {#each section.data as rating}
+
+
+
+
+
{rating.music.name}
+
+ {(rating.achievement / 10000).toFixed(2)}%
+
+
+
{rating.calc.toFixed(1)}
+
+
+
{rating.music.note.lv}
+
+ {/each}
+
+ {/each}
+ {/if}
+
+
+
diff --git a/AquaNet/src/pages/UserHome.svelte b/AquaNet/src/pages/UserHome.svelte
index 7f996efc..21aac3cc 100644
--- a/AquaNet/src/pages/UserHome.svelte
+++ b/AquaNet/src/pages/UserHome.svelte
@@ -1,385 +1,426 @@
-
-
-
- {#if d !== null}
-
-
-
{d.user.name}
-
-
-
-
Rating Statistics
-
-
-
-
- DX Rating
- {d.user.rating.toLocaleString()}
-
-
-
- Server Rank
- #{d.user.serverRank.toLocaleString()}
-
-
-
-
-
-
- {return {x: Date.parse(it.date), y: it.rating}}),
- borderColor: '#646cff',
- tension: 0.1,
-
- // TODO: Set X axis span to 3 months
- }
- ]
- }} options={CHARTJS_OPT} />
-
-
-
-
- {#each d.user.ranks as r}
-
- {r.name}
- {r.count}
-
- {/each}
-
-
-
-
-
- Accuracy
- {(d.user.accuracy / 10000).toFixed(2)}%
-
-
-
- Max Combo
- {d.user.maxCombo}
-
-
-
- Full Combo
- {d.user.fullCombo}
-
-
-
- All Perfect
- {d.user.allPerfect}
-
-
-
- DX Score
- {d.user.totalDxScore.toLocaleString()}
-
-
-
-
-
-
-
Play Activity
-
-
-
-
-
- Plays
- {d.user.plays}
-
-
-
- Play Time
- {(d.user.totalPlayTime / 60 / 60).toFixed(1)} hr
-
-
-
- First Seen
- {moment(d.user.joined).format("YYYY-MM-DD")}
-
-
-
- Last Seen
- {moment(d.user.lastSeen).format("YYYY-MM-DD")}
-
-
-
- Last Version
- {d.user.lastVersion}
-
-
-
-
-
-
-
Recent Scores
-
- {#each d.recent as r, i}
-
-
-
-
{r.name}
-
-
- {("" + getMult(r.achievement)[2]).replace("p", "+")}
- {(r.achievement / 10000).toFixed(2)}%
-
- 0})}>
- {r.afterDeluxRating - r.beforeDeluxRating}
-
-
-
-
- {/each}
-
-
- {:else}
- Loading...
- {/if}
-
-
-
\ No newline at end of file
+
+
+
+ {#if d !== null}
+
+
+
{d.user.name}
+
+
+
+
Rating Statistics
+
+
+
+
+ DX Rating
+ {d.user.rating.toLocaleString()}
+
+
+
+ Server Rank
+ #{d.user.serverRank.toLocaleString()}
+
+
+
+
+
+
+ {
+ return {x: Date.parse(it.date), y: it.rating}
+ }),
+ borderColor: '#646cff',
+ tension: 0.1,
+
+ // TODO: Set X axis span to 3 months
+ },
+ ],
+ }}
+ options={CHARTJS_OPT}
+ />
+
+
+
+
+ {#each d.user.ranks as r}
+
+ {r.name}
+ {r.count}
+
+ {/each}
+
+
+
+
+
+ Accuracy
+ {(d.user.accuracy / 10000).toFixed(2)}%
+
+
+
+ Max Combo
+ {d.user.maxCombo}
+
+
+
+ Full Combo
+ {d.user.fullCombo}
+
+
+
+ All Perfect
+ {d.user.allPerfect}
+
+
+
+ DX Score
+ {d.user.totalDxScore.toLocaleString()}
+
+
+
+
+
+
+
Play Activity
+
+
+
+
+
+ Plays
+ {d.user.plays}
+
+
+
+ Play Time
+ {(d.user.totalPlayTime / 60 / 60).toFixed(1)} hr
+
+
+
+ First Seen
+ {moment(d.user.joined).format('YYYY-MM-DD')}
+
+
+
+ Last Seen
+ {moment(d.user.lastSeen).format('YYYY-MM-DD')}
+
+
+
+ Last Version
+ {d.user.lastVersion}
+
+
+
+
+
+
+
Recent Scores
+
+ {#each d.recent as r, i}
+
+
+
+
{r.name}
+
+
+ {('' + getMult(r.achievement)[2]).replace('p', '+')}
+ {(r.achievement / 10000).toFixed(2)}%
+
+ 0,
+ })}
+ >
+ {r.afterDeluxRating - r.beforeDeluxRating}
+
+
+
+
+ {/each}
+
+
+ {:else}
+ Loading...
+ {/if}
+
+
+
diff --git a/AquaNet/svelte.config.js b/AquaNet/svelte.config.js
index b0683fd2..9074d52a 100644
--- a/AquaNet/svelte.config.js
+++ b/AquaNet/svelte.config.js
@@ -1,4 +1,4 @@
-import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
+import {vitePreprocess} from '@sveltejs/vite-plugin-svelte'
export default {
// Consult https://svelte.dev/docs#compile-time-svelte-preprocess
diff --git a/AquaNet/tsconfig.json b/AquaNet/tsconfig.json
index 5fb548f2..04484cd0 100644
--- a/AquaNet/tsconfig.json
+++ b/AquaNet/tsconfig.json
@@ -16,5 +16,5 @@
"isolatedModules": true
},
"include": ["src/**/*.ts", "src/**/*.js", "src/**/*.svelte"],
- "references": [{ "path": "./tsconfig.node.json" }]
+ "references": [{"path": "./tsconfig.node.json"}]
}
diff --git a/AquaNet/vite.config.ts b/AquaNet/vite.config.ts
index d7019694..73ffae3a 100644
--- a/AquaNet/vite.config.ts
+++ b/AquaNet/vite.config.ts
@@ -1,5 +1,5 @@
-import { defineConfig } from 'vite'
-import { svelte } from '@sveltejs/vite-plugin-svelte'
+import {defineConfig} from 'vite'
+import {svelte} from '@sveltejs/vite-plugin-svelte'
// https://vitejs.dev/config/
export default defineConfig({
diff --git a/AquaNet/yarn.lock b/AquaNet/yarn.lock
index 3f692fae..f5201c37 100644
--- a/AquaNet/yarn.lock
+++ b/AquaNet/yarn.lock
@@ -1047,6 +1047,16 @@ postcss@^8.4.35:
picocolors "^1.0.0"
source-map-js "^1.0.2"
+prettier-plugin-svelte@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/prettier-plugin-svelte/-/prettier-plugin-svelte-3.1.2.tgz#2e050eb56dbb467a42c45ad6ce18bb277d28ffa0"
+ integrity sha512-7xfMZtwgAWHMT0iZc8jN4o65zgbAQ3+O32V6W7pXrqNvKnHnkoyQCGCbKeUyXKZLbYE0YhFRnamfxfkEGxm8qA==
+
+prettier@^3.2.5:
+ version "3.2.5"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.2.5.tgz#e52bc3090586e824964a8813b09aba6233b28368"
+ integrity sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==
+
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"