11/**
2- * Generic quota section component.
2+ * 通用 quota 分区组件。
33 */
44
5- import { useCallback , useEffect , useMemo , useRef , useState } from 'react' ;
5+ import { useCallback , useEffect , useMemo , useState } from 'react' ;
66import { useTranslation } from 'react-i18next' ;
77import { Card } from '@/components/ui/Card' ;
88import { Button } from '@/components/ui/Button' ;
99import { EmptyState } from '@/components/ui/EmptyState' ;
10- import { triggerHeaderRefresh } from '@/hooks/useHeaderRefresh' ;
1110import { useNotificationStore , useQuotaStore , useThemeStore } from '@/stores' ;
1211import type { AuthFileItem , ResolvedTheme } from '@/types' ;
13- import { getStatusFromError } from '@/utils/quota' ;
1412import { QuotaCard } from './QuotaCard' ;
1513import type { QuotaStatusState } from './QuotaCard' ;
1614import { useQuotaLoader } from './useQuotaLoader' ;
@@ -111,8 +109,7 @@ export function QuotaSection<TState extends QuotaStatusState, TData>({
111109 Record < string , TState >
112110 > ;
113111
114- /* Removed useRef */
115- const [ columns , gridRef ] = useGridColumns ( 380 ) ; // Min card width 380px matches SCSS
112+ const [ columns , gridRef ] = useGridColumns ( 380 ) ;
116113 const [ viewMode , setViewMode ] = useState < ViewMode > ( 'paged' ) ;
117114 const [ showTooManyWarning , setShowTooManyWarning ] = useState ( false ) ;
118115
@@ -151,40 +148,25 @@ export function QuotaSection<TState extends QuotaStatusState, TData>({
151148 } ;
152149 } , [ showAllAllowed , viewMode ] ) ;
153150
154- // Update page size based on view mode and columns
155151 useEffect ( ( ) => {
156152 if ( effectiveViewMode === 'all' ) {
157153 setPageSize ( Math . max ( 1 , filteredFiles . length ) ) ;
158154 } else {
159- // Paged mode: 3 rows * columns, capped to avoid oversized pages.
160155 setPageSize ( Math . min ( columns * 3 , MAX_ITEMS_PER_PAGE ) ) ;
161156 }
162157 } , [ effectiveViewMode , columns , filteredFiles . length , setPageSize ] ) ;
163158
164159 const { quota, loadQuota } = useQuotaLoader ( config ) ;
165160
166- const pendingQuotaRefreshRef = useRef ( false ) ;
167- const prevFilesLoadingRef = useRef ( loading ) ;
168-
169- const handleRefresh = useCallback ( ( ) => {
170- pendingQuotaRefreshRef . current = true ;
171- void triggerHeaderRefresh ( ) ;
172- } , [ ] ) ;
173-
174- useEffect ( ( ) => {
175- const wasLoading = prevFilesLoadingRef . current ;
176- prevFilesLoadingRef . current = loading ;
177-
178- if ( ! pendingQuotaRefreshRef . current ) return ;
179- if ( loading ) return ;
180- if ( ! wasLoading ) return ;
181-
182- pendingQuotaRefreshRef . current = false ;
183- const scope = effectiveViewMode === 'all' ? 'all' : 'page' ;
184- const targets = effectiveViewMode === 'all' ? filteredFiles : pageItems ;
185- if ( targets . length === 0 ) return ;
186- loadQuota ( targets , scope , setLoading ) ;
187- } , [ loading , effectiveViewMode , filteredFiles , pageItems , loadQuota , setLoading ] ) ;
161+ const refreshScope = effectiveViewMode === 'all' ? 'all' : 'page' ;
162+ const refreshTargets = useMemo (
163+ ( ) => ( refreshScope === 'all' ? filteredFiles : pageItems ) ,
164+ [ filteredFiles , pageItems , refreshScope ]
165+ ) ;
166+ const refreshButtonLabel =
167+ refreshScope === 'all'
168+ ? t ( 'quota_management.refresh_all_credentials' )
169+ : t ( 'quota_management.refresh_current_page_credentials' ) ;
188170
189171 useEffect ( ( ) => {
190172 if ( loading ) return ;
@@ -204,37 +186,46 @@ export function QuotaSection<TState extends QuotaStatusState, TData>({
204186 } ) ;
205187 } , [ filteredFiles , loading , setQuota ] ) ;
206188
189+ const handleRefresh = useCallback ( async ( ) => {
190+ if ( disabled || refreshTargets . length === 0 ) return ;
191+
192+ try {
193+ await loadQuota ( refreshTargets , refreshScope , setLoading ) ;
194+ showNotification (
195+ t ( 'quota_management.refresh_selection_success' , { count : refreshTargets . length } ) ,
196+ 'success'
197+ ) ;
198+ } catch ( err : unknown ) {
199+ const message = err instanceof Error ? err . message : t ( 'common.unknown_error' ) ;
200+ showNotification ( t ( 'quota_management.refresh_selection_failed' , { message } ) , 'error' ) ;
201+ }
202+ } , [
203+ disabled ,
204+ loadQuota ,
205+ refreshScope ,
206+ refreshTargets ,
207+ setLoading ,
208+ showNotification ,
209+ t
210+ ] ) ;
211+
207212 const refreshQuotaForFile = useCallback (
208213 async ( file : AuthFileItem ) => {
209214 if ( disabled || file . disabled ) return ;
210215 if ( quota [ file . name ] ?. status === 'loading' ) return ;
211216
212- setQuota ( ( prev ) => ( {
213- ...prev ,
214- [ file . name ] : config . buildLoadingState ( )
215- } ) ) ;
216-
217217 try {
218- const data = await config . fetchQuota ( file , t ) ;
219- setQuota ( ( prev ) => ( {
220- ...prev ,
221- [ file . name ] : config . buildSuccessState ( data )
222- } ) ) ;
218+ await loadQuota ( [ file ] , 'page' , setLoading ) ;
223219 showNotification ( t ( 'auth_files.quota_refresh_success' , { name : file . name } ) , 'success' ) ;
224220 } catch ( err : unknown ) {
225221 const message = err instanceof Error ? err . message : t ( 'common.unknown_error' ) ;
226- const status = getStatusFromError ( err ) ;
227- setQuota ( ( prev ) => ( {
228- ...prev ,
229- [ file . name ] : config . buildErrorState ( message , status )
230- } ) ) ;
231222 showNotification (
232223 t ( 'auth_files.quota_refresh_failed' , { name : file . name , message } ) ,
233224 'error'
234225 ) ;
235226 }
236227 } ,
237- [ config , disabled , quota , setQuota , showNotification , t ]
228+ [ disabled , loadQuota , quota , setLoading , showNotification , t ]
238229 ) ;
239230
240231 const titleNode = (
@@ -287,14 +278,14 @@ export function QuotaSection<TState extends QuotaStatusState, TData>({
287278 variant = "secondary"
288279 size = "sm"
289280 className = { styles . refreshAllButton }
290- onClick = { handleRefresh }
291- disabled = { disabled || isRefreshing }
281+ onClick = { ( ) => void handleRefresh ( ) }
282+ disabled = { disabled || isRefreshing || refreshTargets . length === 0 }
292283 loading = { isRefreshing }
293- title = { t ( 'quota_management.refresh_all_credentials' ) }
294- aria-label = { t ( 'quota_management.refresh_all_credentials' ) }
284+ title = { refreshButtonLabel }
285+ aria-label = { refreshButtonLabel }
295286 >
296287 { ! isRefreshing && < IconRefreshCw size = { 16 } /> }
297- { t ( 'quota_management.refresh_all_credentials' ) }
288+ { refreshButtonLabel }
298289 </ Button >
299290 </ div >
300291 }
0 commit comments