Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/ElthonJohan/comunitea/llms.txt

Use this file to discover all available pages before exploring further.

ComuniTEA uses Zustand for all global client-side state. There are no React Context providers to wrap — any component can read or mutate state by calling a hook directly, and any non-React utility (in lib/, event handlers, background tasks) can call useXxxStore.getState() to read or .setState() to write. Four of the six stores persist their state to AsyncStorage via Zustand’s persist middleware using custom legacy-compatible storage adapters; the remaining two (useRutinasProgressStore and useVocabularySentenceStore) are in-memory only and reset on app restart.

useSettingsStore

File: stores/settingsStore.ts
Persistence: AsyncStorage key user_voice_preference (via voiceLegacyStorage adapter)
Manages the user’s voice profile selection. The store writes only the raw string 'femenina' or 'masculina' to AsyncStorage (not a Zustand envelope) to maintain compatibility with installations that stored the value directly.
interface SettingsState {
  voice: VoiceType;      // 'femenina' | 'masculina'
  hydrated: boolean;     // true once AsyncStorage has been read
  setVoice: (voice: VoiceType) => void;
}
voice
'femenina' | 'masculina'
required
The currently active voice profile. Passed to getAudioAssets(voice) whenever the app plays pictogram audio. Defaults to 'femenina'.
hydrated
boolean
Set to true by onRehydrateStorage once the store has finished reading from AsyncStorage. Components that depend on voice should gate rendering on hydrated to avoid a flash of the default value.
setVoice
(voice: VoiceType) => void
Updates the active voice and immediately persists the new value to AsyncStorage under the key user_voice_preference.
Usage:
import { useSettingsStore } from '@/stores/settingsStore';

const { voice, setVoice } = useSettingsStore();

useG2ProgressStore

File: stores/g2ProgressStore.ts
Persistence: AsyncStorage key comunitea_ejercicios_g2 (via g2LegacyStorage adapter)
Tracks all progress for the Grupo 2 exercise path — emotions recognition, help-seeking situations, star ratings per exercise and per sub-exercise, and auxiliary registers used by the progress reports.

State Shape

export interface EjerciciosG2State {
  completados:           string[];                      // exercise keys that have been finished
  fallosPorEjercicio:    Record<string, number>;        // wrong attempts per exercise key
  estrellasPorNivel:     Record<number, 1 | 2 | 3>;    // star rating per level (1–3)
  emocionesRegistradas:  string[];                      // last 30 emotion IDs the child identified
  fallosPorSubEjercicio: Record<string, number>;        // wrong attempts per sub-exercise key
  estrellasPorEjercicio: Record<string, 1 | 2 | 3>;    // star rating per sub-exercise
  ayudasRegistradas:     AyudaRegistroG2[];             // last 40 help-seeking records
}

export interface AyudaRegistroG2 {
  situacion: string;   // scenario description
  persona: string;     // person the child would ask for help
}

export type G2SubEjercicio = 'E1' | 'E2' | 'E3';

Actions & Selectors

MethodSignatureDescription
isExerciseDone(nivel, ejIndex) => booleanReturns true if the exercise at (nivel, ejIndex) is in completados.
isLevelComplete(nivel) => booleantrue when all G2_EJERCICIOS_POR_NIVEL exercises for a level are done.
isLevelUnlocked(nivel) => booleanLevel 1 is always unlocked; higher levels require the previous level to be complete.
nivelBadgeState(nivel) => 'bloqueado' | 'disponible' | 'completado'Drives the badge icon on each level card.
firstIncompleteIndex(nivel) => numberReturns the index of the first incomplete exercise — used to resume mid-level.
registerFail(nivel, ejIndex) => Promise<void>Increments the fail counter and fires a gamification event (wrong_attempt).
registerSubFail(nivel: 2|3, sub) => Promise<void>Records a fail against a named sub-exercise slot (E1/E2/E3).
completeExercise(nivel, ejIndex) => Promise<void>Marks an exercise done, calculates level stars on the last exercise, fires exercise_completed.
completeSubExercise(nivel: 2|3, sub) => Promise<void>Completes a named sub-exercise, computes sub-exercise stars, and recalculates level stars when E3 is the last to finish.
registerEmocion(emotionId) => Promise<void>Appends an emotion ID to emocionesRegistradas (capped at 30).
registerAyudaRegistro(situacion, persona) => Promise<void>Appends a help record to ayudasRegistradas (capped at 40).
estrellasNivel(nivel) => 0|1|2|3Star count for a level; 0 if not yet complete.
estrellasSubEjercicio(nivel: 2|3, sub) => 1|2|3|nullStar count for a named sub-exercise; null if not yet rated.
resetAllProgress() => voidResets all G2 state to defaults (useful in the developer settings screen).

