import type { AppOnboardingConfig, AppOnboardingStore, AppOnboardingPreferences, AppOnboardingStep, } from './types'; const ONBOARDING_COMPLETED_KEY = 'onboarding_completed'; const ONBOARDING_PREFERENCES_KEY = 'onboarding_preferences'; /** * Create an app-specific onboarding store * * This factory creates a store that: * - Checks if onboarding was already completed (via deviceSettings) * - Manages step navigation and preference collection * - Saves completion state and preferences to deviceSettings * * @example * ```typescript * import { createAppOnboardingStore } from '@mana/shared-app-onboarding'; * * const calendarOnboarding = createAppOnboardingStore({ * appId: 'calendar', * steps: [ * { * id: 'weekStart', * type: 'select', * question: 'Wann beginnt deine Woche?', * emoji: '📅', * options: [ * { id: 'monday', label: 'Montag', emoji: '1️⃣' }, * { id: 'sunday', label: 'Sonntag', emoji: '7️⃣' }, * ], * defaultValue: 'monday', * }, * ], * userSettings, * }); * ``` */ export function createAppOnboardingStore(config: AppOnboardingConfig): AppOnboardingStore { const { appId, steps, userSettings, onComplete, onSkip } = config; // Build initial preferences from step defaults before creating state const initialPreferences: AppOnboardingPreferences = {}; for (const step of steps) { if ((step.type === 'select' || step.type === 'toggle') && step.defaultValue !== undefined) { initialPreferences[step.id] = step.defaultValue; } } // State let currentStep = $state(0); let preferences = $state(initialPreferences); let saving = $state(false); let completed = $state(false); // Derived values const totalSteps = $derived(steps.length); const isFirstStep = $derived(currentStep === 0); const isLastStep = $derived(currentStep === steps.length - 1); const progress = $derived(((currentStep + 1) / steps.length) * 100); const currentStepConfig = $derived(steps[currentStep]); /** * Check if onboarding was already completed */ function checkCompleted(): boolean { const deviceAppSettings = userSettings.getDeviceAppSettings(); return deviceAppSettings[ONBOARDING_COMPLETED_KEY] === true; } // Derived: should show modal const shouldShow = $derived(!completed && !checkCompleted() && userSettings.loaded); /** * Go to next step */ function next(): void { if (currentStep < steps.length - 1) { currentStep++; } } /** * Go to previous step */ function prev(): void { if (currentStep > 0) { currentStep--; } } /** * Go to specific step */ function goToStep(index: number): void { if (index >= 0 && index < steps.length) { currentStep = index; } } /** * Set a preference value */ function setPreference(key: string, value: unknown): void { preferences = { ...preferences, [key]: value }; } /** * Complete the onboarding and save preferences */ async function complete(): Promise { saving = true; try { // Save to deviceSettings await userSettings.updateDeviceAppSettings({ [ONBOARDING_COMPLETED_KEY]: true, [ONBOARDING_PREFERENCES_KEY]: preferences, ...preferences, // Also spread preferences at top level for easy access }); completed = true; // Call completion callback if (onComplete) { await onComplete(preferences); } } finally { saving = false; } } /** * Skip the onboarding entirely */ async function skip(): Promise { saving = true; try { // Mark as completed but don't save preferences await userSettings.updateDeviceAppSettings({ [ONBOARDING_COMPLETED_KEY]: true, onboarding_skipped: true, }); completed = true; // Call skip callback if (onSkip) { await onSkip(); } } finally { saving = false; } } /** * Reset onboarding (for testing/debugging) */ async function reset(): Promise { saving = true; try { // Remove onboarding flags from deviceSettings await userSettings.updateDeviceAppSettings({ [ONBOARDING_COMPLETED_KEY]: false, onboarding_skipped: false, }); completed = false; currentStep = 0; preferences = { ...initialPreferences }; } finally { saving = false; } } return { get shouldShow() { return shouldShow; }, get currentStep() { return currentStep; }, get totalSteps() { return totalSteps; }, get isFirstStep() { return isFirstStep; }, get isLastStep() { return isLastStep; }, get progress() { return progress; }, get currentStepConfig() { return currentStepConfig; }, get preferences() { return preferences; }, get saving() { return saving; }, get appId() { return appId; }, get steps() { return steps; }, next, prev, goToStep, setPreference, complete, skip, reset, checkCompleted, }; }