39948-vm/frontend/src/hooks/useStorageQuota.ts
2026-03-24 08:20:27 +04:00

111 lines
2.8 KiB
TypeScript

/**
* useStorageQuota Hook
*
* Monitors storage quota and usage for offline assets.
*/
import { useState, useEffect, useCallback } from 'react';
import { StorageManager } from '../lib/offline/StorageManager';
import { PRELOAD_CONFIG } from '../config/preload.config';
import type { StorageQuotaInfo } from '../types/offline';
interface UseStorageQuotaResult extends StorageQuotaInfo {
isLoading: boolean;
error: string | null;
refresh: () => Promise<void>;
requestPersistence: () => Promise<boolean>;
isPersisted: boolean;
isWarning: boolean;
isCritical: boolean;
formatSize: (bytes: number) => string;
}
/**
* Format bytes to human-readable string
*/
const formatBytes = (bytes: number): string => {
if (bytes === 0) return '0 B';
if (bytes === Infinity) return 'Unlimited';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
};
export function useStorageQuota(): UseStorageQuotaResult {
const [quotaInfo, setQuotaInfo] = useState<StorageQuotaInfo>({
usage: 0,
quota: Infinity,
percentUsed: 0,
available: Infinity,
canStore: () => true,
});
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [isPersisted, setIsPersisted] = useState(false);
// Fetch quota info
const refresh = useCallback(async () => {
setIsLoading(true);
setError(null);
try {
const info = await StorageManager.getStorageQuota();
setQuotaInfo(info);
// Check persistence status
if (typeof navigator !== 'undefined' && navigator.storage?.persisted) {
const persisted = await navigator.storage.persisted();
setIsPersisted(persisted);
}
} catch (err) {
setError(
err instanceof Error ? err.message : 'Failed to get storage quota',
);
} finally {
setIsLoading(false);
}
}, []);
// Request persistent storage
const requestPersistence = useCallback(async (): Promise<boolean> => {
try {
const granted = await StorageManager.requestPersistentStorage();
setIsPersisted(granted);
return granted;
} catch {
return false;
}
}, []);
// Initial fetch and periodic refresh
useEffect(() => {
refresh();
// Refresh every 30 seconds
const interval = setInterval(refresh, 30000);
return () => clearInterval(interval);
}, [refresh]);
// Computed values
const isWarning =
quotaInfo.percentUsed >= PRELOAD_CONFIG.storage.warningPercent;
const isCritical =
quotaInfo.percentUsed >= PRELOAD_CONFIG.storage.criticalPercent;
return {
...quotaInfo,
isLoading,
error,
refresh,
requestPersistence,
isPersisted,
isWarning,
isCritical,
formatSize: formatBytes,
};
}