mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 08:13:42 +00:00
[refactor] Introduce ThemingService (#2943)
* [refactor] Introduce ThemingService * [refactor] Implement ThemingService for web * [refactor] Implement ThemingService on browser * [refactor] Implement ThemingService for desktop * [refactor] Remove deprecated platformUtils.service theme methods * [fix] Move ThemingService from libs/common to libs/angular * [fix] Simplify ThemeBuilder's constructor * [fix] Dont notify subscribers of null values from theme$ * [fix] Always notify PaymentComponent of theme changes
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { DragDropModule } from "@angular/cdk/drag-drop";
|
||||
import { LayoutModule } from "@angular/cdk/layout";
|
||||
import { NgModule } from "@angular/core";
|
||||
import { FormsModule } from "@angular/forms";
|
||||
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
|
||||
@@ -18,6 +19,7 @@ import { WildcardRoutingModule } from "./wildcard-routing.module";
|
||||
ServicesModule,
|
||||
InfiniteScrollModule,
|
||||
DragDropModule,
|
||||
LayoutModule,
|
||||
OssRoutingModule,
|
||||
WildcardRoutingModule, // Needs to be last to catch all non-existing routes
|
||||
],
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Inject, Injectable } from "@angular/core";
|
||||
|
||||
import { WINDOW } from "@bitwarden/angular/services/jslib-services.module";
|
||||
import { AbstractThemingService } from "@bitwarden/angular/services/theming/theming.service.abstraction";
|
||||
import { CryptoService as CryptoServiceAbstraction } from "@bitwarden/common/abstractions/crypto.service";
|
||||
import {
|
||||
EnvironmentService as EnvironmentServiceAbstraction,
|
||||
@@ -9,11 +10,9 @@ import {
|
||||
import { EventService as EventLoggingServiceAbstraction } from "@bitwarden/common/abstractions/event.service";
|
||||
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { NotificationsService as NotificationsServiceAbstraction } from "@bitwarden/common/abstractions/notifications.service";
|
||||
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { StateService as StateServiceAbstraction } from "@bitwarden/common/abstractions/state.service";
|
||||
import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/abstractions/twoFactor.service";
|
||||
import { VaultTimeoutService as VaultTimeoutServiceAbstraction } from "@bitwarden/common/abstractions/vaultTimeout.service";
|
||||
import { ThemeType } from "@bitwarden/common/enums/themeType";
|
||||
import { ContainerService } from "@bitwarden/common/services/container.service";
|
||||
import { EventService as EventLoggingService } from "@bitwarden/common/services/event.service";
|
||||
import { VaultTimeoutService as VaultTimeoutService } from "@bitwarden/common/services/vaultTimeout.service";
|
||||
@@ -31,8 +30,8 @@ export class InitService {
|
||||
private eventLoggingService: EventLoggingServiceAbstraction,
|
||||
private twoFactorService: TwoFactorServiceAbstraction,
|
||||
private stateService: StateServiceAbstraction,
|
||||
private platformUtilsService: PlatformUtilsServiceAbstraction,
|
||||
private cryptoService: CryptoServiceAbstraction
|
||||
private cryptoService: CryptoServiceAbstraction,
|
||||
private themingService: AbstractThemingService
|
||||
) {}
|
||||
|
||||
init() {
|
||||
@@ -44,7 +43,6 @@ export class InitService {
|
||||
this.environmentService.setUrls(urls);
|
||||
|
||||
setTimeout(() => this.notificationsService.init(), 3000);
|
||||
|
||||
(this.vaultTimeoutService as VaultTimeoutService).init(true);
|
||||
const locale = await this.stateService.getLocale();
|
||||
await (this.i18nService as I18nService).init(locale);
|
||||
@@ -52,16 +50,7 @@ export class InitService {
|
||||
this.twoFactorService.init();
|
||||
const htmlEl = this.win.document.documentElement;
|
||||
htmlEl.classList.add("locale_" + this.i18nService.translationLocale);
|
||||
|
||||
// Initial theme is set in index.html which must be updated if there are any changes to theming logic
|
||||
this.platformUtilsService.onDefaultSystemThemeChange(async (sysTheme) => {
|
||||
const bwTheme = await this.stateService.getTheme();
|
||||
if (bwTheme === ThemeType.System) {
|
||||
htmlEl.classList.remove("theme_" + ThemeType.Light, "theme_" + ThemeType.Dark);
|
||||
htmlEl.classList.add("theme_" + sysTheme);
|
||||
}
|
||||
});
|
||||
|
||||
await this.themingService.monitorThemeChanges();
|
||||
const containerService = new ContainerService(this.cryptoService);
|
||||
containerService.attachToWindow(this.win);
|
||||
};
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
import { Component, Input, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Subject, takeUntil } from "rxjs";
|
||||
|
||||
import { AbstractThemingService } from "@bitwarden/angular/services/theming/theming.service.abstraction";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { PaymentMethodType } from "@bitwarden/common/enums/paymentMethodType";
|
||||
import { ThemeType } from "@bitwarden/common/enums/themeType";
|
||||
|
||||
@@ -17,7 +18,7 @@ const darkInputPlaceholderColor = ThemeVariables.darkInputPlaceholderColor;
|
||||
selector: "app-payment",
|
||||
templateUrl: "payment.component.html",
|
||||
})
|
||||
export class PaymentComponent implements OnInit {
|
||||
export class PaymentComponent implements OnInit, OnDestroy {
|
||||
@Input() showMethods = true;
|
||||
@Input() showOptions = true;
|
||||
@Input() method = PaymentMethodType.Card;
|
||||
@@ -25,6 +26,8 @@ export class PaymentComponent implements OnInit {
|
||||
@Input() hidePaypal = false;
|
||||
@Input() hideCredit = false;
|
||||
|
||||
private destroy$: Subject<void> = new Subject<void>();
|
||||
|
||||
bank: any = {
|
||||
routing_number: null,
|
||||
account_number: null,
|
||||
@@ -48,9 +51,9 @@ export class PaymentComponent implements OnInit {
|
||||
private StripeElementClasses: any;
|
||||
|
||||
constructor(
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private apiService: ApiService,
|
||||
private logService: LogService
|
||||
private logService: LogService,
|
||||
private themingService: AbstractThemingService
|
||||
) {
|
||||
this.stripeScript = window.document.createElement("script");
|
||||
this.stripeScript.src = "https://js.stripe.com/v3/";
|
||||
@@ -100,6 +103,8 @@ export class PaymentComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
window.document.head.removeChild(this.stripeScript);
|
||||
window.setTimeout(() => {
|
||||
Array.from(window.document.querySelectorAll("iframe")).forEach((el) => {
|
||||
@@ -275,15 +280,16 @@ export class PaymentComponent implements OnInit {
|
||||
}
|
||||
|
||||
private async setTheme() {
|
||||
const theme = await this.platformUtilsService.getEffectiveTheme();
|
||||
if (theme === ThemeType.Dark) {
|
||||
this.StripeElementStyle.base.color = darkInputColor;
|
||||
this.StripeElementStyle.base["::placeholder"].color = darkInputPlaceholderColor;
|
||||
this.StripeElementStyle.invalid.color = darkInputColor;
|
||||
} else {
|
||||
this.StripeElementStyle.base.color = lightInputColor;
|
||||
this.StripeElementStyle.base["::placeholder"].color = lightInputPlaceholderColor;
|
||||
this.StripeElementStyle.invalid.color = lightInputColor;
|
||||
}
|
||||
this.themingService.theme$.pipe(takeUntil(this.destroy$)).subscribe((theme) => {
|
||||
if (theme.effectiveTheme === ThemeType.Dark) {
|
||||
this.StripeElementStyle.base.color = darkInputColor;
|
||||
this.StripeElementStyle.base["::placeholder"].color = darkInputPlaceholderColor;
|
||||
this.StripeElementStyle.invalid.color = darkInputColor;
|
||||
} else {
|
||||
this.StripeElementStyle.base.color = lightInputColor;
|
||||
this.StripeElementStyle.base["::placeholder"].color = lightInputPlaceholderColor;
|
||||
this.StripeElementStyle.invalid.color = lightInputColor;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
import { FormControl } from "@angular/forms";
|
||||
|
||||
import { AbstractThemingService } from "@bitwarden/angular/services/theming/theming.service.abstraction";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
@@ -34,7 +35,8 @@ export class PreferencesComponent implements OnInit {
|
||||
private i18nService: I18nService,
|
||||
private vaultTimeoutService: VaultTimeoutService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private messagingService: MessagingService
|
||||
private messagingService: MessagingService,
|
||||
private themingService: AbstractThemingService
|
||||
) {
|
||||
this.vaultTimeouts = [
|
||||
{ name: i18nService.t("oneMinute"), value: 1 },
|
||||
@@ -100,12 +102,8 @@ export class PreferencesComponent implements OnInit {
|
||||
await this.stateService.setEnableFullWidth(this.enableFullWidth);
|
||||
this.messagingService.send("setFullWidth");
|
||||
if (this.theme !== this.startingTheme) {
|
||||
await this.stateService.setTheme(this.theme);
|
||||
await this.themingService.updateConfiguredTheme(this.theme);
|
||||
this.startingTheme = this.theme;
|
||||
const effectiveTheme = await this.platformUtilsService.getEffectiveTheme();
|
||||
const htmlEl = window.document.documentElement;
|
||||
htmlEl.classList.remove("theme_" + ThemeType.Light, "theme_" + ThemeType.Dark);
|
||||
htmlEl.classList.add("theme_" + effectiveTheme);
|
||||
}
|
||||
await this.stateService.setLocale(this.locale);
|
||||
if (this.locale !== this.startingLocale) {
|
||||
|
||||
@@ -5,21 +5,17 @@ import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { ClientType } from "@bitwarden/common/enums/clientType";
|
||||
import { DeviceType } from "@bitwarden/common/enums/deviceType";
|
||||
import { ThemeType } from "@bitwarden/common/enums/themeType";
|
||||
|
||||
@Injectable()
|
||||
export class WebPlatformUtilsService implements PlatformUtilsService {
|
||||
private browserCache: DeviceType = null;
|
||||
private prefersColorSchemeDark = window.matchMedia("(prefers-color-scheme: dark)");
|
||||
|
||||
constructor(
|
||||
private i18nService: I18nService,
|
||||
private messagingService: MessagingService,
|
||||
private logService: LogService,
|
||||
private stateService: StateService
|
||||
private logService: LogService
|
||||
) {}
|
||||
|
||||
getDevice(): DeviceType {
|
||||
@@ -303,32 +299,4 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
|
||||
supportsSecureStorage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
getDefaultSystemTheme(): Promise<ThemeType.Light | ThemeType.Dark> {
|
||||
return Promise.resolve(this.prefersColorSchemeDark.matches ? ThemeType.Dark : ThemeType.Light);
|
||||
}
|
||||
|
||||
async getEffectiveTheme(): Promise<ThemeType.Light | ThemeType.Dark> {
|
||||
const theme = await this.stateService.getTheme();
|
||||
if (theme === ThemeType.Dark) {
|
||||
return ThemeType.Dark;
|
||||
} else if (theme === ThemeType.System) {
|
||||
return this.getDefaultSystemTheme();
|
||||
} else {
|
||||
return ThemeType.Light;
|
||||
}
|
||||
}
|
||||
|
||||
onDefaultSystemThemeChange(callback: (theme: ThemeType.Light | ThemeType.Dark) => unknown) {
|
||||
try {
|
||||
this.prefersColorSchemeDark.addEventListener("change", ({ matches }) => {
|
||||
callback(matches ? ThemeType.Dark : ThemeType.Light);
|
||||
});
|
||||
} catch (e) {
|
||||
// Safari older than v14
|
||||
this.prefersColorSchemeDark.addListener((ev) => {
|
||||
callback(ev.matches ? ThemeType.Dark : ThemeType.Light);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user