mirror of
https://github.com/bitwarden/browser
synced 2026-02-10 05:30:01 +00:00
monitor initialization
This commit is contained in:
@@ -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$ };
|
||||
|
||||
11
libs/common/src/tools/achievements/meta.ts
Normal file
11
libs/common/src/tools/achievements/meta.ts
Normal 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 };
|
||||
Reference in New Issue
Block a user