Charge les avatars depuis cdn.intra.42.fr avec le même access token, parse image.versions, et documente le flux pour la correction. Co-authored-by: Cursor <cursoragent@cursor.com>
4.8 KiB
4.8 KiB
Étape 4 — Vue 2 : profil complet
Prérequis : étape 3 — recherche (navigation vers vue 2 avec un login valide).
Objectif sujet
| Exigence | Cette étape |
|---|---|
| ≥ 4 infos + photo | login (titre / avatar), email, téléphone, niveau cursus, localisation, wallet — photo : URL API puis GET CDN avec Bearer (_IntraProfileAvatar) |
| Skills | Niveau entier + pourcentage (partie décimale du level API) |
| Projets | Statuts finished et failed |
| Erreurs | Réseau, 401, réponse API inattendue — message + réessai |
| Token | Un seul OAuth : IntraAuthService pour tous les GET |
| Retour vue 1 | AppBar + bouton retour système (Navigator.pop) |
| Layout | LayoutBuilder : colonne sur téléphone, 2 colonnes si largeur ≥ 720 px |
Parcours utilisateur (vue 2)
- Arrivée depuis la recherche avec un objet
userminimal (id,login, parfoisimage). - Chargement : appels API en parallèle (spinner plein écran).
- Succès : photo, carte « Informations », liste skills, liste projets.
- Échec : message rouge + bouton Réessayer.
- Retour : flèche AppBar → vue 1, nouvelle recherche possible.
API 42 utilisée
Doc : api.intra.42.fr/apidoc
| Donnée | Endpoint | Notes |
|---|---|---|
| Fiche user | (déjà renvoyée par la recherche étape 3) | email, phone, location, wallet, image — pas de 2ᵉ GET /users/{id} pour limiter le quota |
| Fiche complète | GET /v2/users/{login} |
inclut souvent cursus_users + skills par cursus |
| Niveau / skills | cursus_users[].skills |
pas /experiences (souvent 403 avec token étudiant) |
| Projets | GET /v2/users/{id}/projects_users?page[size]=100 |
scope OAuth projects ; filtrer côté app : finished / failed |
Tous les appels :
Authorization: Bearer <access_token>
Accept: application/json
Confidentialité : certains champs peuvent être masqués par l’API selon le profil consulté — afficher — si absent (sujet OAuth).
Photo de profil
- URL : extraite du JSON user (
_imageUrldansprofile_screen.dart) —image.link, puisimage.versions, puisimage_url. - Fichier :
GETsurcdn.intra.42.fravecAuthorization: Bearer(IntraAuthService.cdnImageHeaders) — le CDN refuse souvent les requêtes sans token. - UI : widget
_IntraProfileAvatar(bytes en mémoire +CircleAvatar) ; sinon initiale du login.
Détail oral : fiche-soutenance-orale.md §5 « Photo de profil ».
Skills — niveau et pourcentage
L’API renvoie un level flottant (ex. 8.42) :
- Niveau affiché : partie entière (
8) - Pourcentage : partie décimale × 100 (
42 %)
Fichiers (étape 4)
lib/
├── services/
│ ├── intra_auth_service.dart # existant — headers Bearer
│ ├── intra_user_service.dart # existant — recherche
│ └── intra_profile_service.dart # NEW — agrégation profil
└── screens/
└── profile_screen.dart # StatefulWidget — chargement + UI
IntraProfileService
fetchProfile(Map<String, dynamic> summaryUser)→IntraUserProfile- Requêtes séquentielles (pause ~450 ms) + retry sur 429 (
intra_http.dart) - Cache profil 2 min par
user_idpour éviter de re-taper l’API au retour arrière - Mêmes exceptions que l’étape 3 :
IntraNetworkException,IntraUnauthorizedException, etc.
ProfileScreen
- Reçoit le
userde l’étape 3 initState→_load()- Pas de second OAuth
Gestion des erreurs (vue 2)
| Situation | Comportement |
|---|---|
| Pas de réseau | Message + Réessayer |
| Token expiré (401) | « Session expirée… » — reconnecter depuis vue 1 |
| Trop de requêtes (429) | Message dédié ; retry automatique côté client + attendre avant de réessayer |
| Accès refusé (403) | Scopes public + projects sur l’app OAuth, puis reconnexion dans l’app |
| ID manquant | Erreur métier explicite |
| Listes vides skills / projets | Texte informatif, pas de crash |
Critères « étape 4 terminée »
| # | Test |
|---|---|
| 1 | Login valide → photo + au moins 4 champs remplis ou — |
| 2 | Section skills avec niveau et pourcentage |
| 3 | Au moins un projet finished ou failed visible (si l’étudiant en a) |
| 4 | Mode avion sur vue 2 → erreur + réessai |
| 5 | Retour → vue 1 sans perdre la session OAuth |
| 6 | Largeur tablette / fenêtre large → mise en page 2 colonnes |
Ensuite — étape 5
→ Affiner navigation, contraintes et polish responsive sur les deux vues (etape-05-navigation-layout.md à rédiger).