import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:huacu_mobile/models/auth_data.dart'; import 'package:huacu_mobile/models/game.dart'; import 'package:huacu_mobile/ui/widgets/socket_connection_indicator.dart'; import 'package:socket_io_client/socket_io_client.dart' as io; class HomePage extends StatefulWidget { const HomePage({super.key}); @override State createState() => _HomePageState(); } class _HomePageState extends State { final io.Socket socket = Get.find(); final AuthData authData = Get.find(); var guessersPlayers = [].obs; var suggestersPlayers = [].obs; var incommingRequests = [].obs; @override void initState() { super.initState(); socket.on("hello", (idky) { socket.dispose(); Get.offAllNamed("/auth"); }); socket.on("update", (update) { bool ok = update[0]; if (ok) { var data = update[1]; guessersPlayers.value = (data["guessers"] as List? ?? []) .map((e) => e.toString()) .toList(growable: false); suggestersPlayers.value = (data["suggesters"] as List? ?? []) .map((e) => e.toString()) .toList(growable: false); } else { Get.snackbar("Error", "Update failed with message: ${update[1]}"); } }); socket.on("updateNeeded", (data) { socket.emit("getUpdate"); }); socket.on("gameRequest", (requestFrom) { setState(() { incommingRequests.add(requestFrom); }); }); socket.on("requestResponseResult", (data) { bool ok = data[0]; if (ok) { socket.off("hello"); socket.off("update"); socket.off("updateNeeded"); socket.off("gameRequest"); socket.off("requestResponseResult"); Get.put(authData); Get.put(socket); Get.put(Game( data[1]["id"], data[1]["player1"], data[1]["player2"], (data[2] as List).map((e) => e.toString()).toSet(), )); Get.offNamed("/game"); } else { Get.snackbar("Request response", data[1]); } }); socket.emit("getUpdate"); } @override Widget build(BuildContext context) { final shortestSide = MediaQuery.of(context).size.shortestSide; final useMobileLayout = shortestSide < 600; final elements = [ Expanded( child: Padding( padding: const EdgeInsets.all(16), child: _buildPlayerList( guessersPlayers, "Guessers", "guessers", ), ), ), Expanded( child: Padding( padding: const EdgeInsets.all(16), child: _buildPlayerList( suggestersPlayers, "Suggesters", "suggesters", ), ), ), ]; return Scaffold( appBar: AppBar( title: Row( mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text("HUACU"), SocketConnectionIndicator(socket: socket, size: 8), ], ), ), body: useMobileLayout ? Column( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: elements, ) : Row( mainAxisAlignment: MainAxisAlignment.start, mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.start, children: elements, ), ); } Widget _buildPlayerList(RxList players, String title, String team) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title), Obx(() => Text("Count: ${players.length}")), Expanded( child: ObxValue( (data) => ListView.builder( itemCount: data.length, itemBuilder: (context, index) { final username = data[index]; final hasRequest = incommingRequests.contains(username); final you = authData.login == username; return Card( child: ListTile( title: Text( "$username ${you ? "(you)" : ""}".trim(), ), trailing: Row( mainAxisSize: MainAxisSize.min, children: [ if (hasRequest) InkResponse( onTap: () => _handleRequestResponseAction(username, true), child: const Icon(Icons.check), ), if (hasRequest) InkResponse( onTap: () => _handleRequestResponseAction(username, false), child: const Icon(Icons.close), ), if (!you && !hasRequest) InkResponse( onTap: () => _handleSendRequestClick(username), child: const Icon(Icons.connect_without_contact), ), ], ), ), ); }, ), players, ), ), Row( mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.center, children: [ Expanded( child: ObxValue( (data) => players.contains(authData.login) ? ElevatedButton( onPressed: () => _handleLeaveClick(team), child: const Text("Leave"), ) : ElevatedButton( onPressed: () => _handleJoinClick(team), child: const Text("Join"), ), guessersPlayers, ), ), ], ), ], ); } void _joinResponseHandler(dynamic data) { bool ok = data[0]; if (!ok) { int status = data[1]; Get.snackbar("Error", "Join failed with status $status"); } socket.off("joinResponse", _joinResponseHandler); } void _handleJoinClick(String side) { socket.on("joinResponse", _joinResponseHandler); socket.emit("join", side); } void _leaveResponseHandler(dynamic data) { bool ok = data[0]; if (!ok) { int status = data[1]; Get.snackbar("Error", "Leaving failed with status $status"); } socket.off("leaveResponse", _leaveResponseHandler); } void _handleLeaveClick(String side) { socket.on("leaveResponse", _leaveResponseHandler); socket.emit("leave", side); } void _sendRequestResponseHandler(dynamic data) { bool ok = data[0]; if (ok) { Get.snackbar("Success", "Request sent"); } else { Get.snackbar("Error", "Request failed"); } socket.off("sendRequestResponse", _leaveResponseHandler); } void _handleSendRequestClick(String player) { socket.on("sendRequestResponse", _sendRequestResponseHandler); socket.emit("sendRequest", player); } void _handleRequestResponseAction(String data, bool response) { socket.emit("requestResponse", [data, response]); setState(() { incommingRequests.remove(data); }); } }