From 38a3e204279233d241c31d352da19d8a396174a3 Mon Sep 17 00:00:00 2001 From: moxi Date: Sun, 4 Jan 2026 00:45:34 +0800 Subject: [PATCH] feat(quota): enhance QuotaSection with improved view mode handling and refresh functionality - Introduced effective view mode logic to manage 'paged' and 'all' views based on file count. - Added a warning for too many files when in 'all' view, prompting users to switch to 'paged'. - Updated refresh button to handle loading states more effectively and provide clearer user feedback. - Enhanced UI with new translations for view modes and refresh actions. - Adjusted styles for better alignment and spacing in the view mode toggle and refresh button. --- src/components/quota/QuotaSection.tsx | 70 ++++++++++++++++++++------- src/i18n/locales/en.json | 6 ++- src/i18n/locales/zh-CN.json | 5 +- src/pages/QuotaPage.module.scss | 15 ++++-- 4 files changed, 71 insertions(+), 25 deletions(-) diff --git a/src/components/quota/QuotaSection.tsx b/src/components/quota/QuotaSection.tsx index dc3d476..23b6983 100644 --- a/src/components/quota/QuotaSection.tsx +++ b/src/components/quota/QuotaSection.tsx @@ -2,11 +2,12 @@ * Generic quota section component. */ -import { useCallback, useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Card } from '@/components/ui/Card'; import { Button } from '@/components/ui/Button'; import { EmptyState } from '@/components/ui/EmptyState'; +import { triggerHeaderRefresh } from '@/hooks/useHeaderRefresh'; import { useQuotaStore, useThemeStore } from '@/stores'; import type { AuthFileItem, ResolvedTheme } from '@/types'; import { QuotaCard } from './QuotaCard'; @@ -116,6 +117,8 @@ export function QuotaSection({ files, config ]); + const showAllAllowed = filteredFiles.length <= MAX_SHOW_ALL_THRESHOLD; + const effectiveViewMode: ViewMode = viewMode === 'all' && !showAllAllowed ? 'paged' : viewMode; const { pageSize, @@ -129,25 +132,55 @@ export function QuotaSection({ setLoading } = useQuotaPagination(filteredFiles); + useEffect(() => { + if (showAllAllowed) return; + if (viewMode !== 'all') return; + + let cancelled = false; + queueMicrotask(() => { + if (cancelled) return; + setViewMode('paged'); + setShowTooManyWarning(true); + }); + + return () => { + cancelled = true; + }; + }, [showAllAllowed, viewMode]); + // Update page size based on view mode and columns useEffect(() => { - if (viewMode === 'all') { + if (effectiveViewMode === 'all') { setPageSize(Math.max(1, filteredFiles.length)); } else { // Paged mode: 3 rows * columns setPageSize(columns * 3); } - }, [viewMode, columns, filteredFiles.length, setPageSize]); + }, [effectiveViewMode, columns, filteredFiles.length, setPageSize]); const { quota, loadQuota } = useQuotaLoader(config); + const pendingQuotaRefreshRef = useRef(false); + const prevFilesLoadingRef = useRef(loading); + const handleRefresh = useCallback(() => { - if (viewMode === 'all') { - loadQuota(filteredFiles, 'all', setLoading); - } else { - loadQuota(pageItems, 'page', setLoading); - } - }, [loadQuota, filteredFiles, pageItems, viewMode, setLoading]); + pendingQuotaRefreshRef.current = true; + void triggerHeaderRefresh(); + }, []); + + useEffect(() => { + const wasLoading = prevFilesLoadingRef.current; + prevFilesLoadingRef.current = loading; + + if (!pendingQuotaRefreshRef.current) return; + if (loading) return; + if (!wasLoading) return; + + pendingQuotaRefreshRef.current = false; + const scope = effectiveViewMode === 'all' ? 'all' : 'page'; + const targets = effectiveViewMode === 'all' ? filteredFiles : pageItems; + loadQuota(targets, scope, setLoading); + }, [loading, effectiveViewMode, filteredFiles, pageItems, loadQuota, setLoading]); useEffect(() => { if (loading) return; @@ -185,14 +218,14 @@ export function QuotaSection({
} @@ -239,7 +275,7 @@ export function QuotaSection({ /> ))} - {filteredFiles.length > pageSize && viewMode === 'paged' && ( + {filteredFiles.length > pageSize && effectiveViewMode === 'paged' && (