mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 05:43:41 +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:
@@ -2,6 +2,7 @@ import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
|
||||
import { ServerConfig } from "@bitwarden/common/platform/abstractions/config/server-config";
|
||||
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 AutofillPageDetails from "../../models/autofill-page-details";
|
||||
|
||||
@@ -83,6 +84,7 @@ type NotificationBackgroundExtensionMessage = {
|
||||
tab?: chrome.tabs.Tab;
|
||||
sender?: string;
|
||||
notificationType?: string;
|
||||
organizationId?: string;
|
||||
fadeOutNotification?: boolean;
|
||||
};
|
||||
|
||||
@@ -94,6 +96,10 @@ type NotificationBackgroundExtensionMessageHandlers = {
|
||||
[key: string]: CallableFunction;
|
||||
unlockCompleted: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
|
||||
bgGetFolderData: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<FolderView[]>;
|
||||
bgGetCollectionData: ({
|
||||
message,
|
||||
sender,
|
||||
}: BackgroundOnMessageHandlerParams) => Promise<CollectionView[]>;
|
||||
bgCloseNotificationBar: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
|
||||
bgOpenAtRisksPasswords: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
|
||||
bgAdjustNotificationBar: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
|
||||
@@ -101,11 +107,14 @@ type NotificationBackgroundExtensionMessageHandlers = {
|
||||
bgChangedPassword: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
|
||||
bgRemoveTabFromNotificationQueue: ({ sender }: BackgroundSenderParam) => void;
|
||||
bgSaveCipher: ({ message, sender }: BackgroundOnMessageHandlerParams) => void;
|
||||
bgOpenAddEditVaultItemPopout: ({
|
||||
message,
|
||||
sender,
|
||||
}: BackgroundOnMessageHandlerParams) => Promise<void>;
|
||||
bgOpenViewVaultItemPopout: ({
|
||||
message,
|
||||
sender,
|
||||
}: BackgroundOnMessageHandlerParams) => Promise<void>;
|
||||
bgOpenVault: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
|
||||
bgNeverSave: ({ sender }: BackgroundSenderParam) => Promise<void>;
|
||||
bgUnlockPopoutOpened: ({ message, sender }: BackgroundOnMessageHandlerParams) => Promise<void>;
|
||||
bgReopenUnlockPopout: ({ sender }: BackgroundSenderParam) => Promise<void>;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { mock, MockProxy } from "jest-mock-extended";
|
||||
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 { DefaultPolicyService } from "@bitwarden/common/admin-console/services/policy/default-policy.service";
|
||||
import { AccountInfo, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
@@ -53,6 +54,7 @@ describe("NotificationBackground", () => {
|
||||
let notificationBackground: NotificationBackground;
|
||||
const autofillService = mock<AutofillService>();
|
||||
const cipherService = mock<CipherService>();
|
||||
const collectionService = mock<CollectionService>();
|
||||
let activeAccountStatusMock$: BehaviorSubject<AuthenticationStatus>;
|
||||
let authService: MockProxy<AuthService>;
|
||||
const policyService = mock<DefaultPolicyService>();
|
||||
@@ -83,6 +85,7 @@ describe("NotificationBackground", () => {
|
||||
authService,
|
||||
autofillService,
|
||||
cipherService,
|
||||
collectionService,
|
||||
configService,
|
||||
domainSettingsService,
|
||||
environmentService,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// @ts-strict-ignore
|
||||
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 { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
@@ -50,6 +51,7 @@ import {
|
||||
OrganizationCategories,
|
||||
NotificationCipherData,
|
||||
} from "../content/components/cipher/types";
|
||||
import { CollectionView } from "../content/components/common-types";
|
||||
import { NotificationQueueMessageType } from "../enums/notification-queue-message-type.enum";
|
||||
import { AutofillService } from "../services/abstractions/autofill.service";
|
||||
|
||||
@@ -92,9 +94,11 @@ export default class NotificationBackground {
|
||||
bgGetEnableAddedLoginPrompt: () => this.getEnableAddedLoginPrompt(),
|
||||
bgGetExcludedDomains: () => this.getExcludedDomains(),
|
||||
bgGetFolderData: () => this.getFolderData(),
|
||||
bgGetCollectionData: ({ message }) => this.getCollectionData(message),
|
||||
bgGetOrgData: () => this.getOrgData(),
|
||||
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),
|
||||
bgRemoveTabFromNotificationQueue: ({ sender }) =>
|
||||
this.removeTabFromNotificationQueue(sender.tab),
|
||||
@@ -114,6 +118,7 @@ export default class NotificationBackground {
|
||||
private authService: AuthService,
|
||||
private autofillService: AutofillService,
|
||||
private cipherService: CipherService,
|
||||
private collectionService: CollectionService,
|
||||
private configService: ConfigService,
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private environmentService: EnvironmentService,
|
||||
@@ -789,17 +794,36 @@ export default class NotificationBackground {
|
||||
userId,
|
||||
);
|
||||
|
||||
await this.openAddEditVaultItemPopout(senderTab, { cipherId: cipherView.id });
|
||||
await this.openAddEditVaultItemPopout(senderTab, { cipherId: cipherView?.id });
|
||||
}
|
||||
|
||||
private async openVault(
|
||||
private async openAddEditVaultItem(
|
||||
message: NotificationBackgroundExtensionMessage,
|
||||
senderTab: chrome.tabs.Tab,
|
||||
) {
|
||||
if (!message.cipherId) {
|
||||
await this.openAddEditVaultItemPopout(senderTab);
|
||||
const { cipherId, organizationId } = message;
|
||||
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(
|
||||
@@ -898,6 +922,25 @@ export default class NotificationBackground {
|
||||
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> {
|
||||
const env = await firstValueFrom(this.environmentService.environment$);
|
||||
return env.getWebVaultUrl();
|
||||
@@ -924,6 +967,7 @@ export default class NotificationBackground {
|
||||
const organizations = await firstValueFrom(
|
||||
this.organizationService.organizations$(activeUserId),
|
||||
);
|
||||
|
||||
return organizations.map((org) => {
|
||||
const { id, name, productTierType } = org;
|
||||
return {
|
||||
@@ -1054,6 +1098,7 @@ export default class NotificationBackground {
|
||||
cipherView.folderId = folderId;
|
||||
cipherView.type = CipherType.Login;
|
||||
cipherView.login = loginView;
|
||||
cipherView.organizationId = null;
|
||||
|
||||
return cipherView;
|
||||
}
|
||||
|
||||
@@ -26,3 +26,9 @@ export type OrgView = {
|
||||
name: string;
|
||||
productTierType?: ProductTierType;
|
||||
};
|
||||
|
||||
export type CollectionView = {
|
||||
id: string;
|
||||
name: string;
|
||||
organizationId: string;
|
||||
};
|
||||
|
||||
@@ -3,9 +3,12 @@ import { html } from "lit";
|
||||
import { ProductTierType } from "@bitwarden/common/billing/enums";
|
||||
import { Theme } from "@bitwarden/common/platform/enums";
|
||||
|
||||
import { Option, OrgView, FolderView } from "../common-types";
|
||||
import { Business, Family, Folder, User } from "../icons";
|
||||
import { Option, OrgView, FolderView, CollectionView } from "../common-types";
|
||||
import { Business, Family, Folder, User, CollectionShared } from "../icons";
|
||||
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"] {
|
||||
switch (productTierType) {
|
||||
@@ -29,11 +32,13 @@ export type NotificationButtonRowProps = {
|
||||
text: string;
|
||||
handlePrimaryButtonClick: (args: any) => void;
|
||||
};
|
||||
collections?: CollectionView[];
|
||||
theme: Theme;
|
||||
};
|
||||
|
||||
export function NotificationButtonRow({
|
||||
folders,
|
||||
collections,
|
||||
i18n,
|
||||
organizations,
|
||||
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`
|
||||
${ButtonRow({
|
||||
theme,
|
||||
@@ -88,15 +108,27 @@ export function NotificationButtonRow({
|
||||
id: "organization",
|
||||
label: i18n.vault,
|
||||
options: organizationOptions,
|
||||
selectedSignal: selectedVaultSignal,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(folderOptions.length > 1
|
||||
...(folderOptions.length > 1 && !collectionOptions.length
|
||||
? [
|
||||
{
|
||||
id: "folder",
|
||||
label: i18n.folder,
|
||||
options: folderOptions,
|
||||
selectedSignal: selectedFolderSignal,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(collectionOptions.length > 1
|
||||
? [
|
||||
{
|
||||
id: "collection",
|
||||
label: "Collection", // @TODO localize
|
||||
options: collectionOptions,
|
||||
selectedSignal: selectedCollectionSignal,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
NotificationType,
|
||||
} from "../../../notification/abstractions/notification-bar";
|
||||
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 { NotificationBody, componentClassPrefix as notificationBodyClassPrefix } from "./body";
|
||||
@@ -25,6 +25,7 @@ export type NotificationContainerProps = NotificationBarIframeInitData & {
|
||||
handleEditOrUpdateAction: (e: Event) => void;
|
||||
} & {
|
||||
ciphers?: NotificationCipherData[];
|
||||
collections?: CollectionView[];
|
||||
folders?: FolderView[];
|
||||
i18n: { [key: string]: string };
|
||||
organizations?: OrgView[];
|
||||
@@ -36,6 +37,7 @@ export function NotificationContainer({
|
||||
handleEditOrUpdateAction,
|
||||
handleSaveAction,
|
||||
ciphers,
|
||||
collections,
|
||||
folders,
|
||||
i18n,
|
||||
organizations,
|
||||
@@ -64,6 +66,7 @@ export function NotificationContainer({
|
||||
: null}
|
||||
${NotificationFooter({
|
||||
handleSaveAction,
|
||||
collections,
|
||||
folders,
|
||||
i18n,
|
||||
notificationType: type,
|
||||
|
||||
@@ -7,12 +7,13 @@ import {
|
||||
NotificationType,
|
||||
NotificationTypes,
|
||||
} 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 { NotificationButtonRow } from "./button-row";
|
||||
|
||||
export type NotificationFooterProps = {
|
||||
collections?: CollectionView[];
|
||||
folders?: FolderView[];
|
||||
i18n: { [key: string]: string };
|
||||
notificationType?: NotificationType;
|
||||
@@ -22,6 +23,7 @@ export type NotificationFooterProps = {
|
||||
};
|
||||
|
||||
export function NotificationFooter({
|
||||
collections,
|
||||
folders,
|
||||
i18n,
|
||||
notificationType,
|
||||
@@ -36,6 +38,7 @@ export function NotificationFooter({
|
||||
<div class=${notificationFooterStyles({ theme })}>
|
||||
${!isChangeNotification
|
||||
? NotificationButtonRow({
|
||||
collections,
|
||||
folders,
|
||||
organizations,
|
||||
i18n,
|
||||
|
||||
@@ -32,6 +32,9 @@ export class OptionSelection extends LitElement {
|
||||
@property({ type: (selectedOption: Option["value"]) => selectedOption })
|
||||
handleSelectionUpdate?: (args: any) => void;
|
||||
|
||||
@property({ attribute: false })
|
||||
selectedSignal?: { set: (value: any) => void };
|
||||
|
||||
@state()
|
||||
private showMenu = false;
|
||||
|
||||
@@ -77,7 +80,7 @@ export class OptionSelection extends LitElement {
|
||||
private handleOptionSelection = (selectedOption: Option) => {
|
||||
this.showMenu = false;
|
||||
this.selection = selectedOption;
|
||||
|
||||
this.selectedSignal?.set(selectedOption.value);
|
||||
// Any side-effects that should occur from the selection
|
||||
this.handleSelectionUpdate?.(selectedOption.value);
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ export type ButtonRowProps = {
|
||||
label?: string;
|
||||
options: Option[];
|
||||
handleSelectionUpdate?: (args: any) => void;
|
||||
selectedSignal?: { set: (value: any) => void };
|
||||
}[];
|
||||
};
|
||||
|
||||
@@ -32,7 +33,7 @@ export function ButtonRow({ theme, primaryButton, selectButtons }: ButtonRowProp
|
||||
})}
|
||||
<div class=${optionSelectionsStyles}>
|
||||
${selectButtons?.map(
|
||||
({ id, label, options, handleSelectionUpdate }) =>
|
||||
({ id, label, options, handleSelectionUpdate, selectedSignal }) =>
|
||||
html`
|
||||
<option-selection
|
||||
key=${id}
|
||||
@@ -40,6 +41,7 @@ export function ButtonRow({ theme, primaryButton, selectButtons }: ButtonRowProp
|
||||
.label=${label}
|
||||
.options=${options}
|
||||
.handleSelectionUpdate=${handleSelectionUpdate}
|
||||
.selectedSignal=${selectedSignal}
|
||||
></option-selection>
|
||||
` || nothing,
|
||||
)}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
import { signal } from "@lit-labs/signals";
|
||||
|
||||
export const selectedCollection = signal<string>("0");
|
||||
@@ -0,0 +1,3 @@
|
||||
import { signal } from "@lit-labs/signals";
|
||||
|
||||
export const selectedFolder = signal<string>("0");
|
||||
@@ -0,0 +1,3 @@
|
||||
import { signal } from "@lit-labs/signals";
|
||||
|
||||
export const selectedVault = signal<string>("0");
|
||||
@@ -1,7 +1,11 @@
|
||||
import { Theme } from "@bitwarden/common/platform/enums";
|
||||
|
||||
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 = {
|
||||
Add: "add",
|
||||
@@ -19,6 +23,7 @@ type NotificationTaskInfo = {
|
||||
type NotificationBarIframeInitData = {
|
||||
ciphers?: NotificationCipherData[];
|
||||
folders?: FolderView[];
|
||||
collections?: CollectionView[];
|
||||
importType?: string;
|
||||
isVaultLocked?: boolean;
|
||||
launchTimestamp?: number;
|
||||
|
||||
@@ -6,9 +6,11 @@ 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 { CollectionView, OrgView } from "../content/components/common-types";
|
||||
import { NotificationConfirmationContainer } from "../content/components/notification/confirmation/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 { circleCheckIcon } from "../utils/svg-icons";
|
||||
|
||||
@@ -41,6 +43,7 @@ function load() {
|
||||
applyNotificationBarStyle();
|
||||
});
|
||||
}
|
||||
|
||||
function applyNotificationBarStyle() {
|
||||
if (!useComponentBar) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
||||
@@ -140,7 +143,7 @@ async function initNotificationBar(message: NotificationBarWindowMessage) {
|
||||
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());
|
||||
|
||||
const orgId = selectedVaultSignal.get();
|
||||
await Promise.all([
|
||||
new Promise<OrgView[]>((resolve) =>
|
||||
sendPlatformMessage({ command: "bgGetOrgData" }, resolve),
|
||||
@@ -151,13 +154,18 @@ async function initNotificationBar(message: NotificationBarWindowMessage) {
|
||||
new Promise<NotificationCipherData[]>((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,
|
||||
organizations,
|
||||
folders,
|
||||
ciphers,
|
||||
organizations,
|
||||
collections,
|
||||
};
|
||||
|
||||
// @TODO use context to avoid prop drilling
|
||||
return render(
|
||||
NotificationContainer({
|
||||
@@ -254,9 +262,18 @@ function handleCloseNotification(e: Event) {
|
||||
}
|
||||
|
||||
function handleSaveAction(e: Event) {
|
||||
const selectedVault = selectedVaultSignal.get();
|
||||
if (selectedVault.length > 1) {
|
||||
openAddEditVaultItemPopout(e, { organizationId: selectedVault });
|
||||
handleCloseNotification(e);
|
||||
return;
|
||||
}
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
sendSaveCipherMessage(removeIndividualVault());
|
||||
const selectedFolder = selectedFolderSignal.get();
|
||||
|
||||
sendSaveCipherMessage(removeIndividualVault(), selectedFolder);
|
||||
if (removeIndividualVault()) {
|
||||
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) {
|
||||
sendPlatformMessage({
|
||||
command: "bgOpenViewVaultItemPopout",
|
||||
|
||||
@@ -1206,6 +1206,7 @@ export default class MainBackground {
|
||||
this.authService,
|
||||
this.autofillService,
|
||||
this.cipherService,
|
||||
this.collectionService,
|
||||
this.configService,
|
||||
this.domainSettingsService,
|
||||
this.environmentService,
|
||||
|
||||
Reference in New Issue
Block a user