diff --git a/apps/browser/src/vault/popup/components/at-risk-callout/at-risk-password-callout.component.html b/apps/browser/src/vault/popup/components/at-risk-callout/at-risk-password-callout.component.html
index 6426a8958f7..ea71829c9aa 100644
--- a/apps/browser/src/vault/popup/components/at-risk-callout/at-risk-password-callout.component.html
+++ b/apps/browser/src/vault/popup/components/at-risk-callout/at-risk-password-callout.component.html
@@ -10,11 +10,12 @@
}
-@if (showTasksResolved()) {
+@if (showTasksResolvedBanner) {
{{ "atRiskLoginsSecured" | i18n }}
diff --git a/apps/browser/src/vault/popup/components/at-risk-callout/at-risk-password-callout.component.ts b/apps/browser/src/vault/popup/components/at-risk-callout/at-risk-password-callout.component.ts
index 873271f6342..5347690824b 100644
--- a/apps/browser/src/vault/popup/components/at-risk-callout/at-risk-password-callout.component.ts
+++ b/apps/browser/src/vault/popup/components/at-risk-callout/at-risk-password-callout.component.ts
@@ -1,12 +1,11 @@
import { CommonModule } from "@angular/common";
-import { Component, computed, inject, effect } from "@angular/core";
+import { Component, inject, effect } from "@angular/core";
import { toSignal } from "@angular/core/rxjs-interop";
import { RouterModule } from "@angular/router";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
-import { StateProvider } from "@bitwarden/common/platform/state";
import { AnchorLinkDirective, CalloutModule, BannerModule } from "@bitwarden/components";
import { I18nPipe } from "@bitwarden/ui-common";
import {
@@ -35,6 +34,13 @@ export class AtRiskPasswordCalloutComponent {
private atRiskPasswordStateSignal = toSignal(
this.atRiskPasswordCalloutService.atRiskPasswordState(this.userIdSignal()!).state$,
+ {
+ initialValue: {
+ hadPendingTasks: false,
+ showTasksCompleteBanner: false,
+ tasksBannerDismissed: false,
+ } as AtRiskPasswordCalloutData,
+ },
);
currentPendingTasks = toSignal(
@@ -44,14 +50,37 @@ export class AtRiskPasswordCalloutComponent {
},
);
- showTasksResolved = computed(() => {
- if (this.atRiskPasswordStateSignal() && this.currentPendingTasks().length === 0) {
- return true;
- }
- });
+ showTasksResolvedBanner: boolean = false;
- constructor(private stateProvider: StateProvider) {
+ constructor() {
effect(() => {
+ // If the user had the banner showing and left the extension, when they come back the banner should still appear
+ if (
+ this.atRiskPasswordStateSignal()?.showTasksCompleteBanner &&
+ this.currentPendingTasks().length === 0 &&
+ !this.atRiskPasswordStateSignal()?.hadPendingTasks
+ ) {
+ this.showTasksResolvedBanner = true;
+ }
+
+ // If the user has resolved all tasks, we will show the banner
+ if (
+ this.atRiskPasswordStateSignal()?.hadPendingTasks &&
+ this.currentPendingTasks().length === 0
+ ) {
+ const updateObject: AtRiskPasswordCalloutData = {
+ hadPendingTasks: false,
+ showTasksCompleteBanner: true,
+ tasksBannerDismissed: false,
+ };
+ this.atRiskPasswordCalloutService.updateAtRiskPasswordState(
+ this.userIdSignal()!,
+ updateObject,
+ );
+ this.showTasksResolvedBanner = true;
+ }
+
+ // Will show callout, will remove any previous dismissed banner state
if (this.currentPendingTasks().length > 0) {
const updateObject: AtRiskPasswordCalloutData = {
hadPendingTasks: true,
@@ -65,4 +94,15 @@ export class AtRiskPasswordCalloutComponent {
}
});
}
+
+ successBannerDismissed() {
+ // If the user dismisses the banner, we will update the state to reflect that
+ const updateObject: AtRiskPasswordCalloutData = {
+ hadPendingTasks: false,
+ showTasksCompleteBanner: false,
+ tasksBannerDismissed: true,
+ };
+ this.atRiskPasswordCalloutService.updateAtRiskPasswordState(this.userIdSignal()!, updateObject);
+ this.showTasksResolvedBanner = false;
+ }
}
diff --git a/apps/web/src/app/vault/services/at-risk-password-callout.service.ts b/apps/web/src/app/vault/services/at-risk-password-callout.service.ts
index 3af5433adf7..a0a306c5837 100644
--- a/apps/web/src/app/vault/services/at-risk-password-callout.service.ts
+++ b/apps/web/src/app/vault/services/at-risk-password-callout.service.ts
@@ -2,13 +2,14 @@ import { Injectable } from "@angular/core";
import { combineLatest, map, Observable } from "rxjs";
import {
+ SingleUserState,
StateProvider,
UserKeyDefinition,
VAULT_AT_RISK_PASSWORDS_DISK,
} from "@bitwarden/common/platform/state";
-import { UserId } from "@bitwarden/common/types/guid";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { SecurityTask, SecurityTaskType, TaskService } from "@bitwarden/common/vault/tasks";
+import { UserId } from "@bitwarden/user-core";
export type AtRiskPasswordCalloutData = {
hadPendingTasks: boolean;
@@ -52,7 +53,7 @@ export class AtRiskPasswordCalloutService {
);
}
- atRiskPasswordState(userId: UserId) {
+ atRiskPasswordState(userId: UserId): SingleUserState {
return this.stateProvider.getUser(userId, AT_RISK_PASSWORD_CALLOUT_KEY);
}