From cf20a83278f2780006c19288f0ef24fd707957b0 Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 17 Mar 2025 14:28:30 -0400 Subject: [PATCH] Adding initial count validators --- .../validators/attachment-added-validator.ts | 44 +++++ .../config/item-created-count-config.ts | 150 ++++++++++++++++++ .../validators/item-folder-added-validator.ts | 39 +++++ .../vault-item-created-count-validator.ts | 55 +++++++ 4 files changed, 288 insertions(+) create mode 100644 libs/common/src/tools/achievements/validators/attachment-added-validator.ts create mode 100644 libs/common/src/tools/achievements/validators/config/item-created-count-config.ts create mode 100644 libs/common/src/tools/achievements/validators/item-folder-added-validator.ts create mode 100644 libs/common/src/tools/achievements/validators/vault-item-created-count-validator.ts diff --git a/libs/common/src/tools/achievements/validators/attachment-added-validator.ts b/libs/common/src/tools/achievements/validators/attachment-added-validator.ts new file mode 100644 index 00000000000..ee9797a2202 --- /dev/null +++ b/libs/common/src/tools/achievements/validators/attachment-added-validator.ts @@ -0,0 +1,44 @@ +import { earnedEvent } from "../achievement-events"; +import { Type } from "../data"; +import { + AchievementId, + AchievementProgressEvent, + AchievementValidator, + MetricId, + UserActionEvent, +} from "../types"; + +export class AttachmentAddedValidator implements AchievementValidator { + base: AchievementValidator; + get achievement() { + return "item-attached" as AchievementId; + } + get name() { + return "1st attachment added to item"; + } + get metric() { + // Does this need to match vault-item-created-count-validator metric id for "item-quantity" + return "item-quantity" as MetricId; + } + get validator() { + return Type.HasTag; + } + get active() { + return this.base.active; + } + get hidden() { + return false; + } + + constructor() { + this.base.active = "until-earned"; + } + + trigger(item: UserActionEvent) { + return item.tags?.includes("with-attachment") ?? false; + } + + award(_measured: AchievementProgressEvent[]) { + return [earnedEvent(this.achievement)]; + } +} diff --git a/libs/common/src/tools/achievements/validators/config/item-created-count-config.ts b/libs/common/src/tools/achievements/validators/config/item-created-count-config.ts new file mode 100644 index 00000000000..7fe9e37e407 --- /dev/null +++ b/libs/common/src/tools/achievements/validators/config/item-created-count-config.ts @@ -0,0 +1,150 @@ +import { CipherType } from "../../../../vault/enums"; +import { Type } from "../../data"; +import { Achievement, AchievementId, MetricId } from "../../types"; + +/** + * Creates the ability to add configs for different achievements at + * defined thresholds for items created in the vault. Creating the config + * with no cipher type is used for validations where the type is not needed + */ +export class ItemCreatedCountConfig implements Achievement { + // Define the achievements here + + // Any vault items added + static readonly ItemCreated = new ItemCreatedCountConfig( + "vault-item-created", + "1st item added", + 1, + ); + static readonly ItemsCreated10 = new ItemCreatedCountConfig( + "vault-items-created-ten", + "10 items created", + 10, + ); + static readonly ItemsCreated50 = new ItemCreatedCountConfig( + "vault-items-created-fifty", + "50 items created", + 50, + ); + static readonly ItemsCreated100 = new ItemCreatedCountConfig( + "vault-items-created-one-hundred", + "100 items created", + 100, + ); + + // Login items added + static readonly LoginItemCreated = new ItemCreatedCountConfig( + "login-item-created", + "1st login item added", + 1, + CipherType.Login, + ); + static readonly LoginItemCreated10 = new ItemCreatedCountConfig( + "login-item-created-ten", + "10 login items added", + 10, + CipherType.Login, + ); + static readonly LoginItemCreated50 = new ItemCreatedCountConfig( + "login-item-created-fifty", + "50 login items added", + 50, + CipherType.Login, + ); + static readonly LoginItemCreated100 = new ItemCreatedCountConfig( + "login-item-created-one-hundred", + "100 login items added", + 100, + CipherType.Login, + ); + + // Card items + static readonly CardItemCreated = new ItemCreatedCountConfig( + "card-item-created", + "1st card item added", + 1, + CipherType.Card, + ); + static readonly CardItemCreated10 = new ItemCreatedCountConfig( + "card-item-created-ten", + "10 card items added", + 10, + CipherType.Card, + ); + static readonly CardItemCreated50 = new ItemCreatedCountConfig( + "card-item-created-fifty", + "50 card items added", + 50, + CipherType.Card, + ); + static readonly CardItemCreated100 = new ItemCreatedCountConfig( + "card-item-created-one-hundred", + "100 card items added", + 100, + CipherType.Card, + ); + + // Note items + static readonly NoteItemCreated = new ItemCreatedCountConfig( + "note-item-created", + "1st card item added", + 1, + CipherType.SecureNote, + ); + static readonly NoteItemCreated10 = new ItemCreatedCountConfig( + "note-item-created-ten", + "10 card items added", + 10, + CipherType.SecureNote, + ); + static readonly NoteItemCreated50 = new ItemCreatedCountConfig( + "note-item-created-fifty", + "50 card items added", + 50, + CipherType.SecureNote, + ); + static readonly NoteItemCreated100 = new ItemCreatedCountConfig( + "note-item-created-one-hundred", + "100 card items added", + 100, + CipherType.SecureNote, + ); + + base: Achievement; + get achievement() { + return this.base.achievement; + } + + get name() { + return this.base.name; + } + + get validator() { + return this.base.validator; + } + + get active() { + return this.base.active; + } + + get hidden() { + return this.base.hidden; + } + cipherType: CipherType = null; + threshold: number; + private constructor(key: string, name: string, threshold: number, cipherType: CipherType = null) { + this.cipherType = cipherType; + this.threshold = threshold; + this.base = { + achievement: key as AchievementId, + name: name, + validator: Type.Threshold, + active: { + metric: `item-${cipherType ? `${CipherType[cipherType]}-` : ""}quantity` as MetricId, + low: threshold - 1, + high: threshold, + }, + hidden: false, + }; + } +} diff --git a/libs/common/src/tools/achievements/validators/item-folder-added-validator.ts b/libs/common/src/tools/achievements/validators/item-folder-added-validator.ts new file mode 100644 index 00000000000..3ab9b654e0c --- /dev/null +++ b/libs/common/src/tools/achievements/validators/item-folder-added-validator.ts @@ -0,0 +1,39 @@ +import { earnedEvent } from "../achievement-events"; +import { Type } from "../data"; +import { + AchievementId, + AchievementProgressEvent, + AchievementValidator, + UserActionEvent, +} from "../types"; + +export class ItemFolderAddedValidator implements AchievementValidator { + base: AchievementValidator; + get achievement() { + return "item-folder-added" as AchievementId; + } + get name() { + return "1st item added to folder"; + } + get validator() { + return Type.HasTag; + } + get active() { + return this.base.active; + } + get hidden() { + return false; + } + + constructor() { + this.base.active = "until-earned"; + } + + trigger(item: UserActionEvent) { + return item.tags?.includes("with-folder") ?? false; + } + + award(_measured: AchievementProgressEvent[]) { + return [earnedEvent(this.achievement)]; + } +} diff --git a/libs/common/src/tools/achievements/validators/vault-item-created-count-validator.ts b/libs/common/src/tools/achievements/validators/vault-item-created-count-validator.ts new file mode 100644 index 00000000000..213b20f08b1 --- /dev/null +++ b/libs/common/src/tools/achievements/validators/vault-item-created-count-validator.ts @@ -0,0 +1,55 @@ +import { CipherType } from "../../../vault/enums"; +import { EventFormat } from "../../log/ecs-format"; +import { earnedEvent, progressEvent } from "../achievement-events"; +import { AchievementProgressEvent, AchievementValidator, MetricId } from "../types"; + +import { ItemCreatedCountConfig } from "./config/item-created-count-config"; + +/** + * Creates a validator that will be customized to a custom count achievement + * @param config contains the information to create the validator and achievement info. + * The defined config should be input as example: ItemCreatedCountConfig.ItemCreated + * + * Will track the count achievements for all vault items and individual vault items. + */ +export class VaultItemCreatedCountValidator implements AchievementValidator { + private itemCreatedProgress: MetricId; + constructor(private config: ItemCreatedCountConfig) { + this.itemCreatedProgress = + `item-${config.cipherType ? `${CipherType[config.cipherType]}-` : ""}quantity` as MetricId; + } + + base: AchievementValidator; + get achievement() { + return this.config.achievement; + } + get name() { + return this.config.name; + } + get validator() { + return this.config.validator; + } + get active() { + return this.config.active; + } + get hidden() { + return this.config.hidden; + } + + trigger(item: EventFormat) { + return ( + item.action === + `vault-${this.config.cipherType ? `${CipherType[this.config.cipherType]}-` : ""}item-added` + ); + } + + measure(_item: EventFormat, progress: Map) { + const value = 1 + (progress.get(this.itemCreatedProgress) ?? 0); + return [progressEvent(this.itemCreatedProgress, value)]; + } + + award(_measured: AchievementProgressEvent[], progress: Map) { + const value = progress.get(this.itemCreatedProgress) ?? 0; + return value >= this.config.threshold ? [earnedEvent(this.achievement)] : []; + } +}