import 'package:flutter/material.dart' hide LockState; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../config/app_config.dart'; import '../../data/services/function_button_service.dart'; import '../../injection_container.dart'; import '../blocs/connection/connection_bloc.dart'; import '../blocs/camera/camera_bloc.dart'; import '../blocs/monitor/monitor_bloc.dart'; import '../blocs/ptz/ptz_bloc.dart'; import '../blocs/alarm/alarm_bloc.dart'; import '../blocs/lock/lock_bloc.dart'; import '../blocs/lock/lock_event.dart'; import '../blocs/lock/lock_state.dart'; import '../blocs/sequence/sequence_bloc.dart'; import '../blocs/sequence/sequence_event.dart'; import '../blocs/wall/wall_bloc.dart'; import '../blocs/wall/wall_event.dart'; import '../widgets/wall_grid/wall_grid.dart'; import '../widgets/toolbar/bottom_toolbar.dart'; import '../widgets/connection_status_bar.dart'; import '../widgets/ptz_control.dart'; import '../widgets/sequence_panel.dart'; import '../widgets/takeover_dialog.dart'; class KeyboardScreen extends StatelessWidget { const KeyboardScreen({super.key}); @override Widget build(BuildContext context) { return MultiBlocProvider( providers: [ BlocProvider(create: (_) => sl()), BlocProvider(create: (_) => sl()), BlocProvider(create: (_) => sl()), BlocProvider(create: (_) => sl()), BlocProvider(create: (_) => sl()), BlocProvider(create: (_) => sl()), BlocProvider( create: (_) => sl()..add(LoadSequences()), ), BlocProvider( create: (_) => sl()..add(const LoadWallConfig()), ), ], child: BlocListener( listenWhen: (prev, curr) => prev.pendingTakeover != curr.pendingTakeover && curr.pendingTakeover != null, listener: (context, state) { if (state.pendingTakeover != null) { showTakeoverDialog(context, state.pendingTakeover!); } }, child: const _KeyboardScreenContent(), ), ); } } class _KeyboardScreenContent extends StatelessWidget { const _KeyboardScreenContent(); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFF0D1117), body: Column( children: [ // Top bar with connection status Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: const BoxDecoration( color: Color(0xFF161B22), border: Border( bottom: BorderSide(color: Color(0xFF30363D), width: 1), ), ), child: Row( children: [ const Text( 'COPILOT Keyboard', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold, ), ), const SizedBox(width: 12), const Text( 'v0.3.0-build5', style: TextStyle( color: Color(0xFF7F7F7F), fontSize: 11, ), ), const Spacer(), const ConnectionStatusBar(), ], ), ), // Main content area Expanded( child: LayoutBuilder( builder: (context, constraints) { // Wide layout with PTZ on the side if (constraints.maxWidth > 1200) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ // Wall grid Expanded( flex: 4, child: Padding( padding: const EdgeInsets.all(16), child: const WallGrid(), ), ), // PTZ controls sidebar Container( width: 220, padding: const EdgeInsets.all(16), decoration: const BoxDecoration( color: Color(0xFF161B22), border: Border( left: BorderSide(color: Color(0xFF30363D), width: 1), ), ), child: const SingleChildScrollView( child: PtzControl(), ), ), ], ); } // Narrow layout - PTZ in bottom sheet or collapsed return Padding( padding: const EdgeInsets.all(16), child: const WallGrid(), ); }, ), ), // Bottom toolbar BottomToolbar( onSearchPressed: () => _showSearchDialog(context), onPrepositionPressed: () => _showPrepositionDialog(context), onPlaybackPressed: () => _showPlaybackOverlay(context), onAlarmListPressed: () => _showAlarmListDialog(context), onSequencePressed: () => _showSequenceDialog(context), onLockPressed: () => _toggleLock(context), onFunctionButtonPressed: (buttonId) => _executeFunctionButton(context, buttonId), ), ], ), ); } void _showSearchDialog(BuildContext context) { showDialog( context: context, builder: (context) => AlertDialog( backgroundColor: const Color(0xFF1A202C), title: const Text( 'Hledat kameru', style: TextStyle(color: Colors.white), ), content: SizedBox( width: 400, child: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( style: const TextStyle(color: Colors.white), decoration: const InputDecoration( hintText: 'Číslo nebo název kamery', hintStyle: TextStyle(color: Colors.white54), prefixIcon: Icon(Icons.search, color: Colors.white54), border: OutlineInputBorder(), enabledBorder: OutlineInputBorder( borderSide: BorderSide(color: Colors.white24), ), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: Color(0xFF00D4FF)), ), ), autofocus: true, ), const SizedBox(height: 16), const Text( 'Funkce bude implementována v další fázi.', style: TextStyle(color: Colors.white54, fontSize: 12), ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Zavřít'), ), ], ), ); } void _showPrepositionDialog(BuildContext context) { showDialog( context: context, builder: (context) => AlertDialog( backgroundColor: const Color(0xFF1A202C), title: const Text( 'Prepozice', style: TextStyle(color: Colors.white), ), content: const SizedBox( width: 400, height: 300, child: Center( child: Text( 'Seznam prepozic bude implementován v další fázi.', style: TextStyle(color: Colors.white54), ), ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Zavřít'), ), ], ), ); } void _showPlaybackOverlay(BuildContext context) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Režim přehrávání (PvZ) bude implementován v další fázi.'), backgroundColor: Color(0xFF2D3748), ), ); } void _showSequenceDialog(BuildContext context) { final wallState = context.read().state; final viewerId = wallState.selectedViewerId; if (viewerId == null) return; // Refresh sequence list context.read().add(LoadSequences()); showDialog( context: context, builder: (_) => BlocProvider.value( value: context.read(), child: AlertDialog( backgroundColor: const Color(0xFF1A202C), title: Text( 'Sekvence — Monitor $viewerId', style: const TextStyle(color: Colors.white), ), content: SizedBox( width: 500, height: 400, child: SingleChildScrollView( child: SequencePanel(viewerId: viewerId), ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Zavřít'), ), ], ), ), ); } void _showAlarmListDialog(BuildContext context) { showDialog( context: context, builder: (context) => AlertDialog( backgroundColor: const Color(0xFF1A202C), title: const Text( 'Historie alarmů', style: TextStyle(color: Colors.white), ), content: const SizedBox( width: 500, height: 400, child: Center( child: Text( 'Seznam alarmů bude implementován v další fázi.', style: TextStyle(color: Colors.white54), ), ), ), actions: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Zavřít'), ), ], ), ); } void _toggleLock(BuildContext context) { final wallState = context.read().state; final viewerId = wallState.selectedViewerId; if (viewerId == null) return; final viewerState = wallState.getViewerState(viewerId); final cameraId = viewerState.currentCameraId; if (cameraId <= 0) return; final lockBloc = context.read(); final lockState = lockBloc.state; if (lockState.isCameraLockedByMe( cameraId, sl().keyboardId)) { lockBloc.add(ReleaseLock(cameraId)); } else { lockBloc.add(TryLock(cameraId)); } } void _executeFunctionButton(BuildContext context, String buttonId) { final wallBloc = context.read(); final wallId = wallBloc.state.config?.id ?? '1'; final service = sl(); if (!service.hasActions(wallId, buttonId)) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('$buttonId — žádná akce pro tuto stěnu'), backgroundColor: const Color(0xFF2D3748), duration: const Duration(seconds: 1), ), ); return; } service.execute(wallId, buttonId); } }