1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 22:33:35 +00:00

[PM-16428] Option for primary click action to autofill on Vault view (#12557)

* add option for primary click action to autofill

* setting option string

* autofill setting for click items to autofill

* fix showQuickCopyActions

* apply setting
This commit is contained in:
Kyle Spearrin
2024-12-27 08:51:17 -05:00
committed by GitHub
parent b3155d19dd
commit f434334a88
12 changed files with 110 additions and 7 deletions

View File

@@ -1004,6 +1004,9 @@
"showIdentitiesCurrentTabDesc": { "showIdentitiesCurrentTabDesc": {
"message": "List identity items on the Tab page for easy autofill." "message": "List identity items on the Tab page for easy autofill."
}, },
"clickToAutofillOnVault": {
"message": "Click items to autofill on Vault view"
},
"clearClipboard": { "clearClipboard": {
"message": "Clear clipboard", "message": "Clear clipboard",
"description": "Clipboard is the operating system thing where you copy/paste data to on your device." "description": "Clipboard is the operating system thing where you copy/paste data to on your device."

View File

@@ -120,7 +120,7 @@
/> />
<bit-label for="showCardsSuggestions">{{ "showCardsInVaultView" | i18n }}</bit-label> <bit-label for="showCardsSuggestions">{{ "showCardsInVaultView" | i18n }}</bit-label>
</bit-form-control> </bit-form-control>
<bit-form-control disableMargin> <bit-form-control>
<input <input
bitCheckbox bitCheckbox
id="showIdentitiesSuggestions" id="showIdentitiesSuggestions"
@@ -132,6 +132,18 @@
{{ "showIdentitiesInVaultView" | i18n }} {{ "showIdentitiesInVaultView" | i18n }}
</bit-label> </bit-label>
</bit-form-control> </bit-form-control>
<bit-form-control disableMargin>
<input
bitCheckbox
id="clickToAutofill"
type="checkbox"
(change)="updateClickItemsVaultView()"
[(ngModel)]="clickItemsVaultView"
/>
<bit-label for="clickToAutofill" class="tw-whitespace-normal">
{{ "clickToAutofillOnVault" | i18n }}
</bit-label>
</bit-form-control>
</bit-card> </bit-card>
</bit-section> </bit-section>
<bit-section> <bit-section>

View File

@@ -110,6 +110,7 @@ export class AutofillComponent implements OnInit {
uriMatchOptions: { name: string; value: UriMatchStrategySetting }[]; uriMatchOptions: { name: string; value: UriMatchStrategySetting }[];
showCardsCurrentTab: boolean = true; showCardsCurrentTab: boolean = true;
showIdentitiesCurrentTab: boolean = true; showIdentitiesCurrentTab: boolean = true;
clickItemsVaultView: boolean = false;
autofillKeyboardHelperText: string; autofillKeyboardHelperText: string;
accountSwitcherEnabled: boolean = false; accountSwitcherEnabled: boolean = false;
@@ -207,6 +208,10 @@ export class AutofillComponent implements OnInit {
this.showIdentitiesCurrentTab = await firstValueFrom( this.showIdentitiesCurrentTab = await firstValueFrom(
this.vaultSettingsService.showIdentitiesCurrentTab$, this.vaultSettingsService.showIdentitiesCurrentTab$,
); );
this.clickItemsVaultView = await firstValueFrom(
this.vaultSettingsService.clickItemsToAutofillVaultView$,
);
} }
async updateInlineMenuVisibility() { async updateInlineMenuVisibility() {
@@ -413,4 +418,8 @@ export class AutofillComponent implements OnInit {
async updateShowInlineMenuIdentities() { async updateShowInlineMenuIdentities() {
await this.autofillSettingsService.setShowInlineMenuIdentities(this.showInlineMenuIdentities); await this.autofillSettingsService.setShowInlineMenuIdentities(this.showInlineMenuIdentities);
} }
async updateClickItemsVaultView() {
await this.vaultSettingsService.setClickItemsToAutofillVaultView(this.clickItemsVaultView);
}
} }

View File

@@ -6,4 +6,5 @@
(onRefresh)="refreshCurrentTab()" (onRefresh)="refreshCurrentTab()"
[description]="(showEmptyAutofillTip$ | async) ? ('autofillSuggestionsTip' | i18n) : null" [description]="(showEmptyAutofillTip$ | async) ? ('autofillSuggestionsTip' | i18n) : null"
showAutofillButton showAutofillButton
[primaryActionAutofill]="clickItemsToAutofillVaultView"
></app-vault-list-items-container> ></app-vault-list-items-container>

View File

@@ -1,8 +1,9 @@
import { CommonModule } from "@angular/common"; import { CommonModule } from "@angular/common";
import { Component } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { combineLatest, map, Observable } from "rxjs"; import { combineLatest, firstValueFrom, map, Observable } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module"; import { JslibModule } from "@bitwarden/angular/jslib.module";
import { VaultSettingsService } from "@bitwarden/common/vault/abstractions/vault-settings/vault-settings.service";
import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherType } from "@bitwarden/common/vault/enums";
import { import {
IconButtonModule, IconButtonModule,
@@ -31,7 +32,7 @@ import { VaultListItemsContainerComponent } from "../vault-list-items-container/
selector: "app-autofill-vault-list-items", selector: "app-autofill-vault-list-items",
templateUrl: "autofill-vault-list-items.component.html", templateUrl: "autofill-vault-list-items.component.html",
}) })
export class AutofillVaultListItemsComponent { export class AutofillVaultListItemsComponent implements OnInit {
/** /**
* The list of ciphers that can be used to autofill the current page. * The list of ciphers that can be used to autofill the current page.
* @protected * @protected
@@ -45,6 +46,8 @@ export class AutofillVaultListItemsComponent {
*/ */
protected showRefresh: boolean = BrowserPopupUtils.inSidebar(window); protected showRefresh: boolean = BrowserPopupUtils.inSidebar(window);
clickItemsToAutofillVaultView = false;
/** /**
* Observable that determines whether the empty autofill tip should be shown. * Observable that determines whether the empty autofill tip should be shown.
* The tip is shown when there are no login ciphers to autofill, no filter is applied, and autofill is allowed in * The tip is shown when there are no login ciphers to autofill, no filter is applied, and autofill is allowed in
@@ -65,10 +68,17 @@ export class AutofillVaultListItemsComponent {
constructor( constructor(
private vaultPopupItemsService: VaultPopupItemsService, private vaultPopupItemsService: VaultPopupItemsService,
private vaultPopupAutofillService: VaultPopupAutofillService, private vaultPopupAutofillService: VaultPopupAutofillService,
private vaultSettingsService: VaultSettingsService,
) { ) {
// TODO: Migrate logic to show Autofill policy toast PM-8144 // TODO: Migrate logic to show Autofill policy toast PM-8144
} }
async ngOnInit() {
this.clickItemsToAutofillVaultView = await firstValueFrom(
this.vaultSettingsService.clickItemsToAutofillVaultView$,
);
}
/** /**
* Refreshes the current tab to re-populate the autofill ciphers. * Refreshes the current tab to re-populate the autofill ciphers.
* @protected * @protected

View File

@@ -18,6 +18,11 @@
</button> </button>
</ng-container> </ng-container>
</ng-container> </ng-container>
<ng-container *ngIf="showViewOption">
<button type="button" bitMenuItem (click)="onView()">
{{ "view" | i18n }}
</button>
</ng-container>
<button type="button" bitMenuItem (click)="toggleFavorite()"> <button type="button" bitMenuItem (click)="toggleFavorite()">
{{ favoriteText | i18n }} {{ favoriteText | i18n }}
</button> </button>

View File

@@ -46,6 +46,13 @@ export class ItemMoreOptionsComponent implements OnInit {
return this._cipher$.value; return this._cipher$.value;
} }
/**
* Flag to show view item menu option. Used when something else is
* assigned as the primary action for the item, such as autofill.
*/
@Input({ transform: booleanAttribute })
showViewOption: boolean;
/** /**
* Flag to hide the autofill menu options. Used for items that are * Flag to hide the autofill menu options. Used for items that are
* already in the autofill list suggestion. * already in the autofill list suggestion.
@@ -111,6 +118,16 @@ export class ItemMoreOptionsComponent implements OnInit {
await this.vaultPopupAutofillService.doAutofillAndSave(this.cipher, false); await this.vaultPopupAutofillService.doAutofillAndSave(this.cipher, false);
} }
async onView() {
const repromptPassed = await this.passwordRepromptService.passwordRepromptCheck(this.cipher);
if (!repromptPassed) {
return;
}
await this.router.navigate(["/view-cipher"], {
queryParams: { cipherId: this.cipher.id, type: this.cipher.type },
});
}
/** /**
* Toggles the favorite status of the cipher and updates it on the server. * Toggles the favorite status of the cipher and updates it on the server.
*/ */

View File

@@ -27,9 +27,11 @@
<button <button
bit-item-content bit-item-content
type="button" type="button"
(click)="onViewCipher(cipher)" (click)="primaryActionAutofill ? doAutofill(cipher) : onViewCipher(cipher)"
(dblclick)="launchCipher(cipher)" (dblclick)="launchCipher(cipher)"
[appA11yTitle]="'viewItemTitle' | i18n: cipher.name" [appA11yTitle]="
(primaryActionAutofill ? 'autofillTitle' : 'viewItemTitle') | i18n: cipher.name
"
class="{{ itemHeightClass }}" class="{{ itemHeightClass }}"
> >
<app-vault-icon slot="start" [cipher]="cipher"></app-vault-icon> <app-vault-icon slot="start" [cipher]="cipher"></app-vault-icon>
@@ -49,7 +51,7 @@
<span slot="secondary">{{ cipher.subTitle }}</span> <span slot="secondary">{{ cipher.subTitle }}</span>
</button> </button>
<ng-container slot="end"> <ng-container slot="end">
<bit-item-action *ngIf="showAutofillButton"> <bit-item-action *ngIf="showAutofillButton && !primaryActionAutofill">
<button <button
type="button" type="button"
bitBadge bitBadge
@@ -75,6 +77,7 @@
<app-item-more-options <app-item-more-options
[cipher]="cipher" [cipher]="cipher"
[hideAutofillOptions]="showAutofillButton" [hideAutofillOptions]="showAutofillButton"
[showViewOption]="primaryActionAutofill"
></app-item-more-options> ></app-item-more-options>
</ng-container> </ng-container>
</bit-item> </bit-item>

View File

@@ -124,6 +124,12 @@ export class VaultListItemsContainerComponent implements AfterViewInit {
@Input({ transform: booleanAttribute }) @Input({ transform: booleanAttribute })
showAutofillButton: boolean; showAutofillButton: boolean;
/**
* Option to perform autofill operation as the primary action for autofill suggestions.
*/
@Input({ transform: booleanAttribute })
primaryActionAutofill: boolean;
/** /**
* Remove the bottom margin from the bit-section in this component * Remove the bottom margin from the bit-section in this component
* (used for containers at the end of the page where bottom margin is not needed) * (used for containers at the end of the page where bottom margin is not needed)

View File

@@ -19,6 +19,12 @@ export abstract class VaultSettingsService {
*/ */
showIdentitiesCurrentTab$: Observable<boolean>; showIdentitiesCurrentTab$: Observable<boolean>;
/** /**
/**
* An observable monitoring the state of the click items on the Vault view
* for Autofill suggestions.
*/
clickItemsToAutofillVaultView$: Observable<boolean>;
/**
/** /**
* Saves the enable passkeys setting to disk. * Saves the enable passkeys setting to disk.
@@ -35,4 +41,10 @@ export abstract class VaultSettingsService {
* @param value The new value for the show identities on tab page setting. * @param value The new value for the show identities on tab page setting.
*/ */
setShowIdentitiesCurrentTab: (value: boolean) => Promise<void>; setShowIdentitiesCurrentTab: (value: boolean) => Promise<void>;
/**
* Saves the click items on vault View for Autofill suggestions to disk.
* @param value The new value for the click items on vault View for
* Autofill suggestions setting.
*/
setClickItemsToAutofillVaultView: (value: boolean) => Promise<void>;
} }

View File

@@ -25,3 +25,12 @@ export const SHOW_IDENTITIES_CURRENT_TAB = new UserKeyDefinition<boolean>(
clearOn: [], // do not clear user settings clearOn: [], // do not clear user settings
}, },
); );
export const CLICK_ITEMS_AUTOFILL_VAULT_VIEW = new UserKeyDefinition<boolean>(
VAULT_SETTINGS_DISK,
"clickItemsToAutofillOnVaultView",
{
deserializer: (obj) => obj,
clearOn: [], // do not clear user settings
},
);

