增加足迹和收藏
This commit is contained in:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user