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

Pm 22882 display simple dialog when advanced matching strategy selected for global setting (#15531)

* PM-22882

* add bit hints

* export dialog and implement in autofill component

* remove unnecessary non null assertion

* set to previous on cancel

* add advanced options message to web and desktop

* tweak styling

* add warning capitalized to web and desktop
This commit is contained in:
Daniel Riera
2025-07-11 15:36:26 -04:00
committed by GitHub
parent a32f745c99
commit 4fade99be5
6 changed files with 90 additions and 12 deletions

View File

@@ -262,10 +262,15 @@
*ngFor="let option of uriMatchOptions" *ngFor="let option of uriMatchOptions"
[label]="option.name" [label]="option.name"
[value]="option.value" [value]="option.value"
[disabled]="option.disabled"
></bit-option> ></bit-option>
</bit-select> </bit-select>
<bit-hint class="tw-text-sm" id="defaultUriMatchHelp"> <bit-hint *ngIf="getMatchHints() as hints">
{{ "defaultUriMatchDetectionDesc" | i18n }} {{ hints[0] | i18n }}
<ng-container *ngIf="hints.length > 1">
<b>{{ "warningCapitalized" | i18n }}:</b>
{{ hints[1] | i18n }}
</ng-container>
</bit-hint> </bit-hint>
</bit-form-field> </bit-form-field>
</bit-card> </bit-card>

View File

@@ -11,7 +11,17 @@ import {
ReactiveFormsModule, ReactiveFormsModule,
} from "@angular/forms"; } from "@angular/forms";
import { RouterModule } from "@angular/router"; 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 { JslibModule } from "@bitwarden/angular/jslib.module";
import { NudgesService, NudgeType } from "@bitwarden/angular/vault"; import { NudgesService, NudgeType } from "@bitwarden/angular/vault";
@@ -59,6 +69,7 @@ import {
SelectModule, SelectModule,
TypographyModule, TypographyModule,
} from "@bitwarden/components"; } from "@bitwarden/components";
import { AdvancedUriOptionDialogComponent } from "@bitwarden/vault";
import { AutofillBrowserSettingsService } from "../../../autofill/services/autofill-browser-settings.service"; import { AutofillBrowserSettingsService } from "../../../autofill/services/autofill-browser-settings.service";
import { BrowserApi } from "../../../platform/browser/browser-api"; import { BrowserApi } from "../../../platform/browser/browser-api";
@@ -131,6 +142,7 @@ export class AutofillComponent implements OnInit {
defaultUriMatch: new FormControl(), defaultUriMatch: new FormControl(),
}); });
advancedOptionWarningMap: Partial<Record<UriMatchStrategySetting, string>>;
enableAutofillOnPageLoad: boolean = false; enableAutofillOnPageLoad: boolean = false;
enableInlineMenu: boolean = false; enableInlineMenu: boolean = false;
enableInlineMenuOnIconSelect: boolean = false; enableInlineMenuOnIconSelect: boolean = false;
@@ -143,7 +155,7 @@ export class AutofillComponent implements OnInit {
clearClipboard: ClearClipboardDelaySetting; clearClipboard: ClearClipboardDelaySetting;
clearClipboardOptions: { name: string; value: ClearClipboardDelaySetting }[]; clearClipboardOptions: { name: string; value: ClearClipboardDelaySetting }[];
defaultUriMatch: UriMatchStrategySetting = UriMatchStrategy.Domain; defaultUriMatch: UriMatchStrategySetting = UriMatchStrategy.Domain;
uriMatchOptions: { name: string; value: UriMatchStrategySetting }[]; uriMatchOptions: { name: string; value: UriMatchStrategySetting; disabled?: boolean }[];
showCardsCurrentTab: boolean = true; showCardsCurrentTab: boolean = true;
showIdentitiesCurrentTab: boolean = true; showIdentitiesCurrentTab: boolean = true;
autofillKeyboardHelperText: string; autofillKeyboardHelperText: string;
@@ -181,11 +193,16 @@ export class AutofillComponent implements OnInit {
this.uriMatchOptions = [ this.uriMatchOptions = [
{ name: i18nService.t("baseDomainOptionRecommended"), value: UriMatchStrategy.Domain }, { name: i18nService.t("baseDomainOptionRecommended"), value: UriMatchStrategy.Domain },
{ name: i18nService.t("host"), value: UriMatchStrategy.Host }, { 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("exact"), value: UriMatchStrategy.Exact },
{ name: i18nService.t("never"), value: UriMatchStrategy.Never }, { 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.browserClientVendor = BrowserApi.getBrowserClientVendor(window);
this.disablePasswordManagerURI = DisablePasswordManagerUris[this.browserClientVendor]; this.disablePasswordManagerURI = DisablePasswordManagerUris[this.browserClientVendor];
@@ -319,10 +336,13 @@ export class AutofillComponent implements OnInit {
}); });
this.additionalOptionsForm.controls.defaultUriMatch.valueChanges this.additionalOptionsForm.controls.defaultUriMatch.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef)) .pipe(
.subscribe((value) => { startWith(this.defaultUriMatch),
void this.domainSettingsService.setDefaultUriMatchStrategy(value); pairwise(),
}); concatMap(([previous, current]) => this.handleAdvancedMatch(previous, current)),
takeUntilDestroyed(this.destroyRef),
)
.subscribe();
const command = await this.platformUtilsService.getAutofillKeyboardShortcut(); const command = await this.platformUtilsService.getAutofillKeyboardShortcut();
await this.setAutofillKeyboardHelperText(command); await this.setAutofillKeyboardHelperText(command);
@@ -510,6 +530,29 @@ export class AutofillComponent implements OnInit {
await this.updateDefaultBrowserAutofillDisabled(); await this.updateDefaultBrowserAutofillDisabled();
}; };
private async handleAdvancedMatch(
previous: UriMatchStrategySetting | null,
current: UriMatchStrategySetting | null,
): Promise<void> {
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<boolean> { async privacyPermissionGranted(): Promise<boolean> {
return await BrowserApi.permissionsGranted(["privacy"]); return await BrowserApi.permissionsGranted(["privacy"]);
} }
@@ -529,4 +572,17 @@ export class AutofillComponent implements OnInit {
async updateShowInlineMenuIdentities() { async updateShowInlineMenuIdentities() {
await this.autofillSettingsService.setShowInlineMenuIdentities(this.showInlineMenuIdentities); 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;
}
} }

View File

@@ -3565,6 +3565,14 @@
"uriMatchWarningDialogLink": { "uriMatchWarningDialogLink": {
"message": "More about match detection", "message": "More about match detection",
"description": "Link to match detection docs on warning dialog for advance match strategy" "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": { "success": {
"message": "Success" "message": "Success"

View File

@@ -8941,6 +8941,14 @@
"uriMatchWarningDialogLink": { "uriMatchWarningDialogLink": {
"message": "More about match detection", "message": "More about match detection",
"description": "Link to match detection docs on warning dialog for advance match strategy" "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": { "maintainYourSubscription": {
"message": "To maintain your subscription for $ORG$, ", "message": "To maintain your subscription for $ORG$, ",

View File

@@ -7,8 +7,8 @@
<span bitDialogTitle> <span bitDialogTitle>
{{ "warningCapitalized" | i18n }} {{ "warningCapitalized" | i18n }}
</span> </span>
<div bitDialogContent> <div bitDialogContent class="tw-mb-1">
<p> <p class="tw-mb-1 tw-hyphens-none">
{{ contentKey | i18n }} {{ contentKey | i18n }}
<br /> <br />
<button bitLink type="button" linkType="primary" (click)="openLink($event)"> <button bitLink type="button" linkType="primary" (click)="openLink($event)">

View File

@@ -11,3 +11,4 @@ export { DefaultCipherFormConfigService } from "./services/default-cipher-form-c
export { CipherFormGeneratorComponent } from "./components/cipher-generator/cipher-form-generator.component"; export { CipherFormGeneratorComponent } from "./components/cipher-generator/cipher-form-generator.component";
export { CipherFormContainer } from "../cipher-form/cipher-form-container"; export { CipherFormContainer } from "../cipher-form/cipher-form-container";
export { CipherFormComponent } from "./components/cipher-form.component"; export { CipherFormComponent } from "./components/cipher-form.component";
export { AdvancedUriOptionDialogComponent } from "./components/autofill-options/advanced-uri-option-dialog.component";