mirror of
https://github.com/bitwarden/browser
synced 2026-02-12 14:34:02 +00:00
Merge master into branch
This commit is contained in:
@@ -7,7 +7,7 @@ module.exports = {
|
||||
displayName: "libs/angular tests",
|
||||
preset: "jest-preset-angular",
|
||||
testMatch: ["**/+(*.)+(spec).+(ts)"],
|
||||
setupFilesAfterEnv: ["<rootDir>/spec/test.ts"],
|
||||
setupFilesAfterEnv: ["<rootDir>/spec/test.setup.ts"],
|
||||
collectCoverage: true,
|
||||
coverageReporters: ["html", "lcov"],
|
||||
coverageDirectory: "coverage",
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Directive, EventEmitter, Input, OnInit, Output } from "@angular/core";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
|
||||
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||
import { FileDownloadService } from "@bitwarden/common/abstractions/fileDownload/fileDownload.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
@@ -36,7 +37,8 @@ export class AttachmentsComponent implements OnInit {
|
||||
protected apiService: ApiService,
|
||||
protected win: Window,
|
||||
protected logService: LogService,
|
||||
protected stateService: StateService
|
||||
protected stateService: StateService,
|
||||
protected fileDownloadService: FileDownloadService
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -171,7 +173,10 @@ export class AttachmentsComponent implements OnInit {
|
||||
? attachment.key
|
||||
: await this.cryptoService.getOrgKey(this.cipher.organizationId);
|
||||
const decBuf = await this.cryptoService.decryptFromBytes(buf, key);
|
||||
this.platformUtilsService.saveFile(this.win, decBuf, null, attachment.fileName);
|
||||
this.fileDownloadService.download({
|
||||
fileName: attachment.fileName,
|
||||
blobData: decBuf,
|
||||
});
|
||||
} catch (e) {
|
||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred"));
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||
import { EventService } from "@bitwarden/common/abstractions/event.service";
|
||||
import { ExportService } from "@bitwarden/common/abstractions/export.service";
|
||||
import { FileDownloadService } from "@bitwarden/common/abstractions/fileDownload/fileDownload.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
@@ -58,11 +59,12 @@ export class ExportComponent implements OnInit {
|
||||
protected win: Window,
|
||||
private logService: LogService,
|
||||
private userVerificationService: UserVerificationService,
|
||||
protected formBuilder: FormBuilder,
|
||||
protected modalService: ModalService,
|
||||
protected apiService: ApiService,
|
||||
protected stateService: StateService,
|
||||
protected modalConfig: ModalConfig
|
||||
protected modalConfig: ModalConfig,
|
||||
private formBuilder: FormBuilder,
|
||||
protected fileDownloadService: FileDownloadService
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
@@ -217,6 +219,10 @@ export class ExportComponent implements OnInit {
|
||||
|
||||
private downloadFile(csv: string): void {
|
||||
const fileName = this.getFileName();
|
||||
this.platformUtilsService.saveFile(this.win, csv, { type: "text/plain" }, fileName);
|
||||
this.fileDownloadService.download({
|
||||
fileName: fileName,
|
||||
blobData: csv,
|
||||
blobOptions: { type: "text/plain" },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import { BroadcasterService } from "@bitwarden/common/abstractions/broadcaster.s
|
||||
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
|
||||
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
||||
import { EventService } from "@bitwarden/common/abstractions/event.service";
|
||||
import { FileDownloadService } from "@bitwarden/common/abstractions/fileDownload/fileDownload.service";
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PasswordRepromptService } from "@bitwarden/common/abstractions/passwordReprompt.service";
|
||||
@@ -76,7 +77,8 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
protected apiService: ApiService,
|
||||
protected passwordRepromptService: PasswordRepromptService,
|
||||
private logService: LogService,
|
||||
protected stateService: StateService
|
||||
protected stateService: StateService,
|
||||
protected fileDownloadService: FileDownloadService
|
||||
) {}
|
||||
|
||||
ngOnInit() {
|
||||
@@ -373,7 +375,10 @@ export class ViewComponent implements OnDestroy, OnInit {
|
||||
? attachment.key
|
||||
: await this.cryptoService.getOrgKey(this.cipher.organizationId);
|
||||
const decBuf = await this.cryptoService.decryptFromBytes(buf, key);
|
||||
this.platformUtilsService.saveFile(this.win, decBuf, null, attachment.fileName);
|
||||
this.fileDownloadService.download({
|
||||
fileName: attachment.fileName,
|
||||
blobData: decBuf,
|
||||
});
|
||||
} catch (e) {
|
||||
this.platformUtilsService.showToast("error", null, this.i18nService.t("errorOccurred"));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { InjectionToken, Injector, LOCALE_ID, NgModule } from "@angular/core";
|
||||
|
||||
import { ThemingService } from "@bitwarden/angular/services/theming/theming.service";
|
||||
import { AbstractThemingService } from "@bitwarden/angular/services/theming/theming.service.abstraction";
|
||||
import { AbstractEncryptService } from "@bitwarden/common/abstractions/abstractEncrypt.service";
|
||||
import { ApiService as ApiServiceAbstraction } from "@bitwarden/common/abstractions/api.service";
|
||||
import { AppIdService as AppIdServiceAbstraction } from "@bitwarden/common/abstractions/appId.service";
|
||||
import { AuditService as AuditServiceAbstraction } from "@bitwarden/common/abstractions/audit.service";
|
||||
@@ -30,7 +33,7 @@ import { SendService as SendServiceAbstraction } from "@bitwarden/common/abstrac
|
||||
import { SettingsService as SettingsServiceAbstraction } from "@bitwarden/common/abstractions/settings.service";
|
||||
import { StateService as StateServiceAbstraction } from "@bitwarden/common/abstractions/state.service";
|
||||
import { StateMigrationService as StateMigrationServiceAbstraction } from "@bitwarden/common/abstractions/stateMigration.service";
|
||||
import { StorageService as StorageServiceAbstraction } from "@bitwarden/common/abstractions/storage.service";
|
||||
import { AbstractStorageService } from "@bitwarden/common/abstractions/storage.service";
|
||||
import { SyncService as SyncServiceAbstraction } from "@bitwarden/common/abstractions/sync.service";
|
||||
import { TokenService as TokenServiceAbstraction } from "@bitwarden/common/abstractions/token.service";
|
||||
import { TotpService as TotpServiceAbstraction } from "@bitwarden/common/abstractions/totp.service";
|
||||
@@ -49,6 +52,7 @@ import { CipherService } from "@bitwarden/common/services/cipher.service";
|
||||
import { CollectionService } from "@bitwarden/common/services/collection.service";
|
||||
import { ConsoleLogService } from "@bitwarden/common/services/consoleLog.service";
|
||||
import { CryptoService } from "@bitwarden/common/services/crypto.service";
|
||||
import { EncryptService } from "@bitwarden/common/services/encrypt.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/services/environment.service";
|
||||
import { EventService } from "@bitwarden/common/services/event.service";
|
||||
import { ExportService } from "@bitwarden/common/services/export.service";
|
||||
@@ -84,7 +88,8 @@ import { PasswordRepromptService } from "./passwordReprompt.service";
|
||||
import { ValidationService } from "./validation.service";
|
||||
|
||||
export const WINDOW = new InjectionToken<Window>("WINDOW");
|
||||
export const SECURE_STORAGE = new InjectionToken<StorageServiceAbstraction>("SECURE_STORAGE");
|
||||
export const MEMORY_STORAGE = new InjectionToken<AbstractStorageService>("MEMORY_STORAGE");
|
||||
export const SECURE_STORAGE = new InjectionToken<AbstractStorageService>("SECURE_STORAGE");
|
||||
export const STATE_FACTORY = new InjectionToken<StateFactory>("STATE_FACTORY");
|
||||
export const STATE_SERVICE_USE_CACHE = new InjectionToken<boolean>("STATE_SERVICE_USE_CACHE");
|
||||
export const LOGOUT_CALLBACK = new InjectionToken<(expired: boolean, userId?: string) => void>(
|
||||
@@ -94,6 +99,7 @@ export const LOCKED_CALLBACK = new InjectionToken<() => void>("LOCKED_CALLBACK")
|
||||
export const CLIENT_TYPE = new InjectionToken<boolean>("CLIENT_TYPE");
|
||||
export const LOCALES_DIRECTORY = new InjectionToken<string>("LOCALES_DIRECTORY");
|
||||
export const SYSTEM_LANGUAGE = new InjectionToken<string>("SYSTEM_LANGUAGE");
|
||||
export const LOG_MAC_FAILURES = new InjectionToken<string>("LOG_MAC_FAILURES");
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
@@ -137,10 +143,14 @@ export const SYSTEM_LANGUAGE = new InjectionToken<string>("SYSTEM_LANGUAGE");
|
||||
provide: LOCKED_CALLBACK,
|
||||
useValue: null,
|
||||
},
|
||||
{
|
||||
provide: LOG_MAC_FAILURES,
|
||||
useValue: true,
|
||||
},
|
||||
{
|
||||
provide: AppIdServiceAbstraction,
|
||||
useClass: AppIdService,
|
||||
deps: [StorageServiceAbstraction],
|
||||
deps: [AbstractStorageService],
|
||||
},
|
||||
{
|
||||
provide: AuditServiceAbstraction,
|
||||
@@ -231,6 +241,7 @@ export const SYSTEM_LANGUAGE = new InjectionToken<string>("SYSTEM_LANGUAGE");
|
||||
useClass: CryptoService,
|
||||
deps: [
|
||||
CryptoFunctionServiceAbstraction,
|
||||
AbstractEncryptService,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
LogService,
|
||||
StateServiceAbstraction,
|
||||
@@ -313,8 +324,9 @@ export const SYSTEM_LANGUAGE = new InjectionToken<string>("SYSTEM_LANGUAGE");
|
||||
provide: StateServiceAbstraction,
|
||||
useClass: StateService,
|
||||
deps: [
|
||||
StorageServiceAbstraction,
|
||||
AbstractStorageService,
|
||||
SECURE_STORAGE,
|
||||
MEMORY_STORAGE,
|
||||
LogService,
|
||||
StateMigrationServiceAbstraction,
|
||||
STATE_FACTORY,
|
||||
@@ -324,7 +336,7 @@ export const SYSTEM_LANGUAGE = new InjectionToken<string>("SYSTEM_LANGUAGE");
|
||||
{
|
||||
provide: StateMigrationServiceAbstraction,
|
||||
useClass: StateMigrationService,
|
||||
deps: [StorageServiceAbstraction, SECURE_STORAGE, STATE_FACTORY],
|
||||
deps: [AbstractStorageService, SECURE_STORAGE, STATE_FACTORY],
|
||||
},
|
||||
{
|
||||
provide: ExportServiceAbstraction,
|
||||
@@ -360,6 +372,11 @@ export const SYSTEM_LANGUAGE = new InjectionToken<string>("SYSTEM_LANGUAGE");
|
||||
useClass: WebCryptoFunctionService,
|
||||
deps: [WINDOW],
|
||||
},
|
||||
{
|
||||
provide: AbstractEncryptService,
|
||||
useClass: EncryptService,
|
||||
deps: [CryptoFunctionServiceAbstraction, LogService, LOG_MAC_FAILURES],
|
||||
},
|
||||
{
|
||||
provide: EventServiceAbstraction,
|
||||
useClass: EventService,
|
||||
@@ -423,6 +440,10 @@ export const SYSTEM_LANGUAGE = new InjectionToken<string>("SYSTEM_LANGUAGE");
|
||||
useClass: TwoFactorService,
|
||||
deps: [I18nServiceAbstraction, PlatformUtilsServiceAbstraction],
|
||||
},
|
||||
{
|
||||
provide: AbstractThemingService,
|
||||
useClass: ThemingService,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class JslibServicesModule {}
|
||||
|
||||
6
libs/angular/src/services/theming/theme.ts
Normal file
6
libs/angular/src/services/theming/theme.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { ThemeType } from "@bitwarden/common/enums/themeType";
|
||||
|
||||
export interface Theme {
|
||||
configuredTheme: ThemeType;
|
||||
effectiveTheme: ThemeType;
|
||||
}
|
||||
19
libs/angular/src/services/theming/themeBuilder.ts
Normal file
19
libs/angular/src/services/theming/themeBuilder.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { ThemeType } from "@bitwarden/common/enums/themeType";
|
||||
|
||||
import { Theme } from "./theme";
|
||||
|
||||
export class ThemeBuilder implements Theme {
|
||||
get effectiveTheme(): ThemeType {
|
||||
return this.configuredTheme != ThemeType.System ? this.configuredTheme : this.systemTheme;
|
||||
}
|
||||
|
||||
constructor(readonly configuredTheme: ThemeType, readonly systemTheme: ThemeType) {}
|
||||
|
||||
updateSystemTheme(systemTheme: ThemeType): ThemeBuilder {
|
||||
return new ThemeBuilder(this.configuredTheme, systemTheme);
|
||||
}
|
||||
|
||||
updateConfiguredTheme(configuredTheme: ThemeType): ThemeBuilder {
|
||||
return new ThemeBuilder(configuredTheme, this.systemTheme);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { ThemeType } from "@bitwarden/common/enums/themeType";
|
||||
|
||||
import { Theme } from "./theme";
|
||||
|
||||
export abstract class AbstractThemingService {
|
||||
theme$: Observable<Theme>;
|
||||
monitorThemeChanges: () => Promise<void>;
|
||||
updateSystemTheme: (systemTheme: ThemeType) => void;
|
||||
updateConfiguredTheme: (theme: ThemeType) => Promise<void>;
|
||||
}
|
||||
71
libs/angular/src/services/theming/theming.service.ts
Normal file
71
libs/angular/src/services/theming/theming.service.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { MediaMatcher } from "@angular/cdk/layout";
|
||||
import { DOCUMENT } from "@angular/common";
|
||||
import { Inject, Injectable } from "@angular/core";
|
||||
import { BehaviorSubject, filter, fromEvent, Observable } from "rxjs";
|
||||
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { ThemeType } from "@bitwarden/common/enums/themeType";
|
||||
|
||||
import { Theme } from "./theme";
|
||||
import { ThemeBuilder } from "./themeBuilder";
|
||||
import { AbstractThemingService } from "./theming.service.abstraction";
|
||||
|
||||
@Injectable()
|
||||
export class ThemingService implements AbstractThemingService {
|
||||
private _theme = new BehaviorSubject<ThemeBuilder | null>(null);
|
||||
theme$: Observable<Theme> = this._theme.pipe(filter((x) => x !== null));
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private mediaMatcher: MediaMatcher,
|
||||
@Inject(DOCUMENT) private document: Document
|
||||
) {
|
||||
this.monitorThemeChanges();
|
||||
}
|
||||
|
||||
async monitorThemeChanges(): Promise<void> {
|
||||
this._theme.next(
|
||||
new ThemeBuilder(await this.stateService.getTheme(), await this.getSystemTheme())
|
||||
);
|
||||
this.monitorConfiguredThemeChanges();
|
||||
this.monitorSystemThemeChanges();
|
||||
}
|
||||
|
||||
updateSystemTheme(systemTheme: ThemeType): void {
|
||||
this._theme.next(this._theme.getValue().updateSystemTheme(systemTheme));
|
||||
}
|
||||
|
||||
async updateConfiguredTheme(theme: ThemeType): Promise<void> {
|
||||
await this.stateService.setTheme(theme);
|
||||
this._theme.next(this._theme.getValue().updateConfiguredTheme(theme));
|
||||
}
|
||||
|
||||
protected monitorConfiguredThemeChanges(): void {
|
||||
this.theme$.subscribe((theme: Theme) => {
|
||||
this.document.documentElement.classList.remove(
|
||||
"theme_" + ThemeType.Light,
|
||||
"theme_" + ThemeType.Dark,
|
||||
"theme_" + ThemeType.Nord,
|
||||
"theme_" + ThemeType.SolarizedDark
|
||||
);
|
||||
this.document.documentElement.classList.add("theme_" + theme.effectiveTheme);
|
||||
});
|
||||
}
|
||||
|
||||
// We use a media match query for monitoring the system theme on web and browser, but this doesn't work for electron apps on Linux.
|
||||
// In desktop we override these methods to track systemTheme with the electron renderer instead, which works for all OSs.
|
||||
protected async getSystemTheme(): Promise<ThemeType> {
|
||||
return this.mediaMatcher.matchMedia("(prefers-color-scheme: dark)").matches
|
||||
? ThemeType.Dark
|
||||
: ThemeType.Light;
|
||||
}
|
||||
|
||||
protected monitorSystemThemeChanges(): void {
|
||||
fromEvent<MediaQueryListEvent>(
|
||||
this.mediaMatcher.matchMedia("(prefers-color-scheme: dark)"),
|
||||
"change"
|
||||
).subscribe((event) => {
|
||||
this.updateSystemTheme(event.matches ? ThemeType.Dark : ThemeType.Light);
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user