1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-10 05:30:01 +00:00

monitor initialization

This commit is contained in:
✨ Audrey ✨
2025-03-11 14:14:32 -04:00
parent a6568d423a
commit 97b2766c89
2 changed files with 68 additions and 26 deletions

View File

@@ -1,34 +1,65 @@
import {
Observable,
OperatorFunction,
concatMap,
filter,
from,
map,
pipe,
withLatestFrom,
} from "rxjs";
import { Observable, OperatorFunction, concatMap, from, map, pipe, withLatestFrom } from "rxjs";
import { EventFormat } from "../log/ecs-format";
import { achievementMonitors$, achievementsLocal$, userActionIn$ } from "./inputs";
import { AchievementFormat, AchievementWatch } from "./types";
import {
achievementMonitors$,
achievementsLocal$ as achievementsLog$,
userActionIn$,
} from "./inputs";
import { isProgress } from "./meta";
import { AchievementFormat, AchievementWatch, Earned, Progress } from "./types";
// the formal even processor
function validate(
achievements$: Observable<AchievementWatch[]>,
// OPTIMIZATION: compute the list of active monitors from trigger criteria
function active(
status$: Observable<AchievementFormat[]>,
): OperatorFunction<EventFormat, AchievementFormat> {
// compute list of active monitors
const monitors$ = achievements$.pipe(
withLatestFrom(achievementsLocal$),
filter(([monitors, local]) => {
// 🧩 TODO: filter out inactive monitors by reviewing local store
// and interpreting triggers.
return true;
): OperatorFunction<AchievementWatch[], AchievementWatch[]> {
return pipe(
withLatestFrom(status$),
map(([monitors, log]) => {
// partition the log into progress and earned achievements
const progress: Progress[] = [];
const earned: Earned[] = [];
for (const l of log) {
if (isProgress(l.achievement)) {
progress.push(l.achievement);
} else {
earned.push(l.achievement);
}
}
const progressByName = new Map(progress.map((a) => [a.name, a.value]));
const earnedByName = new Set(earned.map((e) => e.name));
// compute list of active achievements
const active = monitors.filter((m) => {
if (m.trigger === "once") {
// monitor disabled if already achieved
return !earnedByName.has(m.achievement);
}
// monitor disabled if outside of threshold
const progress = progressByName.get(m.achievement) ?? 0;
if (m.trigger.high ?? Number.POSITIVE_INFINITY < progress) {
return false;
} else if (m.trigger.low ?? 0 > progress) {
return false;
}
// otherwise you're within the threshold, so the monitor is active
return true;
});
return active;
}),
);
}
// the formal event processor
function validate(
monitors$: Observable<AchievementWatch[]>,
status$: Observable<AchievementFormat[]>,
): OperatorFunction<EventFormat, AchievementFormat> {
// analyze the incoming event stream to identify achievements
const processor = pipe(
withLatestFrom(monitors$),
@@ -48,10 +79,10 @@ function validate(
return processor;
}
const liveMonitors$ = achievementMonitors$.pipe(active(achievementsLog$));
// pre-wired achievement stream; this is the prototype's host, and
// in the full version is wired by the application
const validatedAchievements$ = userActionIn$.pipe(
validate(achievementMonitors$, achievementsLocal$),
);
const validatedAchievements$ = userActionIn$.pipe(validate(liveMonitors$, achievementsLog$));
export { validate, validatedAchievements$ };

View File

@@ -0,0 +1,11 @@
import { Earned, Progress } from "./types";
function isProgress(achievement: any): achievement is Progress {
return achievement.type === "progress" && "value" in achievement;
}
function isEarned(achievement: any): achievement is Earned {
return !isProgress(achievement);
}
export { isProgress, isEarned };