madari-oss/lib/features/settings/screen/security_screen.dart
Madari Developers 16fe4a653f Project import generated by Copybara.
GitOrigin-RevId: 829626e92d5dba6a4586d1e7c4bd1615ec396e88
2025-01-02 18:46:26 +00:00

250 lines
7.4 KiB
Dart

import 'package:flutter/material.dart';
import 'package:madari_client/engine/engine.dart';
import 'package:pocketbase/pocketbase.dart';
class SecurityScreen extends StatefulWidget {
const SecurityScreen({super.key});
@override
State<SecurityScreen> createState() => _SecurityScreenState();
}
class _SecurityScreenState extends State<SecurityScreen> {
Future<void> _showChangePasswordDialog() async {
return showDialog(
context: context,
builder: (context) => const ChangeDialog(),
);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Security'),
),
body: ListView(
children: [
ListTile(
leading: const Icon(Icons.lock),
title: const Text('Change Password'),
subtitle: const Text('Update your account password'),
onTap: _showChangePasswordDialog,
),
const Divider(),
const ListTile(
leading: Icon(Icons.security),
title: Text('Two-Factor Authentication'),
subtitle: Text('Coming soon'),
enabled: false,
),
const Divider(),
const ListTile(
leading: Icon(Icons.history),
title: Text('Login History'),
subtitle: Text('Coming soon'),
enabled: false,
),
],
),
);
}
}
class ChangeDialog extends StatefulWidget {
const ChangeDialog({
super.key,
});
@override
State<ChangeDialog> createState() => _ChangeDialogState();
}
class _ChangeDialogState extends State<ChangeDialog> {
@override
void dispose() {
_currentPasswordController.dispose();
_newPasswordController.dispose();
_confirmPasswordController.dispose();
super.dispose();
}
PocketBase get pocketBase => AppEngine.engine.pb;
final _formKey = GlobalKey<FormState>();
final _currentPasswordController = TextEditingController();
final _newPasswordController = TextEditingController();
final _confirmPasswordController = TextEditingController();
bool _isLoading = false;
bool _obscureCurrentPassword = true;
bool _obscureNewPassword = true;
bool _obscureConfirmPassword = true;
Future<void> _changePassword() async {
if (!_formKey.currentState!.validate()) return;
setState(() => _isLoading = true);
try {
final userId = pocketBase.authStore.record!.id;
// First verify the current password
await pocketBase.collection('users').authWithPassword(
pocketBase.authStore.record!.getStringValue("email"),
_currentPasswordController.text,
);
// If verification successful, update the password
await pocketBase.collection('users').update(
userId,
body: {
'password': _newPasswordController.text,
'passwordConfirm': _confirmPasswordController.text,
},
);
// Clear the form
_currentPasswordController.clear();
_newPasswordController.clear();
_confirmPasswordController.clear();
// Close the dialog
if (mounted) Navigator.pop(context);
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Password changed successfully'),
backgroundColor: Colors.green,
),
);
}
} catch (e) {
if (context.mounted && mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Error: ${e.toString()}'),
backgroundColor: Colors.red,
),
);
}
} finally {
if (mounted) {
setState(() => _isLoading = false);
}
}
}
@override
Widget build(BuildContext context) {
return AlertDialog(
title: const Text('Change Password'),
content: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextFormField(
controller: _currentPasswordController,
obscureText: _obscureCurrentPassword,
decoration: InputDecoration(
labelText: 'Current Password',
suffixIcon: IconButton(
icon: Icon(
_obscureCurrentPassword
? Icons.visibility_off
: Icons.visibility,
),
onPressed: () {
setState(() {
_obscureCurrentPassword = !_obscureCurrentPassword;
});
},
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter your current password';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _newPasswordController,
obscureText: _obscureNewPassword,
decoration: InputDecoration(
labelText: 'New Password',
suffixIcon: IconButton(
icon: Icon(
_obscureNewPassword
? Icons.visibility_off
: Icons.visibility,
),
onPressed: () {
setState(() {
_obscureNewPassword = !_obscureNewPassword;
});
},
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a new password';
}
if (value.length < 8) {
return 'Password must be at least 8 characters';
}
return null;
},
),
const SizedBox(height: 16),
TextFormField(
controller: _confirmPasswordController,
obscureText: _obscureConfirmPassword,
decoration: InputDecoration(
labelText: 'Confirm New Password',
suffixIcon: IconButton(
icon: Icon(
_obscureConfirmPassword
? Icons.visibility_off
: Icons.visibility,
),
onPressed: () {
setState(() {
_obscureConfirmPassword = !_obscureConfirmPassword;
});
},
),
),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please confirm your new password';
}
if (value != _newPasswordController.text) {
return 'Passwords do not match';
}
return null;
},
),
],
),
),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text('Cancel'),
),
ElevatedButton(
onPressed: _isLoading ? null : _changePassword,
child: _isLoading
? const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
)
: const Text('Change Password'),
),
],
);
}
}