mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 13:53:34 +00:00
PM-25075 [Remove - Step 1] Flagged logic from clients/server and clients notification-refresh feature flag (#16113)
* PM-25075 wip parking work * remove flag from enums and add fade out * fix tests * remove flags from enum file after merge conflict re introduced * remove dead code paths * change naming back to bgUnlockPopoutOpened
This commit is contained in:
@@ -43,17 +43,13 @@ describe("AuthPopoutWindow", () => {
|
|||||||
singleActionKey: AuthPopoutType.unlockExtension,
|
singleActionKey: AuthPopoutType.unlockExtension,
|
||||||
senderWindowId: 1,
|
senderWindowId: 1,
|
||||||
});
|
});
|
||||||
expect(sendMessageDataSpy).toHaveBeenCalledWith(senderTab, "bgUnlockPopoutOpened", {
|
expect(sendMessageDataSpy).toHaveBeenCalledWith(senderTab, "bgUnlockPopoutOpened", {});
|
||||||
skipNotification: false,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("sends an indication that the presenting the notification bar for unlocking the extension should be skipped", async () => {
|
it("sends the bgUnlockPopoutOpened message", async () => {
|
||||||
await openUnlockPopout(senderTab, true);
|
await openUnlockPopout(senderTab);
|
||||||
|
|
||||||
expect(sendMessageDataSpy).toHaveBeenCalledWith(senderTab, "bgUnlockPopoutOpened", {
|
expect(sendMessageDataSpy).toHaveBeenCalledWith(senderTab, "bgUnlockPopoutOpened", {});
|
||||||
skipNotification: true,
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("closes any existing popup window types that are open to the unlock extension route", async () => {
|
it("closes any existing popup window types that are open to the unlock extension route", async () => {
|
||||||
|
|||||||
@@ -20,9 +20,8 @@ const extensionUnlockUrls = new Set([
|
|||||||
* Opens a window that facilitates unlocking / logging into the extension.
|
* Opens a window that facilitates unlocking / logging into the extension.
|
||||||
*
|
*
|
||||||
* @param senderTab - Used to determine the windowId of the sender.
|
* @param senderTab - Used to determine the windowId of the sender.
|
||||||
* @param skipNotification - Used to determine whether to show the unlock notification.
|
|
||||||
*/
|
*/
|
||||||
async function openUnlockPopout(senderTab: chrome.tabs.Tab, skipNotification = false) {
|
async function openUnlockPopout(senderTab: chrome.tabs.Tab) {
|
||||||
const existingPopoutWindowTabs = await BrowserApi.tabsQuery({ windowType: "popup" });
|
const existingPopoutWindowTabs = await BrowserApi.tabsQuery({ windowType: "popup" });
|
||||||
existingPopoutWindowTabs.forEach((tab) => {
|
existingPopoutWindowTabs.forEach((tab) => {
|
||||||
if (extensionUnlockUrls.has(tab.url)) {
|
if (extensionUnlockUrls.has(tab.url)) {
|
||||||
@@ -36,7 +35,7 @@ async function openUnlockPopout(senderTab: chrome.tabs.Tab, skipNotification = f
|
|||||||
singleActionKey: AuthPopoutType.unlockExtension,
|
singleActionKey: AuthPopoutType.unlockExtension,
|
||||||
senderWindowId: senderTab.windowId,
|
senderWindowId: senderTab.windowId,
|
||||||
});
|
});
|
||||||
await BrowserApi.tabSendMessageData(senderTab, "bgUnlockPopoutOpened", { skipNotification });
|
await BrowserApi.tabSendMessageData(senderTab, "bgUnlockPopoutOpened", {});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -141,7 +141,6 @@ type NotificationBackgroundExtensionMessageHandlers = {
|
|||||||
sender,
|
sender,
|
||||||
}: BackgroundOnMessageHandlerParams) => Promise<void>;
|
}: BackgroundOnMessageHandlerParams) => Promise<void>;
|
||||||
bgNeverSave: ({ sender }: BackgroundSenderParam) => Promise<void>;
|
bgNeverSave: ({ sender }: BackgroundSenderParam) => Promise<void>;
|
||||||
bgUnlockPopoutOpened: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
|
|
||||||
bgReopenUnlockPopout: ({ sender }: BackgroundSenderParam) => Promise<void>;
|
bgReopenUnlockPopout: ({ sender }: BackgroundSenderParam) => Promise<void>;
|
||||||
checkNotificationQueue: ({ sender }: BackgroundSenderParam) => Promise<void>;
|
checkNotificationQueue: ({ sender }: BackgroundSenderParam) => Promise<void>;
|
||||||
collectPageDetailsResponse: ({ message }: BackgroundMessageParam) => Promise<void>;
|
collectPageDetailsResponse: ({ message }: BackgroundMessageParam) => Promise<void>;
|
||||||
|
|||||||
@@ -817,6 +817,7 @@ describe("NotificationBackground", () => {
|
|||||||
reprompt: CipherRepromptType.None,
|
reprompt: CipherRepromptType.None,
|
||||||
});
|
});
|
||||||
getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView);
|
getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView);
|
||||||
|
taskService.tasksEnabled$.mockImplementation(() => of(false));
|
||||||
|
|
||||||
sendMockExtensionMessage(message, sender);
|
sendMockExtensionMessage(message, sender);
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
@@ -865,7 +866,7 @@ describe("NotificationBackground", () => {
|
|||||||
reprompt: CipherRepromptType.Password,
|
reprompt: CipherRepromptType.Password,
|
||||||
});
|
});
|
||||||
getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView);
|
getDecryptedCipherByIdSpy.mockResolvedValueOnce(cipherView);
|
||||||
|
taskService.tasksEnabled$.mockImplementation(() => of(false));
|
||||||
sendMockExtensionMessage(message, sender);
|
sendMockExtensionMessage(message, sender);
|
||||||
await flushPromises();
|
await flushPromises();
|
||||||
|
|
||||||
@@ -913,9 +914,6 @@ describe("NotificationBackground", () => {
|
|||||||
taskService.pendingTasks$.mockImplementation(() =>
|
taskService.pendingTasks$.mockImplementation(() =>
|
||||||
of([mockSecurityTask, mockSecurityTask2]),
|
of([mockSecurityTask, mockSecurityTask2]),
|
||||||
);
|
);
|
||||||
jest
|
|
||||||
.spyOn(notificationBackground as any, "getNotificationFlag")
|
|
||||||
.mockResolvedValueOnce(true);
|
|
||||||
jest.spyOn(notificationBackground as any, "getOrgData").mockResolvedValueOnce([
|
jest.spyOn(notificationBackground as any, "getOrgData").mockResolvedValueOnce([
|
||||||
{
|
{
|
||||||
id: mockOrgId,
|
id: mockOrgId,
|
||||||
@@ -1372,74 +1370,6 @@ describe("NotificationBackground", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("bgUnlockPopoutOpened message handler", () => {
|
|
||||||
let pushUnlockVaultToQueueSpy: jest.SpyInstance;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
pushUnlockVaultToQueueSpy = jest.spyOn(
|
|
||||||
notificationBackground as any,
|
|
||||||
"pushUnlockVaultToQueue",
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("skips pushing the unlock vault message to the queue if the message indicates that the notification should be skipped", async () => {
|
|
||||||
const tabMock = createChromeTabMock();
|
|
||||||
const sender = mock<chrome.runtime.MessageSender>({ tab: tabMock });
|
|
||||||
const message: NotificationBackgroundExtensionMessage = {
|
|
||||||
command: "bgUnlockPopoutOpened",
|
|
||||||
data: { skipNotification: true },
|
|
||||||
};
|
|
||||||
|
|
||||||
sendMockExtensionMessage(message, sender);
|
|
||||||
await flushPromises();
|
|
||||||
|
|
||||||
expect(pushUnlockVaultToQueueSpy).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("skips pushing the unlock vault message to the queue if the auth status is not `Locked`", async () => {
|
|
||||||
const tabMock = createChromeTabMock();
|
|
||||||
const sender = mock<chrome.runtime.MessageSender>({ tab: tabMock });
|
|
||||||
const message: NotificationBackgroundExtensionMessage = {
|
|
||||||
command: "bgUnlockPopoutOpened",
|
|
||||||
};
|
|
||||||
activeAccountStatusMock$.next(AuthenticationStatus.LoggedOut);
|
|
||||||
|
|
||||||
sendMockExtensionMessage(message, sender);
|
|
||||||
await flushPromises();
|
|
||||||
|
|
||||||
expect(pushUnlockVaultToQueueSpy).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("skips pushing the unlock vault message to the queue if the notification queue already has an item", async () => {
|
|
||||||
const tabMock = createChromeTabMock();
|
|
||||||
const sender = mock<chrome.runtime.MessageSender>({ tab: tabMock });
|
|
||||||
const message: NotificationBackgroundExtensionMessage = {
|
|
||||||
command: "bgUnlockPopoutOpened",
|
|
||||||
};
|
|
||||||
activeAccountStatusMock$.next(AuthenticationStatus.Locked);
|
|
||||||
notificationBackground["notificationQueue"] = [mock<AddLoginQueueMessage>()];
|
|
||||||
|
|
||||||
sendMockExtensionMessage(message, sender);
|
|
||||||
await flushPromises();
|
|
||||||
|
|
||||||
expect(pushUnlockVaultToQueueSpy).not.toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it("sends an unlock vault message to the queue if the user has a locked vault", async () => {
|
|
||||||
const tabMock = createChromeTabMock({ url: "https://example.com" });
|
|
||||||
const sender = mock<chrome.runtime.MessageSender>({ tab: tabMock });
|
|
||||||
const message: NotificationBackgroundExtensionMessage = {
|
|
||||||
command: "bgUnlockPopoutOpened",
|
|
||||||
};
|
|
||||||
activeAccountStatusMock$.next(AuthenticationStatus.Locked);
|
|
||||||
|
|
||||||
sendMockExtensionMessage(message, sender);
|
|
||||||
await flushPromises();
|
|
||||||
|
|
||||||
expect(pushUnlockVaultToQueueSpy).toHaveBeenCalledWith("example.com", sender.tab);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("checkNotificationQueue", () => {
|
describe("checkNotificationQueue", () => {
|
||||||
let doNotificationQueueCheckSpy: jest.SpyInstance;
|
let doNotificationQueueCheckSpy: jest.SpyInstance;
|
||||||
let getTabFromCurrentWindowSpy: jest.SpyInstance;
|
let getTabFromCurrentWindowSpy: jest.SpyInstance;
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import {
|
|||||||
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
|
||||||
import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service";
|
import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service";
|
||||||
import { ProductTierType } from "@bitwarden/common/billing/enums/product-tier-type.enum";
|
import { ProductTierType } from "@bitwarden/common/billing/enums/product-tier-type.enum";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
||||||
import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
|
import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { ServerConfig } from "@bitwarden/common/platform/abstractions/config/server-config";
|
import { ServerConfig } from "@bitwarden/common/platform/abstractions/config/server-config";
|
||||||
@@ -67,7 +66,6 @@ import { TemporaryNotificationChangeLoginService } from "../services/notificatio
|
|||||||
import {
|
import {
|
||||||
AddChangePasswordNotificationQueueMessage,
|
AddChangePasswordNotificationQueueMessage,
|
||||||
AddLoginQueueMessage,
|
AddLoginQueueMessage,
|
||||||
AddUnlockVaultQueueMessage,
|
|
||||||
AddLoginMessageData,
|
AddLoginMessageData,
|
||||||
NotificationQueueMessageItem,
|
NotificationQueueMessageItem,
|
||||||
LockedVaultPendingNotificationsData,
|
LockedVaultPendingNotificationsData,
|
||||||
@@ -116,12 +114,10 @@ export default class NotificationBackground {
|
|||||||
bgSaveCipher: ({ message, sender }) => this.handleSaveCipherMessage(message, sender),
|
bgSaveCipher: ({ message, sender }) => this.handleSaveCipherMessage(message, sender),
|
||||||
bgHandleReprompt: ({ message, sender }: any) =>
|
bgHandleReprompt: ({ message, sender }: any) =>
|
||||||
this.handleCipherUpdateRepromptResponse(message),
|
this.handleCipherUpdateRepromptResponse(message),
|
||||||
bgUnlockPopoutOpened: ({ message, sender }) => this.unlockVault(message, sender.tab),
|
|
||||||
checkNotificationQueue: ({ sender }) => this.checkNotificationQueue(sender.tab),
|
checkNotificationQueue: ({ sender }) => this.checkNotificationQueue(sender.tab),
|
||||||
collectPageDetailsResponse: ({ message }) =>
|
collectPageDetailsResponse: ({ message }) =>
|
||||||
this.handleCollectPageDetailsResponseMessage(message),
|
this.handleCollectPageDetailsResponseMessage(message),
|
||||||
getWebVaultUrlForNotification: () => this.getWebVaultUrl(),
|
getWebVaultUrlForNotification: () => this.getWebVaultUrl(),
|
||||||
notificationRefreshFlagValue: () => this.getNotificationFlag(),
|
|
||||||
unlockCompleted: ({ message, sender }) => this.handleUnlockCompleted(message, sender),
|
unlockCompleted: ({ message, sender }) => this.handleUnlockCompleted(message, sender),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -351,15 +347,6 @@ export default class NotificationBackground {
|
|||||||
return await firstValueFrom(this.configService.serverConfig$);
|
return await firstValueFrom(this.configService.serverConfig$);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the current value of the notification refresh feature flag
|
|
||||||
* @returns Promise<boolean> indicating if the feature is enabled
|
|
||||||
*/
|
|
||||||
async getNotificationFlag(): Promise<boolean> {
|
|
||||||
const flagValue = await this.configService.getFeatureFlag(FeatureFlag.NotificationRefresh);
|
|
||||||
return flagValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the current authentication status of the user.
|
* Gets the current authentication status of the user.
|
||||||
* @returns Promise<AuthenticationStatus> - The current authentication status of the user.
|
* @returns Promise<AuthenticationStatus> - The current authentication status of the user.
|
||||||
@@ -465,11 +452,6 @@ export default class NotificationBackground {
|
|||||||
data: ModifyLoginCipherFormData,
|
data: ModifyLoginCipherFormData,
|
||||||
tab: chrome.tabs.Tab,
|
tab: chrome.tabs.Tab,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
const flag = await this.getNotificationFlag();
|
|
||||||
if (!flag) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const activeUserId = await firstValueFrom(
|
const activeUserId = await firstValueFrom(
|
||||||
this.accountService.activeAccount$.pipe(getOptionalUserId),
|
this.accountService.activeAccount$.pipe(getOptionalUserId),
|
||||||
);
|
);
|
||||||
@@ -683,34 +665,6 @@ export default class NotificationBackground {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets up a notification to unlock the vault when the user
|
|
||||||
* attempts to autofill a cipher while the vault is locked.
|
|
||||||
*
|
|
||||||
* @param message - Extension message, determines if the notification should be skipped
|
|
||||||
* @param tab - The tab that the message was sent from
|
|
||||||
*/
|
|
||||||
private async unlockVault(message: NotificationBackgroundExtensionMessage, tab: chrome.tabs.Tab) {
|
|
||||||
const notificationRefreshFlagEnabled = await this.getNotificationFlag();
|
|
||||||
if (message.data?.skipNotification) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notificationRefreshFlagEnabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentAuthStatus = await this.getAuthStatus();
|
|
||||||
if (currentAuthStatus !== AuthenticationStatus.Locked || this.notificationQueue.length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const loginDomain = Utils.getDomain(tab.url);
|
|
||||||
if (loginDomain) {
|
|
||||||
await this.pushUnlockVaultToQueue(loginDomain, tab);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async pushChangePasswordToQueue(
|
private async pushChangePasswordToQueue(
|
||||||
cipherId: string,
|
cipherId: string,
|
||||||
loginDomain: string,
|
loginDomain: string,
|
||||||
@@ -734,20 +688,6 @@ export default class NotificationBackground {
|
|||||||
await this.checkNotificationQueue(tab);
|
await this.checkNotificationQueue(tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async pushUnlockVaultToQueue(loginDomain: string, tab: chrome.tabs.Tab) {
|
|
||||||
this.removeTabFromNotificationQueue(tab);
|
|
||||||
const launchTimestamp = new Date().getTime();
|
|
||||||
const message: AddUnlockVaultQueueMessage = {
|
|
||||||
type: NotificationType.UnlockVault,
|
|
||||||
domain: loginDomain,
|
|
||||||
tab: tab,
|
|
||||||
launchTimestamp,
|
|
||||||
expires: new Date(launchTimestamp + 0.5 * 60000), // 30 seconds
|
|
||||||
wasVaultLocked: true,
|
|
||||||
};
|
|
||||||
await this.sendNotificationQueueMessage(tab, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves a cipher based on the message sent from the notification bar. If the vault
|
* Saves a cipher based on the message sent from the notification bar. If the vault
|
||||||
* is locked, the message will be added to the notification queue and the unlock
|
* is locked, the message will be added to the notification queue and the unlock
|
||||||
@@ -906,12 +846,11 @@ export default class NotificationBackground {
|
|||||||
}
|
}
|
||||||
const cipher = await this.cipherService.encrypt(cipherView, userId);
|
const cipher = await this.cipherService.encrypt(cipherView, userId);
|
||||||
|
|
||||||
const shouldGetTasks = await this.getNotificationFlag();
|
|
||||||
try {
|
try {
|
||||||
if (!cipherView.edit) {
|
if (!cipherView.edit) {
|
||||||
throw new Error("You do not have permission to edit this cipher.");
|
throw new Error("You do not have permission to edit this cipher.");
|
||||||
}
|
}
|
||||||
const tasks = shouldGetTasks ? await this.getSecurityTasks(userId) : [];
|
const tasks = await this.getSecurityTasks(userId);
|
||||||
const updatedCipherTask = tasks.find((task) => task.cipherId === cipherView?.id);
|
const updatedCipherTask = tasks.find((task) => task.cipherId === cipherView?.id);
|
||||||
const cipherHasTask = !!updatedCipherTask?.id;
|
const cipherHasTask = !!updatedCipherTask?.id;
|
||||||
|
|
||||||
|
|||||||
@@ -2107,7 +2107,7 @@ export class OverlayBackground implements OverlayBackgroundInterface {
|
|||||||
"addToLockedVaultPendingNotifications",
|
"addToLockedVaultPendingNotifications",
|
||||||
retryMessage,
|
retryMessage,
|
||||||
);
|
);
|
||||||
await this.openUnlockPopout(sender.tab, true);
|
await this.openUnlockPopout(sender.tab);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -100,10 +100,19 @@ describe("ContentMessageHandler", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("forwards the message to the extension background if it is present in the forwardCommands list", () => {
|
it("forwards the message to the extension background if it is present in the forwardCommands list", () => {
|
||||||
sendMockExtensionMessage({ command: "bgUnlockPopoutOpened" });
|
const forwardCommands = [
|
||||||
|
"addToLockedVaultPendingNotifications",
|
||||||
|
"unlockCompleted",
|
||||||
|
"addedCipher",
|
||||||
|
];
|
||||||
|
|
||||||
expect(sendMessageSpy).toHaveBeenCalledTimes(1);
|
forwardCommands.forEach((command) => {
|
||||||
expect(sendMessageSpy).toHaveBeenCalledWith({ command: "bgUnlockPopoutOpened" });
|
sendMockExtensionMessage({ command });
|
||||||
|
|
||||||
|
expect(sendMessageSpy).toHaveBeenCalledWith({ command });
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(sendMessageSpy).toHaveBeenCalledTimes(forwardCommands.length);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { render } from "lit";
|
import { render } from "lit";
|
||||||
|
|
||||||
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums";
|
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums";
|
||||||
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
|
|
||||||
import type { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
import type { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||||
|
|
||||||
import { AdjustNotificationBarMessageData } from "../background/abstractions/notification.background";
|
|
||||||
import { NotificationCipherData } from "../content/components/cipher/types";
|
import { NotificationCipherData } from "../content/components/cipher/types";
|
||||||
import { CollectionView, I18n, OrgView } from "../content/components/common-types";
|
import { CollectionView, I18n, OrgView } from "../content/components/common-types";
|
||||||
import { AtRiskNotification } from "../content/components/notification/at-risk-password/container";
|
import { AtRiskNotification } from "../content/components/notification/at-risk-password/container";
|
||||||
@@ -12,8 +10,6 @@ import { NotificationConfirmationContainer } from "../content/components/notific
|
|||||||
import { NotificationContainer } from "../content/components/notification/container";
|
import { NotificationContainer } from "../content/components/notification/container";
|
||||||
import { selectedFolder as selectedFolderSignal } from "../content/components/signals/selected-folder";
|
import { selectedFolder as selectedFolderSignal } from "../content/components/signals/selected-folder";
|
||||||
import { selectedVault as selectedVaultSignal } from "../content/components/signals/selected-vault";
|
import { selectedVault as selectedVaultSignal } from "../content/components/signals/selected-vault";
|
||||||
import { buildSvgDomElement } from "../utils";
|
|
||||||
import { circleCheckIcon } from "../utils/svg-icons";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
NotificationBarWindowMessageHandlers,
|
NotificationBarWindowMessageHandlers,
|
||||||
@@ -23,34 +19,18 @@ import {
|
|||||||
NotificationTypes,
|
NotificationTypes,
|
||||||
} from "./abstractions/notification-bar";
|
} from "./abstractions/notification-bar";
|
||||||
|
|
||||||
const logService = new ConsoleLogService(false);
|
|
||||||
let notificationBarIframeInitData: NotificationBarIframeInitData = {};
|
let notificationBarIframeInitData: NotificationBarIframeInitData = {};
|
||||||
let windowMessageOrigin: string;
|
let windowMessageOrigin: string;
|
||||||
let useComponentBar = false;
|
|
||||||
|
|
||||||
const notificationBarWindowMessageHandlers: NotificationBarWindowMessageHandlers = {
|
const notificationBarWindowMessageHandlers: NotificationBarWindowMessageHandlers = {
|
||||||
initNotificationBar: ({ message }) => initNotificationBar(message),
|
initNotificationBar: ({ message }) => initNotificationBar(message),
|
||||||
saveCipherAttemptCompleted: ({ message }) =>
|
saveCipherAttemptCompleted: ({ message }) => handleSaveCipherConfirmation(message),
|
||||||
useComponentBar
|
|
||||||
? handleSaveCipherConfirmation(message)
|
|
||||||
: handleSaveCipherAttemptCompletedMessage(message),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
globalThis.addEventListener("load", load);
|
globalThis.addEventListener("load", load);
|
||||||
|
|
||||||
function load() {
|
function load() {
|
||||||
setupWindowMessageListener();
|
setupWindowMessageListener();
|
||||||
sendPlatformMessage({ command: "notificationRefreshFlagValue" }, (flagValue) => {
|
|
||||||
useComponentBar = flagValue;
|
|
||||||
applyNotificationBarStyle();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function applyNotificationBarStyle() {
|
|
||||||
if (!useComponentBar) {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
||||||
require("./bar.scss");
|
|
||||||
}
|
|
||||||
postMessageToParent({ command: "initNotificationBar" });
|
postMessageToParent({ command: "initNotificationBar" });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,25 +79,6 @@ function getI18n() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to locate an element by ID within a template’s content and casts it to the specified type.
|
|
||||||
*
|
|
||||||
* @param templateElement - The template whose content will be searched for the element.
|
|
||||||
* @param elementId - The ID of the element being searched for.
|
|
||||||
* @returns The typed element if found, otherwise log error.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
const findElementById = <ElementType extends HTMLElement>(
|
|
||||||
templateElement: HTMLTemplateElement,
|
|
||||||
elementId: string,
|
|
||||||
): ElementType => {
|
|
||||||
const element = templateElement.content.getElementById(elementId);
|
|
||||||
if (!element) {
|
|
||||||
throw new Error(`Element with ID "${elementId}" not found in template.`);
|
|
||||||
}
|
|
||||||
return element as ElementType;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the localized header message for the notification bar based on the notification type.
|
* Returns the localized header message for the notification bar based on the notification type.
|
||||||
*
|
*
|
||||||
@@ -204,25 +165,6 @@ export function getNotificationTestId(
|
|||||||
}[notificationType];
|
}[notificationType];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the text content of an element identified by ID within a template's content.
|
|
||||||
*
|
|
||||||
* @param template - The template whose content will be searched for the element.
|
|
||||||
* @param elementId - The ID of the element whose text content is to be set.
|
|
||||||
* @param text - The text content to set for the specified element.
|
|
||||||
* @returns void
|
|
||||||
*
|
|
||||||
* This function attempts to locate an element by its ID within the content of a given HTML template.
|
|
||||||
* If the element is found, it updates the element's text content with the provided text.
|
|
||||||
* If the element is not found, the function does nothing, ensuring that the operation is safe and does not throw errors.
|
|
||||||
*/
|
|
||||||
function setElementText(template: HTMLTemplateElement, elementId: string, text: string): void {
|
|
||||||
const element = template.content.getElementById(elementId);
|
|
||||||
if (element) {
|
|
||||||
element.textContent = text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function initNotificationBar(message: NotificationBarWindowMessage) {
|
async function initNotificationBar(message: NotificationBarWindowMessage) {
|
||||||
const { initData } = message;
|
const { initData } = message;
|
||||||
if (!initData) {
|
if (!initData) {
|
||||||
@@ -232,189 +174,119 @@ async function initNotificationBar(message: NotificationBarWindowMessage) {
|
|||||||
notificationBarIframeInitData = initData;
|
notificationBarIframeInitData = initData;
|
||||||
const {
|
const {
|
||||||
isVaultLocked,
|
isVaultLocked,
|
||||||
removeIndividualVault: personalVaultDisallowed, // renamed to avoid local method collision
|
removeIndividualVault: personalVaultDisallowed,
|
||||||
theme,
|
theme,
|
||||||
} = notificationBarIframeInitData;
|
} = notificationBarIframeInitData;
|
||||||
const i18n = getI18n();
|
const i18n = getI18n();
|
||||||
const resolvedTheme = getResolvedTheme(theme ?? ThemeTypes.Light);
|
const resolvedTheme = getResolvedTheme(theme ?? ThemeTypes.Light);
|
||||||
|
|
||||||
if (useComponentBar) {
|
const resolvedType = resolveNotificationType(notificationBarIframeInitData);
|
||||||
const resolvedType = resolveNotificationType(notificationBarIframeInitData);
|
const headerMessage = getNotificationHeaderMessage(i18n, resolvedType);
|
||||||
const headerMessage = getNotificationHeaderMessage(i18n, resolvedType);
|
const notificationTestId = getNotificationTestId(resolvedType);
|
||||||
const notificationTestId = getNotificationTestId(resolvedType);
|
appendHeaderMessageToTitle(headerMessage);
|
||||||
appendHeaderMessageToTitle(headerMessage);
|
|
||||||
|
|
||||||
document.body.innerHTML = "";
|
document.body.innerHTML = "";
|
||||||
// Current implementations utilize a require for scss files which creates the need to remove the node.
|
|
||||||
document.head.querySelectorAll('link[rel="stylesheet"]').forEach((node) => node.remove());
|
|
||||||
|
|
||||||
if (isVaultLocked) {
|
if (isVaultLocked) {
|
||||||
const notificationConfig = {
|
const notificationConfig = {
|
||||||
|
...notificationBarIframeInitData,
|
||||||
|
headerMessage,
|
||||||
|
type: resolvedType,
|
||||||
|
notificationTestId,
|
||||||
|
theme: resolvedTheme,
|
||||||
|
personalVaultIsAllowed: !personalVaultDisallowed,
|
||||||
|
handleCloseNotification,
|
||||||
|
handleEditOrUpdateAction,
|
||||||
|
i18n,
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveAction = () => {
|
||||||
|
sendSaveCipherMessage(true);
|
||||||
|
|
||||||
|
render(
|
||||||
|
NotificationContainer({
|
||||||
|
...notificationConfig,
|
||||||
|
handleSaveAction: () => {},
|
||||||
|
isLoading: true,
|
||||||
|
}),
|
||||||
|
document.body,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const UnlockNotification = NotificationContainer({ ...notificationConfig, handleSaveAction });
|
||||||
|
|
||||||
|
return render(UnlockNotification, document.body);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle AtRiskPasswordNotification render
|
||||||
|
if (notificationBarIframeInitData.type === NotificationTypes.AtRiskPassword) {
|
||||||
|
return render(
|
||||||
|
AtRiskNotification({
|
||||||
|
...notificationBarIframeInitData,
|
||||||
|
type: notificationBarIframeInitData.type as NotificationType,
|
||||||
|
theme: resolvedTheme,
|
||||||
|
i18n,
|
||||||
|
notificationTestId,
|
||||||
|
params: initData.params,
|
||||||
|
handleCloseNotification,
|
||||||
|
}),
|
||||||
|
document.body,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default scenario: add or update password
|
||||||
|
const orgId = selectedVaultSignal.get();
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
new Promise<OrgView[]>((resolve) => sendPlatformMessage({ command: "bgGetOrgData" }, resolve)),
|
||||||
|
new Promise<FolderView[]>((resolve) =>
|
||||||
|
sendPlatformMessage({ command: "bgGetFolderData" }, resolve),
|
||||||
|
),
|
||||||
|
new Promise<NotificationCipherData[]>((resolve) =>
|
||||||
|
sendPlatformMessage({ command: "bgGetDecryptedCiphers" }, resolve),
|
||||||
|
),
|
||||||
|
new Promise<CollectionView[]>((resolve) =>
|
||||||
|
sendPlatformMessage({ command: "bgGetCollectionData", orgId }, resolve),
|
||||||
|
),
|
||||||
|
]).then(([organizations, folders, ciphers, collections]) => {
|
||||||
|
notificationBarIframeInitData = {
|
||||||
|
...notificationBarIframeInitData,
|
||||||
|
organizations,
|
||||||
|
folders,
|
||||||
|
ciphers,
|
||||||
|
collections,
|
||||||
|
};
|
||||||
|
|
||||||
|
// @TODO use context to avoid prop drilling
|
||||||
|
return render(
|
||||||
|
NotificationContainer({
|
||||||
...notificationBarIframeInitData,
|
...notificationBarIframeInitData,
|
||||||
headerMessage,
|
headerMessage,
|
||||||
type: resolvedType,
|
type: resolvedType,
|
||||||
notificationTestId,
|
|
||||||
theme: resolvedTheme,
|
theme: resolvedTheme,
|
||||||
|
notificationTestId,
|
||||||
personalVaultIsAllowed: !personalVaultDisallowed,
|
personalVaultIsAllowed: !personalVaultDisallowed,
|
||||||
handleCloseNotification,
|
handleCloseNotification,
|
||||||
|
handleSaveAction,
|
||||||
handleEditOrUpdateAction,
|
handleEditOrUpdateAction,
|
||||||
i18n,
|
i18n,
|
||||||
};
|
}),
|
||||||
|
document.body,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
const handleSaveAction = () => {
|
|
||||||
sendSaveCipherMessage(true);
|
|
||||||
|
|
||||||
render(
|
|
||||||
NotificationContainer({
|
|
||||||
...notificationConfig,
|
|
||||||
handleSaveAction: () => {},
|
|
||||||
isLoading: true,
|
|
||||||
}),
|
|
||||||
document.body,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const UnlockNotification = NotificationContainer({ ...notificationConfig, handleSaveAction });
|
|
||||||
|
|
||||||
return render(UnlockNotification, document.body);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle AtRiskPasswordNotification render
|
|
||||||
if (notificationBarIframeInitData.type === NotificationTypes.AtRiskPassword) {
|
|
||||||
return render(
|
|
||||||
AtRiskNotification({
|
|
||||||
...notificationBarIframeInitData,
|
|
||||||
type: notificationBarIframeInitData.type as NotificationType,
|
|
||||||
theme: resolvedTheme,
|
|
||||||
i18n,
|
|
||||||
notificationTestId,
|
|
||||||
params: initData.params,
|
|
||||||
handleCloseNotification,
|
|
||||||
}),
|
|
||||||
document.body,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default scenario: add or update password
|
|
||||||
const orgId = selectedVaultSignal.get();
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
new Promise<OrgView[]>((resolve) =>
|
|
||||||
sendPlatformMessage({ command: "bgGetOrgData" }, resolve),
|
|
||||||
),
|
|
||||||
new Promise<FolderView[]>((resolve) =>
|
|
||||||
sendPlatformMessage({ command: "bgGetFolderData" }, resolve),
|
|
||||||
),
|
|
||||||
new Promise<NotificationCipherData[]>((resolve) =>
|
|
||||||
sendPlatformMessage({ command: "bgGetDecryptedCiphers" }, resolve),
|
|
||||||
),
|
|
||||||
new Promise<CollectionView[]>((resolve) =>
|
|
||||||
sendPlatformMessage({ command: "bgGetCollectionData", orgId }, resolve),
|
|
||||||
),
|
|
||||||
]).then(([organizations, folders, ciphers, collections]) => {
|
|
||||||
notificationBarIframeInitData = {
|
|
||||||
...notificationBarIframeInitData,
|
|
||||||
organizations,
|
|
||||||
folders,
|
|
||||||
ciphers,
|
|
||||||
collections,
|
|
||||||
};
|
|
||||||
|
|
||||||
// @TODO use context to avoid prop drilling
|
|
||||||
return render(
|
|
||||||
NotificationContainer({
|
|
||||||
...notificationBarIframeInitData,
|
|
||||||
headerMessage,
|
|
||||||
type: resolvedType,
|
|
||||||
theme: resolvedTheme,
|
|
||||||
notificationTestId,
|
|
||||||
personalVaultIsAllowed: !personalVaultDisallowed,
|
|
||||||
handleCloseNotification,
|
|
||||||
handleSaveAction,
|
|
||||||
handleEditOrUpdateAction,
|
|
||||||
i18n,
|
|
||||||
}),
|
|
||||||
document.body,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
setNotificationBarTheme();
|
|
||||||
|
|
||||||
(document.getElementById("logo") as HTMLImageElement).src = isVaultLocked
|
|
||||||
? chrome.runtime.getURL("images/icon38_locked.png")
|
|
||||||
: chrome.runtime.getURL("images/icon38.png");
|
|
||||||
|
|
||||||
setupLogoLink(i18n.appName);
|
|
||||||
|
|
||||||
// i18n for "Add" template
|
|
||||||
const addTemplate = document.getElementById("template-add") as HTMLTemplateElement;
|
|
||||||
|
|
||||||
const neverButton = findElementById<HTMLButtonElement>(addTemplate, "never-save");
|
|
||||||
neverButton.textContent = i18n.never;
|
|
||||||
|
|
||||||
const selectFolder = findElementById<HTMLSelectElement>(addTemplate, "select-folder");
|
|
||||||
selectFolder.hidden = isVaultLocked || removeIndividualVault();
|
|
||||||
selectFolder.setAttribute("aria-label", i18n.folder);
|
|
||||||
|
|
||||||
const addButton = findElementById<HTMLButtonElement>(addTemplate, "add-save");
|
|
||||||
addButton.textContent = i18n.notificationAddSave;
|
|
||||||
|
|
||||||
const addEditButton = findElementById<HTMLButtonElement>(addTemplate, "add-edit");
|
|
||||||
// If Remove Individual Vault policy applies, "Add" opens the edit tab, so we hide the Edit button
|
|
||||||
addEditButton.hidden = removeIndividualVault();
|
|
||||||
addEditButton.textContent = i18n.notificationEdit;
|
|
||||||
|
|
||||||
setElementText(addTemplate, "add-text", i18n.notificationAddDesc);
|
|
||||||
|
|
||||||
// i18n for "Change" (update password) template
|
|
||||||
const changeTemplate = document.getElementById("template-change") as HTMLTemplateElement;
|
|
||||||
|
|
||||||
const changeButton = findElementById<HTMLSelectElement>(changeTemplate, "change-save");
|
|
||||||
changeButton.textContent = i18n.notificationUpdate;
|
|
||||||
|
|
||||||
const changeEditButton = findElementById<HTMLButtonElement>(changeTemplate, "change-edit");
|
|
||||||
changeEditButton.textContent = i18n.notificationEdit;
|
|
||||||
|
|
||||||
setElementText(changeTemplate, "change-text", i18n.notificationChangeDesc);
|
|
||||||
|
|
||||||
// i18n for "Unlock" (unlock extension) template
|
|
||||||
const unlockTemplate = document.getElementById("template-unlock") as HTMLTemplateElement;
|
|
||||||
|
|
||||||
const unlockButton = findElementById<HTMLButtonElement>(unlockTemplate, "unlock-vault");
|
|
||||||
unlockButton.textContent = i18n.notificationUnlock;
|
|
||||||
|
|
||||||
setElementText(unlockTemplate, "unlock-text", i18n.notificationUnlockDesc);
|
|
||||||
|
|
||||||
// i18n for body content
|
|
||||||
const closeButton = document.getElementById("close-button");
|
|
||||||
if (closeButton) {
|
|
||||||
closeButton.title = i18n.close;
|
|
||||||
}
|
|
||||||
|
|
||||||
const notificationType = initData.type;
|
|
||||||
if (notificationType === "add") {
|
|
||||||
handleTypeAdd();
|
|
||||||
} else if (notificationType === "change") {
|
|
||||||
handleTypeChange();
|
|
||||||
} else if (notificationType === "unlock") {
|
|
||||||
handleTypeUnlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
closeButton?.addEventListener("click", handleCloseNotification);
|
|
||||||
|
|
||||||
globalThis.addEventListener("resize", adjustHeight);
|
|
||||||
adjustHeight();
|
|
||||||
}
|
|
||||||
function handleEditOrUpdateAction(e: Event) {
|
function handleEditOrUpdateAction(e: Event) {
|
||||||
const notificationType = initData?.type;
|
const notificationType = initData?.type;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
notificationType === "add" ? sendSaveCipherMessage(true) : sendSaveCipherMessage(false);
|
notificationType === "add" ? sendSaveCipherMessage(true) : sendSaveCipherMessage(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCloseNotification(e: Event) {
|
function handleCloseNotification(e: Event) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
sendPlatformMessage({
|
sendPlatformMessage({
|
||||||
command: "bgCloseNotificationBar",
|
command: "bgCloseNotificationBar",
|
||||||
|
fadeOutNotification: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,57 +311,6 @@ function handleSaveAction(e: Event) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTypeAdd() {
|
|
||||||
setContent(document.getElementById("template-add") as HTMLTemplateElement);
|
|
||||||
|
|
||||||
const addButton = document.getElementById("add-save");
|
|
||||||
addButton?.addEventListener("click", (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
// If Remove Individual Vault policy applies, "Add" opens the edit tab
|
|
||||||
sendSaveCipherMessage(removeIndividualVault(), getSelectedFolder());
|
|
||||||
});
|
|
||||||
|
|
||||||
if (removeIndividualVault()) {
|
|
||||||
// Everything past this point is only required if user has an individual vault
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const editButton = document.getElementById("add-edit");
|
|
||||||
editButton?.addEventListener("click", (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
sendSaveCipherMessage(true, getSelectedFolder());
|
|
||||||
});
|
|
||||||
|
|
||||||
const neverButton = document.getElementById("never-save");
|
|
||||||
neverButton?.addEventListener("click", (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
sendPlatformMessage({
|
|
||||||
command: "bgNeverSave",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
loadFolderSelector();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTypeChange() {
|
|
||||||
setContent(document.getElementById("template-change") as HTMLTemplateElement);
|
|
||||||
const changeButton = document.getElementById("change-save");
|
|
||||||
changeButton?.addEventListener("click", (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
sendSaveCipherMessage(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
const editButton = document.getElementById("change-edit");
|
|
||||||
editButton?.addEventListener("click", (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
sendSaveCipherMessage(true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendSaveCipherMessage(edit: boolean, folder?: string) {
|
function sendSaveCipherMessage(edit: boolean, folder?: string) {
|
||||||
sendPlatformMessage({
|
sendPlatformMessage({
|
||||||
command: "bgSaveCipher",
|
command: "bgSaveCipher",
|
||||||
@@ -498,36 +319,6 @@ function sendSaveCipherMessage(edit: boolean, folder?: string) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSaveCipherAttemptCompletedMessage(message: NotificationBarWindowMessage) {
|
|
||||||
const addSaveButtonContainers = document.querySelectorAll(".add-change-cipher-buttons");
|
|
||||||
const notificationBarOuterWrapper = document.getElementById("notification-bar-outer-wrapper");
|
|
||||||
if (message?.error) {
|
|
||||||
addSaveButtonContainers.forEach((element) => {
|
|
||||||
element.textContent = chrome.i18n.getMessage("saveCipherAttemptFailed");
|
|
||||||
element.classList.add("error-message");
|
|
||||||
notificationBarOuterWrapper?.classList.add("error-event");
|
|
||||||
});
|
|
||||||
|
|
||||||
adjustHeight();
|
|
||||||
logService.error(`Error encountered when saving credentials: ${message.error}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const messageName =
|
|
||||||
notificationBarIframeInitData.type === "add" ? "passwordSaved" : "passwordUpdated";
|
|
||||||
|
|
||||||
addSaveButtonContainers.forEach((element) => {
|
|
||||||
element.textContent = chrome.i18n.getMessage(messageName);
|
|
||||||
element.prepend(buildSvgDomElement(circleCheckIcon));
|
|
||||||
element.classList.add("success-message");
|
|
||||||
notificationBarOuterWrapper?.classList.add("success-event");
|
|
||||||
});
|
|
||||||
adjustHeight();
|
|
||||||
globalThis.setTimeout(
|
|
||||||
() => sendPlatformMessage({ command: "bgCloseNotificationBar", fadeOutNotification: true }),
|
|
||||||
3000,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function openAddEditVaultItemPopout(
|
function openAddEditVaultItemPopout(
|
||||||
e: Event,
|
e: Event,
|
||||||
options: {
|
options: {
|
||||||
@@ -583,27 +374,6 @@ function handleSaveCipherConfirmation(message: NotificationBarWindowMessage) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleTypeUnlock() {
|
|
||||||
setContent(document.getElementById("template-unlock") as HTMLTemplateElement);
|
|
||||||
|
|
||||||
const unlockButton = document.getElementById("unlock-vault");
|
|
||||||
unlockButton?.addEventListener("click", (e) => {
|
|
||||||
sendPlatformMessage({
|
|
||||||
command: "bgReopenUnlockPopout",
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setContent(template: HTMLTemplateElement) {
|
|
||||||
const content = document.getElementById("content");
|
|
||||||
while (content?.firstChild) {
|
|
||||||
content?.removeChild(content.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
const newElement = template.content.cloneNode(true) as HTMLElement;
|
|
||||||
content?.appendChild(newElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendPlatformMessage(
|
function sendPlatformMessage(
|
||||||
msg: Record<string, unknown>,
|
msg: Record<string, unknown>,
|
||||||
responseCallback?: (response: any) => void,
|
responseCallback?: (response: any) => void,
|
||||||
@@ -615,51 +385,10 @@ function sendPlatformMessage(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadFolderSelector() {
|
|
||||||
const populateFolderData = (folderData: FolderView[]) => {
|
|
||||||
const select = document.getElementById("select-folder");
|
|
||||||
if (!select) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!folderData?.length) {
|
|
||||||
select.appendChild(new Option(chrome.i18n.getMessage("noFoldersFound"), undefined, true));
|
|
||||||
select.setAttribute("disabled", "true");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
select.appendChild(new Option(chrome.i18n.getMessage("selectFolder"), undefined, true));
|
|
||||||
folderData.forEach((folder: FolderView) => {
|
|
||||||
// Select "No Folder" (id=null) folder by default
|
|
||||||
select.appendChild(new Option(folder.name, folder.id || "", false));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
sendPlatformMessage({ command: "bgGetFolderData" }, populateFolderData);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSelectedFolder(): string {
|
|
||||||
return (document.getElementById("select-folder") as HTMLSelectElement).value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeIndividualVault(): boolean {
|
function removeIndividualVault(): boolean {
|
||||||
return Boolean(notificationBarIframeInitData?.removeIndividualVault);
|
return Boolean(notificationBarIframeInitData?.removeIndividualVault);
|
||||||
}
|
}
|
||||||
|
|
||||||
function adjustHeight() {
|
|
||||||
const body = document.querySelector("body");
|
|
||||||
if (!body) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const data: AdjustNotificationBarMessageData = {
|
|
||||||
height: body.scrollHeight,
|
|
||||||
};
|
|
||||||
sendPlatformMessage({
|
|
||||||
command: "bgAdjustNotificationBar",
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function setupWindowMessageListener() {
|
function setupWindowMessageListener() {
|
||||||
globalThis.addEventListener("message", handleWindowMessage);
|
globalThis.addEventListener("message", handleWindowMessage);
|
||||||
}
|
}
|
||||||
@@ -682,18 +411,6 @@ function handleWindowMessage(event: MessageEvent) {
|
|||||||
handler({ message });
|
handler({ message });
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupLogoLink(linkText: string) {
|
|
||||||
const logoLink = document.getElementById("logo-link") as HTMLAnchorElement;
|
|
||||||
logoLink.title = linkText;
|
|
||||||
const setWebVaultUrlLink = (webVaultURL: string) => {
|
|
||||||
const newVaultURL = webVaultURL && decodeURIComponent(webVaultURL);
|
|
||||||
if (newVaultURL && newVaultURL !== logoLink.href) {
|
|
||||||
logoLink.href = newVaultURL;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
sendPlatformMessage({ command: "getWebVaultUrlForNotification" }, setWebVaultUrlLink);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTheme(globalThis: any, theme: NotificationBarIframeInitData["theme"]) {
|
function getTheme(globalThis: any, theme: NotificationBarIframeInitData["theme"]) {
|
||||||
if (theme === ThemeTypes.System) {
|
if (theme === ThemeTypes.System) {
|
||||||
return globalThis.matchMedia("(prefers-color-scheme: dark)").matches
|
return globalThis.matchMedia("(prefers-color-scheme: dark)").matches
|
||||||
@@ -712,12 +429,6 @@ function getResolvedTheme(theme: Theme) {
|
|||||||
return resolvedTheme;
|
return resolvedTheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setNotificationBarTheme() {
|
|
||||||
const theme = getTheme(globalThis, notificationBarIframeInitData.theme);
|
|
||||||
|
|
||||||
document.documentElement.classList.add(`theme_${theme}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
function postMessageToParent(message: NotificationBarWindowMessage) {
|
function postMessageToParent(message: NotificationBarWindowMessage) {
|
||||||
globalThis.parent.postMessage(message, windowMessageOrigin || "*");
|
globalThis.parent.postMessage(message, windowMessageOrigin || "*");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
exports[`OverlayNotificationsContentService opening the notification bar creates the notification bar elements and appends them to the body 1`] = `
|
exports[`OverlayNotificationsContentService opening the notification bar creates the notification bar elements and appends them to the body 1`] = `
|
||||||
<div
|
<div
|
||||||
id="bit-notification-bar"
|
id="bit-notification-bar"
|
||||||
style="height: 82px !important; width: 430px !important; max-width: calc(100% - 20px) !important; min-height: initial !important; top: 10px !important; right: 10px !important; padding: 0px !important; position: fixed !important; z-index: 2147483647 !important; visibility: visible !important; border-radius: 4px !important; background-color: transparent !important; overflow: hidden !important; transition: box-shadow 0.15s ease !important; transition-delay: 0.15s;"
|
style="height: 400px !important; width: 430px !important; max-width: calc(100% - 20px) !important; min-height: initial !important; top: 10px !important; right: 0px !important; padding: 0px !important; position: fixed !important; z-index: 2147483647 !important; visibility: visible !important; border-radius: 4px !important; background-color: transparent !important; overflow: hidden !important; transition: box-shadow 0.15s ease !important; transition-delay: 0.15s;"
|
||||||
>
|
>
|
||||||
<iframe
|
<iframe
|
||||||
id="bit-notification-bar-iframe"
|
id="bit-notification-bar-iframe"
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { mock, MockProxy } from "jest-mock-extended";
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
|
|
||||||
import AutofillInit from "../../../content/autofill-init";
|
import AutofillInit from "../../../content/autofill-init";
|
||||||
|
import { NotificationType } from "../../../enums/notification-type.enum";
|
||||||
import { DomQueryService } from "../../../services/abstractions/dom-query.service";
|
import { DomQueryService } from "../../../services/abstractions/dom-query.service";
|
||||||
import DomElementVisibilityService from "../../../services/dom-element-visibility.service";
|
import DomElementVisibilityService from "../../../services/dom-element-visibility.service";
|
||||||
import { flushPromises, sendMockExtensionMessage } from "../../../spec/testing-utils";
|
import { flushPromises, sendMockExtensionMessage } from "../../../spec/testing-utils";
|
||||||
import * as utils from "../../../utils";
|
import * as utils from "../../../utils";
|
||||||
import { sendExtensionMessage } from "../../../utils";
|
|
||||||
import { NotificationTypeData } from "../abstractions/overlay-notifications-content.service";
|
import { NotificationTypeData } from "../abstractions/overlay-notifications-content.service";
|
||||||
|
|
||||||
import { OverlayNotificationsContentService } from "./overlay-notifications-content.service";
|
import { OverlayNotificationsContentService } from "./overlay-notifications-content.service";
|
||||||
@@ -19,11 +19,7 @@ describe("OverlayNotificationsContentService", () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
jest.useFakeTimers();
|
jest.useFakeTimers();
|
||||||
jest
|
jest.spyOn(utils, "sendExtensionMessage").mockImplementation(jest.fn());
|
||||||
.spyOn(utils, "sendExtensionMessage")
|
|
||||||
.mockImplementation((command: string) =>
|
|
||||||
Promise.resolve(command === "notificationRefreshFlagValue" ? false : true),
|
|
||||||
);
|
|
||||||
domQueryService = mock<DomQueryService>();
|
domQueryService = mock<DomQueryService>();
|
||||||
domElementVisibilityService = new DomElementVisibilityService();
|
domElementVisibilityService = new DomElementVisibilityService();
|
||||||
overlayNotificationsContentService = new OverlayNotificationsContentService();
|
overlayNotificationsContentService = new OverlayNotificationsContentService();
|
||||||
@@ -51,37 +47,6 @@ describe("OverlayNotificationsContentService", () => {
|
|||||||
expect(bodyAppendChildSpy).not.toHaveBeenCalled();
|
expect(bodyAppendChildSpy).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("applies correct styles when notificationRefreshFlag is true", async () => {
|
|
||||||
(sendExtensionMessage as jest.Mock).mockResolvedValue(true);
|
|
||||||
sendMockExtensionMessage({
|
|
||||||
command: "openNotificationBar",
|
|
||||||
data: {
|
|
||||||
type: "change",
|
|
||||||
typeData: mock<NotificationTypeData>(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
await flushPromises();
|
|
||||||
|
|
||||||
const barElement = overlayNotificationsContentService["notificationBarElement"]!;
|
|
||||||
expect(barElement.style.height).toBe("400px");
|
|
||||||
expect(barElement.style.right).toBe("0px");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("applies correct styles when notificationRefreshFlag is false", async () => {
|
|
||||||
sendMockExtensionMessage({
|
|
||||||
command: "openNotificationBar",
|
|
||||||
data: {
|
|
||||||
type: "change",
|
|
||||||
typeData: mock<NotificationTypeData>(),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
await flushPromises();
|
|
||||||
|
|
||||||
const barElement = overlayNotificationsContentService["notificationBarElement"]!;
|
|
||||||
expect(barElement.style.height).toBe("82px");
|
|
||||||
expect(barElement.style.right).toBe("10px");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("closes the notification bar if the notification bar type has changed", async () => {
|
it("closes the notification bar if the notification bar type has changed", async () => {
|
||||||
overlayNotificationsContentService["currentNotificationBarType"] = "add";
|
overlayNotificationsContentService["currentNotificationBarType"] = "add";
|
||||||
const closeNotificationBarSpy = jest.spyOn(
|
const closeNotificationBarSpy = jest.spyOn(
|
||||||
@@ -92,7 +57,7 @@ describe("OverlayNotificationsContentService", () => {
|
|||||||
sendMockExtensionMessage({
|
sendMockExtensionMessage({
|
||||||
command: "openNotificationBar",
|
command: "openNotificationBar",
|
||||||
data: {
|
data: {
|
||||||
type: "change",
|
type: NotificationType.ChangePassword,
|
||||||
typeData: mock<NotificationTypeData>(),
|
typeData: mock<NotificationTypeData>(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -105,7 +70,7 @@ describe("OverlayNotificationsContentService", () => {
|
|||||||
sendMockExtensionMessage({
|
sendMockExtensionMessage({
|
||||||
command: "openNotificationBar",
|
command: "openNotificationBar",
|
||||||
data: {
|
data: {
|
||||||
type: "change",
|
type: NotificationType.ChangePassword,
|
||||||
typeData: mock<NotificationTypeData>(),
|
typeData: mock<NotificationTypeData>(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -118,7 +83,7 @@ describe("OverlayNotificationsContentService", () => {
|
|||||||
sendMockExtensionMessage({
|
sendMockExtensionMessage({
|
||||||
command: "openNotificationBar",
|
command: "openNotificationBar",
|
||||||
data: {
|
data: {
|
||||||
type: "change",
|
type: NotificationType.ChangePassword,
|
||||||
typeData: mock<NotificationTypeData>({
|
typeData: mock<NotificationTypeData>({
|
||||||
launchTimestamp: Date.now(),
|
launchTimestamp: Date.now(),
|
||||||
}),
|
}),
|
||||||
@@ -135,7 +100,7 @@ describe("OverlayNotificationsContentService", () => {
|
|||||||
sendMockExtensionMessage({
|
sendMockExtensionMessage({
|
||||||
command: "openNotificationBar",
|
command: "openNotificationBar",
|
||||||
data: {
|
data: {
|
||||||
type: "change",
|
type: NotificationType.ChangePassword,
|
||||||
typeData: mock<NotificationTypeData>(),
|
typeData: mock<NotificationTypeData>(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -154,7 +119,7 @@ describe("OverlayNotificationsContentService", () => {
|
|||||||
sendMockExtensionMessage({
|
sendMockExtensionMessage({
|
||||||
command: "openNotificationBar",
|
command: "openNotificationBar",
|
||||||
data: {
|
data: {
|
||||||
type: "change",
|
type: NotificationType.ChangePassword,
|
||||||
typeData: mock<NotificationTypeData>(),
|
typeData: mock<NotificationTypeData>(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -211,8 +176,21 @@ describe("OverlayNotificationsContentService", () => {
|
|||||||
).toBe("0");
|
).toBe("0");
|
||||||
|
|
||||||
jest.advanceTimersByTime(150);
|
jest.advanceTimersByTime(150);
|
||||||
|
});
|
||||||
|
|
||||||
expect(sendExtensionMessage).toHaveBeenCalledWith("bgRemoveTabFromNotificationQueue");
|
it("triggers a fadeout of the notification bar and removes from the notification queue", () => {
|
||||||
|
sendMockExtensionMessage({
|
||||||
|
command: "closeNotificationBar",
|
||||||
|
data: { fadeOutNotification: true, type: NotificationType.ChangePassword },
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(
|
||||||
|
overlayNotificationsContentService["notificationBarIframeElement"]?.style.opacity,
|
||||||
|
).toBe("0");
|
||||||
|
|
||||||
|
jest.advanceTimersByTime(150);
|
||||||
|
|
||||||
|
expect(utils.sendExtensionMessage).toHaveBeenCalledWith("bgRemoveTabFromNotificationQueue");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("closes the notification bar without a fadeout", () => {
|
it("closes the notification bar without a fadeout", () => {
|
||||||
@@ -232,7 +210,7 @@ describe("OverlayNotificationsContentService", () => {
|
|||||||
sendMockExtensionMessage({
|
sendMockExtensionMessage({
|
||||||
command: "openNotificationBar",
|
command: "openNotificationBar",
|
||||||
data: {
|
data: {
|
||||||
type: "change",
|
type: NotificationType.ChangePassword,
|
||||||
typeData: mock<NotificationTypeData>(),
|
typeData: mock<NotificationTypeData>(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -256,7 +234,7 @@ describe("OverlayNotificationsContentService", () => {
|
|||||||
sendMockExtensionMessage({
|
sendMockExtensionMessage({
|
||||||
command: "openNotificationBar",
|
command: "openNotificationBar",
|
||||||
data: {
|
data: {
|
||||||
type: "change",
|
type: NotificationType.ChangePassword,
|
||||||
typeData: mock<NotificationTypeData>(),
|
typeData: mock<NotificationTypeData>(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -286,7 +264,7 @@ describe("OverlayNotificationsContentService", () => {
|
|||||||
sendMockExtensionMessage({
|
sendMockExtensionMessage({
|
||||||
command: "openNotificationBar",
|
command: "openNotificationBar",
|
||||||
data: {
|
data: {
|
||||||
type: "change",
|
type: NotificationType.ChangePassword,
|
||||||
typeData: mock<NotificationTypeData>(),
|
typeData: mock<NotificationTypeData>(),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -20,33 +20,25 @@ export class OverlayNotificationsContentService
|
|||||||
private notificationBarElement: HTMLElement | null = null;
|
private notificationBarElement: HTMLElement | null = null;
|
||||||
private notificationBarIframeElement: HTMLIFrameElement | null = null;
|
private notificationBarIframeElement: HTMLIFrameElement | null = null;
|
||||||
private currentNotificationBarType: NotificationType | null = null;
|
private currentNotificationBarType: NotificationType | null = null;
|
||||||
private notificationRefreshFlag: boolean = false;
|
private notificationBarContainerStyles: Partial<CSSStyleDeclaration> = {
|
||||||
private getNotificationBarStyles(): Partial<CSSStyleDeclaration> {
|
height: "400px",
|
||||||
const styles: Partial<CSSStyleDeclaration> = {
|
width: "430px",
|
||||||
height: "400px",
|
maxWidth: "calc(100% - 20px)",
|
||||||
width: "430px",
|
minHeight: "initial",
|
||||||
maxWidth: "calc(100% - 20px)",
|
top: "10px",
|
||||||
minHeight: "initial",
|
right: "0px",
|
||||||
top: "10px",
|
padding: "0",
|
||||||
right: "0px",
|
position: "fixed",
|
||||||
padding: "0",
|
zIndex: "2147483647",
|
||||||
position: "fixed",
|
visibility: "visible",
|
||||||
zIndex: "2147483647",
|
borderRadius: "4px",
|
||||||
visibility: "visible",
|
border: "none",
|
||||||
borderRadius: "4px",
|
backgroundColor: "transparent",
|
||||||
border: "none",
|
overflow: "hidden",
|
||||||
backgroundColor: "transparent",
|
transition: "box-shadow 0.15s ease",
|
||||||
overflow: "hidden",
|
transitionDelay: "0.15s",
|
||||||
transition: "box-shadow 0.15s ease",
|
};
|
||||||
transitionDelay: "0.15s",
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!this.notificationRefreshFlag) {
|
|
||||||
styles.height = "82px";
|
|
||||||
styles.right = "10px";
|
|
||||||
}
|
|
||||||
return styles;
|
|
||||||
}
|
|
||||||
private notificationBarIframeElementStyles: Partial<CSSStyleDeclaration> = {
|
private notificationBarIframeElementStyles: Partial<CSSStyleDeclaration> = {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
@@ -57,6 +49,7 @@ export class OverlayNotificationsContentService
|
|||||||
borderRadius: "4px",
|
borderRadius: "4px",
|
||||||
colorScheme: "auto",
|
colorScheme: "auto",
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly extensionMessageHandlers: OverlayNotificationsExtensionMessageHandlers = {
|
private readonly extensionMessageHandlers: OverlayNotificationsExtensionMessageHandlers = {
|
||||||
openNotificationBar: ({ message }) => this.handleOpenNotificationBarMessage(message),
|
openNotificationBar: ({ message }) => this.handleOpenNotificationBarMessage(message),
|
||||||
closeNotificationBar: ({ message }) => this.handleCloseNotificationBarMessage(message),
|
closeNotificationBar: ({ message }) => this.handleCloseNotificationBarMessage(message),
|
||||||
@@ -91,6 +84,7 @@ export class OverlayNotificationsContentService
|
|||||||
if (this.currentNotificationBarType && type !== this.currentNotificationBarType) {
|
if (this.currentNotificationBarType && type !== this.currentNotificationBarType) {
|
||||||
this.closeNotificationBar();
|
this.closeNotificationBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
const initData = {
|
const initData = {
|
||||||
type: type as NotificationType,
|
type: type as NotificationType,
|
||||||
isVaultLocked: typeData.isVaultLocked,
|
isVaultLocked: typeData.isVaultLocked,
|
||||||
@@ -101,10 +95,6 @@ export class OverlayNotificationsContentService
|
|||||||
params,
|
params,
|
||||||
};
|
};
|
||||||
|
|
||||||
await sendExtensionMessage("notificationRefreshFlagValue").then((notificationRefreshFlag) => {
|
|
||||||
this.notificationRefreshFlag = !!notificationRefreshFlag;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (globalThis.document.readyState === "loading") {
|
if (globalThis.document.readyState === "loading") {
|
||||||
document.addEventListener("DOMContentLoaded", () => this.openNotificationBar(initData));
|
document.addEventListener("DOMContentLoaded", () => this.openNotificationBar(initData));
|
||||||
return;
|
return;
|
||||||
@@ -215,14 +205,8 @@ export class OverlayNotificationsContentService
|
|||||||
{ transform: "translateX(0)", opacity: "1" },
|
{ transform: "translateX(0)", opacity: "1" },
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
if (!this.notificationRefreshFlag) {
|
|
||||||
setElementStyles(
|
this.notificationBarIframeElement?.removeEventListener(
|
||||||
this.notificationBarElement,
|
|
||||||
{ boxShadow: "2px 4px 6px 0px #0000001A" },
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
this.notificationBarIframeElement.removeEventListener(
|
|
||||||
EVENTS.LOAD,
|
EVENTS.LOAD,
|
||||||
this.handleNotificationBarIframeOnLoad,
|
this.handleNotificationBarIframeOnLoad,
|
||||||
);
|
);
|
||||||
@@ -236,7 +220,7 @@ export class OverlayNotificationsContentService
|
|||||||
this.notificationBarElement = globalThis.document.createElement("div");
|
this.notificationBarElement = globalThis.document.createElement("div");
|
||||||
this.notificationBarElement.id = "bit-notification-bar";
|
this.notificationBarElement.id = "bit-notification-bar";
|
||||||
|
|
||||||
setElementStyles(this.notificationBarElement, this.getNotificationBarStyles(), true);
|
setElementStyles(this.notificationBarElement, this.notificationBarContainerStyles, true);
|
||||||
|
|
||||||
this.notificationBarElement.appendChild(this.notificationBarIframeElement);
|
this.notificationBarElement.appendChild(this.notificationBarIframeElement);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,8 +83,8 @@ export class AutofillOverlayContentService implements AutofillOverlayContentServ
|
|||||||
unsetMostRecentlyFocusedField: () => this.unsetMostRecentlyFocusedField(),
|
unsetMostRecentlyFocusedField: () => this.unsetMostRecentlyFocusedField(),
|
||||||
checkIsMostRecentlyFocusedFieldWithinViewport: () =>
|
checkIsMostRecentlyFocusedFieldWithinViewport: () =>
|
||||||
this.checkIsMostRecentlyFocusedFieldWithinViewport(),
|
this.checkIsMostRecentlyFocusedFieldWithinViewport(),
|
||||||
bgUnlockPopoutOpened: () => this.blurMostRecentlyFocusedField(true),
|
|
||||||
bgVaultItemRepromptPopoutOpened: () => this.blurMostRecentlyFocusedField(true),
|
bgVaultItemRepromptPopoutOpened: () => this.blurMostRecentlyFocusedField(true),
|
||||||
|
bgUnlockPopoutOpened: () => this.blurMostRecentlyFocusedField(true),
|
||||||
redirectAutofillInlineMenuFocusOut: ({ message }) =>
|
redirectAutofillInlineMenuFocusOut: ({ message }) =>
|
||||||
this.redirectInlineMenuFocusOut(message?.data?.direction),
|
this.redirectInlineMenuFocusOut(message?.data?.direction),
|
||||||
getSubFrameOffsets: ({ message }) => this.getSubFrameOffsets(message),
|
getSubFrameOffsets: ({ message }) => this.getSubFrameOffsets(message),
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ export enum FeatureFlag {
|
|||||||
PM14938_BrowserExtensionLoginApproval = "pm-14938-browser-extension-login-approvals",
|
PM14938_BrowserExtensionLoginApproval = "pm-14938-browser-extension-login-approvals",
|
||||||
|
|
||||||
/* Autofill */
|
/* Autofill */
|
||||||
NotificationRefresh = "notification-refresh",
|
|
||||||
MacOsNativeCredentialSync = "macos-native-credential-sync",
|
MacOsNativeCredentialSync = "macos-native-credential-sync",
|
||||||
WindowsDesktopAutotype = "windows-desktop-autotype",
|
WindowsDesktopAutotype = "windows-desktop-autotype",
|
||||||
|
|
||||||
@@ -75,7 +74,6 @@ export const DefaultFeatureFlagValue = {
|
|||||||
[FeatureFlag.CollectionVaultRefactor]: FALSE,
|
[FeatureFlag.CollectionVaultRefactor]: FALSE,
|
||||||
|
|
||||||
/* Autofill */
|
/* Autofill */
|
||||||
[FeatureFlag.NotificationRefresh]: FALSE,
|
|
||||||
[FeatureFlag.MacOsNativeCredentialSync]: FALSE,
|
[FeatureFlag.MacOsNativeCredentialSync]: FALSE,
|
||||||
[FeatureFlag.WindowsDesktopAutotype]: FALSE,
|
[FeatureFlag.WindowsDesktopAutotype]: FALSE,
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user