1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-24 16:43:27 +00:00

saved WIP

This commit is contained in:
jng
2025-07-17 18:16:07 -04:00
parent 127fed70ac
commit a8c6e3ffe2
4 changed files with 88 additions and 13 deletions

View File

@@ -4264,7 +4264,7 @@
},
"uriMatchDefaultStrategyHint": {
"message": "URI match detection is how Bitwarden identifies autofill suggestions.",
"description": "Explains to the user that URI match detection determines how Bitwarden suggests autofill options, and clarifies that this default strategy applies when no specific match detection is set for a login item."
"description": "Explains to the user that URI match detection determines how Bitwarden suggests autofill options, and clarifies that this default strategy applies when no specific match detection is set for a login item."
},
"regExAdvancedOptionWarning": {
"message": "\"Regular expression\" is an advanced option with increased risk of exposing credentials.",
@@ -5457,5 +5457,8 @@
"wasmNotSupported": {
"message": "WebAssembly is not supported on your browser or is not enabled. WebAssembly is required to use the Bitwarden app.",
"description": "'WebAssembly' is a technical term and should not be translated."
},
"atRiskLoginsSecured": {
"message": "Great job securing your at-risk logins!"
}
}

View File

@@ -1,9 +1,21 @@
<bit-callout *ngIf="(pendingTasks$ | async)?.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'">
{{
(taskCount === 1 ? "reviewAndChangeAtRiskPassword" : "reviewAndChangeAtRiskPasswordsPlural")
| i18n: taskCount.toString()
}}
</a>
</bit-callout>
@if (currentPendingTasks()) {
<bit-callout *ngIf="(pendingTasks$ | async)?.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'">
{{
(taskCount === 1 ? "reviewAndChangeAtRiskPassword" : "reviewAndChangeAtRiskPasswordsPlural")
| i18n: taskCount.toString()
}}
</a>
</bit-callout>
}
@if (showTasksResolved()) {
<bit-banner
class="-tw-m-6 tw-flex tw-flex-col tw-pb-6 tw-pt-2"
bannerType="info"
[showClose]="true"
>
{{ "atRiskLoginsSecured" | i18n }}
</bit-banner>
}

View File

@@ -1,24 +1,55 @@
import { CommonModule } from "@angular/common";
import { Component, inject } from "@angular/core";
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 { AnchorLinkDirective, CalloutModule } from "@bitwarden/components";
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"],
},
);
@Component({
selector: "vault-at-risk-password-callout",
imports: [CommonModule, AnchorLinkDirective, RouterModule, CalloutModule, I18nPipe],
imports: [
CommonModule,
AnchorLinkDirective,
RouterModule,
CalloutModule,
I18nPipe,
BannerModule,
JslibModule,
],
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 userIdSignal = toSignal(this.activeAccount$, { initialValue: null });
protected pendingTasks$ = this.activeAccount$.pipe(
switchMap((userId) =>
@@ -39,4 +70,32 @@ export class AtRiskPasswordCalloutComponent {
}),
),
);
private atRiskPasswordStateSignal = toSignal(
this.atRiskPasswordState(this.userIdSignal()!).state$,
);
currentPendingTasks = toSignal(this.pendingTasks$, { initialValue: [] });
showTasksResolved = computed(() => {
if (this.atRiskPasswordStateSignal() && this.currentPendingTasks().length === 0) {
return true;
}
});
constructor(private stateProvider: StateProvider) {
effect(() => {
if (this.currentPendingTasks().length > 0) {
this.updateAtRiskPasswordState(this.userIdSignal()!, true);
}
});
}
private atRiskPasswordState(userId: UserId) {
return this.stateProvider.getUser(userId, AT_RISK_ITEMS);
}
private updateAtRiskPasswordState(userId: UserId, hasAtRiskPassword: boolean) {
void this.atRiskPasswordState(userId).update(() => hasAtRiskPassword);
}
}

View File

@@ -206,3 +206,4 @@ export const VAULT_BROWSER_INTRO_CAROUSEL = new StateDefinition(
"vaultBrowserIntroCarousel",
"disk",
);
export const VAULT_AT_RISK_PASSWORDS_DISK = new StateDefinition("vaultAtRiskPasswords", "disk");