import { useAuthSession } from "./useAuthSession"; // Module-level singleton state — shared across all components that call useFavorite(). const favoritedCardIds = ref>(new Set()) const loadingIds = ref>(new Set()) /** * Favorite management composable. * Provides a reactive set of favorited card IDs and toggle function. */ export function useFavorite() { const { loggedIn } = useAuthSession() const { $toast } = useNuxtApp() /** * Check if a card is favorited. */ function isFavorited(cardId: number): boolean { return favoritedCardIds.value.has(cardId) } /** * Toggle favorite for a card. * Optimistically updates local state. */ async function toggle(cardId: number): Promise { if (!loggedIn.value) { $toast.info('请先登录') return false } if (loadingIds.value.has(cardId)) return isFavorited(cardId) const wasFavorited = favoritedCardIds.value.has(cardId) // Optimistic update const next = new Set(favoritedCardIds.value) if (wasFavorited) { next.delete(cardId) } else { next.add(cardId) } favoritedCardIds.value = next loadingIds.value = new Set(loadingIds.value).add(cardId) try { const raw = await $fetch<{ code: number; data: { favorited: boolean } }>( `/api/cards/${cardId}/favorite`, { method: 'POST' }, ) if (raw.code !== 0 || !raw.data.favorited) { // Server says unfavorited — ensure it's removed const corrected = new Set(favoritedCardIds.value) corrected.delete(cardId) favoritedCardIds.value = corrected } return raw.data.favorited } catch { // Revert optimistic update const reverted = new Set(favoritedCardIds.value) if (wasFavorited) { reverted.add(cardId) } else { reverted.delete(cardId) } favoritedCardIds.value = reverted $toast.error('操作失败,请稍后重试') return wasFavorited } finally { const next = new Set(loadingIds.value) next.delete(cardId) loadingIds.value = next } } /** * Sync favorited status from server response (batch). */ function syncFromCards(cards: Array<{ id: number; isFavorited?: boolean }>) { const next = new Set(favoritedCardIds.value) for (const card of cards) { if (card.isFavorited) { next.add(card.id) } else { next.delete(card.id) } } favoritedCardIds.value = next } return { favoritedCardIds, loadingIds, isFavorited, toggle, syncFromCards, } }