[SG-900] Autofill callout updates (#4738)

* Updated messages

* Implement method in platformUtils to get autofill command

* Updates to callout in current tab component

* Add autofill keyboard shortcut to autofill settings

* style updates

* Add routing animation for autofill settings

* Remove extra function

* Remove unnecessary safari logic

* Remove autofill settings transition added in another PR

* Fix callout still present after clicking 'Got it' (#4797)

---------

Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com>
This commit is contained in:
Robyn MacCallum 2023-02-17 14:38:22 -05:00 committed by GitHub
parent 4780a9ce18
commit 999a40e755
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 162 additions and 45 deletions

View File

@ -2111,25 +2111,49 @@
}
}
},
"tryAutofillPageLoad": {
"message": "Try auto-fill on page load?"
},
"tryAutofill": {
"howToAutofill": {
"message": "How to auto-fill"
},
"autofillPageLoadInfo": {
"message": "Login forms will automatically fill in matching credentials if you turn on auto-fill on page load."
},
"autofillSelectInfo": {
"message": "Select an item from this page to auto-fill the active tab's form."
"message": "Select an item from this page or use the shortcut: $COMMAND$. You can also try auto-fill on page load.",
"placeholders": {
"command": {
"content": "$1",
"example": "CTRL+Shift+L"
}
}
},
"autofillTurnedOn": {
"message": "Auto-fill on page load turned on"
"autofillSelectInfoNoCommand": {
"message": "Select an item from this page or set a shortcut in settings. You can also try auto-fill on page load."
},
"turnOn": {
"message": "Turn on"
"gotIt": {
"message": "Got it"
},
"notNow": {
"message": "Not now"
"autofillSettings": {
"message": "Auto-fill settings"
},
"autofillShortcut": {
"message": "Auto-fill keyboard shortcut"
},
"autofillShortcutNotSet": {
"message": "The auto-fill shortcut is not set. Change this in the browser's settings."
},
"autofillShortcutText": {
"message": "The auto-fill shortcut is: $COMMAND$. Change this in the browser's settings.",
"placeholders": {
"command": {
"content": "$1",
"example": "CTRL+Shift+L"
}
}
},
"autofillShortcutTextSafari": {
"message": "Default auto-fill shortcut: $COMMAND$.",
"placeholders": {
"command": {
"content": "$1",
"example": "CTRL+Shift+L"
}
}
}
}

View File

@ -354,6 +354,7 @@
&.box-content-row-flex,
.box-content-row-flex,
&.box-content-row-checkbox,
&.box-content-row-link,
&.box-content-row-input,
&.box-content-row-slider,
&.box-content-row-multi {
@ -398,6 +399,7 @@
}
&.box-content-row-checkbox,
&.box-content-row-link,
&.box-content-row-input,
&.box-content-row-slider {
padding-top: 10px;

View File

@ -28,7 +28,7 @@
&.callout-half {
font-weight: bold;
max-width: 45%;
max-width: 50%;
}
&:hover:not([disabled]) {

View File

@ -72,4 +72,19 @@
{{ "defaultUriMatchDetectionDesc" | i18n }}
</div>
</div>
<div class="box">
<div class="box-content">
<button
type="button"
class="box-content-row box-content-row-link box-content-row-flex"
(click)="commandSettings()"
>
<div class="row-main">{{ "autofillShortcut" | i18n }}</div>
<i class="bwi bwi-external-link bwi-lg bwi-fw" aria-hidden="true"></i>
</button>
</div>
<div id="autofillKeyboardHelp" class="box-footer">
{{ autofillKeyboardHelperText }}
</div>
</div>
</main>

View File

@ -1,6 +1,7 @@
import { Component, OnInit } from "@angular/core";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { StateService } from "@bitwarden/common/abstractions/state.service";
import { UriMatchType } from "@bitwarden/common/enums/uriMatchType";
@ -16,8 +17,13 @@ export class AutofillComponent implements OnInit {
autoFillOnPageLoadOptions: any[];
defaultUriMatch = UriMatchType.Domain;
uriMatchOptions: any[];
autofillKeyboardHelperText: string;
constructor(private stateService: StateService, i18nService: I18nService) {
constructor(
private stateService: StateService,
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService
) {
this.autoFillOnPageLoadOptions = [
{ name: i18nService.t("autoFillOnPageLoadYes"), value: true },
{ name: i18nService.t("autoFillOnPageLoadNo"), value: false },
@ -40,6 +46,9 @@ export class AutofillComponent implements OnInit {
const defaultUriMatch = await this.stateService.getDefaultUriMatch();
this.defaultUriMatch = defaultUriMatch == null ? UriMatchType.Domain : defaultUriMatch;
const command = await this.platformUtilsService.getAutofillKeyboardShortcut();
await this.setAutofillKeyboardHelperText(command);
}
async updateAutoFillOnPageLoad() {
@ -57,4 +66,26 @@ export class AutofillComponent implements OnInit {
AboutAutofill() {
BrowserApi.createNewTab("https://bitwarden.com/help/auto-fill-browser/");
}
private async setAutofillKeyboardHelperText(command: string) {
if (command) {
this.autofillKeyboardHelperText = this.i18nService.t("autofillShortcutText", command);
} else {
this.autofillKeyboardHelperText = this.i18nService.t("autofillShortcutNotSet");
}
}
async commandSettings() {
if (this.platformUtilsService.isChrome()) {
BrowserApi.createNewTab("chrome://extensions/shortcuts");
} else if (this.platformUtilsService.isOpera()) {
BrowserApi.createNewTab("opera://extensions/shortcuts");
} else if (this.platformUtilsService.isEdge()) {
BrowserApi.createNewTab("edge://extensions/shortcuts");
} else if (this.platformUtilsService.isVivaldi()) {
BrowserApi.createNewTab("vivaldi://extensions/shortcuts");
} else {
BrowserApi.createNewTab("https://bitwarden.com/help/keyboard-shortcuts");
}
}
}

View File

@ -377,4 +377,31 @@ export default class BrowserPlatformUtilsService implements PlatformUtilsService
supportsSecureStorage(): boolean {
return false;
}
async getAutofillKeyboardShortcut(): Promise<string> {
let autofillCommand: string;
// You can not change the command in Safari or obtain it programmatically
if (this.isSafari()) {
autofillCommand = "Cmd+Shift+L";
} else if (this.isFirefox()) {
autofillCommand = (await browser.commands.getAll()).find(
(c) => c.name === "autofill_login"
).shortcut;
// Firefox is returing Ctrl instead of Cmd for the modifier key on macOS if
// the command is the default one set on installation.
if (
(await browser.runtime.getPlatformInfo()).os === "mac" &&
autofillCommand === "Ctrl+Shift+L"
) {
autofillCommand = "Cmd+Shift+L";
}
} else {
await new Promise((resolve) =>
chrome.commands.getAll((c) =>
resolve((autofillCommand = c.find((c) => c.name === "autofill_login").shortcut))
)
);
}
return autofillCommand;
}
}

View File

@ -36,27 +36,20 @@
</div>
<ng-container *ngIf="loaded">
<app-vault-select (onVaultSelectionChanged)="load()"></app-vault-select>
<app-callout
*ngIf="showTryAutofillOnPageLoad"
type="info"
title="{{ 'tryAutofillPageLoad' | i18n }}"
>
<p>{{ "autofillPageLoadInfo" | i18n }}</p>
<app-callout *ngIf="showHowToAutofill" type="info" title="{{ 'howToAutofill' | i18n }}">
<p>{{ autofillCalloutText }}</p>
<button
type="button"
class="btn primary callout-half"
appStopClick
(click)="setAutofillOnPageLoad()"
(click)="dismissCallout()"
>
{{ "turnOn" | i18n }}
{{ "gotIt" | i18n }}
</button>
<button type="button" class="btn callout-half" appStopClick (click)="notNow()">
{{ "notNow" | i18n }}
<button type="button" class="btn callout-half" appStopClick (click)="goToSettings()">
{{ "autofillSettings" | i18n }}
</button>
</app-callout>
<app-callout *ngIf="showSelectAutofillCallout" type="info" title="{{ 'tryAutofill' | i18n }}">
<p>{{ "autofillSelectInfo" | i18n }}</p>
</app-callout>
<div class="box list" *ngIf="loginCiphers">
<h2 class="box-header">
{{ "typeLogins" | i18n }}

View File

@ -42,8 +42,8 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
loaded = false;
isLoading = false;
showOrganizations = false;
showTryAutofillOnPageLoad = false;
showSelectAutofillCallout = false;
showHowToAutofill = false;
autofillCalloutText: string;
protected search$ = new Subject<void>();
private destroy$ = new Subject<void>();
@ -103,10 +103,12 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
if (!this.syncService.syncInProgress) {
await this.load();
await this.setCallout();
} else {
this.loadedTimeout = window.setTimeout(async () => {
if (!this.isLoading) {
await this.load();
await this.setCallout();
}
}, 5000);
}
@ -114,11 +116,6 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
this.search$
.pipe(debounceTime(500), takeUntil(this.destroy$))
.subscribe(() => this.searchVault());
this.showTryAutofillOnPageLoad =
this.loginCiphers.length > 0 &&
!(await this.stateService.getEnableAutoFillOnPageLoad()) &&
!(await this.stateService.getDismissedAutofillCallout());
}
ngOnDestroy() {
@ -274,17 +271,32 @@ export class CurrentTabComponent implements OnInit, OnDestroy {
this.isLoading = this.loaded = true;
}
async setAutofillOnPageLoad() {
await this.stateService.setEnableAutoFillOnPageLoad(true);
this.platformUtilsService.showToast("success", null, this.i18nService.t("autofillTurnedOn"));
await this.fillCipher(this.loginCiphers[0], 3000);
await this.stateService.setDismissedAutofillCallout(true);
this.showTryAutofillOnPageLoad = false;
async goToSettings() {
this.router.navigate(["autofill"]);
}
async notNow() {
async dismissCallout() {
await this.stateService.setDismissedAutofillCallout(true);
this.showTryAutofillOnPageLoad = false;
this.showSelectAutofillCallout = true;
this.showHowToAutofill = false;
}
private async setCallout() {
this.showHowToAutofill =
this.loginCiphers.length > 0 &&
!(await this.stateService.getEnableAutoFillOnPageLoad()) &&
!(await this.stateService.getDismissedAutofillCallout());
if (this.showHowToAutofill) {
const autofillCommand = await this.platformUtilsService.getAutofillKeyboardShortcut();
await this.setAutofillCalloutText(autofillCommand);
}
}
private setAutofillCalloutText(command: string) {
if (command) {
this.autofillCalloutText = this.i18nService.t("autofillSelectInfo", command);
} else {
this.autofillCalloutText = this.i18nService.t("autofillSelectInfoNoCommand");
}
}
}

View File

@ -150,4 +150,8 @@ export class CliPlatformUtilsService implements PlatformUtilsService {
supportsSecureStorage(): boolean {
return false;
}
getAutofillKeyboardShortcut(): Promise<string> {
return null;
}
}

View File

@ -184,4 +184,8 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
supportsSecureStorage(): boolean {
return true;
}
getAutofillKeyboardShortcut(): Promise<string> {
return null;
}
}

View File

@ -262,4 +262,8 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
supportsSecureStorage() {
return false;
}
getAutofillKeyboardShortcut(): Promise<string> {
return null;
}
}

View File

@ -44,4 +44,5 @@ export abstract class PlatformUtilsService {
supportsBiometric: () => Promise<boolean>;
authenticateBiometric: () => Promise<boolean>;
supportsSecureStorage: () => boolean;
getAutofillKeyboardShortcut: () => Promise<string>;
}