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:
@@ -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>;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,3 +26,9 @@ export type OrgView = {
|
|||||||
name: string;
|
name: string;
|
||||||
productTierType?: ProductTierType;
|
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 { 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,
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
: []),
|
: []),
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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,
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -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 { 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;
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user