From c8deb3610069a9f13222cfc840001f051bb5204c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristijan=20Ribari=C4=87?= Date: Wed, 16 Oct 2024 10:08:45 +0200 Subject: [PATCH] Re-implemented workspace reordering to be based on dragging. --- src/ZenWorkspaces.mjs | 132 ++++++++++++++++++++--------- src/ZenWorkspacesStorage.mjs | 156 ++++------------------------------- 2 files changed, 109 insertions(+), 179 deletions(-) diff --git a/src/ZenWorkspaces.mjs b/src/ZenWorkspaces.mjs index 728547f..53f458f 100644 --- a/src/ZenWorkspaces.mjs +++ b/src/ZenWorkspaces.mjs @@ -4,6 +4,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature { */ _lastSelectedWorkspaceTabs = {}; _inChangingWorkspace = false; + draggedElement = null; async init() { if (!this.shouldHaveWorkspaces) { @@ -324,6 +325,66 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature { element.classList.add('identity-color-' + containerGroup.color); element.setAttribute('data-usercontextid', containerGroup.userContextId); } + if (this.isReorderModeOn(browser)) { + element.setAttribute('draggable', 'true'); + } + + element.addEventListener('dragstart', function (event) { + if (this.isReorderModeOn(browser)) { + this.draggedElement = element; + event.dataTransfer.effectAllowed = 'move'; + event.dataTransfer.setData('text/plain', element.getAttribute('zen-workspace-id')); + element.classList.add('dragging'); + } else { + event.preventDefault(); + } + }.bind(browser.ZenWorkspaces)); + + element.addEventListener('dragover', function (event) { + if (this.isReorderModeOn(browser) && this.draggedElement) { + event.preventDefault(); + event.dataTransfer.dropEffect = 'move'; + } + }.bind(browser.ZenWorkspaces)); + + element.addEventListener('dragenter', function (event) { + if (this.isReorderModeOn(browser) && this.draggedElement) { + element.classList.add('dragover'); + } + }.bind(browser.ZenWorkspaces)); + + element.addEventListener('dragleave', function (event) { + element.classList.remove('dragover'); + }.bind(browser.ZenWorkspaces)); + + element.addEventListener('drop', async function (event) { + event.preventDefault(); + element.classList.remove('dragover'); + if (this.isReorderModeOn(browser)) { + const draggedWorkspaceId = event.dataTransfer.getData('text/plain'); + const targetWorkspaceId = element.getAttribute('zen-workspace-id'); + if (draggedWorkspaceId !== targetWorkspaceId) { + await this.moveWorkspace(draggedWorkspaceId, targetWorkspaceId); + await this._propagateWorkspaceData(); + } + if (this.draggedElement) { + this.draggedElement.classList.remove('dragging'); + this.draggedElement = null; + } + } + }.bind(browser.ZenWorkspaces)); + + element.addEventListener('dragend', function (event) { + if (this.draggedElement) { + this.draggedElement.classList.remove('dragging'); + this.draggedElement = null; + } + const workspaceElements = browser.document.querySelectorAll('.zen-workspace-button'); + for (const elem of workspaceElements) { + elem.classList.remove('dragover'); + } + }.bind(browser.ZenWorkspaces)); + let childs = browser.MozXULElement.parseXULToFragment(`
@@ -333,15 +394,7 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature {
-
- - - - - - -
- + @@ -349,16 +402,6 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature { // use text content instead of innerHTML to avoid XSS childs.querySelector('.zen-workspace-icon').textContent = browser.ZenWorkspaces.getWorkspaceIcon(workspace); - childs.querySelector('.zen-workspace-order-up').addEventListener('click', (async (event) => { - event.stopPropagation(); - event.preventDefault(); - await browser.ZenWorkspaces.moveWorkspaceUp(event, workspace.uuid); - }).bind(browser.ZenWorkspaces)); - childs.querySelector('.zen-workspace-order-down').addEventListener('click', (async (event) => { - event.stopPropagation(); - event.preventDefault(); - await browser.ZenWorkspaces.moveWorkspaceDown(event, workspace.uuid); - }).bind(browser.ZenWorkspaces)); childs.querySelector('.zen-workspace-name').textContent = workspace.name; if (containerGroup) { childs.querySelector('.zen-workspace-container').textContent = ContextualIdentityService.getUserContextLabel( @@ -376,6 +419,9 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature { }).bind(browser.ZenWorkspaces)); element.appendChild(childs); element.onclick = (async () => { + if (this.isReorderModeOn(browser)) { + return; // Return early if reorder mode is on + } if (event.target.closest('.zen-workspace-actions')) { return; // Ignore clicks on the actions button } @@ -410,38 +456,46 @@ var ZenWorkspaces = new (class extends ZenMultiWindowFeature { }); } - isLastWorkspace(workspace) { - let workspaces = this._workspaceCache; - return workspaces.workspaces.indexOf(workspace) === workspaces.workspaces.length - 1; + isReorderModeOn(browser) { + return browser.document.getElementById('PanelUI-zen-workspaces-list').getAttribute('reorder-mode') === 'true'; } - isFirstWorkspace(workspace) { - let workspaces = this._workspaceCache; - return workspaces.workspaces.indexOf(workspace) === 0; - } - - async moveWorkspaceUp(event, uuid) { - await ZenWorkspacesStorage.moveWorkspaceUp(uuid); - await this._propagateWorkspaceData(); - } - - async moveWorkspaceDown(event, uuid) { - await ZenWorkspacesStorage.moveWorkspaceDown(uuid); - await this._propagateWorkspaceData(); - } - - toggleReorderMode () { + toggleReorderMode() { const workspacesList = document.getElementById("PanelUI-zen-workspaces-list"); const reorderModeButton = document.getElementById("PanelUI-zen-workspaces-reorder-mode"); - if(workspacesList.getAttribute('reorder-mode') === 'true' && reorderModeButton.getAttribute('active') === 'true') { + const isActive = workspacesList.getAttribute('reorder-mode') === 'true'; + if (isActive) { workspacesList.removeAttribute('reorder-mode'); reorderModeButton.removeAttribute('active'); } else { workspacesList.setAttribute('reorder-mode', 'true'); reorderModeButton.setAttribute('active', 'true'); } + + // Update draggable attribute + const workspaceElements = document.querySelectorAll('.zen-workspace-button'); + workspaceElements.forEach(elem => { + if (isActive) { + elem.removeAttribute('draggable'); + } else { + elem.setAttribute('draggable', 'true'); + } + }); } + + async moveWorkspace(draggedWorkspaceId, targetWorkspaceId) { + const workspaces = (await this._workspaces()).workspaces; + const draggedIndex = workspaces.findIndex(w => w.uuid === draggedWorkspaceId); + const draggedWorkspace = workspaces.splice(draggedIndex, 1)[0]; + const targetIndex = workspaces.findIndex(w => w.uuid === targetWorkspaceId); + workspaces.splice(targetIndex, 0, draggedWorkspace); + + await ZenWorkspacesStorage.updateWorkspacePositions(workspaces); + } + + + async openWorkspacesDialog(event) { if (!this.workspaceEnabled) { return; diff --git a/src/ZenWorkspacesStorage.mjs b/src/ZenWorkspacesStorage.mjs index 477d5a9..27b6416 100644 --- a/src/ZenWorkspacesStorage.mjs +++ b/src/ZenWorkspacesStorage.mjs @@ -296,163 +296,39 @@ var ZenWorkspacesStorage = { return result.length ? parseInt(result[0].getResultByName('value'), 10) : 0; }, - async moveWorkspaceUp(uuid, notifyObservers = true) { + async updateWorkspacePositions(workspaces) { const changedUUIDs = new Set(); - await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.moveWorkspaceUp', async (db) => { + await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.updateWorkspacePositions', async (db) => { await db.executeTransaction(async () => { const now = Date.now(); - // Get the current workspace - const currentWorkspaceRows = await db.execute(` - SELECT uuid, "position" FROM zen_workspaces WHERE uuid = :uuid - `, { uuid }); + for (let i = 0; i < workspaces.length; i++) { + const workspace = workspaces[i]; + const newPosition = (i + 1) * 1000; - if (currentWorkspaceRows.length === 0) { - // Workspace not found - return; - } - - const currentWorkspace = { - uuid: currentWorkspaceRows[0].getResultByName('uuid'), - position: currentWorkspaceRows[0].getResultByName('position') - }; - - // Find the workspace before this one - const previousWorkspaceRows = await db.execute(` - SELECT uuid, "position" FROM zen_workspaces - WHERE "position" < :currentPosition - ORDER BY "position" DESC - LIMIT 1 - `, { currentPosition: currentWorkspace.position }); - - if (previousWorkspaceRows.length === 0) { - // No previous workspace; already at the top - return; - } - - const previousWorkspace = { - uuid: previousWorkspaceRows[0].getResultByName('uuid'), - position: previousWorkspaceRows[0].getResultByName('position') - }; - - // Swap positions - await db.execute(` + await db.execute(` UPDATE zen_workspaces SET "position" = :newPosition WHERE uuid = :uuid - `, { uuid: currentWorkspace.uuid, newPosition: previousWorkspace.position }); + `, { newPosition, uuid: workspace.uuid }); - await db.execute(` - UPDATE zen_workspaces - SET "position" = :newPosition - WHERE uuid = :uuid - `, { uuid: previousWorkspace.uuid, newPosition: currentWorkspace.position }); + changedUUIDs.add(workspace.uuid); - // Record the changes - await db.execute(` + // Record the change + await db.execute(` INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp) - VALUES (:uuid1, :timestamp), (:uuid2, :timestamp) + VALUES (:uuid, :timestamp) `, { - uuid1: currentWorkspace.uuid, - uuid2: previousWorkspace.uuid, - timestamp: Math.floor(now / 1000) - }); - - changedUUIDs.add(currentWorkspace.uuid); - changedUUIDs.add(previousWorkspace.uuid); + uuid: workspace.uuid, + timestamp: Math.floor(now / 1000) + }); + } await this.updateLastChangeTimestamp(db); - - // Check if reordering is necessary - if (this.shouldReorderWorkspaces(null, previousWorkspace.position, currentWorkspace.position)) { - await this.reorderAllWorkspaces(db, changedUUIDs); - } }); }); - if (notifyObservers) { - this._notifyWorkspacesChanged("zen-workspace-updated", Array.from(changedUUIDs)); - } - }, - - async moveWorkspaceDown(uuid, notifyObservers = true) { - const changedUUIDs = new Set(); - - await PlacesUtils.withConnectionWrapper('ZenWorkspacesStorage.moveWorkspaceDown', async (db) => { - await db.executeTransaction(async () => { - const now = Date.now(); - - // Get the current workspace - const currentWorkspaceRows = await db.execute(` - SELECT uuid, "position" FROM zen_workspaces WHERE uuid = :uuid - `, { uuid }); - - if (currentWorkspaceRows.length === 0) { - // Workspace not found - return; - } - - const currentWorkspace = { - uuid: currentWorkspaceRows[0].getResultByName('uuid'), - position: currentWorkspaceRows[0].getResultByName('position') - }; - - // Find the workspace after this one - const nextWorkspaceRows = await db.execute(` - SELECT uuid, "position" FROM zen_workspaces - WHERE "position" > :currentPosition - ORDER BY "position" ASC - LIMIT 1 - `, { currentPosition: currentWorkspace.position }); - - if (nextWorkspaceRows.length === 0) { - // No next workspace; already at the bottom - return; - } - - const nextWorkspace = { - uuid: nextWorkspaceRows[0].getResultByName('uuid'), - position: nextWorkspaceRows[0].getResultByName('position') - }; - - // Swap positions - await db.execute(` - UPDATE zen_workspaces - SET "position" = :newPosition - WHERE uuid = :uuid - `, { uuid: currentWorkspace.uuid, newPosition: nextWorkspace.position }); - - await db.execute(` - UPDATE zen_workspaces - SET "position" = :newPosition - WHERE uuid = :uuid - `, { uuid: nextWorkspace.uuid, newPosition: currentWorkspace.position }); - - // Record the changes - await db.execute(` - INSERT OR REPLACE INTO zen_workspaces_changes (uuid, timestamp) - VALUES (:uuid1, :timestamp), (:uuid2, :timestamp) - `, { - uuid1: currentWorkspace.uuid, - uuid2: nextWorkspace.uuid, - timestamp: Math.floor(now / 1000) - }); - - changedUUIDs.add(currentWorkspace.uuid); - changedUUIDs.add(nextWorkspace.uuid); - - await this.updateLastChangeTimestamp(db); - - // Check if reordering is necessary - if (this.shouldReorderWorkspaces(currentWorkspace.position, nextWorkspace.position, null)) { - await this.reorderAllWorkspaces(db, changedUUIDs); - } - }); - }); - - if (notifyObservers) { - this._notifyWorkspacesChanged("zen-workspace-updated", Array.from(changedUUIDs)); - } + this._notifyWorkspacesChanged("zen-workspace-updated", Array.from(changedUUIDs)); }, };