Changes you see? To many to name them. And a splash screen for flutter web of course.

This commit is contained in:
Marco 2023-06-28 12:37:59 +02:00
parent e27f1c7c22
commit 43fca47dae
14 changed files with 157 additions and 56 deletions

View File

@ -2,6 +2,7 @@ import 'package:mchess/api/move.dart';
enum MessageType { enum MessageType {
move, move,
invalidMove,
colorDetermined; colorDetermined;
String toJson() => name; String toJson() => name;
@ -20,9 +21,13 @@ class ApiWebsocketMessage {
final MessageType type; final MessageType type;
final ApiMove? move; final ApiMove? move;
final ApiColor? color; final ApiColor? color;
final String? reason;
ApiWebsocketMessage( ApiWebsocketMessage(
{required this.type, required this.move, required this.color}); {required this.type,
required this.move,
required this.color,
required this.reason});
factory ApiWebsocketMessage.fromJson(Map<String, dynamic> json) { factory ApiWebsocketMessage.fromJson(Map<String, dynamic> json) {
final type = MessageType.fromJson(json['messageType']); final type = MessageType.fromJson(json['messageType']);
@ -30,11 +35,25 @@ class ApiWebsocketMessage {
switch (type) { switch (type) {
case MessageType.colorDetermined: case MessageType.colorDetermined:
ret = ApiWebsocketMessage( ret = ApiWebsocketMessage(
type: type, move: null, color: ApiColor.fromJson(json['color'])); type: type,
move: null,
color: ApiColor.fromJson(json['color']),
reason: null);
break; break;
case MessageType.move: case MessageType.move:
ret = ApiWebsocketMessage( ret = ApiWebsocketMessage(
type: type, move: ApiMove.fromJson(json['move']), color: null); type: type,
move: ApiMove.fromJson(json['move']),
color: null,
reason: null);
break;
case MessageType.invalidMove:
ret = ApiWebsocketMessage(
type: type,
move: ApiMove.fromJson(json['move']),
color: null,
reason: json['reason'],
);
} }
return ret; return ret;
} }

View File

@ -18,7 +18,7 @@ class ChessApp extends StatelessWidget {
useMaterial3: true, useMaterial3: true,
), ),
routerConfig: ChessAppRouter.getInstance().router, routerConfig: ChessAppRouter.getInstance().router,
title: 'mChess v0.1.1337', title: 'mChess',
), ),
), ),
); );

View File

