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>
121 lines
3.2 KiB
Dart
121 lines
3.2 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
|
|
import '../../../config/app_config.dart';
|
|
import '../../../data/services/bridge_service.dart';
|
|
import 'connection_event.dart';
|
|
import 'connection_state.dart';
|
|
|
|
class ConnectionBloc extends Bloc<ConnectionEvent, ConnectionState> {
|
|
final BridgeService _bridgeService;
|
|
final AppConfig _config;
|
|
StreamSubscription? _statusSubscription;
|
|
|
|
ConnectionBloc({
|
|
required BridgeService bridgeService,
|
|
required AppConfig config,
|
|
}) : _bridgeService = bridgeService,
|
|
_config = config,
|
|
super(const ConnectionState()) {
|
|
on<ConnectAll>(_onConnectAll);
|
|
on<ConnectServer>(_onConnectServer);
|
|
on<DisconnectServer>(_onDisconnectServer);
|
|
on<DisconnectAll>(_onDisconnectAll);
|
|
on<RetryConnections>(_onRetryConnections);
|
|
on<ConnectionStatusUpdated>(_onConnectionStatusUpdated);
|
|
|
|
// Subscribe to connection status changes
|
|
_statusSubscription = _bridgeService.connectionStatus.listen((status) {
|
|
add(ConnectionStatusUpdated(status));
|
|
});
|
|
}
|
|
|
|
Future<void> _onConnectAll(
|
|
ConnectAll event,
|
|
Emitter<ConnectionState> emit,
|
|
) async {
|
|
emit(state.copyWith(overallStatus: ConnectionOverallStatus.connecting));
|
|
|
|
try {
|
|
await _bridgeService.connectAll();
|
|
} catch (e) {
|
|
emit(state.copyWith(
|
|
overallStatus: ConnectionOverallStatus.disconnected,
|
|
error: e.toString(),
|
|
));
|
|
}
|
|
}
|
|
|
|
Future<void> _onConnectServer(
|
|
ConnectServer event,
|
|
Emitter<ConnectionState> emit,
|
|
) async {
|
|
try {
|
|
await _bridgeService.connect(event.serverId);
|
|
} catch (e) {
|
|
emit(state.copyWith(error: 'Failed to connect to ${event.serverId}: $e'));
|
|
}
|
|
}
|
|
|
|
Future<void> _onDisconnectServer(
|
|
DisconnectServer event,
|
|
Emitter<ConnectionState> emit,
|
|
) async {
|
|
await _bridgeService.disconnect(event.serverId);
|
|
}
|
|
|
|
Future<void> _onDisconnectAll(
|
|
DisconnectAll event,
|
|
Emitter<ConnectionState> emit,
|
|
) async {
|
|
await _bridgeService.disconnectAll();
|
|
emit(state.copyWith(overallStatus: ConnectionOverallStatus.disconnected));
|
|
}
|
|
|
|
Future<void> _onRetryConnections(
|
|
RetryConnections event,
|
|
Emitter<ConnectionState> emit,
|
|
) async {
|
|
// Retry only disconnected servers
|
|
final disconnected = state.serverStatus.entries
|
|
.where((e) => !e.value)
|
|
.map((e) => e.key)
|
|
.toList();
|
|
|
|
for (final serverId in disconnected) {
|
|
await _bridgeService.connect(serverId);
|
|
}
|
|
}
|
|
|
|
void _onConnectionStatusUpdated(
|
|
ConnectionStatusUpdated event,
|
|
Emitter<ConnectionState> emit,
|
|
) {
|
|
final status = event.status;
|
|
ConnectionOverallStatus overall;
|
|
|
|
if (status.isEmpty) {
|
|
overall = ConnectionOverallStatus.disconnected;
|
|
} else if (status.values.every((v) => v)) {
|
|
overall = ConnectionOverallStatus.connected;
|
|
} else if (status.values.any((v) => v)) {
|
|
overall = ConnectionOverallStatus.partial;
|
|
} else {
|
|
overall = ConnectionOverallStatus.disconnected;
|
|
}
|
|
|
|
emit(state.copyWith(
|
|
overallStatus: overall,
|
|
serverStatus: status,
|
|
error: null,
|
|
));
|
|
}
|
|
|
|
@override
|
|
Future<void> close() {
|
|
_statusSubscription?.cancel();
|
|
return super.close();
|
|
}
|
|
}
|