From 63c8fe805e3ecf2c7221b086ea61e3be8e9a48d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=9B=A8=E9=9C=96=E9=93=83?= Date: Fri, 20 Mar 2026 01:09:36 +0800 Subject: [PATCH] feat: add StorageCleanerPage component --- .../popup/pages/StorageCleanerPage.tsx | 328 ++++++++++++++++++ 1 file changed, 328 insertions(+) create mode 100644 entrypoints/popup/pages/StorageCleanerPage.tsx diff --git a/entrypoints/popup/pages/StorageCleanerPage.tsx b/entrypoints/popup/pages/StorageCleanerPage.tsx new file mode 100644 index 0000000..04ed851 --- /dev/null +++ b/entrypoints/popup/pages/StorageCleanerPage.tsx @@ -0,0 +1,328 @@ +import { useState, useEffect, useCallback } from 'react'; +import { + Paper, + Typography, + Box, + Checkbox, + Button, + FormControlLabel, + Alert, + Snackbar, +} from '@mui/material'; +import RefreshIcon from '@mui/icons-material/Refresh'; +import WarningIcon from '@mui/icons-material/Warning'; +import { storageUtil } from '@/utils/chromeStorage'; +import type { StorageCleanerOptions, CleaningResult, StorageCleanerPreferences } from 'types/storage'; +import { + getCurrentTab, + isRestrictedUrl, + clearStorage, + formatCleaningResult, +} from '@/utils/storageCleaner'; + +const DEFAULT_OPTIONS: StorageCleanerOptions = { + localStorage: true, + sessionStorage: true, + indexedDB: true, + cookies: true, + cacheStorage: true, + serviceWorkers: true, +}; + +const DEFAULT_PREFERENCES: StorageCleanerPreferences = { + autoRefresh: true, + selectedTypes: DEFAULT_OPTIONS, +}; + +export default function StorageCleanerPage() { + const [domain, setDomain] = useState(''); + const [error, setError] = useState(''); + const [options, setOptions] = useState(DEFAULT_OPTIONS); + const [autoRefresh, setAutoRefresh] = useState(true); + const [loading, setLoading] = useState(false); + const [result, setResult] = useState(null); + const [showConfirm, setShowConfirm] = useState(false); + const [snackbar, setSnackbar] = useState<{ open: boolean; message: string }>({ + open: false, + message: '', + }); + + // Load tab info and user preferences + useEffect(() => { + const loadInfo = async () => { + const tab = await getCurrentTab(); + + if (!tab || !tab.url) { + setError('无法获取当前标签页'); + return; + } + + if (isRestrictedUrl(tab.url)) { + setError('存储清理功能不支持此页面'); + return; + } + + setDomain(new URL(tab.url).hostname); + + // Load user preferences + const prefs = await storageUtil.get( + 'storageCleaner/preferences', + DEFAULT_PREFERENCES, + ); + setAutoRefresh(prefs?.autoRefresh ?? DEFAULT_PREFERENCES.autoRefresh); + setOptions(prefs?.selectedTypes ?? DEFAULT_PREFERENCES.selectedTypes); + }; + + loadInfo(); + }, []); + + const handleOptionChange = useCallback((key: keyof StorageCleanerOptions) => { + setOptions((prev) => ({ ...prev, [key]: !prev[key] })); + }, []); + + const handleClean = useCallback(async () => { + const tab = await getCurrentTab(); + + if (!tab || !tab.id || !tab.url) { + setSnackbar({ open: true, message: '无法获取当前标签页' }); + return; + } + + setLoading(true); + + try { + const cleaningResult = await clearStorage(tab.id, tab.url, options); + setResult(cleaningResult); + + // Save user preferences + await storageUtil.set('storageCleaner/preferences', { + autoRefresh, + selectedTypes: options, + }); + + // Auto refresh if enabled + if (autoRefresh && cleaningResult.success && tab.id !== undefined) { + setSnackbar({ open: true, message: '页面即将刷新,Popup 将关闭' }); + setTimeout(() => { + chrome.tabs.reload(tab.id!); + }, 1500); + } + } catch (err) { + setSnackbar({ open: true, message: `清理失败: ${String(err)}` }); + } finally { + setLoading(false); + setShowConfirm(false); + } + }, [options, autoRefresh]); + + const handleRefresh = useCallback(async () => { + const tab = await getCurrentTab(); + if (tab?.id !== undefined) { + setSnackbar({ open: true, message: '页面即将刷新,Popup 将关闭' }); + setTimeout(() => { + chrome.tabs.reload(tab.id!); + }, 1500); + } + }, []); + + if (error) { + return ( + + }> + {error} + + + ); + } + + return ( + + {/* Header */} + + + 存储清理 + + + 当前页面: {domain || '加载中...'} + + + + {/* Storage Type Options */} + + + 选择要清理的存储类型: + + + handleOptionChange('localStorage')} + /> + } + label="localStorage" + /> + handleOptionChange('sessionStorage')} + /> + } + label="sessionStorage" + /> + handleOptionChange('indexedDB')} + /> + } + label="IndexedDB" + /> + handleOptionChange('cookies')} + /> + } + label="Cookies" + /> + handleOptionChange('cacheStorage')} + /> + } + label="Cache Storage" + /> + handleOptionChange('serviceWorkers')} + /> + } + label="Service Workers" + /> + + + + {/* Auto Refresh Option */} + + setAutoRefresh(e.target.checked)} + /> + } + label="清理完成后自动刷新页面" + /> + + + {/* Action Buttons */} + + + + + {/* Result Display */} + {result && ( + + + {result.success ? formatCleaningResult(result) : result.error || '清理失败'} + + {!autoRefresh && result.success && ( + + )} + + )} + + {/* Confirmation Dialog */} + {showConfirm && ( + + 确认清理 + + 将清理以下存储类型: + + + {options.localStorage && ( + - localStorage + )} + {options.sessionStorage && ( + - sessionStorage + )} + {options.indexedDB && - IndexedDB} + {options.cookies && - Cookies} + {options.cacheStorage && ( + - Cache Storage + )} + {options.serviceWorkers && ( + - Service Workers + )} + + + 此操作不可撤销。 + + + + + + + )} + + {/* Snackbar */} + setSnackbar({ ...snackbar, open: false })} + > + + {snackbar.message} + + + + ); +}