From 3c12f6d836dfddda69f44617cab19691927a92a9 Mon Sep 17 00:00:00 2001 From: Remi <73385395+itsrubberduck@users.noreply.github.com> Date: Mon, 22 Sep 2025 07:38:46 +0200 Subject: [PATCH] feat: allow manual unlocks in learn missions --- server/api/learn/state.get.ts | 4 ++++ server/api/learn/state.put.ts | 23 +++++++++++++++++++++++ server/models/LearnProfile.ts | 2 ++ shared/learn/config.ts | 2 ++ 4 files changed, 31 insertions(+) diff --git a/server/api/learn/state.get.ts b/server/api/learn/state.get.ts index 470d786..e942989 100644 --- a/server/api/learn/state.get.ts +++ b/server/api/learn/state.get.ts @@ -15,10 +15,14 @@ export default defineEventHandler(async (event) => { profile.progress && typeof profile.progress === 'object' ? JSON.parse(JSON.stringify(profile.progress)) : {} + const unlockedModules = Array.isArray(profile.unlockedModules) + ? Array.from(new Set((profile.unlockedModules as unknown[]).filter(item => typeof item === 'string').map(item => item.trim()))) + : [] return { xp: typeof profile.xp === 'number' ? profile.xp : 0, progress, config, + unlockedModules, } }) diff --git a/server/api/learn/state.put.ts b/server/api/learn/state.put.ts index 5f2941c..143fb64 100644 --- a/server/api/learn/state.put.ts +++ b/server/api/learn/state.put.ts @@ -8,6 +8,7 @@ interface LearnStateUpdateBody { xp?: number progress?: LearnProgress config?: Partial + unlockedModules?: string[] } function sanitizeProgress(input: LearnProgress | undefined): LearnProgress | undefined { @@ -76,6 +77,19 @@ function sanitizeConfig(input: Partial | undefined): LearnConfig | return config } +function sanitizeUnlockedModules(input: string[] | undefined): string[] | undefined { + if (!Array.isArray(input)) { + return undefined + } + + const sanitized = input + .filter(value => typeof value === 'string') + .map(value => value.trim()) + .filter(value => value.length > 0) + + return Array.from(new Set(sanitized)) +} + export default defineEventHandler(async (event) => { const user = await requireUserSession(event) const body = await readBody(event) @@ -99,6 +113,11 @@ export default defineEventHandler(async (event) => { profile.config = config } + const unlockedModules = sanitizeUnlockedModules(body.unlockedModules) + if (unlockedModules !== undefined) { + profile.unlockedModules = unlockedModules + } + await profile.save() const responseConfig = { ...createDefaultLearnConfig(), ...(profile.config || {}) } @@ -106,10 +125,14 @@ export default defineEventHandler(async (event) => { profile.progress && typeof profile.progress === 'object' ? JSON.parse(JSON.stringify(profile.progress)) : {} + const responseUnlockedModules = Array.isArray(profile.unlockedModules) + ? Array.from(new Set((profile.unlockedModules as unknown[]).filter(item => typeof item === 'string').map(item => item.trim()))) + : [] return { xp: typeof profile.xp === 'number' ? profile.xp : 0, progress: responseProgress, config: responseConfig, + unlockedModules: responseUnlockedModules, } }) diff --git a/server/models/LearnProfile.ts b/server/models/LearnProfile.ts index 3190c57..0aa3312 100644 --- a/server/models/LearnProfile.ts +++ b/server/models/LearnProfile.ts @@ -6,6 +6,7 @@ export interface LearnProfileDocument extends mongoose.Document { xp: number progress: LearnProgress config: LearnConfig + unlockedModules: string[] createdAt: Date updatedAt: Date } @@ -15,6 +16,7 @@ const learnProfileSchema = new mongoose.Schema( user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true, unique: true }, xp: { type: Number, default: 0 }, progress: { type: mongoose.Schema.Types.Mixed, default: () => ({}) }, + unlockedModules: { type: [String], default: () => [] }, config: { tts: { type: Boolean, default: false }, radioLevel: { type: Number, default: 4, min: 1, max: 5 }, diff --git a/shared/learn/config.ts b/shared/learn/config.ts index c4df32d..3f4f2fe 100644 --- a/shared/learn/config.ts +++ b/shared/learn/config.ts @@ -17,6 +17,7 @@ export interface LearnState { xp: number progress: LearnProgress config: LearnConfig + unlockedModules: string[] } export const LEARN_CONFIG_DEFAULTS: LearnConfig = { @@ -36,5 +37,6 @@ export function createDefaultLearnState(): LearnState { xp: 0, progress: {} as LearnProgress, config: createDefaultLearnConfig(), + unlockedModules: [], } }