mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 05:30:01 +00:00
Services
Naive EventStore (will be replaced by AchievementHub) AchievementService functionality overlaps with AchievementHub, besides retrieving Achievement configuration AchievementNotifier subscribes to the AchievementService and filters on AchievementEarned and per Device - Needs to also be migrated to listen to the AchievementHub
This commit is contained in:
@@ -245,6 +245,10 @@ import { AuditService } from "@bitwarden/common/services/audit.service";
|
||||
import { EventCollectionService } from "@bitwarden/common/services/event/event-collection.service";
|
||||
import { EventUploadService } from "@bitwarden/common/services/event/event-upload.service";
|
||||
import { SearchService } from "@bitwarden/common/services/search.service";
|
||||
import { AchievementService as AchievementServiceAbstraction } from "@bitwarden/common/tools/achievements/achievement.service.abstraction";
|
||||
import { DefaultAchievementService } from "@bitwarden/common/tools/achievements/default-achievement.service";
|
||||
import { EventStore } from "@bitwarden/common/tools/achievements/event-store";
|
||||
import { EventStoreAbstraction } from "@bitwarden/common/tools/achievements/event-store.abstraction.service";
|
||||
import {
|
||||
PasswordStrengthService,
|
||||
PasswordStrengthServiceAbstraction,
|
||||
@@ -329,6 +333,8 @@ import { NoopViewCacheService } from "../platform/services/noop-view-cache.servi
|
||||
import { AngularThemingService } from "../platform/services/theming/angular-theming.service";
|
||||
import { AbstractThemingService } from "../platform/services/theming/theming.service.abstraction";
|
||||
import { safeProvider, SafeProvider } from "../platform/utils/safe-provider";
|
||||
import { AchievementNotifierService as AchievementNotifierServiceAbstraction } from "../tools/achievements/achievement-notifier.abstraction";
|
||||
import { AchievementNotifierService } from "../tools/achievements/achievement-notifier.service";
|
||||
|
||||
import {
|
||||
LOCALES_DIRECTORY,
|
||||
@@ -1482,6 +1488,27 @@ const safeProviders: SafeProvider[] = [
|
||||
ToastService,
|
||||
],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: EventStoreAbstraction,
|
||||
useClass: EventStore,
|
||||
deps: [],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: AchievementServiceAbstraction,
|
||||
useClass: DefaultAchievementService,
|
||||
deps: [EventStoreAbstraction],
|
||||
}),
|
||||
safeProvider({
|
||||
provide: AchievementNotifierServiceAbstraction,
|
||||
useClass: AchievementNotifierService,
|
||||
deps: [
|
||||
AccountServiceAbstraction,
|
||||
AchievementServiceAbstraction,
|
||||
PlatformUtilsServiceAbstraction,
|
||||
I18nServiceAbstraction,
|
||||
ToastService,
|
||||
],
|
||||
}),
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
export abstract class AchievementNotifierService {
|
||||
abstract init(): Promise<void>;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { firstValueFrom, switchMap, tap } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||
import { AchievementService } from "@bitwarden/common/tools/achievements/achievement.service.abstraction";
|
||||
import { ToastService } from "@bitwarden/components";
|
||||
|
||||
import { AchievementNotifierService as AchievementNotifierServiceAbstraction } from "./achievement-notifier.abstraction";
|
||||
|
||||
export class AchievementNotifierService implements AchievementNotifierServiceAbstraction {
|
||||
constructor(
|
||||
private accountService: AccountService,
|
||||
private achievementService: AchievementService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
private i18nService: I18nService,
|
||||
private toastService: ToastService,
|
||||
) {}
|
||||
|
||||
async init() {
|
||||
await this.setupListeners();
|
||||
}
|
||||
|
||||
private async setupListeners() {
|
||||
// FIXME Implement achievements earned filter and notififer
|
||||
/* Get the userId from the accountService
|
||||
* Subscribe to achievementService.achievementsEarned$(userId)
|
||||
* Retrieve current device and filter out messages that are not for this client/device (achievements should be only shown on the device that earned them)
|
||||
* Retrieve Achievement by AchievementId via the achievementService
|
||||
* Use information from Achievement to fill out the options for the notification (toast)
|
||||
* Invoke showing toast
|
||||
*/
|
||||
// FIXME getClientType browswer and achievementEarned.service.name.extension won't match
|
||||
const account = await firstValueFrom(this.accountService.activeAccount$);
|
||||
this.achievementService
|
||||
.achievementsEarned$(account.id)
|
||||
.pipe(
|
||||
// Removing filter for testing purposes
|
||||
// filter(achievementEarned => achievementEarned.service.name == this.platformUtilsService.getClientType())).pipe(
|
||||
switchMap((earned) => this.achievementService.achievementById$(earned.achievement.name)),
|
||||
tap((achievement) => {
|
||||
//eslint-disable-next-line no-console
|
||||
console.log(achievement);
|
||||
}),
|
||||
)
|
||||
.subscribe((achievement) => {
|
||||
this.toastService.showToast({
|
||||
variant: "info",
|
||||
title: achievement.name,
|
||||
message: achievement.description,
|
||||
});
|
||||
});
|
||||
|
||||
// FIXME Migrate to use achievementHub.earned$() instead of achievementService.achievementsEarned$
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { Achievement, AchievementEarnedEvent, AchievementProgressEvent } from "./types";
|
||||
|
||||
export abstract class AchievementService {
|
||||
abstract achievementById$: (achievementId: string) => Observable<Achievement>;
|
||||
|
||||
abstract achievementsEarned$: (userId: UserId) => Observable<AchievementEarnedEvent>;
|
||||
abstract achievementsInProgress$: (userId: UserId) => Observable<AchievementProgressEvent>;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { filter, find, from, Observable } from "rxjs";
|
||||
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { AchievementService as AchievementServiceAbstraction } from "./achievement.service.abstraction";
|
||||
import { EventStoreAbstraction } from "./event-store.abstraction.service";
|
||||
import {
|
||||
VaultItems_1_Added_Achievement,
|
||||
VaultItems_10_Added_Achievement,
|
||||
} from "./examples/achievements";
|
||||
import { isEarnedEvent, isProgressEvent } from "./meta";
|
||||
import {
|
||||
Achievement,
|
||||
AchievementEarnedEvent,
|
||||
AchievementEvent,
|
||||
AchievementId,
|
||||
AchievementProgressEvent,
|
||||
} from "./types";
|
||||
|
||||
// Service might be deprecated in favor of the AchievmentHub
|
||||
// The hub is currently missing a way of listing all achievements, finding by id, but that could be possibly done via the AchievementManager
|
||||
export class DefaultAchievementService implements AchievementServiceAbstraction {
|
||||
private _achievements: Achievement[] = [
|
||||
VaultItems_1_Added_Achievement,
|
||||
VaultItems_10_Added_Achievement,
|
||||
];
|
||||
|
||||
private _achievementsSubject = from(this._achievements);
|
||||
|
||||
achievementById$: (achievementId: string) => Observable<Achievement>;
|
||||
|
||||
// Provided by the AchievementHub
|
||||
achievementsEarned$: (userId: UserId) => Observable<AchievementEarnedEvent>;
|
||||
|
||||
// Provided by the AchievementHub
|
||||
achievementsInProgress$: (userId: UserId) => Observable<AchievementProgressEvent>;
|
||||
|
||||
constructor(protected eventStore: EventStoreAbstraction) {
|
||||
this.achievementById$ = (achievementId: AchievementId) =>
|
||||
this._achievementsSubject.pipe(find((item: Achievement) => item.name === achievementId));
|
||||
|
||||
this.achievementsEarned$ = (userId: UserId) => {
|
||||
return this.eventStore.events$.pipe(
|
||||
filter(
|
||||
(event): event is AchievementEarnedEvent =>
|
||||
isEarnedEvent(event as AchievementEvent) && event.user.id === userId,
|
||||
),
|
||||
);
|
||||
};
|
||||
|
||||
this.achievementsInProgress$ = (userId: UserId) => {
|
||||
return this.eventStore.events$.pipe(
|
||||
filter(
|
||||
(event): event is AchievementProgressEvent =>
|
||||
isProgressEvent(event as AchievementEvent) && event.user.id === userId,
|
||||
),
|
||||
);
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import { AchievementEarnedEvent, AchievementProgressEvent, UserActionEvent } from "./types";
|
||||
|
||||
export abstract class EventStoreAbstraction {
|
||||
abstract events$: Observable<UserActionEvent | AchievementProgressEvent | AchievementEarnedEvent>;
|
||||
abstract addEvent(
|
||||
event: UserActionEvent | AchievementProgressEvent | AchievementEarnedEvent,
|
||||
): boolean;
|
||||
}
|
||||
22
libs/common/src/tools/achievements/event-store.ts
Normal file
22
libs/common/src/tools/achievements/event-store.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Observable, Subject } from "rxjs";
|
||||
|
||||
import { EventStoreAbstraction } from "./event-store.abstraction.service";
|
||||
import { AchievementEarnedEvent, AchievementProgressEvent } from "./types";
|
||||
|
||||
// Will be replaced by the achievementHub
|
||||
export class EventStore implements EventStoreAbstraction {
|
||||
private _events = new Subject<AchievementProgressEvent | AchievementEarnedEvent>();
|
||||
|
||||
events$: Observable<AchievementProgressEvent | AchievementEarnedEvent> =
|
||||
this._events.asObservable();
|
||||
|
||||
constructor() {}
|
||||
|
||||
addEvent(event: AchievementProgressEvent | AchievementEarnedEvent): boolean {
|
||||
// FIXME Collapse existing of same metric/higher count AchievementProgressEvents
|
||||
//eslint-disable-next-line no-console
|
||||
console.log("EventStore.addEvent", event);
|
||||
this._events.next(event);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user