1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 06:13:38 +00:00

Pm 19366 handle selections in notification dropdown component to save ciphers appropriately (#14070)

* initial approach for folder selection via queryselect

* handle folder selection with signals

* custom signal when needed on option select to track individual select values

* add vault signal

* initial approach for collection data

* different calls for collections, add collection signal, alter approach

* add appropriate icon for collections dropdown

* populate vault with notification queue

* org id added to extension message type

* clean up naming for upcoming change

* use reduce in getCollections
This commit is contained in:
Daniel Riera
2025-04-25 11:16:00 -04:00
committed by GitHub
parent ab7016fd6b
commit 4943e70965
15 changed files with 169 additions and 20 deletions

View File

@@ -2,6 +2,7 @@ import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
import { ServerConfig } from "@bitwarden/common/platform/abstractions/config/server-config"; import { ServerConfig } from "@bitwarden/common/platform/abstractions/config/server-config";
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
import { CollectionView } from "../../content/components/common-types";
import { NotificationQueueMessageTypes } from "../../enums/notification-queue-message-type.enum"; import { NotificationQueueMessageTypes } from "../../enums/notification-queue-message-type.enum";
import AutofillPageDetails from "../../models/autofill-page-details"; import AutofillPageDetails from "../../models/autofill-page-details";
@@ -83,6 +84,7 @@ type NotificationBackgroundExtensionMessage = {
tab?: chrome.tabs.Tab; tab?: chrome.tabs.Tab;
sender?: string; sender?: string;
notificationType?: string; notificationType?: string;
organizationId?: string;
fadeOutNotification?: boolean; fadeOutNotification?: boolean;
}; };
@@ -94,6 +96,10 @@ type NotificationBackgroundExtensionMessageHandlers = {
[key: string]: CallableFunction; [key: string]: CallableFunction;
unlockCompleted: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>; unlockCompleted: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
bgGetFolderData: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<FolderView[]>; bgGetFolderData: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<FolderView[]>;
bgGetCollectionData: ({
message,
sender,
}: BackgroundOnMessageHandlerParams) => Promise<CollectionView[]>;
bgCloseNotificationBar: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>; bgCloseNotificationBar: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
bgOpenAtRisksPasswords: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>; bgOpenAtRisksPasswords: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
bgAdjustNotificationBar: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>; bgAdjustNotificationBar: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
@@ -101,11 +107,14 @@ type NotificationBackgroundExtensionMessageHandlers = {
bgChangedPassword: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>; bgChangedPassword: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
bgRemoveTabFromNotificationQueue: ({ sender }: BackgroundSenderParam) => void; bgRemoveTabFromNotificationQueue: ({ sender }: BackgroundSenderParam) => void;
bgSaveCipher: ({ message, sender }: BackgroundOnMessageHandlerParams) => void; bgSaveCipher: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
bgOpenAddEditVaultItemPopout: ({
message,
sender,
}: BackgroundOnMessageHandlerParams) => Promise<void>;
bgOpenViewVaultItemPopout: ({ bgOpenViewVaultItemPopout: ({
message, message,
sender, sender,
}: BackgroundOnMessageHandlerParams) => Promise<void>; }: BackgroundOnMessageHandlerParams) => Promise<void>;
bgOpenVault: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
bgNeverSave: ({ sender }: BackgroundSenderParam) => Promise<void>; bgNeverSave: ({ sender }: BackgroundSenderParam) => Promise<void>;
bgUnlockPopoutOpened: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>; bgUnlockPopoutOpened: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
bgReopenUnlockPopout: ({ sender }: BackgroundSenderParam) => Promise<void>; bgReopenUnlockPopout: ({ sender }: BackgroundSenderParam) => Promise<void>;

View File

@@ -1,6 +1,7 @@
import { mock, MockProxy } from "jest-mock-extended"; import { mock, MockProxy } from "jest-mock-extended";
import { BehaviorSubject, firstValueFrom, of } from "rxjs"; import { BehaviorSubject, firstValueFrom, of } from "rxjs";
import { CollectionService } from "@bitwarden/admin-console/common";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { DefaultPolicyService } from "@bitwarden/common/admin-console/services/policy/default-policy.service"; import { DefaultPolicyService } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
@@ -53,6 +54,7 @@ describe("NotificationBackground", () => {
let notificationBackground: NotificationBackground; let notificationBackground: NotificationBackground;
const autofillService = mock<AutofillService>(); const autofillService = mock<AutofillService>();
const cipherService = mock<CipherService>(); const cipherService = mock<CipherService>();
const collectionService = mock<CollectionService>();
let activeAccountStatusMock$: BehaviorSubject<AuthenticationStatus>; let activeAccountStatusMock$: BehaviorSubject<AuthenticationStatus>;
let authService: MockProxy<AuthService>; let authService: MockProxy<AuthService>;
const policyService = mock<DefaultPolicyService>(); const policyService = mock<DefaultPolicyService>();
@@ -83,6 +85,7 @@ describe("NotificationBackground", () => {
authService, authService,
autofillService, autofillService,
cipherService, cipherService,
collectionService,
configService, configService,
domainSettingsService, domainSettingsService,
environmentService, environmentService,

View File

@@ -2,6 +2,7 @@
// @ts-strict-ignore // @ts-strict-ignore
import { firstValueFrom, switchMap, map, of } from "rxjs"; import { firstValueFrom, switchMap, map, of } from "rxjs";
import { CollectionService } from "@bitwarden/admin-console/common";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; 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";
@@ -50,6 +51,7 @@ import {
OrganizationCategories, OrganizationCategories,
NotificationCipherData, NotificationCipherData,
} from "../content/components/cipher/types"; } from "../content/components/cipher/types";
import { CollectionView } from "../content/components/common-types";
import { NotificationQueueMessageType } from "../enums/notification-queue-message-type.enum"; import { NotificationQueueMessageType } from "../enums/notification-queue-message-type.enum";
import { AutofillService } from "../services/abstractions/autofill.service"; import { AutofillService } from "../services/abstractions/autofill.service";
@@ -92,9 +94,11 @@ export default class NotificationBackground {
bgGetEnableAddedLoginPrompt: () => this.getEnableAddedLoginPrompt(), bgGetEnableAddedLoginPrompt: () => this.getEnableAddedLoginPrompt(),
bgGetExcludedDomains: () => this.getExcludedDomains(), bgGetExcludedDomains: () => this.getExcludedDomains(),
bgGetFolderData: () => this.getFolderData(), bgGetFolderData: () => this.getFolderData(),
bgGetCollectionData: ({ message }) => this.getCollectionData(message),
bgGetOrgData: () => this.getOrgData(), bgGetOrgData: () => this.getOrgData(),
bgNeverSave: ({ sender }) => this.saveNever(sender.tab), bgNeverSave: ({ sender }) => this.saveNever(sender.tab),
bgOpenVault: ({ message, sender }) => this.openVault(message, sender.tab), bgOpenAddEditVaultItemPopout: ({ message, sender }) =>
this.openAddEditVaultItem(message, sender.tab),
bgOpenViewVaultItemPopout: ({ message, sender }) => this.viewItem(message, sender.tab), bgOpenViewVaultItemPopout: ({ message, sender }) => this.viewItem(message, sender.tab),
bgRemoveTabFromNotificationQueue: ({ sender }) => bgRemoveTabFromNotificationQueue: ({ sender }) =>
this.removeTabFromNotificationQueue(sender.tab), this.removeTabFromNotificationQueue(sender.tab),
@@ -114,6 +118,7 @@ export default class NotificationBackground {
private authService: AuthService, private authService: AuthService,
private autofillService: AutofillService, private autofillService: AutofillService,
private cipherService: CipherService, private cipherService: CipherService,
private collectionService: CollectionService,
private configService: ConfigService, private configService: ConfigService,
private domainSettingsService: DomainSettingsService, private domainSettingsService: DomainSettingsService,
private environmentService: EnvironmentService, private environmentService: EnvironmentService,
@@ -789,17 +794,36 @@ export default class NotificationBackground {
userId, userId,
); );
await this.openAddEditVaultItemPopout(senderTab, { cipherId: cipherView.id }); await this.openAddEditVaultItemPopout(senderTab, { cipherId: cipherView?.id });
} }
private async openVault( private async openAddEditVaultItem(
message: NotificationBackgroundExtensionMessage, message: NotificationBackgroundExtensionMessage,
senderTab: chrome.tabs.Tab, senderTab: chrome.tabs.Tab,
) { ) {
if (!message.cipherId) { const { cipherId, organizationId } = message;
await this.openAddEditVaultItemPopout(senderTab); const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getOptionalUserId));
if (cipherId) {
await this.openAddEditVaultItemPopout(senderTab, { cipherId });
return;
} }
await this.openAddEditVaultItemPopout(senderTab, { cipherId: message.cipherId });
const queueItem = this.notificationQueue.find((item) => item.tab.id === senderTab.id);
if (queueItem?.type === NotificationQueueMessageType.AddLogin) {
const cipherView = this.convertAddLoginQueueMessageToCipherView(queueItem);
cipherView.organizationId = organizationId;
if (userId) {
await this.cipherService.setAddEditCipherInfo({ cipher: cipherView }, userId);
}
await this.openAddEditVaultItemPopout(senderTab);
this.removeTabFromNotificationQueue(senderTab);
return;
}
await this.openAddEditVaultItemPopout(senderTab);
} }
private async viewItem( private async viewItem(
@@ -898,6 +922,25 @@ export default class NotificationBackground {
return await firstValueFrom(this.folderService.folderViews$(activeUserId)); return await firstValueFrom(this.folderService.folderViews$(activeUserId));
} }
private async getCollectionData(
message: NotificationBackgroundExtensionMessage,
): Promise<CollectionView[]> {
const collections = (await this.collectionService.getAllDecrypted()).reduce<CollectionView[]>(
(acc, collection) => {
if (collection.organizationId === message?.orgId) {
acc.push({
id: collection.id,
name: collection.name,
organizationId: collection.organizationId,
});
}
return acc;
},
[],
);
return collections;
}
private async getWebVaultUrl(): Promise<string> { private async getWebVaultUrl(): Promise<string> {
const env = await firstValueFrom(this.environmentService.environment$); const env = await firstValueFrom(this.environmentService.environment$);
return env.getWebVaultUrl(); return env.getWebVaultUrl();
@@ -924,6 +967,7 @@ export default class NotificationBackground {
const organizations = await firstValueFrom( const organizations = await firstValueFrom(
this.organizationService.organizations$(activeUserId), this.organizationService.organizations$(activeUserId),
); );
return organizations.map((org) => { return organizations.map((org) => {
const { id, name, productTierType } = org; const { id, name, productTierType } = org;
return { return {
@@ -1054,6 +1098,7 @@ export default class NotificationBackground {
cipherView.folderId = folderId; cipherView.folderId = folderId;
cipherView.type = CipherType.Login; cipherView.type = CipherType.Login;
cipherView.login = loginView; cipherView.login = loginView;
cipherView.organizationId = null;
return cipherView; return cipherView;
} }

View File

@@ -26,3 +26,9 @@ export type OrgView = {
name: string; name: string;
productTierType?: ProductTierType; productTierType?: ProductTierType;
}; };
export type CollectionView = {
id: string;
name: string;
organizationId: string;
};

View File

@@ -3,9 +3,12 @@ import { html } from "lit";
import { ProductTierType } from "@bitwarden/common/billing/enums"; import { ProductTierType } from "@bitwarden/common/billing/enums";
import { Theme } from "@bitwarden/common/platform/enums"; import { Theme } from "@bitwarden/common/platform/enums";
import { Option, OrgView, FolderView } from "../common-types"; import { Option, OrgView, FolderView, CollectionView } from "../common-types";
import { Business, Family, Folder, User } from "../icons"; import { Business, Family, Folder, User, CollectionShared } from "../icons";
import { ButtonRow } from "../rows/button-row"; import { ButtonRow } from "../rows/button-row";
import { selectedCollection as selectedCollectionSignal } from "../signals/selected-collection";
import { selectedFolder as selectedFolderSignal } from "../signals/selected-folder";
import { selectedVault as selectedVaultSignal } from "../signals/selected-vault";
function getVaultIconByProductTier(productTierType?: ProductTierType): Option["icon"] { function getVaultIconByProductTier(productTierType?: ProductTierType): Option["icon"] {
switch (productTierType) { switch (productTierType) {
@@ -29,11 +32,13 @@ export type NotificationButtonRowProps = {
text: string; text: string;
handlePrimaryButtonClick: (args: any) => void; handlePrimaryButtonClick: (args: any) => void;
}; };
collections?: CollectionView[];
theme: Theme; theme: Theme;
}; };
export function NotificationButtonRow({ export function NotificationButtonRow({
folders, folders,
collections,
i18n, i18n,
organizations, organizations,
primaryButton, primaryButton,
@@ -77,6 +82,21 @@ export function NotificationButtonRow({
) )
: []; : [];
const collectionOptions: Option[] = collections?.length
? collections.reduce<Option[]>(
(options, { id, name }: any) => [
...options,
{
icon: CollectionShared,
text: name,
value: id === null ? "0" : id,
default: id === null,
},
],
[],
)
: [];
return html` return html`
${ButtonRow({ ${ButtonRow({
theme, theme,
@@ -88,15 +108,27 @@ export function NotificationButtonRow({
id: "organization", id: "organization",
label: i18n.vault, label: i18n.vault,
options: organizationOptions, options: organizationOptions,
selectedSignal: selectedVaultSignal,
}, },
] ]
: []), : []),
...(folderOptions.length > 1 ...(folderOptions.length > 1 && !collectionOptions.length
? [ ? [
{ {
id: "folder", id: "folder",
label: i18n.folder, label: i18n.folder,
options: folderOptions, options: folderOptions,
selectedSignal: selectedFolderSignal,
},
]
: []),
...(collectionOptions.length > 1
? [
{
id: "collection",
label: "Collection", // @TODO localize
options: collectionOptions,
selectedSignal: selectedCollectionSignal,
}, },
] ]
: []), : []),

View File

@@ -9,7 +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 { CollectionView, 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";
@@ -25,6 +25,7 @@ export type NotificationContainerProps = NotificationBarIframeInitData & {
handleEditOrUpdateAction: (e: Event) => void; handleEditOrUpdateAction: (e: Event) => void;
} & { } & {
ciphers?: NotificationCipherData[]; ciphers?: NotificationCipherData[];
collections?: CollectionView[];
folders?: FolderView[]; folders?: FolderView[];
i18n: { [key: string]: string }; i18n: { [key: string]: string };
organizations?: OrgView[]; organizations?: OrgView[];
@@ -36,6 +37,7 @@ export function NotificationContainer({
handleEditOrUpdateAction, handleEditOrUpdateAction,
handleSaveAction, handleSaveAction,
ciphers, ciphers,
collections,
folders, folders,
i18n, i18n,
organizations, organizations,
@@ -64,6 +66,7 @@ export function NotificationContainer({
: null} : null}
${NotificationFooter({ ${NotificationFooter({
handleSaveAction, handleSaveAction,
collections,
folders, folders,
i18n, i18n,
notificationType: type, notificationType: type,

View File

@@ -7,12 +7,13 @@ import {
NotificationType, NotificationType,
NotificationTypes, NotificationTypes,
} from "../../../notification/abstractions/notification-bar"; } from "../../../notification/abstractions/notification-bar";
import { OrgView, FolderView } from "../common-types"; import { OrgView, FolderView, CollectionView } from "../common-types";
import { spacing, themes } from "../constants/styles"; import { spacing, themes } from "../constants/styles";
import { NotificationButtonRow } from "./button-row"; import { NotificationButtonRow } from "./button-row";
export type NotificationFooterProps = { export type NotificationFooterProps = {
collections?: CollectionView[];
folders?: FolderView[]; folders?: FolderView[];
i18n: { [key: string]: string }; i18n: { [key: string]: string };
notificationType?: NotificationType; notificationType?: NotificationType;
@@ -22,6 +23,7 @@ export type NotificationFooterProps = {
}; };
export function NotificationFooter({ export function NotificationFooter({
collections,
folders, folders,
i18n, i18n,
notificationType, notificationType,
@@ -36,6 +38,7 @@ export function NotificationFooter({
<div class=${notificationFooterStyles({ theme })}> <div class=${notificationFooterStyles({ theme })}>
${!isChangeNotification ${!isChangeNotification
? NotificationButtonRow({ ? NotificationButtonRow({
collections,
folders, folders,
organizations, organizations,
i18n, i18n,

View File

@@ -32,6 +32,9 @@ export class OptionSelection extends LitElement {
@property({ type: (selectedOption: Option["value"]) => selectedOption }) @property({ type: (selectedOption: Option["value"]) => selectedOption })
handleSelectionUpdate?: (args: any) => void; handleSelectionUpdate?: (args: any) => void;
@property({ attribute: false })
selectedSignal?: { set: (value: any) => void };
@state() @state()
private showMenu = false; private showMenu = false;
@@ -77,7 +80,7 @@ export class OptionSelection extends LitElement {
private handleOptionSelection = (selectedOption: Option) => { private handleOptionSelection = (selectedOption: Option) => {
this.showMenu = false; this.showMenu = false;
this.selection = selectedOption; this.selection = selectedOption;
this.selectedSignal?.set(selectedOption.value);
// Any side-effects that should occur from the selection // Any side-effects that should occur from the selection
this.handleSelectionUpdate?.(selectedOption.value); this.handleSelectionUpdate?.(selectedOption.value);
}; };

View File

@@ -19,6 +19,7 @@ export type ButtonRowProps = {
label?: string; label?: string;
options: Option[]; options: Option[];
handleSelectionUpdate?: (args: any) => void; handleSelectionUpdate?: (args: any) => void;
selectedSignal?: { set: (value: any) => void };
}[]; }[];
}; };
@@ -32,7 +33,7 @@ export function ButtonRow({ theme, primaryButton, selectButtons }: ButtonRowProp
})} })}
<div class=${optionSelectionsStyles}> <div class=${optionSelectionsStyles}>
${selectButtons?.map( ${selectButtons?.map(
({ id, label, options, handleSelectionUpdate }) => ({ id, label, options, handleSelectionUpdate, selectedSignal }) =>
html` html`
<option-selection <option-selection
key=${id} key=${id}
@@ -40,6 +41,7 @@ export function ButtonRow({ theme, primaryButton, selectButtons }: ButtonRowProp
.label=${label} .label=${label}
.options=${options} .options=${options}
.handleSelectionUpdate=${handleSelectionUpdate} .handleSelectionUpdate=${handleSelectionUpdate}
.selectedSignal=${selectedSignal}
></option-selection> ></option-selection>
` || nothing, ` || nothing,
)} )}

View File

@@ -0,0 +1,3 @@
import { signal } from "@lit-labs/signals";
export const selectedCollection = signal<string>("0");

View File

@@ -0,0 +1,3 @@
import { signal } from "@lit-labs/signals";
export const selectedFolder = signal<string>("0");

View File

@@ -0,0 +1,3 @@
import { signal } from "@lit-labs/signals";
export const selectedVault = signal<string>("0");

View File

@@ -1,7 +1,11 @@
import { Theme } from "@bitwarden/common/platform/enums"; import { Theme } from "@bitwarden/common/platform/enums";
import { NotificationCipherData } from "../../../autofill/content/components/cipher/types"; import { NotificationCipherData } from "../../../autofill/content/components/cipher/types";
import { FolderView, OrgView } from "../../../autofill/content/components/common-types"; import {
FolderView,
OrgView,
CollectionView,
} from "../../../autofill/content/components/common-types";
const NotificationTypes = { const NotificationTypes = {
Add: "add", Add: "add",
@@ -19,6 +23,7 @@ type NotificationTaskInfo = {
type NotificationBarIframeInitData = { type NotificationBarIframeInitData = {
ciphers?: NotificationCipherData[]; ciphers?: NotificationCipherData[];
folders?: FolderView[]; folders?: FolderView[];
collections?: CollectionView[];
importType?: string; importType?: string;
isVaultLocked?: boolean; isVaultLocked?: boolean;
launchTimestamp?: number; launchTimestamp?: number;

View File

@@ -6,9 +6,11 @@ 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 { NotificationCipherData } from "../content/components/cipher/types";
import { OrgView } from "../content/components/common-types"; import { CollectionView, 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 { selectedFolder as selectedFolderSignal } from "../content/components/signals/selected-folder";
import { selectedVault as selectedVaultSignal } from "../content/components/signals/selected-vault";
import { buildSvgDomElement } from "../utils"; import { buildSvgDomElement } from "../utils";
import { circleCheckIcon } from "../utils/svg-icons"; import { circleCheckIcon } from "../utils/svg-icons";
@@ -41,6 +43,7 @@ function load() {
applyNotificationBarStyle(); applyNotificationBarStyle();
}); });
} }
function applyNotificationBarStyle() { function applyNotificationBarStyle() {
if (!useComponentBar) { if (!useComponentBar) {
// eslint-disable-next-line @typescript-eslint/no-require-imports // eslint-disable-next-line @typescript-eslint/no-require-imports
@@ -140,7 +143,7 @@ async function initNotificationBar(message: NotificationBarWindowMessage) {
document.body.innerHTML = ""; document.body.innerHTML = "";
// 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());
const orgId = selectedVaultSignal.get();
await Promise.all([ await Promise.all([
new Promise<OrgView[]>((resolve) => new Promise<OrgView[]>((resolve) =>
sendPlatformMessage({ command: "bgGetOrgData" }, resolve), sendPlatformMessage({ command: "bgGetOrgData" }, resolve),
@@ -151,13 +154,18 @@ async function initNotificationBar(message: NotificationBarWindowMessage) {
new Promise<NotificationCipherData[]>((resolve) => new Promise<NotificationCipherData[]>((resolve) =>
sendPlatformMessage({ command: "bgGetDecryptedCiphers" }, resolve), sendPlatformMessage({ command: "bgGetDecryptedCiphers" }, resolve),
), ),
]).then(([organizations, folders, ciphers]) => { new Promise<CollectionView[]>((resolve) =>
sendPlatformMessage({ command: "bgGetCollectionData", orgId }, resolve),
),
]).then(([organizations, folders, ciphers, collections]) => {
notificationBarIframeInitData = { notificationBarIframeInitData = {
...notificationBarIframeInitData, ...notificationBarIframeInitData,
organizations,
folders, folders,
ciphers, ciphers,
organizations, collections,
}; };
// @TODO use context to avoid prop drilling // @TODO use context to avoid prop drilling
return render( return render(
NotificationContainer({ NotificationContainer({
@@ -254,9 +262,18 @@ function handleCloseNotification(e: Event) {
} }
function handleSaveAction(e: Event) { function handleSaveAction(e: Event) {
const selectedVault = selectedVaultSignal.get();
if (selectedVault.length > 1) {
openAddEditVaultItemPopout(e, { organizationId: selectedVault });
handleCloseNotification(e);
return;
}
e.preventDefault(); e.preventDefault();
sendSaveCipherMessage(removeIndividualVault()); const selectedFolder = selectedFolderSignal.get();
sendSaveCipherMessage(removeIndividualVault(), selectedFolder);
if (removeIndividualVault()) { if (removeIndividualVault()) {
return; return;
} }
@@ -351,6 +368,17 @@ function handleSaveCipherAttemptCompletedMessage(message: NotificationBarWindowM
); );
} }
function openAddEditVaultItemPopout(
e: Event,
options: { cipherId?: string; organizationId?: string },
) {
e.preventDefault();
sendPlatformMessage({
command: "bgOpenAddEditVaultItemPopout",
...options,
});
}
function openViewVaultItemPopout(cipherId: string) { function openViewVaultItemPopout(cipherId: string) {
sendPlatformMessage({ sendPlatformMessage({
command: "bgOpenViewVaultItemPopout", command: "bgOpenViewVaultItemPopout",

View File

@@ -1206,6 +1206,7 @@ export default class MainBackground {
this.authService, this.authService,
this.autofillService, this.autofillService,
this.cipherService, this.cipherService,
this.collectionService,
this.configService, this.configService,
this.domainSettingsService, this.domainSettingsService,
this.environmentService, this.environmentService,