Files
js/
components/
app-data-component.js
libs/
data-server.js
utils/
storage-util.js
servers.js
index.js
Source Code
index.js
{
urls: [
"js/libs/data-server.js",
"js/utils/storage-util.js",
],
},
{
urls: [
"js/components/app-data-component.js",
],
callback: async function() {
// setup db
{
let dbName = "app-db1";
let dbVersion = 1;
await storageUtil.Init(dbName, dbVersion);
}
},
},
js/components/app-data-component.js
let compoAppData = (function() {
// # self
let SELF = {
GetActiveWorkspaceId,
Init_,
Configure,
ChangeWorkspace_,
Add,
Delete_,
Rename,
SetActiveWorkspaceId,
ClearData_,
Save_,
server: {
RemoveItem: (id) => server.RemoveItem(id),
// AddItem: (item) => server.AddItem(item),
GetAll: () => server.GetAll(),
GetById: (id) => server.GetItem(id),
GetIndex: (id) => server.GetIndex(id),
Clear: () => server.Clear()
},
GetStoreData_,
};
let data = {
activeId: -1,
items: [],
};
// # local
let local = {
storageName: 'app-profiles',
serversConfig: { },
storeKeyPrefixes: { }
};
let server = new DataServer({
dataItems: data.items,
adaptor: {
lookupKey: 'id',
GetItem: (item, value) => item.id == value,
}
});
// # function
function Configure(serversConfig) {
for (let key in serversConfig) {
local.storeKeyPrefixes[key] = '';
}
local.serversConfig = serversConfig;
}
async function GetStoreData_() {
let workspaceMap = await storageUtil.getItem_(local.storageName);
let entries = [];
for (let workspace of workspaceMap.items) {
let {id} = workspace;
for (let key in local.storeKeyPrefixes) {
let storeKey = `${key}-${id}`;
let value = await storageUtil.getItem_(storeKey);
if (value === null) continue;
entries.push({
key: storeKey,
value,
});
}
}
entries.push({
key: local.storageName,
value: workspaceMap,
})
return {
entries,
};
}
// # delete
async function Delete_(id) {
let item = server.GetItem(id);
server.RemoveItem(id);
let storeKeys = getStoreKeys(id);
for (let key in storeKeys) {
let storeKey = storeKeys[key];
await storageUtil.removeItem_(storeKey);
}
}
// # change, # restore
async function ChangeWorkspace_(id) {
SetActiveWorkspaceId(id);
let item = server.GetItem(id);
let storeKeys = getStoreKeys(item.id);
for (let key in local.serversConfig) {
let serverObj = local.serversConfig[key];
await serverObj.ChangeWorkspace_(storeKeys[key]);
}
}
function getStoreKeys(id) {
let storekeys = {};
for (let key in local.storeKeyPrefixes) {
let value = `${key}-${id}`;
storekeys[key] = value;
}
return storekeys;
}
function SetActiveWorkspaceId(id) {
data.activeId = id;
}
function Rename(id, name) {
let item = server.GetItem(id);
item.name = name;
}
function GetActiveWorkspaceId() {
return data.activeId;
}
// # add, # create
function Add({name}) {
let id = Date.now();
let item = {
name,
id,
};
data.items.push(item);
return id;
}
async function ClearData_() {
await storageUtil.removeItem_(local.storageName);
}
// # init
async function Init_() {
await restoreData_();
if (data.items.length == 0) {
let id = await Add({
name: 'Default',
});
SetActiveWorkspaceId(id);
await Save_();
}
await ChangeWorkspace_(data.activeId);
}
async function Save_() {
await storageUtil.setItem_(local.storageName, data);
}
// # restore
async function restoreData_() {
let storeData = await storageUtil.getItem_(local.storageName);
if (!storeData) return;
data = storeData;
server.SetDataItems(data.items);
}
return SELF;
})();
js/libs/data-server.js
function DataServer(config = {
lookupKey: 'id',
}) {
let SELF = {
GetAll,
GetItem,
AddItem,
UpdateItem,
GetIndex,
Clear,
RemoveItem,
SetDataItems,
};
function SetDataItems(dataItems) {
config.dataItems = dataItems;
}
function AddItem(itemObj) {
let index = GetIndex(itemObj[config.lookupKey]);
if (index >= 0) return false;
config.dataItems.push(itemObj);
return true;
}
function UpdateItem(value, itemObj) {
let itemIndex = GetIndex(value);
if (itemIndex < 0) return false;
config.dataItems[itemIndex] = itemObj;
return true;
}
function Clear() {
config.dataItems.length = 0;
}
function GetIndex(value) {
let items = GetAll();
let lookupCallback = config.adaptor?.GetItem ?? lookup;
return items.findIndex(item => lookupCallback(item, value));
}
function GetAll() {
return config.dataItems;
}
function GetItem(value) {
let lookupCallback = config.adaptor?.GetItem ?? lookup;
let itemObj = config.dataItems.find(item => lookupCallback(item, value));
if (itemObj !== undefined) return itemObj;
return null;
}
function lookup(item, value) {
return item[config.lookupKey] == value;
}
function RemoveItem(value) {
let itemIndex = GetIndex(value);
if (itemIndex < 0) return null;
let item = config.dataItems.splice(itemIndex, 1);
return item;
}
return SELF;
}
js/utils/storage-util.js
let storageUtil = (function() {
let db = null;
let dbVersion = 1;
let storeName = 'public';
let dbName = '';
let SELF = {
Init,
openDb_,
setItem_,
getItem_,
removeItem_,
clearStore_,
};
// # init
function Init(_dbName, _dbVersion) {
dbName = _dbName;
dbVersion = _dbVersion;
}
// # Clear all items from a specific object store
async function clearStore_() {
return new Promise(async (resolve, reject) => {
await openDb_(); // Ensure the database is open
const transaction = db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
const request = store.clear();
request.onsuccess = () => resolve(true);
request.onerror = (event) => reject('Error clearing store: ' + event.target.errorCode);
});
}
// # Open IndexedDB
async function openDb_() {
return new Promise((resolve, reject) => {
if (db) resolve(db);
const request = indexedDB.open(dbName, dbVersion);
request.onerror = (event) => reject('Error opening database: ' + event.target.errorCode);
request.onsuccess = (event) => {
db = event.target.result;
resolve(db);
};
request.onupgradeneeded = (event) => {
db = event.target.result;
// Create object stores if they don't exist
if (!db.objectStoreNames.contains(storeName)) {
db.createObjectStore(storeName, { keyPath: 'key' });
}
};
});
}
// # Set Item in IndexedDB
async function setItem_(key, value) {
return new Promise(async (resolve, reject) => {
await openDb_();
const transaction = db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
const request = store.put({ key, value });
request.onsuccess = () => {
resolve(true);
};
request.onerror = (event) => reject('Error storing item: ' + event.target.errorCode);
});
}
// # Get Item from IndexedDB or fallback to localStorage
async function getItem_(key) {
return new Promise(async (resolve, reject) => {
await openDb_();
const transaction = db.transaction([storeName], 'readonly');
const store = transaction.objectStore(storeName);
const request = store.get(key);
request.onsuccess = (event) => {
const result = event.target.result;
if (result) {
resolve(result.value);
} else {
resolve(null); // return null if not found
}
};
request.onerror = (event) => reject('Error retrieving item: ' + event.target.errorCode);
});
}
// # Remove Item from IndexedDB and localStorage
async function removeItem_(key) {
return new Promise(async (resolve, reject) => {
await openDb_();
const transaction = db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
const request = store.delete(key);
request.onsuccess = () => {
resolve(true);
};
request.onerror = (event) => reject('Error removing item: ' + event.target.errorCode);
});
}
return SELF;
})();
https://vanillawebdev.blogspot.com/2024/12/app-data-setup.html
https://www.blogger.com/blog/post/edit/8166404610182826392/2622399701193667522
https://www.blogger.com/blog/page/edit/8166404610182826392/2622399701193667522
App Data Setup
December 11, 2024
Files
js/
components/
app-data-component.js
libs/
data-server.js
utils/
storage-util.js
servers.js
index.js
Source Code
index.js
{
urls: [
"js/libs/data-server.js",
"js/utils/storage-util.js",
],
},
{
urls: [
"js/components/app-data-component.js",
],
callback: async function() {
// setup db
{
let dbName = "app-db1";
let dbVersion = 1;
await storageUtil.Init(dbName, dbVersion);
}
},
},
js/components/app-data-component.js
let compoAppData = (function() {
// # self
let SELF = {
GetActiveWorkspaceId,
Init_,
Configure,
ChangeWorkspace_,
Add,
Delete_,
Rename,
SetActiveWorkspaceId,
ClearData_,
Save_,
server: {
RemoveItem: (id) => server.RemoveItem(id),
// AddItem: (item) => server.AddItem(item),
GetAll: () => server.GetAll(),
GetById: (id) => server.GetItem(id),
GetIndex: (id) => server.GetIndex(id),
Clear: () => server.Clear()
},
GetStoreData_,
};
let data = {
activeId: -1,
items: [],
};
// # local
let local = {
storageName: 'app-profiles',
serversConfig: { },
storeKeyPrefixes: { }
};
let server = new DataServer({
dataItems: data.items,
adaptor: {
lookupKey: 'id',
GetItem: (item, value) => item.id == value,
}
});
// # function
function Configure(serversConfig) {
for (let key in serversConfig) {
local.storeKeyPrefixes[key] = '';
}
local.serversConfig = serversConfig;
}
async function GetStoreData_() {
let workspaceMap = await storageUtil.getItem_(local.storageName);
let entries = [];
for (let workspace of workspaceMap.items) {
let {id} = workspace;
for (let key in local.storeKeyPrefixes) {
let storeKey = `${key}-${id}`;
let value = await storageUtil.getItem_(storeKey);
if (value === null) continue;
entries.push({
key: storeKey,
value,
});
}
}
entries.push({
key: local.storageName,
value: workspaceMap,
})
return {
entries,
};
}
// # delete
async function Delete_(id) {
let item = server.GetItem(id);
server.RemoveItem(id);
let storeKeys = getStoreKeys(id);
for (let key in storeKeys) {
let storeKey = storeKeys[key];
await storageUtil.removeItem_(storeKey);
}
}
// # change, # restore
async function ChangeWorkspace_(id) {
SetActiveWorkspaceId(id);
let item = server.GetItem(id);
let storeKeys = getStoreKeys(item.id);
for (let key in local.serversConfig) {
let serverObj = local.serversConfig[key];
await serverObj.ChangeWorkspace_(storeKeys[key]);
}
}
function getStoreKeys(id) {
let storekeys = {};
for (let key in local.storeKeyPrefixes) {
let value = `${key}-${id}`;
storekeys[key] = value;
}
return storekeys;
}
function SetActiveWorkspaceId(id) {
data.activeId = id;
}
function Rename(id, name) {
let item = server.GetItem(id);
item.name = name;
}
function GetActiveWorkspaceId() {
return data.activeId;
}
// # add, # create
function Add({name}) {
let id = Date.now();
let item = {
name,
id,
};
data.items.push(item);
return id;
}
async function ClearData_() {
await storageUtil.removeItem_(local.storageName);
}
// # init
async function Init_() {
await restoreData_();
if (data.items.length == 0) {
let id = await Add({
name: 'Default',
});
SetActiveWorkspaceId(id);
await Save_();
}
await ChangeWorkspace_(data.activeId);
}
async function Save_() {
await storageUtil.setItem_(local.storageName, data);
}
// # restore
async function restoreData_() {
let storeData = await storageUtil.getItem_(local.storageName);
if (!storeData) return;
data = storeData;
server.SetDataItems(data.items);
}
return SELF;
})();
js/libs/data-server.js
function DataServer(config = {
lookupKey: 'id',
}) {
let SELF = {
GetAll,
GetItem,
AddItem,
UpdateItem,
GetIndex,
Clear,
RemoveItem,
SetDataItems,
};
function SetDataItems(dataItems) {
config.dataItems = dataItems;
}
function AddItem(itemObj) {
let index = GetIndex(itemObj[config.lookupKey]);
if (index >= 0) return false;
config.dataItems.push(itemObj);
return true;
}
function UpdateItem(value, itemObj) {
let itemIndex = GetIndex(value);
if (itemIndex < 0) return false;
config.dataItems[itemIndex] = itemObj;
return true;
}
function Clear() {
config.dataItems.length = 0;
}
function GetIndex(value) {
let items = GetAll();
let lookupCallback = config.adaptor?.GetItem ?? lookup;
return items.findIndex(item => lookupCallback(item, value));
}
function GetAll() {
return config.dataItems;
}
function GetItem(value) {
let lookupCallback = config.adaptor?.GetItem ?? lookup;
let itemObj = config.dataItems.find(item => lookupCallback(item, value));
if (itemObj !== undefined) return itemObj;
return null;
}
function lookup(item, value) {
return item[config.lookupKey] == value;
}
function RemoveItem(value) {
let itemIndex = GetIndex(value);
if (itemIndex < 0) return null;
let item = config.dataItems.splice(itemIndex, 1);
return item;
}
return SELF;
}
js/utils/storage-util.js
let storageUtil = (function() {
let db = null;
let dbVersion = 1;
let storeName = 'public';
let dbName = '';
let SELF = {
Init,
openDb_,
setItem_,
getItem_,
removeItem_,
clearStore_,
};
// # init
function Init(_dbName, _dbVersion) {
dbName = _dbName;
dbVersion = _dbVersion;
}
// # Clear all items from a specific object store
async function clearStore_() {
return new Promise(async (resolve, reject) => {
await openDb_(); // Ensure the database is open
const transaction = db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
const request = store.clear();
request.onsuccess = () => resolve(true);
request.onerror = (event) => reject('Error clearing store: ' + event.target.errorCode);
});
}
// # Open IndexedDB
async function openDb_() {
return new Promise((resolve, reject) => {
if (db) resolve(db);
const request = indexedDB.open(dbName, dbVersion);
request.onerror = (event) => reject('Error opening database: ' + event.target.errorCode);
request.onsuccess = (event) => {
db = event.target.result;
resolve(db);
};
request.onupgradeneeded = (event) => {
db = event.target.result;
// Create object stores if they don't exist
if (!db.objectStoreNames.contains(storeName)) {
db.createObjectStore(storeName, { keyPath: 'key' });
}
};
});
}
// # Set Item in IndexedDB
async function setItem_(key, value) {
return new Promise(async (resolve, reject) => {
await openDb_();
const transaction = db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
const request = store.put({ key, value });
request.onsuccess = () => {
resolve(true);
};
request.onerror = (event) => reject('Error storing item: ' + event.target.errorCode);
});
}
// # Get Item from IndexedDB or fallback to localStorage
async function getItem_(key) {
return new Promise(async (resolve, reject) => {
await openDb_();
const transaction = db.transaction([storeName], 'readonly');
const store = transaction.objectStore(storeName);
const request = store.get(key);
request.onsuccess = (event) => {
const result = event.target.result;
if (result) {
resolve(result.value);
} else {
resolve(null); // return null if not found
}
};
request.onerror = (event) => reject('Error retrieving item: ' + event.target.errorCode);
});
}
// # Remove Item from IndexedDB and localStorage
async function removeItem_(key) {
return new Promise(async (resolve, reject) => {
await openDb_();
const transaction = db.transaction([storeName], 'readwrite');
const store = transaction.objectStore(storeName);
const request = store.delete(key);
request.onsuccess = () => {
resolve(true);
};
request.onerror = (event) => reject('Error removing item: ' + event.target.errorCode);
});
}
return SELF;
})();
Comments
Post a Comment