1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-24 04:04:24 +00:00

[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
This commit is contained in:
Daniel Riera
2025-02-06 11:54:09 -05:00
committed by GitHub
parent 28d9202edb
commit 276d9b9a9b
8 changed files with 76 additions and 41 deletions

View File

@@ -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<NotificationCipherData[]>}
*/
async getNotificationCipherData(): Promise<NotificationCipherData[]> {
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.
*/

View File

@@ -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`

View File

@@ -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;

View File

@@ -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<CipherTypeValue> = {
id: string;
name: string;
type: CipherType;
type: CipherTypeValue;
reprompt: CipherRepromptType;
favorite: boolean;
icon: WebsiteIconData;
};
export type CipherData = BaseCipherData<CipherType> & {
accountCreationFieldType?: string;
login?: {
username: string;
@@ -46,3 +45,9 @@ export type CipherData = {
username?: string;
};
};
export type NotificationCipherData = BaseCipherData<typeof CipherTypes.Login> & {
login?: {
username: string;
};
};

View File

@@ -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,

View File

@@ -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;

View File

@@ -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`
<div class=${notificationContainerStyles(theme)}>
${NotificationHeader({

View File

@@ -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();