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.
This commit is contained in:
Marco 2024-01-17 22:50:02 +01:00
parent 7d55a0e123
commit 13bcfb1131
6 changed files with 103 additions and 16 deletions

View File

@ -4,7 +4,8 @@ enum MessageType {
boardState, boardState,
move, move,
invalidMove, invalidMove,
colorDetermined; colorDetermined,
gameEnded;
String toJson() => name; String toJson() => name;
static MessageType fromJson(String json) => values.byName(json); static MessageType fromJson(String json) => values.byName(json);
@ -82,6 +83,16 @@ class ApiWebsocketMessage {
squareInCheck: json['squareInCheck'], squareInCheck: json['squareInCheck'],
playerColor: null, playerColor: null,
); );
case MessageType.gameEnded:
ret = ApiWebsocketMessage(
type: type,
move: null,
turnColor: null,
reason: json['reason'],
position: null,
squareInCheck: null,
playerColor: null,
);
} }
return ret; return ret;
} }

View File

@ -1,5 +1,7 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:developer'; 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/move.dart';
import 'package:mchess/api/websocket_message.dart'; import 'package:mchess/api/websocket_message.dart';
import 'package:mchess/chess_bloc/chess_bloc.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/api/register.dart';
import 'package:mchess/chess_bloc/chess_position.dart'; import 'package:mchess/chess_bloc/chess_position.dart';
import 'package:mchess/connection_cubit/connection_cubit.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/chess_utils.dart';
import 'package:mchess/utils/config.dart' as config;
import 'package:web_socket_channel/web_socket_channel.dart'; import 'package:web_socket_channel/web_socket_channel.dart';
class ServerConnection { class ServerConnection {
@ -38,9 +42,7 @@ class ServerConnection {
} }
void connect(String playerID, lobbyID, String? passphrase) { void connect(String playerID, lobbyID, String? passphrase) {
String url; channel = WebSocketChannel.connect(Uri.parse(config.getWebsocketURL()));
url = 'wss://chess.sw-gross.de:9999/api/ws';
channel = WebSocketChannel.connect(Uri.parse(url));
send( send(
jsonEncode( jsonEncode(
@ -82,6 +84,8 @@ class ServerConnection {
case MessageType.invalidMove: case MessageType.invalidMove:
handleInvalidMoveMessage(apiMessage); 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: <Widget>[
TextButton(
child: const Text('Home'),
onPressed: () {
navigatorKey.currentContext!.goNamed('lobbySelector');
},
),
]);
},
);
}
} }

View File

@ -1,6 +1,7 @@
import 'dart:developer'; import 'dart:developer';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:http/http.dart'; import 'package:http/http.dart';
@ -10,6 +11,7 @@ import 'package:http/http.dart' as http;
import 'dart:convert'; import 'dart:convert';
import 'package:mchess/pages/chess_game.dart'; import 'package:mchess/pages/chess_game.dart';
import 'package:mchess/utils/config.dart' as config;
class HostGameWidget extends StatefulWidget { class HostGameWidget extends StatefulWidget {
const HostGameWidget({super.key}); const HostGameWidget({super.key});
@ -80,9 +82,21 @@ class _HostGameWidgetState extends State<HostGameWidget> {
color: Theme.of(context).colorScheme.primary), color: Theme.of(context).colorScheme.primary),
), ),
const SizedBox(height: 25), const SizedBox(height: 25),
SelectableText( Row(
passphrase, mainAxisAlignment: MainAxisAlignment.center,
style: const TextStyle(fontWeight: FontWeight.bold), 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 SizedBox(height: 25),
const CircularProgressIndicator() const CircularProgressIndicator()
@ -97,14 +111,11 @@ class _HostGameWidgetState extends State<HostGameWidget> {
} }
Future<PlayerInfo?> hostPrivateGame() async { Future<PlayerInfo?> hostPrivateGame() async {
String addr;
Response response; Response response;
addr = 'https://chess.sw-gross.de:9999/api/hostPrivate';
try { try {
response = await http response = await http.get(Uri.parse(config.getHostURL()),
.get(Uri.parse(addr), headers: {"Accept": "application/json"}); headers: {"Accept": "application/json"});
} catch (e) { } catch (e) {
log(e.toString()); log(e.toString());

View File

@ -7,6 +7,7 @@ import 'package:http/http.dart' as http;
import 'package:mchess/api/register.dart'; import 'package:mchess/api/register.dart';
import 'package:mchess/connection_cubit/connection_cubit.dart'; import 'package:mchess/connection_cubit/connection_cubit.dart';
import 'package:mchess/pages/chess_game.dart'; import 'package:mchess/pages/chess_game.dart';
import 'package:mchess/utils/config.dart' as config;
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
class LobbySelector extends StatefulWidget { class LobbySelector extends StatefulWidget {
@ -149,17 +150,14 @@ class _LobbySelectorState extends State<LobbySelector> {
} }
Future<PlayerInfo?> joinPrivateGame() async { Future<PlayerInfo?> joinPrivateGame() async {
String addr;
http.Response response; http.Response response;
// server expects us to send the passphrase // server expects us to send the passphrase
var info = PlayerInfo( var info = PlayerInfo(
playerID: null, lobbyID: null, passphrase: phraseController.text); playerID: null, lobbyID: null, passphrase: phraseController.text);
addr = 'https://chess.sw-gross.de:9999/api/joinPrivate';
try { try {
response = await http.post(Uri.parse(addr), response = await http.post(Uri.parse(config.getJoinURL()),
body: jsonEncode(info), headers: {"Accept": "application/json"}); body: jsonEncode(info), headers: {"Accept": "application/json"});
} catch (e) { } catch (e) {
log(e.toString()); log(e.toString());

View File

@ -1,8 +1,11 @@
import 'package:flutter/widgets.dart';
import 'package:go_router/go_router.dart'; import 'package:go_router/go_router.dart';
import 'package:mchess/pages/chess_game.dart'; import 'package:mchess/pages/chess_game.dart';
import 'package:mchess/pages/lobby_selector.dart'; import 'package:mchess/pages/lobby_selector.dart';
import 'package:mchess/pages/host_game.dart'; import 'package:mchess/pages/host_game.dart';
final navigatorKey = GlobalKey<NavigatorState>();
class ChessAppRouter { class ChessAppRouter {
static final ChessAppRouter _instance = ChessAppRouter._internal(); static final ChessAppRouter _instance = ChessAppRouter._internal();
@ -13,6 +16,7 @@ class ChessAppRouter {
} }
final router = GoRouter( final router = GoRouter(
navigatorKey: navigatorKey,
debugLogDiagnostics: true, debugLogDiagnostics: true,
routes: [ routes: [
GoRoute( GoRoute(

34
lib/utils/config.dart Normal file
View File

@ -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';
}