import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../data/services/sync_service.dart'; import '../../data/services/sound_service.dart'; import '../../core/enums.dart'; import '../../core/sacred_tokens.dart'; import '../../providers.dart'; import '../admin/admin_screen.dart'; import 'main_screen.dart'; import 'adzan_screen.dart'; import 'iqomah_screen.dart'; import 'black_screen.dart'; import 'slideshow_screen.dart'; import 'jumat_screen.dart'; import 'khutbah_screen.dart'; /// The root view that orchestrates all screen states via AnimatedSwitcher. class HomeView extends ConsumerStatefulWidget { const HomeView({super.key}); @override ConsumerState createState() => _HomeViewState(); } class _HomeViewState extends ConsumerState { static const List _adminUnlockSequence = [ LogicalKeyboardKey.arrowUp, LogicalKeyboardKey.arrowUp, LogicalKeyboardKey.arrowDown, LogicalKeyboardKey.arrowDown, LogicalKeyboardKey.arrowLeft, LogicalKeyboardKey.arrowRight, LogicalKeyboardKey.arrowLeft, LogicalKeyboardKey.arrowRight, LogicalKeyboardKey.select, ]; final FocusNode _homeFocusNode = FocusNode(debugLabel: 'home_tv_root'); final List _recentKeys = []; Timer? _comboResetTimer; @override void initState() { super.initState(); WidgetsBinding.instance.addPostFrameCallback((_) { _checkAutoSync(); if (mounted) { _homeFocusNode.requestFocus(); } }); } @override void dispose() { _comboResetTimer?.cancel(); _homeFocusNode.dispose(); super.dispose(); } Future _checkAutoSync() async { final schedule = ref.read(todayScheduleProvider); if (schedule == null) { debugPrint('[AutoSync] No schedule found for today! Starting auto-sync...'); final success = await SyncService.instance.syncMonthlyData(); if (success && mounted) { debugPrint('[AutoSync] Success! Invalidating todayScheduleProvider.'); ref.invalidate(todayScheduleProvider); } else { debugPrint('[AutoSync] Failed or data remained empty.'); } } } KeyEventResult _handleTvKey(FocusNode node, KeyEvent event) { if (event is! KeyDownEvent) return KeyEventResult.ignored; final key = event.logicalKey; if (!_isComboKey(key)) { _resetCombo(); return KeyEventResult.ignored; } _comboResetTimer?.cancel(); _comboResetTimer = Timer(const Duration(seconds: 3), _resetCombo); _recentKeys.add(key); if (_recentKeys.length > _adminUnlockSequence.length) { _recentKeys.removeAt(0); } if (_matchesUnlockSequence()) { _resetCombo(); WidgetsBinding.instance.addPostFrameCallback((_) async { if (!mounted) return; await Navigator.of(context).push( MaterialPageRoute(builder: (_) => const AdminScreen()), ); if (mounted) { _homeFocusNode.requestFocus(); } }); return KeyEventResult.handled; } return KeyEventResult.ignored; } bool _isComboKey(LogicalKeyboardKey key) { return key == LogicalKeyboardKey.arrowUp || key == LogicalKeyboardKey.arrowDown || key == LogicalKeyboardKey.arrowLeft || key == LogicalKeyboardKey.arrowRight || key == LogicalKeyboardKey.select || key == LogicalKeyboardKey.enter; } bool _matchesUnlockSequence() { if (_recentKeys.length != _adminUnlockSequence.length) return false; for (var i = 0; i < _adminUnlockSequence.length; i++) { final current = _recentKeys[i] == LogicalKeyboardKey.enter ? LogicalKeyboardKey.select : _recentKeys[i]; if (current != _adminUnlockSequence[i]) return false; } return true; } void _resetCombo() { _comboResetTimer?.cancel(); _recentKeys.clear(); } @override Widget build(BuildContext context) { // Audio trigger listener ref.listen(screenStateProvider, (previous, next) { if (previous == null) return; // TRIGGER 1: Adzan Beep (Fires precisely when transitioning to Adzan) if (previous.state != ScreenState.adzan && next.state == ScreenState.adzan) { SoundService.instance.playAdzanBeep(); } // TRIGGER 2: 3-Second Iqomah Countdown if (next.state == ScreenState.menujuIqomah && next.iqomahRemaining != null) { // Play precisely on the tick where it is 3 seconds. if (previous.iqomahRemaining?.inSeconds != 3 && next.iqomahRemaining!.inSeconds == 3) { SoundService.instance.playIqomahCountdown(); } } }); final screenData = ref.watch(screenStateProvider); final isMainScreen = ref.watch(isMainScreenProvider); // Determine which screen to display Widget screen; switch (screenData.state) { case ScreenState.normal: case ScreenState.menujuAdzan: if (screenData.isFriday && screenData.nextPrayer?.id == 'dzuhur') { screen = const JumatScreen(key: ValueKey('jumat')); } else { screen = isMainScreen ? const MainScreen(key: ValueKey('main')) : const SlideshowScreen(key: ValueKey('slideshow')); } break; case ScreenState.kembaliNormal: screen = const MainScreen(key: ValueKey('main')); break; case ScreenState.adzan: screen = const AdzanAlertScreen(key: ValueKey('adzan')); break; case ScreenState.menujuIqomah: if (screenData.isFriday && screenData.activePrayer?.id == 'dzuhur') { screen = const KhutbahScreen(key: ValueKey('khutbah')); } else { screen = const IqomahScreen(key: ValueKey('iqomah')); } break; case ScreenState.shalat: screen = const BlackScreen(key: ValueKey('black')); break; } final isSimulating = ref.watch(mockTimeOffsetProvider) != Duration.zero; return Focus( autofocus: true, focusNode: _homeFocusNode, onKeyEvent: _handleTvKey, child: Scaffold( backgroundColor: SacredColors.background, body: Stack( children: [ AnimatedSwitcher( duration: const Duration(milliseconds: 800), transitionBuilder: (child, animation) { return FadeTransition(opacity: animation, child: child); }, child: screen, ), if (isSimulating) Positioned( right: 64, bottom: 64, child: ElevatedButton.icon( onPressed: () { ref.read(mockTimeOffsetProvider.notifier).state = Duration.zero; }, icon: const Icon(Icons.cancel, color: Colors.white), label: const Text( 'BATALKAN SIMULASI', style: TextStyle( fontWeight: FontWeight.bold, letterSpacing: 2, color: Colors.white, ), ), style: ElevatedButton.styleFrom( backgroundColor: Colors.red.shade800, padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), elevation: 10, ), ), ), ], ), ), ); } }