import React, { useCallback, useRef, useState } from 'react'; import { View, Text, FlatList, TouchableOpacity, StyleSheet, ActivityIndicator, } from 'react-native'; import { useFocusEffect } from '@react-navigation/native'; import { obtenirClasse, effectuerTirage, reTirer } from '../storage'; import { couleurs } from '../theme'; function formaterDate(iso) { const d = new Date(iso); const pad = (n) => String(n).padStart(2, '0'); return `${pad(d.getDate())}/${pad(d.getMonth() + 1)}/${d.getFullYear()} ${pad( d.getHours() )}:${pad(d.getMinutes())}`; } export default function DetailClasseScreen({ route, navigation }) { const { classeId } = route.params; const [classe, setClasse] = useState(null); const [enCours, setEnCours] = useState(false); // Ref en plus du state : le state ne se répercute qu'au prochain rendu, // ce qui laisse une fenêtre où un second tap très rapide (avant que le // bouton soit visuellement désactivé) pourrait lancer un 2e tirage pendant // que le premier écrit encore dans le stockage. La ref, elle, est lue et // mise à jour de façon synchrone, donc elle bloque le double-tap immédiatement. const enCoursRef = useRef(false); const recharger = useCallback(async () => { const c = await obtenirClasse(classeId); setClasse(c); if (c) navigation.setOptions({ title: c.nom }); }, [classeId]); useFocusEffect( useCallback(() => { recharger(); }, [recharger]) ); const tirer = async () => { if (enCoursRef.current) return; enCoursRef.current = true; setEnCours(true); try { const c = await effectuerTirage(classeId); setClasse(c); } finally { enCoursRef.current = false; setEnCours(false); } }; const reTirerAction = async () => { if (enCoursRef.current) return; enCoursRef.current = true; setEnCours(true); try { const c = await reTirer(classeId); setClasse(c); } finally { enCoursRef.current = false; setEnCours(false); } }; if (!classe) { return ( ); } const dernierTirage = classe.tirages[0]; return ( {classe.nombreEleves} élèves · pondération max ×{classe.ponderationMax} 🎲 Tirer un élève au sort {dernierTirage && ( <> n°{dernierTirage.numeroEleve} 🔁 Re-tirer (annule et retire) )} Historique des tirages item.id} contentContainerStyle={{ paddingBottom: 24 }} ListEmptyComponent={ Aucun tirage pour le moment. } renderItem={({ item, index }) => ( {item.numeroEleve} {formaterDate(item.dateTirage)} {index === 0 && dernier} )} /> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: couleurs.fond, padding: 16 }, centre: { justifyContent: 'center', alignItems: 'center' }, sousTitre: { color: couleurs.texteSecondaire, marginBottom: 14, fontSize: 14 }, cardTirage: { backgroundColor: couleurs.carte, borderRadius: 14, padding: 20, alignItems: 'center', marginBottom: 22, shadowColor: '#000', shadowOpacity: 0.06, shadowRadius: 4, shadowOffset: { width: 0, height: 1 }, elevation: 1, }, bouton: { backgroundColor: couleurs.primaire, borderRadius: 10, paddingVertical: 14, paddingHorizontal: 20, alignItems: 'center', width: '100%', }, boutonDesactive: { opacity: 0.6 }, boutonTexte: { color: 'white', fontSize: 16, fontWeight: '600' }, numeroTire: { fontSize: 52, fontWeight: '800', color: couleurs.primaire, marginVertical: 14, }, boutonSecondaire: { borderRadius: 10, paddingVertical: 12, paddingHorizontal: 20, alignItems: 'center', width: '100%', backgroundColor: couleurs.fond, borderWidth: 1, borderColor: couleurs.bordure, }, boutonSecondaireTexte: { color: couleurs.texte, fontSize: 15, fontWeight: '600' }, titreHistorique: { fontSize: 16, fontWeight: '700', marginBottom: 10, color: couleurs.texte }, texteVide: { color: couleurs.texteSecondaire }, ligneHistorique: { flexDirection: 'row', alignItems: 'center', paddingVertical: 10, borderBottomWidth: 1, borderBottomColor: couleurs.bordure, }, badge: { backgroundColor: couleurs.primaire, borderRadius: 14, width: 28, height: 28, justifyContent: 'center', alignItems: 'center', marginRight: 12, }, badgeTexte: { color: 'white', fontWeight: '700', fontSize: 13 }, dateHistorique: { color: couleurs.texteSecondaire, flex: 1 }, etiquetteRecent: { fontSize: 11, color: couleurs.succes, fontWeight: '700', textTransform: 'uppercase', }, });