[SG-416] Updates to Bitwarden Authenticator (#3045)

* [SG-416] Changed UI for TOTP codes on free plan and added link to get Premium. On browser, changed back action of premium.component in order to reuse on cipher details.

* [SSG-416] PR Fix

* [SSG-416] fix formatting

* [SSG-416] Updated desktop free plan OTP UI

* [SSG-416] noticed a bad div tag making file changes erratic

* [SG-416] fixed label

* [SSG-416] Fix formatting

* [SSG-416] Changed bootstrap classes to tailwind

* [SSG-416] Added premium and upgrade badge back. Muted placeholder totp code colors and button.

* [SSG-416] Change learn more to upgrade label on get premium modal. Fixed navigation for premium.

* [SSG-416] Removed unused image file.

* [SG-416] Changed browser "Premium subscription required" text to be all hyperlink.

* [SG-416] Fixed missing resource on browser

* [SG-416] Code format with lint
This commit is contained in:
André Filipe da Silva Bispo 2022-08-09 19:03:02 +01:00 committed by GitHub
parent 31cae4390f
commit c4f9c2cca6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 130 additions and 21 deletions

View File

@ -1967,6 +1967,9 @@
},
"ssoKeyConnectorError": {
"message": "Key Connector error: make sure Key Connector is available and working correctly."
},
"premiumSubcriptionRequired": {
"message": "Premium subscription required"
},
"organizationIsDisabled": {
"message": "Organization is disabled."

View File

@ -1,6 +1,6 @@
<header>
<div class="left">
<button type="button" routerLink="/tabs/settings">
<button type="button" (click)="goBack()">
<span class="header-icon"><i class="bwi bwi-angle-left" aria-hidden="true"></i></span>
<span>{{ "back" | i18n }}</span>
</button>

View File

@ -1,4 +1,4 @@
import { CurrencyPipe } from "@angular/common";
import { CurrencyPipe, Location } from "@angular/common";
import { Component } from "@angular/core";
import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/components/premium.component";
@ -21,6 +21,7 @@ export class PremiumComponent extends BasePremiumComponent {
apiService: ApiService,
stateService: StateService,
logService: LogService,
private location: Location,
private currencyPipe: CurrencyPipe
) {
super(i18nService, platformUtilsService, apiService, logService, stateService);
@ -32,4 +33,8 @@ export class PremiumComponent extends BasePremiumComponent {
this.priceString = this.priceString.replace("%price%", thePrice);
}
}
goBack() {
this.location.back();
}
}

View File

@ -139,7 +139,7 @@
<div
class="box-content-row box-content-row-flex totp"
[ngClass]="{ low: totpLow }"
*ngIf="cipher.login.totp && totpCode"
*ngIf="cipher.login.totp && totpCode && canAccessPremium"
>
<div class="row-main">
<span
@ -177,6 +177,20 @@
</button>
</div>
</div>
<div
class="box-content-row box-content-row-flex totp"
*ngIf="cipher.login.totp && !canAccessPremium"
>
<div class="row-main">
<span class="row-label">{{ "verificationCodeTotp" | i18n }}</span>
<span class="row-label">
<a routerLink="/premium">
{{ "premiumSubcriptionRequired" | i18n }}
</a>
</span>
</div>
</div>
</div>
<!-- Card -->
<div *ngIf="cipher.card">

View File

@ -100,7 +100,7 @@
<div
class="box-content-row box-content-row-flex totp"
[ngClass]="{ low: totpLow }"
*ngIf="cipher.login.totp && totpCode"
*ngIf="cipher.login.totp && totpCode && canAccessPremium"
>
<div class="row-main">
<span
@ -138,6 +138,19 @@
</button>
</div>
</div>
<div
class="box-content-row box-content-row-flex totp"
*ngIf="cipher.login.totp && !canAccessPremium"
>
<div class="row-main">
<span class="row-label">{{ "verificationCodeTotp" | i18n }}</span>
<span class="row-label">
<a [routerLink]="" (click)="showGetPremium()"
>{{ "premiumSubcriptionRequired" | i18n }}
</a>
</span>
</div>
</div>
</div>
<!-- Card -->
<div *ngIf="cipher.card">

View File

@ -115,4 +115,10 @@ export class ViewComponent extends BaseViewComponent implements OnChanges {
});
}
}
showGetPremium() {
if (!this.canAccessPremium) {
this.messagingService.send("premiumRequired");
}
}
}

View File

@ -1979,6 +1979,9 @@
"apiKey": {
"message": "API Key"
},
"premiumSubcriptionRequired": {
"message": "Premium subscription required"
},
"organizationIsDisabled": {
"message": "Organization is disabled."
},

View File