View File

@@ -6,6 +6,7 @@ import {
SHOW_CARDS_CURRENT_TAB, SHOW_CARDS_CURRENT_TAB,
SHOW_IDENTITIES_CURRENT_TAB, SHOW_IDENTITIES_CURRENT_TAB,
USER_ENABLE_PASSKEYS, USER_ENABLE_PASSKEYS,
CLICK_ITEMS_AUTOFILL_VAULT_VIEW,
} from "../key-state/vault-settings.state"; } from "../key-state/vault-settings.state";
/** /**
@@ -39,6 +40,14 @@ export class VaultSettingsService implements VaultSettingsServiceAbstraction {
readonly showIdentitiesCurrentTab$: Observable<boolean> = readonly showIdentitiesCurrentTab$: Observable<boolean> =
this.showIdentitiesCurrentTabState.state$.pipe(map((x) => x ?? true)); this.showIdentitiesCurrentTabState.state$.pipe(map((x) => x ?? true));
private clickItemsToAutofillVaultViewState: ActiveUserState<boolean> =
this.stateProvider.getActive(CLICK_ITEMS_AUTOFILL_VAULT_VIEW);
/**
* {@link VaultSettingsServiceAbstraction.clickItemsToAutofillVaultView$$}
*/
readonly clickItemsToAutofillVaultView$: Observable<boolean> =
this.clickItemsToAutofillVaultViewState.state$.pipe(map((x) => x ?? false));
constructor(private stateProvider: StateProvider) {} constructor(private stateProvider: StateProvider) {}
/** /**
@@ -55,6 +64,13 @@ export class VaultSettingsService implements VaultSettingsServiceAbstraction {
await this.showIdentitiesCurrentTabState.update(() => value); await this.showIdentitiesCurrentTabState.update(() => value);
} }
/**
* {@link VaultSettingsServiceAbstraction.setClickItemsToAutofillVaultView}
*/
async setClickItemsToAutofillVaultView(value: boolean): Promise<void> {
await this.clickItemsToAutofillVaultViewState.update(() => value);
}
/** /**
* {@link VaultSettingsServiceAbstraction.setEnablePasskeys} * {@link VaultSettingsServiceAbstraction.setEnablePasskeys}
*/ */