mirror of
https://github.com/bitwarden/browser.git
synced 2024-10-22 07:50:04 +02:00
fix settings not switching when service changes
This commit is contained in:
parent
883aff47a3
commit
8f3ecab354
@ -1,13 +1,35 @@
|
|||||||
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from "@angular/core";
|
import {
|
||||||
|
Component,
|
||||||
|
EventEmitter,
|
||||||
|
Input,
|
||||||
|
OnChanges,
|
||||||
|
OnDestroy,
|
||||||
|
OnInit,
|
||||||
|
Output,
|
||||||
|
SimpleChanges,
|
||||||
|
} from "@angular/core";
|
||||||
import { FormBuilder } from "@angular/forms";
|
import { FormBuilder } from "@angular/forms";
|
||||||
import { BehaviorSubject, skip, Subject, takeUntil } from "rxjs";
|
import {
|
||||||
|
BehaviorSubject,
|
||||||
|
concatMap,
|
||||||
|
map,
|
||||||
|
ReplaySubject,
|
||||||
|
skip,
|
||||||
|
Subject,
|
||||||
|
switchAll,
|
||||||
|
switchMap,
|
||||||
|
takeUntil,
|
||||||
|
withLatestFrom,
|
||||||
|
} from "rxjs";
|
||||||
|
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { IntegrationId } from "@bitwarden/common/tools/integration";
|
import { IntegrationId } from "@bitwarden/common/tools/integration";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
import {
|
import {
|
||||||
|
CredentialGeneratorConfiguration,
|
||||||
CredentialGeneratorService,
|
CredentialGeneratorService,
|
||||||
getForwarderConfiguration,
|
getForwarderConfiguration,
|
||||||
|
NoPolicy,
|
||||||
toCredentialGeneratorConfiguration,
|
toCredentialGeneratorConfiguration,
|
||||||
} from "@bitwarden/generator-core";
|
} from "@bitwarden/generator-core";
|
||||||
|
|
||||||
@ -24,7 +46,7 @@ const Controls = Object.freeze({
|
|||||||
selector: "tools-forwarder-settings",
|
selector: "tools-forwarder-settings",
|
||||||
templateUrl: "forwarder-settings.component.html",
|
templateUrl: "forwarder-settings.component.html",
|
||||||
})
|
})
|
||||||
export class ForwarderSettingsComponent implements OnInit, OnDestroy {
|
export class ForwarderSettingsComponent implements OnInit, OnChanges, OnDestroy {
|
||||||
/** Instantiates the component
|
/** Instantiates the component
|
||||||
* @param accountService queries user availability
|
* @param accountService queries user availability
|
||||||
* @param generatorService settings and policy logic
|
* @param generatorService settings and policy logic
|
||||||
@ -43,7 +65,7 @@ export class ForwarderSettingsComponent implements OnInit, OnDestroy {
|
|||||||
@Input()
|
@Input()
|
||||||
userId: UserId | null;
|
userId: UserId | null;
|
||||||
|
|
||||||
@Input()
|
@Input({ required: true })
|
||||||
forwarder: IntegrationId;
|
forwarder: IntegrationId;
|
||||||
|
|
||||||
/** Emits settings updates and completes if the settings become unavailable.
|
/** Emits settings updates and completes if the settings become unavailable.
|
||||||
@ -61,38 +83,60 @@ export class ForwarderSettingsComponent implements OnInit, OnDestroy {
|
|||||||
[Controls.baseUrl]: [""],
|
[Controls.baseUrl]: [""],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
private forwarderId$ = new ReplaySubject<IntegrationId>(1);
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
const singleUserId$ = this.singleUserId$();
|
const singleUserId$ = this.singleUserId$();
|
||||||
const forwarder = getForwarderConfiguration(this.forwarder);
|
|
||||||
|
|
||||||
|
const forwarder$ = new ReplaySubject<CredentialGeneratorConfiguration<any, NoPolicy>>(1);
|
||||||
|
this.forwarderId$
|
||||||
|
.pipe(
|
||||||
|
map((id) => getForwarderConfiguration(id)),
|
||||||
// type erasure necessary because the configuration properties are
|
// type erasure necessary because the configuration properties are
|
||||||
// determined dynamically at runtime
|
// determined dynamically at runtime
|
||||||
// FIXME: this can be eliminated by unifying the forwarder settings types;
|
// FIXME: this can be eliminated by unifying the forwarder settings types;
|
||||||
// see `ForwarderConfiguration<...>` for details.
|
// see `ForwarderConfiguration<...>` for details.
|
||||||
const configuration = toCredentialGeneratorConfiguration<any>(forwarder);
|
map((forwarder) => toCredentialGeneratorConfiguration<any>(forwarder)),
|
||||||
this.displayDomain = configuration.request.includes("domain");
|
takeUntil(this.destroyed$),
|
||||||
this.displayToken = configuration.request.includes("token");
|
)
|
||||||
this.displayBaseUrl = configuration.request.includes("baseUrl");
|
.subscribe((forwarder) => {
|
||||||
|
this.displayDomain = forwarder.request.includes("domain");
|
||||||
|
this.displayToken = forwarder.request.includes("token");
|
||||||
|
this.displayBaseUrl = forwarder.request.includes("baseUrl");
|
||||||
|
|
||||||
// bind settings to the UI
|
forwarder$.next(forwarder);
|
||||||
const settings = await this.generatorService.settings(configuration, { singleUserId$ });
|
|
||||||
settings.pipe(takeUntil(this.destroyed$)).subscribe((s) => {
|
|
||||||
// skips reactive event emissions to break a subscription cycle
|
|
||||||
this.settings.patchValue(s, { emitEvent: false });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// bind policy to the template
|
const settings$$ = forwarder$.pipe(
|
||||||
this.generatorService
|
concatMap((forwarder) => this.generatorService.settings(forwarder, { singleUserId$ })),
|
||||||
.policy$(configuration, { userId$: singleUserId$ })
|
);
|
||||||
.pipe(takeUntil(this.destroyed$))
|
|
||||||
.subscribe(({ constraints }) => {
|
// bind settings to the reactive form
|
||||||
|
settings$$.pipe(switchAll(), takeUntil(this.destroyed$)).subscribe((settings) => {
|
||||||
|
// skips reactive event emissions to break a subscription cycle
|
||||||
|
this.settings.patchValue(settings as any, { emitEvent: false });
|
||||||
|
});
|
||||||
|
|
||||||
|
// bind policy to the reactive form
|
||||||
|
forwarder$
|
||||||
|
.pipe(
|
||||||
|
switchMap((forwarder) => {
|
||||||
|
const constraints$ = this.generatorService
|
||||||
|
.policy$(forwarder, { userId$: singleUserId$ })
|
||||||
|
.pipe(map(({ constraints }) => [constraints, forwarder] as const));
|
||||||
|
|
||||||
|
return constraints$;
|
||||||
|
}),
|
||||||
|
takeUntil(this.destroyed$),
|
||||||
|
)
|
||||||
|
.subscribe(([constraints, forwarder]) => {
|
||||||
for (const name in Controls) {
|
for (const name in Controls) {
|
||||||
const control = this.settings.get(name);
|
const control = this.settings.get(name);
|
||||||
if (configuration.request.includes(name as any)) {
|
if (forwarder.request.includes(name as any)) {
|
||||||
control.enable({ emitEvent: false });
|
control.enable({ emitEvent: false });
|
||||||
control.setValidators(
|
control.setValidators(
|
||||||
// the configuration's type erasure affects `toValidators` as well
|
// the configuration's type erasure affects `toValidators` as well
|
||||||
toValidators(name, configuration, constraints),
|
toValidators(name, forwarder, constraints),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
control.disable({ emitEvent: false });
|
control.disable({ emitEvent: false });
|
||||||
@ -102,10 +146,27 @@ export class ForwarderSettingsComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// the first emission is the current value; subsequent emissions are updates
|
// the first emission is the current value; subsequent emissions are updates
|
||||||
settings.pipe(skip(1), takeUntil(this.destroyed$)).subscribe(this.onUpdated);
|
settings$$
|
||||||
|
.pipe(
|
||||||
|
map((settings$) => settings$.pipe(skip(1))),
|
||||||
|
switchAll(),
|
||||||
|
takeUntil(this.destroyed$),
|
||||||
|
)
|
||||||
|
.subscribe(this.onUpdated);
|
||||||
|
|
||||||
// now that outputs are set up, connect inputs
|
// now that outputs are set up, connect inputs
|
||||||
this.settings.valueChanges.pipe(takeUntil(this.destroyed$)).subscribe(settings);
|
this.settings.valueChanges
|
||||||
|
.pipe(withLatestFrom(settings$$), takeUntil(this.destroyed$))
|
||||||
|
.subscribe(([value, settings]) => {
|
||||||
|
settings.next(value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
this.refresh$.complete();
|
||||||
|
if ("forwarder" in changes) {
|
||||||
|
this.forwarderId$.next(this.forwarder);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected displayDomain: boolean;
|
protected displayDomain: boolean;
|
||||||
@ -125,6 +186,8 @@ export class ForwarderSettingsComponent implements OnInit, OnDestroy {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly refresh$ = new Subject<void>();
|
||||||
|
|
||||||
private readonly destroyed$ = new Subject<void>();
|
private readonly destroyed$ = new Subject<void>();
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
this.destroyed$.complete();
|
this.destroyed$.complete();
|
||||||
|
Loading…
Reference in New Issue
Block a user