You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

96 lines
2.6 KiB

import { useAuthSession } from "./useAuthSession";
// Module-level singleton state — shared across all components that call useFavorite().
const favoritedCardIds = ref<Set<number>>(new Set())
const loadingIds = ref<Set<number>>(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<boolean> {
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,
}
}