1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-14 23:33:31 +00:00

Use bit-select (+ReactiveForms) in Autofill on page load settings. (#13593)

* Use bit-select (+ReactiveForms) in Autofill on page load settings.

* Use ReactiveForms for Additional options.

* Disable margin reinclude from rebase.
This commit is contained in:
Miles Blackwood
2025-03-31 12:36:52 -04:00
committed by GitHub
parent 4e413283a8
commit 80e58427fb
2 changed files with 219 additions and 138 deletions

View File

@@ -154,121 +154,115 @@
</bit-item>
</bit-section>
<bit-section>
<bit-section-header>
<h2 bitTypography="h6">{{ "enableAutoFillOnPageLoadSectionTitle" | i18n }}</h2>
</bit-section-header>
<bit-card>
<bit-hint class="tw-mb-6 tw-text-sm">
{{ "enableAutoFillOnPageLoadDesc" | i18n }}
<span
><b>{{ "warningCapitalized" | i18n }}</b
>: {{ "experimentalFeature" | i18n }}</span
>
<a
bitLink
class="tw-no-underline"
href="https://bitwarden.com/help/auto-fill-browser/"
rel="noreferrer"
target="_blank"
>
{{ "learnMoreAboutAutofillOnPageLoadLinkText" | i18n }}
</a>
</bit-hint>
<bit-form-control>
<input
bitCheckbox
id="autofillOnPageLoad"
type="checkbox"
(change)="updateAutofillOnPageLoad()"
[(ngModel)]="enableAutofillOnPageLoad"
[disabled]="autofillOnPageLoadFromPolicy$ | async"
/>
<bit-label for="autofillOnPageLoad">{{ "enableAutoFillOnPageLoad" | i18n }}</bit-label>
<bit-hint class="tw-text-sm" *ngIf="autofillOnPageLoadFromPolicy$ | async">{{
"enterprisePolicyRequirementsApplied" | i18n
}}</bit-hint>
</bit-form-control>
<bit-form-field disableMargin>
<bit-label for="defaultAutofill">{{ "defaultAutoFillOnPageLoad" | i18n }}</bit-label>
<select
bitInput
id="defaultAutofill"
(change)="updateAutofillOnPageLoadDefault()"
[(ngModel)]="autofillOnPageLoadDefault"
[disabled]="!enableAutofillOnPageLoad"
>
<option
*ngFor="let o of autofillOnPageLoadOptions"
[ngValue]="o.value"
[label]="o.name"
></option>
</select>
<bit-hint class="tw-text-sm">
{{ "defaultAutoFillOnPageLoadDesc" | i18n }}
<form [formGroup]="autofillOnPageLoadForm">
<bit-section-header>
<legend>
<h2 bitTypography="h6">{{ "enableAutoFillOnPageLoadSectionTitle" | i18n }}</h2>
</legend>
</bit-section-header>
<bit-card>
<bit-hint class="tw-mb-6 tw-text-sm">
{{ "enableAutoFillOnPageLoadDesc" | i18n }}
<span
><b>{{ "warningCapitalized" | i18n }}</b
>: {{ "experimentalFeature" | i18n }}</span
>
<a
bitLink
class="tw-no-underline"
href="https://bitwarden.com/help/auto-fill-browser/"
rel="noreferrer"
target="_blank"
>
{{ "learnMoreAboutAutofillOnPageLoadLinkText" | i18n }}
</a>
</bit-hint>
</bit-form-field>
</bit-card>
<bit-form-control>
<input
formControlName="autofillOnPageLoad"
bitCheckbox
id="autofillOnPageLoad"
type="checkbox"
/>
<bit-label for="autofillOnPageLoad">{{ "enableAutoFillOnPageLoad" | i18n }}</bit-label>
<bit-hint class="tw-text-sm" *ngIf="autofillOnPageLoadFromPolicy$ | async">{{
"enterprisePolicyRequirementsApplied" | i18n
}}</bit-hint>
</bit-form-control>
<bit-form-field disableMargin>
<bit-label for="defaultAutofill">{{ "defaultAutoFillOnPageLoad" | i18n }}</bit-label>
<bit-select formControlName="defaultAutofill" bitInput id="defaultAutofill">
<bit-option
*ngFor="let option of autofillOnPageLoadOptions"
[label]="option.name"
[value]="option.value"
>
</bit-option>
</bit-select>
<bit-hint class="tw-text-sm">
{{ "defaultAutoFillOnPageLoadDesc" | i18n }}
</bit-hint>
</bit-form-field>
</bit-card>
</form>
</bit-section>
<bit-section [disableMargin]="!blockBrowserInjectionsByDomainEnabled">
<bit-section-header>
<h2 bitTypography="h6">{{ "additionalOptions" | i18n }}</h2>
</bit-section-header>
<bit-card>
<bit-form-control>
<input
bitCheckbox
id="context-menu"
type="checkbox"
(change)="updateContextMenuItem()"
[(ngModel)]="enableContextMenuItem"
/>
<bit-label for="context-menu">{{ "enableContextMenuItem" | i18n }}</bit-label>
</bit-form-control>
<bit-form-control>
<input
bitCheckbox
id="totp"
type="checkbox"
(change)="updateAutoTotpCopy()"
[(ngModel)]="enableAutoTotpCopy"
/>
<bit-label for="totp">{{ "enableAutoTotpCopy" | i18n }}</bit-label>
</bit-form-control>
<bit-form-field>
<bit-label for="clearClipboard">{{ "clearClipboard" | i18n }}</bit-label>
<select
aria-describedby="clearClipboardHelp"
bitInput
id="clearClipboard"
(change)="saveClearClipboard()"
[(ngModel)]="clearClipboard"
>
<option
*ngFor="let o of clearClipboardOptions"
[label]="o.name"
[ngValue]="o.value"
></option>
</select>
<bit-hint class="tw-text-sm" id="clearClipboardHelp">
{{ "clearClipboardDesc" | i18n }}
</bit-hint>
</bit-form-field>
<bit-form-field disableMargin>
<bit-label for="defaultUriMatch">{{ "defaultUriMatchDetection" | i18n }}</bit-label>
<select
aria-describedby="defaultUriMatchHelp"
bitInput
id="defaultUriMatch"
(change)="saveDefaultUriMatch()"
[(ngModel)]="defaultUriMatch"
>
<option *ngFor="let o of uriMatchOptions" [label]="o.name" [ngValue]="o.value"></option>
</select>
<bit-hint class="tw-text-sm" id="defaultUriMatchHelp">
{{ "defaultUriMatchDetectionDesc" | i18n }}
</bit-hint>
</bit-form-field>
</bit-card>
<form [formGroup]="additionalOptionsForm">
<bit-section-header>
<h2 bitTypography="h6">{{ "additionalOptions" | i18n }}</h2>
</bit-section-header>
<bit-card>
<bit-form-control>
<input
formControlName="enableContextMenuItem"
bitCheckbox
id="context-menu"
type="checkbox"
/>
<bit-label for="context-menu">{{ "enableContextMenuItem" | i18n }}</bit-label>
</bit-form-control>
<bit-form-control>
<input formControlName="enableAutoTotpCopy" bitCheckbox id="totp" type="checkbox" />
<bit-label for="totp">{{ "enableAutoTotpCopy" | i18n }}</bit-label>
</bit-form-control>
<bit-form-field>
<bit-label for="clearClipboard">{{ "clearClipboard" | i18n }}</bit-label>
<bit-select
formControlName="clearClipboard"
aria-describedby="clearClipboardHelp"
bitInput
id="clearClipboard"
>
<bit-option
*ngFor="let option of clearClipboardOptions"
[label]="option.name"
[value]="option.value"
></bit-option>
</bit-select>
<bit-hint class="tw-text-sm" id="clearClipboardHelp">
{{ "clearClipboardDesc" | i18n }}
</bit-hint>
</bit-form-field>
<bit-form-field disableMargin>
<bit-label for="defaultUriMatch">{{ "defaultUriMatchDetection" | i18n }}</bit-label>
<bit-select
formControlName="defaultUriMatch"
aria-describedby="defaultUriMatchHelp"
bitInput
id="defaultUriMatch"
>
<bit-option
*ngFor="let option of uriMatchOptions"
[label]="option.name"
[value]="option.value"
></bit-option>
</bit-select>
<bit-hint class="tw-text-sm" id="defaultUriMatchHelp">
{{ "defaultUriMatchDetectionDesc" | i18n }}
</bit-hint>
</bit-form-field>
</bit-card>
</form>
</bit-section>
<bit-section *ngIf="blockBrowserInjectionsByDomainEnabled" disableMargin>
<bit-item>

View File

@@ -1,8 +1,15 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { CommonModule } from "@angular/common";
import { Component, OnInit } from "@angular/core";
import { FormsModule } from "@angular/forms";
import { Component, DestroyRef, OnInit } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import {
FormsModule,
ReactiveFormsModule,
FormBuilder,
FormGroup,
FormControl,
} from "@angular/forms";
import { RouterModule } from "@angular/router";
import { firstValueFrom } from "rxjs";
@@ -73,6 +80,7 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co
SectionHeaderComponent,
SelectModule,
TypographyModule,
ReactiveFormsModule,
],
})
export class AutofillComponent implements OnInit {
@@ -94,6 +102,18 @@ export class AutofillComponent implements OnInit {
protected autofillOnPageLoadFromPolicy$ =
this.autofillSettingsService.activateAutofillOnPageLoadFromPolicy$;
protected autofillOnPageLoadForm = new FormGroup({
autofillOnPageLoad: new FormControl(),
defaultAutofill: new FormControl(),
});
protected additionalOptionsForm = new FormGroup({
enableContextMenuItem: new FormControl(),
enableAutoTotpCopy: new FormControl(),
clearClipboard: new FormControl(),
defaultUriMatch: new FormControl(),
});
enableAutofillOnPageLoad: boolean = false;
enableInlineMenu: boolean = false;
enableInlineMenuOnIconSelect: boolean = false;
@@ -121,10 +141,12 @@ export class AutofillComponent implements OnInit {
private messagingService: MessagingService,
private vaultSettingsService: VaultSettingsService,
private configService: ConfigService,
private formBuilder: FormBuilder,
private destroyRef: DestroyRef,
) {
this.autofillOnPageLoadOptions = [
{ name: i18nService.t("autoFillOnPageLoadYes"), value: true },
{ name: i18nService.t("autoFillOnPageLoadNo"), value: false },
{ name: this.i18nService.t("autoFillOnPageLoadYes"), value: true },
{ name: this.i18nService.t("autoFillOnPageLoadNo"), value: false },
];
this.clearClipboardOptions = [
{ name: i18nService.t("never"), value: ClearClipboardDelay.Never },
@@ -181,27 +203,106 @@ export class AutofillComponent implements OnInit {
this.inlineMenuVisibility === AutofillOverlayVisibility.OnFieldFocus ||
this.enableInlineMenuOnIconSelect;
this.autofillSettingsService.activateAutofillOnPageLoadFromPolicy$
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((value) => {
value
? this.autofillOnPageLoadForm.controls.autofillOnPageLoad.disable({ emitEvent: false })
: this.autofillOnPageLoadForm.controls.autofillOnPageLoad.enable({ emitEvent: false });
});
this.enableAutofillOnPageLoad = await firstValueFrom(
this.autofillSettingsService.autofillOnPageLoad$,
);
this.autofillOnPageLoadForm.controls.autofillOnPageLoad.patchValue(
this.enableAutofillOnPageLoad,
{ emitEvent: false },
);
this.autofillOnPageLoadDefault = await firstValueFrom(
this.autofillSettingsService.autofillOnPageLoadDefault$,
);
if (this.enableAutofillOnPageLoad === false) {
this.autofillOnPageLoadForm.controls.defaultAutofill.disable();
}
this.autofillOnPageLoadForm.controls.defaultAutofill.patchValue(
this.autofillOnPageLoadDefault,
{ emitEvent: false },
);
this.autofillOnPageLoadForm.controls.autofillOnPageLoad.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((value) => {
void this.autofillSettingsService.setAutofillOnPageLoad(value);
this.enableDefaultAutofillControl(value);
});
this.autofillOnPageLoadForm.controls.defaultAutofill.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((value) => {
void this.autofillSettingsService.setAutofillOnPageLoadDefault(value);
});
/** Additional options form */
this.enableContextMenuItem = await firstValueFrom(
this.autofillSettingsService.enableContextMenu$,
);
this.additionalOptionsForm.controls.enableContextMenuItem.patchValue(
this.enableContextMenuItem,
{ emitEvent: false },
);
this.enableAutoTotpCopy = await firstValueFrom(this.autofillSettingsService.autoCopyTotp$);
this.additionalOptionsForm.controls.enableAutoTotpCopy.patchValue(this.enableAutoTotpCopy, {
emitEvent: false,
});
this.clearClipboard = await firstValueFrom(this.autofillSettingsService.clearClipboardDelay$);
this.additionalOptionsForm.controls.clearClipboard.patchValue(this.clearClipboard, {
emitEvent: false,
});
const defaultUriMatch = await firstValueFrom(
this.domainSettingsService.defaultUriMatchStrategy$,
);
this.defaultUriMatch = defaultUriMatch == null ? UriMatchStrategy.Domain : defaultUriMatch;
this.additionalOptionsForm.controls.defaultUriMatch.patchValue(this.defaultUriMatch, {
emitEvent: false,
});
this.additionalOptionsForm.controls.enableContextMenuItem.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((value) => {
void this.autofillSettingsService.setEnableContextMenu(value);
this.messagingService.send("bgUpdateContextMenu");
});
this.additionalOptionsForm.controls.enableAutoTotpCopy.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((value) => {
void this.autofillSettingsService.setAutoCopyTotp(value);
});
this.additionalOptionsForm.controls.clearClipboard.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((value) => {
void this.autofillSettingsService.setClearClipboardDelay(value);
});
this.additionalOptionsForm.controls.defaultUriMatch.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((value) => {
void this.domainSettingsService.setDefaultUriMatchStrategy(value);
});
const command = await this.platformUtilsService.getAutofillKeyboardShortcut();
await this.setAutofillKeyboardHelperText(command);
@@ -230,17 +331,16 @@ export class AutofillComponent implements OnInit {
await this.requestPrivacyPermission();
}
}
async updateAutofillOnPageLoad() {
await this.autofillSettingsService.setAutofillOnPageLoad(this.enableAutofillOnPageLoad);
async getAutofillOnPageLoadFromPolicy() {
await firstValueFrom(this.autofillOnPageLoadFromPolicy$);
}
async updateAutofillOnPageLoadDefault() {
await this.autofillSettingsService.setAutofillOnPageLoadDefault(this.autofillOnPageLoadDefault);
}
async saveDefaultUriMatch() {
await this.domainSettingsService.setDefaultUriMatchStrategy(this.defaultUriMatch);
enableDefaultAutofillControl(enable: boolean = true) {
if (enable) {
this.autofillOnPageLoadForm.controls.defaultAutofill.enable();
} else {
this.autofillOnPageLoadForm.controls.defaultAutofill.disable();
}
}
private async setAutofillKeyboardHelperText(command: string) {
@@ -388,19 +488,6 @@ export class AutofillComponent implements OnInit {
return await BrowserApi.permissionsGranted(["privacy"]);
}
async updateContextMenuItem() {
await this.autofillSettingsService.setEnableContextMenu(this.enableContextMenuItem);
this.messagingService.send("bgUpdateContextMenu");
}
async updateAutoTotpCopy() {
await this.autofillSettingsService.setAutoCopyTotp(this.enableAutoTotpCopy);
}
async saveClearClipboard() {
await this.autofillSettingsService.setClearClipboardDelay(this.clearClipboard);
}
async updateShowCardsCurrentTab() {
await this.vaultSettingsService.setShowCardsCurrentTab(this.showCardsCurrentTab);
}