feat(storage): implement MicronStorage for tab management, enhance TileCache with state management, and update DialogUtils and NotificationUtils for improved user interactions
This commit is contained in:
@@ -16,8 +16,10 @@ class DialogUtils {
|
||||
// running inside electron, use ipc confirm
|
||||
return window.electron.confirm(message);
|
||||
} else {
|
||||
// running inside normal browser, use browser alert
|
||||
return window.confirm(message);
|
||||
// running inside normal browser, use custom confirm dialog
|
||||
return new Promise((resolve) => {
|
||||
GlobalEmitter.emit("confirm", { message, resolve });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,19 @@ class ElectronUtils {
|
||||
}
|
||||
}
|
||||
|
||||
static shutdown() {
|
||||
if (window.electron) {
|
||||
window.electron.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
static async getMemoryUsage() {
|
||||
if (window.electron) {
|
||||
return await window.electron.getMemoryUsage();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static showPathInFolder(path) {
|
||||
if (window.electron) {
|
||||
window.electron.showPathInFolder(path);
|
||||
|
||||
@@ -3,6 +3,13 @@ import { reactive } from "vue";
|
||||
// global state
|
||||
const globalState = reactive({
|
||||
unreadConversationsCount: 0,
|
||||
activeCallTab: "phone",
|
||||
blockedDestinations: [],
|
||||
config: {
|
||||
banished_effect_enabled: true,
|
||||
banished_text: "BANISHED",
|
||||
banished_color: "#dc2626",
|
||||
},
|
||||
});
|
||||
|
||||
export default globalState;
|
||||
|
||||
109
meshchatx/src/frontend/js/MicronStorage.js
Normal file
109
meshchatx/src/frontend/js/MicronStorage.js
Normal file
@@ -0,0 +1,109 @@
|
||||
const DB_NAME = "micron_editor_db";
|
||||
const DB_VERSION = 1;
|
||||
const STORE_NAME = "tabs";
|
||||
|
||||
class MicronStorage {
|
||||
constructor() {
|
||||
this.db = null;
|
||||
this.initPromise = this.init();
|
||||
}
|
||||
|
||||
async init() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.db) {
|
||||
resolve(this.db);
|
||||
return;
|
||||
}
|
||||
|
||||
const idb =
|
||||
window.indexedDB ||
|
||||
window.mozIndexedDB ||
|
||||
window.webkitIndexedDB ||
|
||||
window.msIndexedDB ||
|
||||
globalThis.indexedDB;
|
||||
if (!idb) {
|
||||
reject("IndexedDB not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
const request = idb.open(DB_NAME, DB_VERSION);
|
||||
|
||||
request.onerror = (event) => {
|
||||
console.error("IndexedDB error:", event.target.errorCode);
|
||||
reject("IndexedDB error: " + event.target.errorCode);
|
||||
};
|
||||
|
||||
request.onupgradeneeded = (event) => {
|
||||
const db = event.target.result;
|
||||
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
||||
db.createObjectStore(STORE_NAME, { keyPath: "id", autoIncrement: true });
|
||||
}
|
||||
};
|
||||
|
||||
request.onsuccess = (event) => {
|
||||
this.db = event.target.result;
|
||||
resolve(this.db);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async saveTabs(tabs) {
|
||||
await this.initPromise;
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = this.db.transaction([STORE_NAME], "readwrite");
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
|
||||
// Clear existing tabs before saving new ones to maintain order and structure
|
||||
const clearRequest = store.clear();
|
||||
|
||||
clearRequest.onsuccess = () => {
|
||||
if (tabs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure we are storing plain objects, not Vue proxies or other non-cloneable objects.
|
||||
// JSON.parse/stringify is a safe way to strip proxies and ensure serializability
|
||||
// for these simple tab objects.
|
||||
const plainTabs = JSON.parse(JSON.stringify(tabs));
|
||||
|
||||
plainTabs.forEach((tab) => {
|
||||
store.add(tab);
|
||||
});
|
||||
};
|
||||
|
||||
transaction.oncomplete = () => resolve();
|
||||
transaction.onerror = (event) => reject(event.target.error);
|
||||
});
|
||||
}
|
||||
|
||||
async loadTabs() {
|
||||
await this.initPromise;
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = this.db.transaction([STORE_NAME], "readonly");
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const request = store.getAll();
|
||||
|
||||
request.onsuccess = () => {
|
||||
resolve(request.result);
|
||||
};
|
||||
|
||||
request.onerror = () => {
|
||||
reject(request.error);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async clearAll() {
|
||||
await this.initPromise;
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = this.db.transaction([STORE_NAME], "readwrite");
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const request = store.clear();
|
||||
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const micronStorage = new MicronStorage();
|
||||
@@ -1,5 +1,9 @@
|
||||
class NotificationUtils {
|
||||
static showIncomingCallNotification() {
|
||||
if (window.electron) {
|
||||
window.electron.showNotification("Incoming Call", "Someone is calling you.");
|
||||
return;
|
||||
}
|
||||
Notification.requestPermission().then((result) => {
|
||||
if (result === "granted") {
|
||||
new window.Notification("Incoming Call", {
|
||||
@@ -11,6 +15,10 @@ class NotificationUtils {
|
||||
}
|
||||
|
||||
static showMissedCallNotification(from) {
|
||||
if (window.electron) {
|
||||
window.electron.showNotification("Missed Call", `You missed a call from ${from}.`);
|
||||
return;
|
||||
}
|
||||
Notification.requestPermission().then((result) => {
|
||||
if (result === "granted") {
|
||||
new window.Notification("Missed Call", {
|
||||
@@ -22,6 +30,10 @@ class NotificationUtils {
|
||||
}
|
||||
|
||||
static showNewVoicemailNotification(from) {
|
||||
if (window.electron) {
|
||||
window.electron.showNotification("New Voicemail", `You have a new voicemail from ${from}.`);
|
||||
return;
|
||||
}
|
||||
Notification.requestPermission().then((result) => {
|
||||
if (result === "granted") {
|
||||
new window.Notification("New Voicemail", {
|
||||
@@ -32,11 +44,18 @@ class NotificationUtils {
|
||||
});
|
||||
}
|
||||
|
||||
static showNewMessageNotification() {
|
||||
static showNewMessageNotification(from, content) {
|
||||
if (window.electron) {
|
||||
window.electron.showNotification(
|
||||
"New Message",
|
||||
from ? `${from}: ${content || "Sent a message."}` : "Someone sent you a message."
|
||||
);
|
||||
return;
|
||||
}
|
||||
Notification.requestPermission().then((result) => {
|
||||
if (result === "granted") {
|
||||
new window.Notification("New Message", {
|
||||
body: "Someone sent you a message.",
|
||||
body: from ? `${from}: ${content || "Sent a message."}` : "Someone sent you a message.",
|
||||
tag: "new_message", // only ever show one new message notification at a time
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const DB_NAME = "meshchat_map_cache";
|
||||
const DB_VERSION = 1;
|
||||
const DB_VERSION = 2;
|
||||
const STORE_NAME = "tiles";
|
||||
const STATE_STORE = "map_state";
|
||||
|
||||
class TileCache {
|
||||
constructor() {
|
||||
@@ -10,7 +11,20 @@ class TileCache {
|
||||
|
||||
async init() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
||||
const idb =
|
||||
window.indexedDB ||
|
||||
window.mozIndexedDB ||
|
||||
window.webkitIndexedDB ||
|
||||
window.msIndexedDB ||
|
||||
globalThis.indexedDB;
|
||||
|
||||
if (!idb) {
|
||||
console.warn("IndexedDB not supported, map caching will be disabled");
|
||||
reject("IndexedDB not supported");
|
||||
return;
|
||||
}
|
||||
|
||||
const request = idb.open(DB_NAME, DB_VERSION);
|
||||
|
||||
request.onerror = (event) => reject("IndexedDB error: " + event.target.errorCode);
|
||||
|
||||
@@ -19,6 +33,9 @@ class TileCache {
|
||||
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
||||
db.createObjectStore(STORE_NAME);
|
||||
}
|
||||
if (!db.objectStoreNames.contains(STATE_STORE)) {
|
||||
db.createObjectStore(STATE_STORE);
|
||||
}
|
||||
};
|
||||
|
||||
request.onsuccess = (event) => {
|
||||
@@ -52,17 +69,41 @@ class TileCache {
|
||||
});
|
||||
}
|
||||
|
||||
async clear() {
|
||||
async getMapState(key) {
|
||||
await this.initPromise;
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = this.db.transaction([STORE_NAME], "readwrite");
|
||||
const store = transaction.objectStore(STORE_NAME);
|
||||
const request = store.clear();
|
||||
const transaction = this.db.transaction([STATE_STORE], "readonly");
|
||||
const store = transaction.objectStore(STATE_STORE);
|
||||
const request = store.get(key);
|
||||
|
||||
request.onsuccess = () => resolve(request.result);
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
async setMapState(key, data) {
|
||||
await this.initPromise;
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = this.db.transaction([STATE_STORE], "readwrite");
|
||||
const store = transaction.objectStore(STATE_STORE);
|
||||
const request = store.put(data, key);
|
||||
|
||||
request.onsuccess = () => resolve();
|
||||
request.onerror = () => reject(request.error);
|
||||
});
|
||||
}
|
||||
|
||||
async clear() {
|
||||
await this.initPromise;
|
||||
return new Promise((resolve, reject) => {
|
||||
const transaction = this.db.transaction([STORE_NAME, STATE_STORE], "readwrite");
|
||||
transaction.objectStore(STORE_NAME).clear();
|
||||
transaction.objectStore(STATE_STORE).clear();
|
||||
|
||||
transaction.oncomplete = () => resolve();
|
||||
transaction.onerror = () => reject(transaction.error);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default new TileCache();
|
||||
|
||||
Reference in New Issue
Block a user