mirror of
https://github.com/bitwarden/browser
synced 2025-12-13 06:43:35 +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 { 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 { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
@@ -59,6 +60,7 @@ describe("NotificationBackground", () => {
|
||||
const themeStateService = mock<ThemeStateService>();
|
||||
const configService = mock<ConfigService>();
|
||||
const accountService = mock<AccountService>();
|
||||
const organizationService = mock<OrganizationService>();
|
||||
|
||||
const activeAccountSubject = new BehaviorSubject<{ id: UserId } & AccountInfo>({
|
||||
id: "testId" as UserId,
|
||||
@@ -73,18 +75,19 @@ describe("NotificationBackground", () => {
|
||||
authService.activeAccountStatus$ = activeAccountStatusMock$;
|
||||
accountService.activeAccount$ = activeAccountSubject;
|
||||
notificationBackground = new NotificationBackground(
|
||||
accountService,
|
||||
authService,
|
||||
autofillService,
|
||||
cipherService,
|
||||
authService,
|
||||
policyService,
|
||||
folderService,
|
||||
userNotificationSettingsService,
|
||||
configService,
|
||||
domainSettingsService,
|
||||
environmentService,
|
||||
folderService,
|
||||
logService,
|
||||
organizationService,
|
||||
policyService,
|
||||
themeStateService,
|
||||
configService,
|
||||
accountService,
|
||||
userNotificationSettingsService,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// @ts-strict-ignore
|
||||
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 { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
@@ -63,46 +64,48 @@ export default class NotificationBackground {
|
||||
ExtensionCommand.AutofillIdentity,
|
||||
]);
|
||||
private readonly extensionMessageHandlers: NotificationBackgroundExtensionMessageHandlers = {
|
||||
unlockCompleted: ({ message, sender }) => this.handleUnlockCompleted(message, sender),
|
||||
bgGetFolderData: () => this.getFolderData(),
|
||||
bgCloseNotificationBar: ({ message, sender }) =>
|
||||
this.handleCloseNotificationBarMessage(message, sender),
|
||||
bgAddLogin: ({ message, sender }) => this.addLogin(message, sender),
|
||||
bgAdjustNotificationBar: ({ message, sender }) =>
|
||||
this.handleAdjustNotificationBarMessage(message, sender),
|
||||
bgAddLogin: ({ message, sender }) => this.addLogin(message, sender),
|
||||
bgChangedPassword: ({ message, sender }) => this.changedPassword(message, sender),
|
||||
bgRemoveTabFromNotificationQueue: ({ sender }) =>
|
||||
this.removeTabFromNotificationQueue(sender.tab),
|
||||
bgSaveCipher: ({ message, sender }) => this.handleSaveCipherMessage(message, sender),
|
||||
bgNeverSave: ({ sender }) => this.saveNever(sender.tab),
|
||||
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),
|
||||
bgCloseNotificationBar: ({ message, sender }) =>
|
||||
this.handleCloseNotificationBarMessage(message, sender),
|
||||
bgGetActiveUserServerConfig: () => this.getActiveUserServerConfig(),
|
||||
bgGetDecryptedCiphers: () => this.getNotificationCipherData(),
|
||||
bgGetEnableChangedPasswordPrompt: () => this.getEnableChangedPasswordPrompt(),
|
||||
bgGetEnableAddedLoginPrompt: () => this.getEnableAddedLoginPrompt(),
|
||||
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(),
|
||||
notificationRefreshFlagValue: () => this.getNotificationFlag(),
|
||||
bgGetDecryptedCiphers: () => this.getNotificationCipherData(),
|
||||
bgOpenVault: ({ message, sender }) => this.openVault(message, sender.tab),
|
||||
unlockCompleted: ({ message, sender }) => this.handleUnlockCompleted(message, sender),
|
||||
};
|
||||
|
||||
constructor(
|
||||
private accountService: AccountService,
|
||||
private authService: AuthService,
|
||||
private autofillService: AutofillService,
|
||||
private cipherService: CipherService,
|
||||
private authService: AuthService,
|
||||
private policyService: PolicyService,
|
||||
private folderService: FolderService,
|
||||
private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction,
|
||||
private configService: ConfigService,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private environmentService: EnvironmentService,
|
||||
private folderService: FolderService,
|
||||
private logService: LogService,
|
||||
private organizationService: OrganizationService,
|
||||
private policyService: PolicyService,
|
||||
private themeStateService: ThemeStateService,
|
||||
private configService: ConfigService,
|
||||
private accountService: AccountService,
|
||||
private userNotificationSettingsService: UserNotificationSettingsServiceAbstraction,
|
||||
) {}
|
||||
|
||||
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
|
||||
* after an attempted autofill action, and retry the autofill action if the message
|
||||
|
||||
@@ -16,12 +16,12 @@ const { css } = createEmotion({
|
||||
});
|
||||
|
||||
export function NotificationBody({
|
||||
ciphers,
|
||||
ciphers = [],
|
||||
notificationType,
|
||||
theme = ThemeTypes.Light,
|
||||
handleEditOrUpdateAction,
|
||||
}: {
|
||||
ciphers: NotificationCipherData[];
|
||||
ciphers?: NotificationCipherData[];
|
||||
customClasses?: string[];
|
||||
notificationType?: NotificationType;
|
||||
theme: Theme;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
NotificationType,
|
||||
} from "../../../notification/abstractions/notification-bar";
|
||||
import { NotificationCipherData } from "../cipher/types";
|
||||
import { FolderView, OrgView } from "../common-types";
|
||||
import { themes, spacing } from "../constants/styles";
|
||||
|
||||
import { NotificationBody, componentClassPrefix as notificationBodyClassPrefix } from "./body";
|
||||
@@ -20,20 +21,24 @@ import {
|
||||
|
||||
export function NotificationContainer({
|
||||
handleCloseNotification,
|
||||
handleEditOrUpdateAction,
|
||||
handleSaveAction,
|
||||
ciphers,
|
||||
folders,
|
||||
i18n,
|
||||
organizations,
|
||||
theme = ThemeTypes.Light,
|
||||
type,
|
||||
ciphers,
|
||||
handleSaveAction,
|
||||
handleEditOrUpdateAction,
|
||||
}: NotificationBarIframeInitData & {
|
||||
handleCloseNotification: (e: Event) => void;
|
||||
handleSaveAction: (e: Event) => void;
|
||||
handleEditOrUpdateAction: (e: Event) => void;
|
||||
} & {
|
||||
ciphers?: NotificationCipherData[];
|
||||
folders?: FolderView[];
|
||||
i18n: { [key: string]: string };
|
||||
organizations?: OrgView[];
|
||||
type: NotificationType; // @TODO typing override for generic `NotificationBarIframeInitData.type`
|
||||
ciphers: NotificationCipherData[];
|
||||
}) {
|
||||
const headerMessage = getHeaderMessage(i18n, type);
|
||||
const showBody = true;
|
||||
@@ -42,8 +47,8 @@ export function NotificationContainer({
|
||||
<div class=${notificationContainerStyles(theme)}>
|
||||
${NotificationHeader({
|
||||
handleCloseNotification,
|
||||
standalone: showBody,
|
||||
message: headerMessage,
|
||||
standalone: showBody,
|
||||
theme,
|
||||
})}
|
||||
${showBody
|
||||
@@ -56,9 +61,11 @@ export function NotificationContainer({
|
||||
: null}
|
||||
${NotificationFooter({
|
||||
handleSaveAction,
|
||||
theme,
|
||||
notificationType: type,
|
||||
folders,
|
||||
i18n,
|
||||
notificationType: type,
|
||||
organizations,
|
||||
theme,
|
||||
})}
|
||||
</div>
|
||||
`;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
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 = {
|
||||
Add: "add",
|
||||
Change: "change",
|
||||
@@ -9,21 +12,24 @@ const NotificationTypes = {
|
||||
type NotificationType = (typeof NotificationTypes)[keyof typeof NotificationTypes];
|
||||
|
||||
type NotificationBarIframeInitData = {
|
||||
type?: string; // @TODO use `NotificationType`
|
||||
isVaultLocked?: boolean;
|
||||
theme?: Theme;
|
||||
removeIndividualVault?: boolean;
|
||||
importType?: string;
|
||||
applyRedesign?: boolean;
|
||||
ciphers?: NotificationCipherData[];
|
||||
folders?: FolderView[];
|
||||
importType?: string;
|
||||
isVaultLocked?: boolean;
|
||||
launchTimestamp?: number;
|
||||
organizations?: OrgView[];
|
||||
removeIndividualVault?: boolean;
|
||||
theme?: Theme;
|
||||
type?: string; // @TODO use `NotificationType`
|
||||
};
|
||||
|
||||
type NotificationBarWindowMessage = {
|
||||
cipherId?: string;
|
||||
command: string;
|
||||
error?: string;
|
||||
initData?: NotificationBarIframeInitData;
|
||||
username?: string;
|
||||
cipherId?: string;
|
||||
};
|
||||
|
||||
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 { 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 { NotificationContainer } from "../content/components/notification/container";
|
||||
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;
|
||||
if (!initData) {
|
||||
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.
|
||||
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
|
||||
return render(
|
||||
NotificationContainer({
|
||||
@@ -142,7 +160,6 @@ function initNotificationBar(message: NotificationBarWindowMessage) {
|
||||
handleSaveAction,
|
||||
handleEditOrUpdateAction,
|
||||
i18n,
|
||||
ciphers: cipherData,
|
||||
}),
|
||||
document.body,
|
||||
);
|
||||
|
||||
@@ -1173,18 +1173,19 @@ export default class MainBackground {
|
||||
() => this.generatePasswordToClipboard(),
|
||||
);
|
||||
this.notificationBackground = new NotificationBackground(
|
||||
this.accountService,
|
||||
this.authService,
|
||||
this.autofillService,
|
||||
this.cipherService,
|
||||
this.authService,
|
||||
this.policyService,
|
||||
this.folderService,
|
||||
this.userNotificationSettingsService,
|
||||
this.configService,
|
||||
this.domainSettingsService,
|
||||
this.environmentService,
|
||||
this.folderService,
|
||||
this.logService,
|
||||
this.organizationService,
|
||||
this.policyService,
|
||||
this.themeStateService,
|
||||
this.configService,
|
||||
this.accountService,
|
||||
this.userNotificationSettingsService,
|
||||
);
|
||||
|
||||
this.overlayNotificationsBackground = new OverlayNotificationsBackground(
|
||||
|
||||
Reference in New Issue
Block a user