feat: add candidate display option to global game options

This commit is contained in:
Raymond
2026-06-08 12:11:30 -04:00
parent faabd45b7a
commit 7c31dfd4ab
10 changed files with 96 additions and 44 deletions

View File

@@ -1,7 +1,10 @@
# This should reference your AquaDX instance's :80 port.
# Sometimes issues with CORS may appear, the easiest fix is to use a reverse proxy like Caddy to put AquaDX on the /aqua path,
# and set this to match the /aqua path (see example below)
VITE_AQUA_HOST=https://aquadx.net/aqua
# Link to datas (eg, songid, information, etc).
# Leave this as is if you do not have the data.
# Link to converted game data (ex: all_music.json, all_items.json, etc.)
# Profile pictures are also served through this path, you may want to disable profile pictures if using the primary instance for data fetching
VITE_DATA_HOST=https://aquadx.net
VITE_AQUA_CONNECTION=aquadx.hydev.org

View File

@@ -1,32 +1,67 @@
<script>
<script lang="ts">
import { fade } from "svelte/transition";
import { FADE_IN, FADE_OUT } from "../../libs/config";
import { t, ts } from "../../libs/i18n";
import useLocalStorage from "../../libs/hooks/useLocalStorage.svelte";
import RegionSelector from "./RegionSelector.svelte";
import { USER } from "../../libs/sdk";
import type { AquaNetUser } from "../../libs/generalTypes";
import StatusOverlays from "../StatusOverlays.svelte";
const rounding = useLocalStorage("rounding", true);
let me: AquaNetUser;
let submitting = "";
let error = "";
let loading = true;
USER.me().then((m) => {
me = m;
loading = false;
}).catch(e => error = e.message)
function submit(field: string, value: string) {
if (submitting) return
submitting = field
USER.setting(field, value).then(() => {
}).catch(e => error = e.message).finally(() => submitting = "")
}
</script>
<div class="fields">
<blockquote class="info">
{ts("settings.siteNotice")}
</blockquote>
<div class="field">
<div class="bool">
<input id="rounding" type="checkbox" bind:checked={rounding.value}/>
<label for="rounding">
<span class="name">{ts(`settings.fields.rounding.name`)}</span>
<span class="desc">{ts(`settings.fields.rounding.desc`)}</span>
</label>
{#if !loading}
<div class="fields">
<blockquote class="info">
{ts("settings.siteNotice")}
</blockquote>
<div class="field">
<div class="bool">
<input id="rounding" type="checkbox" bind:checked={rounding.value}/>
<label for="rounding">
<span class="name">{ts(`settings.fields.rounding.name`)}</span>
<span class="desc">{ts(`settings.fields.rounding.desc`)}</span>
</label>
</div>
</div>
<div class="field m-t">
<div class="bool">
<input id="displayCandidates" type="checkbox" bind:checked={me.displayCandidates}
on:change={() => submit('displayCandidates', me.displayCandidates.toString())}/>
<label for="displayCandidates">
<span class="name">{ts(`settings.fields.displayCandidates.name`)}</span>
<span class="desc">{ts(`settings.fields.displayCandidates.desc`)}</span>
</label>
</div>
</div>
<div class="divider"></div>
<blockquote class="info">
{ts("settings.regionNotice")}
</blockquote>
<RegionSelector/>
</div>
<div class="divider"></div>
<blockquote class="info">
{ts("settings.regionNotice")}
</blockquote>
<RegionSelector/>
</div>
{/if}
<StatusOverlays {error} {loading} />
<style lang="sass">
@use "../../vars"

View File

@@ -32,6 +32,8 @@ export interface AquaNetUser {
password: string,
optOutOfLeaderboard: boolean,
canModifyKeychips: boolean,
hideCountry: boolean,
displayCandidates: boolean
}
export interface CardSummaryGame {

View File

@@ -26,16 +26,13 @@ export const EN_REF_USER = {
'UserHome.RankDetail.Title': 'Achievement Details',
'UserHome.RankDetail.Level': 'Level',
'UserHome.B50': 'B50',
'UserHome.RatingComposition.Best50': 'Best 50',
'UserHome.RatingComposition.Best35': 'Best 35',
'UserHome.RatingComposition.Best30': 'Best 30',
'UserHome.RatingComposition.Best15': 'Best 15',
'UserHome.RatingComposition.New15': 'New 15',
'UserHome.RatingComposition.New10': 'New 10',
'UserHome.RatingComposition.Best25Candidates': 'Best 25 (Candidates)',
'UserHome.RatingComposition.Platinum': 'Platinum',
'UserHome.RatingComposition.New20': 'New 20',
'UserHome.RatingComposition.Recent10': 'Recent 10',
'UserHome.RatingComposition.Best': 'Best ${n}',
'UserHome.RatingComposition.New': 'New ${n}',
'UserHome.RatingComposition.BestCandidates': 'Best ${n} Candidates',
'UserHome.RatingComposition.NewCandidates': 'New ${n} Candidates',
'UserHome.RatingComposition.Platinum': 'Platinum ${n}',
'UserHome.RatingComposition.PlatinumCandidates': 'Platinum ${n} Candidates',
'UserHome.RatingComposition.Recent': 'Recent ${n}',
'UserHome.AddRival': "Add to Rival",
'UserHome.RemoveRival': "Remove from Rival",
'UserHome.InvalidGame': "Game ${game} is not supported on the web UI yet. We only support maimai, chunithm, wacca, and ongeki for now.",
@@ -260,6 +257,8 @@ export const EN_REF_SETTINGS = {
'settings.fields.optOutOfLeaderboard.desc': 'You will still be able to see yourself on the leaderboard after logging in',
'settings.fields.hideCountry.name': 'Hide Country',
'settings.fields.hideCountry.desc': 'Your country of registration will not be visible on your profile',
'settings.fields.displayCandidates.name': 'Display Candidates',
'settings.fields.displayCandidates.desc': 'Shows upcoming songs that could improve your rating on your profile (when available)',
'settings.fields.enableMusicRank.name': 'Enable Recommended Music Rank on Your Machine',
'settings.fields.enableMusicRank.desc': 'If you have your own ranking, you can turn this off. It only affects your own machine',
'settings.profile.picture': 'Profile Picture',

View File

@@ -65,6 +65,7 @@
const getGameByCode = (code: string) => {
switch (code?.toUpperCase()) {
case 'SDGA':
case 'SDEZ':
return 'mai2';
case 'SDHD':

View File

@@ -328,20 +328,25 @@
</div>
</div>
<RatingComposition title={t("UserHome.RatingComposition.Best50")} comp={d.user.ratingComposition.best50} {allMusics} {game}/>
<RatingComposition title={t("UserHome.RatingComposition.Best35")} comp={d.user.ratingComposition.best35} {allMusics} {game}/>
<RatingComposition title={t("UserHome.RatingComposition.Best30")} comp={d.user.ratingComposition.best30} {allMusics} {game}/>
<RatingComposition title={t("UserHome.RatingComposition.Best15")} comp={d.user.ratingComposition.best15} {allMusics} {game}/>
<RatingComposition title={t("UserHome.RatingComposition.New15")} comp={d.user.ratingComposition.new15} {allMusics} {game}/>
<RatingComposition title={t("UserHome.RatingComposition.New10")} comp={d.user.ratingComposition.new10} {allMusics} {game}/>
<RatingComposition title={t("UserHome.RatingComposition.Best25Candidates")} comp={d.user.ratingComposition.best25_candidates} {allMusics} {game}/>
<RatingComposition title={t("UserHome.RatingComposition.Platinum")} comp={d.user.ratingComposition.pscore} {allMusics} {game}/>
<RatingComposition title={t("UserHome.RatingComposition.Best", {n: 50})} comp={d.user.ratingComposition.best50} {allMusics} {game}/>
<RatingComposition title={t("UserHome.RatingComposition.Best", {n: 35})} comp={d.user.ratingComposition.best35} {allMusics} {game}/>
<RatingComposition title={t("UserHome.RatingComposition.Best", {n: 30})} comp={d.user.ratingComposition.best30} {allMusics} {game}/>
<RatingComposition title={t("UserHome.RatingComposition.Best", {n: 15})} comp={d.user.ratingComposition.best15} {allMusics} {game}/>
<RatingComposition title={t("UserHome.RatingComposition.New", {n : 15})} comp={d.user.ratingComposition.new15} {allMusics} {game}/>
<RatingComposition title={t("UserHome.RatingComposition.New", {n: 10})} comp={d.user.ratingComposition.new10} {allMusics} {game}/>
<RatingComposition title={t("UserHome.RatingComposition.Platinum", {n: 50})} comp={d.user.ratingComposition.pscore50} {allMusics} {game}/>
{#if me && me.displayCandidates && d.user.aquaUser && me.username == d.user.aquaUser.username}
<RatingComposition title={t("UserHome.RatingComposition.BestCandidates", {n: 20})} comp={d.user.ratingComposition.best20_candidates} {allMusics} {game}/>
<RatingComposition title={t("UserHome.RatingComposition.NewCandidates", {n: 10})} comp={d.user.ratingComposition.new10_candidates} {allMusics} {game}/>
<RatingComposition title={t("UserHome.RatingComposition.PlatinumCandidates", {n: 20})} comp={d.user.ratingComposition.pscore20_candidates} {allMusics} {game}/>
{/if}
<!-- Chuni -->
{#if d.user.ratingComposition.new}
<RatingComposition title={t("UserHome.RatingComposition.New20")} comp={d.user.ratingComposition.new} {allMusics} game="chu3"/>
<RatingComposition title={t("UserHome.RatingComposition.New", {n: 20})} comp={d.user.ratingComposition.new} {allMusics} game="chu3"/>
{:else}
<RatingComposition title={t("UserHome.RatingComposition.Recent10")} comp={d.user.ratingComposition.recent10} {allMusics} {game} top={10}/>
<RatingComposition title={t("UserHome.RatingComposition.Recent", {n: 10})} comp={d.user.ratingComposition.recent10} {allMusics} {game} top={10}/>
{/if}
<div class="recent">

View File

@@ -64,6 +64,7 @@ class AquaNetUser(
var profilePicture: String? = "",
var optOutOfLeaderboard: Boolean = false,
var hideCountry: Boolean = false,
var displayCandidates: Boolean = false,
// Email confirmation
var emailConfirmed: Boolean = false,
@@ -266,4 +267,5 @@ class AquaUserServices(
fun checkOptOutOfLeaderboard(optOutOfLeaderboard: Str) = optOutOfLeaderboard.toBoolean()
fun checkHideCountry(hideCountry: Str) = hideCountry.toBoolean()
fun checkDisplayCandidates(displayCandidates: Str) = displayCandidates.toBoolean()
}

View File

@@ -64,8 +64,9 @@ data class Chu3DataExport(
var userMapList: List<UserMap>,
var userMusicDetailList: List<UserMusicDetail>,
var userPlaylogList: List<UserPlaylog>,
var userGeneralDataList: List<UserGeneralData>
var userGeneralDataList: List<UserGeneralData>,
var userMiscDataList: Chu3UserMisc
): IExportClass<Chu3UserData> {
constructor() : this("SDHD",
Chu3UserData(), UserGameOption(), ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList())
Chu3UserData(), UserGameOption(), ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList(), ArrayList(), Chu3UserMisc())
}

View File

@@ -51,8 +51,10 @@ class Ongeki(
genericUserSummary(card, mapOf(
"best50" to (extra["new_rating_base_best"] ?: ""),
"new10" to (extra["new_rating_base_new_best"] ?: ""),
"best25_candidates" to (extra["new_rating_base_next_best"] ?: ""),
"pscore" to (extra["new_rating_base_pscore"] ?: "")
"pscore50" to (extra["new_rating_base_pscore"] ?: ""),
"best20_candidates" to (extra["new_rating_base_next_best"] ?: ""),
"new10_candidates" to (extra["new_rating_base_new_next_best"] ?: ""),
"pscore20_candidates" to (extra["new_rating_base_next_pscore"] ?: "")
))
else
genericUserSummary(card, mapOf(

View File

@@ -0,0 +1,2 @@
ALTER TABLE `aqua_net_user`
ADD COLUMN IF NOT EXISTS `display_candidates` bit NOT NULL DEFAULT b'0';