1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-10 13:23:34 +00:00

add persistent callout in settings for non-premium users

This commit is contained in:
jaasen-livefront
2025-11-05 15:41:21 -08:00
parent f53f3516b7
commit d206832cd3
6 changed files with 61 additions and 42 deletions

View File

@@ -4890,6 +4890,9 @@
"premium": {
"message": "Premium"
},
"unlockFeaturesWithPremium": {
"message": "Unlock reporting, emergency access, and more security features with Premium."
},
"freeOrgsCannotUseAttachments": {
"message": "Free organizations cannot use attachments"
},

View File

@@ -1,4 +1,19 @@
<popup-page>
<bit-spotlight *ngIf="!(hasPremium$ | async)" [persistent]="true">
<span class="tw-text-xs"
>{{ "unlockFeaturesWithPremium" | i18n }}
<button
bitLink
buttonType="primary"
class="!tw-no-underline tw-text-xs !tw-font-bold"
type="button"
(click)="openUpgradeDialog()"
[title]="'upgradeNow' | i18n"
>
{{ "upgradeNow" | i18n }}
</button></span
>
</bit-spotlight>
<popup-header slot="header" pageTitle="{{ 'settings' | i18n }}">
<ng-container slot="end">
<app-pop-out></app-pop-out>

View File

