mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 06:13:38 +00:00
PM-14355 (#13110)
- Initial structure - Implement flag where needed - Apply dynamic styling
This commit is contained in:
@@ -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$);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user