增加足迹和收藏

This commit is contained in:
yml2213
2026-06-12 22:34:52 +08:00
parent c726659866
commit 9b41b9dc84
9 changed files with 610 additions and 3 deletions
@@ -0,0 +1,122 @@
import { computed, ref } from 'vue'
import type { Listing } from '@/features/listings/api/listings'
const historyKey = 'hfb.listing.view.history'
const favoriteKey = 'hfb.listing.favorites'
const maxHistoryItems = 80
export interface ListingCollectionItem {
listing: Listing
viewed_at?: string
favorited_at?: string
}
const historyItems = ref<ListingCollectionItem[]>(readItems(historyKey))
const favoriteItems = ref<ListingCollectionItem[]>(readItems(favoriteKey))
export function useListingCollections() {
const history = computed(() => historyItems.value)
const favorites = computed(() => favoriteItems.value)
const favoriteIDs = computed(() => new Set(favoriteItems.value.map(item => item.listing.id)))
function recordListingView(listing: Listing) {
const nextItem: ListingCollectionItem = {
listing: snapshotListing(listing),
viewed_at: new Date().toISOString(),
}
historyItems.value = [
nextItem,
...historyItems.value.filter(item => item.listing.id !== listing.id),
].slice(0, maxHistoryItems)
writeItems(historyKey, historyItems.value)
}
function isFavorite(listingID: number) {
return favoriteIDs.value.has(listingID)
}
function toggleFavorite(listing: Listing) {
if (isFavorite(listing.id)) {
favoriteItems.value = favoriteItems.value.filter(item => item.listing.id !== listing.id)
writeItems(favoriteKey, favoriteItems.value)
return false
}
favoriteItems.value = [
{
listing: snapshotListing(listing),
favorited_at: new Date().toISOString(),
},
...favoriteItems.value.filter(item => item.listing.id !== listing.id),
]
writeItems(favoriteKey, favoriteItems.value)
return true
}
function removeHistory(listingID: number) {
historyItems.value = historyItems.value.filter(item => item.listing.id !== listingID)
writeItems(historyKey, historyItems.value)
}
function removeFavorite(listingID: number) {
favoriteItems.value = favoriteItems.value.filter(item => item.listing.id !== listingID)
writeItems(favoriteKey, favoriteItems.value)
}
function clearHistory() {
historyItems.value = []
writeItems(historyKey, historyItems.value)
}
return {
history,
favorites,
favoriteIDs,
recordListingView,
isFavorite,
toggleFavorite,
removeHistory,
removeFavorite,
clearHistory,
}
}
function readItems(key: string): ListingCollectionItem[] {
if (typeof localStorage === 'undefined') return []
try {
const raw = localStorage.getItem(key)
if (!raw) return []
const parsed = JSON.parse(raw) as unknown
if (!Array.isArray(parsed)) return []
return parsed
.map(normalizeItem)
.filter((item): item is ListingCollectionItem => Boolean(item))
} catch {
localStorage.removeItem(key)
return []
}
}
function normalizeItem(value: unknown): ListingCollectionItem | null {
if (!isRecord(value) || !isRecord(value.listing)) return null
const id = Number(value.listing.id)
if (!Number.isFinite(id) || id <= 0) return null
return {
listing: value.listing as unknown as Listing,
viewed_at: typeof value.viewed_at === 'string' ? value.viewed_at : undefined,
favorited_at: typeof value.favorited_at === 'string' ? value.favorited_at : undefined,
}
}
function writeItems(key: string, items: ListingCollectionItem[]) {
if (typeof localStorage === 'undefined') return
localStorage.setItem(key, JSON.stringify(items))
}
function snapshotListing(listing: Listing): Listing {
return JSON.parse(JSON.stringify(listing)) as Listing
}
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null
}