From 13bcfb11310afb73ae486604e5299690bc15f5c0 Mon Sep 17 00:00:00 2001 From: Marco Date: Wed, 17 Jan 2024 22:50:02 +0100 Subject: [PATCH] Introduce checkmate screen Show checkmate screen, once the server sends the 'gameEnded' message. Additionally, show an icon that lets users copy the passphrase to the clipboard. --- lib/api/websocket_message.dart | 13 +++++++++++- lib/connection/ws_connection.dart | 35 ++++++++++++++++++++++++++++--- lib/pages/host_game.dart | 27 +++++++++++++++++------- lib/pages/lobby_selector.dart | 6 ++---- lib/utils/chess_router.dart | 4 ++++ lib/utils/config.dart | 34 ++++++++++++++++++++++++++++++ 6 files changed, 103 insertions(+), 16 deletions(-) create mode 100644 lib/utils/config.dart diff --git a/lib/api/websocket_message.dart b/lib/api/websocket_message.dart index 67cb0b8..edc1498 100644 --- a/lib/api/websocket_message.dart +++ b/lib/api/websocket_message.dart @@ -4,7 +4,8 @@ enum MessageType { boardState, move, invalidMove, - colorDetermined; + colorDetermined, + gameEnded; String toJson() => name; static MessageType fromJson(String json) => values.byName(json); @@ -82,6 +83,16 @@ class ApiWebsocketMessage { squareInCheck: json['squareInCheck'], playerColor: null, ); + case MessageType.gameEnded: + ret = ApiWebsocketMessage( + type: type, + move: null, + turnColor: null, + reason: json['reason'], + position: null, + squareInCheck: null, + playerColor: null, + ); } return ret; } diff --git a/lib/connection/ws_connection.dart b/lib/connection/ws_connection.dart index 8edac83..b001179 100644 --- a/lib/connection/ws_connection.dart +++ b/lib/connection/ws_connection.dart @@ -1,5 +1,7 @@ import 'dart:convert'; import 'dart:developer'; +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; import 'package:mchess/api/move.dart'; import 'package:mchess/api/websocket_message.dart'; import 'package:mchess/chess_bloc/chess_bloc.dart'; @@ -7,7 +9,9 @@ import 'package:mchess/chess_bloc/chess_events.dart'; import 'package:mchess/api/register.dart'; import 'package:mchess/chess_bloc/chess_position.dart'; import 'package:mchess/connection_cubit/connection_cubit.dart'; +import 'package:mchess/utils/chess_router.dart'; import 'package:mchess/utils/chess_utils.dart'; +import 'package:mchess/utils/config.dart' as config; import 'package:web_socket_channel/web_socket_channel.dart'; class ServerConnection { @@ -38,9 +42,7 @@ class ServerConnection { } void connect(String playerID, lobbyID, String? passphrase) { - String url; - url = 'wss://chess.sw-gross.de:9999/api/ws'; - channel = WebSocketChannel.connect(Uri.parse(url)); + channel = WebSocketChannel.connect(Uri.parse(config.getWebsocketURL())); send( jsonEncode( @@ -82,6 +84,8 @@ class ServerConnection { case MessageType.invalidMove: handleInvalidMoveMessage(apiMessage); + case MessageType.gameEnded: + handleGameEndedMessage(apiMessage); } } @@ -125,4 +129,29 @@ class ServerConnection { ), ); } + + void handleGameEndedMessage(ApiWebsocketMessage apiMessage) { + showDialog( + context: navigatorKey.currentContext!, + builder: (context) { + String msg = ''; + if (apiMessage.reason == 'whiteIsCheckmated') { + msg = 'Black won! White is checkmated'; + } else if (apiMessage.reason == 'blackIsCheckmated') { + msg = 'White won! Black is checkmated'; + } + return AlertDialog( + title: const Text('Game ended'), + content: Text(msg), + actions: [ + TextButton( + child: const Text('Home'), + onPressed: () { + navigatorKey.currentContext!.goNamed('lobbySelector'); + }, + ), + ]); + }, + ); + } } diff --git a/lib/pages/host_game.dart b/lib/pages/host_game.dart index 7f36c91..1f59fb8 100644 --- a/lib/pages/host_game.dart +++ b/lib/pages/host_game.dart @@ -1,6 +1,7 @@ import 'dart:developer'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:http/http.dart'; @@ -10,6 +11,7 @@ import 'package:http/http.dart' as http; import 'dart:convert'; import 'package:mchess/pages/chess_game.dart'; +import 'package:mchess/utils/config.dart' as config; class HostGameWidget extends StatefulWidget { const HostGameWidget({super.key}); @@ -80,9 +82,21 @@ class _HostGameWidgetState extends State { color: Theme.of(context).colorScheme.primary), ), const SizedBox(height: 25), - SelectableText( - passphrase, - style: const TextStyle(fontWeight: FontWeight.bold), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + SelectableText( + passphrase, + style: const TextStyle(fontWeight: FontWeight.bold), + ), + IconButton( + icon: const Icon(Icons.copy), + onPressed: () async { + await Clipboard.setData( + ClipboardData(text: passphrase)); + }, + ) + ], ), const SizedBox(height: 25), const CircularProgressIndicator() @@ -97,14 +111,11 @@ class _HostGameWidgetState extends State { } Future hostPrivateGame() async { - String addr; Response response; - addr = 'https://chess.sw-gross.de:9999/api/hostPrivate'; - try { - response = await http - .get(Uri.parse(addr), headers: {"Accept": "application/json"}); + response = await http.get(Uri.parse(config.getHostURL()), + headers: {"Accept": "application/json"}); } catch (e) { log(e.toString()); diff --git a/lib/pages/lobby_selector.dart b/lib/pages/lobby_selector.dart index dd0f887..58393cc 100644 --- a/lib/pages/lobby_selector.dart +++ b/lib/pages/lobby_selector.dart @@ -7,6 +7,7 @@ import 'package:http/http.dart' as http; import 'package:mchess/api/register.dart'; import 'package:mchess/connection_cubit/connection_cubit.dart'; import 'package:mchess/pages/chess_game.dart'; +import 'package:mchess/utils/config.dart' as config; import 'package:shared_preferences/shared_preferences.dart'; class LobbySelector extends StatefulWidget { @@ -149,17 +150,14 @@ class _LobbySelectorState extends State { } Future joinPrivateGame() async { - String addr; http.Response response; // server expects us to send the passphrase var info = PlayerInfo( playerID: null, lobbyID: null, passphrase: phraseController.text); - addr = 'https://chess.sw-gross.de:9999/api/joinPrivate'; - try { - response = await http.post(Uri.parse(addr), + response = await http.post(Uri.parse(config.getJoinURL()), body: jsonEncode(info), headers: {"Accept": "application/json"}); } catch (e) { log(e.toString()); diff --git a/lib/utils/chess_router.dart b/lib/utils/chess_router.dart index 0c3216e..b9f667a 100644 --- a/lib/utils/chess_router.dart +++ b/lib/utils/chess_router.dart @@ -1,8 +1,11 @@ +import 'package:flutter/widgets.dart'; import 'package:go_router/go_router.dart'; import 'package:mchess/pages/chess_game.dart'; import 'package:mchess/pages/lobby_selector.dart'; import 'package:mchess/pages/host_game.dart'; +final navigatorKey = GlobalKey(); + class ChessAppRouter { static final ChessAppRouter _instance = ChessAppRouter._internal(); @@ -13,6 +16,7 @@ class ChessAppRouter { } final router = GoRouter( + navigatorKey: navigatorKey, debugLogDiagnostics: true, routes: [ GoRoute( diff --git a/lib/utils/config.dart b/lib/utils/config.dart new file mode 100644 index 0000000..ca484a0 --- /dev/null +++ b/lib/utils/config.dart @@ -0,0 +1,34 @@ +const prodURL = 'chess.sw-gross.de:9999'; +const debugURL = 'localhost:8080'; + +const dbgUrl = false; + +String getHostURL() { + var prot = 'https'; + var domain = prodURL; + if (dbgUrl) { + prot = 'http'; + domain = debugURL; + } + return '$prot://$domain/api/hostPrivate'; +} + +String getJoinURL() { + var prot = 'https'; + var domain = prodURL; + if (dbgUrl) { + prot = 'http'; + domain = debugURL; + } + return '$prot://$domain/api/joinPrivate'; +} + +String getWebsocketURL() { + var prot = 'wss'; + var domain = prodURL; + if (dbgUrl) { + prot = 'ws'; + domain = debugURL; + } + return '$prot://$domain/api/ws'; +}