mirror of
https://github.com/zen-browser/components.git
synced 2025-07-07 22:19:58 +02:00
Merge pull request #36 from kristijanribaric/save-workspaces-in-places-db
feat(workspaces): Store workspaces in a database
This commit is contained in:
commit
a4a77f4dc0
2 changed files with 132 additions and 71 deletions
|
@ -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');
|
||||
}
|
||||
|
@ -44,22 +45,28 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature {
|
|||
|
||||
getActiveWorkspaceFromCache() {
|
||||
try {
|
||||
return this._workspaceCache.workspaces.find((workspace) => workspace.used);
|
||||
const activeWorkspaceId = Services.prefs.getStringPref("zen.workspaces.active", "");
|
||||
return this._workspaceCache.workspaces.find((workspace) => workspace.uuid === activeWorkspaceId);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Wrorkspaces saving/loading
|
||||
get _storeFile() {
|
||||
return PathUtils.join(PathUtils.profileDir, 'zen-workspaces', 'Workspaces.json');
|
||||
}
|
||||
|
||||
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() };
|
||||
// Get the active workspace ID from preferences
|
||||
const activeWorkspaceId = Services.prefs.getStringPref("zen.workspaces.active", "");
|
||||
|
||||
if (activeWorkspaceId) {
|
||||
const activeWorkspace = this._workspaceCache.workspaces.find(w => w.uuid === activeWorkspaceId);
|
||||
// Set the active workspace ID to the first one if the one with selected id doesn't exist
|
||||
if (!activeWorkspace) {
|
||||
Services.prefs.setStringPref("zen.workspaces.active", this._workspaceCache.workspaces[0]?.uuid);
|
||||
}
|
||||
} else {
|
||||
// Set the active workspace ID to the first one if active workspace doesn't exist
|
||||
Services.prefs.setStringPref("zen.workspaces.active", this._workspaceCache.workspaces[0]?.uuid);
|
||||
}
|
||||
}
|
||||
return this._workspaceCache;
|
||||
|
@ -79,10 +86,7 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature {
|
|||
|
||||
async initializeWorkspaces() {
|
||||
Services.prefs.addObserver('zen.workspaces.enabled', this.onWorkspacesEnabledChanged.bind(this));
|
||||
let file = new FileUtils.File(this._storeFile);
|
||||
if (!file.exists()) {
|
||||
await IOUtils.writeJSON(this._storeFile, {});
|
||||
}
|
||||
|
||||
await this.initializeWorkspacesButton();
|
||||
if (this.workspaceEnabled) {
|
||||
this._initializeWorkspaceCreationIcons();
|
||||
|
@ -94,16 +98,14 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature {
|
|||
if (workspaces.workspaces.length === 0) {
|
||||
await this.createAndSaveWorkspace('Default Workspace', true);
|
||||
} else {
|
||||
let activeWorkspace = workspaces.workspaces.find((workspace) => workspace.used);
|
||||
let activeWorkspace = await this.getActiveWorkspace();
|
||||
if (!activeWorkspace) {
|
||||
activeWorkspace = workspaces.workspaces.find((workspace) => workspace.default);
|
||||
activeWorkspace.used = true;
|
||||
await this.saveWorkspaces();
|
||||
Services.prefs.setStringPref("zen.workspaces.active", activeWorkspace.uuid);
|
||||
}
|
||||
if (!activeWorkspace) {
|
||||
activeWorkspace = workspaces.workspaces[0];
|
||||
activeWorkspace.used = true;
|
||||
await this.saveWorkspaces();
|
||||
Services.prefs.setStringPref("zen.workspaces.active", activeWorkspace.uuid);
|
||||
}
|
||||
this.changeWorkspace(activeWorkspace, true);
|
||||
}
|
||||
|
@ -171,45 +173,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 this._propagateWorkspaceData();
|
||||
await this._updateWorkspacesChangeContextMenu();
|
||||
}
|
||||
|
||||
async saveWorkspaces() {
|
||||
await IOUtils.writeJSON(this._storeFile, await this._workspaces());
|
||||
async removeWorkspace(windowID) {
|
||||
let workspacesData = await this._workspaces();
|
||||
console.info('ZenWorkspaces: Removing workspace', windowID);
|
||||
await this.changeWorkspace(workspacesData.workspaces.find((workspace) => workspace.uuid !== windowID));
|
||||
this._deleteAllTabsInWorkspace(windowID);
|
||||
delete this._lastSelectedWorkspaceTabs[windowID];
|
||||
await ZenWorkspacesStorage.removeWorkspace(windowID);
|
||||
this._workspaceCache = null;
|
||||
await this._propagateWorkspaceData();
|
||||
await this._updateWorkspacesChangeContextMenu();
|
||||
}
|
||||
|
||||
async unsafeSaveWorkspaces(workspaces) {
|
||||
await IOUtils.writeJSON(this._storeFile, workspaces);
|
||||
this._workspaceCache = workspaces;
|
||||
isWorkspaceActive(workspace) {
|
||||
const activeWorkspaceId = Services.prefs.getStringPref("zen.workspaces.active", "");
|
||||
return workspace.uuid === activeWorkspaceId;
|
||||
}
|
||||
|
||||
async getActiveWorkspace() {
|
||||
const workspaces = await this._workspaces();
|
||||
const activeWorkspaceId = Services.prefs.getStringPref("zen.workspaces.active", "");
|
||||
return workspaces.workspaces.find(workspace => workspace.uuid === activeWorkspaceId);
|
||||
}
|
||||
// Workspaces dialog UI management
|
||||
|
||||
openSaveDialog() {
|
||||
|
@ -272,7 +263,7 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature {
|
|||
element.className = 'subviewbutton';
|
||||
element.setAttribute('tooltiptext', workspace.name);
|
||||
element.setAttribute('zen-workspace-id', workspace.uuid);
|
||||
if (workspace.used) {
|
||||
if (this.isWorkspaceActive(workspace)) {
|
||||
element.setAttribute('active', 'true');
|
||||
}
|
||||
if (workspace.default) {
|
||||
|
@ -328,7 +319,7 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature {
|
|||
};
|
||||
browser.ZenWorkspaces._workspaceCache = null;
|
||||
let workspaces = await browser.ZenWorkspaces._workspaces();
|
||||
let activeWorkspace = workspaces.workspaces.find((workspace) => workspace.used);
|
||||
let activeWorkspace = await this.getActiveWorkspace();
|
||||
currentContainer.innerHTML = '';
|
||||
workspaceList.innerHTML = '';
|
||||
workspaceList.parentNode.style.display = 'flex';
|
||||
|
@ -343,7 +334,7 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature {
|
|||
currentContainer.appendChild(currentWorkspace);
|
||||
}
|
||||
for (let workspace of workspaces.workspaces) {
|
||||
if (workspace.used) {
|
||||
if (this.isWorkspaceActive(workspace)) {
|
||||
continue;
|
||||
}
|
||||
let workspaceElement = createWorkspaceElement(workspace);
|
||||
|
@ -396,7 +387,7 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature {
|
|||
button.className = 'subviewbutton';
|
||||
button.setAttribute('tooltiptext', workspace.name);
|
||||
button.setAttribute('zen-workspace-id', workspace.uuid);
|
||||
if (workspace.used) {
|
||||
if (this.isWorkspaceActive(workspace)) {
|
||||
button.setAttribute('active', 'true');
|
||||
}
|
||||
if (workspace.default) {
|
||||
|
@ -435,7 +426,7 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature {
|
|||
if (!button) {
|
||||
return;
|
||||
}
|
||||
let activeWorkspace = (await browser.ZenWorkspaces._workspaces()).workspaces.find((workspace) => workspace.used);
|
||||
let activeWorkspace = await this.getActiveWorkspace();
|
||||
if (activeWorkspace) {
|
||||
button.setAttribute('as-button', 'true');
|
||||
button.classList.add('toolbarbutton-1', 'zen-sidebar-action-button');
|
||||
|
@ -579,11 +570,9 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature {
|
|||
if (!this.workspaceEnabled) {
|
||||
return;
|
||||
}
|
||||
let workspaces = await this._workspaces();
|
||||
for (let workspace of workspaces.workspaces) {
|
||||
workspace.used = workspace.uuid === window.uuid;
|
||||
}
|
||||
await this.unsafeSaveWorkspaces(workspaces);
|
||||
|
||||
Services.prefs.setStringPref("zen.workspaces.active", window.uuid);
|
||||
|
||||
const shouldAllowPinnedTabs = this._shouldAllowPinTab;
|
||||
await this.foreachWindowAsActive(async (browser) => {
|
||||
browser.ZenWorkspaces.tabContainer._invalidateCachedTabs();
|
||||
|
@ -624,7 +613,7 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature {
|
|||
|
||||
browser.document.getElementById('tabbrowser-tabs')._positionPinnedTabs();
|
||||
});
|
||||
await this.saveWorkspaces();
|
||||
|
||||
await this._propagateWorkspaceData();
|
||||
}
|
||||
|
||||
|
@ -635,7 +624,7 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature {
|
|||
|
||||
menuPopup.innerHTML = '';
|
||||
|
||||
const activeWorkspace = workspaces.workspaces.find((workspace) => workspace.used);
|
||||
const activeWorkspace = await this.getActiveWorkspace();
|
||||
|
||||
for (let workspace of workspaces.workspaces) {
|
||||
const menuItem = document.createXULElement('menuitem');
|
||||
|
@ -676,8 +665,8 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature {
|
|||
if (tab.getAttribute('zen-workspace-id') || !this.workspaceEnabled) {
|
||||
return;
|
||||
}
|
||||
let workspaces = await this._workspaces();
|
||||
let activeWorkspace = workspaces.workspaces.find((workspace) => workspace.used);
|
||||
|
||||
let activeWorkspace = await this.getActiveWorkspace();
|
||||
if (!activeWorkspace) {
|
||||
return;
|
||||
}
|
||||
|
@ -688,8 +677,7 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature {
|
|||
let tab = gBrowser.getTabForBrowser(browser);
|
||||
let workspaceID = tab.getAttribute('zen-workspace-id');
|
||||
if (!workspaceID) {
|
||||
let workspaces = await this._workspaces();
|
||||
let activeWorkspace = workspaces.workspaces.find((workspace) => workspace.used);
|
||||
let activeWorkspace = await this.getActiveWorkspace();
|
||||
if (!activeWorkspace || tab.hasAttribute('hidden')) {
|
||||
return;
|
||||
}
|
||||
|
@ -724,7 +712,7 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature {
|
|||
defaultMenuItem.removeAttribute('disabled');
|
||||
}
|
||||
let openMenuItem = document.getElementById('context_zenOpenWorkspace');
|
||||
if (workspaces.workspaces.find((workspace) => workspace.uuid === this._contextMenuId).used) {
|
||||
if (workspaces.workspaces.find((workspace) => workspace.uuid === this._contextMenuId && this.isWorkspaceActive(workspace))) {
|
||||
openMenuItem.setAttribute('disabled', 'true');
|
||||
} else {
|
||||
openMenuItem.removeAttribute('disabled');
|
||||
|
@ -748,7 +736,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 +745,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();
|
||||
}
|
||||
|
||||
|
@ -786,7 +771,7 @@ var ZenWorkspaces = new class extends ZenMultiWindowFeature {
|
|||
async changeWorkspaceShortcut(offset = 1) {
|
||||
// Cycle through workspaces
|
||||
let workspaces = await this._workspaces();
|
||||
let activeWorkspace = workspaces.workspaces.find((workspace) => workspace.used);
|
||||
let activeWorkspace = await this.getActiveWorkspace();
|
||||
let workspaceIndex = workspaces.workspaces.indexOf(activeWorkspace);
|
||||
// note: offset can be negative
|
||||
let nextWorkspace = workspaces.workspaces[(workspaceIndex + offset + workspaces.workspaces.length) % workspaces.workspaces.length];
|
||||
|
|
76
src/ZenWorkspacesStorage.mjs
Normal file
76
src/ZenWorkspacesStorage.mjs
Normal file
|
@ -0,0 +1,76 @@
|
|||
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 zen_workspaces (
|
||||
id INTEGER PRIMARY KEY,
|
||||
uuid TEXT UNIQUE NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
icon TEXT,
|
||||
is_default 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 zen_workspaces (
|
||||
uuid, name, icon, is_default, container_id, created_at, updated_at
|
||||
) VALUES (
|
||||
:uuid, :name, :icon, :is_default, :container_id,
|
||||
COALESCE((SELECT created_at FROM zen_workspaces WHERE uuid = :uuid), :now),
|
||||
:now
|
||||
)
|
||||
`, {
|
||||
uuid: workspace.uuid,
|
||||
name: workspace.name,
|
||||
icon: workspace.icon || null,
|
||||
is_default: workspace.default ? 1 : 0,
|
||||
container_id: workspace.containerTabId || null,
|
||||
now
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
async getWorkspaces() {
|
||||
const db = await PlacesUtils.promiseDBConnection();
|
||||
const rows = await db.execute(`
|
||||
SELECT * FROM zen_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"),
|
||||
containerTabId: row.getResultByName("container_id")
|
||||
}));
|
||||
},
|
||||
|
||||
async removeWorkspace(uuid) {
|
||||
await PlacesUtils.withConnectionWrapper("ZenWorkspacesStorage.removeWorkspace", async db => {
|
||||
await db.execute(`
|
||||
DELETE FROM zen_workspaces WHERE uuid = :uuid
|
||||
`, { uuid });
|
||||
});
|
||||
},
|
||||
|
||||
async setDefaultWorkspace(uuid) {
|
||||
await PlacesUtils.withConnectionWrapper("ZenWorkspacesStorage.setDefaultWorkspace", async db => {
|
||||
await db.executeTransaction(async function() {
|
||||
await db.execute(`UPDATE zen_workspaces SET is_default = 0`);
|
||||
await db.execute(`UPDATE zen_workspaces SET is_default = 1 WHERE uuid = :uuid`, { uuid });
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue