2025-12-19 23:07:36 +00:00
|
|
|
/**
|
|
|
|
|
* Persistence.js
|
|
|
|
|
* Handles asynchronous saving and loading using IndexedDB.
|
2025-12-22 04:40:48 +00:00
|
|
|
* Manages both Active Runs and Persistent Roster data.
|
2025-12-19 23:07:36 +00:00
|
|
|
*/
|
|
|
|
|
const DB_NAME = "AetherShardsDB";
|
2025-12-22 04:40:48 +00:00
|
|
|
const RUN_STORE = "Runs";
|
|
|
|
|
const ROSTER_STORE = "Roster";
|
|
|
|
|
const VERSION = 2; // Bumped version to add Roster store
|
2025-12-19 23:07:36 +00:00
|
|
|
|
|
|
|
|
export class Persistence {
|
|
|
|
|
constructor() {
|
|
|
|
|
this.db = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async init() {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
const request = indexedDB.open(DB_NAME, VERSION);
|
|
|
|
|
|
|
|
|
|
request.onerror = (e) => reject("DB Error: " + e.target.error);
|
|
|
|
|
|
|
|
|
|
request.onupgradeneeded = (e) => {
|
|
|
|
|
const db = e.target.result;
|
2025-12-22 04:40:48 +00:00
|
|
|
|
|
|
|
|
// Create Runs Store if missing
|
|
|
|
|
if (!db.objectStoreNames.contains(RUN_STORE)) {
|
|
|
|
|
db.createObjectStore(RUN_STORE, { keyPath: "id" });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create Roster Store if missing
|
|
|
|
|
if (!db.objectStoreNames.contains(ROSTER_STORE)) {
|
|
|
|
|
db.createObjectStore(ROSTER_STORE, { keyPath: "id" });
|
2025-12-19 23:07:36 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
request.onsuccess = (e) => {
|
|
|
|
|
this.db = e.target.result;
|
|
|
|
|
resolve();
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-22 04:40:48 +00:00
|
|
|
// --- RUN DATA ---
|
|
|
|
|
|
2025-12-19 23:07:36 +00:00
|
|
|
async saveRun(runData) {
|
|
|
|
|
if (!this.db) await this.init();
|
2025-12-22 04:40:48 +00:00
|
|
|
return this._put(RUN_STORE, { ...runData, id: "active_run" });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async loadRun() {
|
|
|
|
|
if (!this.db) await this.init();
|
|
|
|
|
return this._get(RUN_STORE, "active_run");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async clearRun() {
|
|
|
|
|
if (!this.db) await this.init();
|
|
|
|
|
return this._delete(RUN_STORE, "active_run");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- ROSTER DATA ---
|
2025-12-19 23:07:36 +00:00
|
|
|
|
2025-12-22 04:40:48 +00:00
|
|
|
async saveRoster(rosterData) {
|
|
|
|
|
if (!this.db) await this.init();
|
|
|
|
|
// Wrap the raw data object in an ID for storage
|
|
|
|
|
return this._put(ROSTER_STORE, { id: "player_roster", data: rosterData });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async loadRoster() {
|
|
|
|
|
if (!this.db) await this.init();
|
|
|
|
|
const result = await this._get(ROSTER_STORE, "player_roster");
|
|
|
|
|
return result ? result.data : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- INTERNAL HELPERS ---
|
|
|
|
|
|
|
|
|
|
_put(storeName, item) {
|
|
|
|
|
return new Promise((resolve, reject) => {
|
|
|
|
|
const tx = this.db.transaction([storeName], "readwrite");
|
|
|
|
|
const store = tx.objectStore(storeName);
|
|
|
|
|
const req = store.put(item);
|
2025-12-19 23:07:36 +00:00
|
|
|
req.onsuccess = () => resolve();
|
|
|
|
|
req.onerror = () => reject(req.error);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-22 04:40:48 +00:00
|
|
|
_get(storeName, key) {
|
2025-12-19 23:07:36 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
2025-12-22 04:40:48 +00:00
|
|
|
const tx = this.db.transaction([storeName], "readonly");
|
|
|
|
|
const store = tx.objectStore(storeName);
|
|
|
|
|
const req = store.get(key);
|
2025-12-19 23:07:36 +00:00
|
|
|
req.onsuccess = () => resolve(req.result);
|
|
|
|
|
req.onerror = () => reject(req.error);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-22 04:40:48 +00:00
|
|
|
_delete(storeName, key) {
|
2025-12-19 23:07:36 +00:00
|
|
|
return new Promise((resolve, reject) => {
|
2025-12-22 04:40:48 +00:00
|
|
|
const tx = this.db.transaction([storeName], "readwrite");
|
|
|
|
|
const store = tx.objectStore(storeName);
|
|
|
|
|
const req = store.delete(key);
|
2025-12-19 23:07:36 +00:00
|
|
|
req.onsuccess = () => resolve();
|
|
|
|
|
req.onerror = () => reject(req.error);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|