diff --git a/apps/browser/src/autofill/popup/settings/autofill.component.html b/apps/browser/src/autofill/popup/settings/autofill.component.html index aa9c8648885..543c7482bf7 100644 --- a/apps/browser/src/autofill/popup/settings/autofill.component.html +++ b/apps/browser/src/autofill/popup/settings/autofill.component.html @@ -262,10 +262,15 @@ *ngFor="let option of uriMatchOptions" [label]="option.name" [value]="option.value" + [disabled]="option.disabled" > - - {{ "defaultUriMatchDetectionDesc" | i18n }} + + {{ hints[0] | i18n }} + + {{ "warningCapitalized" | i18n }}: + {{ hints[1] | i18n }} + diff --git a/apps/browser/src/autofill/popup/settings/autofill.component.ts b/apps/browser/src/autofill/popup/settings/autofill.component.ts index 852b79cad1d..19afb903cb8 100644 --- a/apps/browser/src/autofill/popup/settings/autofill.component.ts +++ b/apps/browser/src/autofill/popup/settings/autofill.component.ts @@ -11,7 +11,17 @@ import { ReactiveFormsModule, } from "@angular/forms"; import { RouterModule } from "@angular/router"; -import { filter, firstValueFrom, map, Observable, shareReplay, switchMap } from "rxjs"; +import { + concatMap, + filter, + firstValueFrom, + map, + Observable, + pairwise, + shareReplay, + startWith, + switchMap, +} from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { NudgesService, NudgeType } from "@bitwarden/angular/vault"; @@ -59,6 +69,7 @@ import { SelectModule, TypographyModule, } from "@bitwarden/components"; +import { AdvancedUriOptionDialogComponent } from "@bitwarden/vault"; import { AutofillBrowserSettingsService } from "../../../autofill/services/autofill-browser-settings.service"; import { BrowserApi } from "../../../platform/browser/browser-api"; @@ -131,6 +142,7 @@ export class AutofillComponent implements OnInit { defaultUriMatch: new FormControl(), }); + advancedOptionWarningMap: Partial>; enableAutofillOnPageLoad: boolean = false; enableInlineMenu: boolean = false; enableInlineMenuOnIconSelect: boolean = false; @@ -143,7 +155,7 @@ export class AutofillComponent implements OnInit { clearClipboard: ClearClipboardDelaySetting; clearClipboardOptions: { name: string; value: ClearClipboardDelaySetting }[]; defaultUriMatch: UriMatchStrategySetting = UriMatchStrategy.Domain; - uriMatchOptions: { name: string; value: UriMatchStrategySetting }[]; + uriMatchOptions: { name: string; value: UriMatchStrategySetting; disabled?: boolean }[]; showCardsCurrentTab: boolean = true; showIdentitiesCurrentTab: boolean = true; autofillKeyboardHelperText: string; @@ -181,11 +193,16 @@ export class AutofillComponent implements OnInit { this.uriMatchOptions = [ { name: i18nService.t("baseDomainOptionRecommended"), value: UriMatchStrategy.Domain }, { name: i18nService.t("host"), value: UriMatchStrategy.Host }, - { name: i18nService.t("startsWith"), value: UriMatchStrategy.StartsWith }, - { name: i18nService.t("regEx"), value: UriMatchStrategy.RegularExpression }, { name: i18nService.t("exact"), value: UriMatchStrategy.Exact }, { name: i18nService.t("never"), value: UriMatchStrategy.Never }, + { name: this.i18nService.t("uriAdvancedOption"), value: null, disabled: true }, + { name: i18nService.t("startsWith"), value: UriMatchStrategy.StartsWith }, + { name: i18nService.t("regEx"), value: UriMatchStrategy.RegularExpression }, ]; + this.advancedOptionWarningMap = { + [UriMatchStrategy.StartsWith]: "startsWithAdvancedOptionWarning", + [UriMatchStrategy.RegularExpression]: "regExAdvancedOptionWarning", + }; this.browserClientVendor = BrowserApi.getBrowserClientVendor(window); this.disablePasswordManagerURI = DisablePasswordManagerUris[this.browserClientVendor]; @@ -319,10 +336,13 @@ export class AutofillComponent implements OnInit { }); this.additionalOptionsForm.controls.defaultUriMatch.valueChanges - .pipe(takeUntilDestroyed(this.destroyRef)) - .subscribe((value) => { - void this.domainSettingsService.setDefaultUriMatchStrategy(value); - }); + .pipe( + startWith(this.defaultUriMatch), + pairwise(), + concatMap(([previous, current]) => this.handleAdvancedMatch(previous, current)), + takeUntilDestroyed(this.destroyRef), + ) + .subscribe(); const command = await this.platformUtilsService.getAutofillKeyboardShortcut(); await this.setAutofillKeyboardHelperText(command); @@ -510,6 +530,29 @@ export class AutofillComponent implements OnInit { await this.updateDefaultBrowserAutofillDisabled(); }; + private async handleAdvancedMatch( + previous: UriMatchStrategySetting | null, + current: UriMatchStrategySetting | null, + ): Promise { + const valueChange = previous !== current; + const isAdvanced = + current === UriMatchStrategy.StartsWith || current === UriMatchStrategy.RegularExpression; + if (!valueChange || !isAdvanced) { + return await this.domainSettingsService.setDefaultUriMatchStrategy(current); + } + AdvancedUriOptionDialogComponent.open(this.dialogService, { + contentKey: this.advancedOptionWarningMap[current], + onContinue: async () => { + this.additionalOptionsForm.controls.defaultUriMatch.setValue(current); + await this.domainSettingsService.setDefaultUriMatchStrategy(current); + }, + onCancel: async () => { + this.additionalOptionsForm.controls.defaultUriMatch.setValue(previous); + await this.domainSettingsService.setDefaultUriMatchStrategy(previous); + }, + }); + } + async privacyPermissionGranted(): Promise { return await BrowserApi.permissionsGranted(["privacy"]); } @@ -529,4 +572,17 @@ export class AutofillComponent implements OnInit { async updateShowInlineMenuIdentities() { await this.autofillSettingsService.setShowInlineMenuIdentities(this.showInlineMenuIdentities); } + + getMatchHints() { + const hints = ["uriMatchDefaultStrategyHint"]; + const strategy = this.additionalOptionsForm.get("defaultUriMatch") + ?.value as UriMatchStrategySetting; + if ( + strategy === UriMatchStrategy.StartsWith || + strategy === UriMatchStrategy.RegularExpression + ) { + hints.push(this.advancedOptionWarningMap[strategy]); + } + return hints; + } } diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index a139c0c712c..881d30bd171 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -3565,6 +3565,14 @@ "uriMatchWarningDialogLink": { "message": "More about match detection", "description": "Link to match detection docs on warning dialog for advance match strategy" + }, + "uriAdvancedOption":{ + "message": "Advanced options", + "description": "Advanced option placeholder for uri option component" + }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" }, "success": { "message": "Success" diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 9150028f4d6..b272dc32e3b 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -8941,6 +8941,14 @@ "uriMatchWarningDialogLink": { "message": "More about match detection", "description": "Link to match detection docs on warning dialog for advance match strategy" + }, + "uriAdvancedOption":{ + "message": "Advanced options", + "description": "Advanced option placeholder for uri option component" + }, + "warningCapitalized": { + "message": "Warning", + "description": "Warning (should maintain locale-relevant capitalization)" }, "maintainYourSubscription": { "message": "To maintain your subscription for $ORG$, ", diff --git a/libs/vault/src/cipher-form/components/autofill-options/advanced-uri-option-dialog.component.html b/libs/vault/src/cipher-form/components/autofill-options/advanced-uri-option-dialog.component.html index 627989a3397..6c792d240df 100644 --- a/libs/vault/src/cipher-form/components/autofill-options/advanced-uri-option-dialog.component.html +++ b/libs/vault/src/cipher-form/components/autofill-options/advanced-uri-option-dialog.component.html @@ -7,8 +7,8 @@ {{ "warningCapitalized" | i18n }} -
-

+

+

{{ contentKey | i18n }}