From 276d9b9a9bf0b6f3edd5f50832f94f4050c4b3f9 Mon Sep 17 00:00:00 2001 From: Daniel Riera Date: Thu, 6 Feb 2025 11:54:09 -0500 Subject: [PATCH] [PM-17487] Load cipher data into new notification experience (#13185) * PM-17487 - Initial structure - Implement new getNotificationCipherData function in background - Update stories to match new naming - Edit types to be relevant to current functionality * update jsdoc * export types so eslint does not treat them as unused * -Fix types -Promise.all on background * clean comments --- .../background/notification.background.ts | 37 +++++++++++++++++++ .../content/components/cipher/cipher-info.ts | 4 +- .../content/components/cipher/cipher-item.ts | 4 +- .../content/components/cipher/types.ts | 21 +++++++---- .../notification/body.lit-stories.ts | 6 +-- .../content/components/notification/body.ts | 4 +- .../components/notification/container.ts | 15 ++------ apps/browser/src/autofill/notification/bar.ts | 26 +++++++------ 8 files changed, 76 insertions(+), 41 deletions(-) diff --git a/apps/browser/src/autofill/background/notification.background.ts b/apps/browser/src/autofill/background/notification.background.ts index ad3bee97d8a..a091256b28d 100644 --- a/apps/browser/src/autofill/background/notification.background.ts +++ b/apps/browser/src/autofill/background/notification.background.ts @@ -25,6 +25,7 @@ import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-stat import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherType } from "@bitwarden/common/vault/enums"; +import { buildCipherIcon } from "@bitwarden/common/vault/icon/build-cipher-icon"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view"; import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; @@ -32,6 +33,7 @@ import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; import { openUnlockPopout } from "../../auth/popup/utils/auth-popout-window"; import { BrowserApi } from "../../platform/browser/browser-api"; import { openAddEditVaultItemPopout } from "../../vault/popup/utils/vault-popout-window"; +import { NotificationCipherData } from "../content/components/cipher/types"; import { NotificationQueueMessageType } from "../enums/notification-queue-message-type.enum"; import { AutofillService } from "../services/abstractions/autofill.service"; @@ -82,6 +84,7 @@ export default class NotificationBackground { bgGetActiveUserServerConfig: () => this.getActiveUserServerConfig(), getWebVaultUrlForNotification: () => this.getWebVaultUrl(), notificationRefreshFlagValue: () => this.getNotificationFlag(), + bgGetDecryptedCiphers: () => this.getNotificationCipherData(), }; private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); @@ -132,6 +135,40 @@ export default class NotificationBackground { return await firstValueFrom(this.domainSettingsService.neverDomains$); } + /** + * + * Gets the current active tab and retrieves all decrypted ciphers + * for the tab's URL. It constructs and returns an array of `NotificationCipherData` objects. + * If no active tab or URL is found, it returns an empty array. + * + * @returns {Promise} + */ + + async getNotificationCipherData(): Promise { + const [currentTab, showFavicons, env] = await Promise.all([ + BrowserApi.getTabFromCurrentWindow(), + firstValueFrom(this.domainSettingsService.showFavicons$), + firstValueFrom(this.environmentService.environment$), + ]); + const iconsServerUrl = env.getIconsUrl(); + const decryptedCiphers = await this.cipherService.getAllDecryptedForUrl(currentTab.url); + + return decryptedCiphers.map((view) => { + const { id, name, reprompt, favorite, login } = view; + return { + id, + name, + type: CipherType.Login, + reprompt, + favorite, + icon: buildCipherIcon(iconsServerUrl, view, showFavicons), + login: login && { + username: login.username, + }, + }; + }); + } + /** * Gets the active user server config from the config service. */ diff --git a/apps/browser/src/autofill/content/components/cipher/cipher-info.ts b/apps/browser/src/autofill/content/components/cipher/cipher-info.ts index de374b44a97..6ff32353938 100644 --- a/apps/browser/src/autofill/content/components/cipher/cipher-info.ts +++ b/apps/browser/src/autofill/content/components/cipher/cipher-info.ts @@ -6,10 +6,10 @@ import { Theme } from "@bitwarden/common/platform/enums"; import { themes, typography } from "../../../content/components/constants/styles"; import { CipherInfoIndicatorIcons } from "./cipher-indicator-icons"; -import { CipherData } from "./types"; +import { NotificationCipherData } from "./types"; // @TODO support other cipher types (card, identity, notes, etc) -export function CipherInfo({ cipher, theme }: { cipher: CipherData; theme: Theme }) { +export function CipherInfo({ cipher, theme }: { cipher: NotificationCipherData; theme: Theme }) { const { name, login } = cipher; return html` diff --git a/apps/browser/src/autofill/content/components/cipher/cipher-item.ts b/apps/browser/src/autofill/content/components/cipher/cipher-item.ts index 651c20cac3a..96b44d2c0cc 100644 --- a/apps/browser/src/autofill/content/components/cipher/cipher-item.ts +++ b/apps/browser/src/autofill/content/components/cipher/cipher-item.ts @@ -12,7 +12,7 @@ import { import { CipherAction } from "./cipher-action"; import { CipherIcon } from "./cipher-icon"; import { CipherInfo } from "./cipher-info"; -import { CipherData } from "./types"; +import { NotificationCipherData } from "./types"; const cipherIconWidth = "24px"; @@ -22,7 +22,7 @@ export function CipherItem({ notificationType, theme = ThemeTypes.Light, }: { - cipher: CipherData; + cipher: NotificationCipherData; handleAction?: (e: Event) => void; notificationType?: NotificationType; theme: Theme; diff --git a/apps/browser/src/autofill/content/components/cipher/types.ts b/apps/browser/src/autofill/content/components/cipher/types.ts index acdee756570..ff29f9b559f 100644 --- a/apps/browser/src/autofill/content/components/cipher/types.ts +++ b/apps/browser/src/autofill/content/components/cipher/types.ts @@ -1,6 +1,4 @@ -// FIXME: Remove when updating file. Eslint update -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const CipherTypes = { +export const CipherTypes = { Login: 1, SecureNote: 2, Card: 3, @@ -9,9 +7,7 @@ const CipherTypes = { type CipherType = (typeof CipherTypes)[keyof typeof CipherTypes]; -// FIXME: Remove when updating file. Eslint update -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const CipherRepromptTypes = { +export const CipherRepromptTypes = { None: 0, Password: 1, } as const; @@ -25,13 +21,16 @@ export type WebsiteIconData = { icon: string; }; -export type CipherData = { +type BaseCipherData = { id: string; name: string; - type: CipherType; + type: CipherTypeValue; reprompt: CipherRepromptType; favorite: boolean; icon: WebsiteIconData; +}; + +export type CipherData = BaseCipherData & { accountCreationFieldType?: string; login?: { username: string; @@ -46,3 +45,9 @@ export type CipherData = { username?: string; }; }; + +export type NotificationCipherData = BaseCipherData & { + login?: { + username: string; + }; +}; diff --git a/apps/browser/src/autofill/content/components/lit-stories/notification/body.lit-stories.ts b/apps/browser/src/autofill/content/components/lit-stories/notification/body.lit-stories.ts index 90616647b0e..c4b32e8b0f1 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/notification/body.lit-stories.ts +++ b/apps/browser/src/autofill/content/components/lit-stories/notification/body.lit-stories.ts @@ -5,11 +5,11 @@ import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; import { NotificationType } from "../../../../notification/abstractions/notification-bar"; -import { CipherData } from "../../cipher/types"; +import { NotificationCipherData } from "../../cipher/types"; import { NotificationBody } from "../../notification/body"; type Args = { - ciphers: CipherData[]; + ciphers: NotificationCipherData[]; notificationType: NotificationType; theme: Theme; }; @@ -38,7 +38,7 @@ export default { fallbackImage: "https://example.com/fallback.png", icon: "icon-class", }, - login: { username: "user@example.com", passkey: null }, + login: { username: "user@example.com" }, }, ], theme: ThemeTypes.Light, diff --git a/apps/browser/src/autofill/content/components/notification/body.ts b/apps/browser/src/autofill/content/components/notification/body.ts index 6a3ed2e5d1e..6dc957ab8b8 100644 --- a/apps/browser/src/autofill/content/components/notification/body.ts +++ b/apps/browser/src/autofill/content/components/notification/body.ts @@ -5,7 +5,7 @@ import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums"; import { NotificationType } from "../../../notification/abstractions/notification-bar"; import { CipherItem } from "../cipher"; -import { CipherData } from "../cipher/types"; +import { NotificationCipherData } from "../cipher/types"; import { scrollbarStyles, spacing, themes, typography } from "../constants/styles"; import { ItemRow } from "../rows/item-row"; @@ -20,7 +20,7 @@ export function NotificationBody({ notificationType, theme = ThemeTypes.Light, }: { - ciphers: CipherData[]; + ciphers: NotificationCipherData[]; customClasses?: string[]; notificationType?: NotificationType; theme: Theme; diff --git a/apps/browser/src/autofill/content/components/notification/container.ts b/apps/browser/src/autofill/content/components/notification/container.ts index 8bd07ab8296..8d1b57c80cd 100644 --- a/apps/browser/src/autofill/content/components/notification/container.ts +++ b/apps/browser/src/autofill/content/components/notification/container.ts @@ -8,8 +8,7 @@ import { NotificationTypes, NotificationType, } from "../../../notification/abstractions/notification-bar"; -import { createAutofillOverlayCipherDataMock } from "../../../spec/autofill-mocks"; -import { CipherData } from "../cipher/types"; +import { NotificationCipherData } from "../cipher/types"; import { themes, spacing } from "../constants/styles"; import { NotificationBody, componentClassPrefix as notificationBodyClassPrefix } from "./body"; @@ -24,23 +23,15 @@ export function NotificationContainer({ i18n, theme = ThemeTypes.Light, type, + ciphers, }: NotificationBarIframeInitData & { handleCloseNotification: (e: Event) => void } & { i18n: { [key: string]: string }; type: NotificationType; // @TODO typing override for generic `NotificationBarIframeInitData.type` + ciphers: NotificationCipherData[]; }) { const headerMessage = getHeaderMessage(i18n, type); const showBody = true; - // @TODO remove mock ciphers for development - const ciphers = [ - createAutofillOverlayCipherDataMock(1), - { ...createAutofillOverlayCipherDataMock(2), icon: { imageEnabled: false } }, - { - ...createAutofillOverlayCipherDataMock(3), - icon: { imageEnabled: true, image: "https://localhost:8443/icons/webtests.dev/icon.png" }, - }, - ] as CipherData[]; - return html`
${NotificationHeader({ diff --git a/apps/browser/src/autofill/notification/bar.ts b/apps/browser/src/autofill/notification/bar.ts index 202b144258d..2316df19857 100644 --- a/apps/browser/src/autofill/notification/bar.ts +++ b/apps/browser/src/autofill/notification/bar.ts @@ -84,23 +84,25 @@ 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 themeType = getTheme(globalThis, theme); // There are other possible passed theme values, but for now, resolve to dark or light const resolvedTheme: Theme = themeType === ThemeTypes.Dark ? ThemeTypes.Dark : ThemeTypes.Light; - // @TODO use context to avoid prop drilling - return render( - NotificationContainer({ - ...notificationBarIframeInitData, - type: notificationBarIframeInitData.type as NotificationType, - theme: resolvedTheme, - handleCloseNotification, - i18n, - }), - document.body, - ); + sendPlatformMessage({ command: "bgGetDecryptedCiphers" }, (cipherData) => { + // @TODO use context to avoid prop drilling + return render( + NotificationContainer({ + ...notificationBarIframeInitData, + type: notificationBarIframeInitData.type as NotificationType, + theme: resolvedTheme, + handleCloseNotification, + i18n, + ciphers: cipherData, + }), + document.body, + ); + }); } setNotificationBarTheme();