file size limits, keep active filters in session
This commit is contained in:
@@ -13,6 +13,7 @@ import SidebarLayout from '../../components/library/SidebarLayout';
|
||||
import ToolbarLayout from '../../components/library/ToolbarLayout';
|
||||
import MinimalLayout from '../../components/library/MinimalLayout';
|
||||
import { useLibraryLayout } from '../../hooks/useLibraryLayout';
|
||||
import { useLibraryFilters, clearLibraryFilters } from '../../hooks/useLibraryFilters';
|
||||
|
||||
type ViewMode = 'grid' | 'list';
|
||||
type SortOption = 'createdAt' | 'title' | 'authorName' | 'rating' | 'wordCount' | 'lastReadAt';
|
||||
@@ -26,17 +27,21 @@ export default function LibraryContent() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [searchLoading, setSearchLoading] = useState(false);
|
||||
const [randomLoading, setRandomLoading] = useState(false);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [selectedTags, setSelectedTags] = useState<string[]>([]);
|
||||
const [viewMode, setViewMode] = useState<ViewMode>('list');
|
||||
const [sortOption, setSortOption] = useState<SortOption>('lastReadAt');
|
||||
const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('desc');
|
||||
|
||||
// Persisted filter state (survives navigation within session)
|
||||
const [searchQuery, setSearchQuery] = useLibraryFilters<string>('searchQuery', '');
|
||||
const [selectedTags, setSelectedTags] = useLibraryFilters<string[]>('selectedTags', []);
|
||||
const [viewMode, setViewMode] = useLibraryFilters<ViewMode>('viewMode', 'list');
|
||||
const [sortOption, setSortOption] = useLibraryFilters<SortOption>('sortOption', 'lastReadAt');
|
||||
const [sortDirection, setSortDirection] = useLibraryFilters<'asc' | 'desc'>('sortDirection', 'desc');
|
||||
const [advancedFilters, setAdvancedFilters] = useLibraryFilters<AdvancedFilters>('advancedFilters', {});
|
||||
|
||||
// Non-persisted state (resets on navigation)
|
||||
const [page, setPage] = useState(0);
|
||||
const [totalPages, setTotalPages] = useState(1);
|
||||
const [totalElements, setTotalElements] = useState(0);
|
||||
const [refreshTrigger, setRefreshTrigger] = useState(0);
|
||||
const [urlParamsProcessed, setUrlParamsProcessed] = useState(false);
|
||||
const [advancedFilters, setAdvancedFilters] = useState<AdvancedFilters>({});
|
||||
|
||||
// Initialize filters from URL parameters
|
||||
useEffect(() => {
|
||||
@@ -209,11 +214,15 @@ export default function LibraryContent() {
|
||||
}
|
||||
};
|
||||
|
||||
const clearFilters = () => {
|
||||
const handleClearFilters = () => {
|
||||
// Clear state
|
||||
setSearchQuery('');
|
||||
setSelectedTags([]);
|
||||
setAdvancedFilters({});
|
||||
setPage(0);
|
||||
// Clear sessionStorage
|
||||
clearLibraryFilters();
|
||||
// Trigger refresh
|
||||
setRefreshTrigger(prev => prev + 1);
|
||||
};
|
||||
|
||||
@@ -266,7 +275,7 @@ export default function LibraryContent() {
|
||||
onSortDirectionToggle: handleSortDirectionToggle,
|
||||
onAdvancedFiltersChange: handleAdvancedFiltersChange,
|
||||
onRandomStory: handleRandomStory,
|
||||
onClearFilters: clearFilters,
|
||||
onClearFilters: handleClearFilters,
|
||||
};
|
||||
|
||||
const renderContent = () => {
|
||||
@@ -280,7 +289,7 @@ export default function LibraryContent() {
|
||||
}
|
||||
</p>
|
||||
{searchQuery || selectedTags.length > 0 || Object.values(advancedFilters).some(v => v !== undefined && v !== '' && v !== 'all' && v !== false) ? (
|
||||
<Button variant="ghost" onClick={clearFilters}>
|
||||
<Button variant="ghost" onClick={handleClearFilters}>
|
||||
Clear Filters
|
||||
</Button>
|
||||
) : (
|
||||
|
||||
68
frontend/src/hooks/useLibraryFilters.ts
Normal file
68
frontend/src/hooks/useLibraryFilters.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { useState, useEffect, Dispatch, SetStateAction } from 'react';
|
||||
|
||||
/**
|
||||
* Custom hook for persisting library filter state in sessionStorage.
|
||||
* Filters are preserved during the browser session but cleared when the tab is closed.
|
||||
*
|
||||
* @param key - Unique identifier for the filter value in sessionStorage
|
||||
* @param defaultValue - Default value if no stored value exists
|
||||
* @returns Tuple of [value, setValue] similar to useState
|
||||
*/
|
||||
export function useLibraryFilters<T>(
|
||||
key: string,
|
||||
defaultValue: T
|
||||
): [T, Dispatch<SetStateAction<T>>] {
|
||||
// Initialize state from sessionStorage or use default value
|
||||
const [value, setValue] = useState<T>(() => {
|
||||
// SSR safety: sessionStorage only available in browser
|
||||
if (typeof window === 'undefined') {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
try {
|
||||
const stored = sessionStorage.getItem(`library_filter_${key}`);
|
||||
if (stored === null) {
|
||||
return defaultValue;
|
||||
}
|
||||
return JSON.parse(stored) as T;
|
||||
} catch (error) {
|
||||
console.warn(`Failed to parse sessionStorage value for library_filter_${key}:`, error);
|
||||
return defaultValue;
|
||||
}
|
||||
});
|
||||
|
||||
// Persist to sessionStorage whenever value changes
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
try {
|
||||
sessionStorage.setItem(`library_filter_${key}`, JSON.stringify(value));
|
||||
} catch (error) {
|
||||
console.warn(`Failed to save to sessionStorage for library_filter_${key}:`, error);
|
||||
}
|
||||
}, [key, value]);
|
||||
|
||||
return [value, setValue];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all library filters from sessionStorage.
|
||||
* Useful for "Clear Filters" button or when switching libraries.
|
||||
*/
|
||||
export function clearLibraryFilters(): void {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
try {
|
||||
// Get all sessionStorage keys
|
||||
const keys = Object.keys(sessionStorage);
|
||||
|
||||
// Remove only library filter keys
|
||||
keys.forEach(key => {
|
||||
if (key.startsWith('library_filter_')) {
|
||||
sessionStorage.removeItem(key);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.warn('Failed to clear library filters from sessionStorage:', error);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user