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 @@
+
|