docs: cadrage initial Storytime (specs par jalon, roadmap, CLAUDE.md)
Lecteur d'histoires cadenassé pour le coucher (Android/Flutter). - CLAUDE.md : principes craftsmanship/TDD/clean code/clean archi + decisions techniques - ROADMAP.md : suivi haut niveau des 7 jalons, a tenir a jour par etape - docs/specs/ : specs completes decoupees par jalon, etapes en sous-fichiers - .gitignore Flutter (pubspec.lock versionne, projet applicatif) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
# 2.1 — Domaine de lecture
|
||||
|
||||
## Objectif
|
||||
Modéliser le cœur métier de la lecture : ce qu'est une histoire jouable et l'état
|
||||
de lecture, indépendamment de toute techno audio.
|
||||
|
||||
## Périmètre & hors-périmètre
|
||||
- Inclus : entités, value objects, interface `PlaybackRepository`, use cases.
|
||||
- Exclus : implémentation audio (2.2), UI (2.3).
|
||||
|
||||
## Dépendances
|
||||
Jalon 0.
|
||||
|
||||
## Conception
|
||||
- **Domain** (`features/playback/domain/`) :
|
||||
- `Episode` (entité) : `id`, `title`, `audioUrl`, `duration?`, `artworkUrl?`. Immuable.
|
||||
- `PlaybackState` (value object) : `status` (`idle`/`loading`/`playing`/`paused`/`completed`/`error`), `current` (`Episode?`), `position`, `bufferedPosition`.
|
||||
- `PlaybackRepository` (interface) :
|
||||
- `Future<Result<Unit>> load(List<Episode> queue, {int startIndex})`
|
||||
- `Future<Result<Unit>> play()` / `pause()` / `next()` / `previous()`
|
||||
- `Future<Result<Unit>> seek(Duration position)`
|
||||
- `Stream<PlaybackState> watch()`
|
||||
- `PlaybackFailure` : `AudioSourceFailure`, `PlaybackUnavailableFailure`.
|
||||
- **Application** (`features/playback/application/`) :
|
||||
- `PlayStoryUseCase` (charge la file + démarre à l'index voulu).
|
||||
- `TogglePlayPauseUseCase`, `SkipNextUseCase`, `SkipPreviousUseCase`, `SeekUseCase`.
|
||||
- `WatchPlaybackStateUseCase`.
|
||||
|
||||
## Plan TDD
|
||||
1. **Red** : `playback_state_test.dart` — `copyWith`, transitions et égalité (immuabilité) de `PlaybackState`.
|
||||
2. **Green** : implémenter entités/value objects.
|
||||
3. **Red** : `play_story_use_case_test.dart` — avec `PlaybackRepository` mocké : charge la file et appelle `play` ; index hors bornes → `Err`.
|
||||
4. **Green** : implémenter le use case.
|
||||
5. **Red** : use cases toggle/skip/seek — vérifient l'appel correct au repo selon l'état.
|
||||
6. **Green** : implémenter.
|
||||
7. **Refactor**.
|
||||
|
||||
## Definition of Done
|
||||
- Tests domaine + use cases verts, sans import Flutter/just_audio.
|
||||
- `tool/check.sh` passe ; étape 2.1 cochée dans `ROADMAP.md`.
|
||||
|
||||
## Risques / notes
|
||||
- `Episode` est volontairement minimal ; J3 fournira de quoi le construire depuis un flux RSS. Garder le mapping RSS→Episode dans la feature `podcasts`, pas ici.
|
||||
@@ -0,0 +1,37 @@
|
||||
# 2.2 — Service audio (just_audio + audio_service)
|
||||
|
||||
## Objectif
|
||||
Implémenter `PlaybackRepository` avec `just_audio` pour le streaming et
|
||||
`audio_service` pour la lecture en arrière-plan / contrôles écran verrouillé.
|
||||
|
||||
## Périmètre & hors-périmètre
|
||||
- Inclus : impl. concrète du repository, configuration `audio_service`, mapping états plugin → `PlaybackState`, mapping erreurs → `PlaybackFailure`.
|
||||
- Exclus : domaine (2.1), UI (2.3).
|
||||
|
||||
## Dépendances
|
||||
2.1.
|
||||
|
||||
## Conception
|
||||
- **Data** (`features/playback/data/`) :
|
||||
- `JustAudioPlaybackRepository implements PlaybackRepository`.
|
||||
- Encapsule un `AudioPlayer` (just_audio) + une `ConcatenatingAudioSource` pour la file.
|
||||
- Expose `watch()` en combinant les streams just_audio (`playerStateStream`, `positionStream`, `currentIndexStream`) → `PlaybackState`.
|
||||
- Capture les erreurs de source (URL invalide, réseau) → `AudioSourceFailure`.
|
||||
- Intégration `audio_service` : `AudioHandler` reliant les commandes système (notification, casque) au repository.
|
||||
- **DI** : `playbackRepositoryProvider` (override en test par un fake).
|
||||
- **Android** : permissions/manifest requis par `audio_service` (service de premier plan, etc.).
|
||||
|
||||
## Plan TDD
|
||||
1. **Red** : `just_audio_playback_repository_test.dart` — en abstrayant `AudioPlayer` derrière une fine façade mockable : `load` configure la source et `play` délègue ; une exception de source → `AudioSourceFailure`. Le mapping des streams vers `PlaybackState` est vérifié sur des événements simulés.
|
||||
2. **Green** : implémenter la façade + le repository + le mapping.
|
||||
3. **Validation manuelle** : lire une URL audio de test sur l'appareil (la lecture réelle ne s'automatise pas de façon fiable en unit test) — consigner le résultat.
|
||||
4. **Refactor**.
|
||||
|
||||
## Definition of Done
|
||||
- Test du repository vert (façade mockée) ; lecture réelle vérifiée manuellement.
|
||||
- Lecture en arrière-plan fonctionnelle (écran éteint = audio continue).
|
||||
- `tool/check.sh` passe ; étape 2.2 cochée dans `ROADMAP.md`.
|
||||
|
||||
## Risques / notes
|
||||
- `audio_service` impose une configuration Android précise (manifest, init). Prévoir un temps de mise au point.
|
||||
- Garder la frontière nette : aucune logique métier dans cette couche, uniquement adaptation au plugin.
|
||||
@@ -0,0 +1,34 @@
|
||||
# 2.3 — UI lecteur + contrôleur Riverpod
|
||||
|
||||
## Objectif
|
||||
Offrir un lecteur utilisable par l'enfant : gros boutons lecture/pause,
|
||||
précédent/suivant, barre de progression, titre de l'histoire en cours.
|
||||
|
||||
## Périmètre & hors-périmètre
|
||||
- Inclus : contrôleur Riverpod (`AsyncNotifier`/`Notifier`) consommant les use cases, widget lecteur.
|
||||
- Exclus : la liste des histoires (jalon 4) ; ici on teste avec une file en dur.
|
||||
|
||||
## Dépendances
|
||||
2.1, 2.2.
|
||||
|
||||
## Conception
|
||||
- **Presentation** (`features/playback/presentation/`) :
|
||||
- `PlaybackController` (Riverpod `Notifier`) : expose `PlaybackState`, méthodes `play/pause/next/previous/seek` déléguant aux use cases ; s'abonne à `WatchPlaybackStateUseCase`.
|
||||
- `PlayerView` (widget) : grand bouton central play/pause (icône claire), boutons précédent/suivant, `Slider` de progression, titre + vignette.
|
||||
- Composants tactiles larges, lisibles le soir (thème de 0.3).
|
||||
- Pas de logique métier dans le widget : tout passe par le contrôleur → use cases.
|
||||
|
||||
## Plan TDD
|
||||
1. **Red** : `playback_controller_test.dart` — avec use cases mockés, `play()` appelle `TogglePlayPauseUseCase` ; l'état du contrôleur reflète les `PlaybackState` émis.
|
||||
2. **Green** : implémenter le contrôleur.
|
||||
3. **Red** : `player_view_test.dart` (widget) — tap sur le bouton play déclenche `controller.play` (provider overridé par un mock) ; l'icône bascule play/pause selon l'état ; le titre s'affiche.
|
||||
4. **Green** : implémenter le widget.
|
||||
5. **Refactor**.
|
||||
|
||||
## Definition of Done
|
||||
- Tests contrôleur + widget verts.
|
||||
- Démo manuelle : avec une file en dur, lecture/pause/suivant/seek fonctionnent et l'UI suit.
|
||||
- `tool/check.sh` passe ; étape 2.3 cochée dans `ROADMAP.md`.
|
||||
|
||||
## Risques / notes
|
||||
- Soigner l'accessibilité tactile (taille des cibles) dès maintenant : c'est l'écran que l'enfant manipule.
|
||||
@@ -0,0 +1,25 @@
|
||||
# Jalon 2 — Lecture audio
|
||||
|
||||
## Objectif
|
||||
Lire un épisode audio en streaming : lecture, pause, suivant/précédent, position,
|
||||
le tout derrière une interface de domaine propre et pilotable depuis l'UI.
|
||||
|
||||
## Périmètre
|
||||
- Lecture d'une URL audio (streaming).
|
||||
- Contrôles : play/pause, suivant/précédent, seek, progression.
|
||||
- Lecture en arrière-plan / écran verrouillé (`audio_service`).
|
||||
- Testable avec un flux/épisode « en dur » (pas besoin de J3).
|
||||
|
||||
## Hors-périmètre
|
||||
- Téléchargement hors-ligne (exclu v1).
|
||||
- File d'attente avancée / aléatoire : on garde une liste ordonnée simple.
|
||||
|
||||
## Étapes
|
||||
1. [2.1 — Domaine de lecture](01-domaine-lecture.md)
|
||||
2. [2.2 — Service audio (just_audio + audio_service)](02-service-audio.md)
|
||||
3. [2.3 — UI lecteur + contrôleur Riverpod](03-ui-lecteur.md)
|
||||
|
||||
## Definition of Done (jalon)
|
||||
- Un épisode de test se lit, se met en pause, avance/recule ; la progression s'affiche.
|
||||
- Domain/use cases testés sans dépendance Flutter/audio.
|
||||
- `tool/check.sh` passe ; `ROADMAP.md` 2.1→2.3 cochées.
|
||||
Reference in New Issue
Block a user