mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 15:53:27 +00:00
PM-19291 Pass relevant folder and vault data to drop down component within notification footer (#13901)
* PM-19291 - Pass relevant data into dropdown component - Clean up some files - Pass all data into notificationBarIframeInitData using promise all * fix tests
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { mock, MockProxy } from "jest-mock-extended";
|
import { mock, MockProxy } from "jest-mock-extended";
|
||||||
import { BehaviorSubject, firstValueFrom } from "rxjs";
|
import { BehaviorSubject, firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
import { PolicyService } from "@bitwarden/common/admin-console/services/policy/policy.service";
|
||||||
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
@@ -59,6 +60,7 @@ describe("NotificationBackground", () => {
|
|||||||
const themeStateService = mock<ThemeStateService>();
|
const themeStateService = mock<ThemeStateService>();
|
||||||
const configService = mock<ConfigService>();
|
const configService = mock<ConfigService>();
|
||||||
const accountService = mock<AccountService>();
|
const accountService = mock<AccountService>();
|
||||||
|
const organizationService = mock<OrganizationService>();
|
||||||
|
|
||||||
const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>({
|
const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>({
|
||||||
id: "testId" as UserId,
|
id: "testId" as UserId,
|
||||||
@@ -73,18 +75,19 @@ describe("NotificationBackground", () => {
|
|||||||
authService.activeAccountStatus$ = activeAccountStatusMock$;
|
authService.activeAccountStatus$ = activeAccountStatusMock$;
|
||||||
accountService.activeAccount$ = activeAccountSubject;
|
accountService.activeAccount$ = activeAccountSubject;
|
||||||
notificationBackground = new NotificationBackground(
|
notificationBackground = new NotificationBackground(
|
||||||
|
accountService,
|
||||||
|
authService,
|
||||||
autofillService,
|
autofillService,
|
||||||
cipherService,
|
cipherService,
|
||||||
authService,
|
configService,
|
||||||
policyService,
|
|
||||||
folderService,
|
|
||||||
userNotificationSettingsService,
|
|
||||||
domainSettingsService,
|
domainSettingsService,
|
||||||
environmentService,
|
environmentService,
|
||||||
|
folderService,
|
||||||
logService,
|
logService,
|
||||||
|
organizationService,
|
||||||
|
policyService,
|
||||||
themeStateService,
|
themeStateService,
|
||||||
configService,
|
userNotificationSettingsService,
|
||||||
accountService,
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
@@ -63,46 +64,48 @@ export default class NotificationBackground {
|
|||||||
ExtensionCommand.AutofillIdentity,
|
ExtensionCommand.AutofillIdentity,
|
||||||
]);
|
]);
|
||||||
private readonly extensionMessageHandlers: NotificationBackgroundExtensionMessageHandlers = {
|
private readonly extensionMessageHandlers: NotificationBackgroundExtensionMessageHandlers = {
|
||||||
unlockCompleted: ({ message, sender }) => this.handleUnlockCompleted(message, sender),
|
bgAddLogin: ({ message, sender }) => this.addLogin(message, sender),
|
||||||
bgGetFolderData: () => this.getFolderData(),
|
|
||||||
bgCloseNotificationBar: ({ message, sender }) =>
|
|
||||||
this.handleCloseNotificationBarMessage(message, sender),
|
|
||||||
bgAdjustNotificationBar: ({ message, sender }) =>
|
bgAdjustNotificationBar: ({ message, sender }) =>
|
||||||
this.handleAdjustNotificationBarMessage(message, sender),
|
this.handleAdjustNotificationBarMessage(message, sender),
|
||||||
bgAddLogin: ({ message, sender }) => this.addLogin(message, sender),
|
|
||||||
bgChangedPassword: ({ message, sender }) => this.changedPassword(message, sender),
|
bgChangedPassword: ({ message, sender }) => this.changedPassword(message, sender),
|
||||||
bgRemoveTabFromNotificationQueue: ({ sender }) =>
|
bgCloseNotificationBar: ({ message, sender }) =>
|
||||||
this.removeTabFromNotificationQueue(sender.tab),
|
this.handleCloseNotificationBarMessage(message, sender),
|
||||||
bgSaveCipher: ({ message, sender }) => this.handleSaveCipherMessage(message, sender),
|
bgGetActiveUserServerConfig: () => this.getActiveUserServerConfig(),
|
||||||
bgNeverSave: ({ sender }) => this.saveNever(sender.tab),
|
bgGetDecryptedCiphers: () => this.getNotificationCipherData(),
|
||||||
collectPageDetailsResponse: ({ message }) =>
|
|
||||||
this.handleCollectPageDetailsResponseMessage(message),
|
|
||||||
bgUnlockPopoutOpened: ({ message, sender }) => this.unlockVault(message, sender.tab),
|
|
||||||
checkNotificationQueue: ({ sender }) => this.checkNotificationQueue(sender.tab),
|
|
||||||
bgReopenUnlockPopout: ({ sender }) => this.openUnlockPopout(sender.tab),
|
|
||||||
bgGetEnableChangedPasswordPrompt: () => this.getEnableChangedPasswordPrompt(),
|
bgGetEnableChangedPasswordPrompt: () => this.getEnableChangedPasswordPrompt(),
|
||||||
bgGetEnableAddedLoginPrompt: () => this.getEnableAddedLoginPrompt(),
|
bgGetEnableAddedLoginPrompt: () => this.getEnableAddedLoginPrompt(),
|
||||||
bgGetExcludedDomains: () => this.getExcludedDomains(),
|
bgGetExcludedDomains: () => this.getExcludedDomains(),
|
||||||
bgGetActiveUserServerConfig: () => this.getActiveUserServerConfig(),
|
bgGetFolderData: () => this.getFolderData(),
|
||||||
|
bgGetOrgData: () => this.getOrgData(),
|
||||||
|
bgNeverSave: ({ sender }) => this.saveNever(sender.tab),
|
||||||
|
bgOpenVault: ({ message, sender }) => this.openVault(message, sender.tab),
|
||||||
|
bgRemoveTabFromNotificationQueue: ({ sender }) =>
|
||||||
|
this.removeTabFromNotificationQueue(sender.tab),
|
||||||
|
bgReopenUnlockPopout: ({ sender }) => this.openUnlockPopout(sender.tab),
|
||||||
|
bgSaveCipher: ({ message, sender }) => this.handleSaveCipherMessage(message, sender),
|
||||||
|
bgUnlockPopoutOpened: ({ message, sender }) => this.unlockVault(message, sender.tab),
|
||||||
|
checkNotificationQueue: ({ sender }) => this.checkNotificationQueue(sender.tab),
|
||||||
|
collectPageDetailsResponse: ({ message }) =>
|
||||||
|
this.handleCollectPageDetailsResponseMessage(message),
|
||||||
getWebVaultUrlForNotification: () => this.getWebVaultUrl(),
|
getWebVaultUrlForNotification: () => this.getWebVaultUrl(),
|
||||||
notificationRefreshFlagValue: () => this.getNotificationFlag(),
|
notificationRefreshFlagValue: () => this.getNotificationFlag(),
|
||||||
bgGetDecryptedCiphers: () => this.getNotificationCipherData(),
|
unlockCompleted: ({ message, sender }) => this.handleUnlockCompleted(message, sender),
|
||||||
bgOpenVault: ({ message, sender }) => this.openVault(message, sender.tab),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private accountService: AccountService,
|
||||||
|
private authService: AuthService,
|
||||||
private autofillService: AutofillService,
|
private autofillService: AutofillService,
|
||||||
private cipherService: CipherService,
|
private cipherService: CipherService,
|
||||||
private authService: AuthService,
|
private configService: ConfigService,
|
||||||
private policyService: PolicyService,
|
|
||||||
private folderService: FolderService,
|
|
||||||
private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction,
|
|
||||||
private domainSettingsService: DomainSettingsService,
|
private domainSettingsService: DomainSettingsService,
|
||||||
private environmentService: EnvironmentService,
|
private environmentService: EnvironmentService,
|
||||||
|
private folderService: FolderService,
|
||||||
private logService: LogService,
|
private logService: LogService,
|
||||||
|
private organizationService: OrganizationService,
|
||||||
|
private policyService: PolicyService,
|
||||||
private themeStateService: ThemeStateService,
|
private themeStateService: ThemeStateService,
|
||||||
private configService: ConfigService,
|
private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction,
|
||||||
private accountService: AccountService,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
@@ -744,6 +747,26 @@ export default class NotificationBackground {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the first value found from the organization service organizations$ observable.
|
||||||
|
*/
|
||||||
|
private async getOrgData() {
|
||||||
|
const activeUserId = await firstValueFrom(
|
||||||
|
this.accountService.activeAccount$.pipe(getOptionalUserId),
|
||||||
|
);
|
||||||
|
const organizations = await firstValueFrom(
|
||||||
|
this.organizationService.organizations$(activeUserId),
|
||||||
|
);
|
||||||
|
return organizations.map((org) => {
|
||||||
|
const { id, name, productTierType } = org;
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
productTierType,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles the unlockCompleted extension message. Will close the notification bar
|
* Handles the unlockCompleted extension message. Will close the notification bar
|
||||||
* after an attempted autofill action, and retry the autofill action if the message
|
* after an attempted autofill action, and retry the autofill action if the message
|
||||||
|
|||||||
@@ -16,12 +16,12 @@ const { css } = createEmotion({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function NotificationBody({
|
export function NotificationBody({
|
||||||
ciphers,
|
ciphers = [],
|
||||||
notificationType,
|
notificationType,
|
||||||
theme = ThemeTypes.Light,
|
theme = ThemeTypes.Light,
|
||||||
handleEditOrUpdateAction,
|
handleEditOrUpdateAction,
|
||||||
}: {
|
}: {
|
||||||
ciphers: NotificationCipherData[];
|
ciphers?: NotificationCipherData[];
|
||||||
customClasses?: string[];
|
customClasses?: string[];
|
||||||
notificationType?: NotificationType;
|
notificationType?: NotificationType;
|
||||||
theme: Theme;
|
theme: Theme;
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import {
|
|||||||
NotificationType,
|
NotificationType,
|
||||||
} from "../../../notification/abstractions/notification-bar";
|
} from "../../../notification/abstractions/notification-bar";
|
||||||
import { NotificationCipherData } from "../cipher/types";
|
import { NotificationCipherData } from "../cipher/types";
|
||||||
|
import { FolderView, OrgView } from "../common-types";
|
||||||
import { themes, spacing } from "../constants/styles";
|
import { themes, spacing } from "../constants/styles";
|
||||||
|
|
||||||
import { NotificationBody, componentClassPrefix as notificationBodyClassPrefix } from "./body";
|
import { NotificationBody, componentClassPrefix as notificationBodyClassPrefix } from "./body";
|
||||||
@@ -20,20 +21,24 @@ import {
|
|||||||
|
|
||||||
export function NotificationContainer({
|
export function NotificationContainer({
|
||||||
handleCloseNotification,
|
handleCloseNotification,
|
||||||
|
handleEditOrUpdateAction,
|
||||||
|
handleSaveAction,
|
||||||
|
ciphers,
|
||||||
|
folders,
|
||||||
i18n,
|
i18n,
|
||||||
|
organizations,
|
||||||
theme = ThemeTypes.Light,
|
theme = ThemeTypes.Light,
|
||||||
type,
|
type,
|
||||||
ciphers,
|
|
||||||
handleSaveAction,
|
|
||||||
handleEditOrUpdateAction,
|
|
||||||
}: NotificationBarIframeInitData & {
|
}: NotificationBarIframeInitData & {
|
||||||
handleCloseNotification: (e: Event) => void;
|
handleCloseNotification: (e: Event) => void;
|
||||||
handleSaveAction: (e: Event) => void;
|
handleSaveAction: (e: Event) => void;
|
||||||
handleEditOrUpdateAction: (e: Event) => void;
|
handleEditOrUpdateAction: (e: Event) => void;
|
||||||
} & {
|
} & {
|
||||||
|
ciphers?: NotificationCipherData[];
|
||||||
|
folders?: FolderView[];
|
||||||
i18n: { [key: string]: string };
|
i18n: { [key: string]: string };
|
||||||
|
organizations?: OrgView[];
|
||||||
type: NotificationType; // @TODO typing override for generic `NotificationBarIframeInitData.type`
|
type: NotificationType; // @TODO typing override for generic `NotificationBarIframeInitData.type`
|
||||||
ciphers: NotificationCipherData[];
|
|
||||||
}) {
|
}) {
|
||||||
const headerMessage = getHeaderMessage(i18n, type);
|
const headerMessage = getHeaderMessage(i18n, type);
|
||||||
const showBody = true;
|
const showBody = true;
|
||||||
@@ -42,8 +47,8 @@ export function NotificationContainer({
|
|||||||
<div class=${notificationContainerStyles(theme)}>
|
<div class=${notificationContainerStyles(theme)}>
|
||||||
${NotificationHeader({
|
${NotificationHeader({
|
||||||
handleCloseNotification,
|
handleCloseNotification,
|
||||||
standalone: showBody,
|
|
||||||
message: headerMessage,
|
message: headerMessage,
|
||||||
|
standalone: showBody,
|
||||||
theme,
|
theme,
|
||||||
})}
|
})}
|
||||||
${showBody
|
${showBody
|
||||||
@@ -56,9 +61,11 @@ export function NotificationContainer({
|
|||||||
: null}
|
: null}
|
||||||
${NotificationFooter({
|
${NotificationFooter({
|
||||||
handleSaveAction,
|
handleSaveAction,
|
||||||
theme,
|
folders,
|
||||||
notificationType: type,
|
|
||||||
i18n,
|
i18n,
|
||||||
|
notificationType: type,
|
||||||
|
organizations,
|
||||||
|
theme,
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { Theme } from "@bitwarden/common/platform/enums";
|
import { Theme } from "@bitwarden/common/platform/enums";
|
||||||
|
|
||||||
|
import { NotificationCipherData } from "../../../autofill/content/components/cipher/types";
|
||||||
|
import { FolderView, OrgView } from "../../../autofill/content/components/common-types";
|
||||||
|
|
||||||
const NotificationTypes = {
|
const NotificationTypes = {
|
||||||
Add: "add",
|
Add: "add",
|
||||||
Change: "change",
|
Change: "change",
|
||||||
@@ -9,21 +12,24 @@ const NotificationTypes = {
|
|||||||
type NotificationType = (typeof NotificationTypes)[keyof typeof NotificationTypes];
|
type NotificationType = (typeof NotificationTypes)[keyof typeof NotificationTypes];
|
||||||
|
|
||||||
type NotificationBarIframeInitData = {
|
type NotificationBarIframeInitData = {
|
||||||
type?: string; // @TODO use `NotificationType`
|
|
||||||
isVaultLocked?: boolean;
|
|
||||||
theme?: Theme;
|
|
||||||
removeIndividualVault?: boolean;
|
|
||||||
importType?: string;
|
|
||||||
applyRedesign?: boolean;
|
applyRedesign?: boolean;
|
||||||
|
ciphers?: NotificationCipherData[];
|
||||||
|
folders?: FolderView[];
|
||||||
|
importType?: string;
|
||||||
|
isVaultLocked?: boolean;
|
||||||
launchTimestamp?: number;
|
launchTimestamp?: number;
|
||||||
|
organizations?: OrgView[];
|
||||||
|
removeIndividualVault?: boolean;
|
||||||
|
theme?: Theme;
|
||||||
|
type?: string; // @TODO use `NotificationType`
|
||||||
};
|
};
|
||||||
|
|
||||||
type NotificationBarWindowMessage = {
|
type NotificationBarWindowMessage = {
|
||||||
|
cipherId?: string;
|
||||||
command: string;
|
command: string;
|
||||||
error?: string;
|
error?: string;
|
||||||
initData?: NotificationBarIframeInitData;
|
initData?: NotificationBarIframeInitData;
|
||||||
username?: string;
|
username?: string;
|
||||||
cipherId?: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
type NotificationBarWindowMessageHandlers = {
|
type NotificationBarWindowMessageHandlers = {
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { ConsoleLogService } from "@bitwarden/common/platform/services/console-l
|
|||||||
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 { AdjustNotificationBarMessageData } from "../background/abstractions/notification.background";
|
||||||
|
import { NotificationCipherData } from "../content/components/cipher/types";
|
||||||
|
import { OrgView } from "../content/components/common-types";
|
||||||
import { NotificationConfirmationContainer } from "../content/components/notification/confirmation-container";
|
import { NotificationConfirmationContainer } from "../content/components/notification/confirmation-container";
|
||||||
import { NotificationContainer } from "../content/components/notification/container";
|
import { NotificationContainer } from "../content/components/notification/container";
|
||||||
import { buildSvgDomElement } from "../utils";
|
import { buildSvgDomElement } from "../utils";
|
||||||
@@ -115,7 +117,7 @@ function setElementText(template: HTMLTemplateElement, elementId: string, text:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function initNotificationBar(message: NotificationBarWindowMessage) {
|
async function initNotificationBar(message: NotificationBarWindowMessage) {
|
||||||
const { initData } = message;
|
const { initData } = message;
|
||||||
if (!initData) {
|
if (!initData) {
|
||||||
return;
|
return;
|
||||||
@@ -131,7 +133,23 @@ function initNotificationBar(message: NotificationBarWindowMessage) {
|
|||||||
// Current implementations utilize a require for scss files which creates the need to remove the node.
|
// 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());
|
document.head.querySelectorAll('link[rel="stylesheet"]').forEach((node) => node.remove());
|
||||||
|
|
||||||
sendPlatformMessage({ command: "bgGetDecryptedCiphers" }, (cipherData) => {
|
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),
|
||||||
|
),
|
||||||
|
]).then(([organizations, folders, ciphers]) => {
|
||||||
|
notificationBarIframeInitData = {
|
||||||
|
...notificationBarIframeInitData,
|
||||||
|
folders,
|
||||||
|
ciphers,
|
||||||
|
organizations,
|
||||||
|
};
|
||||||
// @TODO use context to avoid prop drilling
|
// @TODO use context to avoid prop drilling
|
||||||
return render(
|
return render(
|
||||||
NotificationContainer({
|
NotificationContainer({
|
||||||
@@ -142,7 +160,6 @@ function initNotificationBar(message: NotificationBarWindowMessage) {
|
|||||||
handleSaveAction,
|
handleSaveAction,
|
||||||
handleEditOrUpdateAction,
|
handleEditOrUpdateAction,
|
||||||
i18n,
|
i18n,
|
||||||
ciphers: cipherData,
|
|
||||||
}),
|
}),
|
||||||
document.body,
|
document.body,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1173,18 +1173,19 @@ export default class MainBackground {
|
|||||||
() => this.generatePasswordToClipboard(),
|
() => this.generatePasswordToClipboard(),
|
||||||
);
|
);
|
||||||
this.notificationBackground = new NotificationBackground(
|
this.notificationBackground = new NotificationBackground(
|
||||||
|
this.accountService,
|
||||||
|
this.authService,
|
||||||
this.autofillService,
|
this.autofillService,
|
||||||
this.cipherService,
|
this.cipherService,
|
||||||
this.authService,
|
this.configService,
|
||||||
this.policyService,
|
|
||||||
this.folderService,
|
|
||||||
this.userNotificationSettingsService,
|
|
||||||
this.domainSettingsService,
|
this.domainSettingsService,
|
||||||
this.environmentService,
|
this.environmentService,
|
||||||
|
this.folderService,
|
||||||
this.logService,
|
this.logService,
|
||||||
|
this.organizationService,
|
||||||
|
this.policyService,
|
||||||
this.themeStateService,
|
this.themeStateService,
|
||||||
this.configService,
|
this.userNotificationSettingsService,
|
||||||
this.accountService,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.overlayNotificationsBackground = new OverlayNotificationsBackground(
|
this.overlayNotificationsBackground = new OverlayNotificationsBackground(
|
||||||
|
|||||||
Reference in New Issue
Block a user