From 2ab2666ad018c95f10abbce06e0c7dace7e9753e Mon Sep 17 00:00:00 2001 From: Raymond <101374892+raymonable@users.noreply.github.com> Date: Tue, 30 Sep 2025 01:56:26 -0400 Subject: [PATCH] Feat: Favorites (for all supported games) (#174) --- AquaNet/src/libs/generalTypes.ts | 3 +- AquaNet/src/libs/i18n/en_ref.ts | 1 + AquaNet/src/libs/i18n/zh.ts | 1 + AquaNet/src/pages/UserHome.svelte | 71 ++++++++++++++++++- .../aqua/net/games/GameApiController.kt | 5 +- .../java/icu/samnyan/aqua/net/games/Models.kt | 3 +- .../icu/samnyan/aqua/net/games/chu3/Chusan.kt | 4 +- .../samnyan/aqua/net/games/mai2/Maimai2.kt | 2 +- .../icu/samnyan/aqua/net/games/wacca/Wacca.kt | 5 +- 9 files changed, 87 insertions(+), 8 deletions(-) diff --git a/AquaNet/src/libs/generalTypes.ts b/AquaNet/src/libs/generalTypes.ts index 715722b0..ed49d2c0 100644 --- a/AquaNet/src/libs/generalTypes.ts +++ b/AquaNet/src/libs/generalTypes.ts @@ -107,7 +107,8 @@ export interface GenericGameSummary { lastVersion: string ratingComposition: { [key: string]: any } recent: GenericGamePlaylog[] - rival?: boolean + rival?: boolean, + favorites?: number[] } export interface MusicMeta { diff --git a/AquaNet/src/libs/i18n/en_ref.ts b/AquaNet/src/libs/i18n/en_ref.ts index eb9b9ab5..3f268d8e 100644 --- a/AquaNet/src/libs/i18n/en_ref.ts +++ b/AquaNet/src/libs/i18n/en_ref.ts @@ -28,6 +28,7 @@ export const EN_REF_USER = { '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.", 'UserHome.ShowMoreRecent': 'Show more', + 'UserHome.FavoriteSongs': 'Favorite Songs' } export const EN_REF_Welcome = { diff --git a/AquaNet/src/libs/i18n/zh.ts b/AquaNet/src/libs/i18n/zh.ts index a56eefda..047e9c17 100644 --- a/AquaNet/src/libs/i18n/zh.ts +++ b/AquaNet/src/libs/i18n/zh.ts @@ -40,6 +40,7 @@ const zhUser: typeof EN_REF_USER = { 'UserHome.RemoveRival': "移除劲敌", 'UserHome.InvalidGame': "游戏 ${game} 还不支持网页端查看。我们目前只支持舞萌、中二、华卡和音击。", 'UserHome.ShowMoreRecent': "显示更多", + 'UserHome.FavoriteSongs': "收藏歌曲" } const zhWelcome: typeof EN_REF_Welcome = { diff --git a/AquaNet/src/pages/UserHome.svelte b/AquaNet/src/pages/UserHome.svelte index 1ddf9fe4..e2f63705 100644 --- a/AquaNet/src/pages/UserHome.svelte +++ b/AquaNet/src/pages/UserHome.svelte @@ -325,7 +325,6 @@ {/if} -

{t('UserHome.RecentScores')}

@@ -368,6 +367,22 @@ {/if}
+ + {#if d.user.favorites != null && d.user.favorites.length > 0} +
+

{t('UserHome.FavoriteSongs')}

+
+ {#each d.user.favorites as favoriteSongId, i} +
+ +
+
{allMusics[favoriteSongId.toString()] ? allMusics[favoriteSongId.toString()].name : t("UserHome.UnknownSong")}
+
+
+ {/each} +
+
+ {/if} {/if} @@ -554,6 +569,60 @@ flex-direction: row justify-content: space-between + .favorites + .scores + display: flex + flex-wrap: wrap + flex-direction: row + gap: 20px + + + // Image and song info + > div + display: flex + align-items: center + width: calc(calc(100% / 3) - 20px) // what the fuck is going on anymore + gap: 20px + + background-color: rgba(white, 0.03) + border-radius: vars.$border-radius + + img + width: 50px + height: 50px + border-radius: vars.$border-radius + object-fit: cover + + // Song info and score + > div.info + flex: 1 + display: flex + justify-content: space-between + overflow: hidden + flex-direction: column + + .first-line + display: flex + flex-direction: row + + // Limit song name to one line + .song-title + max-width: 100% + overflow: hidden + text-overflow: ellipsis + white-space: nowrap + + // Make song score and rank not wrap + > div:last-child + white-space: nowrap + + @media (max-width: vars.$w-mobile) + flex-direction: column + gap: 0 + + .rank-text + text-align: left + // Recent Scores section .recent .scores diff --git a/src/main/java/icu/samnyan/aqua/net/games/GameApiController.kt b/src/main/java/icu/samnyan/aqua/net/games/GameApiController.kt index b1b43a4d..09bc3502 100644 --- a/src/main/java/icu/samnyan/aqua/net/games/GameApiController.kt +++ b/src/main/java/icu/samnyan/aqua/net/games/GameApiController.kt @@ -148,7 +148,7 @@ abstract class GameApiController(val name: String, userDataClass: userMusicRepo.findByUser_Card_ExtIdAndMusicIdIn(card.extId, musicList) } - fun genericUserSummary(card: Card, ratingComp: Map, rival: Boolean? = null): GenericGameSummary { + fun genericUserSummary(card: Card, ratingComp: Map, rival: Boolean? = null, favorites: List? = null): GenericGameSummary { // Summary values: total plays, player rating, server-wide ranking // number of each rank, max combo, number of full combo, number of all perfect val user = userDataRepo.findByCard(card) ?: (404 - "Game data not found") @@ -199,7 +199,8 @@ abstract class GameApiController(val name: String, userDataClass: ratingComposition = ratingComp, recent = plays.sortedBy { it.userPlayDate.toString() }.takeLast(100).reversed(), lastPlayedHost = user.lastClientId?.let { us.userRepo.findByKeychip(it)?.username }, - rival = rival + rival = rival, + favorites = favorites ) } diff --git a/src/main/java/icu/samnyan/aqua/net/games/Models.kt b/src/main/java/icu/samnyan/aqua/net/games/Models.kt index 82ce9058..447d7898 100644 --- a/src/main/java/icu/samnyan/aqua/net/games/Models.kt +++ b/src/main/java/icu/samnyan/aqua/net/games/Models.kt @@ -47,7 +47,8 @@ data class GenericGameSummary( val recent: List, - val rival: Boolean? + val rival: Boolean?, + val favorites: List? ) data class GenericRankingPlayer( diff --git a/src/main/java/icu/samnyan/aqua/net/games/chu3/Chusan.kt b/src/main/java/icu/samnyan/aqua/net/games/chu3/Chusan.kt index b803b544..16bfb6f1 100644 --- a/src/main/java/icu/samnyan/aqua/net/games/chu3/Chusan.kt +++ b/src/main/java/icu/samnyan/aqua/net/games/chu3/Chusan.kt @@ -60,7 +60,9 @@ class Chusan( "new" to (extra["rating_new_list"] ?: ""), ) - genericUserSummary(card, ratingComposition) + val misc = rp.userMisc.findByUser_Card_ExtId(card.extId).firstOrNull() + + genericUserSummary(card, ratingComposition, null, misc?.favMusic) } /** diff --git a/src/main/java/icu/samnyan/aqua/net/games/mai2/Maimai2.kt b/src/main/java/icu/samnyan/aqua/net/games/mai2/Maimai2.kt index 08787113..49cec172 100644 --- a/src/main/java/icu/samnyan/aqua/net/games/mai2/Maimai2.kt +++ b/src/main/java/icu/samnyan/aqua/net/games/mai2/Maimai2.kt @@ -66,7 +66,7 @@ class Maimai2( } } - genericUserSummary(card, ratingComposition, isMyRival) + genericUserSummary(card, ratingComposition, isMyRival, extra["favorite_music"]?.split(",")?.mapNotNull{it -> it.toIntOrNull()}) } @API("user-rating") diff --git a/src/main/java/icu/samnyan/aqua/net/games/wacca/Wacca.kt b/src/main/java/icu/samnyan/aqua/net/games/wacca/Wacca.kt index 3ee3611d..473fb776 100644 --- a/src/main/java/icu/samnyan/aqua/net/games/wacca/Wacca.kt +++ b/src/main/java/icu/samnyan/aqua/net/games/wacca/Wacca.kt @@ -31,7 +31,10 @@ class Wacca( override suspend fun userSummary(@RP username: String, @RP token: String?) = us.cardByName(username) { card -> // TODO: Rating composition - genericUserSummary(card, mapOf()) + + val data = userDataRepo.findByCard_ExtId(card.extId) + + genericUserSummary(card, mapOf(), null, if (data.isPresent) data.get().favoriteSongs else null) } override val shownRanks: List> = waccaScores.filter { it.first > 85 * 10000 }