diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 127e07f25e8..4db8952c92c 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -5187,5 +5187,8 @@ }, "changeAtRiskPassword": { "message": "Change at-risk password" + }, + "achievements": { + "message": "Achievements" } } diff --git a/apps/browser/src/auth/popup/account-switching/account-switcher.component.html b/apps/browser/src/auth/popup/account-switching/account-switcher.component.html index de8ab4c7b08..bd8c025db5a 100644 --- a/apps/browser/src/auth/popup/account-switching/account-switcher.component.html +++ b/apps/browser/src/auth/popup/account-switching/account-switcher.component.html @@ -13,6 +13,27 @@ + + + +

Achievements

+
+ + + + See all achievements + + + + +
+

{{ "availableAccounts" | i18n }}

diff --git a/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts b/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts index 78bee121afb..c7db6f89812 100644 --- a/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts +++ b/apps/browser/src/auth/popup/account-switching/account-switcher.component.ts @@ -1,6 +1,6 @@ import { CommonModule, Location } from "@angular/common"; import { Component, OnDestroy, OnInit } from "@angular/core"; -import { Router } from "@angular/router"; +import { Router, RouterLink } from "@angular/router"; import { Subject, firstValueFrom, map, of, startWith, switchMap } from "rxjs"; import { JslibModule } from "@bitwarden/angular/jslib.module"; @@ -50,6 +50,7 @@ import { AccountSwitcherService } from "./services/account-switcher.service"; SectionComponent, SectionHeaderComponent, TypographyModule, + RouterLink, ], }) export class AccountSwitcherComponent implements OnInit, OnDestroy { diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index b33940a68d2..05f656f2f4b 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -71,6 +71,7 @@ import { NotificationsSettingsComponent } from "../autofill/popup/settings/notif import { PremiumV2Component } from "../billing/popup/settings/premium-v2.component"; import BrowserPopupUtils from "../platform/popup/browser-popup-utils"; import { popupRouterCacheGuard } from "../platform/popup/view-cache/popup-router-cache.service"; +import { AchievementsComponent } from "../tools/popup/achievements/achievements.component"; import { CredentialGeneratorHistoryComponent } from "../tools/popup/generator/credential-generator-history.component"; import { CredentialGeneratorComponent } from "../tools/popup/generator/credential-generator.component"; import { SendAddEditComponent as SendAddEditV2Component } from "../tools/popup/send-v2/add-edit/send-add-edit.component"; @@ -232,6 +233,12 @@ const routes: Routes = [ canActivate: [unauthGuardFn(unauthRouteOverrides)], data: { elevation: 1 } satisfies RouteDataProperties, }, + { + path: "achievements", + component: AchievementsComponent, + canActivate: [authGuard], + data: { elevation: 1 } satisfies RouteDataProperties, + }, { path: "view-cipher", component: ViewV2Component, diff --git a/apps/browser/src/popup/services/init.service.ts b/apps/browser/src/popup/services/init.service.ts index fe6fba85a4b..90eff1e9499 100644 --- a/apps/browser/src/popup/services/init.service.ts +++ b/apps/browser/src/popup/services/init.service.ts @@ -2,6 +2,7 @@ import { DOCUMENT } from "@angular/common"; import { inject, Inject, Injectable } from "@angular/core"; import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction"; +import { AchievementNotifierService } from "@bitwarden/angular/tools/achievements/achievement-notifier.abstraction"; import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service"; @@ -26,6 +27,7 @@ export class InitService { private logService: LogServiceAbstraction, private themingService: AbstractThemingService, private sdkLoadService: SdkLoadService, + private achievementNotifierService: AchievementNotifierService, private viewCacheService: PopupViewCacheService, @Inject(DOCUMENT) private document: Document, ) {} @@ -38,6 +40,7 @@ export class InitService { this.twoFactorService.init(); await this.viewCacheService.init(); await this.sizeService.init(); + await this.achievementNotifierService.init(); const htmlEl = window.document.documentElement; this.themingService.applyThemeChangesTo(this.document); diff --git a/apps/browser/src/tools/popup/achievements/achievements.component.html b/apps/browser/src/tools/popup/achievements/achievements.component.html new file mode 100644 index 00000000000..73defa852e9 --- /dev/null +++ b/apps/browser/src/tools/popup/achievements/achievements.component.html @@ -0,0 +1,13 @@ + + + + + + + + + + + diff --git a/apps/browser/src/tools/popup/achievements/achievements.component.ts b/apps/browser/src/tools/popup/achievements/achievements.component.ts new file mode 100644 index 00000000000..3d008bdc8ee --- /dev/null +++ b/apps/browser/src/tools/popup/achievements/achievements.component.ts @@ -0,0 +1,66 @@ +import { CommonModule } from "@angular/common"; +import { Component, OnInit } from "@angular/core"; +import { firstValueFrom } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { EventStoreAbstraction } from "@bitwarden/common/tools/achievements/event-store.abstraction.service"; +import { VaultItems_10_Added_Achievement } from "@bitwarden/common/tools/achievements/examples/achievements"; +import { AchievementEarnedEvent, AchievementId } from "@bitwarden/common/tools/achievements/types"; +import { UserId } from "@bitwarden/common/types/guid"; +import { ButtonModule, IconModule } from "@bitwarden/components"; + +import { PopOutComponent } from "../../../platform/popup/components/pop-out.component"; +import { PopupFooterComponent } from "../../../platform/popup/layout/popup-footer.component"; +import { PopupHeaderComponent } from "../../../platform/popup/layout/popup-header.component"; +import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.component"; + +@Component({ + templateUrl: "achievements.component.html", + standalone: true, + imports: [ + CommonModule, + JslibModule, + PopupPageComponent, + PopupHeaderComponent, + PopupFooterComponent, + PopOutComponent, + ButtonModule, + IconModule, + ], +}) +export class AchievementsComponent implements OnInit { + private currentUserId: UserId; + + constructor( + private eventStore: EventStoreAbstraction, + private accountService: AccountService, + ) {} + + async ngOnInit() { + this.currentUserId = (await firstValueFrom(this.accountService.activeAccount$)).id; + } + + testAchievement() { + const earnedAchievement: AchievementEarnedEvent = { + "@timestamp": Date.now(), + event: { + kind: "alert", + category: "session", + }, + service: { + name: "web", + type: "client", + node: { + name: "an-installation-identifier-for-this-client-instance", + }, + environment: "local", + version: "2025.3.1-innovation-sprint", + }, + user: { id: this.currentUserId }, + achievement: { type: "earned", name: VaultItems_10_Added_Achievement.name as AchievementId }, + }; + + this.eventStore.addEvent(earnedAchievement); + } +}