mirror of
https://github.com/zen-browser/components.git
synced 2025-07-08 19:49:59 +02:00
Merge pull request #45 from kristijanribaric/fix(workspaces)-change-tab-to-different-workspace-fix
Fix(workspaces) performance optimizations
This commit is contained in:
commit
f2921d75b4
2 changed files with 149 additions and 119 deletions
|
@ -30,15 +30,19 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
Services.obs.addObserver(this, "weave:engine:sync:finish");
|
Services.obs.addObserver(this, "weave:engine:sync:finish");
|
||||||
}
|
}
|
||||||
|
|
||||||
observe(subject, topic, data) {
|
async observe(subject, topic, data) {
|
||||||
if (topic === "weave:engine:sync:finish" && data === "workspaces") {
|
if (topic === "weave:engine:sync:finish" && data === "workspaces") {
|
||||||
this._workspaceCache = null; // Clear cache to fetch fresh data
|
try {
|
||||||
this.updateWorkspaceStrip();
|
const lastChangeTimestamp = await ZenWorkspacesStorage.getLastChangeTimestamp();
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateWorkspaceStrip() {
|
if (!this._workspaceCache || !this._workspaceCache.lastChangeTimestamp || lastChangeTimestamp > this._workspaceCache.lastChangeTimestamp) {
|
||||||
this._propagateWorkspaceData().catch(console.error);
|
this._workspaceCache = null;
|
||||||
|
await this._propagateWorkspaceData();
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error updating workspaces after sync:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get shouldHaveWorkspaces() {
|
get shouldHaveWorkspaces() {
|
||||||
|
@ -72,8 +76,16 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
}
|
}
|
||||||
|
|
||||||
async _workspaces() {
|
async _workspaces() {
|
||||||
if (!this._workspaceCache) {
|
if (this._workspaceCache) {
|
||||||
this._workspaceCache = { workspaces: await ZenWorkspacesStorage.getWorkspaces() };
|
return this._workspaceCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
const [workspaces, lastChangeTimestamp] = await Promise.all([
|
||||||
|
ZenWorkspacesStorage.getWorkspaces(),
|
||||||
|
ZenWorkspacesStorage.getLastChangeTimestamp()
|
||||||
|
]);
|
||||||
|
|
||||||
|
this._workspaceCache = { workspaces, lastChangeTimestamp };
|
||||||
// Get the active workspace ID from preferences
|
// Get the active workspace ID from preferences
|
||||||
const activeWorkspaceId = Services.prefs.getStringPref('zen.workspaces.active', '');
|
const activeWorkspaceId = Services.prefs.getStringPref('zen.workspaces.active', '');
|
||||||
|
|
||||||
|
@ -89,7 +101,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
}
|
}
|
||||||
// sort by position
|
// sort by position
|
||||||
this._workspaceCache.workspaces.sort((a, b) => (a.position ?? Infinity) - (b.position ?? Infinity));
|
this._workspaceCache.workspaces.sort((a, b) => (a.position ?? Infinity) - (b.position ?? Infinity));
|
||||||
}
|
|
||||||
return this._workspaceCache;
|
return this._workspaceCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -832,6 +844,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
|
||||||
delete this._lastSelectedWorkspaceTabs[previousWorkspaceID];
|
delete this._lastSelectedWorkspaceTabs[previousWorkspaceID];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this._workspaceCache = null;
|
||||||
const workspaces = await this._workspaces();
|
const workspaces = await this._workspaces();
|
||||||
await this.changeWorkspace(workspaces.workspaces.find((workspace) => workspace.uuid === workspaceID));
|
await this.changeWorkspace(workspaces.workspaces.find((workspace) => workspace.uuid === workspaceID));
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,11 @@ var ZenWorkspacesStorage = {
|
||||||
)
|
)
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
// Create an index on the uuid column
|
||||||
|
await db.execute(`
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_zen_workspaces_uuid ON zen_workspaces(uuid)
|
||||||
|
`);
|
||||||
|
|
||||||
// Create the changes tracking table if it doesn't exist
|
// Create the changes tracking table if it doesn't exist
|
||||||
await db.execute(`
|
await db.execute(`
|
||||||
CREATE TABLE IF NOT EXISTS zen_workspaces_changes (
|
CREATE TABLE IF NOT EXISTS zen_workspaces_changes (
|
||||||
|
@ -28,6 +33,11 @@ var ZenWorkspacesStorage = {
|
||||||
timestamp INTEGER NOT NULL
|
timestamp INTEGER NOT NULL
|
||||||
)
|
)
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
// Create an index on the uuid column for changes tracking table
|
||||||
|
await db.execute(`
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_zen_workspaces_changes_uuid ON zen_workspaces_changes(uuid)
|
||||||
|
`);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -63,50 +73,26 @@ var ZenWorkspacesStorage = {
|
||||||
const changedUUIDs = new Set();
|
const changedUUIDs = new Set();
|
||||||
|
|
||||||
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.saveWorkspace', async (db) => {
|
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.saveWorkspace', async (db) => {
|
||||||
|
await db.executeTransaction(async () => {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
await db.executeTransaction(async function() {
|
// Handle default workspace
|
||||||
// If the workspace is set as default, unset is_default for all other workspaces
|
|
||||||
if (workspace.default) {
|
if (workspace.default) {
|
||||||
await db.execute(`UPDATE zen_workspaces SET is_default = 0 WHERE uuid != :uuid`, { uuid: workspace.uuid });
|
await db.execute(`UPDATE zen_workspaces SET is_default = 0 WHERE uuid != :uuid`, { uuid: workspace.uuid });
|
||||||
|
|
||||||
// Collect UUIDs of workspaces that were unset as default
|
|
||||||
const unsetDefaultRows = await db.execute(`SELECT uuid FROM zen_workspaces WHERE is_default = 0 AND uuid != :uuid`, { uuid: workspace.uuid });
|
const unsetDefaultRows = await db.execute(`SELECT uuid FROM zen_workspaces WHERE is_default = 0 AND uuid != :uuid`, { uuid: workspace.uuid });
|
||||||
for (const row of unsetDefaultRows) {
|
for (const row of unsetDefaultRows) {
|
||||||
changedUUIDs.add(row.getResultByName('uuid'));
|
changedUUIDs.add(row.getResultByName('uuid'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the current maximum position
|
let newPosition;
|
||||||
const maxOrderResult = await db.execute(`SELECT MAX("position") as max_position FROM zen_workspaces`);
|
if ('position' in workspace && Number.isFinite(workspace.position)) {
|
||||||
const maxOrder = maxOrderResult[0].getResultByName('max_position') || 0;
|
newPosition = workspace.position;
|
||||||
|
|
||||||
let newOrder;
|
|
||||||
|
|
||||||
if ('position' in workspace && workspace.position !== null && Number.isInteger(workspace.position)) {
|
|
||||||
// If position is provided, check if it's already occupied
|
|
||||||
const occupiedOrderResult = await db.execute(`
|
|
||||||
SELECT uuid FROM zen_workspaces WHERE "position" = :position AND uuid != :uuid
|
|
||||||
`, { position: workspace.position, uuid: workspace.uuid });
|
|
||||||
|
|
||||||
if (occupiedOrderResult.length > 0) {
|
|
||||||
// If the position is occupied, shift the positions of subsequent workspaces
|
|
||||||
await db.execute(`
|
|
||||||
UPDATE zen_workspaces
|
|
||||||
SET "position" = "position" + 1
|
|
||||||
WHERE "position" >= :position AND uuid != :uuid
|
|
||||||
`, { position: workspace.position, uuid: workspace.uuid });
|
|
||||||
|
|
||||||
// Collect UUIDs of workspaces whose positions were shifted
|
|
||||||
for (const row of occupiedOrderResult) {
|
|
||||||
changedUUIDs.add(row.getResultByName('uuid'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newOrder = workspace.position;
|
|
||||||
} else {
|
} else {
|
||||||
// If no position is provided, set it to the last position
|
// Get the maximum position
|
||||||
newOrder = maxOrder + 1;
|
const maxPositionResult = await db.execute(`SELECT MAX("position") as max_position FROM zen_workspaces`);
|
||||||
|
const maxPosition = maxPositionResult[0].getResultByName('max_position') || 0;
|
||||||
|
newPosition = maxPosition + 1000; // Add a large increment to avoid frequent reordering
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert or replace the workspace
|
// Insert or replace the workspace
|
||||||
|
@ -126,20 +112,21 @@ var ZenWorkspacesStorage = {
|
||||||
is_default: workspace.default ? 1 : 0,
|
is_default: workspace.default ? 1 : 0,
|
||||||
container_id: workspace.containerTabId || null,
|
container_id: workspace.containerTabId || null,
|
||||||
now,
|
now,
|
||||||
position: newOrder
|
position: newPosition
|
||||||
});
|
});
|
||||||
|
|
||||||
// Record the change in the changes tracking table
|
// Record the change
|
||||||
await db.execute(`
|
await db.execute(`
|
||||||
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
|
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
|
||||||
VALUES (:uuid, :timestamp)
|
VALUES (:uuid, :timestamp)
|
||||||
`, {
|
`, {
|
||||||
uuid: workspace.uuid,
|
uuid: workspace.uuid,
|
||||||
timestamp: Math.floor(now / 1000) // Unix timestamp in seconds
|
timestamp: Math.floor(now / 1000)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add the main workspace UUID to the changed set
|
|
||||||
changedUUIDs.add(workspace.uuid);
|
changedUUIDs.add(workspace.uuid);
|
||||||
|
|
||||||
|
await this.updateLastChangeTimestamp(db);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -150,7 +137,7 @@ var ZenWorkspacesStorage = {
|
||||||
|
|
||||||
async getWorkspaces() {
|
async getWorkspaces() {
|
||||||
const db = await PlacesUtils.promiseDBConnection();
|
const db = await PlacesUtils.promiseDBConnection();
|
||||||
const rows = await db.execute(`
|
const rows = await db.executeCached(`
|
||||||
SELECT * FROM zen_workspaces ORDER BY created_at ASC
|
SELECT * FROM zen_workspaces ORDER BY created_at ASC
|
||||||
`);
|
`);
|
||||||
return rows.map((row) => ({
|
return rows.map((row) => ({
|
||||||
|
@ -183,6 +170,8 @@ var ZenWorkspacesStorage = {
|
||||||
uuid,
|
uuid,
|
||||||
timestamp: Math.floor(now / 1000)
|
timestamp: Math.floor(now / 1000)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await this.updateLastChangeTimestamp(db);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (notifyObservers) {
|
if (notifyObservers) {
|
||||||
|
@ -194,6 +183,7 @@ var ZenWorkspacesStorage = {
|
||||||
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.wipeAllWorkspaces', async (db) => {
|
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.wipeAllWorkspaces', async (db) => {
|
||||||
await db.execute(`DELETE FROM zen_workspaces`);
|
await db.execute(`DELETE FROM zen_workspaces`);
|
||||||
await db.execute(`DELETE FROM zen_workspaces_changes`);
|
await db.execute(`DELETE FROM zen_workspaces_changes`);
|
||||||
|
await this.updateLastChangeTimestamp(db);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -201,7 +191,7 @@ var ZenWorkspacesStorage = {
|
||||||
const changedUUIDs = [];
|
const changedUUIDs = [];
|
||||||
|
|
||||||
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.setDefaultWorkspace', async (db) => {
|
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.setDefaultWorkspace', async (db) => {
|
||||||
await db.executeTransaction(async function () {
|
await db.executeTransaction(async () => {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
// Unset the default flag for all other workspaces
|
// Unset the default flag for all other workspaces
|
||||||
await db.execute(`UPDATE zen_workspaces SET is_default = 0 WHERE uuid != :uuid`, { uuid });
|
await db.execute(`UPDATE zen_workspaces SET is_default = 0 WHERE uuid != :uuid`, { uuid });
|
||||||
|
@ -226,6 +216,8 @@ var ZenWorkspacesStorage = {
|
||||||
|
|
||||||
// Add the main workspace UUID to the changed set
|
// Add the main workspace UUID to the changed set
|
||||||
changedUUIDs.push(uuid);
|
changedUUIDs.push(uuid);
|
||||||
|
|
||||||
|
await this.updateLastChangeTimestamp(db);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -265,64 +257,46 @@ var ZenWorkspacesStorage = {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateWorkspaceOrder(uuid, newOrder, notifyObservers = true) {
|
async updateWorkspaceOrder(uuid, newPosition, notifyObservers = true) {
|
||||||
const changedUUIDs = [uuid];
|
const changedUUIDs = new Set([uuid]);
|
||||||
|
|
||||||
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.updateWorkspaceOrder', async (db) => {
|
await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.updateWorkspaceOrder', async (db) => {
|
||||||
await db.executeTransaction(async function () {
|
await db.executeTransaction(async () => {
|
||||||
// Get the current position of the workspace
|
|
||||||
const currentOrderResult = await db.execute(`
|
|
||||||
SELECT "position" FROM zen_workspaces WHERE uuid = :uuid
|
|
||||||
`, { uuid });
|
|
||||||
const currentOrder = currentOrderResult[0].getResultByName('position');
|
|
||||||
|
|
||||||
if (currentOrder === newOrder) {
|
|
||||||
return; // No change needed
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newOrder > currentOrder) {
|
|
||||||
// Moving down: decrement position of workspaces between old and new positions
|
|
||||||
const rows = await db.execute(`
|
|
||||||
SELECT uuid FROM zen_workspaces
|
|
||||||
WHERE "position" > :currentOrder AND "position" <= :newOrder
|
|
||||||
`, { currentOrder, newOrder });
|
|
||||||
|
|
||||||
await db.execute(`
|
|
||||||
UPDATE zen_workspaces
|
|
||||||
SET "position" = "position" - 1
|
|
||||||
WHERE "position" > :currentOrder AND "position" <= :newOrder
|
|
||||||
`, { currentOrder, newOrder });
|
|
||||||
|
|
||||||
for (const row of rows) {
|
|
||||||
changedUUIDs.push(row.getResultByName('uuid'));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Moving up: increment position of workspaces between new and old positions
|
|
||||||
const rows = await db.execute(`
|
|
||||||
SELECT uuid FROM zen_workspaces
|
|
||||||
WHERE "position" >= :newOrder AND "position" < :currentOrder
|
|
||||||
`, { currentOrder, newOrder });
|
|
||||||
|
|
||||||
await db.execute(`
|
|
||||||
UPDATE zen_workspaces
|
|
||||||
SET "position" = "position" + 1
|
|
||||||
WHERE "position" >= :newOrder AND "position" < :currentOrder
|
|
||||||
`, { currentOrder, newOrder });
|
|
||||||
|
|
||||||
for (const row of rows) {
|
|
||||||
changedUUIDs.push(row.getResultByName('uuid'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the new position for the workspace
|
|
||||||
await db.execute(`
|
|
||||||
UPDATE zen_workspaces
|
|
||||||
SET "position" = :newOrder
|
|
||||||
WHERE uuid = :uuid
|
|
||||||
`, { uuid, newOrder });
|
|
||||||
|
|
||||||
// Record the change for the specified workspace
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
|
||||||
|
// Get the positions of the workspaces before and after the new position
|
||||||
|
const neighborPositions = await db.execute(`
|
||||||
|
SELECT
|
||||||
|
(SELECT MAX("position") FROM zen_workspaces WHERE "position" < :newPosition) as before_position,
|
||||||
|
(SELECT MIN("position") FROM zen_workspaces WHERE "position" > :newPosition) as after_position
|
||||||
|
`, { newPosition });
|
||||||
|
|
||||||
|
let beforePosition = neighborPositions[0].getResultByName('before_position');
|
||||||
|
let afterPosition = neighborPositions[0].getResultByName('after_position');
|
||||||
|
|
||||||
|
// Calculate the new position
|
||||||
|
if (beforePosition === null && afterPosition === null) {
|
||||||
|
// This is the only workspace
|
||||||
|
newPosition = 1000;
|
||||||
|
} else if (beforePosition === null) {
|
||||||
|
// This will be the first workspace
|
||||||
|
newPosition = Math.floor(afterPosition / 2);
|
||||||
|
} else if (afterPosition === null) {
|
||||||
|
// This will be the last workspace
|
||||||
|
newPosition = beforePosition + 1000;
|
||||||
|
} else {
|
||||||
|
// This workspace will be between two others
|
||||||
|
newPosition = Math.floor((beforePosition + afterPosition) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the workspace's position
|
||||||
|
await db.execute(`
|
||||||
|
UPDATE zen_workspaces
|
||||||
|
SET "position" = :newPosition
|
||||||
|
WHERE uuid = :uuid
|
||||||
|
`, { uuid, newPosition });
|
||||||
|
|
||||||
|
// Record the change
|
||||||
await db.execute(`
|
await db.execute(`
|
||||||
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
|
INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp)
|
||||||
VALUES (:uuid, :timestamp)
|
VALUES (:uuid, :timestamp)
|
||||||
|
@ -331,13 +305,56 @@ var ZenWorkspacesStorage = {
|
||||||
timestamp: Math.floor(now / 1000)
|
timestamp: Math.floor(now / 1000)
|
||||||
});
|
});
|
||||||
|
|
||||||
// Add the main workspace UUID to the changed set
|
await this.updateLastChangeTimestamp(db);
|
||||||
changedUUIDs.push(uuid);
|
|
||||||
|
// Check if reordering is necessary
|
||||||
|
if (this.shouldReorderWorkspaces(beforePosition, newPosition, afterPosition)) {
|
||||||
|
await this.reorderAllWorkspaces(db, changedUUIDs);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (notifyObservers) {
|
if (notifyObservers) {
|
||||||
this._notifyWorkspacesChanged("zen-workspace-updated", changedUUIDs);
|
this._notifyWorkspacesChanged("zen-workspace-updated", Array.from(changedUUIDs));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
shouldReorderWorkspaces(before, current, after) {
|
||||||
|
const minGap = 1; // Minimum allowed gap between positions
|
||||||
|
return (before !== null && current - before < minGap) || (after !== null && after - current < minGap);
|
||||||
|
},
|
||||||
|
|
||||||
|
async reorderAllWorkspaces(db, changedUUIDs) {
|
||||||
|
const workspaces = await db.execute(`
|
||||||
|
SELECT uuid
|
||||||
|
FROM zen_workspaces
|
||||||
|
ORDER BY "position" ASC
|
||||||
|
`);
|
||||||
|
|
||||||
|
for (let i = 0; i < workspaces.length; i++) {
|
||||||
|
const newPosition = (i + 1) * 1000; // Use large increments
|
||||||
|
await db.execute(`
|
||||||
|
UPDATE zen_workspaces
|
||||||
|
SET "position" = :newPosition
|
||||||
|
WHERE uuid = :uuid
|
||||||
|
`, { newPosition, uuid: workspaces[i].getResultByName('uuid') });
|
||||||
|
changedUUIDs.add(workspaces[i].getResultByName('uuid'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async updateLastChangeTimestamp(db) {
|
||||||
|
const now = Date.now();
|
||||||
|
await db.execute(`
|
||||||
|
INSERT OR REPLACE INTO moz_meta (key, value)
|
||||||
|
VALUES ('zen_workspaces_last_change', :now)
|
||||||
|
`, { now });
|
||||||
|
},
|
||||||
|
|
||||||
|
async getLastChangeTimestamp() {
|
||||||
|
const db = await PlacesUtils.promiseDBConnection();
|
||||||
|
const result = await db.executeCached(`
|
||||||
|
SELECT value FROM moz_meta WHERE key = 'zen_workspaces_last_change'
|
||||||
|
`);
|
||||||
|
return result.length ? parseInt(result[0].getResultByName('value'), 10) : 0;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue