mirror of
https://github.com/bitwarden/browser.git
synced 2024-10-08 05:47:50 +02:00
integrate username and password generators into browser extension
This commit is contained in:
parent
abf7a5146f
commit
5619504778
@ -1,62 +1,10 @@
|
|||||||
<bit-section>
|
<bit-section>
|
||||||
<!-- Password/Passphrase Toggle -->
|
<tools-password-generator
|
||||||
<bit-toggle-group
|
*ngIf="type === 'password'"
|
||||||
*ngIf="isPassword"
|
(onGenerated)="onCredentialGenerated($event)"
|
||||||
class="tw-w-full tw-justify-center tw-mt-3 tw-mb-5"
|
></tools-password-generator>
|
||||||
(selectedChange)="updatePasswordType($event)"
|
<tools-username-generator
|
||||||
[selected]="passwordType$ | async"
|
*ngIf="type === 'username'"
|
||||||
>
|
(onGenerated)="onCredentialGenerated($event)"
|
||||||
<bit-toggle [value]="'password'">
|
></tools-username-generator>
|
||||||
{{ "password" | i18n }}
|
|
||||||
</bit-toggle>
|
|
||||||
<bit-toggle [value]="'passphrase'">
|
|
||||||
{{ "passphrase" | i18n }}
|
|
||||||
</bit-toggle>
|
|
||||||
</bit-toggle-group>
|
|
||||||
|
|
||||||
<!-- Generated Password/Passphrase/Username -->
|
|
||||||
<bit-item>
|
|
||||||
<bit-item-content>
|
|
||||||
<bit-color-password [password]="generatedValue"></bit-color-password>
|
|
||||||
</bit-item-content>
|
|
||||||
<ng-container slot="end">
|
|
||||||
<bit-item-action>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
bitIconButton="bwi-clone"
|
|
||||||
size="small"
|
|
||||||
[appCopyClick]="generatedValue"
|
|
||||||
showToast
|
|
||||||
[appA11yTitle]="'copyValue' | i18n"
|
|
||||||
data-testid="copy-value-button"
|
|
||||||
></button>
|
|
||||||
</bit-item-action>
|
|
||||||
<bit-item-action>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
bitIconButton="bwi-generate"
|
|
||||||
size="small"
|
|
||||||
(click)="regenerate$.next()"
|
|
||||||
[appA11yTitle]="regenerateButtonTitle"
|
|
||||||
data-testid="regenerate-button"
|
|
||||||
></button>
|
|
||||||
</bit-item-action>
|
|
||||||
</ng-container>
|
|
||||||
</bit-item>
|
|
||||||
</bit-section>
|
|
||||||
|
|
||||||
<!-- Generator Options -->
|
|
||||||
<!-- TODO: Replace with Generator Options Component(s) when available
|
|
||||||
It is expected that the generator options component(s) will internally update the options stored in state
|
|
||||||
which will trigger regeneration automatically in this dialog.
|
|
||||||
-->
|
|
||||||
<bit-section>
|
|
||||||
<bit-section-header>
|
|
||||||
<h2 bitTypography="h5">{{ "options" | i18n }}</h2>
|
|
||||||
</bit-section-header>
|
|
||||||
<bit-card>
|
|
||||||
<em bitTypography="body2"
|
|
||||||
>Placeholder: Replace with Generator Options Component(s) when available</em
|
|
||||||
>
|
|
||||||
</bit-card>
|
|
||||||
</bit-section>
|
</bit-section>
|
||||||
|
@ -1,217 +1,103 @@
|
|||||||
import { ComponentFixture, fakeAsync, TestBed, tick } from "@angular/core/testing";
|
import { Component, EventEmitter, Output } from "@angular/core";
|
||||||
import { mock, MockProxy } from "jest-mock-extended";
|
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||||
import { BehaviorSubject } from "rxjs";
|
import { By } from "@angular/platform-browser";
|
||||||
|
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
|
||||||
import {
|
import {
|
||||||
PasswordGenerationServiceAbstraction,
|
PasswordGeneratorComponent,
|
||||||
PasswordGeneratorOptions,
|
UsernameGeneratorComponent,
|
||||||
UsernameGenerationServiceAbstraction,
|
} from "@bitwarden/generator-components";
|
||||||
UsernameGeneratorOptions,
|
|
||||||
} from "@bitwarden/generator-legacy";
|
|
||||||
import { CipherFormGeneratorComponent } from "@bitwarden/vault";
|
import { CipherFormGeneratorComponent } from "@bitwarden/vault";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "tools-password-generator",
|
||||||
|
template: `<ng-content></ng-content>`,
|
||||||
|
standalone: true,
|
||||||
|
})
|
||||||
|
class MockPasswordGeneratorComponent {
|
||||||
|
@Output() onGenerated = new EventEmitter();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "tools-username-generator",
|
||||||
|
template: `<ng-content></ng-content>`,
|
||||||
|
standalone: true,
|
||||||
|
})
|
||||||
|
class MockUsernameGeneratorComponent {
|
||||||
|
@Output() onGenerated = new EventEmitter();
|
||||||
|
}
|
||||||
|
|
||||||
describe("CipherFormGeneratorComponent", () => {
|
describe("CipherFormGeneratorComponent", () => {
|
||||||
let component: CipherFormGeneratorComponent;
|
let component: CipherFormGeneratorComponent;
|
||||||
let fixture: ComponentFixture<CipherFormGeneratorComponent>;
|
let fixture: ComponentFixture<CipherFormGeneratorComponent>;
|
||||||
|
|
||||||
let mockLegacyPasswordGenerationService: MockProxy<PasswordGenerationServiceAbstraction>;
|
|
||||||
let mockLegacyUsernameGenerationService: MockProxy<UsernameGenerationServiceAbstraction>;
|
|
||||||
let mockPlatformUtilsService: MockProxy<PlatformUtilsService>;
|
|
||||||
|
|
||||||
let passwordOptions$: BehaviorSubject<any>;
|
|
||||||
let usernameOptions$: BehaviorSubject<any>;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
passwordOptions$ = new BehaviorSubject([
|
|
||||||
{
|
|
||||||
type: "password",
|
|
||||||
},
|
|
||||||
] as [PasswordGeneratorOptions]);
|
|
||||||
usernameOptions$ = new BehaviorSubject([
|
|
||||||
{
|
|
||||||
type: "word",
|
|
||||||
},
|
|
||||||
] as [UsernameGeneratorOptions]);
|
|
||||||
|
|
||||||
mockPlatformUtilsService = mock<PlatformUtilsService>();
|
|
||||||
|
|
||||||
mockLegacyPasswordGenerationService = mock<PasswordGenerationServiceAbstraction>();
|
|
||||||
mockLegacyPasswordGenerationService.getOptions$.mockReturnValue(passwordOptions$);
|
|
||||||
|
|
||||||
mockLegacyUsernameGenerationService = mock<UsernameGenerationServiceAbstraction>();
|
|
||||||
mockLegacyUsernameGenerationService.getOptions$.mockReturnValue(usernameOptions$);
|
|
||||||
|
|
||||||
await TestBed.configureTestingModule({
|
await TestBed.configureTestingModule({
|
||||||
imports: [CipherFormGeneratorComponent],
|
imports: [CipherFormGeneratorComponent],
|
||||||
providers: [
|
providers: [{ provide: I18nService, useValue: { t: (key: string) => key } }],
|
||||||
{ provide: I18nService, useValue: { t: (key: string) => key } },
|
})
|
||||||
{
|
.overrideComponent(CipherFormGeneratorComponent, {
|
||||||
provide: PasswordGenerationServiceAbstraction,
|
remove: { imports: [PasswordGeneratorComponent, UsernameGeneratorComponent] },
|
||||||
useValue: mockLegacyPasswordGenerationService,
|
add: { imports: [MockPasswordGeneratorComponent, MockUsernameGeneratorComponent] },
|
||||||
},
|
})
|
||||||
{
|
.compileComponents();
|
||||||
provide: UsernameGenerationServiceAbstraction,
|
|
||||||
useValue: mockLegacyUsernameGenerationService,
|
|
||||||
},
|
|
||||||
{ provide: PlatformUtilsService, useValue: mockPlatformUtilsService },
|
|
||||||
],
|
|
||||||
}).compileComponents();
|
|
||||||
|
|
||||||
fixture = TestBed.createComponent(CipherFormGeneratorComponent);
|
fixture = TestBed.createComponent(CipherFormGeneratorComponent);
|
||||||
component = fixture.componentInstance;
|
component = fixture.componentInstance;
|
||||||
});
|
|
||||||
|
|
||||||
it("should create", () => {
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(component).toBeTruthy();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should use the appropriate text based on generator type", () => {
|
|
||||||
component.type = "password";
|
|
||||||
component.ngOnChanges();
|
|
||||||
expect(component["regenerateButtonTitle"]).toBe("regeneratePassword");
|
|
||||||
|
|
||||||
component.type = "username";
|
|
||||||
component.ngOnChanges();
|
|
||||||
expect(component["regenerateButtonTitle"]).toBe("regenerateUsername");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should emit regenerate$ when user clicks the regenerate button", fakeAsync(() => {
|
|
||||||
const regenerateSpy = jest.spyOn(component["regenerate$"], "next");
|
|
||||||
|
|
||||||
fixture.nativeElement.querySelector("button[data-testid='regenerate-button']").click();
|
|
||||||
|
|
||||||
expect(regenerateSpy).toHaveBeenCalled();
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should emit valueGenerated whenever a new value is generated", fakeAsync(() => {
|
|
||||||
const valueGeneratedSpy = jest.spyOn(component.valueGenerated, "emit");
|
|
||||||
|
|
||||||
mockLegacyPasswordGenerationService.generatePassword.mockResolvedValue("generated-password");
|
|
||||||
component.type = "password";
|
|
||||||
|
|
||||||
component.ngOnChanges();
|
|
||||||
tick();
|
|
||||||
|
|
||||||
expect(valueGeneratedSpy).toHaveBeenCalledWith("generated-password");
|
|
||||||
}));
|
|
||||||
|
|
||||||
describe("password generation", () => {
|
describe("password generation", () => {
|
||||||
|
let passwordGenerator: MockPasswordGeneratorComponent;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
component.type = "password";
|
component.type = "password";
|
||||||
});
|
|
||||||
|
|
||||||
it("should update the generated value when the password options change", fakeAsync(() => {
|
|
||||||
mockLegacyPasswordGenerationService.generatePassword
|
|
||||||
.mockResolvedValueOnce("first-password")
|
|
||||||
.mockResolvedValueOnce("second-password");
|
|
||||||
|
|
||||||
component.ngOnChanges();
|
|
||||||
tick();
|
|
||||||
|
|
||||||
expect(component["generatedValue"]).toBe("first-password");
|
|
||||||
|
|
||||||
passwordOptions$.next([{ type: "password" }]);
|
|
||||||
tick();
|
|
||||||
|
|
||||||
expect(component["generatedValue"]).toBe("second-password");
|
|
||||||
expect(mockLegacyPasswordGenerationService.generatePassword).toHaveBeenCalledTimes(2);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should show password type toggle when the generator type is password", () => {
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(fixture.nativeElement.querySelector("bit-toggle-group")).toBeTruthy();
|
passwordGenerator = fixture.debugElement.query(
|
||||||
|
By.directive(MockPasswordGeneratorComponent),
|
||||||
|
).componentInstance;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should update the generated value when the password type is updated", fakeAsync(async () => {
|
it("only shows `PasswordGeneratorComponent`", () => {
|
||||||
mockLegacyPasswordGenerationService.generatePassword
|
expect(passwordGenerator).toBeTruthy();
|
||||||
.mockResolvedValueOnce("first-password")
|
expect(fixture.debugElement.query(By.directive(MockUsernameGeneratorComponent))).toBeNull();
|
||||||
.mockResolvedValueOnce("second-password");
|
});
|
||||||
|
|
||||||
component.ngOnChanges();
|
it("invokes `valueGenerated` with the generated credential", () => {
|
||||||
tick();
|
jest.spyOn(component.valueGenerated, "emit");
|
||||||
|
|
||||||
expect(component["generatedValue"]).toBe("first-password");
|
passwordGenerator.onGenerated.emit({ credential: "new-cred-password!" });
|
||||||
|
|
||||||
await component["updatePasswordType"]("passphrase");
|
expect(component.valueGenerated.emit).toHaveBeenCalledTimes(1);
|
||||||
tick();
|
expect(component.valueGenerated.emit).toHaveBeenCalledWith("new-cred-password!");
|
||||||
|
});
|
||||||
expect(component["generatedValue"]).toBe("second-password");
|
|
||||||
expect(mockLegacyPasswordGenerationService.generatePassword).toHaveBeenCalledTimes(2);
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should update the password history when a new password is generated", fakeAsync(() => {
|
|
||||||
mockLegacyPasswordGenerationService.generatePassword.mockResolvedValue("new-password");
|
|
||||||
|
|
||||||
component.ngOnChanges();
|
|
||||||
tick();
|
|
||||||
|
|
||||||
expect(mockLegacyPasswordGenerationService.generatePassword).toHaveBeenCalledTimes(1);
|
|
||||||
expect(mockLegacyPasswordGenerationService.addHistory).toHaveBeenCalledWith("new-password");
|
|
||||||
expect(component["generatedValue"]).toBe("new-password");
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should regenerate the password when regenerate$ emits", fakeAsync(() => {
|
|
||||||
mockLegacyPasswordGenerationService.generatePassword
|
|
||||||
.mockResolvedValueOnce("first-password")
|
|
||||||
.mockResolvedValueOnce("second-password");
|
|
||||||
|
|
||||||
component.ngOnChanges();
|
|
||||||
tick();
|
|
||||||
|
|
||||||
expect(component["generatedValue"]).toBe("first-password");
|
|
||||||
|
|
||||||
component["regenerate$"].next();
|
|
||||||
tick();
|
|
||||||
|
|
||||||
expect(component["generatedValue"]).toBe("second-password");
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("username generation", () => {
|
describe("username generation", () => {
|
||||||
|
let usernameGenerator: MockUsernameGeneratorComponent;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
component.type = "username";
|
component.type = "username";
|
||||||
});
|
|
||||||
|
|
||||||
it("should update the generated value when the username options change", fakeAsync(() => {
|
|
||||||
mockLegacyUsernameGenerationService.generateUsername
|
|
||||||
.mockResolvedValueOnce("first-username")
|
|
||||||
.mockResolvedValueOnce("second-username");
|
|
||||||
|
|
||||||
component.ngOnChanges();
|
|
||||||
tick();
|
|
||||||
|
|
||||||
expect(component["generatedValue"]).toBe("first-username");
|
|
||||||
|
|
||||||
usernameOptions$.next([{ type: "word" }]);
|
|
||||||
tick();
|
|
||||||
|
|
||||||
expect(component["generatedValue"]).toBe("second-username");
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should regenerate the username when regenerate$ emits", fakeAsync(() => {
|
|
||||||
mockLegacyUsernameGenerationService.generateUsername
|
|
||||||
.mockResolvedValueOnce("first-username")
|
|
||||||
.mockResolvedValueOnce("second-username");
|
|
||||||
|
|
||||||
component.ngOnChanges();
|
|
||||||
tick();
|
|
||||||
|
|
||||||
expect(component["generatedValue"]).toBe("first-username");
|
|
||||||
|
|
||||||
component["regenerate$"].next();
|
|
||||||
tick();
|
|
||||||
|
|
||||||
expect(component["generatedValue"]).toBe("second-username");
|
|
||||||
}));
|
|
||||||
|
|
||||||
it("should not show password type toggle when the generator type is username", () => {
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
|
|
||||||
expect(fixture.nativeElement.querySelector("bit-toggle-group")).toBeNull();
|
usernameGenerator = fixture.debugElement.query(
|
||||||
|
By.directive(MockUsernameGeneratorComponent),
|
||||||
|
).componentInstance;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("only shows `UsernameGeneratorComponent`", () => {
|
||||||
|
expect(usernameGenerator).toBeTruthy();
|
||||||
|
expect(fixture.debugElement.query(By.directive(MockPasswordGeneratorComponent))).toBeNull();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("invokes `valueGenerated` with the generated credential", () => {
|
||||||
|
jest.spyOn(component.valueGenerated, "emit");
|
||||||
|
|
||||||
|
usernameGenerator.onGenerated.emit({ credential: "new-cred-username!" });
|
||||||
|
|
||||||
|
expect(component.valueGenerated.emit).toHaveBeenCalledTimes(1);
|
||||||
|
expect(component.valueGenerated.emit).toHaveBeenCalledWith("new-cred-username!");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,36 +1,12 @@
|
|||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { Component, DestroyRef, EventEmitter, Input, OnChanges, Output } from "@angular/core";
|
import { Component, EventEmitter, Input, Output } from "@angular/core";
|
||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
|
||||||
import {
|
|
||||||
combineLatest,
|
|
||||||
map,
|
|
||||||
merge,
|
|
||||||
shareReplay,
|
|
||||||
startWith,
|
|
||||||
Subject,
|
|
||||||
Subscription,
|
|
||||||
switchMap,
|
|
||||||
take,
|
|
||||||
tap,
|
|
||||||
} from "rxjs";
|
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { SectionComponent } from "@bitwarden/components";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
|
||||||
import {
|
import {
|
||||||
CardComponent,
|
PasswordGeneratorComponent,
|
||||||
ColorPasswordModule,
|
UsernameGeneratorComponent,
|
||||||
IconButtonModule,
|
} from "@bitwarden/generator-components";
|
||||||
ItemModule,
|
import { GeneratedCredential } from "@bitwarden/generator-core";
|
||||||
SectionComponent,
|
|
||||||
SectionHeaderComponent,
|
|
||||||
ToggleGroupModule,
|
|
||||||
TypographyModule,
|
|
||||||
} from "@bitwarden/components";
|
|
||||||
import { GeneratorType } from "@bitwarden/generator-core";
|
|
||||||
import {
|
|
||||||
PasswordGenerationServiceAbstraction,
|
|
||||||
UsernameGenerationServiceAbstraction,
|
|
||||||
} from "@bitwarden/generator-legacy";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders a password or username generator UI and emits the most recently generated value.
|
* Renders a password or username generator UI and emits the most recently generated value.
|
||||||
@ -40,20 +16,9 @@ import {
|
|||||||
selector: "vault-cipher-form-generator",
|
selector: "vault-cipher-form-generator",
|
||||||
templateUrl: "./cipher-form-generator.component.html",
|
templateUrl: "./cipher-form-generator.component.html",
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [
|
imports: [CommonModule, SectionComponent, PasswordGeneratorComponent, UsernameGeneratorComponent],
|
||||||
CommonModule,
|
|
||||||
CardComponent,
|
|
||||||
SectionComponent,
|
|
||||||
ToggleGroupModule,
|
|
||||||
JslibModule,
|
|
||||||
ItemModule,
|
|
||||||
ColorPasswordModule,
|
|
||||||
IconButtonModule,
|
|
||||||
SectionHeaderComponent,
|
|
||||||
TypographyModule,
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
export class CipherFormGeneratorComponent implements OnChanges {
|
export class CipherFormGeneratorComponent {
|
||||||
/**
|
/**
|
||||||
* The type of generator form to show.
|
* The type of generator form to show.
|
||||||
*/
|
*/
|
||||||
@ -66,118 +31,8 @@ export class CipherFormGeneratorComponent implements OnChanges {
|
|||||||
@Output()
|
@Output()
|
||||||
valueGenerated = new EventEmitter<string>();
|
valueGenerated = new EventEmitter<string>();
|
||||||
|
|
||||||
protected get isPassword() {
|
/** Event handler for both generation components */
|
||||||
return this.type === "password";
|
onCredentialGenerated = (generatedCred: GeneratedCredential) => {
|
||||||
}
|
this.valueGenerated.emit(generatedCred.credential);
|
||||||
|
|
||||||
protected regenerateButtonTitle: string;
|
|
||||||
protected regenerate$ = new Subject<void>();
|
|
||||||
protected passwordTypeSubject$ = new Subject<GeneratorType>();
|
|
||||||
/**
|
|
||||||
* The currently generated value displayed to the user.
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
protected generatedValue: string = "";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current username generation options.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private usernameOptions$ = this.legacyUsernameGenerationService.getOptions$();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current password type selected in the UI. Starts with the saved value from the service.
|
|
||||||
* @protected
|
|
||||||
*/
|
|
||||||
protected passwordType$ = merge(
|
|
||||||
this.legacyPasswordGenerationService.getOptions$().pipe(
|
|
||||||
take(1),
|
|
||||||
map(([options]) => options.type),
|
|
||||||
),
|
|
||||||
this.passwordTypeSubject$,
|
|
||||||
).pipe(shareReplay({ bufferSize: 1, refCount: false }));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current password generation options.
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private passwordOptions$ = combineLatest([
|
|
||||||
this.legacyPasswordGenerationService.getOptions$(),
|
|
||||||
this.passwordType$,
|
|
||||||
]).pipe(
|
|
||||||
map(([[options], type]) => {
|
|
||||||
options.type = type;
|
|
||||||
return options;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tracks the regenerate$ subscription
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
private subscription: Subscription | null;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private i18nService: I18nService,
|
|
||||||
private legacyPasswordGenerationService: PasswordGenerationServiceAbstraction,
|
|
||||||
private legacyUsernameGenerationService: UsernameGenerationServiceAbstraction,
|
|
||||||
private destroyRef: DestroyRef,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
ngOnChanges() {
|
|
||||||
this.regenerateButtonTitle = this.i18nService.t(
|
|
||||||
this.isPassword ? "regeneratePassword" : "regenerateUsername",
|
|
||||||
);
|
|
||||||
|
|
||||||
// If we have a previous subscription, clear it
|
|
||||||
if (this.subscription) {
|
|
||||||
this.subscription.unsubscribe();
|
|
||||||
this.subscription = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isPassword) {
|
|
||||||
this.setupPasswordGeneration();
|
|
||||||
} else {
|
|
||||||
this.setupUsernameGeneration();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private setupPasswordGeneration() {
|
|
||||||
this.subscription = this.regenerate$
|
|
||||||
.pipe(
|
|
||||||
startWith(null),
|
|
||||||
switchMap(() => this.passwordOptions$),
|
|
||||||
switchMap((options) => this.legacyPasswordGenerationService.generatePassword(options)),
|
|
||||||
tap(async (password) => {
|
|
||||||
await this.legacyPasswordGenerationService.addHistory(password);
|
|
||||||
}),
|
|
||||||
takeUntilDestroyed(this.destroyRef),
|
|
||||||
)
|
|
||||||
.subscribe((password) => {
|
|
||||||
this.generatedValue = password;
|
|
||||||
this.valueGenerated.emit(password);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private setupUsernameGeneration() {
|
|
||||||
this.subscription = this.regenerate$
|
|
||||||
.pipe(
|
|
||||||
startWith(null),
|
|
||||||
switchMap(() => this.usernameOptions$),
|
|
||||||
switchMap((options) => this.legacyUsernameGenerationService.generateUsername(options)),
|
|
||||||
takeUntilDestroyed(this.destroyRef),
|
|
||||||
)
|
|
||||||
.subscribe((username) => {
|
|
||||||
this.generatedValue = username;
|
|
||||||
this.valueGenerated.emit(username);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Switch the password generation type.
|
|
||||||
* @param value The new password generation type.
|
|
||||||
*/
|
|
||||||
protected updatePasswordType = async (value: GeneratorType) => {
|
|
||||||
this.passwordTypeSubject$.next(value);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user