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

PM-20106 Pass indicator data to notification bar cipher items (#14246)

* PM-20106 initial approach whihc preserves exisiting indicator file style

* refactored approach to be able to pass any icon when or if needed in the future

* address feedback
This commit is contained in:
Daniel Riera
2025-04-15 17:19:58 -04:00
committed by GitHub
parent b66430b25c
commit a61d878081
4 changed files with 70 additions and 21 deletions

View File

@@ -16,6 +16,7 @@ import {
} from "@bitwarden/common/autofill/constants"; } from "@bitwarden/common/autofill/constants";
import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service"; import { DomainSettingsService } from "@bitwarden/common/autofill/services/domain-settings.service";
import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service"; import { UserNotificationSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/user-notification-settings.service";
import { ProductTierType } from "@bitwarden/common/billing/enums/product-tier-type.enum";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { NeverDomains } from "@bitwarden/common/models/domain/domain-service"; import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
@@ -41,7 +42,11 @@ import { SecurityTask } from "@bitwarden/common/vault/tasks/models/security-task
import { openUnlockPopout } from "../../auth/popup/utils/auth-popout-window"; import { openUnlockPopout } from "../../auth/popup/utils/auth-popout-window";
import { BrowserApi } from "../../platform/browser/browser-api"; import { BrowserApi } from "../../platform/browser/browser-api";
import { openAddEditVaultItemPopout } from "../../vault/popup/utils/vault-popout-window"; import { openAddEditVaultItemPopout } from "../../vault/popup/utils/vault-popout-window";
import { NotificationCipherData } from "../content/components/cipher/types"; import {
OrganizationCategory,
OrganizationCategories,
NotificationCipherData,
} from "../content/components/cipher/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";
@@ -174,8 +179,29 @@ export default class NotificationBackground {
activeUserId, activeUserId,
); );
const organizations = await firstValueFrom(
this.organizationService.organizations$(activeUserId),
);
return decryptedCiphers.map((view) => { return decryptedCiphers.map((view) => {
const { id, name, reprompt, favorite, login } = view; const { id, name, reprompt, favorite, login, organizationId } = view;
const organizationType = organizationId
? organizations.find((org) => org.id === organizationId)?.productTierType
: null;
const organizationCategories: OrganizationCategory[] = [];
if (
[ProductTierType.Teams, ProductTierType.Enterprise, ProductTierType.TeamsStarter].includes(
organizationType,
)
) {
organizationCategories.push(OrganizationCategories.business);
}
if ([ProductTierType.Families, ProductTierType.Free].includes(organizationType)) {
organizationCategories.push(OrganizationCategories.family);
}
return { return {
id, id,
@@ -183,6 +209,7 @@ export default class NotificationBackground {
type: CipherType.Login, type: CipherType.Login,
reprompt, reprompt,
favorite, favorite,
...(organizationCategories.length ? { organizationCategories } : {}),
icon: buildCipherIcon(iconsServerUrl, view, showFavicons), icon: buildCipherIcon(iconsServerUrl, view, showFavicons),
login: login && { login: login && {
username: login.username, username: login.username,

View File

@@ -1,30 +1,35 @@
import { css } from "@emotion/css"; import { css } from "@emotion/css";
import { html } from "lit"; import { html, TemplateResult } from "lit";
import { Theme } from "@bitwarden/common/platform/enums"; import { Theme } from "@bitwarden/common/platform/enums";
import { themes } from "../../../content/components/constants/styles"; import { themes } from "../../../content/components/constants/styles";
import { Business, Users } from "../../../content/components/icons"; import { Business, Users } from "../../../content/components/icons";
// @TODO connect data source to icon checks import { OrganizationCategories, OrganizationCategory } from "./types";
// @TODO support other indicator types (attachments, etc)
const cipherIndicatorIconsMap: Record<
OrganizationCategory,
(args: { color: string; theme: Theme }) => TemplateResult
> = {
[OrganizationCategories.business]: Business,
[OrganizationCategories.family]: Users,
};
export function CipherInfoIndicatorIcons({ export function CipherInfoIndicatorIcons({
showBusinessIcon, organizationCategories = [],
showFamilyIcon,
theme, theme,
}: { }: {
showBusinessIcon?: boolean; organizationCategories?: OrganizationCategory[];
showFamilyIcon?: boolean;
theme: Theme; theme: Theme;
}) { }) {
const indicatorIcons = [ return html`
...(showBusinessIcon ? [Business({ color: themes[theme].text.muted, theme })] : []), <span class=${cipherInfoIndicatorIconsStyles}>
...(showFamilyIcon ? [Users({ color: themes[theme].text.muted, theme })] : []), ${organizationCategories.map((name) =>
]; cipherIndicatorIconsMap[name]?.({ color: themes[theme].text.muted, theme }),
)}
return indicatorIcons.length </span>
? html` <span class=${cipherInfoIndicatorIconsStyles}> ${indicatorIcons} </span> ` `;
: null; // @TODO null case should be handled by parent
} }
const cipherInfoIndicatorIconsStyles = css` const cipherInfoIndicatorIconsStyles = css`

View File

@@ -1,5 +1,5 @@
import { css } from "@emotion/css"; import { css } from "@emotion/css";
import { html } from "lit"; import { html, nothing } from "lit";
import { Theme } from "@bitwarden/common/platform/enums"; import { Theme } from "@bitwarden/common/platform/enums";
@@ -8,14 +8,22 @@ import { themes, typography } from "../../../content/components/constants/styles
import { CipherInfoIndicatorIcons } from "./cipher-indicator-icons"; import { CipherInfoIndicatorIcons } from "./cipher-indicator-icons";
import { NotificationCipherData } from "./types"; import { NotificationCipherData } from "./types";
// @TODO support other cipher types (card, identity, notes, etc)
export function CipherInfo({ cipher, theme }: { cipher: NotificationCipherData; theme: Theme }) { export function CipherInfo({ cipher, theme }: { cipher: NotificationCipherData; theme: Theme }) {
const { name, login } = cipher; const { name, login, organizationCategories } = cipher;
const hasIndicatorIcons = organizationCategories?.length;
return html` return html`
<div> <div>
<span class=${cipherInfoPrimaryTextStyles(theme)}> <span class=${cipherInfoPrimaryTextStyles(theme)}>
${[name, CipherInfoIndicatorIcons({ theme })]} ${[
name,
hasIndicatorIcons
? CipherInfoIndicatorIcons({
theme,
organizationCategories,
})
: nothing,
]}
</span> </span>
${login?.username ${login?.username

View File

@@ -14,6 +14,14 @@ export const CipherRepromptTypes = {
type CipherRepromptType = (typeof CipherRepromptTypes)[keyof typeof CipherRepromptTypes]; type CipherRepromptType = (typeof CipherRepromptTypes)[keyof typeof CipherRepromptTypes];
export type OrganizationCategory =
(typeof OrganizationCategories)[keyof typeof OrganizationCategories];
export const OrganizationCategories = {
business: "business",
family: "family",
} as const;
export type WebsiteIconData = { export type WebsiteIconData = {
imageEnabled: boolean; imageEnabled: boolean;
image: string; image: string;
@@ -50,4 +58,5 @@ export type NotificationCipherData = BaseCipherData<typeof CipherTypes.Login> &
login?: { login?: {
username: string; username: string;
}; };
organizationCategories?: OrganizationCategory[];
}; };