Files
COPILOT/copilot_keyboard/lib/presentation/widgets/takeover_dialog.dart
klas 40143734fc Initial commit: COPILOT D6 Flutter keyboard controller
Flutter web app replacing legacy WPF CCTV surveillance keyboard controller.
Includes wall overview, section view with monitor grid, camera input,
PTZ control, alarm/lock/sequence BLoCs, and legacy-matching UI styling.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 14:57:38 +01:00

148 lines
4.3 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../blocs/lock/lock_bloc.dart';
import '../blocs/lock/lock_event.dart';
import '../blocs/lock/lock_state.dart';
/// Show the takeover confirmation dialog.
/// Called when another keyboard requests takeover of a camera we have locked.
void showTakeoverDialog(BuildContext context, TakeoverRequest request) {
showDialog(
context: context,
barrierDismissible: false,
builder: (_) => BlocProvider.value(
value: context.read<LockBloc>(),
child: _TakeoverDialog(request: request),
),
);
}
class _TakeoverDialog extends StatefulWidget {
final TakeoverRequest request;
const _TakeoverDialog({required this.request});
@override
State<_TakeoverDialog> createState() => _TakeoverDialogState();
}
class _TakeoverDialogState extends State<_TakeoverDialog> {
static const _autoRejectSeconds = 30;
late Timer _autoRejectTimer;
int _remainingSeconds = _autoRejectSeconds;
@override
void initState() {
super.initState();
_autoRejectTimer = Timer.periodic(const Duration(seconds: 1), (_) {
if (_remainingSeconds <= 1) {
_reject();
} else {
setState(() => _remainingSeconds--);
}
});
}
@override
void dispose() {
_autoRejectTimer.cancel();
super.dispose();
}
void _confirm() {
_autoRejectTimer.cancel();
context.read<LockBloc>().add(
ConfirmTakeover(widget.request.cameraId, confirm: true),
);
Navigator.of(context).pop();
}
void _reject() {
_autoRejectTimer.cancel();
context.read<LockBloc>().add(
ConfirmTakeover(widget.request.cameraId, confirm: false),
);
if (mounted) Navigator.of(context).pop();
}
@override
Widget build(BuildContext context) {
return AlertDialog(
backgroundColor: const Color(0xFF1A202C),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: const BorderSide(color: Color(0xFFED8936), width: 2),
),
title: const Row(
children: [
Icon(Icons.warning_amber_rounded, color: Color(0xFFED8936), size: 28),
SizedBox(width: 8),
Text(
'Požadavek na převzetí',
style: TextStyle(color: Colors.white),
),
],
),
content: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
RichText(
text: TextSpan(
style: const TextStyle(color: Colors.white70, fontSize: 14),
children: [
const TextSpan(text: 'Klávesnice '),
TextSpan(
text: widget.request.requestingKeyboard,
style: const TextStyle(
fontWeight: FontWeight.bold, color: Colors.white),
),
const TextSpan(text: ' požaduje převzetí kamery '),
TextSpan(
text: '${widget.request.cameraId}',
style: const TextStyle(
fontWeight: FontWeight.bold, color: Color(0xFF00D4FF)),
),
const TextSpan(text: '.'),
],
),
),
const SizedBox(height: 16),
// Countdown progress
LinearProgressIndicator(
value: _remainingSeconds / _autoRejectSeconds,
backgroundColor: const Color(0xFF2D3748),
valueColor:
const AlwaysStoppedAnimation<Color>(Color(0xFFED8936)),
),
const SizedBox(height: 4),
Text(
'Automatické zamítnutí za $_remainingSeconds s',
style: const TextStyle(color: Colors.white54, fontSize: 12),
),
],
),
actions: [
TextButton(
onPressed: _reject,
child: const Text(
'Zamítnout',
style: TextStyle(color: Color(0xFFDC2626)),
),
),
ElevatedButton(
onPressed: _confirm,
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFFED8936),
foregroundColor: Colors.white,
),
child: const Text('Povolit převzetí'),
),
],
);
}
}