mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 19:36:36 +08:00
fix: group local search series results
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { FormEvent, useEffect, useState } from 'react'
|
||||
import { FormEvent, useEffect, useMemo, useState } from 'react'
|
||||
import { Link } from 'react-router-dom'
|
||||
import { Loader2, Rss, Search, Sparkles, Wand2 } from 'lucide-react'
|
||||
import toast from 'react-hot-toast'
|
||||
@@ -8,6 +8,7 @@ import { imageURL } from '../api/client'
|
||||
import { subscriptionsAPI } from '../api/subscriptions'
|
||||
import { MediaCard } from '../components/MediaCard'
|
||||
import type { Media } from '../types'
|
||||
import { groupSeries, seriesCardLink } from '../utils/groupSeries'
|
||||
|
||||
// AIAssistantPage exposes the two AI helpers backed by the Go server:
|
||||
// - smart search: parses a natural-language query into a SearchIntent +
|
||||
@@ -30,6 +31,7 @@ export function AIAssistantPage() {
|
||||
const [items, setItems] = useState<Media[]>([])
|
||||
const [externalItems, setExternalItems] = useState<ExternalMediaResult[]>([])
|
||||
const [subscribing, setSubscribing] = useState('')
|
||||
const localCards = useMemo(() => groupSeries(items), [items])
|
||||
|
||||
const [recs, setRecs] = useState<string[] | null>(null)
|
||||
const [recommending, setRecommending] = useState(false)
|
||||
@@ -186,11 +188,21 @@ export function AIAssistantPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{items.length > 0 && (
|
||||
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6">
|
||||
{items.map((m) => (
|
||||
<MediaCard key={m.id} media={m} />
|
||||
))}
|
||||
{localCards.length > 0 && (
|
||||
<div className="space-y-2">
|
||||
<div className="text-sm font-semibold text-ink-100">
|
||||
本地媒体库 · {localCards.length} 个合集 / {items.length} 个条目
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6">
|
||||
{localCards.map((card) => (
|
||||
<MediaCard
|
||||
key={card.key}
|
||||
media={card.rep}
|
||||
count={card.count}
|
||||
linkTo={seriesCardLink(card)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { imageURL } from '../api/client'
|
||||
import { libraryAPI } from '../api/library'
|
||||
import { MediaCard } from '../components/MediaCard'
|
||||
import type { Library, Media } from '../types'
|
||||
import { artworkScore, groupSeries, type SeriesCard } from '../utils/groupSeries'
|
||||
import { artworkScore, groupSeries, seriesCardLink, type SeriesCard } from '../utils/groupSeries'
|
||||
|
||||
type LibraryPreview = {
|
||||
library: Library
|
||||
@@ -206,7 +206,7 @@ function LibraryShelf({ preview }: { preview: LibraryPreview }) {
|
||||
<MediaCard
|
||||
media={card.rep}
|
||||
count={card.count}
|
||||
linkTo={mediaCardLink(card)}
|
||||
linkTo={seriesCardLink(card)}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
@@ -226,13 +226,6 @@ function latestCards(items: Media[]): SeriesCard[] {
|
||||
.slice(0, 10)
|
||||
}
|
||||
|
||||
function mediaCardLink(card: SeriesCard): string {
|
||||
if (card.count > 1) {
|
||||
return `/library/${card.rep.library_id}?series=${encodeURIComponent(card.key)}`
|
||||
}
|
||||
return `/media/${card.rep.id}`
|
||||
}
|
||||
|
||||
function mediaTime(media: Media): number {
|
||||
return Date.parse(media.updated_at || media.created_at || '') || 0
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ChangeEvent, FormEvent, useCallback, useEffect, useState } from 'react'
|
||||
import { ChangeEvent, FormEvent, useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import toast from 'react-hot-toast'
|
||||
import { Rss, Sparkles } from 'lucide-react'
|
||||
|
||||
@@ -8,6 +8,7 @@ import { mediaAPI } from '../api/library'
|
||||
import { subscriptionsAPI } from '../api/subscriptions'
|
||||
import { MediaCard } from '../components/MediaCard'
|
||||
import type { Media } from '../types'
|
||||
import { groupSeries, seriesCardLink } from '../utils/groupSeries'
|
||||
|
||||
export function SearchPage() {
|
||||
const [q, setQ] = useState('')
|
||||
@@ -20,6 +21,7 @@ export function SearchPage() {
|
||||
const [hasSearched, setHasSearched] = useState(false)
|
||||
const [externalItems, setExternalItems] = useState<ExternalMediaResult[]>([])
|
||||
const [subscribing, setSubscribing] = useState('')
|
||||
const localCards = useMemo(() => groupSeries(items), [items])
|
||||
|
||||
useEffect(() => {
|
||||
aiAPI
|
||||
@@ -84,7 +86,7 @@ export function SearchPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const showEmpty = !loading && !error && hasSearched && items.length === 0
|
||||
const showEmpty = !loading && !error && hasSearched && localCards.length === 0
|
||||
const showIdle = !loading && !error && !hasSearched
|
||||
|
||||
return (
|
||||
@@ -160,12 +162,19 @@ export function SearchPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{items.length > 0 && (
|
||||
{localCards.length > 0 && (
|
||||
<>
|
||||
<div className="text-sm font-semibold text-ink-100">本地媒体库</div>
|
||||
<div className="text-sm font-semibold text-ink-100">
|
||||
本地媒体库 · {localCards.length} 个合集 / {items.length} 个条目
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 xl:grid-cols-6">
|
||||
{items.map((m) => (
|
||||
<MediaCard key={m.id} media={m} />
|
||||
{localCards.map((card) => (
|
||||
<MediaCard
|
||||
key={card.key}
|
||||
media={card.rep}
|
||||
count={card.count}
|
||||
linkTo={seriesCardLink(card)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -82,6 +82,13 @@ export function groupSeries(items: Media[]): SeriesCard[] {
|
||||
return Array.from(groups.values())
|
||||
}
|
||||
|
||||
export function seriesCardLink(card: SeriesCard): string {
|
||||
if (card.count > 1) {
|
||||
return `/library/${card.rep.library_id}?series=${encodeURIComponent(card.key)}`
|
||||
}
|
||||
return `/media/${card.rep.id}`
|
||||
}
|
||||
|
||||
export function artworkScore(media: Media): number {
|
||||
const poster = (media.poster_url ?? '').toLowerCase()
|
||||
const backdrop = (media.backdrop_url ?? '').toLowerCase()
|
||||
|
||||
Reference in New Issue
Block a user