You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

369 lines
12 KiB

import 'package:flutter/material.dart';
import '../../data/session/session_service.dart';
import '../../data/firebase/firebase_service.dart';
/// Screen for user session management (login/logout).
class SessionScreen extends StatefulWidget {
final SessionService? sessionService;
final FirebaseService? firebaseService;
final VoidCallback? onSessionChanged;
const SessionScreen({
super.key,
this.sessionService,
this.firebaseService,
this.onSessionChanged,
});
@override
State<SessionScreen> createState() => _SessionScreenState();
}
class _SessionScreenState extends State<SessionScreen> {
final TextEditingController _usernameController = TextEditingController();
final TextEditingController _userIdController = TextEditingController();
final TextEditingController _emailController = TextEditingController();
final TextEditingController _passwordController = TextEditingController();
bool _isLoading = false;
bool _useFirebaseAuth = false;
@override
void initState() {
super.initState();
// Check if Firebase Auth is available
_useFirebaseAuth = widget.firebaseService?.isEnabled == true &&
widget.firebaseService?.config.authEnabled == true;
}
@override
void dispose() {
_usernameController.dispose();
_userIdController.dispose();
_emailController.dispose();
_passwordController.dispose();
super.dispose();
}
Future<void> _handleLogin() async {
if (widget.sessionService == null) return;
setState(() {
_isLoading = true;
});
try {
if (_useFirebaseAuth && widget.firebaseService != null) {
// Use Firebase Auth for authentication
final email = _emailController.text.trim();
final password = _passwordController.text.trim();
if (email.isEmpty || password.isEmpty) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please enter email and password'),
),
);
}
return;
}
// Authenticate with Firebase
final firebaseUser = await widget.firebaseService!.loginWithEmailPassword(
email: email,
password: password,
);
// Create session with Firebase user info
await widget.sessionService!.login(
id: firebaseUser.uid,
username: firebaseUser.email?.split('@').first ?? firebaseUser.uid,
token: await firebaseUser.getIdToken(),
);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Login successful'),
),
);
setState(() {});
// Notify parent that session state changed
widget.onSessionChanged?.call();
}
} else {
// Simple validation mode (no Firebase Auth)
final username = _usernameController.text.trim();
final userId = _userIdController.text.trim();
if (username.isEmpty || userId.isEmpty) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Please enter username and user ID'),
),
);
}
return;
}
// Basic validation: require minimum length
if (userId.length < 3) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('User ID must be at least 3 characters'),
),
);
}
return;
}
if (username.length < 2) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Username must be at least 2 characters'),
),
);
}
return;
}
// Create session (demo mode - no real authentication)
await widget.sessionService!.login(
id: userId,
username: username,
);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Login successful (demo mode)'),
backgroundColor: Colors.orange,
),
);
setState(() {});
// Notify parent that session state changed
widget.onSessionChanged?.call();
}
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Login failed: ${e.toString().replaceAll('FirebaseException: ', '').replaceAll('SessionException: ', '')}'),
backgroundColor: Colors.red,
),
);
}
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
Future<void> _handleLogout() async {
if (widget.sessionService == null) return;
setState(() {
_isLoading = true;
});
try {
// Logout from session service first
await widget.sessionService!.logout();
// Also logout from Firebase Auth if enabled
if (_useFirebaseAuth && widget.firebaseService != null) {
try {
await widget.firebaseService!.logout();
} catch (e) {
// Log error but don't fail logout - session is already cleared
debugPrint('Warning: Firebase logout failed: $e');
}
}
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Logout successful'),
),
);
setState(() {});
// Notify parent that session state changed
widget.onSessionChanged?.call();
}
} catch (e) {
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Logout failed: ${e.toString().replaceAll('SessionException: ', '')}'),
backgroundColor: Colors.red,
),
);
}
} finally {
if (mounted) {
setState(() {
_isLoading = false;
});
}
}
}
@override
Widget build(BuildContext context) {
final isLoggedIn = widget.sessionService?.isLoggedIn ?? false;
final currentUser = widget.sessionService?.currentUser;
return Scaffold(
appBar: AppBar(
title: const Text('Session'),
),
body: _isLoading
? const Center(child: CircularProgressIndicator())
: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
if (isLoggedIn && currentUser != null) ...[
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text(
'Current Session',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
Text('User ID: ${currentUser.id}'),
Text('Username: ${currentUser.username}'),
Text(
'Created: ${DateTime.fromMillisecondsSinceEpoch(currentUser.createdAt).toString().split('.')[0]}',
),
],
),
),
),
const SizedBox(height: 16),
ElevatedButton(
onPressed: _handleLogout,
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
foregroundColor: Colors.white,
),
child: const Text('Logout'),
),
] else ...[
const Icon(
Icons.person_outline,
size: 64,
color: Colors.grey,
),
const SizedBox(height: 16),
const Text(
'Login',
style: TextStyle(
fontSize: 24,
fontWeight: FontWeight.bold,
),
textAlign: TextAlign.center,
),
if (!_useFirebaseAuth) ...[
const SizedBox(height: 8),
Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.orange.shade50,
borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.orange.shade200),
),
child: Row(
children: [
Icon(Icons.info_outline, color: Colors.orange.shade700, size: 20),
const SizedBox(width: 8),
Expanded(
child: Text(
'Demo mode: No authentication required. Enter any valid user ID and username.',
style: TextStyle(
fontSize: 12,
color: Colors.orange.shade700,
),
),
),
],
),
),
],
const SizedBox(height: 24),
if (_useFirebaseAuth) ...[
TextField(
controller: _emailController,
decoration: const InputDecoration(
labelText: 'Email',
hintText: 'Enter your email',
border: OutlineInputBorder(),
),
keyboardType: TextInputType.emailAddress,
autofillHints: const [AutofillHints.email],
),
const SizedBox(height: 16),
TextField(
controller: _passwordController,
decoration: const InputDecoration(
labelText: 'Password',
hintText: 'Enter your password',
border: OutlineInputBorder(),
),
obscureText: true,
autofillHints: const [AutofillHints.password],
),
] else ...[
TextField(
controller: _userIdController,
decoration: const InputDecoration(
labelText: 'User ID',
hintText: 'Enter user ID (min 3 characters)',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 16),
TextField(
controller: _usernameController,
decoration: const InputDecoration(
labelText: 'Username',
hintText: 'Enter username (min 2 characters)',
border: OutlineInputBorder(),
),
),
],
const SizedBox(height: 24),
ElevatedButton(
onPressed: _isLoading ? null : _handleLogin,
child: _isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Login'),
),
],
],
),
),
);
}
}

Powered by TurnKey Linux.