Make passphrase entry a dialog instead of a page.
Additionally, we set some groundwork for storing the game data (lobby id, player id, passphrase) in permanent storage in order to reconnect with it later.
This commit is contained in:
parent
4a9047fd67
commit
7a51e71767
3
.gitignore
vendored
3
.gitignore
vendored
@ -42,3 +42,6 @@ app.*.map.json
|
||||
/android/app/debug
|
||||
/android/app/profile
|
||||
/android/app/release
|
||||
|
||||
# Custom ignores
|
||||
deploy-web.sh
|
||||
|
@ -1,3 +1,4 @@
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
class PlayerInfo {
|
||||
@ -25,6 +26,42 @@ class PlayerInfo {
|
||||
'lobbyID': lobbyID,
|
||||
'passphrase': passphrase,
|
||||
};
|
||||
|
||||
void store() async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
await prefs.setBool("contains", true);
|
||||
await prefs.setString("playerID", playerID.toString());
|
||||
await prefs.setString("lobbyID", lobbyID.toString());
|
||||
await prefs.setString("passphrase", passphrase.toString());
|
||||
}
|
||||
|
||||
void delete() async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
|
||||
await prefs.setBool("contains", false);
|
||||
}
|
||||
|
||||
Future<PlayerInfo?> get() async {
|
||||
final SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||
var contains = prefs.getBool("contains");
|
||||
var playerID = prefs.getString("playerID");
|
||||
var lobbyID = prefs.getString("lobbyID");
|
||||
var passphrase = prefs.getString("passphrase");
|
||||
|
||||
if (contains == null ||
|
||||
!contains ||
|
||||
playerID == null ||
|
||||
lobbyID == null ||
|
||||
passphrase == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return PlayerInfo(
|
||||
playerID: UuidValue.fromString(playerID),
|
||||
lobbyID: UuidValue.fromString(lobbyID),
|
||||
passphrase: passphrase);
|
||||
}
|
||||
}
|
||||
|
||||
class WebsocketMessageIdentifyPlayer {
|
||||
|
@ -85,4 +85,18 @@ class ChessGameArguments {
|
||||
required this.playerID,
|
||||
required this.passphrase,
|
||||
});
|
||||
|
||||
bool isValid() {
|
||||
try {
|
||||
lobbyID.validate();
|
||||
playerID.validate();
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (passphrase == null) return false;
|
||||
if (passphrase!.isEmpty) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,9 @@ class _HostGameWidgetState extends State<HostGameWidget> {
|
||||
@override
|
||||
void initState() {
|
||||
registerResponse = hostPrivateGame();
|
||||
registerResponse.then((value) {
|
||||
value?.store();
|
||||
});
|
||||
connectToWebsocket(registerResponse);
|
||||
|
||||
super.initState();
|
||||
|
@ -1,118 +0,0 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:http/http.dart';
|
||||
import 'package:mchess/api/register.dart';
|
||||
import 'package:mchess/connection_cubit/connection_cubit.dart';
|
||||
import 'package:mchess/pages/chess_game.dart';
|
||||
|
||||
class JoinGameWidget extends StatefulWidget {
|
||||
const JoinGameWidget({
|
||||
super.key,
|
||||
});
|
||||
|
||||
@override
|
||||
State<JoinGameWidget> createState() => _JoinGameWidgetState();
|
||||
}
|
||||
|
||||
class _JoinGameWidgetState extends State<JoinGameWidget> {
|
||||
final myController = TextEditingController();
|
||||
late Future<PlayerInfo?> joinGameFuture;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: TextField(
|
||||
controller: myController,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Enter passphrase here',
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
joinGameFuture = joinPrivateGame();
|
||||
switchToGame(joinGameFuture);
|
||||
},
|
||||
icon: const Icon(Icons.check),
|
||||
)),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: () {
|
||||
context.push('/');
|
||||
},
|
||||
child: const Icon(Icons.cancel),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void switchToGame(Future<PlayerInfo?> resp) {
|
||||
resp.then((value) {
|
||||
if (value == null) return;
|
||||
|
||||
var chessGameArgs = ChessGameArguments(
|
||||
lobbyID: value.lobbyID!,
|
||||
playerID: value.playerID!,
|
||||
passphrase: value.passphrase);
|
||||
|
||||
ConnectionCubit.getInstance().connect(
|
||||
value.playerID!.uuid,
|
||||
value.lobbyID!.uuid,
|
||||
value.passphrase,
|
||||
);
|
||||
|
||||
context.push('/game', extra: chessGameArgs);
|
||||
});
|
||||
}
|
||||
|
||||
Future<PlayerInfo?> joinPrivateGame() async {
|
||||
String addr;
|
||||
Response response;
|
||||
|
||||
// server expects us to send the passphrase
|
||||
var info = PlayerInfo(
|
||||
playerID: null, lobbyID: null, passphrase: myController.text);
|
||||
|
||||
if (kDebugMode) {
|
||||
addr = 'http://localhost:8080/api/joinPrivate';
|
||||
} else {
|
||||
addr = 'https://chess.sw-gross.de:9999/api/joinPrivate';
|
||||
}
|
||||
|
||||
try {
|
||||
response = await http.post(Uri.parse(addr),
|
||||
body: jsonEncode(info), headers: {"Accept": "application/json"});
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
|
||||
if (!context.mounted) return null;
|
||||
|
||||
const snackBar = SnackBar(
|
||||
backgroundColor: Colors.amberAccent,
|
||||
content: Text("mChess server is not responding. Try again or give up"),
|
||||
);
|
||||
Future.delayed(const Duration(seconds: 2), () {
|
||||
ScaffoldMessenger.of(context).clearSnackBars();
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
context.push('/'); // We go back to lobby selector
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var info = PlayerInfo.fromJson(jsonDecode(response.body));
|
||||
log('Player info received from server: ');
|
||||
log('lobbyID: ${info.lobbyID}');
|
||||
log('playerID: ${info.playerID}');
|
||||
log('passphrase: ${info.passphrase}');
|
||||
return info;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -1,13 +1,36 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
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:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class LobbySelector extends StatelessWidget {
|
||||
class LobbySelector extends StatefulWidget {
|
||||
const LobbySelector({super.key});
|
||||
|
||||
@override
|
||||
State<LobbySelector> createState() => _LobbySelectorState();
|
||||
}
|
||||
|
||||
class _LobbySelectorState extends State<LobbySelector> {
|
||||
final buttonStyle = const ButtonStyle();
|
||||
final myController = TextEditingController();
|
||||
late Future<PlayerInfo?> joinGameFuture;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
SharedPreferences.getInstance().then((prefs) {
|
||||
final playerID = prefs.getString("playerID");
|
||||
final lobbyID = prefs.getString("lobbyID");
|
||||
final passphrase = prefs.getString("passphrase");
|
||||
log("lobbyID: $lobbyID and playerID: $playerID and passphrase: $passphrase");
|
||||
});
|
||||
|
||||
return Scaffold(
|
||||
body: Center(
|
||||
child: Column(
|
||||
@ -15,7 +38,7 @@ class LobbySelector extends StatelessWidget {
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
_dialogBuilder(context);
|
||||
buildJoinOrHostDialog(context);
|
||||
},
|
||||
child: const Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@ -34,23 +57,65 @@ class LobbySelector extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _dialogBuilder(BuildContext context) {
|
||||
Future<void> buildJoinOrHostDialog(BuildContext context) {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return Scaffold(
|
||||
body: AlertDialog(
|
||||
title: const Text('Host or join?'),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: const Text('Cancel'),
|
||||
onPressed: () {
|
||||
context.pop();
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: const Text('Host'),
|
||||
onPressed: () {
|
||||
context.push('/host');
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: const Text('Join'),
|
||||
onPressed: () {
|
||||
buildEnterPassphraseDialog(context);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> buildEnterPassphraseDialog(BuildContext context) {
|
||||
return showDialog<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Host or join?'),
|
||||
title: const Text('Enter the passphrase here:'),
|
||||
content: TextField(
|
||||
controller: myController,
|
||||
decoration: InputDecoration(
|
||||
hintText: 'Enter passphrase here',
|
||||
suffixIcon: IconButton(
|
||||
onPressed: () {
|
||||
joinGameFuture = joinPrivateGame();
|
||||
joinGameFuture.then((value) {
|
||||
if (value == null) return;
|
||||
switchToGame(value);
|
||||
});
|
||||
},
|
||||
icon: const Icon(Icons.check),
|
||||
)),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: const Text('Host'),
|
||||
child: const Text('Cancel'),
|
||||
onPressed: () {
|
||||
context.push('/host');
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: const Text('Join'),
|
||||
onPressed: () {
|
||||
context.push('/join');
|
||||
context.pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
@ -58,4 +123,73 @@ class LobbySelector extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void switchToGame(PlayerInfo info) {
|
||||
var chessGameArgs = ChessGameArguments(
|
||||
lobbyID: info.lobbyID!,
|
||||
playerID: info.playerID!,
|
||||
passphrase: info.passphrase);
|
||||
|
||||
ConnectionCubit.getInstance().connect(
|
||||
info.playerID!.uuid,
|
||||
info.lobbyID!.uuid,
|
||||
info.passphrase,
|
||||
);
|
||||
|
||||
if (!chessGameArgs.isValid()) {
|
||||
context.push('/');
|
||||
const snackBar = SnackBar(
|
||||
backgroundColor: Colors.amberAccent,
|
||||
content: Text("Game information is corrupted"),
|
||||
);
|
||||
ScaffoldMessenger.of(context).clearSnackBars();
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
}
|
||||
|
||||
context.push('/game', extra: chessGameArgs);
|
||||
}
|
||||
|
||||
Future<PlayerInfo?> joinPrivateGame() async {
|
||||
String addr;
|
||||
http.Response response;
|
||||
|
||||
// server expects us to send the passphrase
|
||||
var info = PlayerInfo(
|
||||
playerID: null, lobbyID: null, passphrase: myController.text);
|
||||
|
||||
if (kDebugMode) {
|
||||
addr = 'http://localhost:8080/api/joinPrivate';
|
||||
} else {
|
||||
addr = 'https://chess.sw-gross.de:9999/api/joinPrivate';
|
||||
}
|
||||
|
||||
try {
|
||||
response = await http.post(Uri.parse(addr),
|
||||
body: jsonEncode(info), headers: {"Accept": "application/json"});
|
||||
} catch (e) {
|
||||
log(e.toString());
|
||||
|
||||
if (!context.mounted) return null;
|
||||
|
||||
const snackBar = SnackBar(
|
||||
backgroundColor: Colors.amberAccent,
|
||||
content: Text("mChess server is not responding. Try again or give up"),
|
||||
);
|
||||
ScaffoldMessenger.of(context).clearSnackBars();
|
||||
ScaffoldMessenger.of(context).showSnackBar(snackBar);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var info = PlayerInfo.fromJson(jsonDecode(response.body));
|
||||
log('Player info received from server: ');
|
||||
log('lobbyID: ${info.lobbyID}');
|
||||
log('playerID: ${info.playerID}');
|
||||
log('passphrase: ${info.passphrase}');
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:mchess/pages/chess_game.dart';
|
||||
import 'package:mchess/pages/join_game.dart';
|
||||
import 'package:mchess/pages/lobby_selector.dart';
|
||||
import 'package:mchess/pages/host_game.dart';
|
||||
import 'package:mchess/pages/prepare_random_game.dart';
|
||||
@ -35,12 +34,6 @@ class ChessAppRouter {
|
||||
builder: (context, state) {
|
||||
return const HostGameWidget();
|
||||
}),
|
||||
GoRoute(
|
||||
path: '/join',
|
||||
name: 'join',
|
||||
builder: (context, state) {
|
||||
return const JoinGameWidget();
|
||||
}),
|
||||
GoRoute(
|
||||
path: '/game',
|
||||
name: 'game',
|
||||
|
@ -5,6 +5,8 @@
|
||||
import FlutterMacOS
|
||||
import Foundation
|
||||
|
||||
import shared_preferences_foundation
|
||||
|
||||
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||
}
|
||||
|
134
pubspec.lock
134
pubspec.lock
@ -81,6 +81,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.1"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "7bf0adc28a23d395f19f3f1eb21dd7cfd1dd9f8e1c50051c069122e6853bc878"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.0"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
@ -208,6 +224,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.1"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "94b1e0dd80970c1ce43d5d4e050a9918fce4f4a775e6142424c30a29a363265c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -216,6 +256,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.2"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "0a279f0707af40c890e80b1e9df8bb761694c074ba7e1d4ab1bc4b728e200b59"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: f4f88d4a900933e7267e2b353594774fc0d07fb072b47eedcd5b54e1ea3269f8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.7"
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -232,6 +288,62 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.1"
|
||||
shared_preferences:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.4"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: "7b15ffb9387ea3e237bb7a66b8a23d2147663d391cafc5c8f37b2e7b4bde5d21"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
@ -305,10 +417,10 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: uuid
|
||||
sha256: df5a4d8f22ee4ccd77f8839ac7cb274ebc11ef9adcce8b92be14b797fe889921
|
||||
sha256: "22c94e5ad1e75f9934b766b53c742572ee2677c56bc871d850a57dad0f82127f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.1"
|
||||
version: "4.2.2"
|
||||
vector_graphics:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -357,6 +469,22 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: b0f37db61ba2f2e9b7a78a1caece0052564d1bc70668156cf3a29d676fe4e574
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "589ada45ba9e39405c198fe34eb0f607cddb2108527e658136120892beac46d2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.3"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -367,4 +495,4 @@ packages:
|
||||
version: "6.5.0"
|
||||
sdks:
|
||||
dart: ">=3.2.0 <4.0.0"
|
||||
flutter: ">=3.10.0"
|
||||
flutter: ">=3.16.0"
|
||||
|
@ -44,6 +44,7 @@ dependencies:
|
||||
go_router: ^12.0.0
|
||||
http: ^1.0.0
|
||||
uuid: ^4.0.0
|
||||
shared_preferences: ^2.2.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Loading…
Reference in New Issue
Block a user