refactor: deduplicate shared patterns, replace hardcoded colors

This commit is contained in:
edde746
2026-03-03 22:43:00 +01:00
parent 8f3d52142d
commit cd2ab9d9cd
42 changed files with 685 additions and 612 deletions
@@ -8,31 +8,17 @@ import 'package:web_socket_channel/io.dart';
import '../../models/companion_remote/remote_command.dart';
import '../../models/companion_remote/remote_session.dart';
import '../../utils/app_logger.dart';
import '../base_peer_service.dart';
enum RemotePeerErrorType {
connectionFailed,
peerDisconnected,
dataChannelError,
serverError,
timeout,
invalidSession,
authFailed,
networkError,
unknown,
}
// Re-export so callers that import from here get the types.
export '../base_peer_service.dart' show PeerError, PeerErrorType;
class RemotePeerError {
final RemotePeerErrorType type;
final String message;
final dynamic originalError;
/// Backward-compatible aliases so existing callers that reference the
/// Remote-specific names keep compiling.
typedef RemotePeerErrorType = PeerErrorType;
typedef RemotePeerError = PeerError;
const RemotePeerError({required this.type, required this.message, this.originalError});
@override
String toString() => 'RemotePeerError($type): $message';
}
class CompanionRemotePeerService {
class CompanionRemotePeerService with KeepaliveMixin {
// Server-side (host) fields
HttpServer? _server;
WebSocket? _clientSocket;
@@ -52,7 +38,11 @@ class CompanionRemotePeerService {
final _errorController = StreamController<RemotePeerError>.broadcast();
final _connectionStateController = StreamController<RemoteSessionStatus>.broadcast();
Timer? _pingTimer;
// Keepalive (via KeepaliveMixin)
@override
Duration get pingInterval => const Duration(seconds: 5);
@override
Duration get pongTimeout => Duration.zero; // No pong timeout; host just replies inline
// Auth rate limiting
int _failedAuthAttempts = 0;
@@ -291,7 +281,7 @@ class CompanionRemotePeerService {
_clientSocket = null;
_deviceDisconnectedController.add(null);
_connectionStateController.add(RemoteSessionStatus.disconnected);
_stopPingTimer();
stopKeepalive();
}
},
onError: (error) {
@@ -328,6 +318,7 @@ class CompanionRemotePeerService {
_connectionStateController.add(RemoteSessionStatus.connecting);
_channel = IOWebSocketChannel.connect(Uri.parse(url));
await _channel!.ready;
// Send authentication message
final authMessage = jsonEncode({
@@ -361,7 +352,7 @@ class CompanionRemotePeerService {
sendDeviceInfo(deviceName, platform);
// Start ping timer
_startPingTimer();
startKeepalive();
} else if (messageType == 'authFailed') {
final message = json['message'] as String? ?? 'Authentication failed';
appLogger.w('CompanionRemote: $message');
@@ -395,7 +386,7 @@ class CompanionRemotePeerService {
appLogger.d('CompanionRemote: Connection closed');
_deviceDisconnectedController.add(null);
_connectionStateController.add(RemoteSessionStatus.disconnected);
_stopPingTimer();
stopKeepalive();
// Reconnection is handled by CompanionRemoteProvider
},
onError: (error) {
@@ -432,7 +423,9 @@ class CompanionRemotePeerService {
onTimeout: () async {
// Clean up channel on timeout
if (_channel != null) {
await _channel!.sink.close();
try {
await _channel!.sink.close();
} catch (_) {}
_channel = null;
}
throw const RemotePeerError(type: RemotePeerErrorType.timeout, message: 'Timed out joining session');
@@ -528,18 +521,16 @@ class CompanionRemotePeerService {
}
}
void _startPingTimer() {
_stopPingTimer();
_pingTimer = Timer.periodic(const Duration(seconds: 5), (_) {
if (isConnected) {
sendCommand(const RemoteCommand(type: RemoteCommandType.ping));
}
});
@override
void sendPing() {
if (isConnected) {
sendCommand(const RemoteCommand(type: RemoteCommandType.ping));
}
}
void _stopPingTimer() {
_pingTimer?.cancel();
_pingTimer = null;
@override
void onPongTimeout() {
// Not used — pong timeout is disabled for companion remote.
}
bool _shouldSendAck(RemoteCommand command) {
@@ -594,15 +585,19 @@ class CompanionRemotePeerService {
Future<void> disconnect() async {
appLogger.d('CompanionRemote: Disconnecting');
_stopPingTimer();
stopKeepalive();
if (_clientSocket != null) {
await _clientSocket!.close();
try {
await _clientSocket!.close();
} catch (_) {}
_clientSocket = null;
}
if (_channel != null) {
await _channel!.sink.close();
try {
await _channel!.sink.close();
} catch (_) {}
_channel = null;
}