From a874a341e03fde09a5fdfbe9e9e22dbd32760b3d Mon Sep 17 00:00:00 2001 From: Dariusz L Date: Tue, 24 Jun 2025 08:21:34 +0200 Subject: [PATCH] Migrate canvas state storage to IndexedDB Replaced localStorage usage in Canvas.js with asynchronous IndexedDB operations for saving, loading, and removing canvas state. Added a new db.js module to handle IndexedDB interactions, improving scalability and reliability of state persistence. --- js/Canvas.js | 55 ++++++++++++++--------------- js/db.js | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+), 30 deletions(-) create mode 100644 js/db.js diff --git a/js/Canvas.js b/js/Canvas.js index 474e5cc..91c61cc 100644 --- a/js/Canvas.js +++ b/js/Canvas.js @@ -1,3 +1,5 @@ +import { getCanvasState, setCanvasState, removeCanvasState } from "./db.js"; + export class Canvas { constructor(node, widget) { this.node = node; @@ -82,28 +84,20 @@ export class Canvas { // this.saveState(); // Wywołanie przeniesione do loadInitialState } - getLocalStorageKey() { + async loadStateFromDB() { + console.log("Attempting to load state from IndexedDB for node:", this.node.id); if (!this.node.id) { - console.error("Node ID is not available for generating localStorage key."); - return null; + console.error("Node ID is not available for loading state from DB."); + return false; } - return `canvas-state-${this.node.id}`; - } - - async loadStateFromLocalStorage() { - console.log("Attempting to load state from localStorage for node:", this.node.id); - const key = this.getLocalStorageKey(); - if (!key) return false; try { - const savedStateJSON = localStorage.getItem(key); - if (!savedStateJSON) { - console.log("No saved state found in localStorage for key:", key); + const savedState = await getCanvasState(this.node.id); + if (!savedState) { + console.log("No saved state found in IndexedDB for node:", this.node.id); return false; } - console.log("Found saved state in localStorage:", savedStateJSON.substring(0, 200) + "..."); - - const savedState = JSON.parse(savedStateJSON); + console.log("Found saved state in IndexedDB."); this.width = savedState.width || 512; this.height = savedState.height || 512; @@ -144,16 +138,18 @@ export class Canvas { console.log("Canvas state loaded successfully from localStorage for node", this.node.id); return true; } catch (e) { - console.error("Error loading canvas state from localStorage:", e); - localStorage.removeItem(key); + console.error("Error loading canvas state from IndexedDB:", e); + await removeCanvasState(this.node.id).catch(err => console.error("Failed to remove corrupted state:", err)); return false; } } - - saveStateToLocalStorage() { - console.log("Attempting to save state to localStorage for node:", this.node.id); - const key = this.getLocalStorageKey(); - if (!key) return; + + async saveStateToDB() { + console.log("Attempting to save state to IndexedDB for node:", this.node.id); + if (!this.node.id) { + console.error("Node ID is not available for saving state to DB."); + return; + } try { const state = { @@ -172,17 +168,16 @@ export class Canvas { width: this.width, height: this.height, }; - const stateJSON = JSON.stringify(state); - localStorage.setItem(key, stateJSON); - console.log("Canvas state saved to localStorage:", stateJSON.substring(0, 200) + "..."); + await setCanvasState(this.node.id, state); + console.log("Canvas state saved to IndexedDB."); } catch (e) { - console.error("Error saving canvas state to localStorage:", e); + console.error("Error saving canvas state to IndexedDB:", e); } } async loadInitialState() { console.log("Loading initial state for node:", this.node.id); - const loaded = await this.loadStateFromLocalStorage(); + const loaded = await this.loadStateFromDB(); if (!loaded) { console.log("No saved state found, initializing from node data."); await this.initNodeData(); @@ -220,7 +215,7 @@ export class Canvas { } this.redoStack = []; this.updateHistoryButtons(); - this.saveStateToLocalStorage(); + this.saveStateToDB(); } undo() { @@ -1138,7 +1133,7 @@ export class Canvas { this.render(); if (saveHistory) { - this.saveStateToLocalStorage(); + this.saveStateToDB(); } } diff --git a/js/db.js b/js/db.js new file mode 100644 index 0000000..e34f6e1 --- /dev/null +++ b/js/db.js @@ -0,0 +1,97 @@ +const DB_NAME = 'CanvasNodeDB'; +const STORE_NAME = 'CanvasState'; +const DB_VERSION = 1; + +let db; + +function openDB() { + return new Promise((resolve, reject) => { + if (db) { + resolve(db); + return; + } + + console.log("Opening IndexedDB..."); + const request = indexedDB.open(DB_NAME, DB_VERSION); + + request.onerror = (event) => { + console.error("IndexedDB error:", event.target.error); + reject("Error opening IndexedDB."); + }; + + request.onsuccess = (event) => { + db = event.target.result; + console.log("IndexedDB opened successfully."); + resolve(db); + }; + + request.onupgradeneeded = (event) => { + console.log("Upgrading IndexedDB..."); + const db = event.target.result; + if (!db.objectStoreNames.contains(STORE_NAME)) { + db.createObjectStore(STORE_NAME, { keyPath: 'id' }); + console.log("Object store created:", STORE_NAME); + } + }; + }); +} + +export async function getCanvasState(id) { + console.log(`DB: Getting state for id: ${id}`); + const db = await openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([STORE_NAME], 'readonly'); + const store = transaction.objectStore(STORE_NAME); + const request = store.get(id); + + request.onerror = (event) => { + console.error("DB: Error getting canvas state:", event.target.error); + reject("Error getting state."); + }; + + request.onsuccess = (event) => { + console.log(`DB: Get success for id: ${id}`, event.target.result ? 'found' : 'not found'); + resolve(event.target.result ? event.target.result.state : null); + }; + }); +} + +export async function setCanvasState(id, state) { + console.log(`DB: Setting state for id: ${id}`); + const db = await openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([STORE_NAME], 'readwrite'); + const store = transaction.objectStore(STORE_NAME); + const request = store.put({ id, state }); + + request.onerror = (event) => { + console.error("DB: Error setting canvas state:", event.target.error); + reject("Error setting state."); + }; + + request.onsuccess = () => { + console.log(`DB: Set success for id: ${id}`); + resolve(); + }; + }); +} + +export async function removeCanvasState(id) { + console.log(`DB: Removing state for id: ${id}`); + const db = await openDB(); + return new Promise((resolve, reject) => { + const transaction = db.transaction([STORE_NAME], 'readwrite'); + const store = transaction.objectStore(STORE_NAME); + const request = store.delete(id); + + request.onerror = (event) => { + console.error("DB: Error removing canvas state:", event.target.error); + reject("Error removing state."); + }; + + request.onsuccess = () => { + console.log(`DB: Remove success for id: ${id}`); + resolve(); + }; + }); +} \ No newline at end of file