1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 06:13:38 +00:00
- Initial structure
- Implement flag where needed
- Apply dynamic styling
This commit is contained in:
Daniel Riera
2025-01-29 11:17:50 -05:00
committed by GitHub
parent e73cb3e3ff
commit d2fec919b3
3 changed files with 111 additions and 28 deletions

View File

@@ -14,6 +14,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 { 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";
import { ServerConfig } from "@bitwarden/common/platform/abstractions/config/server-config"; import { ServerConfig } from "@bitwarden/common/platform/abstractions/config/server-config";
@@ -80,6 +81,7 @@ export default class NotificationBackground {
bgGetExcludedDomains: () => this.getExcludedDomains(), bgGetExcludedDomains: () => this.getExcludedDomains(),
bgGetActiveUserServerConfig: () => this.getActiveUserServerConfig(), bgGetActiveUserServerConfig: () => this.getActiveUserServerConfig(),
getWebVaultUrlForNotification: () => this.getWebVaultUrl(), getWebVaultUrlForNotification: () => this.getWebVaultUrl(),
notificationRefreshFlagValue: () => this.getNotificationFlag(),
}; };
private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id)); private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id));
@@ -137,6 +139,15 @@ export default class NotificationBackground {
return await firstValueFrom(this.configService.serverConfig$); return await firstValueFrom(this.configService.serverConfig$);
} }
/**
* Gets the current value of the notification refresh feature flag
* @returns Promise<boolean> indicating if the feature is enabled
*/
async getNotificationFlag(): Promise<boolean> {
const flagValue = await this.configService.getFeatureFlag(FeatureFlag.NotificationRefresh);
return flagValue;
}
private async getAuthStatus() { private async getAuthStatus() {
return await firstValueFrom(this.authService.activeAccountStatus$); return await firstValueFrom(this.authService.activeAccountStatus$);
} }

View File

