Changes
Co-authored-by: Reifonas <211114984+Reifonas@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
// Armazena os arquivos GLB recentes (após conversão) para reuso rápido,
|
||||
// sem necessidade de recarregar da nuvem ou do disco local.
|
||||
//
|
||||
// Estratégia:
|
||||
// - Metadados (lista JSON ordenada) em localStorage → leitura síncrona / rápida
|
||||
// - Blob real do GLB em IndexedDB → suporta arquivos grandes
|
||||
|
||||
const META_KEY = 'tsxr_recent_files_v1';
|
||||
const DB_NAME = 'tsxr-recent';
|
||||
const STORE = 'glb';
|
||||
const MAX_RECENTS = 6;
|
||||
|
||||
export interface RecentFileMeta {
|
||||
id: string; // chave no IndexedDB
|
||||
fileName: string; // nome do arquivo GLB
|
||||
fileSize: number; // bytes
|
||||
addedAt: number; // timestamp ms
|
||||
}
|
||||
|
||||
// ── IndexedDB helpers ─────────────────────────────────────────────
|
||||
function openDB(): Promise<IDBDatabase> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const req = indexedDB.open(DB_NAME, 1);
|
||||
req.onupgradeneeded = () => {
|
||||
const db = req.result;
|
||||
if (!db.objectStoreNames.contains(STORE)) db.createObjectStore(STORE);
|
||||
};
|
||||
req.onsuccess = () => resolve(req.result);
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
}
|
||||
|
||||
async function idbPut(id: string, blob: Blob): Promise<void> {
|
||||
const db = await openDB();
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const tx = db.transaction(STORE, 'readwrite');
|
||||
tx.objectStore(STORE).put(blob, id);
|
||||
tx.oncomplete = () => resolve();
|
||||
tx.onerror = () => reject(tx.error);
|
||||
});
|
||||
db.close();
|
||||
}
|
||||
|
||||
async function idbGet(id: string): Promise<Blob | undefined> {
|
||||
const db = await openDB();
|
||||
const blob = await new Promise<Blob | undefined>((resolve, reject) => {
|
||||
const tx = db.transaction(STORE, 'readonly');
|
||||
const req = tx.objectStore(STORE).get(id);
|
||||
req.onsuccess = () => resolve(req.result as Blob | undefined);
|
||||
req.onerror = () => reject(req.error);
|
||||
});
|
||||
db.close();
|
||||
return blob;
|
||||
}
|
||||
|
||||
async function idbDelete(id: string): Promise<void> {
|
||||
const db = await openDB();
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const tx = db.transaction(STORE, 'readwrite');
|
||||
tx.objectStore(STORE).delete(id);
|
||||
tx.oncomplete = () => resolve();
|
||||
tx.onerror = () => reject(tx.error);
|
||||
});
|
||||
db.close();
|
||||
}
|
||||
|
||||
// ── Metadata helpers ─────────────────────────────────────────────
|
||||
export function listRecentFiles(): RecentFileMeta[] {
|
||||
try {
|
||||
const raw = localStorage.getItem(META_KEY);
|
||||
if (!raw) return [];
|
||||
const parsed = JSON.parse(raw) as RecentFileMeta[];
|
||||
return Array.isArray(parsed) ? parsed : [];
|
||||
} catch {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function saveMeta(list: RecentFileMeta[]) {
|
||||
try { localStorage.setItem(META_KEY, JSON.stringify(list)); } catch {}
|
||||
}
|
||||
|
||||
/** Registra um GLB nos recentes (substitui se mesmo fileName) e mantém apenas MAX_RECENTS. */
|
||||
export async function addRecentFile(fileName: string, blob: Blob): Promise<void> {
|
||||
try {
|
||||
const list = listRecentFiles();
|
||||
const existing = list.find(r => r.fileName === fileName);
|
||||
const id = existing?.id ?? `rf_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
||||
await idbPut(id, blob);
|
||||
const filtered = list.filter(r => r.fileName !== fileName);
|
||||
const next: RecentFileMeta[] = [
|
||||
{ id, fileName, fileSize: blob.size, addedAt: Date.now() },
|
||||
...filtered,
|
||||
].slice(0, MAX_RECENTS);
|
||||
// Limpa blobs removidos
|
||||
for (const r of filtered.slice(MAX_RECENTS - 1)) {
|
||||
try { await idbDelete(r.id); } catch {}
|
||||
}
|
||||
saveMeta(next);
|
||||
} catch (e) {
|
||||
console.warn('[recentFiles] addRecent failed', e);
|
||||
}
|
||||
}
|
||||
|
||||
/** Recupera o blob de um recente e devolve um object URL pronto para uso. */
|
||||
export async function loadRecentFile(id: string): Promise<{ url: string; blob: Blob } | null> {
|
||||
const blob = await idbGet(id);
|
||||
if (!blob) return null;
|
||||
return { url: URL.createObjectURL(blob), blob };
|
||||
}
|
||||
|
||||
export async function removeRecentFile(id: string): Promise<void> {
|
||||
const list = listRecentFiles().filter(r => r.id !== id);
|
||||
saveMeta(list);
|
||||
try { await idbDelete(id); } catch {}
|
||||
}
|
||||
|
||||
export async function clearRecentFiles(): Promise<void> {
|
||||
const list = listRecentFiles();
|
||||
for (const r of list) { try { await idbDelete(r.id); } catch {} }
|
||||
saveMeta([]);
|
||||
}
|
||||
Reference in New Issue
Block a user