From c47adfa7fabde168c3de96d991c9c86db1e9d316 Mon Sep 17 00:00:00 2001 From: Dmitri Date: Tue, 14 Apr 2026 20:28:58 +0400 Subject: [PATCH] improved cache cleanup for offline mode --- frontend/src/hooks/useOfflineMode.ts | 17 ++++++++++- frontend/src/lib/offline/DownloadManager.ts | 14 +++++++++ frontend/src/lib/offline/StorageManager.ts | 34 ++++++++++++++------- 3 files changed, 53 insertions(+), 12 deletions(-) diff --git a/frontend/src/hooks/useOfflineMode.ts b/frontend/src/hooks/useOfflineMode.ts index 65e44e3..30be3b1 100644 --- a/frontend/src/hooks/useOfflineMode.ts +++ b/frontend/src/hooks/useOfflineMode.ts @@ -481,9 +481,24 @@ export function useOfflineMode( const deleteOfflineData = useCallback(async () => { if (!projectId) return; + // Get storage keys before deleting (to clear in-memory blob URLs) + const assets = discoverAssets(); + const storageKeys = assets.map((a) => a.storageKey); + + // Clear in-memory blob URLs for this project's assets + downloadManager.clearBlobUrlsForKeys(storageKeys); + + // Delete from persistent storage await StorageManager.deleteProjectAssets(projectId); await OfflineDbManager.deleteProject(projectId); + // Reset refs used for progress tracking + assetsRef.current = []; + downloadedCountRef.current = 0; + downloadedBytesRef.current = 0; + + // Reset state + setDiscoveredAssets([]); setProjectInfo(null); setStatus('not_downloaded'); setProgress(0); @@ -491,7 +506,7 @@ export function useOfflineMode( setTotalAssets(0); setDownloadedBytes(0); setTotalBytes(0); - }, [projectId]); + }, [projectId, discoverAssets]); // Check for updates by comparing discovered assets with stored project const checkForUpdates = useCallback(async (): Promise => { diff --git a/frontend/src/lib/offline/DownloadManager.ts b/frontend/src/lib/offline/DownloadManager.ts index 02da5a8..5886d29 100644 --- a/frontend/src/lib/offline/DownloadManager.ts +++ b/frontend/src/lib/offline/DownloadManager.ts @@ -756,6 +756,20 @@ class DownloadManagerClass { } } + /** + * Clear blob URLs for specific storage keys (call when deleting project offline data) + */ + clearBlobUrlsForKeys(storageKeys: string[]): void { + for (const key of storageKeys) { + const blobUrl = this.readyBlobUrls.get(key); + if (blobUrl) { + URL.revokeObjectURL(blobUrl); + this.readyBlobUrls.delete(key); + } + this.partialDownloadsReady.delete(key); + } + } + /** * Check if URL is an image based on extension */ diff --git a/frontend/src/lib/offline/StorageManager.ts b/frontend/src/lib/offline/StorageManager.ts index 6f36974..f23d9a9 100644 --- a/frontend/src/lib/offline/StorageManager.ts +++ b/frontend/src/lib/offline/StorageManager.ts @@ -9,6 +9,7 @@ import { OFFLINE_CONFIG } from '../../config/offline.config'; import { PRELOAD_CONFIG } from '../../config/preload.config'; import { OfflineDbManager } from '../offlineDb/OfflineDbManager'; import { extractStoragePath } from '../assetUrl'; +import { logger } from '../logger'; import type { OfflineAsset, AssetVariantType, @@ -260,23 +261,34 @@ export class StorageManager { // Delete from IndexedDB await OfflineDbManager.deleteProjectAssets(projectId); - // Cache API cleanup is more complex - we'd need to track URLs - // For now, we rely on the service worker to handle this + // Delete from Cache API - iterate through cache and delete entries for this project + if (typeof caches !== 'undefined') { + try { + const cache = await caches.open(OFFLINE_CONFIG.cacheNames.assets); + const keys = await cache.keys(); + + for (const request of keys) { + const response = await cache.match(request); + if (response) { + const cachedProjectId = response.headers.get('X-Project-Id'); + if (cachedProjectId === projectId) { + await cache.delete(request); + } + } + } + } catch (error) { + logger.error('[StorageManager] Failed to clear Cache API:', error); + } + } } /** * Get total storage used */ static async getTotalStorageUsed(): Promise { - let total = 0; - - // IndexedDB storage - total += await OfflineDbManager.getTotalAssetsSize(); - - // Cache API storage (approximate from quota) - const quota = await this.getStorageQuota(); - // Note: quota.usage includes all storage, not just our caches - + // IndexedDB storage (primary tracking) + const total = await OfflineDbManager.getTotalAssetsSize(); + // Note: Cache API size is included in browser's quota.usage but not tracked separately return total; }