mirror of
https://github.com/ZHANGTIANYAO1/teamspeak-music-bot.git
synced 2026-10-01 20:42:50 +08:00
fix(favorites): error handling + state hydration + input validation [#87 review]
- addFavorite/removeFavorite now wrap axios in try/catch: a 409 (already favorited,
common on a stale heart) or 404 resyncs instead of throwing an unhandled promise
rejection; other errors surface a toast.
- fetchHomeData refreshes favorites BEFORE the TTL cache-return (was appended after
the early return, so warm-cache loads never refreshed); removed the now-redundant
trailing call. App.vue onMounted also hydrates favorites so deep-links to Search/
Playlist show correct hearts.
- favorites API: GET /check rejects non-string (array) query params with 400 instead
of a 500; POST defaults req.body to {} so a missing JSON body yields the intended 400.
This commit is contained in:
1 parent
140020f63a
commit
f19f56a666
3 files changed
+35
-12
No files matched your search
@@ -143,6 +143,10 @@ function cycleMobileMode() {
|
||||
onMounted(async () => {
|
||||
playerStore.loadTheme();
|
||||
connect();
|
||||
// Hydrate favorites once per session so deep-links / hard refreshes onto
|
||||
// Search or Playlist render hearts correctly without first visiting Home.
|
||||
// (fire-and-forget; fetchFavorites swallows the 401 when not yet logged in.)
|
||||
playerStore.fetchFavorites();
|
||||
syncTimer = setInterval(() => playerStore.syncElapsed(), 3000);
|
||||
mobileRaf = requestAnimationFrame(updateMobileProgress);
|
||||
// Reconcile the dedicated-link scope only after the bot list is known: the
|
||||
|
||||
@@ -473,15 +473,33 @@ export const usePlayerStore = defineStore('player', {
|
||||
},
|
||||
|
||||
async addFavorite(playlist: { platform: string; playlistId: string; name: string; coverUrl: string; songCount: number }) {
|
||||
await axios.post('/api/favorites', playlist);
|
||||
await this.fetchFavorites();
|
||||
this.notify('已收藏', 'info');
|
||||
try {
|
||||
await axios.post('/api/favorites', playlist);
|
||||
await this.fetchFavorites();
|
||||
this.notify('已收藏', 'info');
|
||||
} catch (err: any) {
|
||||
// 409 = already favorited (e.g. stale heart); just resync so the UI converges.
|
||||
if (err?.response?.status === 409) {
|
||||
await this.fetchFavorites();
|
||||
return;
|
||||
}
|
||||
this.notify('收藏失败', 'error');
|
||||
}
|
||||
},
|
||||
|
||||
async removeFavorite(id: number) {
|
||||
await axios.delete(`/api/favorites/${id}`);
|
||||
await this.fetchFavorites();
|
||||
this.notify('已取消收藏', 'info');
|
||||
try {
|
||||
await axios.delete(`/api/favorites/${id}`);
|
||||
await this.fetchFavorites();
|
||||
this.notify('已取消收藏', 'info');
|
||||
} catch (err: any) {
|
||||
// 404 = already gone; resync. Otherwise report failure.
|
||||
if (err?.response?.status === 404) {
|
||||
await this.fetchFavorites();
|
||||
return;
|
||||
}
|
||||
this.notify('取消收藏失败', 'error');
|
||||
}
|
||||
},
|
||||
|
||||
isFavorited(playlistId: string, platform: string): boolean {
|
||||
@@ -505,6 +523,10 @@ export const usePlayerStore = defineStore('player', {
|
||||
this.authStatus.netease = newAuth.netease;
|
||||
this.authStatus.qq = newAuth.qq;
|
||||
|
||||
// Favorites are user-local and cheap; always refresh them, even on a
|
||||
// home-data cache hit, so hearts stay correct across tabs/sessions.
|
||||
this.fetchFavorites();
|
||||
|
||||
// Cache hit only if auth is unchanged AND within TTL.
|
||||
if (
|
||||
!authChanged &&
|
||||
@@ -575,9 +597,6 @@ export const usePlayerStore = defineStore('player', {
|
||||
if (authOk) {
|
||||
this.lastFetchTime = Date.now();
|
||||
}
|
||||
|
||||
// Fetch favorites in parallel — not cached by TTL; always fresh
|
||||
this.fetchFavorites();
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in new issue
Block a user