diff --git a/lib/Screens/MainView/section_page_detail.dart b/lib/Screens/MainView/section_page_detail.dart index 5f7244e..4cd7e6b 100644 --- a/lib/Screens/MainView/section_page_detail.dart +++ b/lib/Screens/MainView/section_page_detail.dart @@ -49,11 +49,13 @@ class _SectionPageDetailState extends State { bool init = false; DateTime? _sectionOpenTime; StatisticsService? _statisticsService; + Timer? _idleTimer; @override void initState() { super.initState(); _sectionOpenTime = DateTime.now(); + _restartIdleTimer(); WidgetsBinding.instance.addPostFrameCallback((_) { final tabletAppContext = Provider.of(context, listen: false).getContext() as TabletAppContext; _statisticsService = tabletAppContext.statisticsService; @@ -64,8 +66,25 @@ class _SectionPageDetailState extends State { }); } + /// Un visiteur part sans fermer l'écran ; le suivant trouve le contenu du + /// précédent. Le minuteur vit ici parce que cet écran enveloppe les treize + /// types : l'écrire dans les écrans eux-mêmes le ferait écrire treize fois. + void _restartIdleTimer() { + _idleTimer?.cancel(); + _idleTimer = Timer(kKioskIdleTimeout, _returnHome); + } + + void _returnHome() { + if (!mounted) return; + Navigator.of(context).popUntil((route) => route.isFirst); + } + @override void dispose() { + _idleTimer?.cancel(); + // Le retour automatique passe par ce dispose, donc il émet un sectionLeave + // avec sa durée réelle. Sans lui, une consultation abandonnée resterait + // ouverte jusqu'au visiteur suivant et fausserait la statistique. final duration = _sectionOpenTime != null ? DateTime.now().difference(_sectionOpenTime!).inSeconds : null; @@ -85,7 +104,14 @@ class _SectionPageDetailState extends State { return Scaffold( resizeToAvoidBottomInset: false, - body: SingleChildScrollView( + // Listener plutôt que GestureDetector : il observe les pointeurs sans + // entrer en concurrence avec les gestes des écrans en dessous — une carte + // qu'on fait glisser ou un quiz qu'on tape ne doivent rien perdre. + body: Listener( + behavior: HitTestBehavior.translucent, + onPointerDown: (_) => _restartIdleTimer(), + onPointerMove: (_) => _restartIdleTimer(), + child: SingleChildScrollView( child: Container( height: size.height, width: size.width, @@ -236,6 +262,7 @@ class _SectionPageDetailState extends State { ), ), ), + ), ), /*floatingActionButtonLocation: FloatingActionButtonLocation.endTop, floatingActionButton: Padding( diff --git a/lib/constants.dart b/lib/constants.dart index 18ee9e3..d0deb43 100644 --- a/lib/constants.dart +++ b/lib/constants.dart @@ -17,6 +17,10 @@ const kBackgroundLight = Color(0xfff3f3f3); const List languages = ["FR", "NL", "EN", "DE", "IT", "ES", "PL", "CN", "AR", "UK"]; // hmmmm depends on config.. +/// Délai sans interaction au bout duquel la borne revient à l'accueil, pour que +/// le visiteur suivant ne trouve pas le contenu du précédent. +const Duration kKioskIdleTimeout = Duration(minutes: 5); + const kTitleSize = 32.0; const kDescriptionSize = 22.0;