1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-20 02:03:39 +00:00

[BEEEP] [PM-28239] Migrate generators to standalone (#17386)

* Migrate generators to use standalone and control flow

* Resolve feedback

* Add variable for account

* Fix generators
This commit is contained in:
Oscar Hinton
2025-12-11 12:04:15 +01:00
committed by GitHub
parent 7183d77f7b
commit 267e488390
24 changed files with 481 additions and 288 deletions

View File

@@ -136,6 +136,7 @@ import {
DialogService, DialogService,
ToastService, ToastService,
} from "@bitwarden/components"; } from "@bitwarden/components";
import { GeneratorServicesModule } from "@bitwarden/generator-components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { import {
BiometricsService, BiometricsService,
@@ -743,7 +744,7 @@ const safeProviders: SafeProvider[] = [
]; ];
@NgModule({ @NgModule({
imports: [JslibServicesModule], imports: [JslibServicesModule, GeneratorServicesModule],
declarations: [], declarations: [],
// Do not register your dependency here! Add it to the typesafeProviders array using the helper function // Do not register your dependency here! Add it to the typesafeProviders array using the helper function
providers: safeProviders, providers: safeProviders,

View File

@@ -102,6 +102,7 @@ import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/s
import { SyncService } from "@bitwarden/common/platform/sync"; import { SyncService } from "@bitwarden/common/platform/sync";
import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherService as CipherServiceAbstraction } from "@bitwarden/common/vault/abstractions/cipher.service";
import { DialogService, ToastService } from "@bitwarden/components"; import { DialogService, ToastService } from "@bitwarden/components";
import { GeneratorServicesModule } from "@bitwarden/generator-components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { import {
KdfConfigService, KdfConfigService,
@@ -499,7 +500,7 @@ const safeProviders: SafeProvider[] = [
]; ];
@NgModule({ @NgModule({
imports: [JslibServicesModule], imports: [JslibServicesModule, GeneratorServicesModule],
declarations: [], declarations: [],
// Do not register your dependency here! Add it to the typesafeProviders array using the helper function // Do not register your dependency here! Add it to the typesafeProviders array using the helper function
providers: safeProviders, providers: safeProviders,

View File

@@ -112,6 +112,7 @@ import {
} from "@bitwarden/common/platform/theming/theme-state.service"; } from "@bitwarden/common/platform/theming/theme-state.service";
import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service";
import { DialogService, ToastService } from "@bitwarden/components"; import { DialogService, ToastService } from "@bitwarden/components";
import { GeneratorServicesModule } from "@bitwarden/generator-components";
import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy"; import { PasswordGenerationServiceAbstraction } from "@bitwarden/generator-legacy";
import { import {
KdfConfigService, KdfConfigService,
@@ -484,7 +485,7 @@ const safeProviders: SafeProvider[] = [
@NgModule({ @NgModule({
declarations: [], declarations: [],
imports: [CommonModule, JslibServicesModule], imports: [CommonModule, JslibServicesModule, GeneratorServicesModule],
// Do not register your dependency here! Add it to the typesafeProviders array using the helper function // Do not register your dependency here! Add it to the typesafeProviders array using the helper function
providers: safeProviders, providers: safeProviders,
}) })

View File

@@ -8,15 +8,18 @@ import {
Output, Output,
SimpleChanges, SimpleChanges,
} from "@angular/core"; } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { map, ReplaySubject, skip, Subject, takeUntil, withLatestFrom } from "rxjs"; import { map, ReplaySubject, skip, Subject, takeUntil, withLatestFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { FormFieldModule } from "@bitwarden/components";
import { import {
CatchallGenerationOptions, CatchallGenerationOptions,
CredentialGeneratorService, CredentialGeneratorService,
BuiltIn, BuiltIn,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
/** Options group for catchall emails */ /** Options group for catchall emails */
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
@@ -24,7 +27,7 @@ import {
@Component({ @Component({
selector: "tools-catchall-settings", selector: "tools-catchall-settings",
templateUrl: "catchall-settings.component.html", templateUrl: "catchall-settings.component.html",
standalone: false, imports: [ReactiveFormsModule, FormFieldModule, JslibModule, I18nPipe],
}) })
export class CatchallSettingsComponent implements OnInit, OnDestroy, OnChanges { export class CatchallSettingsComponent implements OnInit, OnDestroy, OnChanges {
/** Instantiates the component /** Instantiates the component

View File

@@ -1,8 +1,11 @@
<bit-dialog #dialog background="alt"> <bit-dialog #dialog background="alt">
<span bitDialogTitle>{{ "generatorHistory" | i18n }}</span> <span bitDialogTitle>{{ "generatorHistory" | i18n }}</span>
<ng-container bitDialogContent> <ng-container bitDialogContent>
<bit-empty-credential-history *ngIf="!(hasHistory$ | async)" style="display: contents" /> @if (hasHistory$ | async) {
<bit-credential-generator-history [account]="account$ | async" *ngIf="hasHistory$ | async" /> <bit-credential-generator-history [account]="account$ | async" />
} @else {
<bit-empty-credential-history style="display: contents" />
}
</ng-container> </ng-container>
<ng-container bitDialogFooter> <ng-container bitDialogFooter>
<button <button

View File

@@ -1,4 +1,5 @@
<bit-item *ngFor="let credential of credentials$ | async"> @for (credential of credentials$ | async; track credential) {
<bit-item>
<bit-item-content> <bit-item-content>
<bit-color-password class="tw-font-mono" [password]="credential.credential" /> <bit-color-password class="tw-font-mono" [password]="credential.credential" />
<div slot="secondary"> <div slot="secondary">
@@ -19,4 +20,5 @@
</button> </button>
</bit-item-action> </bit-item-action>
</ng-container> </ng-container>
</bit-item> </bit-item>
}

View File

@@ -23,7 +23,6 @@ import {
import { AlgorithmsByType, CredentialGeneratorService } from "@bitwarden/generator-core"; import { AlgorithmsByType, CredentialGeneratorService } from "@bitwarden/generator-core";
import { GeneratedCredential, GeneratorHistoryService } from "@bitwarden/generator-history"; import { GeneratedCredential, GeneratorHistoryService } from "@bitwarden/generator-history";
import { GeneratorModule } from "./generator.module";
import { translate } from "./util"; import { translate } from "./util";
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
@@ -32,13 +31,12 @@ import { translate } from "./util";
selector: "bit-credential-generator-history", selector: "bit-credential-generator-history",
templateUrl: "credential-generator-history.component.html", templateUrl: "credential-generator-history.component.html",
imports: [ imports: [
ColorPasswordModule,
CommonModule, CommonModule,
ColorPasswordModule,
IconButtonModule, IconButtonModule,
NoItemsModule, NoItemsModule,
JslibModule, JslibModule,
ItemModule, ItemModule,
GeneratorModule,
], ],
}) })
export class CredentialGeneratorHistoryComponent implements OnChanges, OnInit, OnDestroy { export class CredentialGeneratorHistoryComponent implements OnChanges, OnInit, OnDestroy {

View File

@@ -6,9 +6,11 @@
(selectedChange)="onRootChanged({ nav: $event })" (selectedChange)="onRootChanged({ nav: $event })"
attr.aria-label="{{ 'type' | i18n }}" attr.aria-label="{{ 'type' | i18n }}"
> >
<bit-toggle *ngFor="let option of rootOptions$ | async" [value]="option.value"> @for (option of rootOptions$ | async; track option) {
<bit-toggle [value]="option.value">
{{ option.label }} {{ option.label }}
</bit-toggle> </bit-toggle>
}
</bit-toggle-group> </bit-toggle-group>
<nudge-generator-spotlight></nudge-generator-spotlight> <nudge-generator-spotlight></nudge-generator-spotlight>
@@ -40,19 +42,26 @@
></button> ></button>
</div> </div>
</bit-card> </bit-card>
<tools-password-settings @let showAlgorithm = showAlgorithm$ | async;
@let account = account$ | async;
@switch (showAlgorithm?.id) {
@case (Algorithm.password) {
<tools-password-settings
class="tw-mt-6" class="tw-mt-6"
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.password" [account]="account"
[account]="account$ | async"
(onUpdated)="generate('password settings')" (onUpdated)="generate('password settings')"
/> />
<tools-passphrase-settings }
@case (Algorithm.passphrase) {
<tools-passphrase-settings
class="tw-mt-6" class="tw-mt-6"
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.passphrase" [account]="account"
[account]="account$ | async"
(onUpdated)="generate('passphrase settings')" (onUpdated)="generate('passphrase settings')"
/> />
<bit-section *ngIf="(category$ | async) !== 'password'"> }
}
@if ((category$ | async) !== "password") {
<bit-section>
<bit-section-header> <bit-section-header>
<h2 bitTypography="h6">{{ "options" | i18n }}</h2> <h2 bitTypography="h6">{{ "options" | i18n }}</h2>
</bit-section-header> </bit-section-header>
@@ -67,12 +76,13 @@
data-testid="username-type" data-testid="username-type"
> >
</bit-select> </bit-select>
<bit-hint *ngIf="!!(credentialTypeHint$ | async)">{{ @if (credentialTypeHint$ | async) {
credentialTypeHint$ | async <bit-hint>{{ credentialTypeHint$ | async }}</bit-hint>
}}</bit-hint> }
</bit-form-field> </bit-form-field>
</form> </form>
<form *ngIf="showForwarder$ | async" [formGroup]="forwarder" class="tw-container"> @if (showForwarder$ | async) {
<form [formGroup]="forwarder" class="tw-container">
<bit-form-field> <bit-form-field>
<bit-label>{{ "service" | i18n }}</bit-label> <bit-label>{{ "service" | i18n }}</bit-label>
<bit-select <bit-select
@@ -83,26 +93,29 @@
</bit-select> </bit-select>
</bit-form-field> </bit-form-field>
</form> </form>
}
@if (showAlgorithm?.id === Algorithm.catchall) {
<tools-catchall-settings <tools-catchall-settings
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.catchall" [account]="account"
[account]="account$ | async"
(onUpdated)="generate('catchall settings')" (onUpdated)="generate('catchall settings')"
/> />
<tools-forwarder-settings }
*ngIf="!!(forwarderId$ | async)" @if (forwarderId$ | async; as forwarderId) {
[account]="account$ | async" <tools-forwarder-settings [account]="account" [forwarder]="forwarderId" />
[forwarder]="forwarderId$ | async" }
/> @if (showAlgorithm?.id === Algorithm.plusAddress) {
<tools-subaddress-settings <tools-subaddress-settings
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.plusAddress" [account]="account"
[account]="account$ | async"
(onUpdated)="generate('subaddress settings')" (onUpdated)="generate('subaddress settings')"
/> />
}
@if (showAlgorithm?.id === Algorithm.username) {
<tools-username-settings <tools-username-settings
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.username" [account]="account"
[account]="account$ | async"
(onUpdated)="generate('username settings')" (onUpdated)="generate('username settings')"
/> />
}
</bit-card> </bit-card>
</div> </div>
</bit-section> </bit-section>
}

View File

@@ -1,4 +1,5 @@
import { LiveAnnouncer } from "@angular/cdk/a11y"; import { LiveAnnouncer } from "@angular/cdk/a11y";
import { AsyncPipe } from "@angular/common";
import { import {
Component, Component,
EventEmitter, EventEmitter,
@@ -10,7 +11,7 @@ import {
Output, Output,
SimpleChanges, SimpleChanges,
} from "@angular/core"; } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { import {
BehaviorSubject, BehaviorSubject,
catchError, catchError,
@@ -27,6 +28,7 @@ import {
withLatestFrom, withLatestFrom,
} from "rxjs"; } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -37,7 +39,23 @@ import {
ifEnabledSemanticLoggerProvider, ifEnabledSemanticLoggerProvider,
} from "@bitwarden/common/tools/log"; } from "@bitwarden/common/tools/log";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
import { ToastService, Option } from "@bitwarden/components"; import {
ToastService,
Option,
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
CopyClickDirective,
SectionComponent,
SectionHeaderComponent,
ToggleGroupModule,
TypographyModule,
FormFieldModule,
SelectModule,
} from "@bitwarden/components";
import { import {
CredentialType, CredentialType,
CredentialGeneratorService, CredentialGeneratorService,
@@ -55,7 +73,15 @@ import {
Type, Type,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { GeneratorHistoryService } from "@bitwarden/generator-history"; import { GeneratorHistoryService } from "@bitwarden/generator-history";
import { I18nPipe } from "@bitwarden/ui-common";
import { CatchallSettingsComponent } from "./catchall-settings.component";
import { ForwarderSettingsComponent } from "./forwarder-settings.component";
import { NudgeGeneratorSpotlightComponent } from "./nudge-generator-spotlight.component";
import { PassphraseSettingsComponent } from "./passphrase-settings.component";
import { PasswordSettingsComponent } from "./password-settings.component";
import { SubaddressSettingsComponent } from "./subaddress-settings.component";
import { UsernameSettingsComponent } from "./username-settings.component";
import { translate } from "./util"; import { translate } from "./util";
// constants used to identify navigation selections that are not // constants used to identify navigation selections that are not
@@ -69,7 +95,32 @@ const NONE_SELECTED = "none";
@Component({ @Component({
selector: "tools-credential-generator", selector: "tools-credential-generator",
templateUrl: "credential-generator.component.html", templateUrl: "credential-generator.component.html",
standalone: false, imports: [
ToggleGroupModule,
NudgeGeneratorSpotlightComponent,
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
CopyClickDirective,
PasswordSettingsComponent,
PassphraseSettingsComponent,
SectionComponent,
SectionHeaderComponent,
TypographyModule,
ReactiveFormsModule,
FormFieldModule,
SelectModule,
CatchallSettingsComponent,
ForwarderSettingsComponent,
SubaddressSettingsComponent,
UsernameSettingsComponent,
AsyncPipe,
JslibModule,
I18nPipe,
],
}) })
export class CredentialGeneratorComponent implements OnInit, OnChanges, OnDestroy { export class CredentialGeneratorComponent implements OnInit, OnChanges, OnDestroy {
private readonly destroyed = new Subject<void>(); private readonly destroyed = new Subject<void>();

View File

@@ -1,5 +1,6 @@
<form [formGroup]="settings" class="tw-container"> <form [formGroup]="settings" class="tw-container">
<bit-form-field *ngIf="displayDomain"> @if (displayDomain) {
<bit-form-field>
<bit-label>{{ "forwarderDomainName" | i18n }}</bit-label> <bit-label>{{ "forwarderDomainName" | i18n }}</bit-label>
<input <input
bitInput bitInput
@@ -10,7 +11,9 @@
/> />
<bit-hint>{{ "forwarderDomainNameHint" | i18n }}</bit-hint> <bit-hint>{{ "forwarderDomainNameHint" | i18n }}</bit-hint>
</bit-form-field> </bit-form-field>
<bit-form-field *ngIf="displayToken"> }
@if (displayToken) {
<bit-form-field>
<bit-label>{{ "apiKey" | i18n }}</bit-label> <bit-label>{{ "apiKey" | i18n }}</bit-label>
<input bitInput formControlName="token" type="password" (change)="save('password')" /> <input bitInput formControlName="token" type="password" (change)="save('password')" />
<button <button
@@ -21,8 +24,11 @@
(change)="save('token')" (change)="save('token')"
></button> ></button>
</bit-form-field> </bit-form-field>
<bit-form-field *ngIf="displayBaseUrl" disableMargin> }
@if (displayBaseUrl) {
<bit-form-field disableMargin>
<bit-label>{{ "selfHostBaseUrl" | i18n }}</bit-label> <bit-label>{{ "selfHostBaseUrl" | i18n }}</bit-label>
<input bitInput formControlName="baseUrl" type="text" (change)="save('baseUrl')" /> <input bitInput formControlName="baseUrl" type="text" (change)="save('baseUrl')" />
</bit-form-field> </bit-form-field>
}
</form> </form>

View File

@@ -8,16 +8,24 @@ import {
Output, Output,
SimpleChanges, SimpleChanges,
} from "@angular/core"; } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { map, ReplaySubject, skip, Subject, switchAll, takeUntil, withLatestFrom } from "rxjs"; import { map, ReplaySubject, skip, Subject, switchAll, takeUntil, withLatestFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { VendorId } from "@bitwarden/common/tools/extension"; import { VendorId } from "@bitwarden/common/tools/extension";
import {
FormFieldModule,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
} from "@bitwarden/components";
import { import {
CredentialGeneratorService, CredentialGeneratorService,
ForwarderOptions, ForwarderOptions,
GeneratorMetadata, GeneratorMetadata,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
const Controls = Object.freeze({ const Controls = Object.freeze({
domain: "domain", domain: "domain",
@@ -31,7 +39,15 @@ const Controls = Object.freeze({
@Component({ @Component({
selector: "tools-forwarder-settings", selector: "tools-forwarder-settings",
templateUrl: "forwarder-settings.component.html", templateUrl: "forwarder-settings.component.html",
standalone: false, imports: [
ReactiveFormsModule,
FormFieldModule,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
JslibModule,
I18nPipe,
],
}) })
export class ForwarderSettingsComponent implements OnInit, OnChanges, OnDestroy { export class ForwarderSettingsComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the component /** Instantiates the component

View File

@@ -1,67 +1,13 @@
import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core"; import { NgModule } from "@angular/core";
import { ReactiveFormsModule } from "@angular/forms";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import {
CardComponent,
ColorPasswordModule,
CheckboxModule,
FormFieldModule,
IconButtonModule,
InputModule,
ItemModule,
SectionComponent,
SectionHeaderComponent,
SelectModule,
ToggleGroupModule,
TypographyModule,
} from "@bitwarden/components";
import { CatchallSettingsComponent } from "./catchall-settings.component";
import { CredentialGeneratorComponent } from "./credential-generator.component"; import { CredentialGeneratorComponent } from "./credential-generator.component";
import { ForwarderSettingsComponent } from "./forwarder-settings.component";
import { GeneratorServicesModule } from "./generator-services.module";
import { NudgeGeneratorSpotlightComponent } from "./nudge-generator-spotlight.component";
import { PassphraseSettingsComponent } from "./passphrase-settings.component";
import { PasswordGeneratorComponent } from "./password-generator.component"; import { PasswordGeneratorComponent } from "./password-generator.component";
import { PasswordSettingsComponent } from "./password-settings.component";
import { SubaddressSettingsComponent } from "./subaddress-settings.component";
import { UsernameGeneratorComponent } from "./username-generator.component"; import { UsernameGeneratorComponent } from "./username-generator.component";
import { UsernameSettingsComponent } from "./username-settings.component";
/** Shared module containing generator component dependencies */ /** Shared module containing generator component dependencies */
/** @deprecated Use individual components instead. */
@NgModule({ @NgModule({
imports: [ imports: [CredentialGeneratorComponent, PasswordGeneratorComponent, UsernameGeneratorComponent],
CardComponent,
ColorPasswordModule,
CheckboxModule,
CommonModule,
FormFieldModule,
GeneratorServicesModule,
IconButtonModule,
InputModule,
ItemModule,
JslibModule,
ReactiveFormsModule,
SectionComponent,
SectionHeaderComponent,
SelectModule,
ToggleGroupModule,
TypographyModule,
NudgeGeneratorSpotlightComponent,
],
declarations: [
CatchallSettingsComponent,
CredentialGeneratorComponent,
ForwarderSettingsComponent,
SubaddressSettingsComponent,
PasswordGeneratorComponent,
PassphraseSettingsComponent,
PasswordSettingsComponent,
UsernameGeneratorComponent,
UsernameSettingsComponent,
],
exports: [CredentialGeneratorComponent, PasswordGeneratorComponent, UsernameGeneratorComponent], exports: [CredentialGeneratorComponent, PasswordGeneratorComponent, UsernameGeneratorComponent],
}) })
export class GeneratorModule { export class GeneratorModule {

View File

@@ -1,5 +1,15 @@
/**
* This file contains the public interface for the generator components library.
*
* Be mindful of what you export here, as those components should be considered stable
* and part of the public API contract.
*/
export { CredentialGeneratorComponent } from "./credential-generator.component";
export { CredentialGeneratorHistoryComponent } from "./credential-generator-history.component"; export { CredentialGeneratorHistoryComponent } from "./credential-generator-history.component";
export { CredentialGeneratorHistoryDialogComponent } from "./credential-generator-history-dialog.component"; export { CredentialGeneratorHistoryDialogComponent } from "./credential-generator-history-dialog.component";
export { EmptyCredentialHistoryComponent } from "./empty-credential-history.component"; export { EmptyCredentialHistoryComponent } from "./empty-credential-history.component";
export { GeneratorModule } from "./generator.module"; export { GeneratorModule } from "./generator.module";
export { GeneratorServicesModule, SYSTEM_SERVICE_PROVIDER } from "./generator-services.module"; export { GeneratorServicesModule, SYSTEM_SERVICE_PROVIDER } from "./generator-services.module";
export { PasswordGeneratorComponent } from "./password-generator.component";
export { UsernameGeneratorComponent } from "./username-generator.component";

View File

@@ -1,4 +1,5 @@
<div class="tw-mb-4" *ngIf="showGeneratorSpotlight$ | async"> @if (showGeneratorSpotlight$ | async) {
<div class="tw-mb-4">
<bit-spotlight <bit-spotlight
[title]="'generatorNudgeTitle' | i18n" [title]="'generatorNudgeTitle' | i18n"
(onDismiss)="dismissGeneratorSpotlight(NudgeType.GeneratorNudgeStatus)" (onDismiss)="dismissGeneratorSpotlight(NudgeType.GeneratorNudgeStatus)"
@@ -13,4 +14,5 @@
</span> </span>
</p> </p>
</bit-spotlight> </bit-spotlight>
</div> </div>
}

View File

@@ -1,7 +1,9 @@
<bit-section [disableMargin]="disableMargin"> <bit-section [disableMargin]="disableMargin">
<bit-section-header *ngIf="showHeader"> @if (showHeader) {
<bit-section-header>
<h6 bitTypography="h6">{{ "options" | i18n }}</h6> <h6 bitTypography="h6">{{ "options" | i18n }}</h6>
</bit-section-header> </bit-section-header>
}
<form [formGroup]="settings" class="tw-container"> <form [formGroup]="settings" class="tw-container">
<div class="tw-mb-4"> <div class="tw-mb-4">
<bit-card> <bit-card>
@@ -51,7 +53,9 @@
/> />
<bit-label>{{ "includeNumber" | i18n }}</bit-label> <bit-label>{{ "includeNumber" | i18n }}</bit-label>
</bit-form-control> </bit-form-control>
<p *ngIf="policyInEffect" bitTypography="helper">{{ "generatorPolicyInEffect" | i18n }}</p> @if (policyInEffect) {
<p bitTypography="helper">{{ "generatorPolicyInEffect" | i18n }}</p>
}
</bit-card> </bit-card>
</div> </div>
</form> </form>

View File

@@ -1,4 +1,5 @@
import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { AsyncPipe } from "@angular/common";
import { import {
OnInit, OnInit,
Input, Input,
@@ -9,9 +10,10 @@ import {
SimpleChanges, SimpleChanges,
OnChanges, OnChanges,
} from "@angular/core"; } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { skip, takeUntil, Subject, map, withLatestFrom, ReplaySubject, tap } from "rxjs"; import { skip, takeUntil, Subject, map, withLatestFrom, ReplaySubject, tap } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -20,11 +22,21 @@ import {
disabledSemanticLoggerProvider, disabledSemanticLoggerProvider,
ifEnabledSemanticLoggerProvider, ifEnabledSemanticLoggerProvider,
} from "@bitwarden/common/tools/log"; } from "@bitwarden/common/tools/log";
import {
SectionComponent,
SectionHeaderComponent,
BaseCardDirective,
CardComponent,
TypographyModule,
FormFieldModule,
CheckboxModule,
} from "@bitwarden/components";
import { import {
CredentialGeneratorService, CredentialGeneratorService,
PassphraseGenerationOptions, PassphraseGenerationOptions,
BuiltIn, BuiltIn,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
const Controls = Object.freeze({ const Controls = Object.freeze({
numWords: "numWords", numWords: "numWords",
@@ -39,7 +51,19 @@ const Controls = Object.freeze({
@Component({ @Component({
selector: "tools-passphrase-settings", selector: "tools-passphrase-settings",
templateUrl: "passphrase-settings.component.html", templateUrl: "passphrase-settings.component.html",
standalone: false, imports: [
SectionComponent,
SectionHeaderComponent,
TypographyModule,
ReactiveFormsModule,
BaseCardDirective,
CardComponent,
FormFieldModule,
CheckboxModule,
AsyncPipe,
JslibModule,
I18nPipe,
],
}) })
export class PassphraseSettingsComponent implements OnInit, OnChanges, OnDestroy { export class PassphraseSettingsComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the component /** Instantiates the component

View File

@@ -1,15 +1,18 @@
<bit-toggle-group @if (showCredentialTypes$ | async) {
<bit-toggle-group
fullWidth fullWidth
class="tw-mb-4" class="tw-mb-4"
[selected]="credentialType$ | async" [selected]="credentialType$ | async"
(selectedChange)="onCredentialTypeChanged($event)" (selectedChange)="onCredentialTypeChanged($event)"
*ngIf="showCredentialTypes$ | async"
attr.aria-label="{{ 'type' | i18n }}" attr.aria-label="{{ 'type' | i18n }}"
> >
<bit-toggle *ngFor="let option of passwordOptions$ | async" [value]="option.value"> @for (option of passwordOptions$ | async; track option) {
<bit-toggle [value]="option.value">
{{ option.label }} {{ option.label }}
</bit-toggle> </bit-toggle>
</bit-toggle-group> }
</bit-toggle-group>
}
<bit-card class="tw-flex tw-justify-between tw-mb-4"> <bit-card class="tw-flex tw-justify-between tw-mb-4">
<div class="tw-grow tw-flex tw-items-center tw-min-w-0"> <div class="tw-grow tw-flex tw-items-center tw-min-w-0">
<bit-color-password class="tw-font-mono" [password]="value$ | async"></bit-color-password> <bit-color-password class="tw-font-mono" [password]="value$ | async"></bit-color-password>
@@ -37,17 +40,19 @@
></button> ></button>
</div> </div>
</bit-card> </bit-card>
<tools-password-settings @if ((algorithm$ | async)?.id === Algorithm.password) {
<tools-password-settings
class="tw-mt-6" class="tw-mt-6"
*ngIf="(algorithm$ | async)?.id === Algorithm.password"
[account]="account$ | async" [account]="account$ | async"
[disableMargin]="disableMargin" [disableMargin]="disableMargin"
(onUpdated)="generate('password settings')" (onUpdated)="generate('password settings')"
/> />
<tools-passphrase-settings }
@if ((algorithm$ | async)?.id === Algorithm.passphrase) {
<tools-passphrase-settings
class="tw-mt-6" class="tw-mt-6"
*ngIf="(algorithm$ | async)?.id === Algorithm.passphrase"
[account]="account$ | async" [account]="account$ | async"
(onUpdated)="generate('passphrase settings')" (onUpdated)="generate('passphrase settings')"
[disableMargin]="disableMargin" [disableMargin]="disableMargin"
/> />
}

View File

@@ -1,5 +1,6 @@
import { LiveAnnouncer } from "@angular/cdk/a11y"; import { LiveAnnouncer } from "@angular/cdk/a11y";
import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { AsyncPipe } from "@angular/common";
import { import {
Component, Component,
EventEmitter, EventEmitter,
@@ -24,6 +25,7 @@ import {
withLatestFrom, withLatestFrom,
} from "rxjs"; } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -33,7 +35,18 @@ import {
ifEnabledSemanticLoggerProvider, ifEnabledSemanticLoggerProvider,
} from "@bitwarden/common/tools/log"; } from "@bitwarden/common/tools/log";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
import { ToastService, Option } from "@bitwarden/components"; import {
ToastService,
Option,
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
CopyClickDirective,
ToggleGroupModule,
} from "@bitwarden/components";
import { import {
CredentialGeneratorService, CredentialGeneratorService,
GeneratedCredential, GeneratedCredential,
@@ -49,7 +62,10 @@ import {
Profile, Profile,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { GeneratorHistoryService } from "@bitwarden/generator-history"; import { GeneratorHistoryService } from "@bitwarden/generator-history";
import { I18nPipe } from "@bitwarden/ui-common";
import { PassphraseSettingsComponent } from "./passphrase-settings.component";
import { PasswordSettingsComponent } from "./password-settings.component";
import { toAlgorithmInfo, translate } from "./util"; import { toAlgorithmInfo, translate } from "./util";
/** Options group for passwords */ /** Options group for passwords */
@@ -58,7 +74,21 @@ import { toAlgorithmInfo, translate } from "./util";
@Component({ @Component({
selector: "tools-password-generator", selector: "tools-password-generator",
templateUrl: "password-generator.component.html", templateUrl: "password-generator.component.html",
standalone: false, imports: [
ToggleGroupModule,
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
CopyClickDirective,
PasswordSettingsComponent,
PassphraseSettingsComponent,
AsyncPipe,
JslibModule,
I18nPipe,
],
}) })
export class PasswordGeneratorComponent implements OnInit, OnChanges, OnDestroy { export class PasswordGeneratorComponent implements OnInit, OnChanges, OnDestroy {
constructor( constructor(

View File

@@ -1,7 +1,9 @@
<bit-section [disableMargin]="disableMargin"> <bit-section [disableMargin]="disableMargin">
<bit-section-header *ngIf="showHeader"> @if (showHeader) {
<bit-section-header>
<h2 bitTypography="h6">{{ "options" | i18n }}</h2> <h2 bitTypography="h6">{{ "options" | i18n }}</h2>
</bit-section-header> </bit-section-header>
}
<form [formGroup]="settings" class="tw-container"> <form [formGroup]="settings" class="tw-container">
<div class="tw-mb-4"> <div class="tw-mb-4">
<bit-card> <bit-card>
@@ -97,7 +99,9 @@
/> />
<bit-label>{{ "avoidAmbiguous" | i18n }}</bit-label> <bit-label>{{ "avoidAmbiguous" | i18n }}</bit-label>
</bit-form-control> </bit-form-control>
<p *ngIf="policyInEffect" bitTypography="helper">{{ "generatorPolicyInEffect" | i18n }}</p> @if (policyInEffect) {
<p bitTypography="helper">{{ "generatorPolicyInEffect" | i18n }}</p>
}
</bit-card> </bit-card>
</div> </div>
</form> </form>

View File

@@ -1,4 +1,5 @@
import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { AsyncPipe } from "@angular/common";
import { import {
OnInit, OnInit,
Input, Input,
@@ -9,16 +10,27 @@ import {
SimpleChanges, SimpleChanges,
OnChanges, OnChanges,
} from "@angular/core"; } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { takeUntil, Subject, map, filter, tap, skip, ReplaySubject, withLatestFrom } from "rxjs"; import { takeUntil, Subject, map, filter, tap, skip, ReplaySubject, withLatestFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import {
SectionComponent,
SectionHeaderComponent,
BaseCardDirective,
CardComponent,
FormFieldModule,
TypographyModule,
CheckboxModule,
} from "@bitwarden/components";
import { import {
CredentialGeneratorService, CredentialGeneratorService,
PasswordGenerationOptions, PasswordGenerationOptions,
BuiltIn, BuiltIn,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
import { hasRangeOfValues } from "./util"; import { hasRangeOfValues } from "./util";
@@ -39,7 +51,19 @@ const Controls = Object.freeze({
@Component({ @Component({
selector: "tools-password-settings", selector: "tools-password-settings",
templateUrl: "password-settings.component.html", templateUrl: "password-settings.component.html",
standalone: false, imports: [
SectionComponent,
SectionHeaderComponent,
TypographyModule,
ReactiveFormsModule,
BaseCardDirective,
CardComponent,
FormFieldModule,
CheckboxModule,
AsyncPipe,
JslibModule,
I18nPipe,
],
}) })
export class PasswordSettingsComponent implements OnInit, OnChanges, OnDestroy { export class PasswordSettingsComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the component /** Instantiates the component

View File

@@ -8,15 +8,18 @@ import {
Output, Output,
SimpleChanges, SimpleChanges,
} from "@angular/core"; } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { map, ReplaySubject, skip, Subject, takeUntil, withLatestFrom } from "rxjs"; import { map, ReplaySubject, skip, Subject, takeUntil, withLatestFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { FormFieldModule } from "@bitwarden/components";
import { import {
CredentialGeneratorService, CredentialGeneratorService,
BuiltIn, BuiltIn,
SubaddressGenerationOptions, SubaddressGenerationOptions,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
/** Options group for plus-addressed emails */ /** Options group for plus-addressed emails */
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
@@ -24,7 +27,7 @@ import {
@Component({ @Component({
selector: "tools-subaddress-settings", selector: "tools-subaddress-settings",
templateUrl: "subaddress-settings.component.html", templateUrl: "subaddress-settings.component.html",
standalone: false, imports: [ReactiveFormsModule, FormFieldModule, JslibModule, I18nPipe],
}) })
export class SubaddressSettingsComponent implements OnInit, OnChanges, OnDestroy { export class SubaddressSettingsComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the component /** Instantiates the component

View File

@@ -42,12 +42,13 @@
data-testid="username-type" data-testid="username-type"
> >
</bit-select> </bit-select>
<bit-hint *ngIf="!!(credentialTypeHint$ | async)">{{ @if (credentialTypeHint$ | async) {
credentialTypeHint$ | async <bit-hint>{{ credentialTypeHint$ | async }}</bit-hint>
}}</bit-hint> }
</bit-form-field> </bit-form-field>
</form> </form>
<form *ngIf="showForwarder$ | async" [formGroup]="forwarder" class="tw-container"> @if (showForwarder$ | async) {
<form [formGroup]="forwarder" class="tw-container">
<bit-form-field> <bit-form-field>
<bit-label>{{ "service" | i18n }}</bit-label> <bit-label>{{ "service" | i18n }}</bit-label>
<bit-select <bit-select
@@ -58,26 +59,24 @@
</bit-select> </bit-select>
</bit-form-field> </bit-form-field>
</form> </form>
<tools-catchall-settings }
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.catchall" @let showAlgorithm = showAlgorithm$ | async;
[account]="account$ | async" @let account = account$ | async;
(onUpdated)="generate('catchall settings')" @if (showAlgorithm?.id === Algorithm.catchall) {
/> <tools-catchall-settings [account]="account" (onUpdated)="generate('catchall settings')" />
<tools-forwarder-settings }
*ngIf="!!(forwarderId$ | async)" @if (forwarderId$ | async; as forwarderId) {
[forwarder]="forwarderId$ | async" <tools-forwarder-settings [forwarder]="forwarderId" [account]="account" />
[account]="account$ | async" }
/> @if (showAlgorithm?.id === Algorithm.plusAddress) {
<tools-subaddress-settings <tools-subaddress-settings
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.plusAddress" [account]="account"
[account]="account$ | async"
(onUpdated)="generate('subaddress settings')" (onUpdated)="generate('subaddress settings')"
/> />
<tools-username-settings }
*ngIf="(showAlgorithm$ | async)?.id === Algorithm.username" @if (showAlgorithm?.id === Algorithm.username) {
[account]="account$ | async" <tools-username-settings [account]="account" (onUpdated)="generate('username settings')" />
(onUpdated)="generate('username settings')" }
/>
</bit-card> </bit-card>
</div> </div>
</bit-section> </bit-section>

View File

@@ -1,5 +1,6 @@
import { LiveAnnouncer } from "@angular/cdk/a11y"; import { LiveAnnouncer } from "@angular/cdk/a11y";
import { coerceBooleanProperty } from "@angular/cdk/coercion"; import { coerceBooleanProperty } from "@angular/cdk/coercion";
import { NgClass, AsyncPipe } from "@angular/common";
import { import {
Component, Component,
EventEmitter, EventEmitter,
@@ -11,7 +12,7 @@ import {
Output, Output,
SimpleChanges, SimpleChanges,
} from "@angular/core"; } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { import {
BehaviorSubject, BehaviorSubject,
catchError, catchError,
@@ -28,6 +29,7 @@ import {
withLatestFrom, withLatestFrom,
} from "rxjs"; } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
@@ -38,7 +40,22 @@ import {
ifEnabledSemanticLoggerProvider, ifEnabledSemanticLoggerProvider,
} from "@bitwarden/common/tools/log"; } from "@bitwarden/common/tools/log";
import { UserId } from "@bitwarden/common/types/guid"; import { UserId } from "@bitwarden/common/types/guid";
import { ToastService, Option } from "@bitwarden/components"; import {
ToastService,
Option,
AriaDisableDirective,
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
CopyClickDirective,
BitIconButtonComponent,
TooltipDirective,
SectionComponent,
SectionHeaderComponent,
SelectComponent,
TypographyModule,
FormFieldModule,
} from "@bitwarden/components";
import { import {
AlgorithmInfo, AlgorithmInfo,
CredentialGeneratorService, CredentialGeneratorService,
@@ -55,7 +72,12 @@ import {
Algorithm, Algorithm,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { GeneratorHistoryService } from "@bitwarden/generator-history"; import { GeneratorHistoryService } from "@bitwarden/generator-history";
import { I18nPipe } from "@bitwarden/ui-common";
import { CatchallSettingsComponent } from "./catchall-settings.component";
import { ForwarderSettingsComponent } from "./forwarder-settings.component";
import { SubaddressSettingsComponent } from "./subaddress-settings.component";
import { UsernameSettingsComponent } from "./username-settings.component";
import { toAlgorithmInfo, translate } from "./util"; import { toAlgorithmInfo, translate } from "./util";
// constants used to identify navigation selections that are not // constants used to identify navigation selections that are not
@@ -69,7 +91,29 @@ const NONE_SELECTED = "none";
@Component({ @Component({
selector: "tools-username-generator", selector: "tools-username-generator",
templateUrl: "username-generator.component.html", templateUrl: "username-generator.component.html",
standalone: false, imports: [
BaseCardDirective,
CardComponent,
ColorPasswordComponent,
AriaDisableDirective,
TooltipDirective,
BitIconButtonComponent,
CopyClickDirective,
SectionComponent,
SectionHeaderComponent,
TypographyModule,
NgClass,
ReactiveFormsModule,
SelectComponent,
FormFieldModule,
CatchallSettingsComponent,
ForwarderSettingsComponent,
SubaddressSettingsComponent,
UsernameSettingsComponent,
AsyncPipe,
JslibModule,
I18nPipe,
],
}) })
export class UsernameGeneratorComponent implements OnInit, OnChanges, OnDestroy { export class UsernameGeneratorComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the username generator /** Instantiates the username generator

View File

@@ -8,15 +8,18 @@ import {
Output, Output,
SimpleChanges, SimpleChanges,
} from "@angular/core"; } from "@angular/core";
import { FormBuilder } from "@angular/forms"; import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
import { map, ReplaySubject, skip, Subject, takeUntil, withLatestFrom } from "rxjs"; import { map, ReplaySubject, skip, Subject, takeUntil, withLatestFrom } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { Account } from "@bitwarden/common/auth/abstractions/account.service"; import { Account } from "@bitwarden/common/auth/abstractions/account.service";
import { FormFieldModule, CheckboxModule } from "@bitwarden/components";
import { import {
CredentialGeneratorService, CredentialGeneratorService,
EffUsernameGenerationOptions, EffUsernameGenerationOptions,
BuiltIn, BuiltIn,
} from "@bitwarden/generator-core"; } from "@bitwarden/generator-core";
import { I18nPipe } from "@bitwarden/ui-common";
/** Options group for usernames */ /** Options group for usernames */
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
@@ -24,7 +27,7 @@ import {
@Component({ @Component({
selector: "tools-username-settings", selector: "tools-username-settings",
templateUrl: "username-settings.component.html", templateUrl: "username-settings.component.html",
standalone: false, imports: [ReactiveFormsModule, FormFieldModule, CheckboxModule, JslibModule, I18nPipe],
}) })
export class UsernameSettingsComponent implements OnInit, OnChanges, OnDestroy { export class UsernameSettingsComponent implements OnInit, OnChanges, OnDestroy {
/** Instantiates the component /** Instantiates the component