5. Page de Rejoindre (JoinPageComponent)
Vue d'ensemble
La page de rejoindre permet aux utilisateurs de découvrir et rejoindre des parties en cours ou en attente créées par d'autres joueurs. Elle offre une vue publique des parties disponibles avec toutes leurs informations, ainsi qu'un système de saisie de code pour rejoindre des parties privées ou verrouillées.
Localisation
Fichier: client/src/app/pages/join-page/join-page.component.ts
Route: /join
Fonctionnalités Principales
Liste des Parties Disponibles
Affiche en temps réel toutes les parties publiques ou accessibles par l'utilisateur.
Informations affichées par partie:
- Code d'accès: Code à 4 chiffres unique
- Nom de la partie: Titre ou nom du jeu utilisé
- Nom de l'administrateur: Créateur de la partie
- Joueurs: Nombre actuel / maximum
- Taille de la carte: Dimensions de la grille
- Statut: "En attente" ou "En cours"
- Accessibilité:
- "Ouverte" (publique, non verrouillée)
- "Verrouillée" (code requis)
- "Pleine" (maximum de joueurs atteint)
- "Amis seulement" (visible uniquement aux amis)
- Frais d'entrée: Coût en monnaie virtuelle (0 si gratuit)
- Drop-in/Drop-out: Indique si on peut rejoindre en cours de jeu
- Aperçu de la carte: Miniature de la grille de jeu
Filtrage des Parties
Critères de visibilité:
- Parties publiques (
visibility: 'public') - Parties d'amis si l'utilisateur est ami avec le créateur (
visibility: 'friends') - Parties avec drop-in/drop-out activé (même si en cours)
Parties affichées:
- Toutes les parties en attente accessibles
- Parties en cours avec drop-in/drop-out activé
Parties masquées:
- Parties privées dont l'utilisateur n'est pas ami avec le créateur
- Parties en cours sans drop-in/drop-out
Rejoindre une Partie depuis la Liste
Conditions pour rejoindre:
- Partie non verrouillée
- Partie non pleine
- Visibilité appropriée (publique ou amis)
- Frais d'entrée disponibles dans la monnaie virtuelle
- Pour parties en cours : drop-in/drop-out activé
Processus de join:
- Utilisateur clique sur une partie dans la liste
- Partie sélectionnée est mise en surbrillance
- Utilisateur clique sur "Rejoindre"
- Validation du code d'accès automatique
- Vérification des conditions (frais, accessibilité)
- Si partie en attente : redirection vers création de personnage
- Si partie en cours (drop-in) : rejoindre directement la partie
Rejoindre via Code d'Accès
Alternative pour rejoindre des parties non publiques ou verrouillées.
Processus:
- Utilisateur clique sur "Entrer un code"
- Popup de saisie de code s'affiche
- Saisie d'un code à 4 chiffres
- Validation du code côté serveur
- Vérification si l'utilisateur était déjà dans cette partie (returning player)
- Si valid et nouvelle partie : redirection vers création de personnage
- Si returning player : rejoint directement la partie en cours
Validation du code:
- Format: exactement 4 chiffres
- Validation en temps réel (chaque chiffre)
- Auto-focus sur le champ suivant
- Backspace pour revenir au champ précédent
- Validation serveur pour existence et accessibilité
Rafraîchissement Automatique
Mécanisme:
- Rafraîchissement toutes les 10 secondes
- Requête manuelle via bouton "Actualiser"
- Mise à jour en temps réel via Socket.IO
Événements Socket.IO écoutés:
AvailableGamesUpdated: Nouvelle liste de partiesGameUpdated: Une partie a été modifiéeGameLockUpdated: Verrouillage/déverrouillageGameListUpdated: Liste complète mise à jour
Drop-In (Rejoindre Partie en Cours)
Fonctionnalité permettant de rejoindre une partie déjà commencée.
Conditions:
allowDropInOutactivé sur la partie- Partie pas pleine
- Accessibilité respectée
Processus spécifique drop-in:
- Validation du code/sélection de la partie
- Serveur vérifie les conditions de drop-in
- Serveur envoie les données complètes de la partie via
DropInGame - Client reçoit l'état complet du jeu
- Joueur est placé à une position libre sur la carte
- Redirection directe vers la vue de jeu (pas de création de personnage)
- Le joueur rejoint avec un personnage par défaut généré
Événement Socket.IO:
this.socketService.listen<OnGoingGame>('DropInGame').subscribe((game) => {
this.waitingRoomService.setCurrentGame(game);
this.router.navigate(['/game-view']);
});
Services Utilisés
SocketService
Méthodes:
emit(SocketIOEvents.GetAvailableGames): Demande la liste des partieslisten<AvailableGameInfo[]>(SocketIOEvents.AvailableGamesUpdated): Reçoit la listelisten(SocketIOEvents.GameUpdated): Notification de mise à jourlisten(SocketIOEvents.GameLockUpdated): Notification de verrouillagelisten<AvailableGameInfo[]>(SocketIOEvents.GameListUpdated): Liste complète
JoinCharacterCreationService
Méthodes:
isCodeValid(): Valide le code d'accès côté serveur- Retourne:
{ valid: boolean, message: string, isReturningPlayer: boolean }
- Retourne:
getAccessCode(): Récupère le code sous forme de stringgetAccessCodeArray(): Récupère le code sous forme d'arrayresetAccessCode(): Réinitialise le codeonInputChange(event, index): Gère la saisie d'un chiffreonBackspace(event, index): Gère la suppression
Validation:
- Vérifie l'existence de la partie
- Vérifie les permissions d'accès
- Vérifie si l'utilisateur était déjà dans la partie
- Retourne des messages d'erreur localisés
WaitingRoomService
Méthodes utilisées pour drop-in:
onDropInGameData(): Observable des données de drop-insetCurrentGame(game): Définit la partie courantesetCurrentPlayer(player): Définit le joueur actuel
UserService
Méthodes:
getCurrentUsername(): Récupère le username de l'utilisateurgetUserProfile(): Récupère le profil complet (pour vérifier monnaie virtuelle)
Interfaces
AvailableGameInfo
Structure des informations d'une partie disponible:
interface AvailableGameInfo {
code: string; // Code à 4 chiffres
name: string; // Nom de la partie/jeu
adminName: string; // Nom du créateur
players: number; // Nombre de joueurs actuels
maxPlayers: number; // Maximum de joueurs
mapSize: number; // Taille de la carte
previewImage: string; // URL de l'aperçu (base64 ou URL)
status: 'waiting' | 'in-progress'; // Statut
isLocked: boolean; // Partie verrouillée
isFull: boolean; // Partie pleine
accessibility: string; // 'open', 'locked', 'full', 'friends-only'
entryFee: number; // Frais d'entrée (0 si gratuit)
visibility: 'public' | 'friends'; // Visibilité
dropInOut: boolean; // Drop-in/drop-out activé
}
État du Composant
Variables principales:
availableGames: AvailableGameInfo[] // Liste des parties
selectedGame: AvailableGameInfo | null // Partie sélectionnée
isJoinCodePopupActive: boolean // Popup de code active
isCodeValid: boolean // Validation du code
codeErrorMsg: string // Message d'erreur code
codeInputs: number[] // Array pour inputs (0-3)
accessCode: string[] // Code saisi (4 chiffres)
subscriptions: Subscription[] // Souscriptions Socket.IO
Méthodes Principales
ngOnInit()
Initialise la page et configure les listeners Socket.IO.
Actions:
- Souscription aux événements Socket.IO
- Chargement initial de la liste des parties
- Configuration du rafraîchissement automatique (10s)
- Initialisation du code d'accès
ngOnDestroy()
Nettoie les souscriptions pour éviter les fuites mémoire.
loadAvailableGames()
Demande la liste actualisée des parties disponibles.
Flux:
this.socketService.emit(SocketIOEvents.GetAvailableGames);
// Serveur répond via SocketIOEvents.AvailableGamesUpdated
canJoinGame(game: AvailableGameInfo): boolean
Détermine si l'utilisateur peut rejoindre une partie.
Logique:
if (game.status === 'waiting') {
return !game.isLocked && !game.isFull &&
(game.visibility === 'public' || game.dropInOut);
} else {
return !game.isFull &&
(game.visibility === 'public' || game.dropInOut);
}
joinGame(game: AvailableGameInfo)
Rejoint une partie sélectionnée depuis la liste.
Flux:
- Vérification via
canJoinGame() - Définition du code d'accès dans
JoinCharacterCreationService - Validation du code via
isCodeValid() - Si returning player : gestion automatique
- Sinon : navigation vers création de personnage avec query params
this.router.navigate(['/character-creation'], { queryParams: { mode: 'join', gameCode: game.code } });
showJoinCodePopup()
Affiche la popup de saisie de code manuel.
closeJoinCodePopup()
Ferme la popup et réinitialise le code.
onInputChange(event: Event, index: number)
Gère la saisie d'un chiffre dans un champ du code.
Comportement:
- Accepte uniquement les chiffres (0-9)
- Auto-focus sur le champ suivant si chiffre saisi
- Limite à 1 caractère par champ
onBackspace(event: KeyboardEvent, index: number)
Gère la suppression avec la touche Backspace.
Comportement:
- Si champ vide : focus sur le champ précédent
- Si champ rempli : efface le caractère
redirectToCharacterCreation()
Valide le code saisi et redirige vers la création de personnage.
Flux:
- Validation du code via
JoinCharacterCreationService - Si invalide : affichage du message d'erreur
- Si returning player : fermeture popup et rejoindre directement
- Si valide : navigation vers création de personnage
selectGame(game: AvailableGameInfo)
Sélectionne une partie dans la liste (met en surbrillance).
joinSelectedGame()
Rejoint la partie actuellement sélectionnée.
goBack()
Retour à la page d'accueil.
Traductions
Utilise le pipe TranslatePipe pour les libellés.
Clés de traduction:
JOIN_PAGE.TITLE: "Rejoindre une Partie"JOIN_PAGE.ENTER_CODE: "Entrer un Code"JOIN_PAGE.REFRESH: "Actualiser"JOIN_PAGE.NO_GAMES_AVAILABLE: "Aucune partie disponible"JOIN_PAGE.JOIN_BUTTON: "Rejoindre"JOIN_PAGE.PLAYERS: "Joueurs"JOIN_PAGE.MAP_SIZE: "Taille"JOIN_PAGE.ENTRY_FEE: "Frais"JOIN_PAGE.STATUS_WAITING: "En attente"JOIN_PAGE.STATUS_IN_PROGRESS: "En cours"JOIN_PAGE.ACCESSIBILITY_OPEN: "Ouverte"JOIN_PAGE.ACCESSIBILITY_LOCKED: "Verrouillée"JOIN_PAGE.ACCESSIBILITY_FULL: "Pleine"JOIN_PAGE.ACCESSIBILITY_FRIENDS_ONLY: "Amis seulement"JOIN_PAGE.DROP_IN_OUT: "Drop-in/out"JOIN_PAGE.CODE_POPUP_TITLE: "Entrer le Code d'Accès"JOIN_PAGE.CANCEL: "Annuler"JOIN_PAGE.VALIDATE: "Valider"
Interface Utilisateur
Structure de la Page
Disposition:
+--------------------------------------+
| [← Retour] [Actualiser] |
| |
| Parties Disponibles |
| |
| [Entrer un Code] |
| |
| +--------------------------------+ |
| | [Aperçu] Nom: ... | |
| | Admin: ... | |
| | Joueurs: 2/4 | |
| | Carte: 10x10 | |
| | Statut: En attente | |
| | Frais: 50 coins | |
| | [Rejoindre] | |
| +--------------------------------+ |
| |
| +--------------------------------+ |
| | ... | |
| +--------------------------------+ |
| |
+--------------------------------------+
Carte de Partie
Chaque partie est affichée dans une carte contenant:
En-tête:
- Aperçu de la carte (miniature)
- Code d'accès
Corps:
- Nom de la partie
- Nom de l'administrateur
- Joueurs (actuel/max) avec icône
- Taille de carte avec icône
- Statut avec badge coloré
- Accessibilité avec icône
- Frais d'entrée (si > 0)
- Badge "Drop-in/out" si activé
Pied:
- Bouton "Rejoindre" (désactivé si conditions non remplies)
États visuels:
- Carte normale : bordure standard
- Carte sélectionnée : bordure highlighted
- Carte non accessible : opacité réduite, bouton désactivé
- Partie pleine : badge rouge
- Partie verrouillée : icône cadenas
Popup de Code d'Accès
Structure:
+----------------------------+
| Entrer le Code d'Accès |
| |
| [_] [_] [_] [_] |
| |
| [Message d'erreur] |
| |
| [Annuler] [Valider] |
+----------------------------+
Caractéristiques:
- 4 champs pour les chiffres
- Auto-focus et navigation automatique
- Validation visuelle (vert/rouge)
- Message d'erreur en rouge
- Boutons d'action
Style et Thème
Couleurs par statut:
- En attente : Vert (#4CAF50)
- En cours : Orange (#FF9800)
- Pleine : Rouge (#F44336)
- Verrouillée : Gris (#9E9E9E)
Badges:
- Drop-in/out : Cyan néon
- Frais d'entrée : Jaune or
- Accessibilité : Selon le type
Animations:
- Transition au survol des cartes
- Effet de sélection
- Chargement avec spinner
- Apparition des cartes en fade-in
Navigation
Entrée sur la Page
Depuis:
- Page d'accueil via bouton "Rejoindre une Partie"
- Navigation directe vers
/join
Sortie de la Page
Vers:
/character-creation(création de personnage pour nouvelle partie)- Query params:
{ mode: 'join', gameCode: '1234' }
- Query params:
/game-view(vue de jeu pour drop-in)/home(retour à l'accueil)
Cas d'Usage Typiques
Cas 1: Rejoindre une Partie Publique en Attente
- Utilisateur accède à
/join - Liste des parties affichée automatiquement
- Utilisateur clique sur une partie "En attente"
- Carte mise en surbrillance
- Utilisateur clique sur "Rejoindre"
- Validation réussie
- Redirection vers
/character-creation?mode=join&gameCode=1234
Cas 2: Drop-In dans une Partie en Cours
- Utilisateur voit une partie "En cours" avec badge "Drop-in/out"
- Clique sur la partie
- Clique sur "Rejoindre"
- Validation drop-in réussie
- Événement
DropInGamereçu avec état complet - Personnage par défaut créé automatiquement
- Redirection directe vers
/game-view
Cas 3: Rejoindre via Code d'Accès
- Utilisateur clique sur "Entrer un Code"
- Popup affichée
- Saisit le code à 4 chiffres: "1234"
- Clique sur "Valider"
- Code validé côté serveur
- Redirection vers création de personnage
Cas 4: Code Invalide
- Utilisateur entre un code dans la popup
- Clique sur "Valider"
- Serveur retourne
{ valid: false, message: "Partie introuvable" } - Message d'erreur affiché en rouge
- Code reste dans les champs pour correction
Cas 5: Returning Player
- Utilisateur avait quitté une partie (drop-out)
- Entre le code de cette partie
- Serveur détecte
isReturningPlayer: true - Événement
DropInGameenvoyé avec joueur existant - Redirection directe vers
/game-view - Joueur reprend son personnage et sa position
Cas 6: Partie Pleine
- Utilisateur voit une partie avec "4/4 joueurs"
- Badge rouge "Pleine" affiché
- Bouton "Rejoindre" désactivé et grisé
- Impossible de sélectionner la partie
Cas 7: Frais d'Entrée Insuffisants
- Partie avec frais de 100 coins
- Utilisateur a seulement 50 coins
- Tentative de rejoindre
- Serveur rejette avec erreur "Monnaie virtuelle insuffisante"
- Message d'erreur affiché
- Retour à la liste
Événements Socket.IO
Écoutés
AvailableGamesUpdated
- Payload:
AvailableGameInfo[] - Reçu après demande via
GetAvailableGames - Met à jour la liste complète des parties
GameUpdated
- Payload: Vide (notification)
- Déclenche un rafraîchissement de la liste
- Émis quand une partie est modifiée
GameLockUpdated
- Payload: Vide (notification)
- Déclenche un rafraîchissement de la liste
- Émis quand une partie est verrouillée/déverrouillée
GameListUpdated
- Payload:
AvailableGameInfo[] - Mise à jour en temps réel de la liste
- Mise à jour automatique sans requête
DropInGame
- Payload:
OnGoingGame - Reçu lors d'un drop-in réussi
- Contient toutes les données de la partie en cours
Émis
GetAvailableGames
- Payload: Vide
- Demande la liste des parties disponibles
- Réponse via
AvailableGamesUpdated
Validation et Sécurité
Validation Côté Client
- Format du code : exactement 4 chiffres
- Vérification de
canJoinGame()avant tentative - Vérification de la monnaie virtuelle disponible
Validation Côté Serveur
- Existence de la partie
- Permissions d'accès (visibilité, verrouillage)
- Capacité de la partie (pas pleine)
- Frais d'entrée disponibles
- Conditions de drop-in si applicable
Gestion des Erreurs
Erreurs possibles:
- "Partie introuvable"
- "Code d'accès invalide"
- "Partie verrouillée"
- "Partie pleine"
- "Monnaie virtuelle insuffisante"
- "Cette partie est réservée aux amis"
- "Drop-in non autorisé pour cette partie"
Performance
Optimisations
- Rafraîchissement limité à 10 secondes
- Liste paginée si trop de parties (futur)
- Images d'aperçu optimisées (miniatures)
- Mise à jour en temps réel via Socket.IO (pas de polling constant)
- Désabonnement automatique des événements au
ngOnDestroy
Gestion Mémoire
- Cleanup des souscriptions Socket.IO
- Cleanup du timer de rafraîchissement
- Réinitialisation de l'état au changement de page
Points d'Attention
Drop-In/Drop-Out
- Vérifie toujours si
allowDropInOutest activé - Personnage par défaut créé côté serveur pour drop-in
- Position de spawn disponible requise
- Pas de création de personnage pour drop-in
Returning Players
- Détection automatique côté serveur
- Restauration du personnage existant
- Redirection directe sans création de personnage
- Gestion des cas où le joueur avait quitté temporairement
Frais d'Entrée
- Affichés clairement sur chaque partie
- Vérification de la balance avant join
- Déduction automatique lors du join réussi
- Remboursement si partie annulée
Améliorations Futures
- Filtres avancés (par mode, taille, frais)
- Tri (par joueurs, date création, frais)
- Pagination de la liste
- Recherche par nom de partie
- Favoris et parties récentes
- Notifications push pour nouvelles parties
- Invitation directe d'amis
- Aperçu détaillé de la carte au clic
- Historique des parties jouées