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:
parent
4780a9ce18
commit
999a40e755
@ -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": {
|
||||||
"autofillTurnedOn": {
|
"command": {
|
||||||
"message": "Auto-fill on page load turned on"
|
"content": "$1",
|
||||||
},
|
"example": "CTRL+Shift+L"
|
||||||
"turnOn": {
|
}
|
||||||
"message": "Turn on"
|
}
|
||||||
},
|
},
|
||||||
"notNow": {
|
"autofillSelectInfoNoCommand": {
|
||||||
"message": "Not now"
|
"message": "Select an item from this page or set a shortcut in settings. You can also try auto-fill on page load."
|
||||||
|
},
|
||||||
|
"gotIt": {
|
||||||
|
"message": "Got it"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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]) {
|
||||||
|
@ -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>
|
||||||
|
@ -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");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 }}
|
||||||
|
@ -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");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,4 +150,8 @@ export class CliPlatformUtilsService implements PlatformUtilsService {
|
|||||||
supportsSecureStorage(): boolean {
|
supportsSecureStorage(): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAutofillKeyboardShortcut(): Promise<string> {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -184,4 +184,8 @@ export class ElectronPlatformUtilsService implements PlatformUtilsService {
|
|||||||
supportsSecureStorage(): boolean {
|
supportsSecureStorage(): boolean {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAutofillKeyboardShortcut(): Promise<string> {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -262,4 +262,8 @@ export class WebPlatformUtilsService implements PlatformUtilsService {
|
|||||||
supportsSecureStorage() {
|
supportsSecureStorage() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getAutofillKeyboardShortcut(): Promise<string> {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user