diff --git a/src/css/popup.css b/src/css/popup.css index 3ce65aa..4b8e5be 100644 --- a/src/css/popup.css +++ b/src/css/popup.css @@ -668,6 +668,14 @@ span ~ .panel-header-text { fill: #0094fb; } +.container-panel-row:hover { + background-color: #ebebeb !important; +} + +.highlight { + background-color: #0094fb3f !important; +} + /* Panel Footer */ .panel-footer { align-items: center; diff --git a/src/js/popup.js b/src/js/popup.js index 1349ffa..2a2afcd 100644 --- a/src/js/popup.js +++ b/src/js/popup.js @@ -77,7 +77,7 @@ const Logic = { _previousPanel: null, _panels: {}, _onboardingVariation: null, - + _currentSelectedIdentities: [], async init() { // Remove browserAction "upgraded" badge when opening panel this.clearBrowserActionBadge(); @@ -265,6 +265,10 @@ const Logic = { }); }, + async resetSelectedIdentities() { + this._currentSelectedIdentities = []; + }, + getPanelSelector(panel) { if (this._onboardingVariation === "securityOnboarding" && // eslint-disable-next-line no-prototype-builtins @@ -330,6 +334,26 @@ const Logic = { } return this._currentIdentity; }, + currentSelectedIdentities() { + if (!this._currentSelectedIdentities) { + throw new Error("current Selected Identities must be set before calling Logic.currentSelectedIdentities."); + } + return this._currentSelectedIdentities; + }, + + addSelectedIdentity(identity) { + if (!this._currentSelectedIdentities.includes(identity)) { + this._currentSelectedIdentities.push(identity); + } + }, + + removeSelectedIdentity(identity) { + const index = this._currentSelectedIdentities.indexOf(identity); + + if (index !== -1) { + this._currentSelectedIdentities.splice(index, 1); + } + }, currentUserContextId() { const identity = Logic.currentIdentity(); @@ -960,23 +984,65 @@ Logic.registerPanel(P_CONTAINER_INFO, { Logic.registerPanel(P_CONTAINERS_EDIT, { panelSelector: "#edit-containers-panel", + lastSelected: null, + shiftOn: false, + + /* This function is the handler of deletion for both keypress delete and delete button. + The keypress delete support for both backspace and delete key. + */ + async deleteHandler() { + const selectedIdentities = Logic.currentSelectedIdentities(); + if (selectedIdentities.length > 0) { + await Logic.showPanel(P_CONTAINER_DELETE); + } + }, + + /* The function is to update the delete button. + The delete button shows up once any containers are selected. + */ + updateDeleteButton(selectedContainers) { + const deleteButton = document.querySelector("div.panel-footer.panel-footer-secondary"); + if (selectedContainers.length === 0) { + deleteButton.classList.add("hide"); + } else { + deleteButton.classList.remove("hide"); + } + }, // This method is called when the object is registered. initialize() { Logic.addEnterHandler(document.querySelector("#exit-edit-mode-link"), () => { Logic.showPanel(P_CONTAINERS_LIST); }); + + Logic.addEnterHandler(document.querySelector("#delete-link"), this.deleteHandler); + + document.addEventListener("keydown", e => { + if (e.keyCode === 16) { + this.shiftOn = true; + } else if (e.keyCode === 8 || e.keyCode === 48) { + this.deleteHandler(); + } + }); + + document.addEventListener("keyup", e => { + if (e.keyCode === 16) { + this.shiftOn = false; + } + }); }, // This method is called when the panel is shown. prepare() { + Logic.resetSelectedIdentities(); + this.updateDeleteButton(Logic.currentSelectedIdentities()); const fragment = document.createDocumentFragment(); Logic.identities().forEach(identity => { const tr = document.createElement("tr"); fragment.appendChild(tr); tr.classList.add("container-panel-row"); tr.innerHTML = escaped` - +
end) { + start = [end, end=start][0]; + } + + // get container panel rows to update highlight status + const rows = document.querySelectorAll(".unstriped .container-panel-row"); + + + // select or unselected target containers + if (index === -1) { + for (let i = start; i <= end; i++) { + Logic.addSelectedIdentity(identities[i]); + rows[i].classList.add("highlight"); + } + } else { + for (let i = start; i <= end; i++) { + Logic.removeSelectedIdentity(identities[i]); + rows[i].classList.remove("highlight"); + } + } + + this.lastSelected = identity; + this.updateDeleteButton(currentSelectedIdentity); } }); }); @@ -1190,6 +1292,7 @@ Logic.registerPanel(P_CONTAINER_DELETE, { // This method is called when the object is registered. initialize() { Logic.addEnterHandler(document.querySelector("#delete-container-cancel-link"), () => { + Logic.resetSelectedIdentities(); Logic.showPreviousPanel(); }); @@ -1199,35 +1302,56 @@ Logic.registerPanel(P_CONTAINER_DELETE, { if you want to do anything post delete do it in the background script. Browser console currently warns about not listening also. */ + + // if current identity is not null, then delete single, otherwise, delete all selected + let currentSelection; try { - await Logic.removeIdentity(Logic.userContextId(Logic.currentIdentity().cookieStoreId)); + currentSelection = [Logic.currentIdentity()]; + } catch (e) { + currentSelection = Logic.currentSelectedIdentities(); + } + + try { + // loop through each selected container + for (let i = 0; i < currentSelection.length; i++) { + await Logic.removeIdentity(Logic.userContextId(currentSelection[i].cookieStoreId)); + } await Logic.refreshIdentities(); + await Logic.resetSelectedIdentities(); Logic.showPreviousPanel(); } catch (e) { - Logic.showPanel(P_CONTAINERS_LIST); + await Logic.showPanel(P_CONTAINERS_LIST); } }); }, // This method is called when the panel is shown. prepare() { - const identity = Logic.currentIdentity(); - - // Populating the panel: name, icon, and warning message - document.getElementById("delete-container-name").textContent = identity.name; - - const totalNumberOfTabs = identity.numberOfHiddenTabs + identity.numberOfOpenTabs; - let warningMessage = ""; - if (totalNumberOfTabs > 0) { - const grammaticalNumTabs = totalNumberOfTabs > 1 ? "tabs" : "tab"; - warningMessage = `If you remove this container now, ${totalNumberOfTabs} container ${grammaticalNumTabs} will be closed.`; + // if current identity is not null, then show single container information, otherwise show all selected + let currentSelection; + try { + currentSelection = [Logic.currentIdentity()]; + } catch (e) { + currentSelection = Logic.currentSelectedIdentities(); } - document.getElementById("delete-container-tab-warning").textContent = warningMessage; - - const icon = document.getElementById("delete-container-icon"); - icon.setAttribute("data-identity-icon", identity.icon); - icon.setAttribute("data-identity-color", identity.color); + // right now for mult-selection, it displays the first item in the selection at the icon and name + // Populating the panel: name, icon, and warning message + document.getElementById("delete-container-name").textContent = currentSelection[0].name; + document.getElementById("delete-container-tab-warning").textContent = ""; + for (let i = 0; i < currentSelection.length; i++) { + const identity = currentSelection[i]; + const totalNumberOfTabs = identity.numberOfHiddenTabs + identity.numberOfOpenTabs; + let warningMessage = ""; + if (totalNumberOfTabs > 0) { + const grammaticalNumTabs = totalNumberOfTabs > 1 ? "tabs" : "tab"; + warningMessage =`If you remove ${identity.name} container now, ${totalNumberOfTabs} container ${grammaticalNumTabs} will be closed.`; + } + document.getElementById("delete-container-tab-warning").textContent += warningMessage; + const icon = document.getElementById("delete-container-icon"); + icon.setAttribute("data-identity-icon", identity.icon); + icon.setAttribute("data-identity-color", identity.color); + } return Promise.resolve(null); }, }); diff --git a/src/popup.html b/src/popup.html index 813a02d..8e03d4d 100644 --- a/src/popup.html +++ b/src/popup.html @@ -179,6 +179,9 @@
+