83 lines
2.6 KiB
TypeScript
83 lines
2.6 KiB
TypeScript
import type { RecipeItem } from '~/types'
|
|
import type { DbRecipeItem } from '~/utils/db'
|
|
import type { FavoriteEntry } from '~/utils/userData'
|
|
import { useStorage } from '@vueuse/core'
|
|
import { computed } from 'vue'
|
|
import { namespace } from '~/constants'
|
|
import { userDataMeta } from './userDataMeta'
|
|
|
|
export type { FavoriteEntry }
|
|
|
|
// Store favorite entries with timestamp in localStorage
|
|
const rawFavorites = useStorage(`${namespace}:favorites`, [] as any)
|
|
|
|
// Migration: if old format number[] exists, convert to FavoriteEntry[] with current time
|
|
function ensureFavoriteEntries(): FavoriteEntry[] {
|
|
const now = Date.now()
|
|
const v = rawFavorites.value
|
|
if (Array.isArray(v)) {
|
|
if (v.length === 0)
|
|
return []
|
|
// old format: array of numbers
|
|
if (typeof v[0] === 'number') {
|
|
const migrated: FavoriteEntry[] = (v as number[]).map(id => ({ id, time: now }))
|
|
rawFavorites.value = migrated
|
|
return migrated
|
|
}
|
|
// new format
|
|
if (typeof v[0] === 'object' && v[0] && 'id' in v[0])
|
|
return v as FavoriteEntry[]
|
|
}
|
|
// fallback
|
|
rawFavorites.value = []
|
|
return []
|
|
}
|
|
|
|
export const favoriteEntries = computed<FavoriteEntry[]>(() => ensureFavoriteEntries())
|
|
export const favoriteRecipeIds = computed<number[]>(() => favoriteEntries.value.map(e => e.id))
|
|
|
|
function getId(item: RecipeItem | DbRecipeItem): number | null {
|
|
// Only support DbRecipeItem with numeric id for now
|
|
return typeof (item as DbRecipeItem).id === 'number' ? (item as DbRecipeItem).id! : null
|
|
}
|
|
|
|
export function isFavorited(item: RecipeItem | DbRecipeItem) {
|
|
const id = getId(item)
|
|
if (id == null)
|
|
return false
|
|
return favoriteRecipeIds.value.includes(id)
|
|
}
|
|
|
|
export function toggleFavorite(item: RecipeItem | DbRecipeItem) {
|
|
const id = getId(item)
|
|
if (id == null)
|
|
return
|
|
const list = ensureFavoriteEntries()
|
|
const idx = list.findIndex(e => e.id === id)
|
|
const time = Date.now()
|
|
if (idx >= 0)
|
|
list.splice(idx, 1)
|
|
else
|
|
list.push({ id, time })
|
|
rawFavorites.value = list
|
|
|
|
// Keep one tombstone per id so the removal can sync to other devices.
|
|
const favoriteRemovals = userDataMeta.value.favoriteRemovals.filter(r => r.id !== id)
|
|
if (idx >= 0)
|
|
favoriteRemovals.push({ id, time })
|
|
userDataMeta.value = { ...userDataMeta.value, favoriteRemovals }
|
|
}
|
|
|
|
export function setFavoriteEntries(list: FavoriteEntry[]) {
|
|
rawFavorites.value = list
|
|
}
|
|
|
|
export function getFavoriteTime(item: RecipeItem | DbRecipeItem): number | null {
|
|
const id = getId(item)
|
|
if (id == null)
|
|
return null
|
|
const list = ensureFavoriteEntries()
|
|
const entry = list.find(e => e.id === id)
|
|
return entry?.time ?? null
|
|
}
|