import { API } from 'api' export class NodeStore { constructor() {//{{{ if (!('indexedDB' in window)) { throw 'Missing IndexedDB' } this.db = null }//}}} async initializeDB() {//{{{ return new Promise((resolve, reject) => { const req = indexedDB.open('notes', 3) // Schema upgrades for IndexedDB. // These can start from different points depending on updates to Notes2 since a device was online. req.onupgradeneeded = (event) => { let treeNodes let nodes let appState const db = event.target.result const trx = event.target.transaction for (let i = event.oldVersion + 1; i <= event.newVersion; i++) { console.log(`Upgrade to schema ${i}`) // The schema transformations. switch (i) { case 1: treeNodes = db.createObjectStore('treeNodes', { keyPath: 'UUID' }) treeNodes.createIndex('nameIndex', 'Name', { unique: false }) nodes = db.createObjectStore('nodes', { keyPath: 'UUID' }) nodes.createIndex('nameIndex', 'Name', { unique: false }) break case 2: trx.objectStore('treeNodes').createIndex('parentIndex', 'ParentUUID', { unique: false }) break case 3: appState = db.createObjectStore('appState', { keyPath: 'key' }) } } } req.onsuccess = (event) => { this.db = event.target.result resolve() } req.onerror = (event) => { reject(event.target.error) } }) }//}}} async getAppState(key) {//{{{ return new Promise((resolve, reject) => { const trx = this.db.transaction('appState', 'readonly') const appState = trx.objectStore('appState') const getRequest = appState.get(key) getRequest.onsuccess = (event) => { if (event.target.result !== undefined) { resolve(event.target.result) } else { resolve(null) } } getRequest.onerror = (event) => reject(event.target.error) }) }//}}} async setAppState(key, value) {//{{{ return new Promise((resolve, reject) => { try { const t = this.db.transaction('appState', 'readwrite') const appState = t.objectStore('appState') t.onerror = (event) => { console.log('transaction error', event.target.error) reject(event.target.error) } t.oncomplete = () => { resolve() } const record = { key, value } const addReq = appState.put(record) addReq.onerror = (event) => { console.log('Error!', event.target.error, key, value) } } catch (e) { reject(e) } }) }//}}} async updateTreeRecords(records) {//{{{ return new Promise((resolve, reject) => { const t = this.db.transaction('treeNodes', 'readwrite') const nodeStore = t.objectStore('treeNodes') t.onerror = (event) => { console.log('transaction error', event.target.error) reject(event.target.error) } t.oncomplete = () => { resolve() } // records is an object, not an array. for (const i in records) { const record = records[i] let addReq let op if (record.Deleted) { op = 'deleting' addReq = nodeStore.delete(record.UUID) } else { op = 'upserting' addReq = nodeStore.put(record) } addReq.onsuccess = () => { console.log(`${op} ${record.UUID} (${record.Name})`) } addReq.onerror = (event) => { console.log(`error ${op} ${record.UUID}`, event.target.error) reject(event.target.error) } } }) }//}}} async add(records) {//{{{ return new Promise((resolve, reject) => { try { const t = this.db.transaction('nodes', 'readwrite') const nodeStore = t.objectStore('nodes') t.onerror = (event) => { console.log('transaction error', event.target.error) reject(event.target.error) } t.oncomplete = () => { resolve() } // records is an object, not an array. for (const recordIdx in records) { const record = records[recordIdx] const addReq = nodeStore.put(record) addReq.onsuccess = () => { console.log('OK!', record.ID, record.Name) } addReq.onerror = (event) => { console.log('Error!', event.target.error, record.ID) } } } catch (e) { console.log(e) } }) }//}}} async get(id) {//{{{ return new Promise((resolve, reject) => { // Node is always returned from IndexedDB if existing there. // Otherwise an attempt to get it from backend is executed. const trx = this.db.transaction('nodes', 'readonly') const nodeStore = trx.objectStore('nodes') const getRequest = nodeStore.get(id) getRequest.onsuccess = (event) => { // Node found in IndexedDB and returned. if (event.target.result !== undefined) { resolve(event.target.result) return } // Node not found and a request to the backend is made. API.query("POST", `/node/retrieve/${id}`, {}) .then(res => { const trx = this.db.transaction('nodes', 'readwrite') const nodeStore = trx.objectStore('nodes') const putRequest = nodeStore.put(res.Node) putRequest.onsuccess = () => resolve(res.Node) putRequest.onerror = (event) => { reject(event.target.error) } }) .catch(e => reject(e)) } }) }//}}} async getTreeNodes() {//{{{ return new Promise((resolve, reject) => { const trx = this.db.transaction('nodes', 'readonly') const nodeStore = trx.objectStore('nodes') const req = nodeStore.getAll() req.onsuccess = (event) => resolve(event.target.result) req.onerror = (event) => reject(event.target.error) }) }//}}} } // vim: foldmethod=marker