Migration Note

On rehydration, the store automatically runs migrateG2LegacyThreeSlotsToSix — this backfills exercise slots 4–6 for users who completed a level back when only 3 exercises existed per level, preventing a stuck “Próximamente” state.

Standalone Loaders

Two async helpers read G2 data directly from AsyncStorage without touching the store — useful in background report generation:
export async function loadG2EmocionesRegistradas(): Promise<string[]>
export async function loadG2AyudasRegistradas(): Promise<AyudaRegistroG2[]>

useG3ProgressStore

File: stores/g3ProgressStore.ts
Persistence: AsyncStorage key comunitea_ejercicios_g3 (via g3LegacyStorage adapter)
Tracks progress for the Grupo 3 exercise path — a linear “camino” (path) of levels, each containing exactly 3 exercises. Stars are awarded per level based on total wrong attempts across its 3 exercises.

State Shape

export interface EjercicioG3State {
  completados:        string[];                    // exercise keys finished
  fallosPorEjercicio: Record<string, number>;      // wrong attempts per exercise key
  estrellasPorNivel:  Record<number, 1 | 2 | 3>;  // star rating per level
  caminoCompleto:     boolean;                     // true when the full path is done
}

Actions & Selectors

MethodSignatureDescription
isExerciseDone(nivel, ejIndex) => booleanChecks if a specific exercise is complete.
isLevelComplete(nivel) => booleantrue when all 3 exercises for the level are done.
isLevelUnlocked(nivel) => booleanLevel 1 always unlocked; requires previous level complete otherwise.
nivelBadgeState(nivel) => 'bloqueado' | 'disponible' | 'completado'Drives badge rendering on the path map.
firstIncompleteIndex(nivel) => numberFirst incomplete exercise index (0–2).
registerFail(nivel, ejIndex) => Promise<void>Increments fail counter and fires wrong_attempt gamification event.
completeExercise(nivel, ejIndex) => Promise<void>Marks done; when ejIndex === 2 (last exercise), calculates level stars from total fails.
setCaminoCompleto() => Promise<void>Persists 'true' to STORAGE_KEYS.EJERCICIOS_G3_COMPLETADOS and sets caminoCompleto: true.
estrellasNivel(nivel) => 0|1|2|3Star count; 0 if level not yet complete.
segmentBetweenCompleted(fromNivel, toNivel) => booleanUsed by the path-map UI to decide whether to draw an active connector line between two nodes.
resetAllProgress() => Promise<void>Clears all G3 progress including the EJERCICIOS_G3_COMPLETADOS key.

useRutinasProgressStore

File: stores/rutinasProgressStore.ts
Persistence: None — in-memory only, resets on app restart.
Tracks how many steps of each daily routine exercise the child has completed. The store is intentionally lightweight and does not persist — routine completion is a daily activity that starts fresh each session.
type ProgressMap = Record<string, number>;

interface RutinasState {
  progress: ProgressMap;
  setEjercicioCompletados: (ejercicioId: string, completados: number) => void;
}
The initial state seeds three routines with example values:
progress: {
  manana:  1,   // morning routine — 1 step done
  colegio: 0,   // school routine  — not started
  noche:   2,   // night routine   — 2 steps done
}
progress
Record<string, number>
Maps a routine exercise ID (e.g., 'manana', 'colegio', 'noche') to the number of completed steps.
setEjercicioCompletados
(ejercicioId: string, completados: number) => void
Sets (or overwrites) the completed step count for a given routine ID.
A module-level helper is also exported for convenience from non-React contexts:
export function setEjercicioCompletados(ejercicioId: string, completados: number): void {
  useRutinasProgressStore.getState().setEjercicioCompletados(ejercicioId, completados);
}