@ -147,7 +147,7 @@ export class AppComponent implements OnDestroy, OnInit {
const premiumConfirmed = await this.platformUtilsService.showDialog(
this.i18nService.t("premiumRequiredDesc"),
this.i18nService.t("premiumRequired"),
this.i18nService.t("learnMore"),
this.i18nService.t("upgrade"),
this.i18nService.t("cancel")
);
if (premiumConfirmed) {

View File

@ -166,8 +166,8 @@
</div>
</div>
</div>
<div class="row">
<div class="col-6 form-group">
<div class="tw-flex tw-flex-row">
<div class="tw-mb-4 tw-w-1/2">
<label for="loginTotp">{{ "authenticatorKeyTotp" | i18n }}</label>
<input
id="loginTotp"
@ -179,14 +179,41 @@
[disabled]="cipher.isDeleted || !cipher.viewPassword || viewOnly"
/>
</div>
<div class="col-6 form-group totp d-flex align-items-end" [ngClass]="{ low: totpLow }">
<div *ngIf="!cipher.login.totp || !totpCode">
<img
src="../../images/totp-countdown.png"
id="totpImage"
<div class="tw-mb-4 tw-ml-4 tw-flex tw-w-1/2 tw-items-end" [ngClass]="{ low: totpLow }">
<div
class="totp tw-flex tw-flex-row tw-items-center"
*ngIf="!cipher.login.totp || (cipher.login.totp && !canAccessPremium)"
>
<span class="totp-countdown">
<span class="totp-sec tw-text-muted">15</span>
<svg>
<g>
<circle
class="totp-circle-muted inner"
r="12.6"
cy="16"
cx="16"
opacity="0.25"
[ngStyle]="{ 'stroke-dashoffset.px': 40 }"
></circle>
<circle
class="totp-circle-muted outer"
opacity="0.25"
r="14"
cy="16"
cx="16"
></circle>
</g>
</svg>
</span>
<span
class="totp-code tw-mr-3 tw-ml-2 tw-text-muted"
title="{{ 'verificationCodeTotp' | i18n }}"
class="ml-2"
/>
>--- ---</span
>
<i class="bwi bwi-lg bwi-clone tw-text-muted" aria-hidden="true"></i>
</div>
<div class="tw-pb-2" *ngIf="!cipher.login.totp || !totpCode">
<app-premium-badge
*ngIf="!organization && !cipher.organizationId"
class="ml-3"
@ -209,8 +236,11 @@
{{ "upgrade" | i18n }}
</a>
</div>
<div *ngIf="cipher.login.totp && totpCode" class="d-flex align-items-center">
<span class="totp-countdown mr-3 ml-2">
<div
*ngIf="cipher.login.totp && totpCode && canAccessPremium"
class="totp tw-flex tw-flex-row tw-items-center"
>
<span class="totp-countdown">
<span class="totp-sec">{{ totpSec }}</span>
<svg>
<g>
@ -225,16 +255,18 @@
</g>
</svg>
</span>
<span class="totp-code mr-2" title="{{ 'verificationCodeTotp' | i18n }}">{{
totpCodeFormatted
}}</span>
<span
class="totp-code tw-mr-2 tw-ml-2 tw-mt-1"
title="{{ 'verificationCodeTotp' | i18n }}"
>{{ totpCodeFormatted }}</span
>
<button
type="button"
class="btn btn-link"
class="tw-items-center tw-border-none tw-bg-transparent tw-text-primary-500"
appA11yTitle="{{ 'copyVerificationCode' | i18n }}"
(click)="copy(totpCode, 'verificationCodeTotp', 'TOTP')"
>
<i class="bwi bwi-clone" aria-hidden="true"></i>
<i class="bwi bwi-lg bwi-clone" aria-hidden="true"></i>
</button>
</div>
</div>

View File

@ -152,6 +152,17 @@ export class AddEditComponent extends BaseAddEditComponent {
});
}
showGetPremium() {
if (this.canAccessPremium) {
return;
}
if (this.cipher.organizationUseTotp) {
this.upgradeOrganization();
} else {
this.premiumRequired();
}
}
viewHistory() {
this.viewingPasswordHistory = !this.viewingPasswordHistory;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 950 B

View File

@ -5220,6 +5220,9 @@
}
}
},
"premiumSubcriptionRequired": {
"message": "Premium subscription required"
},
"scim": {
"message": "SCIM Provisioning",
"description": "The text, 'SCIM', is an acronymn and should not be translated."

View File

@ -130,6 +130,25 @@
stroke-width: 2;
}
}
.totp-circle-muted {
fill: none;
@include themify($themes) {
stroke: themed("info");
}
&.inner {
stroke-dasharray: 78.6;
stroke-dashoffset: 0;
stroke-width: 3;
}
&.outer {
stroke-dasharray: 88;
stroke-dashoffset: 0;
stroke-width: 2;
}
}
}
> .align-items-center {