feat(discover): content discovery panel with For You, tags, playlists, collections (M13.1-M13.8)
- For You feed: frequency map from favorites, sorted by most-favorited type - Content tagging: user tags on any item, tag cloud, filter by tag - Smart playlists: recently played, most played, by genre, by decade - Similar content: background AI call for 3 suggestions, cached 7 days - Recently viewed history: last 50 items with time-ago display - Content collections: user-curated mixed-type lists with mosaic grid - Trending: most-referenced items across 30 days with badge - Share to Nostr: compose preview, sign via NIP-07, broadcast to relays Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
d5d8a1f8b2
commit
52e62e33cd
@@ -0,0 +1,89 @@
|
||||
import { ref } from 'vue'
|
||||
import type { FavoriteType } from '@/stores/favorites'
|
||||
|
||||
const STORAGE_KEY = 'aiui-content-collections'
|
||||
|
||||
export interface CollectionItem {
|
||||
id: string
|
||||
type: FavoriteType
|
||||
title: string
|
||||
}
|
||||
|
||||
export interface ContentCollection {
|
||||
id: string
|
||||
name: string
|
||||
description: string
|
||||
items: CollectionItem[]
|
||||
createdAt: number
|
||||
updatedAt: number
|
||||
}
|
||||
|
||||
const collections = ref<ContentCollection[]>([])
|
||||
|
||||
function load() {
|
||||
try {
|
||||
const stored = localStorage.getItem(STORAGE_KEY)
|
||||
if (stored) collections.value = JSON.parse(stored)
|
||||
} catch { /* ignore */ }
|
||||
}
|
||||
|
||||
function save() {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(collections.value))
|
||||
}
|
||||
|
||||
load()
|
||||
|
||||
export function useContentCollections() {
|
||||
function createCollection(name: string, description = ''): ContentCollection {
|
||||
const collection: ContentCollection = {
|
||||
id: crypto.randomUUID(),
|
||||
name,
|
||||
description,
|
||||
items: [],
|
||||
createdAt: Date.now(),
|
||||
updatedAt: Date.now(),
|
||||
}
|
||||
collections.value.push(collection)
|
||||
save()
|
||||
return collection
|
||||
}
|
||||
|
||||
function deleteCollection(id: string) {
|
||||
collections.value = collections.value.filter(c => c.id !== id)
|
||||
save()
|
||||
}
|
||||
|
||||
function addToCollection(collectionId: string, item: CollectionItem) {
|
||||
const collection = collections.value.find(c => c.id === collectionId)
|
||||
if (!collection) return
|
||||
if (collection.items.find(i => i.id === item.id)) return
|
||||
collection.items.push(item)
|
||||
collection.updatedAt = Date.now()
|
||||
save()
|
||||
}
|
||||
|
||||
function removeFromCollection(collectionId: string, itemId: string) {
|
||||
const collection = collections.value.find(c => c.id === collectionId)
|
||||
if (!collection) return
|
||||
collection.items = collection.items.filter(i => i.id !== itemId)
|
||||
collection.updatedAt = Date.now()
|
||||
save()
|
||||
}
|
||||
|
||||
function renameCollection(id: string, name: string) {
|
||||
const collection = collections.value.find(c => c.id === id)
|
||||
if (!collection) return
|
||||
collection.name = name
|
||||
collection.updatedAt = Date.now()
|
||||
save()
|
||||
}
|
||||
|
||||
return {
|
||||
collections,
|
||||
createCollection,
|
||||
deleteCollection,
|
||||
addToCollection,
|
||||
removeFromCollection,
|
||||
renameCollection,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user