@@ -852,10 +852,10 @@ async function loadNotificationBar() {
} }
closeBar(false); closeBar(false);
openBar(type, notificationBarUrl, notificationBarInitData); void openBar(type, notificationBarUrl, notificationBarInitData);
} }
function openBar( async function openBar(
type: string, type: string,
barPage: string, barPage: string,
notificationBarInitData: NotificationBarIframeInitData, notificationBarInitData: NotificationBarIframeInitData,
@@ -865,22 +865,38 @@ async function loadNotificationBar() {
if (document.body == null) { if (document.body == null) {
return; return;
} }
const useComponentBar: boolean = await sendExtensionMessage("notificationRefreshFlagValue");
setupInitNotificationBarMessageListener(notificationBarInitData); setupInitNotificationBarMessageListener(notificationBarInitData);
const barPageUrl: string = chrome.runtime.getURL(barPage); const barPageUrl: string = chrome.runtime.getURL(barPage);
function getIframeStyle(useComponentBar: boolean): string {
return (
(useComponentBar
? "height: calc(276px + 25px); width: 450px; right: 0;"
: "height: 42px; width: 100%;") + " border: 0; min-height: initial;"
);
}
notificationBarIframe = document.createElement("iframe"); notificationBarIframe = document.createElement("iframe");
notificationBarIframe.style.cssText = notificationBarIframe.style.cssText = getIframeStyle(useComponentBar);
"height: 42px; width: 100%; border: 0; min-height: initial;";
notificationBarIframe.id = "bit-notification-bar-iframe"; notificationBarIframe.id = "bit-notification-bar-iframe";
notificationBarIframe.src = barPageUrl; notificationBarIframe.src = barPageUrl;
function getFrameStyle(useComponentBar: boolean): string {
return (
(useComponentBar
? "height: calc(276px + 25px); width: 450px; right: 0;"
: "height: 42px; width: 100%; left: 0;") +
" top: 0; padding: 0; position: fixed;" +
" z-index: 2147483647; visibility: visible;"
);
}
const frameDiv = document.createElement("div"); const frameDiv = document.createElement("div");
frameDiv.setAttribute("aria-live", "polite"); frameDiv.setAttribute("aria-live", "polite");
frameDiv.id = "bit-notification-bar"; frameDiv.id = "bit-notification-bar";
frameDiv.style.cssText = frameDiv.style.cssText = getFrameStyle(useComponentBar);
"height: 42px; width: 100%; top: 0; left: 0; padding: 0; position: fixed; " +
"z-index: 2147483647; visibility: visible;";
frameDiv.appendChild(notificationBarIframe); frameDiv.appendChild(notificationBarIframe);
document.body.appendChild(frameDiv); document.body.appendChild(frameDiv);

View File

@@ -1,10 +1,13 @@
// FIXME: Update this file to be type safe and remove this and next line // FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore // @ts-strict-ignore
import { ThemeTypes } from "@bitwarden/common/platform/enums"; import { render } from "lit";
import { Theme, ThemeTypes } from "@bitwarden/common/platform/enums";
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
import type { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; 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 { NotificationContainer } from "../content/components/notification/container";
import { buildSvgDomElement } from "../utils"; import { buildSvgDomElement } from "../utils";
import { circleCheckIcon } from "../utils/svg-icons"; import { circleCheckIcon } from "../utils/svg-icons";
@@ -12,15 +15,13 @@ import {
NotificationBarWindowMessageHandlers, NotificationBarWindowMessageHandlers,
NotificationBarWindowMessage, NotificationBarWindowMessage,
NotificationBarIframeInitData, NotificationBarIframeInitData,
NotificationType,
} from "./abstractions/notification-bar"; } from "./abstractions/notification-bar";
// FIXME: Remove when updating file. Eslint update
// eslint-disable-next-line @typescript-eslint/no-require-imports
require("./bar.scss");
const logService = new ConsoleLogService(false); const logService = new ConsoleLogService(false);
let notificationBarIframeInitData: NotificationBarIframeInitData = {}; let notificationBarIframeInitData: NotificationBarIframeInitData = {};
let windowMessageOrigin: string; let windowMessageOrigin: string;
let useComponentBar = false;
const notificationBarWindowMessageHandlers: NotificationBarWindowMessageHandlers = { const notificationBarWindowMessageHandlers: NotificationBarWindowMessageHandlers = {
initNotificationBar: ({ message }) => initNotificationBar(message), initNotificationBar: ({ message }) => initNotificationBar(message),
saveCipherAttemptCompleted: ({ message }) => handleSaveCipherAttemptCompletedMessage(message), saveCipherAttemptCompleted: ({ message }) => handleSaveCipherAttemptCompletedMessage(message),
@@ -29,6 +30,17 @@ const notificationBarWindowMessageHandlers: NotificationBarWindowMessageHandlers
globalThis.addEventListener("load", load); globalThis.addEventListener("load", load);
function load() { function load() {
setupWindowMessageListener(); setupWindowMessageListener();
sendPlatformMessage({ command: "notificationRefreshFlagValue" }, (flagValue) => {
useComponentBar = flagValue;
applyNotificationBarStyle();
});
}
function applyNotificationBarStyle() {
if (!useComponentBar) {
// eslint-disable-next-line @typescript-eslint/no-require-imports
require("./bar.scss");
}
postMessageToParent({ command: "initNotificationBar" }); postMessageToParent({ command: "initNotificationBar" });
} }
@@ -39,12 +51,7 @@ function initNotificationBar(message: NotificationBarWindowMessage) {
} }
notificationBarIframeInitData = initData; notificationBarIframeInitData = initData;
const { isVaultLocked } = notificationBarIframeInitData; const { isVaultLocked, theme } = notificationBarIframeInitData;
setNotificationBarTheme();
(document.getElementById("logo") as HTMLImageElement).src = isVaultLocked
? chrome.runtime.getURL("images/icon38_locked.png")
: chrome.runtime.getURL("images/icon38.png");
const i18n = { const i18n = {
appName: chrome.i18n.getMessage("appName"), appName: chrome.i18n.getMessage("appName"),
@@ -58,8 +65,50 @@ function initNotificationBar(message: NotificationBarWindowMessage) {
notificationChangeDesc: chrome.i18n.getMessage("notificationChangeDesc"), notificationChangeDesc: chrome.i18n.getMessage("notificationChangeDesc"),
notificationUnlock: chrome.i18n.getMessage("notificationUnlock"), notificationUnlock: chrome.i18n.getMessage("notificationUnlock"),
notificationUnlockDesc: chrome.i18n.getMessage("notificationUnlockDesc"), notificationUnlockDesc: chrome.i18n.getMessage("notificationUnlockDesc"),
// @TODO move values to message catalog
saveAction: "Save",
saveAsNewLoginAction: "Save as new login",
updateLoginAction: "Update login",
saveLoginPrompt: "Save login?",
updateLoginPrompt: "Update existing login?",
loginSaveSuccess: "Login saved",
loginSaveSuccessDetails: "Login saved to Bitwarden.",
loginUpdateSuccess: "Login saved",
loginUpdateSuccessDetails: "Login updated in Bitwarden.",
saveFailure: "Error saving",
saveFailureDetails: "Oh no! We couldn't save this. Try entering the details as a New item",
}; };
if (useComponentBar) {
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,
);
}
setNotificationBarTheme();
(document.getElementById("logo") as HTMLImageElement).src = isVaultLocked
? chrome.runtime.getURL("images/icon38_locked.png")
: chrome.runtime.getURL("images/icon38.png");
setupLogoLink(i18n); setupLogoLink(i18n);
// i18n for "Add" template // i18n for "Add" template
@@ -106,7 +155,7 @@ function initNotificationBar(message: NotificationBarWindowMessage) {
closeButton.title = i18n.close; closeButton.title = i18n.close;
const notificationType = initData.type; const notificationType = initData.type;
if (initData.type === "add") { if (notificationType === "add") {
handleTypeAdd(); handleTypeAdd();
} else if (notificationType === "change") { } else if (notificationType === "change") {
handleTypeChange(); handleTypeChange();
@@ -114,15 +163,17 @@ function initNotificationBar(message: NotificationBarWindowMessage) {
handleTypeUnlock(); handleTypeUnlock();
} }
closeButton.addEventListener("click", (e) => { closeButton.addEventListener("click", handleCloseNotification);
globalThis.addEventListener("resize", adjustHeight);
adjustHeight();
}
function handleCloseNotification(e: Event) {
e.preventDefault(); e.preventDefault();
sendPlatformMessage({ sendPlatformMessage({
command: "bgCloseNotificationBar", command: "bgCloseNotificationBar",
}); });
});
globalThis.addEventListener("resize", adjustHeight);
adjustHeight();
} }
function handleTypeAdd() { function handleTypeAdd() {
@@ -317,14 +368,19 @@ function setupLogoLink(i18n: Record<string, string>) {
sendPlatformMessage({ command: "getWebVaultUrlForNotification" }, setWebVaultUrlLink); sendPlatformMessage({ command: "getWebVaultUrlForNotification" }, setWebVaultUrlLink);
} }
function setNotificationBarTheme() { function getTheme(globalThis: any, theme: NotificationBarIframeInitData["theme"]) {
let theme = notificationBarIframeInitData.theme;
if (theme === ThemeTypes.System) { if (theme === ThemeTypes.System) {
theme = globalThis.matchMedia("(prefers-color-scheme: dark)").matches return globalThis.matchMedia("(prefers-color-scheme: dark)").matches
? ThemeTypes.Dark ? ThemeTypes.Dark
: ThemeTypes.Light; : ThemeTypes.Light;
} }
return theme;
}
function setNotificationBarTheme() {
const theme = getTheme(globalThis, notificationBarIframeInitData.theme);
document.documentElement.classList.add(`theme_${theme}`); document.documentElement.classList.add(`theme_${theme}`);
if (notificationBarIframeInitData.applyRedesign) { if (notificationBarIframeInitData.applyRedesign) {