mirror of
https://github.com/bitwarden/browser
synced 2026-02-21 03:43:58 +00:00
created at risk password callout service to hold state for callout data. wip
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
@if (currentPendingTasks()) {
|
||||
<bit-callout *ngIf="(pendingTasks$ | async)?.length as taskCount" type="warning" [title]="''">
|
||||
@if (currentPendingTasks().length > 0) {
|
||||
<bit-callout *ngIf="currentPendingTasks().length as taskCount" type="warning" [title]="''">
|
||||
<i class="bwi bwi-exclamation-triangle tw-text-warning" aria-hidden="true"></i>
|
||||
<a bitLink [routerLink]="'/at-risk-passwords'">
|
||||
{{
|
||||
|
||||
@@ -2,35 +2,17 @@ import { CommonModule } from "@angular/common";
|
||||
import { Component, computed, inject, effect } from "@angular/core";
|
||||
import { toSignal } from "@angular/core/rxjs-interop";
|
||||
import { RouterModule } from "@angular/router";
|
||||
import { combineLatest, map, switchMap } from "rxjs";
|
||||
|
||||
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,
|
||||
UserKeyDefinition,
|
||||
VAULT_AT_RISK_PASSWORDS_DISK,
|
||||
} from "@bitwarden/common/platform/state";
|
||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||
import { SecurityTaskType, TaskService } from "@bitwarden/common/vault/tasks";
|
||||
import { StateProvider } from "@bitwarden/common/platform/state";
|
||||
import { AnchorLinkDirective, CalloutModule, BannerModule } from "@bitwarden/components";
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
import { UserId } from "@bitwarden/user-core";
|
||||
|
||||
// Move this state provider code and methods into a new at risk password callout service
|
||||
// a show/hide boolean for the congrats banner
|
||||
// a dismissed boolean for the banner dismissal
|
||||
// a hadPendingTasks boolean to track if the user had pending tasks previously
|
||||
|
||||
const AT_RISK_ITEMS = new UserKeyDefinition<boolean>(
|
||||
VAULT_AT_RISK_PASSWORDS_DISK,
|
||||
"atRiskPasswords",
|
||||
{
|
||||
deserializer: (atRiskItems) => atRiskItems,
|
||||
clearOn: ["logout", "lock"],
|
||||
},
|
||||
);
|
||||
import {
|
||||
AtRiskPasswordCalloutData,
|
||||
AtRiskPasswordCalloutService,
|
||||
} from "@bitwarden/web-vault/app/vault/services/at-risk-password-callout.service";
|
||||
|
||||
@Component({
|
||||
selector: "vault-at-risk-password-callout",
|
||||
@@ -43,39 +25,24 @@ const AT_RISK_ITEMS = new UserKeyDefinition<boolean>(
|
||||
BannerModule,
|
||||
JslibModule,
|
||||
],
|
||||
providers: [AtRiskPasswordCalloutService],
|
||||
templateUrl: "./at-risk-password-callout.component.html",
|
||||
})
|
||||
export class AtRiskPasswordCalloutComponent {
|
||||
private taskService = inject(TaskService);
|
||||
private cipherService = inject(CipherService);
|
||||
private activeAccount$ = inject(AccountService).activeAccount$.pipe(getUserId);
|
||||
private atRiskPasswordCalloutService = inject(AtRiskPasswordCalloutService);
|
||||
private userIdSignal = toSignal(this.activeAccount$, { initialValue: null });
|
||||
|
||||
protected pendingTasks$ = this.activeAccount$.pipe(
|
||||
switchMap((userId) =>
|
||||
combineLatest([
|
||||
this.taskService.pendingTasks$(userId),
|
||||
this.cipherService.cipherViews$(userId),
|
||||
]),
|
||||
),
|
||||
map(([tasks, ciphers]) =>
|
||||
tasks.filter((t) => {
|
||||
const associatedCipher = ciphers.find((c) => c.id === t.cipherId);
|
||||
|
||||
return (
|
||||
t.type === SecurityTaskType.UpdateAtRiskCredential &&
|
||||
associatedCipher &&
|
||||
!associatedCipher.isDeleted
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
private atRiskPasswordStateSignal = toSignal(
|
||||
this.atRiskPasswordState(this.userIdSignal()!).state$,
|
||||
this.atRiskPasswordCalloutService.atRiskPasswordState(this.userIdSignal()!).state$,
|
||||
);
|
||||
|
||||
currentPendingTasks = toSignal(this.pendingTasks$, { initialValue: [] });
|
||||
currentPendingTasks = toSignal(
|
||||
this.atRiskPasswordCalloutService.pendingTasks$(this.userIdSignal()!),
|
||||
{
|
||||
initialValue: [],
|
||||
},
|
||||
);
|
||||
|
||||
showTasksResolved = computed(() => {
|
||||
if (this.atRiskPasswordStateSignal() && this.currentPendingTasks().length === 0) {
|
||||
@@ -86,16 +53,16 @@ export class AtRiskPasswordCalloutComponent {
|
||||
constructor(private stateProvider: StateProvider) {
|
||||
effect(() => {
|
||||
if (this.currentPendingTasks().length > 0) {
|
||||
this.updateAtRiskPasswordState(this.userIdSignal()!, true);
|
||||
const updateObject: AtRiskPasswordCalloutData = {
|
||||
hadPendingTasks: true,
|
||||
showTasksCompleteBanner: false,
|
||||
tasksBannerDismissed: false,
|
||||
};
|
||||
this.atRiskPasswordCalloutService.updateAtRiskPasswordState(
|
||||
this.userIdSignal()!,
|
||||
updateObject,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private atRiskPasswordState(userId: UserId) {
|
||||
return this.stateProvider.getUser(userId, AT_RISK_ITEMS);
|
||||
}
|
||||
|
||||
private updateAtRiskPasswordState(userId: UserId, hasAtRiskPassword: boolean) {
|
||||
void this.atRiskPasswordState(userId).update(() => hasAtRiskPassword);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { combineLatest, map, Observable } from "rxjs";
|
||||
|
||||
import {
|
||||
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";
|
||||
|
||||
export type AtRiskPasswordCalloutData = {
|
||||
hadPendingTasks: boolean;
|
||||
showTasksCompleteBanner: boolean;
|
||||
tasksBannerDismissed: boolean;
|
||||
};
|
||||
|
||||
const AT_RISK_PASSWORD_CALLOUT_KEY = new UserKeyDefinition<AtRiskPasswordCalloutData>(
|
||||
VAULT_AT_RISK_PASSWORDS_DISK,
|
||||
"atRiskPasswords",
|
||||
{
|
||||
deserializer: (jsonData) => jsonData,
|
||||
clearOn: ["logout", "lock"],
|
||||
},
|
||||
);
|
||||
|
||||
@Injectable()
|
||||
export class AtRiskPasswordCalloutService {
|
||||
constructor(
|
||||
private taskService: TaskService,
|
||||
private cipherService: CipherService,
|
||||
private stateProvider: StateProvider,
|
||||
) {}
|
||||
|
||||
pendingTasks$(userId: UserId): Observable<SecurityTask[]> {
|
||||
return combineLatest([
|
||||
this.taskService.pendingTasks$(userId),
|
||||
this.cipherService.cipherViews$(userId),
|
||||
]).pipe(
|
||||
map(([tasks, ciphers]) =>
|
||||
tasks.filter((t: SecurityTask) => {
|
||||
const associatedCipher = ciphers.find((c) => c.id === t.cipherId);
|
||||
|
||||
return (
|
||||
t.type === SecurityTaskType.UpdateAtRiskCredential &&
|
||||
associatedCipher &&
|
||||
!associatedCipher.isDeleted
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
atRiskPasswordState(userId: UserId) {
|
||||
return this.stateProvider.getUser(userId, AT_RISK_PASSWORD_CALLOUT_KEY);
|
||||
}
|
||||
|
||||
updateAtRiskPasswordState(userId: UserId, updatedState: AtRiskPasswordCalloutData): void {
|
||||
void this.atRiskPasswordState(userId).update(() => updatedState);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user