diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index e43699f..df1caa9 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -19,6 +19,7 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature { this._expandWorkspacesStrip.bind(this) ); ChromeUtils.defineLazyGetter(this, 'tabContainer', () => document.getElementById('tabbrowser-tabs')); + await ZenWorkspacesStorage.init(); await this.initializeWorkspaces(); console.info('ZenWorkspaces: ZenWorkspaces initialized'); } @@ -57,10 +58,7 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature { async _workspaces() { if (!this._workspaceCache) { - this._workspaceCache = await IOUtils.readJSON(this._storeFile); - if (!this._workspaceCache.workspaces) { - this._workspaceCache.workspaces = []; - } + this._workspaceCache = { workspaces: await ZenWorkspacesStorage.getWorkspaces() }; } return this._workspaceCache; } @@ -171,42 +169,34 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature { } async saveWorkspace(workspaceData) { - let json = await IOUtils.readJSON(this._storeFile); - if (typeof json.workspaces === 'undefined') { - json.workspaces = []; - } - let existing = json.workspaces.findIndex((workspace) => workspace.uuid === workspaceData.uuid); - if (existing >= 0) { - json.workspaces[existing] = workspaceData; - } else { - json.workspaces.push(workspaceData); - } - console.info('ZenWorkspaces: Saving workspace', workspaceData); - await IOUtils.writeJSON(this._storeFile, json); + await ZenWorkspacesStorage.saveWorkspace(workspaceData); this._workspaceCache = null; - await this._updateWorkspacesChangeContextMenu(); } async removeWorkspace(windowID) { - let json = await this._workspaces(); console.info('ZenWorkspaces: Removing workspace', windowID); await this.changeWorkspace(json.workspaces.find((workspace) => workspace.uuid !== windowID)); this._deleteAllTabsInWorkspace(windowID); delete this._lastSelectedWorkspaceTabs[windowID]; - json.workspaces = json.workspaces.filter((workspace) => workspace.uuid !== windowID); - await this.unsafeSaveWorkspaces(json); + await ZenWorkspacesStorage.removeWorkspace(windowID); + this._workspaceCache = null; await this._propagateWorkspaceData(); await this._updateWorkspacesChangeContextMenu(); } async saveWorkspaces() { - await IOUtils.writeJSON(this._storeFile, await this._workspaces()); + const workspaces = await this._workspaces(); + for (const workspace of workspaces.workspaces) { + await ZenWorkspacesStorage.saveWorkspace(workspace); + } this._workspaceCache = null; } async unsafeSaveWorkspaces(workspaces) { - await IOUtils.writeJSON(this._storeFile, workspaces); + for (const workspace of workspaces.workspaces) { + await ZenWorkspacesStorage.saveWorkspace(workspace); + } this._workspaceCache = workspaces; } @@ -624,6 +614,8 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature { browser.document.getElementById('tabbrowser-tabs')._positionPinnedTabs(); }); + + await ZenWorkspacesStorage.setActiveWorkspace(window.uuid); await this.saveWorkspaces(); await this._propagateWorkspaceData(); } @@ -748,7 +740,7 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature { onContextMenuClose() { let target = document.querySelector( - `#PanelUI-zen-workspaces [zen-workspace-id="${this._contextMenuId}"] .zen-workspace-actions` + `#PanelUI-zen-workspaces [zen-workspace-id="${this._contextMenuId}"] .zen-workspace-actions` ); if (target) { target.removeAttribute('active'); @@ -757,11 +749,8 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature { } async setDefaultWorkspace() { - let workspaces = await this._workspaces(); - for (let workspace of workspaces.workspaces) { - workspace.default = workspace.uuid === this._contextMenuId; - } - await this.unsafeSaveWorkspaces(workspaces); + await ZenWorkspacesStorage.setDefaultWorkspace(this._contextMenuId); + this._workspaceCache = null; await this._propagateWorkspaceData(); } diff --git a/src/ZenWorkspacesStorage.mjs b/src/ZenWorkspacesStorage.mjs new file mode 100644 index 0000000..325497d --- /dev/null +++ b/src/ZenWorkspacesStorage.mjs @@ -0,0 +1,89 @@ +var ZenWorkspacesStorage = { + async init() { + console.log('ZenWorkspacesStorage: Initializing...'); + await this._ensureTable(); + }, + + async _ensureTable() { + await PlacesUtils.withConnectionWrapper("ZenWorkspacesStorage._ensureTable", async db => { + await db.execute(` + CREATE TABLE IF NOT EXISTS moz_workspaces ( + id INTEGER PRIMARY KEY, + uuid TEXT UNIQUE NOT NULL, + name TEXT NOT NULL, + icon TEXT, + is_default INTEGER NOT NULL DEFAULT 0, + is_active INTEGER NOT NULL DEFAULT 0, + container_id INTEGER, + created_at INTEGER NOT NULL, + updated_at INTEGER NOT NULL + ) + `); + }); + }, + + async saveWorkspace(workspace) { + await PlacesUtils.withConnectionWrapper("ZenWorkspacesStorage.saveWorkspace", async db => { + const now = Date.now(); + await db.executeCached(` + INSERT OR REPLACE INTO moz_workspaces ( + uuid, name, icon, is_default, is_active, container_id, created_at, updated_at + ) VALUES ( + :uuid, :name, :icon, :is_default, :is_active, :container_id, + COALESCE((SELECT created_at FROM moz_workspaces WHERE uuid = :uuid), :now), + :now + ) + `, { + uuid: workspace.uuid, + name: workspace.name, + icon: workspace.icon || null, + is_default: workspace.default ? 1 : 0, + is_active: workspace.used ? 1 : 0, + container_id: workspace.containerTabId || null, + now + }); + }); + }, + + async getWorkspaces() { + const db = await PlacesUtils.promiseDBConnection(); + const rows = await db.execute(` + SELECT * FROM moz_workspaces ORDER BY created_at ASC + `); + + return rows.map(row => ({ + uuid: row.getResultByName("uuid"), + name: row.getResultByName("name"), + icon: row.getResultByName("icon"), + default: !!row.getResultByName("is_default"), + used: !!row.getResultByName("is_active"), + containerTabId: row.getResultByName("container_id") + })); + }, + + async removeWorkspace(uuid) { + await PlacesUtils.withConnectionWrapper("ZenWorkspacesStorage.removeWorkspace", async db => { + await db.execute(` + DELETE FROM moz_workspaces WHERE uuid = :uuid + `, { uuid }); + }); + }, + + async setActiveWorkspace(uuid) { + await PlacesUtils.withConnectionWrapper("ZenWorkspacesStorage.setActiveWorkspace", async db => { + await db.executeTransaction(async function() { + await db.execute(`UPDATE moz_workspaces SET is_active = 0`); + await db.execute(`UPDATE moz_workspaces SET is_active = 1 WHERE uuid = :uuid`, { uuid }); + }); + }); + }, + + async setDefaultWorkspace(uuid) { + await PlacesUtils.withConnectionWrapper("ZenWorkspacesStorage.setDefaultWorkspace", async db => { + await db.executeTransaction(async function() { + await db.execute(`UPDATE moz_workspaces SET is_default = 0`); + await db.execute(`UPDATE moz_workspaces SET is_default = 1 WHERE uuid = :uuid`, { uuid }); + }); + }); + } +}; \ No newline at end of file