@@ -1,5 +1,5 @@
import { CommonModule } from "@angular/common";
import { Component, OnInit } from "@angular/core";
import { ChangeDetectionStrategy, Component, OnInit } from "@angular/core";
import { RouterModule } from "@angular/router";
import {
combineLatest,
@@ -11,11 +11,20 @@ import {
switchMap,
} from "rxjs";
import { PremiumUpgradeDialogComponent } from "@bitwarden/angular/billing/components";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { NudgesService, NudgeType } from "@bitwarden/angular/vault";
import { SpotlightComponent } from "@bitwarden/angular/vault/components/spotlight/spotlight.component";
import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions";
import { UserId } from "@bitwarden/common/types/guid";
import { BadgeComponent, ItemModule } from "@bitwarden/components";
import {
BadgeComponent,
DialogService,
ItemModule,
LinkModule,
TypographyModule,
} from "@bitwarden/components";
import { CurrentAccountComponent } from "../../../auth/popup/account-switching/current-account.component";
import { AutofillBrowserSettingsService } from "../../../autofill/services/autofill-browser-settings.service";
@@ -24,8 +33,6 @@ import { PopOutComponent } from "../../../platform/popup/components/pop-out.comp
import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component";
import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component";
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
templateUrl: "settings-v2.component.html",
imports: [
@@ -38,7 +45,11 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co
ItemModule,
CurrentAccountComponent,
BadgeComponent,
SpotlightComponent,
TypographyModule,
LinkModule,
],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SettingsV2Component implements OnInit {
NudgeType = NudgeType;
@@ -50,6 +61,11 @@ export class SettingsV2Component implements OnInit {
shareReplay({ bufferSize: 1, refCount: true }),
);
protected hasPremium$ = this.authenticatedAccount$.pipe(
switchMap((account) => this.accountProfileStateService.hasPremiumFromAnySource$(account.id)),
shareReplay({ bufferSize: 1, refCount: true }),
);
showDownloadBitwardenNudge$: Observable<boolean> = this.authenticatedAccount$.pipe(
switchMap((account) =>
this.nudgesService.showNudgeBadge$(NudgeType.DownloadBitwarden, account.id),
@@ -79,8 +95,14 @@ export class SettingsV2Component implements OnInit {
private readonly nudgesService: NudgesService,
private readonly accountService: AccountService,
private readonly autofillBrowserSettingsService: AutofillBrowserSettingsService,
private readonly accountProfileStateService: BillingAccountProfileStateService,
private readonly dialogService: DialogService,
) {}
openUpgradeDialog() {
PremiumUpgradeDialogComponent.open(this.dialogService);
}
async ngOnInit() {
this.isBrowserAutofillSettingOverridden =
await this.autofillBrowserSettingsService.isBrowserAutofillSettingOverridden(

View File

@@ -6,12 +6,6 @@
</popup-header>
<bit-item-group>
<bit-item *ngIf="!(canAccessPremium$ | async)">
<a type="button" bit-item-content routerLink="/premium">
{{ "premiumMembership" | i18n }}
<i slot="end" class="bwi bwi-angle-right" aria-hidden="true"></i>
</a>
</bit-item>
<bit-item
*ngIf="
(familySponsorshipAvailable$ | async) &&

View File

@@ -3,20 +3,20 @@
>
<div class="tw-flex tw-justify-between tw-items-start tw-flex-grow">
<div>
<h2 bitTypography="h4" class="tw-font-semibold !tw-mb-1">{{ title }}</h2>
<h2 bitTypography="h4" class="tw-font-semibold !tw-mb-1">{{ title() }}</h2>
<p
*ngIf="subtitle"
*ngIf="subtitle()"
class="tw-text-main tw-mb-0"
bitTypography="body2"
[innerHTML]="subtitle"
[innerHTML]="subtitle()"
></p>
<ng-content *ngIf="!subtitle"></ng-content>
<ng-content *ngIf="!subtitle()"></ng-content>
</div>
<button
type="button"
bitIconButton="bwi-close"
size="small"
*ngIf="!persistent"
*ngIf="!persistent()"
(click)="handleDismiss()"
class="-tw-me-2"
[label]="'close' | i18n"
@@ -28,10 +28,10 @@
bitButton
type="button"
buttonType="primary"
*ngIf="buttonText"
*ngIf="buttonText()"
(click)="handleButtonClick($event)"
>
{{ buttonText }}
<i *ngIf="buttonIcon" [ngClass]="buttonIcon" class="bwi tw-ml-1" aria-hidden="true"></i>
{{ buttonText() }}
<i *ngIf="buttonIcon()" [ngClass]="buttonIcon()" class="bwi tw-ml-1" aria-hidden="true"></i>
</button>
</div>

View File

@@ -1,43 +1,28 @@
import { CommonModule } from "@angular/common";
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { ChangeDetectionStrategy, Component, input, output } from "@angular/core";
import { ButtonModule, IconButtonModule, TypographyModule } from "@bitwarden/components";
import { I18nPipe } from "@bitwarden/ui-common";
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
selector: "bit-spotlight",
templateUrl: "spotlight.component.html",
imports: [ButtonModule, CommonModule, IconButtonModule, I18nPipe, TypographyModule],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SpotlightComponent {
// The title of the component
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
// eslint-disable-next-line @angular-eslint/prefer-signals
@Input({ required: true }) title: string | null = null;
readonly title = input<string | null>(null);
// The subtitle of the component
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
// eslint-disable-next-line @angular-eslint/prefer-signals
@Input() subtitle?: string | null = null;
readonly subtitle = input<string | null>(null);
// The text to display on the button
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
// eslint-disable-next-line @angular-eslint/prefer-signals
@Input() buttonText?: string;
readonly buttonText = input<string | null>(null);
// Wheter the component can be dismissed, if true, the component will not show a close button
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
// eslint-disable-next-line @angular-eslint/prefer-signals
@Input() persistent = false;
readonly persistent = input(false);
// Optional icon to display on the button
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
// eslint-disable-next-line @angular-eslint/prefer-signals
@Input() buttonIcon: string | null = null;
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
// eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref
@Output() onDismiss = new EventEmitter<void>();
// FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals
// eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref
@Output() onButtonClick = new EventEmitter();
readonly buttonIcon = input<string | null>(null);
readonly onDismiss = output<void>();
readonly onButtonClick = output<MouseEvent>();
handleButtonClick(event: MouseEvent): void {
this.onButtonClick.emit(event);