Changes you see? To many to name them. And a splash screen for flutter web of course.
@ -2,6 +2,7 @@ import 'package:mchess/api/move.dart';
|
||||
|
||||
enum MessageType {
|
||||
move,
|
||||
invalidMove,
|
||||
colorDetermined;
|
||||
|
||||
String toJson() => name;
|
||||
@ -20,9 +21,13 @@ class ApiWebsocketMessage {
|
||||
final MessageType type;
|
||||
final ApiMove? move;
|
||||
final ApiColor? color;
|
||||
final String? reason;
|
||||
|
||||
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) {
|
||||
final type = MessageType.fromJson(json['messageType']);
|
||||
@ -30,11 +35,25 @@ class ApiWebsocketMessage {
|
||||
switch (type) {
|
||||
case MessageType.colorDetermined:
|
||||
ret = ApiWebsocketMessage(
|
||||
type: type, move: null, color: ApiColor.fromJson(json['color']));
|
||||
type: type,
|
||||
move: null,
|
||||
color: ApiColor.fromJson(json['color']),
|
||||
reason: null);
|
||||
break;
|
||||
case MessageType.move:
|
||||
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;
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ class ChessApp extends StatelessWidget {
|
||||
useMaterial3: true,
|
||||
),
|
||||
routerConfig: ChessAppRouter.getInstance().router,
|
||||
title: 'mChess v0.1.1337',
|
||||
title: 'mChess',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -21,8 +21,9 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||
ChessBloc._internal() : super(ChessBoardState.init()) {
|
||||
on<InitBoard>(initBoard);
|
||||
on<ColorDetermined>(flipBoard);
|
||||
on<OpponentPieceMoved>(opponentMoveHandler);
|
||||
on<ReceivedMove>(moveHandler);
|
||||
on<OwnPieceMoved>(ownMoveHandler);
|
||||
on<InvalidMovePlayed>(invalidMoveHandler);
|
||||
}
|
||||
|
||||
factory ChessBloc.getInstance() {
|
||||
@ -45,8 +46,7 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||
emit(ChessBoardState(event.myColor, state.newTurnColor, state.position));
|
||||
}
|
||||
|
||||
void opponentMoveHandler(
|
||||
OpponentPieceMoved event, Emitter<ChessBoardState> emit) {
|
||||
void moveHandler(ReceivedMove event, Emitter<ChessBoardState> emit) {
|
||||
log('opponentMoveHandler()');
|
||||
ChessPosition.getInstance().recordMove(event.startSquare, event.endSquare);
|
||||
var newPosition = ChessPosition.getInstance().currentPosition;
|
||||
@ -66,32 +66,37 @@ class ChessBloc extends Bloc<ChessEvent, ChessBoardState> {
|
||||
|
||||
void ownMoveHandler(OwnPieceMoved event, Emitter<ChessBoardState> emit) {
|
||||
log('ownMoveHandler()');
|
||||
ChessPosition.getInstance().recordMove(event.startSquare, event.endSquare);
|
||||
|
||||
var start = ApiCoordinate(
|
||||
col: event.startSquare.column, row: event.startSquare.row);
|
||||
var end =
|
||||
ApiCoordinate(col: event.endSquare.column, row: event.endSquare.row);
|
||||
var move = ApiMove(startSquare: start, endSquare: end);
|
||||
var message =
|
||||
ApiWebsocketMessage(type: MessageType.move, move: move, color: null);
|
||||
var apiMove = ApiMove(startSquare: start, endSquare: end);
|
||||
var apiMessage = ApiWebsocketMessage(
|
||||
type: MessageType.move, move: apiMove, color: null, reason: null);
|
||||
|
||||
ServerConnection.getInstance().send(jsonEncode(message));
|
||||
ServerConnection.getInstance().send(jsonEncode(apiMessage));
|
||||
|
||||
turnColor = state.newTurnColor == ChessColor.white
|
||||
? ChessColor.black
|
||||
: ChessColor.white;
|
||||
|
||||
var newPosition = ChessPosition.getInstance().currentPosition;
|
||||
//Temporary chess position until server responds with acknoledgement
|
||||
var move = ChessMove.fromApiMove(apiMove);
|
||||
var tempPosition = ChessPosition.getInstance().copyOfCurrentPosition;
|
||||
tempPosition[move.to] = tempPosition[move.from] ?? const ChessPiece.none();
|
||||
tempPosition[move.from] = const ChessPiece.none();
|
||||
|
||||
emit(
|
||||
ChessBoardState(
|
||||
state.bottomColor,
|
||||
turnColor,
|
||||
newPosition,
|
||||
tempPosition,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void invalidMoveHandler(
|
||||
InvalidMovePlayed event, Emitter<ChessBoardState> emit) {
|
||||
emit(ChessBoardState(state.bottomColor, turnColor,
|
||||
ChessPosition.getInstance().currentPosition));
|
||||
}
|
||||
}
|
||||
|
||||
class ChessBoardState {
|
||||
|
@ -2,11 +2,11 @@ import 'package:mchess/utils/chess_utils.dart';
|
||||
|
||||
abstract class ChessEvent {}
|
||||
|
||||
class OpponentPieceMoved extends ChessEvent {
|
||||
class ReceivedMove extends ChessEvent {
|
||||
final ChessCoordinate startSquare;
|
||||
final ChessCoordinate endSquare;
|
||||
|
||||
OpponentPieceMoved({required this.startSquare, required this.endSquare});
|
||||
ReceivedMove({required this.startSquare, required this.endSquare});
|
||||
}
|
||||
|
||||
class OwnPieceMoved extends ChessEvent {
|
||||
@ -25,3 +25,9 @@ class ColorDetermined extends ChessEvent {
|
||||
|
||||
ColorDetermined({required this.myColor});
|
||||
}
|
||||
|
||||
class InvalidMovePlayed extends ChessEvent {
|
||||
final ChessMove move;
|
||||
|
||||
InvalidMovePlayed({required this.move});
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ class ChessPosition {
|
||||
}
|
||||
|
||||
ChessPositionType get currentPosition => position;
|
||||
ChessPositionType get copyOfCurrentPosition => Map.from(position);
|
||||
ChessMove? get lastMove {
|
||||
if (history.isEmpty) return null;
|
||||
return history.last;
|
||||
@ -103,7 +104,7 @@ class ChessPosition {
|
||||
logString = '$logString\n';
|
||||
}
|
||||
|
||||
log(logString);
|
||||
print(logString);
|
||||
}
|
||||
|
||||
void logHistory(ChessMoveHistory hist) {
|
||||
|
@ -69,6 +69,9 @@ class ServerConnection {
|
||||
case MessageType.move:
|
||||
handleIncomingMoveMessage(apiMessage);
|
||||
break;
|
||||
|
||||
case MessageType.invalidMove:
|
||||
handleInvalidMoveMessage(apiMessage);
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,11 +82,13 @@ class ServerConnection {
|
||||
|
||||
void handleIncomingMoveMessage(ApiWebsocketMessage apiMessage) {
|
||||
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()
|
||||
.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!)));
|
||||
}
|
||||
}
|
||||
|
18
pubspec.lock
@ -144,14 +144,6 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.2"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.7"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -341,6 +333,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
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:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -358,5 +358,5 @@ packages:
|
||||
source: hosted
|
||||
version: "6.3.0"
|
||||
sdks:
|
||||
dart: ">=3.0.0 <4.0.0"
|
||||
dart: ">=3.1.0-185.0.dev <4.0.0"
|
||||
flutter: ">=3.7.0-0"
|
||||
|
Before Width: | Height: | Size: 5.2 KiB |
@ -24,7 +24,7 @@
|
||||
<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-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 -->
|
||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||
@ -40,20 +40,85 @@
|
||||
<script src="flutter.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
window.addEventListener('load', function(ev) {
|
||||
// Download main.dart.js
|
||||
</script>
|
||||
|
||||
<!-- 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({
|
||||
serviceWorker: {
|
||||
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>
|
||||
</html>
|
||||
|
@ -10,23 +10,23 @@
|
||||
"prefer_related_applications": false,
|
||||
"icons": [
|
||||
{
|
||||
"src": "icons/Icon-192.png",
|
||||
"src": "web_icons/Icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/Icon-512.png",
|
||||
"src": "web_icons/Icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "icons/Icon-maskable-192.png",
|
||||
"src": "web_icons/Icon-maskable-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
},
|
||||
{
|
||||
"src": "icons/Icon-maskable-512.png",
|
||||
"src": "web_icons/Icon-maskable-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png",
|
||||
"purpose": "maskable"
|
||||
|
BIN
web/web_icons/Icon-192.png
Normal file
After Width: | Height: | Size: 7.3 KiB |
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 8.1 KiB |
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |