mirror of
https://github.com/zen-browser/components.git
synced 2025-07-07 21:59:59 +02:00
feat: Sync Workspaces to Services-Sync
This commit adds a new feature to synchronize workspaces to the Services-Sync framework. - **ZenWorkspacesSync.mjs:** A new file that defines a custom engine for syncing workspaces. - **ZenWorkspaces.mjs:** Modified to register the ZenWorkspacesEngine with the Service engine manager and to update its state using services-sync's notification system. This feature enables users to sync their workspaces across devices and seamlessly switch between them using the services-sync mechanism.
This commit is contained in:
parent
fc7f08c827
commit
187dece11c
2 changed files with 226 additions and 0 deletions
|
@ -19,6 +19,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
);
|
||||
ChromeUtils.defineLazyGetter(this, 'tabContainer', () => document.getElementById('tabbrowser-tabs'));
|
||||
await ZenWorkspacesStorage.init();
|
||||
Weave.Service.engineManager.register(ZenWorkspacesEngine);
|
||||
await this.initializeWorkspaces();
|
||||
console.info('ZenWorkspaces: ZenWorkspaces initialized');
|
||||
}
|
||||
|
@ -190,6 +191,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
this._workspaceCache = null;
|
||||
await this._propagateWorkspaceData();
|
||||
await this._updateWorkspacesChangeContextMenu();
|
||||
Services.obs.notifyObservers(null, "zen-workspace-removed", windowID);
|
||||
}
|
||||
|
||||
isWorkspaceActive(workspace) {
|
||||
|
@ -539,6 +541,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
workspaceData.name = workspaceName;
|
||||
workspaceData.icon = icon?.label;
|
||||
await this.saveWorkspace(workspaceData);
|
||||
Services.obs.notifyObservers(null, "zen-workspace-updated", workspaceData.uuid);
|
||||
await this._propagateWorkspaceData();
|
||||
this.closeWorkspacesSubView();
|
||||
}
|
||||
|
@ -664,6 +667,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
}
|
||||
let workspaceData = this._createWorkspaceData(name, isDefault, icon);
|
||||
await this.saveWorkspace(workspaceData);
|
||||
Services.obs.notifyObservers(null, "zen-workspace-added", workspaceData.uuid);
|
||||
await this.changeWorkspace(workspaceData);
|
||||
}
|
||||
|
||||
|
@ -740,6 +744,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
|||
let userContextId = parseInt(event.target.getAttribute('data-usercontextid'));
|
||||
workspace.containerTabId = userContextId;
|
||||
await this.saveWorkspace(workspace);
|
||||
Services.obs.notifyObservers(null, "zen-workspace-updated", workspaceData.uuid);
|
||||
await this._propagateWorkspaceData();
|
||||
}
|
||||
|
||||
|
|
221
src/ZenWorkspacesSync.mjs
Normal file
221
src/ZenWorkspacesSync.mjs
Normal file
|
@ -0,0 +1,221 @@
|
|||
var { XPCOMUtils } = ChromeUtils.importESModule("resource://gre/modules/XPCOMUtils.sys.mjs");
|
||||
var { LegacyTracker } = ChromeUtils.importESModule("resource://services-sync/engines.sys.mjs");
|
||||
var { Store } = ChromeUtils.importESModule("resource://services-sync/engines.sys.mjs");
|
||||
var { SyncEngine } = ChromeUtils.importESModule("resource://services-sync/engines.sys.mjs");
|
||||
var { CryptoWrapper } = ChromeUtils.importESModule("resource://services-sync/record.sys.mjs");
|
||||
var { Svc,Utils } = ChromeUtils.importESModule("resource://services-sync/util.sys.mjs");
|
||||
var { SCORE_INCREMENT_XLARGE } = ChromeUtils.importESModule("resource://services-sync/constants.sys.mjs");
|
||||
|
||||
function ZenWorkspacesTracker(name, engine) {
|
||||
LegacyTracker.call(this, name, engine);
|
||||
}
|
||||
|
||||
ZenWorkspacesTracker.prototype = {
|
||||
__proto__: LegacyTracker.prototype,
|
||||
|
||||
start() {
|
||||
if (this._started) {
|
||||
return;
|
||||
}
|
||||
this._log.trace("Starting tracker");
|
||||
Services.obs.addObserver(this, "zen-workspace-added");
|
||||
Services.obs.addObserver(this, "zen-workspace-removed");
|
||||
Services.obs.addObserver(this, "zen-workspace-updated");
|
||||
this._started = true;
|
||||
},
|
||||
|
||||
stop() {
|
||||
if (!this._started) {
|
||||
return;
|
||||
}
|
||||
this._log.trace("Stopping tracker");
|
||||
Services.obs.removeObserver(this, "zen-workspace-added");
|
||||
Services.obs.removeObserver(this, "zen-workspace-removed");
|
||||
Services.obs.removeObserver(this, "zen-workspace-updated");
|
||||
this._started = false;
|
||||
},
|
||||
|
||||
observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case "zen-workspace-removed":
|
||||
case "zen-workspace-updated":
|
||||
case "zen-workspace-added":
|
||||
let workspaceID = data;
|
||||
this._log.trace(`Observed ${topic} for ${workspaceID}`);
|
||||
this.addChangedID(workspaceID);
|
||||
this.score += SCORE_INCREMENT_XLARGE;
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function ZenWorkspacesStore(name, engine) {
|
||||
Store.call(this, name, engine);
|
||||
}
|
||||
|
||||
ZenWorkspacesStore.prototype = {
|
||||
__proto__: Store.prototype,
|
||||
|
||||
async getAllIDs() {
|
||||
try {
|
||||
let workspaces = await ZenWorkspacesStorage.getWorkspaces();
|
||||
let ids = {};
|
||||
for (let workspace of workspaces) {
|
||||
ids[workspace.uuid] = true;
|
||||
}
|
||||
return ids;
|
||||
} catch (error) {
|
||||
this._log.error("Error fetching all workspace IDs", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async changeItemID(oldID, newID) {
|
||||
try {
|
||||
let workspaces = await ZenWorkspacesStorage.getWorkspaces();
|
||||
let workspace = workspaces.find(ws => ws.uuid === oldID);
|
||||
if (workspace) {
|
||||
workspace.uuid = newID;
|
||||
await ZenWorkspacesStorage.saveWorkspace(workspace);
|
||||
}
|
||||
} catch (error) {
|
||||
this._log.error(`Error changing workspace ID from ${oldID} to ${newID}`, error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async itemExists(id) {
|
||||
try {
|
||||
let workspaces = await ZenWorkspacesStorage.getWorkspaces();
|
||||
return workspaces.some(ws => ws.uuid === id);
|
||||
} catch (error) {
|
||||
this._log.error(`Error checking if workspace exists with ID ${id}`, error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async createRecord(id, collection) {
|
||||
try {
|
||||
let workspaces = await ZenWorkspacesStorage.getWorkspaces();
|
||||
let workspace = workspaces.find(ws => ws.uuid === id);
|
||||
let record = new ZenWorkspaceRecord(collection, id);
|
||||
|
||||
if (workspace) {
|
||||
record.name = workspace.name;
|
||||
record.icon = workspace.icon;
|
||||
record.default = workspace.default;
|
||||
record.containerTabId = workspace.containerTabId;
|
||||
record.deleted = false;
|
||||
} else {
|
||||
record.deleted = true;
|
||||
}
|
||||
|
||||
return record;
|
||||
} catch (error) {
|
||||
this._log.error(`Error creating record for workspace ID ${id}`, error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async create(record) {
|
||||
try {
|
||||
// Data validation
|
||||
this._validateRecord(record);
|
||||
|
||||
let workspace = {
|
||||
uuid: record.id,
|
||||
name: record.name,
|
||||
icon: record.icon,
|
||||
default: record.default,
|
||||
containerTabId: record.containerTabId,
|
||||
};
|
||||
await ZenWorkspacesStorage.saveWorkspace(workspace);
|
||||
} catch (error) {
|
||||
this._log.error(`Error creating workspace with ID ${record.id}`, error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async update(record) {
|
||||
try {
|
||||
// Data validation
|
||||
this._validateRecord(record);
|
||||
|
||||
await this.create(record);
|
||||
} catch (error) {
|
||||
this._log.error(`Error updating workspace with ID ${record.id}`, error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async remove(record) {
|
||||
try {
|
||||
await ZenWorkspacesStorage.removeWorkspace(record.id);
|
||||
} catch (error) {
|
||||
this._log.error(`Error removing workspace with ID ${record.id}`, error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
async wipe() {
|
||||
try {
|
||||
let workspaces = await ZenWorkspacesStorage.getWorkspaces();
|
||||
for (let workspace of workspaces) {
|
||||
await ZenWorkspacesStorage.removeWorkspace(workspace.uuid);
|
||||
}
|
||||
} catch (error) {
|
||||
this._log.error("Error wiping all workspaces", error);
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
|
||||
_validateRecord(record) {
|
||||
if (!record.id || typeof record.id !== "string") {
|
||||
throw new Error("Invalid workspace ID");
|
||||
}
|
||||
if (!record.name || typeof record.name !== "string") {
|
||||
throw new Error(`Invalid workspace name for ID ${record.id}`);
|
||||
}
|
||||
// 'default' is a boolean; if undefined, default to false
|
||||
if (typeof record.default !== "boolean") {
|
||||
record.default = false;
|
||||
}
|
||||
// 'icon' and 'containerTabId' can be null, but should be validated if present
|
||||
if (record.icon != null && typeof record.icon !== "string") {
|
||||
throw new Error(`Invalid icon for workspace ID ${record.id}`);
|
||||
}
|
||||
if (record.containerTabId != null && typeof record.containerTabId !== "number") {
|
||||
throw new Error(`Invalid containerTabId for workspace ID ${record.id}`);
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
function ZenWorkspacesEngine(service) {
|
||||
SyncEngine.call(this, "Workspaces", service);
|
||||
}
|
||||
|
||||
ZenWorkspacesEngine.prototype = {
|
||||
__proto__: SyncEngine.prototype,
|
||||
|
||||
_storeObj: ZenWorkspacesStore,
|
||||
_trackerObj: ZenWorkspacesTracker,
|
||||
_recordObj: ZenWorkspaceRecord,
|
||||
|
||||
};
|
||||
|
||||
function ZenWorkspaceRecord(collection, id) {
|
||||
CryptoWrapper.call(this, collection, id);
|
||||
}
|
||||
|
||||
ZenWorkspaceRecord.prototype = {
|
||||
__proto__: CryptoWrapper.prototype,
|
||||
_logName: "Sync.Record.ZenWorkspace",
|
||||
|
||||
};
|
||||
|
||||
Utils.deferGetSet(ZenWorkspaceRecord, "cleartext", [
|
||||
"name",
|
||||
"icon",
|
||||
"default",
|
||||
"containerTabId",
|
||||
]);
|
Loading…
Add table
Add a link
Reference in a new issue