simpl-liste/CLAUDE.md
le king fu 117de533d7 fix: subtask input scroll + widget expand/collapse subtasks (#6, #9)
- Fix keyboard hiding subtask input: use precise scrollTo with onLayout position instead of unreliable scrollToEnd (#6)
- Add expand/collapse button in widget for tasks with subtasks (#9)
- Subtasks are now toggleable directly from the widget
- Widget state (expanded tasks) persisted via AsyncStorage
- Update CLAUDE.md with widget docs, build/deploy process, and release workflow

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 10:27:21 -05:00

7.7 KiB
Raw Blame History

Simpl-Liste

Application mobile de gestion de tâches minimaliste par La Compagnie Maximus. Bundle ID : com.lacompagniemaximus.simpliste — Scheme : simplliste

Stack

  • React Native 0.81 + Expo SDK 54 (New Architecture)
  • Expo Router 6 (file-based, typed routes)
  • TypeScript 5.9 (strict)
  • NativeWind 4.2 (Tailwind CSS 3.4 pour RN)
  • Drizzle ORM 0.45 + expo-sqlite 16 (SQLite local, simpliste.db)
  • Zustand 5 + AsyncStorage (state UI persisté)
  • i18next 25 + react-i18next (FR/EN, français par défaut)
  • lucide-react-native (icônes)
  • date-fns 4 (dates)

Scripts

npm start          # Expo dev server
npm run android    # Lancer sur Android
npm run ios        # Lancer sur iOS

Structure

app/
├── _layout.tsx              # Root stack (fonts, migrations, theme)
├── (tabs)/
│   ├── _layout.tsx          # Tab bar (3 onglets)
│   ├── index.tsx            # Inbox
│   ├── lists.tsx            # Toutes les listes
│   └── settings.tsx         # Paramètres + gestion tags
├── task/
│   ├── new.tsx              # Création de tâche (modal)
│   └── [id].tsx             # Détail/édition tâche
└── list/
    └── [id].tsx             # Détail d'une liste

src/
├── components/
│   ├── FilterMenu.tsx       # Modal filtres (bottom sheet)
│   ├── SortMenu.tsx         # Modal tri (bottom sheet)
│   └── task/
│       ├── TagChip.tsx      # Pill tag réutilisable
│       └── TaskItem.tsx     # Rangée de tâche
├── db/
│   ├── client.ts            # Config SQLite + Drizzle
│   ├── schema.ts            # Tables : lists, tasks, tags, task_tags
│   ├── migrations/          # SQL migrations (auto-appliquées au démarrage)
│   └── repository/
│       ├── lists.ts         # CRUD listes + ensureInbox()
│       ├── tags.ts          # CRUD tags + relations task-tag
│       └── tasks.ts         # CRUD tâches, filtres, tri, sous-tâches
├── i18n/
│   ├── index.ts             # Init i18next (détection locale appareil)
│   ├── fr.json              # Traductions françaises
│   └── en.json              # Traductions anglaises
├── lib/
│   ├── priority.ts          # Helpers couleurs priorité
│   ├── recurrence.ts        # Types récurrence + calcul prochaine occurrence
│   ├── uuid.ts              # Wrapper expo-crypto randomUUID
│   └── validation.ts        # Validation UUID pour deep links
├── services/
│   ├── calendar.ts          # Sync expo-calendar
│   ├── icsExport.ts         # Export .ics + partage
│   ├── notifications.ts     # Planification expo-notifications
│   └── widgetSync.ts        # Sync tâches + thème vers widget Android
├── stores/
│   ├── useSettingsStore.ts  # Thème, locale, notifs, calendrier
│   └── useTaskStore.ts      # État tri/filtre
├── theme/
│   └── colors.ts            # Palette centralisée (bleu, crème, terracotta)
└── widgets/
    ├── TaskListWidget.tsx    # Composant widget Android (3 tailles, dark mode)
    └── widgetTaskHandler.ts  # Handler headless pour actions widget

Base de données

4 tables SQLite gérées par Drizzle ORM. Migrations auto-appliquées via useMigrations().

  • lists : id, name, color, icon, position, is_inbox, timestamps
  • tasks : id, title, notes, completed, priority (0-3), due_date, list_id → lists, parent_id (sous-tâches), position, recurrence, calendar_event_id, timestamps
  • tags : id, name, color, timestamps
  • task_tags : task_id → tasks (CASCADE), tag_id → tags (CASCADE)

Générer une migration

npx drizzle-kit generate

Puis mettre à jour src/db/migrations/migrations.js si nécessaire.

Conventions

  • Français par défaut, anglais en second. Toutes les chaînes visibles dans fr.json/en.json
  • Dark mode : résolu localement avec isDark ternary (pas de NativeWind dark: prefix)
  • Composants : headers custom par écran (pas de React Navigation header)
  • Polling 500ms : les écrans rechargent les données par intervalle (pas de live queries)
  • Pas de state global pour les tâches : chargées depuis SQLite par écran
  • Icônes via lucide-react-native, polices Inter (400/500/600/700)
  • UUID générés via expo-crypto
  • Sous-tâches = tâches avec parentId non-null

Palette de couleurs

Token Valeur
bleu #4A90A4
crème #FFF8F0
terracotta #C17767
vert #8BA889
sable #D4A574
violet #7B68EE
rouge #E57373
teal #4DB6AC

Couleurs sombres : fond #1A1A1A, surface #2A2A2A, bordure #3A3A3A, texte #F5F5F5, secondaire #A0A0A0

Widget Android

3 tailles configurées dans app.json (plugin react-native-android-widget) :

  • SimplListeSmall (2×2) — Compteur de tâches + bouton ajout
  • SimplListeMedium (2×4) — Liste de 4 tâches avec indicateur couleur de liste
  • SimplListeLarge (4×4) — Liste de 8 tâches

Sync des données

  • widgetSync.ts lit les tâches depuis SQLite et les cache dans AsyncStorage (widget:tasks)
  • Le thème est lu depuis AsyncStorage (simpl-liste-settingsstate.theme), résolu si system via Appearance.getColorScheme(), et stocké dans widget:isDark
  • widgetTaskHandler.ts gère le rendu headless (quand l'app n'est pas ouverte) en lisant les deux clés AsyncStorage
  • Les couleurs du widget suivent la même palette que l'app (voir LIGHT_COLORS / DARK_COLORS dans TaskListWidget.tsx)

Clés AsyncStorage utilisées par le widget

Clé Contenu
widget:tasks WidgetTask[] sérialisé JSON
widget:isDark boolean sérialisé JSON
simpl-liste-settings Store Zustand persisté (contient state.theme)

Build & déploiement

Profiles EAS dans eas.json :

  • development — APK avec dev client
  • preview — APK de distribution directe (hors Play Store)
  • production — AAB pour le Play Store, autoIncrement: true sur versionCode

Commandes de build

npx eas-cli build --platform android --profile preview --non-interactive   # APK
npx eas-cli build --platform android --profile production --non-interactive # AAB

Important : eas n'est pas installé globalement, utiliser npx --yes eas-cli (pas npx eas).

Processus de release

  1. Bumper version dans app.json ET package.json
  2. Le versionCode Android est auto-incrémenté par EAS (autoIncrement: true)
  3. Build preview (APK) + production (AAB)
  4. Créer la release sur Forgejo via API :
    # Créer la release
    curl -X POST ".../api/v1/repos/maximus/simpl-liste/releases" -d '{"tag_name":"vX.Y.Z",...}'
    # Attacher l'APK
    curl -X POST ".../releases/{id}/assets?name=simpl-liste-vX.Y.Z.apk" -F "attachment=@fichier.apk"
    
  5. Le bouton « Vérifier les mises à jour » dans l'app utilise l'endpoint /releases/latest et propose le téléchargement de l'asset .apk

Repo Forgejo

  • URL : https://git.lacompagniemaximus.com/maximus/simpl-liste
  • Remote git : origin (push via HTTPS avec token dans ~/.git-credentials)
  • Issues : utilisées pour le suivi des bugs/features
  • Releases : distribution APK avec assets attachés

Mises à jour in-app

Le bouton dans Paramètres > À propos appelle GET /api/v1/repos/maximus/simpl-liste/releases/latest (repo public, pas d'auth nécessaire). Compare release.tag_name (ex: v1.0.1) avec Constants.expoConfig.version. Si différent, affiche une Alert avec le changelog (release.body) et un lien vers le premier asset .apk trouvé.