feat: add route persistence and page visibility configuration

- Persist current route to Chrome Storage, restore on popup reopen
- Add page visibility configuration to control which pages are shown
- Update StorageSchema with new storage keys (app/currentRoute, app/visiblePages)
- Add PageType type and PAGE_CONFIG for type-safe page management
- Add navigation button styles with active state and hover effects
- Use loading state to prevent UI flicker during async data load

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
雨霖铃
2026-03-21 09:46:41 +08:00
parent b56674b83b
commit 3bac87717f
4 changed files with 115 additions and 20 deletions
+70 -17
View File
@@ -1,30 +1,83 @@
import { useState } from 'react';
import { Box, Button } from '@mui/material';
import { useState, useEffect } from 'react';
import { Box } from '@mui/material';
import type { PageType } from '@/types/storage';
import { storageUtil } from '@/utils/chromeStorage';
import TimestampPage from './pages/TimestampPage';
import StorageCleanerPage from './pages/StorageCleanerPage';
import './App.css';
type PageType = 'timestamp' | 'storageCleaner';
const PAGE_CONFIG = {
timestamp: { label: '时间戳', defaultVisible: true },
storageCleaner: { label: '存储清理', defaultVisible: true },
} as const satisfies Record<PageType, { label: string; defaultVisible: boolean }>;
function App() {
const [currentPage, setCurrentPage] = useState<PageType>('timestamp');
const [visiblePages, setVisiblePages] = useState<PageType[]>(['timestamp', 'storageCleaner']);
const [isLoaded, setIsLoaded] = useState(false);
useEffect(() => {
loadInitialData();
}, []);
useEffect(() => {
if (isLoaded) {
storageUtil.set('app/currentRoute', currentPage);
}
}, [currentPage, isLoaded]);
const loadInitialData = async () => {
try {
const [savedRoute, savedVisiblePages] = await Promise.all([
storageUtil.get('app/currentRoute', 'timestamp'),
storageUtil.get('app/visiblePages', ['timestamp', 'storageCleaner'] as PageType[]),
]);
if (savedRoute) {
setCurrentPage(savedRoute);
}
if (savedVisiblePages) {
setVisiblePages(savedVisiblePages);
}
} catch (error) {
console.error('Failed to load initial data:', error);
} finally {
setIsLoaded(true);
}
};
const handlePageChange = (page: PageType) => {
setCurrentPage(page);
};
const NavButton = ({ pageKey }: { pageKey: PageType }) => {
const config = PAGE_CONFIG[pageKey];
if (!config) return null;
return (
<Box key={pageKey}>
<button
className={currentPage === pageKey ? 'nav-button active' : 'nav-button'}
onClick={() => handlePageChange(pageKey)}
>
{config.label}
</button>
</Box>
);
};
if (!isLoaded) {
return <div className="app">Loading...</div>;
}
return (
<div className="app">
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 2 }}>
<Button
variant={currentPage === 'timestamp' ? 'contained' : 'outlined'}
onClick={() => setCurrentPage('timestamp')}
>
</Button>
<Button
variant={currentPage === 'storageCleaner' ? 'contained' : 'outlined'}
onClick={() => setCurrentPage('storageCleaner')}
sx={{ ml: 1 }}
>
</Button>
<Box className="nav-container">
{(Object.keys(PAGE_CONFIG) as PageType[])
.filter((key) => visiblePages.includes(key))
.map((key) => <NavButton key={key} pageKey={key} />)}
</Box>
{currentPage === 'timestamp' && <TimestampPage />}
{currentPage === 'storageCleaner' && <StorageCleanerPage />}