diff --git a/app/page.tsx b/app/page.tsx
index eba8c0f..27d40ff 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,84 +1,26 @@
'use client';
-import { useState, useRef, useEffect, Suspense } from 'react';
-import { useRouter, useSearchParams } from 'next/navigation';
+import { Suspense } from 'react';
import { SearchForm } from '@/components/search/SearchForm';
import { NoResults } from '@/components/search/NoResults';
import { PopularFeatures } from '@/components/home/PopularFeatures';
import { WatchHistorySidebar } from '@/components/history/WatchHistorySidebar';
-import { useSearchCache } from '@/lib/hooks/useSearchCache';
-import { useParallelSearch } from '@/lib/hooks/useParallelSearch';
-import { settingsStore } from '@/lib/store/settings-store';
import { Navbar } from '@/components/layout/Navbar';
import { SearchResults } from '@/components/home/SearchResults';
+import { useHomePage } from '@/lib/hooks/useHomePage';
function HomePage() {
- const router = useRouter();
- const searchParams = useSearchParams();
- const { loadFromCache, saveToCache } = useSearchCache();
- const hasLoadedCache = useRef(false);
-
- const [query, setQuery] = useState('');
- const [hasSearched, setHasSearched] = useState(false);
- const [currentSortBy, setCurrentSortBy] = useState('default');
-
- // Search stream hook
const {
+ query,
+ hasSearched,
loading,
results,
availableSources,
completedSources,
totalSources,
- performSearch,
- resetSearch,
- loadCachedResults,
- } = useParallelSearch(
- saveToCache,
- (q: string) => router.replace(`/?q=${encodeURIComponent(q)}`, { scroll: false })
- );
-
- // Load sort preference on mount
- useEffect(() => {
- const settings = settingsStore.getSettings();
- setCurrentSortBy(settings.sortBy);
- }, []);
-
- // Load cached results on mount
- useEffect(() => {
- if (hasLoadedCache.current) return;
- hasLoadedCache.current = true;
-
- const urlQuery = searchParams.get('q');
- const cached = loadFromCache();
-
- if (urlQuery) {
- setQuery(urlQuery);
- if (cached && cached.query === urlQuery && cached.results.length > 0) {
-
- setHasSearched(true);
- loadCachedResults(cached.results, cached.availableSources);
- } else {
-
- setTimeout(() => handleSearch(urlQuery), 100);
- }
- }
- }, [searchParams, loadFromCache, loadCachedResults]);
-
- const handleSearch = (searchQuery: string) => {
- setQuery(searchQuery);
- setHasSearched(true);
- const settings = settingsStore.getSettings();
- // Filter enabled sources
- const enabledSources = settings.sources.filter(s => s.enabled);
- performSearch(searchQuery, enabledSources, currentSortBy as any);
- };
-
- const handleReset = () => {
- setHasSearched(false);
- setQuery('');
- resetSearch();
- router.replace('/', { scroll: false });
- };
+ handleSearch,
+ handleReset,
+ } = useHomePage();
return (
diff --git a/components/player/PlayerNavbar.tsx b/components/player/PlayerNavbar.tsx
index b168f97..3611590 100644
--- a/components/player/PlayerNavbar.tsx
+++ b/components/player/PlayerNavbar.tsx
@@ -1,4 +1,5 @@
import { useRouter } from 'next/navigation';
+import Link from 'next/link';
import Image from 'next/image';
import { Button } from '@/components/ui/Button';
import { ThemeSwitcher } from '@/components/ThemeSwitcher';
@@ -34,7 +35,16 @@ export function PlayerNavbar() {
返回
-
diff --git a/components/settings/SortSettings.tsx b/components/settings/SortSettings.tsx
index e20cdc2..5467332 100644
--- a/components/settings/SortSettings.tsx
+++ b/components/settings/SortSettings.tsx
@@ -1,4 +1,5 @@
-import { sortOptions, type SortOption } from '@/lib/store/settings-store';
+import { type SortOption } from '@/lib/store/settings-store';
+import { sortOptions } from '@/lib/store/settings-helpers';
interface SortSettingsProps {
sortBy: SortOption;
diff --git a/lib/hooks/useHomePage.ts b/lib/hooks/useHomePage.ts
new file mode 100644
index 0000000..95f0a5e
--- /dev/null
+++ b/lib/hooks/useHomePage.ts
@@ -0,0 +1,101 @@
+import { useState, useRef, useEffect } from 'react';
+import { useRouter, useSearchParams } from 'next/navigation';
+import { useSearchCache } from '@/lib/hooks/useSearchCache';
+import { useParallelSearch } from '@/lib/hooks/useParallelSearch';
+import { settingsStore } from '@/lib/store/settings-store';
+
+export function useHomePage() {
+ const router = useRouter();
+ const searchParams = useSearchParams();
+ const { loadFromCache, saveToCache } = useSearchCache();
+ const hasLoadedCache = useRef(false);
+
+ const [query, setQuery] = useState('');
+ const [hasSearched, setHasSearched] = useState(false);
+ const [currentSortBy, setCurrentSortBy] = useState('default');
+
+ // Search stream hook
+ const {
+ loading,
+ results,
+ availableSources,
+ completedSources,
+ totalSources,
+ performSearch,
+ resetSearch,
+ loadCachedResults,
+ applySorting,
+ } = useParallelSearch(
+ saveToCache,
+ (q: string) => router.replace(`/?q=${encodeURIComponent(q)}`, { scroll: false })
+ );
+
+ // Re-sort results when sort preference changes
+ useEffect(() => {
+ if (hasSearched && results.length > 0) {
+ applySorting(currentSortBy as any);
+ }
+ }, [currentSortBy, applySorting, hasSearched, results.length]);
+
+ // Load sort preference on mount and subscribe to changes
+ useEffect(() => {
+ const updateSettings = () => {
+ const settings = settingsStore.getSettings();
+ setCurrentSortBy(settings.sortBy);
+ };
+
+ // Initial load
+ updateSettings();
+
+ // Subscribe to changes
+ const unsubscribe = settingsStore.subscribe(updateSettings);
+ return () => unsubscribe();
+ }, []);
+
+ // Load cached results on mount
+ useEffect(() => {
+ if (hasLoadedCache.current) return;
+ hasLoadedCache.current = true;
+
+ const urlQuery = searchParams.get('q');
+ const cached = loadFromCache();
+
+ if (urlQuery) {
+ setQuery(urlQuery);
+ if (cached && cached.query === urlQuery && cached.results.length > 0) {
+ setHasSearched(true);
+ loadCachedResults(cached.results, cached.availableSources);
+ } else {
+ setTimeout(() => handleSearch(urlQuery), 100);
+ }
+ }
+ }, [searchParams, loadFromCache, loadCachedResults]);
+
+ const handleSearch = (searchQuery: string) => {
+ setQuery(searchQuery);
+ setHasSearched(true);
+ const settings = settingsStore.getSettings();
+ // Filter enabled sources
+ const enabledSources = settings.sources.filter(s => s.enabled);
+ performSearch(searchQuery, enabledSources, currentSortBy as any);
+ };
+
+ const handleReset = () => {
+ setHasSearched(false);
+ setQuery('');
+ resetSearch();
+ router.replace('/', { scroll: false });
+ };
+
+ return {
+ query,
+ hasSearched,
+ loading,
+ results,
+ availableSources,
+ completedSources,
+ totalSources,
+ handleSearch,
+ handleReset,
+ };
+}
diff --git a/lib/store/settings-helpers.ts b/lib/store/settings-helpers.ts
new file mode 100644
index 0000000..2f8663d
--- /dev/null
+++ b/lib/store/settings-helpers.ts
@@ -0,0 +1,56 @@
+import type { AppSettings } from './settings-store';
+
+export const SEARCH_HISTORY_KEY = 'kvideo-search-history';
+export const WATCH_HISTORY_KEY = 'kvideo-watch-history';
+
+export const sortOptions = {
+ 'default': '默认排序',
+ 'relevance': '按相关性',
+ 'latency-asc': '延迟低到高',
+ 'date-desc': '发布时间(新到旧)',
+ 'date-asc': '发布时间(旧到新)',
+ 'rating-desc': '按评分(高到低)',
+ 'name-asc': '按名称(A-Z)',
+ 'name-desc': '按名称(Z-A)',
+} as const;
+
+export function exportSettings(settings: AppSettings, includeHistory: boolean = true): string {
+ const exportData: Record = {
+ settings,
+ };
+
+ if (includeHistory && typeof window !== 'undefined') {
+ const searchHistory = localStorage.getItem(SEARCH_HISTORY_KEY);
+ const watchHistory = localStorage.getItem(WATCH_HISTORY_KEY);
+
+ if (searchHistory) exportData.searchHistory = JSON.parse(searchHistory);
+ if (watchHistory) exportData.watchHistory = JSON.parse(watchHistory);
+ }
+
+ return JSON.stringify(exportData, null, 2);
+}
+
+export function importSettings(
+ jsonString: string,
+ saveSettings: (settings: AppSettings) => void
+): boolean {
+ try {
+ const data = JSON.parse(jsonString);
+
+ if (data.settings) {
+ saveSettings(data.settings);
+ }
+
+ if (data.searchHistory && typeof window !== 'undefined') {
+ localStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(data.searchHistory));
+ }
+
+ if (data.watchHistory && typeof window !== 'undefined') {
+ localStorage.setItem(WATCH_HISTORY_KEY, JSON.stringify(data.watchHistory));
+ }
+
+ return true;
+ } catch {
+ return false;
+ }
+}
diff --git a/lib/store/settings-store.ts b/lib/store/settings-store.ts
index 89e00d8..a37ec4c 100644
--- a/lib/store/settings-store.ts
+++ b/lib/store/settings-store.ts
@@ -15,27 +15,16 @@ export type SortOption =
| 'name-asc'
| 'name-desc';
-interface AppSettings {
+export interface AppSettings {
sources: VideoSource[];
sortBy: SortOption;
searchHistory: boolean;
watchHistory: boolean;
}
-const SETTINGS_KEY = 'kvideo-settings';
-const SEARCH_HISTORY_KEY = 'kvideo-search-history';
-const WATCH_HISTORY_KEY = 'kvideo-watch-history';
+import { exportSettings, importSettings, SEARCH_HISTORY_KEY, WATCH_HISTORY_KEY } from './settings-helpers';
-export const sortOptions: Record = {
- 'default': '默认排序',
- 'relevance': '按相关性',
- 'latency-asc': '延迟低到高',
- 'date-desc': '发布时间(新到旧)',
- 'date-asc': '发布时间(旧到新)',
- 'rating-desc': '按评分(高到低)',
- 'name-asc': '按名称(A-Z)',
- 'name-desc': '按名称(Z-A)',
-};
+const SETTINGS_KEY = 'kvideo-settings';
export const getDefaultSources = (): VideoSource[] => DEFAULT_SOURCES;
@@ -79,49 +68,32 @@ export const settingsStore = {
}
},
+ listeners: new Set<() => void>(),
+
+ subscribe(listener: () => void): () => void {
+ this.listeners.add(listener);
+ return () => {
+ this.listeners.delete(listener);
+ };
+ },
+
+ notifyListeners(): void {
+ this.listeners.forEach((listener) => listener());
+ },
+
saveSettings(settings: AppSettings): void {
if (typeof window !== 'undefined') {
localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
+ this.notifyListeners();
}
},
exportSettings(includeHistory: boolean = true): string {
- const settings = this.getSettings();
- const exportData: Record = {
- settings,
- };
-
- if (includeHistory && typeof window !== 'undefined') {
- const searchHistory = localStorage.getItem(SEARCH_HISTORY_KEY);
- const watchHistory = localStorage.getItem(WATCH_HISTORY_KEY);
-
- if (searchHistory) exportData.searchHistory = JSON.parse(searchHistory);
- if (watchHistory) exportData.watchHistory = JSON.parse(watchHistory);
- }
-
- return JSON.stringify(exportData, null, 2);
+ return exportSettings(this.getSettings(), includeHistory);
},
importSettings(jsonString: string): boolean {
- try {
- const data = JSON.parse(jsonString);
-
- if (data.settings) {
- this.saveSettings(data.settings);
- }
-
- if (data.searchHistory && typeof window !== 'undefined') {
- localStorage.setItem(SEARCH_HISTORY_KEY, JSON.stringify(data.searchHistory));
- }
-
- if (data.watchHistory && typeof window !== 'undefined') {
- localStorage.setItem(WATCH_HISTORY_KEY, JSON.stringify(data.watchHistory));
- }
-
- return true;
- } catch {
- return false;
- }
+ return importSettings(jsonString, (s) => this.saveSettings(s));
},
resetToDefaults(): void {