mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +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:
@@ -23,6 +23,7 @@ import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/comm
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { StateService as BaseStateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
|
||||
import { ThemeType } from "@bitwarden/common/platform/enums";
|
||||
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
|
||||
import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
|
||||
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
|
||||
@@ -32,6 +33,10 @@ import { StorageServiceProvider } from "@bitwarden/common/platform/services/stor
|
||||
import { GlobalStateProvider } from "@bitwarden/common/platform/state";
|
||||
import { MemoryStorageService as MemoryStorageServiceForStateProviders } from "@bitwarden/common/platform/state/storage/memory-storage.service";
|
||||
/* eslint-enable import/no-restricted-paths -- Implementation for memory storage */
|
||||
import {
|
||||
DefaultThemeStateService,
|
||||
ThemeStateService,
|
||||
} from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
|
||||
import { PolicyListService } from "../admin-console/core/policy-list.service";
|
||||
import { HtmlStorageService } from "../core/html-storage.service";
|
||||
@@ -133,6 +138,13 @@ import { WebPlatformUtilsService } from "./web-platform-utils.service";
|
||||
OBSERVABLE_DISK_LOCAL_STORAGE,
|
||||
],
|
||||
},
|
||||
{
|
||||
provide: ThemeStateService,
|
||||
useFactory: (globalStateProvider: GlobalStateProvider) =>
|
||||
// Web chooses to have Light as the default theme
|
||||
new DefaultThemeStateService(globalStateProvider, ThemeType.Light),
|
||||
deps: [GlobalStateProvider],
|
||||
},
|
||||
],
|
||||
})
|
||||
export class CoreModule {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { DOCUMENT } from "@angular/common";
|
||||
import { Inject, Injectable } from "@angular/core";
|
||||
|
||||
import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction";
|
||||
@@ -35,6 +36,7 @@ export class InitService {
|
||||
private themingService: AbstractThemingService,
|
||||
private encryptService: EncryptService,
|
||||
private configService: ConfigService,
|
||||
@Inject(DOCUMENT) private document: Document,
|
||||
) {}
|
||||
|
||||
init() {
|
||||
@@ -55,7 +57,7 @@ export class InitService {
|
||||
this.twoFactorService.init();
|
||||
const htmlEl = this.win.document.documentElement;
|
||||
htmlEl.classList.add("locale_" + this.i18nService.translationLocale);
|
||||
await this.themingService.monitorThemeChanges();
|
||||
this.themingService.applyThemeChangesTo(this.document);
|
||||
const containerService = new ContainerService(this.cryptoService, this.encryptService);
|
||||
containerService.attachToGlobal(this.win);
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { ThemeType } from "@bitwarden/common/platform/enums";
|
||||
import { GlobalState as BaseGlobalState } from "@bitwarden/common/platform/models/domain/global-state";
|
||||
|
||||
export class GlobalState extends BaseGlobalState {
|
||||
theme?: ThemeType = ThemeType.Light;
|
||||
rememberEmail = true;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Component, OnInit } from "@angular/core";
|
||||
import { FormBuilder } from "@angular/forms";
|
||||
import { concatMap, filter, firstValueFrom, map, Observable, Subject, takeUntil, tap } from "rxjs";
|
||||
|
||||
import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction";
|
||||
import { SettingsService } from "@bitwarden/common/abstractions/settings.service";
|
||||
import { VaultTimeoutSettingsService } from "@bitwarden/common/abstractions/vault-timeout/vault-timeout-settings.service";
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
@@ -14,6 +13,7 @@ import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/pl
|
||||
import { StateService } from "@bitwarden/common/platform/abstractions/state.service";
|
||||
import { ThemeType } from "@bitwarden/common/platform/enums";
|
||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||
import { ThemeStateService } from "@bitwarden/common/platform/theming/theme-state.service";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
@Component({
|
||||
@@ -35,7 +35,6 @@ export class PreferencesComponent implements OnInit {
|
||||
themeOptions: any[];
|
||||
|
||||
private startingLocale: string;
|
||||
private startingTheme: ThemeType;
|
||||
private destroy$ = new Subject<void>();
|
||||
|
||||
form = this.formBuilder.group({
|
||||
@@ -54,7 +53,7 @@ export class PreferencesComponent implements OnInit {
|
||||
private vaultTimeoutSettingsService: VaultTimeoutSettingsService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private messagingService: MessagingService,
|
||||
private themingService: AbstractThemingService,
|
||||
private themeStateService: ThemeStateService,
|
||||
private settingsService: SettingsService,
|
||||
private dialogService: DialogService,
|
||||
) {
|
||||
@@ -141,11 +140,10 @@ export class PreferencesComponent implements OnInit {
|
||||
this.vaultTimeoutSettingsService.vaultTimeoutAction$(),
|
||||
),
|
||||
enableFavicons: !(await this.settingsService.getDisableFavicon()),
|
||||
theme: await this.stateService.getTheme(),
|
||||
theme: await firstValueFrom(this.themeStateService.selectedTheme$),
|
||||
locale: (await firstValueFrom(this.i18nService.locale$)) ?? null,
|
||||
};
|
||||
this.startingLocale = initialFormValues.locale;
|
||||
this.startingTheme = initialFormValues.theme;
|
||||
this.form.setValue(initialFormValues, { emitEvent: false });
|
||||
}
|
||||
|
||||
@@ -165,10 +163,7 @@ export class PreferencesComponent implements OnInit {
|
||||
values.vaultTimeoutAction,
|
||||
);
|
||||
await this.settingsService.setDisableFavicon(!values.enableFavicons);
|
||||
if (values.theme !== this.startingTheme) {
|
||||
await this.themingService.updateConfiguredTheme(values.theme);
|
||||
this.startingTheme = values.theme;
|
||||
}
|
||||
await this.themeStateService.setSelectedTheme(values.theme);
|
||||
await this.i18nService.setLocale(values.locale);
|
||||
if (values.locale !== this.startingLocale) {
|
||||
window.location.reload();
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
// Set theme on page load
|
||||
// This is done outside the Angular app to avoid a flash of unthemed content before it loads
|
||||
// The defaultTheme is also set in the html itself to make sure that some theming is always applied
|
||||
(function () {
|
||||
const defaultTheme = "light";
|
||||
const htmlEl = document.documentElement;
|
||||
let theme = defaultTheme;
|
||||
|
||||
const globalState = window.localStorage.getItem("global");
|
||||
if (globalState != null) {
|
||||
const globalStateJson = JSON.parse(globalState);
|
||||
if (globalStateJson != null && globalStateJson.theme != null) {
|
||||
if (globalStateJson.theme.indexOf("system") > -1) {
|
||||
theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||
} else if (globalStateJson.theme.indexOf("dark") > -1) {
|
||||
theme = "dark";
|
||||
}
|
||||
}
|
||||
if (!htmlEl.classList.contains("theme_" + theme)) {
|
||||
htmlEl.classList.remove("theme_" + defaultTheme);
|
||||
htmlEl.classList.add("theme_" + theme);
|
||||
}
|
||||
}
|
||||
})();
|
||||
37
apps/web/src/theme.ts
Normal file
37
apps/web/src/theme.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
// Set theme on page load
|
||||
// This is done outside the Angular app to avoid a flash of unthemed content before it loads
|
||||
const setTheme = () => {
|
||||
const getLegacyTheme = (): string | null => {
|
||||
// MANUAL-STATE-ACCESS: Calling global to get setting before migration
|
||||
// has had a chance to run, can be remove in the future.
|
||||
// Tracking Issue: https://bitwarden.atlassian.net/browse/PM-6676
|
||||
const globalState = window.localStorage.getItem("global");
|
||||
|
||||
const parsedGlobalState = JSON.parse(globalState) as { theme?: string } | null;
|
||||
return parsedGlobalState ? parsedGlobalState.theme : null;
|
||||
};
|
||||
|
||||
const defaultTheme = "light";
|
||||
const htmlEl = document.documentElement;
|
||||
let theme = defaultTheme;
|
||||
|
||||
// First try the new state providers location, then the legacy location
|
||||
const themeFromState =
|
||||
window.localStorage.getItem("global_theming_selection") ?? getLegacyTheme();
|
||||
|
||||
if (themeFromState) {
|
||||
if (themeFromState.indexOf("system") > -1) {
|
||||
theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
||||
} else if (themeFromState.indexOf("dark") > -1) {
|
||||
theme = "dark";
|
||||
}
|
||||
}
|
||||
|
||||
if (!htmlEl.classList.contains("theme_" + theme)) {
|
||||
// The defaultTheme is also set in the html itself to make sure that some theming is always applied
|
||||
htmlEl.classList.remove("theme_" + defaultTheme);
|
||||
htmlEl.classList.add("theme_" + theme);
|
||||
}
|
||||
};
|
||||
|
||||
setTheme();
|
||||
@@ -26,7 +26,12 @@
|
||||
"strictTemplates": true,
|
||||
"preserveWhitespaces": true
|
||||
},
|
||||
"files": ["src/polyfills.ts", "src/main.ts", "../../bitwarden_license/bit-web/src/main.ts"],
|
||||
"files": [
|
||||
"src/polyfills.ts",
|
||||
"src/main.ts",
|
||||
"../../bitwarden_license/bit-web/src/main.ts",
|
||||
"src/theme.ts"
|
||||
],
|
||||
"include": [
|
||||
"src/connectors/*.ts",
|
||||
"src/**/*.stories.ts",
|
||||
|
||||
@@ -323,7 +323,7 @@ const webpackConfig = {
|
||||
"connectors/sso": "./src/connectors/sso.ts",
|
||||
"connectors/captcha": "./src/connectors/captcha.ts",
|
||||
"connectors/duo-redirect": "./src/connectors/duo-redirect.ts",
|
||||
theme_head: "./src/theme.js",
|
||||
theme_head: "./src/theme.ts",
|
||||
},
|
||||
optimization: {
|
||||
splitChunks: {
|
||||
|
||||
Reference in New Issue
Block a user