111 lines
2.8 KiB
TypeScript
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,
|
|
};
|
|
}
|