@ -21,8 +21,9 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
ChessBloc._internal() : super(ChessBoardState.init()) { ChessBloc._internal() : super(ChessBoardState.init()) {
on<InitBoard>(initBoard); on<InitBoard>(initBoard);
on<ColorDetermined>(flipBoard); on<ColorDetermined>(flipBoard);
on<OpponentPieceMoved>(opponentMoveHandler); on<ReceivedMove>(moveHandler);
on<OwnPieceMoved>(ownMoveHandler); on<OwnPieceMoved>(ownMoveHandler);
on<InvalidMovePlayed>(invalidMoveHandler);
} }
factory ChessBloc.getInstance() { factory ChessBloc.getInstance() {
@ -45,8 +46,7 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
emit(ChessBoardState(event.myColor, state.newTurnColor, state.position)); emit(ChessBoardState(event.myColor, state.newTurnColor, state.position));
} }
void opponentMoveHandler( void moveHandler(ReceivedMove event, Emitter<ChessBoardState> emit) {
OpponentPieceMoved event, Emitter<ChessBoardState> emit) {
log('opponentMoveHandler()'); log('opponentMoveHandler()');
ChessPosition.getInstance().recordMove(event.startSquare, event.endSquare); ChessPosition.getInstance().recordMove(event.startSquare, event.endSquare);
var newPosition = ChessPosition.getInstance().currentPosition; var newPosition = ChessPosition.getInstance().currentPosition;
@ -66,32 +66,37 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
void ownMoveHandler(OwnPieceMoved event, Emitter<ChessBoardState> emit) { void ownMoveHandler(OwnPieceMoved event, Emitter<ChessBoardState> emit) {
log('ownMoveHandler()'); log('ownMoveHandler()');
ChessPosition.getInstance().recordMove(event.startSquare, event.endSquare);
var start = ApiCoordinate( var start = ApiCoordinate(
col: event.startSquare.column, row: event.startSquare.row); col: event.startSquare.column, row: event.startSquare.row);
var end = var end =
ApiCoordinate(col: event.endSquare.column, row: event.endSquare.row); ApiCoordinate(col: event.endSquare.column, row: event.endSquare.row);
var move = ApiMove(startSquare: start, endSquare: end); var apiMove = ApiMove(startSquare: start, endSquare: end);
var message = var apiMessage = ApiWebsocketMessage(
ApiWebsocketMessage(type: MessageType.move, move: move, color: null); type: MessageType.move, move: apiMove, color: null, reason: null);
ServerConnection.getInstance().send(jsonEncode(message)); ServerConnection.getInstance().send(jsonEncode(apiMessage));
turnColor = state.newTurnColor == ChessColor.white //Temporary chess position until server responds with acknoledgement
? ChessColor.black var move = ChessMove.fromApiMove(apiMove);
: ChessColor.white; var tempPosition = ChessPosition.getInstance().copyOfCurrentPosition;
tempPosition[move.to] = tempPosition[move.from] ?? const ChessPiece.none();
var newPosition = ChessPosition.getInstance().currentPosition; tempPosition[move.from] = const ChessPiece.none();
emit( emit(
ChessBoardState( ChessBoardState(
state.bottomColor, state.bottomColor,
turnColor, turnColor,
newPosition, tempPosition,
), ),
); );
} }
void invalidMoveHandler(
InvalidMovePlayed event, Emitter<ChessBoardState> emit) {
emit(ChessBoardState(state.bottomColor, turnColor,
ChessPosition.getInstance().currentPosition));
}
} }
class ChessBoardState { class ChessBoardState {

View File

@ -2,11 +2,11 @@ import 'package:mchess/utils/chess_utils.dart';
abstract class ChessEvent {} abstract class ChessEvent {}
class OpponentPieceMoved extends ChessEvent { class ReceivedMove extends ChessEvent {
final ChessCoordinate startSquare; final ChessCoordinate startSquare;
final ChessCoordinate endSquare; final ChessCoordinate endSquare;
OpponentPieceMoved({required this.startSquare, required this.endSquare}); ReceivedMove({required this.startSquare, required this.endSquare});
} }
class OwnPieceMoved extends ChessEvent { class OwnPieceMoved extends ChessEvent {
@ -25,3 +25,9 @@ class ColorDetermined extends ChessEvent {
ColorDetermined({required this.myColor}); ColorDetermined({required this.myColor});
} }
class InvalidMovePlayed extends ChessEvent {
final ChessMove move;
InvalidMovePlayed({required this.move});
}

View File

@ -20,6 +20,7 @@ class ChessPosition {
} }
ChessPositionType get currentPosition => position; ChessPositionType get currentPosition => position;
ChessPositionType get copyOfCurrentPosition => Map.from(position);
ChessMove? get lastMove { ChessMove? get lastMove {
if (history.isEmpty) return null; if (history.isEmpty) return null;
return history.last; return history.last;
@ -103,7 +104,7 @@ class ChessPosition {
logString = '$logString\n'; logString = '$logString\n';
} }
log(logString); print(logString);
} }
void logHistory(ChessMoveHistory hist) { void logHistory(ChessMoveHistory hist) {

View File

@ -69,6 +69,9 @@ class ServerConnection {
case MessageType.move: case MessageType.move:
handleIncomingMoveMessage(apiMessage); handleIncomingMoveMessage(apiMessage);
break; break;
case MessageType.invalidMove:
handleInvalidMoveMessage(apiMessage);
} }
} }
@ -79,11 +82,13 @@ class ServerConnection {
void handleIncomingMoveMessage(ApiWebsocketMessage apiMessage) { void handleIncomingMoveMessage(ApiWebsocketMessage apiMessage) {
var move = ChessMove.fromApiMove(apiMessage.move!); var move = ChessMove.fromApiMove(apiMessage.move!);
if (move == ChessPosition.getInstance().lastMove) {
//This is our own move that got resent by the server. Do not process.
} else {
ChessBloc.getInstance() ChessBloc.getInstance()
.add(OpponentPieceMoved(startSquare: move.from, endSquare: move.to)); .add(ReceivedMove(startSquare: move.from, endSquare: move.to));
} }
void handleInvalidMoveMessage(ApiWebsocketMessage apiMessage) {
log("invalid move message received, with move: ${apiMessage.move.toString()}");
ChessBloc.getInstance()
.add(InvalidMovePlayed(move: ChessMove.fromApiMove(apiMessage.move!)));
} }
} }

