gergle/lib/state/connection_state_bloc.dart

140 lines
3.7 KiB
Dart
Raw Normal View History

2024-12-15 00:52:28 +01:00
import 'dart:convert';
import 'dart:developer' show log;
import 'dart:io';
2024-12-15 00:52:28 +01:00
import 'package:flutter/material.dart';
2024-12-15 00:52:28 +01:00
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:web_socket_channel/web_socket_channel.dart';
import 'package:gergle/api/commands.dart';
import 'package:gergle/api/events.dart';
import 'package:gergle/state/player_state_bloc.dart';
2024-12-15 00:52:28 +01:00
@immutable
sealed class PlayerConnectionState {}
2024-12-15 00:52:28 +01:00
@immutable
class Disconnected extends PlayerConnectionState {}
2024-12-15 00:52:28 +01:00
@immutable
class Connecting extends PlayerConnectionState {
final String uri;
2024-12-15 00:52:28 +01:00
Connecting(this.uri);
}
@immutable
class Connected extends PlayerConnectionState {
final String uri;
final WebSocketChannel channel;
Connected(this.uri, this.channel);
}
@immutable
class ConnectionError extends PlayerConnectionState {
final String message;
final String uri;
ConnectionError(this.message, this.uri);
}
class ConnectionStateBloc extends Bloc<PlayerConnectionEvent, PlayerConnectionState> {
final PlayerStateBloc playerStateBloc;
ConnectionStateBloc(this.playerStateBloc) : super(Disconnected()) {
on<Connect>((event, emit) async {
if (state is Connected) {
if ((state as Connected).uri == event.uri) {
log('Already connected to ${event.uri}');
return;
} else {
// Clear connection, and reconnect
(state as Connected).channel.sink.close();
playerStateBloc.add(const ClearPlayerState());
}
2024-12-15 00:52:28 +01:00
}
emit(Connecting(event.uri));
2024-12-15 00:52:28 +01:00
final channel = WebSocketChannel.connect(
2024-12-15 00:52:28 +01:00
Uri.parse(event.uri),
);
try {
await channel.ready;
} on WebSocketChannelException catch (e) {
late final String message;
if (e.inner is WebSocketException) {
message = (e.inner as WebSocketException).message;
} else {
message = e.message ?? e.toString();
}
log('Error connecting to ${event.uri}: $message');
emit(ConnectionError(message, event.uri));
return;
}
channel.stream.listen(
2024-12-15 00:52:28 +01:00
(event) {
final jsonData = jsonDecode(event as String);
if (jsonData is Map) {
switch (jsonData['type']) {
case 'initial_state':
playerStateBloc.add(
InitialPlayerState.fromJson(jsonData['value']),
);
break;
case 'event':
final event = parseEvent(jsonData['value']);
if (event == null) {
log('Unknown event: ${jsonData['value']}');
} else {
log('Handling event: $event');
playerStateBloc.add(event);
}
break;
default:
log('Unknown message type: ${jsonData['type']}');
log('Message: $jsonData');
break;
}
}
},
onError: (error, stackTrace) {
2024-12-15 00:52:28 +01:00
log('Error: $error');
log('Stack trace: $stackTrace');
2024-12-15 00:52:28 +01:00
},
onDone: () {
add(Disconnect());
log('Connection closed, reconnecting...');
add(Connect(event.uri));
2024-12-15 00:52:28 +01:00
},
);
emit(Connected(event.uri, channel));
2024-12-15 00:52:28 +01:00
});
on<Disconnect>((event, emit) {
if (state is! Connected) {
log('Cannot disconnect when not connected');
return;
}
(state as Connected).channel.sink.close();
2024-12-15 00:52:28 +01:00
playerStateBloc.add(const ClearPlayerState());
emit(Disconnected());
2024-12-15 00:52:28 +01:00
});
on<Command>((event, emit) {
if (state is! Connected) {
2024-12-15 00:52:28 +01:00
log('Cannot send command when not connected');
return;
}
(state as Connected).channel.sink.add(event.toJsonString());
2024-12-15 00:52:28 +01:00
});
}
}