Signup screen and functionality

This commit is contained in:
Shivering Ocelot committed 2025-05-26 11:22:31 -05:00
1 parent 08f0668997
commit c3e55e89f2
6 files changed
+155 -11

No files matched your search

+2
View File
@@ -7,6 +7,8 @@ class ConfigField {
final String name; final String name;
final String type; final String type;
final TextEditingController controller; final TextEditingController controller;
// There should be an additional property to indicate whether this field is deactivated/removed/reset
// meaning it will be set as 'null' (default) rather than any string, otherwise only an empty string can be set
ConfigField({required this.name, required this.type, required this.controller}); ConfigField({required this.name, required this.type, required this.controller});
} }
+20 -2
View File
@@ -1,7 +1,8 @@
import 'package:http/http.dart' as http;
import 'dart:html'; import 'dart:html';
import 'dart:convert'; import 'dart:convert';
import 'dart:async'; // Import for handling Completers import 'dart:async';
class ChatInfo { class ChatInfo {
int id; int id;
@@ -31,6 +32,23 @@ class Backend {
connect(); connect();
} }
static Future<void> signup(String username, String email, String password) async {
final response = await http.post(Uri.parse("$_url/user/signup"),
headers: {
'Content-Type': 'application/json',
},
body: jsonEncode({
'username': username,
'email': email,
'password': password,
})
);
if (response.statusCode < 200 || response.statusCode >= 300) {
throw Exception("Signup Status Code ${response.statusCode}");
}
}
static Future<void> auth(String username, String password) { static Future<void> auth(String username, String password) {
_socket!.send(json.encode({'username': username, 'password': password})); _socket!.send(json.encode({'username': username, 'password': password}));
_credentials = (username: username, password: password); _credentials = (username: username, password: password);
@@ -43,7 +61,7 @@ class Backend {
} }
static void connect() { static void connect() {
_socket = WebSocket(_url!); _socket = WebSocket(_url! + "/ws");
_socket!.onOpen.listen((event) { _socket!.onOpen.listen((event) {
print('Connected to the WebSocket server'); print('Connected to the WebSocket server');
Backend.invalidateCache(chat: true); Backend.invalidateCache(chat: true);
+99 -8
View File
@@ -1,24 +1,52 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'backend.dart';
class LoginScreen extends StatelessWidget { class LoginScreen extends StatelessWidget {
final Function(String, String) onLogin; final Function(String, String) onLogin;
final bool isSignupMode; // Changed to a boolean
LoginScreen({required this.onLogin}); LoginScreen({required this.onLogin, this.isSignupMode = false}); // Default to false (login mode)
final TextEditingController usernameController = TextEditingController(); final TextEditingController usernameController = TextEditingController();
final TextEditingController passwordController = TextEditingController(); final TextEditingController passwordController = TextEditingController();
final TextEditingController emailController = TextEditingController();
final TextEditingController confirmPasswordController = TextEditingController();
final FocusNode usernameFocusNode = FocusNode(); final FocusNode usernameFocusNode = FocusNode();
final FocusNode passwordFocusNode = FocusNode(); final FocusNode passwordFocusNode = FocusNode();
final FocusNode emailFocusNode = FocusNode();
final FocusNode confirmPasswordFocusNode = FocusNode();
void _handleLogin() { void _handleLogin() {
onLogin(usernameController.text, passwordController.text); onLogin(usernameController.text, passwordController.text);
} }
void _handleSignup(BuildContext context) async {
final username = usernameController.text;
final password = passwordController.text;
final email = emailController.text;
if (password != confirmPasswordController.text) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text("Passwords don't match")),
);
return;
}
try {
await Backend.signup(username, email, password);
_handleLogin();
} catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Could not signup: $e')),
);
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar(title: Text('Login')), appBar: AppBar(title: Text(isSignupMode ? 'Signup' : 'Login')), // Dynamic title
body: Center( body: Center(
child: Padding( child: Padding(
padding: const EdgeInsets.all(16.0), padding: const EdgeInsets.all(16.0),
@@ -31,24 +59,87 @@ class LoginScreen extends StatelessWidget {
decoration: InputDecoration(labelText: 'Username'), decoration: InputDecoration(labelText: 'Username'),
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
onSubmitted: (_) { onSubmitted: (_) {
FocusScope.of(context).requestFocus(passwordFocusNode); if (!isSignupMode) { // If not in signup mode, move to password
FocusScope.of(context).requestFocus(passwordFocusNode);
} else { // In signup mode, move to email
FocusScope.of(context).requestFocus(emailFocusNode);
}
}, },
), ),
SizedBox(height: 20), SizedBox(height: 20),
if (isSignupMode) ...[ // Conditionally show email field
TextField(
controller: emailController,
focusNode: emailFocusNode,
decoration: InputDecoration(labelText: 'Email'),
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.next,
onSubmitted: (_) {
FocusScope.of(context).requestFocus(passwordFocusNode);
},
),
SizedBox(height: 20),
],
TextField( TextField(
controller: passwordController, controller: passwordController,
focusNode: passwordFocusNode, focusNode: passwordFocusNode,
decoration: InputDecoration(labelText: 'Password'), decoration: InputDecoration(labelText: 'Password'),
obscureText: true, obscureText: true,
textInputAction: TextInputAction.done, textInputAction: isSignupMode ? TextInputAction.next : TextInputAction.done, // Adjust text action
onSubmitted: (_) { onSubmitted: (_) {
_handleLogin(); if (!isSignupMode) { // If not in signup mode, handle login
_handleLogin();
} else { // In signup mode, move to confirm password
FocusScope.of(context).requestFocus(confirmPasswordFocusNode);
}
}, },
), ),
SizedBox(height: 20), SizedBox(height: 20),
if (isSignupMode) ...[ // Conditionally show confirm password field
TextField(
controller: confirmPasswordController,
focusNode: confirmPasswordFocusNode,
decoration: InputDecoration(labelText: 'Confirm Password'),
obscureText: true,
textInputAction: TextInputAction.done,
onSubmitted: (_) {
_handleSignup(context);
},
),
SizedBox(height: 20),
],
ElevatedButton( ElevatedButton(
onPressed: _handleLogin, onPressed: () {
child: Text('Login'), if (!isSignupMode) { // If not in signup mode, handle login
_handleLogin();
} else { // In signup mode, handle signup
_handleSignup(context);
}
},
child: Text(isSignupMode ? 'Signup' : 'Login'), // Dynamic button text
),
SizedBox(height: 20),
TextButton(
onPressed: () {
if (!isSignupMode) { // If in login mode, navigate to signup
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => LoginScreen(
onLogin: onLogin,
isSignupMode: true, // Push with signup mode
),
),
);
} else { // If in signup mode, pop back to login
Navigator.of(context).pop();
}
},
child: Text(
isSignupMode
? 'Already have an account? Login' // Text for signup mode
: 'Don\'t have an account? Sign Up', // Text for login mode
style: TextStyle(color: Colors.blue),
),
), ),
], ],
), ),
@@ -56,4 +147,4 @@ class LoginScreen extends StatelessWidget {
), ),
); );
} }
} }
+1 -1
View File
@@ -30,7 +30,7 @@ void main() {
print("Main"); print("Main");
Config.loadConfig(); // Load the configuration before running the app Config.loadConfig(); // Load the configuration before running the app
Backend.setup('${Config.backendUrl}/ws'); Backend.setup(Config.backendUrl!);
runApp(HubApp()); runApp(HubApp());
} }
+32
View File
@@ -136,6 +136,22 @@ packages:
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
http:
dependency: "direct main"
description:
name: http
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
url: "https://pub.dev"
source: hosted
version: "1.4.0"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
js: js:
dependency: transitive dependency: transitive
description: description:
@@ -325,6 +341,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.4" version: "0.7.4"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
vector_math: vector_math:
dependency: transitive dependency: transitive
description: description:
@@ -341,6 +365,14 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "14.3.1" version: "14.3.1"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
win32: win32:
dependency: transitive dependency: transitive
description: description:
+1
View File
@@ -37,6 +37,7 @@ dependencies:
# For user credentials and sensitive data # For user credentials and sensitive data
flutter_secure_storage: ^9.2.4 flutter_secure_storage: ^9.2.4
http: ^1.4.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: