Frontend WIP - just websocket support left
This commit is contained in:
@@ -0,0 +1,42 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:frontend/providers/auth.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
final logger = Logger('GameRoomHome');
|
||||
|
||||
class GameRoomHome extends ConsumerStatefulWidget {
|
||||
const GameRoomHome({super.key, this.roomUuid});
|
||||
|
||||
final String? roomUuid;
|
||||
|
||||
@override
|
||||
ConsumerState<GameRoomHome> createState() => _GameRoomHomeState();
|
||||
}
|
||||
|
||||
class _GameRoomHomeState extends ConsumerState<GameRoomHome> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final jwtAsync = ref.watch(jwtNotifierProvider);
|
||||
return Scaffold(
|
||||
body: jwtAsync.when(
|
||||
data: (jwt) {
|
||||
if (jwt == null || jwt.roomUuid != widget.roomUuid) {
|
||||
logger.fine('Tried to open room, but not authenticated / wrong room');
|
||||
// return home
|
||||
context.go('/');
|
||||
}
|
||||
return Column(
|
||||
children: [
|
||||
Text('Authenticated.'),
|
||||
Text('Welcome to room ${widget.roomUuid}'),
|
||||
],
|
||||
);
|
||||
},
|
||||
loading: () => CircularProgressIndicator(),
|
||||
error: (e, st) => Text('$e, $st'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:frontend/features/room/service/game_room.dart';
|
||||
import 'package:frontend/providers/auth.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
|
||||
final logger = Logger('JoinRoomHome');
|
||||
|
||||
class JoinRoomHome extends ConsumerStatefulWidget {
|
||||
const JoinRoomHome({super.key});
|
||||
|
||||
@override
|
||||
ConsumerState<JoinRoomHome> createState() => _JoinRoomHomeState();
|
||||
}
|
||||
|
||||
class _JoinRoomHomeState extends ConsumerState<JoinRoomHome> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _codeController = TextEditingController();
|
||||
final _nameController = TextEditingController();
|
||||
bool _isLoading = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_codeController.dispose();
|
||||
_nameController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final jwtAsync = ref.watch(jwtNotifierProvider);
|
||||
|
||||
jwtAsync.whenData((jwt) {
|
||||
logger.fine('Got jwt: ${jwt == null ? 'NULL' : jwt.toString().substring(10)}');
|
||||
if (jwt == null) return;
|
||||
logger.fine('Navigating to game room screen');
|
||||
WidgetsBinding.instance.addPostFrameCallback(
|
||||
(_) => context.go('/room/${jwt.roomUuid}'),
|
||||
);
|
||||
});
|
||||
|
||||
return Scaffold(
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
TextFormField(
|
||||
controller: _codeController,
|
||||
textCapitalization: TextCapitalization.characters,
|
||||
style: const TextStyle(
|
||||
letterSpacing: 1.5,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Enter Room Code',
|
||||
hintText: 'ABCDEF',
|
||||
helperText: 'Enter 6 uppercase letters',
|
||||
border: OutlineInputBorder(),
|
||||
errorStyle: TextStyle(height: 0.8),
|
||||
counterText: '', // Hides the built-in counter
|
||||
),
|
||||
maxLength: 6,
|
||||
textInputAction: TextInputAction.done,
|
||||
inputFormatters: [
|
||||
// FilteringTextInputFormatter.allow(RegExp('[A-Z]')),
|
||||
UpperCaseTextFormatter(),
|
||||
],
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Room code is required';
|
||||
}
|
||||
if (value.length != 6) {
|
||||
return 'Code must be exactly 6 characters';
|
||||
}
|
||||
if (!RegExp(r'^[A-Z]{6}$').hasMatch(value)) {
|
||||
return 'Only uppercase letters are allowed';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
onChanged: (value) {
|
||||
// Convert to uppercase while typing
|
||||
if (value != value.toUpperCase()) {
|
||||
_codeController.text = value.toUpperCase();
|
||||
_codeController.selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: _codeController.text.length),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
TextFormField(
|
||||
controller: _nameController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Name',
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
keyboardType: TextInputType.text,
|
||||
maxLength: 20,
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return 'Please enter a name ya goof';
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
onPressed: _isLoading
|
||||
? null
|
||||
: () async {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
setState(() => _isLoading = true);
|
||||
try {
|
||||
ref.read(
|
||||
joinRoomProvider(
|
||||
username: _nameController.text,
|
||||
code: _codeController.text,
|
||||
),
|
||||
);
|
||||
// )
|
||||
// .whenData(
|
||||
// (response) {
|
||||
// if (response != null && response.uuid != null) {
|
||||
// logger.fine('Navigating to room ${response.uuid}');
|
||||
// // context.go('room/${response.uuid}');
|
||||
// } else {
|
||||
// ScaffoldMessenger.of(context).showSnackBar(
|
||||
// SnackBar(
|
||||
// content: Text('Unexpected error occurred.'),
|
||||
// backgroundColor: Colors.red,
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// },
|
||||
// );
|
||||
} finally {
|
||||
setState(() => _isLoading = false);
|
||||
}
|
||||
}
|
||||
},
|
||||
child: _isLoading ? const CircularProgressIndicator() : const Text('Join Room'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class UpperCaseTextFormatter extends TextInputFormatter {
|
||||
@override
|
||||
TextEditingValue formatEditUpdate(
|
||||
TextEditingValue oldValue,
|
||||
TextEditingValue newValue,
|
||||
) {
|
||||
return TextEditingValue(
|
||||
text: newValue.text.toUpperCase(),
|
||||
selection: newValue.selection,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:frontend/providers/auth.dart';
|
||||
import 'package:frontend/providers/dio.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
import 'package:shared_models/user.dart';
|
||||
|
||||
part 'game_room.g.dart';
|
||||
|
||||
final logger = Logger('services/joinRoom');
|
||||
|
||||
@riverpod
|
||||
Future<JoinRoomResponse?> joinRoom(Ref ref, {required String username, required String code}) async {
|
||||
final dio = ref.read(dioProvider);
|
||||
|
||||
try {
|
||||
final response = await dio.post<Map<String, dynamic>>(
|
||||
'/join_room',
|
||||
data: JoinRoomRequest(username: username, roomCode: code).toJson(),
|
||||
);
|
||||
|
||||
if (response.statusCode == 200 && response.data != null) {
|
||||
final joinResponse = JoinRoomResponse.fromJson(response.data!);
|
||||
if (joinResponse.token != null) {
|
||||
logger.fine('Setting token: ${joinResponse.token!.substring(10)}');
|
||||
await ref.read(jwtNotifierProvider.notifier).setJwt(joinResponse.token!);
|
||||
}
|
||||
return joinResponse;
|
||||
} else {
|
||||
logger.warning('Could not join room');
|
||||
}
|
||||
} catch (e) {
|
||||
logger.severe('Failed to join room', e, StackTrace.current);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user