mirror of
https://github.com/bitwarden/browser
synced 2025-12-24 04:04:24 +00:00
[PM-5539] Migrate ThemingService (#8219)
* Update ThemingService * Finish ThemingService * Lint * More Tests & Docs * Refactor to ThemeStateService * Rename File * Fix Import * Remove `type` added to imports * Update InitServices * Fix Test * Remove Unreferenced Code * Remove Unneeded Null Check * Add Ticket Link * Add Back THEMING_DISK * Fix Desktop * Create SYSTEM_THEME_OBSERVABLE * Fix Browser Injection * Update Desktop Manual Access * Fix Default Theme * Update Test
This commit is contained in:
@@ -8,6 +8,7 @@ import { DomainSettingsService } from "@bitwarden/common/autofill/services/domai
|
||||
import { UserNotificationSettingsService } from "@bitwarden/common/autofill/services/user-notification-settings.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/services/environment.service";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { FolderView } from "@bitwarden/common/vault/models/view/folder.view";
|
||||
import { CipherService } from "@bitwarden/common/vault/services/cipher.service";
|
||||
@@ -51,6 +52,7 @@ describe("NotificationBackground", () => {
|
||||
const domainSettingsService = mock<DomainSettingsService>();
|
||||
const environmentService = mock<EnvironmentService>();
|
||||
const logService = mock<LogService>();
|
||||
const themeStateService = mock<ThemeStateService>();
|
||||
|
||||
beforeEach(() => {
|
||||
notificationBackground = new NotificationBackground(
|
||||
@@ -64,6 +66,7 @@ describe("NotificationBackground", () => {
|
||||
domainSettingsService,
|
||||
environmentService,
|
||||
logService,
|
||||
themeStateService,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { NeverDomains } from "@bitwarden/common/models/domain/domain-service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
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";
|
||||
@@ -77,6 +78,7 @@ export default class NotificationBackground {
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private environmentService: EnvironmentService,
|
||||
private logService: LogService,
|
||||
private themeStateService: ThemeStateService,
|
||||
) {}
|
||||
|
||||
async init() {
|
||||
@@ -165,7 +167,7 @@ export default class NotificationBackground {
|
||||
const notificationType = notificationQueueMessage.type;
|
||||
const typeData: Record<string, any> = {
|
||||
isVaultLocked: notificationQueueMessage.wasVaultLocked,
|
||||
theme: await this.stateService.getTheme(),
|
||||
theme: await firstValueFrom(this.themeStateService.selectedTheme$),
|
||||
};
|
||||
|
||||
switch (notificationType) {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { mock, mockReset } from "jest-mock-extended";
|
||||
import { of } from "rxjs";
|
||||
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { AuthService } from "@bitwarden/common/auth/services/auth.service";
|
||||
@@ -10,6 +11,7 @@ import { AutofillSettingsService } from "@bitwarden/common/autofill/services/aut
|
||||
import { ThemeType } from "@bitwarden/common/platform/enums";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/services/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/services/i18n.service";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { SettingsService } from "@bitwarden/common/services/settings.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type";
|
||||
@@ -53,6 +55,7 @@ describe("OverlayBackground", () => {
|
||||
const autofillSettingsService = mock<AutofillSettingsService>();
|
||||
const i18nService = mock<I18nService>();
|
||||
const platformUtilsService = mock<BrowserPlatformUtilsService>();
|
||||
const themeStateService = mock<ThemeStateService>();
|
||||
const initOverlayElementPorts = async (options = { initList: true, initButton: true }) => {
|
||||
const { initList, initButton } = options;
|
||||
if (initButton) {
|
||||
@@ -79,12 +82,15 @@ describe("OverlayBackground", () => {
|
||||
autofillSettingsService,
|
||||
i18nService,
|
||||
platformUtilsService,
|
||||
themeStateService,
|
||||
);
|
||||
|
||||
jest
|
||||
.spyOn(overlayBackground as any, "getOverlayVisibility")
|
||||
.mockResolvedValue(AutofillOverlayVisibility.OnFieldFocus);
|
||||
|
||||
themeStateService.selectedTheme$ = of(ThemeType.Light);
|
||||
|
||||
void overlayBackground.init();
|
||||
});
|
||||
|
||||
@@ -993,7 +999,7 @@ describe("OverlayBackground", () => {
|
||||
});
|
||||
|
||||
it("gets the system theme", async () => {
|
||||
jest.spyOn(overlayBackground["stateService"], "getTheme").mockResolvedValue(ThemeType.System);
|
||||
themeStateService.selectedTheme$ = of(ThemeType.System);
|
||||
|
||||
await initOverlayElementPorts({ initList: true, initButton: false });
|
||||
await flushPromises();
|
||||
|
||||
@@ -11,6 +11,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||
import { buildCipherIcon } from "@bitwarden/common/vault/icon/build-cipher-icon";
|
||||
@@ -96,6 +97,7 @@ class OverlayBackground implements OverlayBackgroundInterface {
|
||||
private autofillSettingsService: AutofillSettingsServiceAbstraction,
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private themeStateService: ThemeStateService,
|
||||
) {
|
||||
this.iconsServerUrl = this.environmentService.getIconsUrl();
|
||||
}
|
||||
@@ -695,7 +697,7 @@ class OverlayBackground implements OverlayBackgroundInterface {
|
||||
command: `initAutofillOverlay${isOverlayListPort ? "List" : "Button"}`,
|
||||
authStatus: await this.getAuthStatus(),
|
||||
styleSheetUrl: chrome.runtime.getURL(`overlay/${isOverlayListPort ? "list" : "button"}.css`),
|
||||
theme: await this.stateService.getTheme(),
|
||||
theme: await firstValueFrom(this.themeStateService.selectedTheme$),
|
||||
translations: this.getTranslations(),
|
||||
ciphers: isOverlayListPort ? this.getOverlayCipherData() : null,
|
||||
});
|
||||
|
||||
@@ -116,6 +116,7 @@ import { DefaultSingleUserStateProvider } from "@bitwarden/common/platform/state
|
||||
import { DefaultStateProvider } from "@bitwarden/common/platform/state/implementations/default-state.provider";
|
||||
import { StateEventRegistrarService } from "@bitwarden/common/platform/state/state-event-registrar.service";
|
||||
/* eslint-enable import/no-restricted-paths */
|
||||
import { DefaultThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { AvatarUpdateService } from "@bitwarden/common/services/account/avatar-update.service";
|
||||
import { ApiService } from "@bitwarden/common/services/api.service";
|
||||
import { AuditService } from "@bitwarden/common/services/audit.service";
|
||||
@@ -450,6 +451,9 @@ export default class MainBackground {
|
||||
async () => this.biometricUnlock(),
|
||||
self,
|
||||
);
|
||||
|
||||
const themeStateService = new DefaultThemeStateService(this.globalStateProvider);
|
||||
|
||||
this.i18nService = new I18nService(BrowserApi.getUILanguage(), this.globalStateProvider);
|
||||
this.cryptoService = new BrowserCryptoService(
|
||||
this.keyGenerationService,
|
||||
@@ -858,6 +862,7 @@ export default class MainBackground {
|
||||
this.domainSettingsService,
|
||||
this.environmentService,
|
||||
this.logService,
|
||||
themeStateService,
|
||||
);
|
||||
this.overlayBackground = new OverlayBackground(
|
||||
this.cipherService,
|
||||
@@ -869,6 +874,7 @@ export default class MainBackground {
|
||||
this.autofillSettingsService,
|
||||
this.i18nService,
|
||||
this.platformUtilsService,
|
||||
themeStateService,
|
||||
);
|
||||
this.filelessImporterBackground = new FilelessImporterBackground(
|
||||
this.configService,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { DOCUMENT } from "@angular/common";
|
||||
import { Inject, Injectable } from "@angular/core";
|
||||
|
||||
import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@@ -19,6 +20,7 @@ export class InitService {
|
||||
private logService: LogServiceAbstraction,
|
||||
private themingService: AbstractThemingService,
|
||||
private configService: ConfigService,
|
||||
@Inject(DOCUMENT) private document: Document,
|
||||
) {}
|
||||
|
||||
init() {
|
||||
@@ -34,7 +36,7 @@ export class InitService {
|
||||
}
|
||||
|
||||
const htmlEl = window.document.documentElement;
|
||||
await this.themingService.monitorThemeChanges();
|
||||
this.themingService.applyThemeChangesTo(this.document);
|
||||
htmlEl.classList.add("locale_" + this.i18nService.translationLocale);
|
||||
|
||||
// Workaround for slow performance on external monitors on Chrome + MacOS
|
||||
|
||||
@@ -3,13 +3,13 @@ import { DomSanitizer } from "@angular/platform-browser";
|
||||
import { ToastrService } from "ngx-toastr";
|
||||
|
||||
import { UnauthGuard as BaseUnauthGuardService } from "@bitwarden/angular/auth/guards";
|
||||
import { ThemingService } from "@bitwarden/angular/platform/services/theming/theming.service";
|
||||
import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction";
|
||||
import { AngularThemingService } from "@bitwarden/angular/platform/services/theming/angular-theming.service";
|
||||
import {
|
||||
MEMORY_STORAGE,
|
||||
SECURE_STORAGE,
|
||||
OBSERVABLE_DISK_STORAGE,
|
||||
OBSERVABLE_MEMORY_STORAGE,
|
||||
SYSTEM_THEME_OBSERVABLE,
|
||||
} from "@bitwarden/angular/services/injection-tokens";
|
||||
import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module";
|
||||
import {
|
||||
@@ -488,11 +488,8 @@ function getBgService<T>(service: keyof MainBackground) {
|
||||
deps: [StateServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: AbstractThemingService,
|
||||
useFactory: (
|
||||
stateService: StateServiceAbstraction,
|
||||
platformUtilsService: PlatformUtilsService,
|
||||
) => {
|
||||
provide: SYSTEM_THEME_OBSERVABLE,
|
||||
useFactory: (platformUtilsService: PlatformUtilsService) => {
|
||||
// Safari doesn't properly handle the (prefers-color-scheme) media query in the popup window, it always returns light.
|
||||
// In Safari, we have to use the background page instead, which comes with limitations like not dynamically changing the extension theme when the system theme is changed.
|
||||
let windowContext = window;
|
||||
@@ -501,9 +498,9 @@ function getBgService<T>(service: keyof MainBackground) {
|
||||
windowContext = backgroundWindow;
|
||||
}
|
||||
|
||||
return new ThemingService(stateService, windowContext, document);
|
||||
return AngularThemingService.createSystemThemeFromWindow(windowContext);
|
||||
},
|
||||
deps: [StateServiceAbstraction, PlatformUtilsService],
|
||||
deps: [PlatformUtilsService],
|
||||
},
|
||||
{
|
||||
provide: ConfigService,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { firstValueFrom } from "rxjs";
|
||||
|
||||
import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction";
|
||||
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
|
||||
import { AutofillSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/autofill-settings.service";
|
||||
import { BadgeSettingsServiceAbstraction } from "@bitwarden/common/autofill/services/badge-settings.service";
|
||||
@@ -16,6 +15,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
||||
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { ThemeType } from "@bitwarden/common/platform/enums";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
|
||||
|
||||
import { enableAccountSwitching } from "../../platform/flags";
|
||||
@@ -57,7 +57,7 @@ export class OptionsComponent implements OnInit {
|
||||
private domainSettingsService: DomainSettingsService,
|
||||
private badgeSettingsService: BadgeSettingsServiceAbstraction,
|
||||
i18nService: I18nService,
|
||||
private themingService: AbstractThemingService,
|
||||
private themeStateService: ThemeStateService,
|
||||
private settingsService: SettingsService,
|
||||
private vaultSettingsService: VaultSettingsService,
|
||||
) {
|
||||
@@ -125,7 +125,7 @@ export class OptionsComponent implements OnInit {
|
||||
|
||||
this.enablePasskeys = await firstValueFrom(this.vaultSettingsService.enablePasskeys$);
|
||||
|
||||
this.theme = await this.stateService.getTheme();
|
||||
this.theme = await firstValueFrom(this.themeStateService.selectedTheme$);
|
||||
|
||||
const defaultUriMatch = await firstValueFrom(
|
||||
this.domainSettingsService.defaultUriMatchStrategy$,
|
||||
@@ -186,7 +186,7 @@ export class OptionsComponent implements OnInit {
|
||||
}
|
||||
|
||||
async saveTheme() {
|
||||
await this.themingService.updateConfiguredTheme(this.theme);
|
||||
await this.themeStateService.setSelectedTheme(this.theme);
|
||||
}
|
||||
|
||||
async saveClearClipboard() {
|
||||
|
||||
Reference in New Issue
Block a user