feat: Implement premium search history, add 'auto' fullscreen option, and update project version.

This commit is contained in:
kuekhaoyang
2026-02-16 11:10:09 +08:00
parent e04fe79355
commit d54039663a
9 changed files with 99 additions and 80 deletions
+4 -3
View File
@@ -5,7 +5,7 @@
*/
import { useState, useEffect, useCallback, useRef } from 'react';
import { useSearchHistoryStore } from '@/lib/store/search-history-store';
import { useSearchHistoryStoreSelector } from '@/lib/store/search-history-store';
import type { SearchHistoryItem } from '@/lib/store/search-history-store';
interface UseSearchHistoryReturn {
@@ -23,7 +23,8 @@ interface UseSearchHistoryReturn {
}
export function useSearchHistory(
onSelectHistory?: (query: string) => void
onSelectHistory?: (query: string) => void,
isPremium: boolean = false
): UseSearchHistoryReturn {
const {
searchHistory,
@@ -31,7 +32,7 @@ export function useSearchHistory(
removeFromSearchHistory,
clearSearchHistory,
getRecentSearches,
} = useSearchHistoryStore();
} = useSearchHistoryStoreSelector(isPremium);
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
const [highlightedIndex, setHighlightedIndex] = useState(-1);
+81 -68
View File
@@ -17,7 +17,7 @@ export interface SearchHistoryItem {
interface SearchHistoryStore {
searchHistory: SearchHistoryItem[];
// Actions
addToSearchHistory: (query: string, resultCount?: number) => void;
removeFromSearchHistory: (query: string) => void;
@@ -32,82 +32,95 @@ function normalizeQuery(query: string): string {
return query.trim().toLowerCase();
}
export const useSearchHistoryStore = create<SearchHistoryStore>()(
persist(
(set, get) => ({
searchHistory: [],
const createSearchHistoryStore = (name: string) =>
create<SearchHistoryStore>()(
persist(
(set, get) => ({
searchHistory: [],
addToSearchHistory: (query, resultCount) => {
const trimmedQuery = query.trim();
// Don't add empty queries
if (!trimmedQuery) return;
addToSearchHistory: (query, resultCount) => {
const trimmedQuery = query.trim();
const normalized = normalizeQuery(trimmedQuery);
const timestamp = Date.now();
// Don't add empty queries
if (!trimmedQuery) return;
set((state) => {
// Check if query already exists (case-insensitive)
const existingIndex = state.searchHistory.findIndex(
(item) => normalizeQuery(item.query) === normalized
);
const normalized = normalizeQuery(trimmedQuery);
const timestamp = Date.now();
let newHistory: SearchHistoryItem[];
set((state) => {
// Check if query already exists (case-insensitive)
const existingIndex = state.searchHistory.findIndex(
(item) => normalizeQuery(item.query) === normalized
);
if (existingIndex !== -1) {
// Update existing item and move to top
const updatedItem: SearchHistoryItem = {
query: trimmedQuery, // Keep original casing from new search
timestamp,
resultCount,
};
let newHistory: SearchHistoryItem[];
newHistory = [
updatedItem,
...state.searchHistory.filter((_, index) => index !== existingIndex),
];
} else {
// Add new item at the top
const newItem: SearchHistoryItem = {
query: trimmedQuery,
timestamp,
resultCount,
};
if (existingIndex !== -1) {
// Update existing item and move to top
const updatedItem: SearchHistoryItem = {
query: trimmedQuery, // Keep original casing from new search
timestamp,
resultCount,
};
newHistory = [newItem, ...state.searchHistory];
}
newHistory = [
updatedItem,
...state.searchHistory.filter((_, index) => index !== existingIndex),
];
} else {
// Add new item at the top
const newItem: SearchHistoryItem = {
query: trimmedQuery,
timestamp,
resultCount,
};
// Trim to max items
if (newHistory.length > MAX_HISTORY_ITEMS) {
newHistory = newHistory.slice(0, MAX_HISTORY_ITEMS);
}
newHistory = [newItem, ...state.searchHistory];
}
return { searchHistory: newHistory };
});
},
// Trim to max items
if (newHistory.length > MAX_HISTORY_ITEMS) {
newHistory = newHistory.slice(0, MAX_HISTORY_ITEMS);
}
removeFromSearchHistory: (query) => {
const normalized = normalizeQuery(query);
set((state) => ({
searchHistory: state.searchHistory.filter(
(item) => normalizeQuery(item.query) !== normalized
),
}));
},
return { searchHistory: newHistory };
});
},
clearSearchHistory: () => {
set({ searchHistory: [] });
},
removeFromSearchHistory: (query) => {
const normalized = normalizeQuery(query);
getRecentSearches: (limit = 10) => {
const history = get().searchHistory;
return history.slice(0, limit);
},
}),
{
name: 'kvideo-search-history',
version: 1,
}
)
);
set((state) => ({
searchHistory: state.searchHistory.filter(
(item) => normalizeQuery(item.query) !== normalized
),
}));
},
clearSearchHistory: () => {
set({ searchHistory: [] });
},
getRecentSearches: (limit = 10) => {
const history = get().searchHistory;
return history.slice(0, limit);
},
}),
{
name,
version: 1,
}
)
);
export const useSearchHistoryStore = createSearchHistoryStore('kvideo-search-history');
export const usePremiumSearchHistoryStore = createSearchHistoryStore('kvideo-premium-search-history');
/**
* Helper hook to get the appropriate search history store
*/
export function useSearchHistoryStoreSelector(isPremium = false) {
const normalStore = useSearchHistoryStore();
const premiumStore = usePremiumSearchHistoryStore();
return isPremium ? premiumStore : normalStore;
}