useVocabularySentenceStore

File: stores/vocabularySentenceStore.ts
Persistence: None — in-memory only.
Manages the sentence strip in the vocabulary module — the ordered sequence of VocabularyItem objects the child is building before pressing “play”. Cleared after each sentence is played or manually dismissed.
interface VocabularySentenceState {
  sentence:           VocabularyItem[];
  addToSentence:      (item: VocabularyItem) => void;
  removeFromSentence: (index: number) => void;
  clearSentence:      () => void;
}
sentence
VocabularyItem[]
The ordered array of vocabulary items currently in the strip. Displayed left-to-right in the sentence bar at the top of the vocabulary screen.
addToSentence
(item: VocabularyItem) => void
Appends an item to the end of the sentence strip.
removeFromSentence
(index: number) => void
Removes the item at the given index (used when the child taps a chip in the strip to deselect it).
clearSentence
() => void
Empties the strip (called after playing or after a timeout).
Usage:
import { useVocabularySentenceStore } from '@/stores/vocabularySentenceStore';

const { sentence, addToSentence, clearSentence } = useVocabularySentenceStore();

useAppThemeStore

File: stores/appThemeStore.ts
Persistence: AsyncStorage key comunitea-accent-palette (via standard Zustand createJSONStorage)
Controls the global colour palette of the app. The palette is applied to tabs, buttons, and accent surfaces via constants/Colors.ts colour tokens.
type ThemeColor = 'sage' | 'rojo' | 'azul';

type AppThemeState = {
  palette:    ThemeColor;
  setPalette: (p: ThemeColor) => void;
};
palette
'sage' | 'rojo' | 'azul'
The currently active colour palette. Defaults to 'sage'. Changes take effect immediately across all screens through the useAppThemeStore subscription.
setPalette
(p: ThemeColor) => void
Updates the palette and persists the selection to AsyncStorage.
Usage:
import { useAppThemeStore } from '@/stores/appThemeStore';

const { palette, setPalette } = useAppThemeStore();
useAppThemeStore is the only store that uses the standard Zustand createJSONStorage(() => AsyncStorage) adapter — it writes the full { state, version } envelope. The other persisted stores use custom legacy adapters that write a flat JSON object. Keep this distinction in mind if you ever need to inspect AsyncStorage in a debugger.

Progress Store Re-exports

stores/progressStore.ts is the single barrel export for all exercise-progress stores. Import from here to avoid deep relative paths:
import {
  useG2ProgressStore,
  loadG2EmocionesRegistradas,
  loadG2AyudasRegistradas,
  type EjerciciosG2State,
  type AyudaRegistroG2,
  type G2SubEjercicio,
} from '@/stores/progressStore';

import {
  useG3ProgressStore,
  type EjercicioG3State,
} from '@/stores/progressStore';

import {
  useRutinasProgressStore,
  setEjercicioCompletados,
} from '@/stores/progressStore';

Persistence Summary

StorePersistedAsyncStorage KeyAdapter
useSettingsStoreuser_voice_preferenceCustom legacy (raw string)
useG2ProgressStorecomunitea_ejercicios_g2Custom legacy (flat JSON)
useG3ProgressStorecomunitea_ejercicios_g3Custom legacy (flat JSON)
useAppThemeStorecomunitea-accent-paletteStandard Zustand JSON
useRutinasProgressStoreNone
useVocabularySentenceStoreNone

Accessing Stores Outside React

All Zustand stores expose .getState() and .setState() as static methods on the store hook. This is how the gamification bridge and report loaders read progress data outside the React tree:
// Read current state without a hook
const { voice } = useSettingsStore.getState();

// Write state without a hook (triggers React subscriptions)
useG2ProgressStore.setState({ hydrated: true });

// One-off async read from a utility function
const emociones = await loadG2EmocionesRegistradas();
Prefer calling actions through getState().actionName() rather than calling setState() directly with partial state — actions contain the business logic (fail counting, gamification events, capping array lengths) that raw setState bypasses.

Build docs developers (and LLMs) love