1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-09-27 04:03:00 +02:00

[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": { "howToAutofill": {
"message": "Try auto-fill on page load?"
},
"tryAutofill": {
"message": "How to auto-fill" "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": { "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": { "autofillSelectInfoNoCommand": {
"message": "Auto-fill on page load turned on" "message": "Select an item from this page or set a shortcut in settings. You can also try auto-fill on page load."
}, },
"turnOn": { "gotIt": {
"message": "Turn on" "message": "Got it"
}, },
"notNow": { "autofillSettings": {
"message": "Not now" "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-flex, .box-content-row-flex,
&.box-content-row-checkbox, &.box-content-row-checkbox,
&.box-content-row-link,
&.box-content-row-input, &.box-content-row-input,
&.box-content-row-slider, &.box-content-row-slider,
&.box-content-row-multi { &.box-content-row-multi {
@ -398,6 +399,7 @@
} }
&.box-content-row-checkbox, &.box-content-row-checkbox,
&.box-content-row-link,
&.box-content-row-input, &.box-content-row-input,
&.box-content-row-slider { &.box-content-row-slider {
padding-top: 10px; padding-top: 10px;

View File

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

View File

@ -72,4 +72,19 @@
{{ "defaultUriMatchDetectionDesc" | i18n }} {{ "defaultUriMatchDetectionDesc" | i18n }}
</div> </div>
</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> </main>

View File

@ -1,6 +1,7 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service"; 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 { StateService } from "@bitwarden/common/abstractions/state.service";
import { UriMatchType } from "@bitwarden/common/enums/uriMatchType"; import { UriMatchType } from "@bitwarden/common/enums/uriMatchType";
@ -16,8 +17,13 @@ export class AutofillComponent implements OnInit {
autoFillOnPageLoadOptions: any[]; autoFillOnPageLoadOptions: any[];
defaultUriMatch = UriMatchType.Domain; defaultUriMatch = UriMatchType.Domain;
uriMatchOptions: any[]; uriMatchOptions: any[];
autofillKeyboardHelperText: string;
constructor(private stateService: StateService, i18nService: I18nService) { constructor(
private stateService: StateService,
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService
) {
this.autoFillOnPageLoadOptions = [ this.autoFillOnPageLoadOptions = [
{ name: i18nService.t("autoFillOnPageLoadYes"), value: true }, { name: i18nService.t("autoFillOnPageLoadYes"), value: true },
{ name: i18nService.t("autoFillOnPageLoadNo"), value: false }, { name: i18nService.t("autoFillOnPageLoadNo"), value: false },
@ -40,6 +46,9 @@ export class AutofillComponent implements OnInit {
const defaultUriMatch = await this.stateService.getDefaultUriMatch(); const defaultUriMatch = await this.stateService.getDefaultUriMatch();
this.defaultUriMatch = defaultUriMatch == null ? UriMatchType.Domain : defaultUriMatch; this.defaultUriMatch = defaultUriMatch == null ? UriMatchType.Domain : defaultUriMatch;
const command = await this.platformUtilsService.getAutofillKeyboardShortcut();
await this.setAutofillKeyboardHelperText(command);
} }
async updateAutoFillOnPageLoad() { async updateAutoFillOnPageLoad() {
@ -57,4 +66,26 @@ export class AutofillComponent implements OnInit {
AboutAutofill() { AboutAutofill() {
BrowserApi.createNewTab("https://bitwarden.com/help/auto-fill-browser/"); 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 { supportsSecureStorage(): boolean {
return false; 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> </div>
<ng-container *ngIf="loaded"> <ng-container *ngIf="loaded">
<app-vault-select (onVaultSelectionChanged)="load()"></app-vault-select> <app-vault-select (onVaultSelectionChanged)="load()"></app-vault-select>
<app-callout <app-callout *ngIf="showHowToAutofill" type="info" title="{{ 'howToAutofill' | i18n }}">
*ngIf="showTryAutofillOnPageLoad" <p>{{ autofillCalloutText }}</p>
type="info"
title="{{ 'tryAutofillPageLoad' | i18n }}"
>
<p>{{ "autofillPageLoadInfo" | i18n }}</p>
<button <button
type="button" type="button"
class="btn primary callout-half" class="btn primary callout-half"
appStopClick appStopClick
(click)="setAutofillOnPageLoad()" (click)="dismissCallout()"
> >
{{ "turnOn" | i18n }} {{ "gotIt" | i18n }}
</button> </button>
<button type="button" class="btn callout-half" appStopClick (click)="notNow()"> <button type="button" class="btn callout-half" appStopClick (click)="goToSettings()">
{{ "notNow" | i18n }} {{ "autofillSettings" | i18n }}
</button> </button>
</app-callout> </app-callout>
<app-callout *ngIf="showSelectAutofillCallout" type="info" title="{{ 'tryAutofill' | i18n }}">
<p>{{ "autofillSelectInfo" | i18n }}</p>
</app-callout>
<div class="box list" *ngIf="loginCiphers"> <div class="box list" *ngIf="loginCiphers">
<h2 class="box-header"> <h2 class="box-header">
{{ "typeLogins" | i18n }} {{ "typeLogins" | i18n }}

View File

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

View File

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

View File

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

View File

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