fix: deduplicate all remote drama lists

This commit is contained in:
Zhou Weidong committed 2026-11-10 16:36:21 +08:00
1 parent 4dd79f6136
commit b8c1816f1d
3 files changed
+6 -6

No files matched your search

+2 -2
View File
@@ -4,7 +4,7 @@ import { SectionHeading } from '../components/SectionHeading';
import { videos } from '../data/catalog';
import { useAppStore } from '../app/store';
import { appApi } from '../services/appApi';
import { normalizeDrama, normalizePage } from '../services/normalizers';
import { normalizeDrama, normalizePage, uniqueDramas } from '../services/normalizers';
import { useApi } from '../hooks/useApi';
export function ListingPage({ type }) { const { locale } = useAppStore(); const title = type === 'shorts' ? locale.shortTitle : type === 'subscriptions' ? locale.channels : type === 'history' ? locale.history : locale.library; const api = useApi(() => appApi.dramas().then((payload) => normalizePage(payload).list.map((item) => normalizeDrama(item, locale.code))), [locale.code]); const items = api.data?.length ? api.data : videos; return <><SectionHeading title={title} /><div className="api-note">{api.loading ? locale.loading : api.error ? locale.apiFallback : ''}</div><div className="video-grid">{items.map((video) => <MediaCard key={video.id} video={video} />)}</div></>; }
export function ListingPage({ type }) { const { locale } = useAppStore(); const title = type === 'shorts' ? locale.shortTitle : type === 'subscriptions' ? locale.channels : type === 'history' ? locale.history : locale.library; const api = useApi(() => appApi.dramas().then((payload) => uniqueDramas(normalizePage(payload).list.map((item) => normalizeDrama(item, locale.code)))), [locale.code]); const items = api.data?.length ? api.data : videos; return <><SectionHeading title={title} /><div className="api-note">{api.loading ? locale.loading : api.error ? locale.apiFallback : ''}</div><div className="video-grid">{items.map((video) => <MediaCard key={video.id} video={video} />)}</div></>; }
+2 -2
View File
@@ -5,7 +5,7 @@ import { SectionHeading } from '../components/SectionHeading';
import { useAppStore } from '../app/store';
import { searchCatalog } from '../services/catalogService';
import { appApi } from '../services/appApi';
import { normalizeDrama, normalizePage } from '../services/normalizers';
import { normalizeDrama, normalizePage, uniqueDramas } from '../services/normalizers';
import { useApi } from '../hooks/useApi';
export function SearchPage() { const { locale } = useAppStore(); const [params] = useSearchParams(); const query = params.get('q') || ''; const api = useApi(() => appApi.dramas().then((payload) => normalizePage(payload).list.map((item) => normalizeDrama(item, locale.code))), [locale.code]); const source = api.data?.length ? api.data : searchCatalog('', '', 0); const normalizedQuery = query.toLowerCase(); const results = source.filter((video) => `${video.title} ${video.channel} ${video.genre}`.toLowerCase().includes(normalizedQuery)); return <><SectionHeading title={query ? `Search results for “${query}”` : locale.picks} /><div className="api-note">{api.loading ? locale.loading : api.error ? locale.apiFallback : ''}</div><div className="video-grid">{results.map((video) => <MediaCard key={video.id} video={video} />)}</div></>; }
export function SearchPage() { const { locale } = useAppStore(); const [params] = useSearchParams(); const query = params.get('q') || ''; const api = useApi(() => appApi.dramas().then((payload) => uniqueDramas(normalizePage(payload).list.map((item) => normalizeDrama(item, locale.code)))), [locale.code]); const source = api.data?.length ? api.data : searchCatalog('', '', 0); const normalizedQuery = query.toLowerCase(); const results = source.filter((video) => `${video.title} ${video.channel} ${video.genre}`.toLowerCase().includes(normalizedQuery)); return <><SectionHeading title={query ? `Search results for “${query}”` : locale.picks} /><div className="api-note">{api.loading ? locale.loading : api.error ? locale.apiFallback : ''}</div><div className="video-grid">{results.map((video) => <MediaCard key={video.id} video={video} />)}</div></>; }
+2 -2
View File
@@ -4,9 +4,9 @@ import { Link } from 'react-router-dom';
import { Play } from 'lucide-react';
import { videos } from '../data/catalog';
import { appApi } from '../services/appApi';
import { normalizePage, normalizeDrama } from '../services/normalizers';
import { normalizePage, normalizeDrama, uniqueDramas } from '../services/normalizers';
import { useApi } from '../hooks/useApi';
import { useAppStore } from '../app/store';
function FeedItem({ video }) { const ref = useRef(null); useEffect(() => { const element = ref.current; const observer = new IntersectionObserver(([entry]) => { if (entry.isIntersecting) element.play().catch(() => {}); else element.pause(); }, { threshold: 0.7 }); observer.observe(element); return () => observer.disconnect(); }, []); const src = video.preview?.playUrl || video.videoUrl || ''; return <article className="feed-item"><video ref={ref} src={src} poster={video.image} muted loop playsInline controls={false} /><div className="feed-overlay"><span className="feed-badge">YMB SHORTS</span><h2>{video.title}</h2><p>{video.channel}</p>{src ? <Link to={`/watch/${video.id}`} className="primary-button"><Play size={15} fill="currentColor" /> {video.watch || 'Watch drama'}</Link> : <span className="api-note warning">Video unavailable</span>}</div></article>; }
export function ShortsFeedPage() { const { locale } = useAppStore(); const api = useApi(() => appApi.dramas().then((payload) => normalizePage(payload).list.map((item) => normalizeDrama(item, locale.code))), [locale.code]); const items = api.data || []; return <section className="shorts-feed-page"><div className="feed-heading"><div><span>YMB SHORTS</span><h1>{locale.shortTitle}</h1></div><p>{locale.swipeHint || 'Swipe up to discover the next story'}</p></div>{api.loading && <div className="api-note">Loading real shorts…</div>}{api.error && <div className="api-note warning">{api.error}</div>}{!api.loading && !items.length && <div className="api-note">{locale.empty}</div>}{items.map((video) => <FeedItem key={video.id} video={video} />)}</section>; }
export function ShortsFeedPage() { const { locale } = useAppStore(); const api = useApi(() => appApi.dramas().then((payload) => uniqueDramas(normalizePage(payload).list.map((item) => normalizeDrama(item, locale.code)))), [locale.code]); const items = api.data || []; return <section className="shorts-feed-page"><div className="feed-heading"><div><span>YMB SHORTS</span><h1>{locale.shortTitle}</h1></div><p>{locale.swipeHint || 'Swipe up to discover the next story'}</p></div>{api.loading && <div className="api-note">Loading real shorts…</div>}{api.error && <div className="api-note warning">{api.error}</div>}{!api.loading && !items.length && <div className="api-note">{locale.empty}</div>}{items.map((video) => <FeedItem key={video.id} video={video} />)}</section>; }