1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-26 17:43:22 +00:00

even more cleanup

This commit is contained in:
✨ Audrey ✨
2025-03-20 22:16:30 -04:00
parent bf089705a7
commit 786603559a
16 changed files with 82 additions and 174 deletions

View File

@@ -2,6 +2,7 @@ import { UserId } from "../../types/guid";
import { AchievementEarnedEvent, AchievementId, AchievementProgressEvent, MetricId } from "./types";
// FIXME: see <./types.ts> AchievementValidator
export function progressEvent(name: MetricId, value: number = 1): AchievementProgressEvent {
return {
"@timestamp": Date.now(),
@@ -25,6 +26,7 @@ export function progressEvent(name: MetricId, value: number = 1): AchievementPro
};
}
// FIXME: see <./types.ts> AchievementValidator
export function earnedEvent(name: AchievementId): AchievementEarnedEvent {
return {
"@timestamp": Date.now(),

View File

@@ -97,9 +97,4 @@ export class AchievementHub {
startWith(new Map<MetricId, AchievementProgressEvent>()),
);
}
//Test methods
addEvent(event: AchievementEvent) {
this.achievementLog.next(event);
}
}

View File

@@ -12,7 +12,7 @@ import { SendItemCreatedCountConfig } from "./validators/config/send-created-cou
import { SendItemCreatedCountValidator } from "./validators/send-item-created-count-validator";
import { VaultItemCreatedCountValidator } from "./validators/vault-item-created-count-validator";
export class NextAchievementService implements AchievementService {
export class DefaultAchievementService implements AchievementService {
constructor(private readonly collector: UserEventCollector) {}
private hubs = new Map<string, AchievementHub>();

View File

@@ -1,10 +0,0 @@
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;
}

View File

@@ -1,22 +0,0 @@
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;
}
}

View File

@@ -1,26 +0,0 @@
import { Subject } from "rxjs";
import { Achievement, AchievementEvent, AchievementValidator, UserActionEvent } from "./types";
// sync data from the server (consumed by event store)
const replicationIn$ = new Subject<AchievementEvent>();
// data incoming from the UI (consumed by validator)
const userActionIn$ = new Subject<UserActionEvent>();
// what to look for (consumed by validator)
const achievementMonitors$ = new Subject<AchievementValidator[]>();
// data stored in local state (consumed by validator and achievement list)
const achievementsLocal$ = new Subject<AchievementEvent[]>();
// metadata (consumed by achievement list)
const achievementMetadata$ = new Subject<Achievement>();
export {
replicationIn$,
userActionIn$,
achievementsLocal$,
achievementMonitors$,
achievementMetadata$,
};

View File

@@ -21,49 +21,67 @@ export type AchievementEarnedEvent = EventFormat &
export type AchievementEvent = AchievementProgressEvent | AchievementEarnedEvent;
type MetricCriteria = {
// the metric observed by low/high triggers
/** the metric observed by low/high triggers */
metric: MetricId;
} & RequireAtLeastOne<{
// criteria fail when the metric is less than or equal to `low`
/** criteria fail when the metric is less than or equal to `low` */
low: number;
// criteria fail when the metric is greater than `high`
/** criteria fail when the metric is greater than `high` */
high: number;
}>;
type ActiveCriteria = "until-earned" | MetricCriteria;
// consumed by validator and achievement list (should this include a "toast-alerter"?)
/** consumed by validator and achievement list (should this include a "toast-alerter"?) */
export type Achievement = {
// identifies the achievement being monitored
/** identifies the achievement being monitored */
achievement: AchievementId;
// human-readable name of the achievement
/** human-readable name of the achievement */
name: string;
// human-readable description of the achievement
/** human-readable description of the achievement */
description?: string;
// conditions that determine when the achievement validator should be loaded
// by the processor
/* conditions that determine when the achievement validator should be loaded
* by the processor
*/
active: ActiveCriteria;
// identifies the validator containing filter/measure/earn methods
/** identifies the validator containing filter/measure/earn methods */
validator: ValidatorId;
// whether or not the achievement is hidden until it is earned
/** whether or not the achievement is hidden until it is earned */
hidden: boolean;
};
// consumed by validator
/** An achievement completion monitor */
//
// FIXME:
// * inject a monitor/capture interface into measure and rewards
// * this interface contains methods from <./achievement-events.ts>
// * it constructs context-specific events, filling in device/time/etc
export type AchievementValidator = Achievement & {
// when the watch triggers on incoming user events
trigger: (item: UserActionEvent) => boolean;
/** when the watch triggers on incoming user events
* @param event a monitored user action event
* @returns true when the validator should process the event, otherwise false.
*/
trigger: (event: UserActionEvent) => boolean;
// observe data from the event stream and produces measurements
measure?: (item: UserActionEvent, metrics: Map<MetricId, number>) => AchievementProgressEvent[];
/** observes data from the event stream and produces measurements;
* this runs after the event is triggered.
* @param event a monitored user action event
* @returns a collection of measurements extracted from the event (may be empty)
*/
measure?: (event: UserActionEvent, metrics: Map<MetricId, number>) => AchievementProgressEvent[];
// monitors achievement progress and emits earned achievements
/** monitors achievement progress and emits earned achievements;
* this runs after all measurements are taken.
* @param progress events emitted by `measure`. If `measure` is undefined, this is an empty array.
* @param metrics last-recorded progress value for all achievement metrics
* @returns a collection of achievements awarded by the event.
*/
award?: (
events: AchievementProgressEvent[],
progress: AchievementProgressEvent[],
metrics: Map<MetricId, number>,
) => AchievementEarnedEvent[];
};

View File

@@ -1,11 +0,0 @@
import { ItemCreatedProgressEvent } from "./examples/achievement-events";
import { ItemCreatedProgress } from "./examples/example-validators";
import { mapProgressByName } from "./util";
describe("mapProgressByName", () => {
it("creates a map containing a progress value", () => {
const result = mapProgressByName([ItemCreatedProgressEvent]);
expect(result.get(ItemCreatedProgress)).toEqual(ItemCreatedProgressEvent.achievement.value);
});
});

View File

@@ -1,8 +0,0 @@
import { isProgressEvent } from "./meta";
import { AchievementEvent } from "./types";
export function mapProgressByName(status: AchievementEvent[]) {
return new Map(
status.filter(isProgressEvent).map((e) => [e.achievement.name, e.achievement.value] as const),
);
}