Files
swift_companion/docs/pedagogie/etape-04-vue-profil.md
Ladebeze66 a6ab13ee2f fix(profil): photo CDN avec Bearer OAuth et doc soutenance
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>
2026-10-21 10:38:16 +02:00

4.8 KiB
Raw Blame History

É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)

  1. Arrivée depuis la recherche avec un objet user minimal (id, login, parfois image).
  2. Chargement : appels API en parallèle (spinner plein écran).
  3. Succès : photo, carte « Informations », liste skills, liste projets.
  4. Échec : message rouge + bouton Réessayer.
  5. 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

  1. URL : extraite du JSON user (_imageUrl dans profile_screen.dart) — image.link, puis image.versions, puis image_url.
  2. Fichier : GET sur cdn.intra.42.fr avec Authorization: Bearer (IntraAuthService.cdnImageHeaders) — le CDN refuse souvent les requêtes sans token.
  3. 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_id pour éviter de re-taper l’API au retour arrière
  • Mêmes exceptions que l’étape 3 : IntraNetworkException, IntraUnauthorizedException, etc.

ProfileScreen

  • Reçoit le user de 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).