View File

@ -144,14 +144,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.2" version: "4.0.2"
js:
dependency: transitive
description:
name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
version: "0.6.7"
lints: lints:
dependency: transitive dependency: transitive
description: description:
@ -341,6 +333,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.1.4" version: "2.1.4"
web:
dependency: transitive
description:
name: web
sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10
url: "https://pub.dev"
source: hosted
version: "0.1.4-beta"
web_socket_channel: web_socket_channel:
dependency: "direct main" dependency: "direct main"
description: description:
@ -358,5 +358,5 @@ packages:
source: hosted source: hosted
version: "6.3.0" version: "6.3.0"
sdks: sdks:
dart: ">=3.0.0 <4.0.0" dart: ">=3.1.0-185.0.dev <4.0.0"
flutter: ">=3.7.0-0" flutter: ">=3.7.0-0"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

View File

@ -24,7 +24,7 @@
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="mchess"> <meta name="apple-mobile-web-app-title" content="mchess">
<link rel="apple-touch-icon" href="icons/Icon-192.png"> <link rel="apple-touch-icon" href="web_icons/Icon-192.png">
<!-- Favicon --> <!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/> <link rel="icon" type="image/png" href="favicon.png"/>
@ -40,20 +40,85 @@
<script src="flutter.js" defer></script> <script src="flutter.js" defer></script>
</head> </head>
<body> <body>
<script> </script>
window.addEventListener('load', function(ev) {
// Download main.dart.js <!-- Loading indicator -->
<div id="loading">
<style>
body {
inset: 0;
overflow: hidden;
margin: 0;
padding: 0;
position: fixed;
}
#loading {
align-items: center;
display: flex;
height: 100%;
justify-content: center;
width: 100%;
}
#loading img {
animation: 1s ease-in-out 0s infinite alternate breathe;
opacity: .66;
transition: opacity .4s;
}
#loading.main_done img {
opacity: 1;
}
#loading.init_done img {
animation: .33s ease-in-out 0s 1 forwards zooooom;
opacity: .05;
}
@keyframes breathe {
from {
transform: scale(1)
}
to {
transform: scale(0.95)
}
}
@keyframes zooooom {
from {
transform: scale(1)
}
to {
transform: scale(10)
}
}
</style>
<img src="web_icons/Icon-192.png" alt="Loading indicator..." />
</div>
<script>
window.addEventListener('load', function () {
var loading = document.querySelector('#loading');
_flutter.loader.loadEntrypoint({ _flutter.loader.loadEntrypoint({
serviceWorker: { serviceWorker: {
serviceWorkerVersion: serviceWorkerVersion, serviceWorkerVersion: serviceWorkerVersion,
},
onEntrypointLoaded: function(engineInitializer) {
engineInitializer.initializeEngine().then(function(appRunner) {
appRunner.runApp();
});
} }
}).then(function (engineInitializer) {
loading.classList.add('main_done');
return engineInitializer.initializeEngine();
}).then(function (appRunner) {
loading.classList.add('init_done');
return appRunner.runApp();
}).then(function (app) {
// Wait a few milliseconds so users can see the "zoooom" animation
// before getting rid of the "loading" div.
window.setTimeout(function () {
loading.remove();
}, 200);
}); });
}); });
</script> </script>
</body> </body>
</html> </html>

View File

@ -10,23 +10,23 @@
"prefer_related_applications": false, "prefer_related_applications": false,
"icons": [ "icons": [
{ {
"src": "icons/Icon-192.png", "src": "web_icons/Icon-192.png",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "icons/Icon-512.png", "src": "web_icons/Icon-512.png",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png" "type": "image/png"
}, },
{ {
"src": "icons/Icon-maskable-192.png", "src": "web_icons/Icon-maskable-192.png",
"sizes": "192x192", "sizes": "192x192",
"type": "image/png", "type": "image/png",
"purpose": "maskable" "purpose": "maskable"
}, },
{ {
"src": "icons/Icon-maskable-512.png", "src": "web_icons/Icon-maskable-512.png",
"sizes": "512x512", "sizes": "512x512",
"type": "image/png", "type": "image/png",
"purpose": "maskable" "purpose": "maskable"

BIN
web/web_icons/Icon-192.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

Before

Width:  |  Height:  |  Size: 8.1 KiB

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB