import 'dart:convert'; import 'dart:io'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:frontend/providers/auth.dart'; import 'package:logging/logging.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:shared_models/room.dart'; part 'web_socket.g.dart'; final _logger = Logger('WebSocketNotifier'); @riverpod class WebSocketNotifier extends _$WebSocketNotifier { @override Future<WebSocket?> build() async { return null; } Future<void> connect() async { state = AsyncValue.loading(); final jwt = ref.read(jwtNotifierProvider).valueOrNull; final jwtBody = ref.read(jwtBodyProvider); if (jwt == null || jwtBody == null) { _logger.warning('Tried to connect to ws without jwt token'); return; } final wsUrl = Uri.parse('ws://localhost:8080/room/${jwtBody.roomCode}/ws'); _logger.finest('Attempting to connect to $wsUrl'); try { final connection = await WebSocket.connect(wsUrl.toString(), headers: {'Authorization': 'Bearer: $jwt'}); _logger.fine('Client ws connection established to room ${jwtBody.roomUuid}'); state = AsyncValue.data(connection); } catch (e) { if (e is WebSocketException) { if (e.httpStatusCode == 401) { ref.read(jwtNotifierProvider.notifier).eraseJwt(); } } _logger.warning('Error occurred creating web socket: $e'); } } void sendMessage(GameRoomMessage message) { final msgStr = jsonEncode(message.toJson()); final socket = state.valueOrNull; if (socket == null) { // TODO add queue _logger.info('Socket unavailable... adding to queue'); throw UnimplementedError('No queue available'); } _logger.finest('Sending message $message on websocket'); socket.add(msgStr); } } @riverpod Raw<Stream<dynamic>> webSocketStream(Ref ref) { final connection = ref.watch(webSocketNotifierProvider).valueOrNull; if (connection == null) return Stream.empty(); _logger.finest('Created broadcast stream from ws connection'); return connection.asBroadcastStream(); }