From 8c1bf5ddaf39baf6f4b5285a6ab0bb424b30e8fd Mon Sep 17 00:00:00 2001 From: Antonio Date: Mon, 20 Feb 2023 13:00:43 +0100 Subject: [PATCH] Implement local cache --- package-lock.json | 14 +++++ package.json | 1 + src/core/building/building-database.ts | 81 ++++++++++++++++++++++++-- src/core/building/building-handler.ts | 6 ++ src/core/building/building-scene.ts | 2 +- src/core/building/dexie-utils.ts | 17 ++++++ src/core/database/db-handler.ts | 3 + 7 files changed, 118 insertions(+), 6 deletions(-) create mode 100644 src/core/building/dexie-utils.ts diff --git a/package-lock.json b/package-lock.json index f3ad8e6..f3241fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,7 @@ "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", "client-zip": "^2.3.0", + "dexie": "^3.2.3", "firebase": "^9.17.1", "mapbox-gl": "^2.12.1", "openbim-components": "^0.0.39", @@ -7565,6 +7566,14 @@ "node": ">=0.8.0" } }, + "node_modules/dexie": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/dexie/-/dexie-3.2.3.tgz", + "integrity": "sha512-iHayBd4UYryDCVUNa3PMsJMEnd8yjyh5p7a+RFeC8i8n476BC9wMhVvqiImq5zJZJf5Tuer+s4SSj+AA3x+ZbQ==", + "engines": { + "node": ">=6.0" + } + }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", @@ -23933,6 +23942,11 @@ "minimist": "^1.2.6" } }, + "dexie": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/dexie/-/dexie-3.2.3.tgz", + "integrity": "sha512-iHayBd4UYryDCVUNa3PMsJMEnd8yjyh5p7a+RFeC8i8n476BC9wMhVvqiImq5zJZJf5Tuer+s4SSj+AA3x+ZbQ==" + }, "didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", diff --git a/package.json b/package.json index fa2c1c6..40d64ef 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "@types/react": "^18.0.28", "@types/react-dom": "^18.0.11", "client-zip": "^2.3.0", + "dexie": "^3.2.3", "firebase": "^9.17.1", "mapbox-gl": "^2.12.1", "openbim-components": "^0.0.39", diff --git a/src/core/building/building-database.ts b/src/core/building/building-database.ts index a95fe8d..6dc5cf5 100644 --- a/src/core/building/building-database.ts +++ b/src/core/building/building-database.ts @@ -1,20 +1,91 @@ +import { ModelDatabase } from "./dexie-utils"; import { getApp } from "firebase/app"; -import { getDownloadURL, getStorage, ref } from "firebase/storage"; +import { + FirebaseStorage, + getDownloadURL, + getStorage, + ref, +} from "firebase/storage"; import { Building } from "../../types"; // CORS problem solution: https://stackoverflow.com/a/58613527 export class BuildingDatabase { + private db: ModelDatabase; + + constructor() { + this.db = new ModelDatabase(); + } + async getModels(building: Building) { const appInstance = getApp(); - const storageInstance = getStorage(appInstance); + const instance = getStorage(appInstance); const urls: string[] = []; for (const model of building.models) { - const fileRef = ref(storageInstance, model.id); - const fileUrl = await getDownloadURL(fileRef); - urls.push(fileUrl); + const url = await this.getModelURL(instance, model.id); + urls.push(url); } + this.db.close(); + return urls; } + + async clearCache(building: Building) { + await this.db.open(); + for (const model of building.models) { + localStorage.removeItem(model.id); + } + await this.db.delete(); + this.db = new ModelDatabase(); + this.db.close(); + } + + async deleteModel(id: string) { + await this.db.open(); + if (this.isModelCached(id)) { + localStorage.removeItem(id); + await this.db.models.where("id").equals(id).delete(); + } + this.db.close(); + } + + private async getModelURL(instance: FirebaseStorage, id: string) { + if (this.isModelCached(id)) { + return this.getModelFromLocalCache(id); + } else { + return this.getModelFromFirebase(instance, id); + } + } + + private async getModelFromFirebase(instance: FirebaseStorage, id: string) { + const fileRef = ref(instance, id); + const url = await getDownloadURL(fileRef); + await this.cacheModel(id, url); + console.log("Got model from firebase!"); + return url; + } + + private async getModelFromLocalCache(id: string) { + const found = await this.db.models.where("id").equals(id).toArray(); + const file = found[0].file; + console.log("Got model from local cache!"); + return URL.createObjectURL(file); + } + + private isModelCached(id: string) { + const stored = localStorage.getItem(id); + return stored !== null; + } + + private async cacheModel(id: string, url: string) { + const time = performance.now().toString(); + localStorage.setItem(id, time); + const rawData = await fetch(url); + const file = await rawData.blob(); + await this.db.models.add({ + id, + file, + }); + } } diff --git a/src/core/building/building-handler.ts b/src/core/building/building-handler.ts index 316f05e..47edf30 100644 --- a/src/core/building/building-handler.ts +++ b/src/core/building/building-handler.ts @@ -23,4 +23,10 @@ export const buildingHandler = { } return this.viewer.convertIfcToFragments(ifc); }, + + async deleteModel(id: string) { + if (this.viewer) { + await this.viewer.database.deleteModel(id); + } + }, }; diff --git a/src/core/building/building-scene.ts b/src/core/building/building-scene.ts index 7eca9ba..aa703fa 100644 --- a/src/core/building/building-scene.ts +++ b/src/core/building/building-scene.ts @@ -8,7 +8,7 @@ import { unzip } from "unzipit"; export class BuildingScene { private components: OBC.Components; private fragments: OBC.Fragments; - private database = new BuildingDatabase(); + database = new BuildingDatabase(); constructor(container: HTMLDivElement, building: Building) { this.components = new OBC.Components(); diff --git a/src/core/building/dexie-utils.ts b/src/core/building/dexie-utils.ts new file mode 100644 index 0000000..0581fc3 --- /dev/null +++ b/src/core/building/dexie-utils.ts @@ -0,0 +1,17 @@ +import { Dexie } from "dexie"; + +interface IModel { + id: string; + file: Blob; +} + +export class ModelDatabase extends Dexie { + models!: Dexie.Table; + + constructor() { + super("ModelDatabase"); + this.version(2).stores({ + models: "id, file", + }); + } +} diff --git a/src/core/database/db-handler.ts b/src/core/database/db-handler.ts index f1bedfa..56dcee5 100644 --- a/src/core/database/db-handler.ts +++ b/src/core/database/db-handler.ts @@ -9,6 +9,7 @@ import { deleteDoc, doc, getFirestore, updateDoc } from "firebase/firestore"; import { Events } from "../../middleware/event-handler"; import { Building, Model } from "../../types"; import { getStorage, ref, uploadBytes, deleteObject } from "firebase/storage"; +import { buildingHandler } from "../building/building-handler"; export const databaseHandler = { login: () => { @@ -31,6 +32,7 @@ export const databaseHandler = { for (const model of building.models) { const fileRef = ref(storageInstance, model.id); await deleteObject(fileRef); + await buildingHandler.deleteModel(model.id); } events.trigger({ type: "CLOSE_BUILDING" }); }, @@ -60,6 +62,7 @@ export const databaseHandler = { const storageInstance = getStorage(appInstance); const fileRef = ref(storageInstance, model.id); await deleteObject(fileRef); + await buildingHandler.deleteModel(model.id); events.trigger({ type: "UPDATE_BUILDING", payload: building }); }, };