refactor: Gott-Datei settings_modal.dart in 12 Dateien aufgeteilt
Reine Verschiebung per Dart part/part-of (keine Umbenennung, keine Logikänderung) - settings_modal.dart schrumpft von 5541 auf 270 Zeilen. Verifiziert per automatisiertem Blockvergleich gegen das Original und flutter analyze (Baseline unverändert: 1 bekannter Hinweis). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,364 @@
|
||||
part of '../settings_modal.dart';
|
||||
|
||||
class _ChangePasswordDialog extends ConsumerStatefulWidget {
|
||||
final PyramidTheme pt;
|
||||
const _ChangePasswordDialog({required this.pt});
|
||||
|
||||
@override
|
||||
ConsumerState<_ChangePasswordDialog> createState() => _ChangePasswordDialogState();
|
||||
}
|
||||
|
||||
class _ChangePasswordDialogState extends ConsumerState<_ChangePasswordDialog> {
|
||||
final _oldCtrl = TextEditingController();
|
||||
final _newCtrl = TextEditingController();
|
||||
final _confirmCtrl = TextEditingController();
|
||||
bool _oldVisible = false;
|
||||
bool _newVisible = false;
|
||||
bool _loading = false;
|
||||
String? _error;
|
||||
bool _done = false;
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_oldCtrl.dispose();
|
||||
_newCtrl.dispose();
|
||||
_confirmCtrl.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _submit() async {
|
||||
final oldPw = _oldCtrl.text;
|
||||
final newPw = _newCtrl.text;
|
||||
final confirm = _confirmCtrl.text;
|
||||
if (oldPw.isEmpty || newPw.isEmpty) {
|
||||
setState(() => _error = 'Bitte alle Felder ausfüllen.');
|
||||
return;
|
||||
}
|
||||
if (newPw != confirm) {
|
||||
setState(() => _error = 'Die neuen Passwörter stimmen nicht überein.');
|
||||
return;
|
||||
}
|
||||
if (newPw.length < 8) {
|
||||
setState(() => _error = 'Das neue Passwort muss mindestens 8 Zeichen lang sein.');
|
||||
return;
|
||||
}
|
||||
setState(() { _loading = true; _error = null; });
|
||||
try {
|
||||
final client = await ref.read(matrixClientProvider.future);
|
||||
await client.changePassword(newPw, oldPassword: oldPw);
|
||||
if (mounted) setState(() { _loading = false; _done = true; });
|
||||
} on MatrixException catch (e) {
|
||||
if (mounted) setState(() { _loading = false; _error = e.errorMessage; });
|
||||
} catch (e) {
|
||||
if (mounted) setState(() { _loading = false; _error = e.toString().split('\n').first; });
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final pt = widget.pt;
|
||||
return Dialog(
|
||||
backgroundColor: pt.bg2,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(pt.rXl), side: BorderSide(color: pt.border)),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 420, minWidth: 300),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: _done ? _buildDone(pt) : _buildForm(pt),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDone(PyramidTheme pt) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.check_circle_outline_rounded, color: PyramidColors.success, size: 52),
|
||||
const SizedBox(height: 12),
|
||||
Text('Passwort geändert', style: TextStyle(color: pt.fg, fontSize: 15, fontWeight: FontWeight.w600)),
|
||||
const SizedBox(height: 6),
|
||||
Text('Dein Passwort wurde erfolgreich aktualisiert.',
|
||||
textAlign: TextAlign.center, style: TextStyle(color: pt.fgMuted, fontSize: 13)),
|
||||
const SizedBox(height: 20),
|
||||
SizedBox(width: double.infinity, child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(backgroundColor: pt.accent, foregroundColor: pt.accentFg,
|
||||
elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Schließen'),
|
||||
)),
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildForm(PyramidTheme pt) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(children: [
|
||||
Icon(Icons.lock_outline_rounded, size: 18, color: pt.accent),
|
||||
const SizedBox(width: 8),
|
||||
Text('Passwort ändern', style: TextStyle(color: pt.fg, fontSize: 15, fontWeight: FontWeight.w600)),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
icon: Icon(Icons.close_rounded, color: pt.fgDim, size: 16),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints(maxWidth: 28, maxHeight: 28),
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 20),
|
||||
_PwField(ctrl: _oldCtrl, label: 'Aktuelles Passwort', pt: pt,
|
||||
visible: _oldVisible, onToggle: () => setState(() => _oldVisible = !_oldVisible)),
|
||||
const SizedBox(height: 12),
|
||||
_PwField(ctrl: _newCtrl, label: 'Neues Passwort', pt: pt,
|
||||
visible: _newVisible, onToggle: () => setState(() => _newVisible = !_newVisible)),
|
||||
const SizedBox(height: 12),
|
||||
_PwField(ctrl: _confirmCtrl, label: 'Neues Passwort bestätigen', pt: pt,
|
||||
visible: _newVisible, onToggle: null,
|
||||
onSubmitted: (_) => _submit()),
|
||||
if (_error != null) ...[
|
||||
const SizedBox(height: 10),
|
||||
_StatusCard(pt: pt, color: pt.danger, icon: Icons.error_outline_rounded, text: _error!),
|
||||
],
|
||||
const SizedBox(height: 20),
|
||||
SizedBox(width: double.infinity, child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(backgroundColor: pt.accent, foregroundColor: pt.accentFg,
|
||||
elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
|
||||
onPressed: _loading ? null : _submit,
|
||||
child: _loading
|
||||
? const SizedBox(width: 18, height: 18, child: CircularProgressIndicator.adaptive(strokeWidth: 2))
|
||||
: const Text('Passwort ändern'),
|
||||
)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
class _DeleteAccountDialog extends ConsumerStatefulWidget {
|
||||
final PyramidTheme pt;
|
||||
const _DeleteAccountDialog({required this.pt});
|
||||
|
||||
@override
|
||||
ConsumerState<_DeleteAccountDialog> createState() => _DeleteAccountDialogState();
|
||||
}
|
||||
|
||||
enum _DeleteStep { confirm, auth, deleting, error }
|
||||
|
||||
class _DeleteAccountDialogState extends ConsumerState<_DeleteAccountDialog> {
|
||||
_DeleteStep _step = _DeleteStep.confirm;
|
||||
final _pwCtrl = TextEditingController();
|
||||
bool _pwVisible = false;
|
||||
bool _eraseData = true;
|
||||
String? _error;
|
||||
|
||||
@override
|
||||
void dispose() { _pwCtrl.dispose(); super.dispose(); }
|
||||
|
||||
Future<void> _proceed() async {
|
||||
setState(() { _step = _DeleteStep.deleting; _error = null; });
|
||||
final client = await ref.read(matrixClientProvider.future);
|
||||
try {
|
||||
await client.deactivateAccount(erase: _eraseData);
|
||||
} on MatrixException catch (e) {
|
||||
if (e.requireAdditionalAuthentication && mounted) {
|
||||
// UIA required — try with password
|
||||
try {
|
||||
await client.deactivateAccount(
|
||||
erase: _eraseData,
|
||||
auth: AuthenticationData(
|
||||
type: AuthenticationTypes.password,
|
||||
session: e.session,
|
||||
additionalFields: {
|
||||
'identifier': {'type': 'm.id.user', 'user': client.userID},
|
||||
'password': _pwCtrl.text,
|
||||
},
|
||||
),
|
||||
);
|
||||
} on MatrixException catch (e2) {
|
||||
if (mounted) setState(() { _step = _DeleteStep.error; _error = e2.errorMessage; });
|
||||
return;
|
||||
} catch (e2) {
|
||||
if (mounted) setState(() { _step = _DeleteStep.error; _error = e2.toString().split('\n').first; });
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (mounted) setState(() { _step = _DeleteStep.error; _error = e.errorMessage; });
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) setState(() { _step = _DeleteStep.error; _error = e.toString().split('\n').first; });
|
||||
return;
|
||||
}
|
||||
// Konto wurde gelöscht — ausloggen
|
||||
unawaited(AuthLog.write('User-initiated logout (Konto gelöscht)'));
|
||||
try { await client.logout(); } catch (_) {}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final pt = widget.pt;
|
||||
return Dialog(
|
||||
backgroundColor: pt.bg2,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(pt.rXl), side: BorderSide(color: pt.border)),
|
||||
child: ConstrainedBox(
|
||||
constraints: const BoxConstraints(maxWidth: 440, minWidth: 300),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: switch (_step) {
|
||||
_DeleteStep.confirm => _buildConfirm(pt),
|
||||
_DeleteStep.auth => _buildAuth(pt),
|
||||
_DeleteStep.deleting => const Center(
|
||||
child: Padding(padding: EdgeInsets.all(32), child: CircularProgressIndicator.adaptive()),
|
||||
),
|
||||
_DeleteStep.error => _buildError(pt),
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildConfirm(PyramidTheme pt) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(children: [
|
||||
Icon(Icons.warning_rounded, size: 20, color: pt.danger),
|
||||
const SizedBox(width: 8),
|
||||
Text('Konto löschen', style: TextStyle(color: pt.danger, fontSize: 15, fontWeight: FontWeight.w700)),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
icon: Icon(Icons.close_rounded, color: pt.fgDim, size: 16),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
padding: EdgeInsets.zero,
|
||||
constraints: const BoxConstraints(maxWidth: 28, maxHeight: 28),
|
||||
),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: pt.danger.withAlpha(20),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: pt.danger.withAlpha(60)),
|
||||
),
|
||||
child: Text(
|
||||
'Diese Aktion ist unwiderruflich. Dein Konto, alle Sitzungen und '
|
||||
'deine Mitgliedschaft in Räumen werden dauerhaft gelöscht.',
|
||||
style: TextStyle(color: pt.fg, fontSize: 13, height: 1.5),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
GestureDetector(
|
||||
onTap: () => setState(() => _eraseData = !_eraseData),
|
||||
child: Row(children: [
|
||||
SizedBox(
|
||||
width: 18, height: 18,
|
||||
child: Checkbox(
|
||||
value: _eraseData,
|
||||
onChanged: (v) => setState(() => _eraseData = v ?? true),
|
||||
activeColor: pt.accent,
|
||||
side: BorderSide(color: pt.border),
|
||||
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(child: Text('Nachrichten und Daten vom Server löschen',
|
||||
style: TextStyle(color: pt.fg, fontSize: 13))),
|
||||
]),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(children: [
|
||||
Expanded(child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(foregroundColor: pt.fg, side: BorderSide(color: pt.border),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Abbrechen'),
|
||||
)),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(backgroundColor: pt.danger, foregroundColor: Colors.white,
|
||||
elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
|
||||
onPressed: () => setState(() => _step = _DeleteStep.auth),
|
||||
child: const Text('Weiter'),
|
||||
)),
|
||||
]),
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildAuth(PyramidTheme pt) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(children: [
|
||||
Icon(Icons.lock_outline_rounded, size: 18, color: pt.danger),
|
||||
const SizedBox(width: 8),
|
||||
Text('Konto löschen bestätigen', style: TextStyle(color: pt.fg, fontSize: 15, fontWeight: FontWeight.w600)),
|
||||
]),
|
||||
const SizedBox(height: 16),
|
||||
Text('Gib dein Passwort ein, um die Löschung zu bestätigen.',
|
||||
style: TextStyle(color: pt.fgMuted, fontSize: 13)),
|
||||
const SizedBox(height: 16),
|
||||
TextField(
|
||||
controller: _pwCtrl,
|
||||
autofocus: true,
|
||||
obscureText: !_pwVisible,
|
||||
style: TextStyle(color: pt.fg, fontSize: 13),
|
||||
decoration: InputDecoration(
|
||||
labelText: 'Passwort',
|
||||
labelStyle: TextStyle(color: pt.fgMuted, fontSize: 13),
|
||||
filled: true, fillColor: pt.bg3,
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(8)),
|
||||
enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.border)),
|
||||
focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.danger)),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(_pwVisible ? Icons.visibility_off_rounded : Icons.visibility_rounded, size: 16, color: pt.fgDim),
|
||||
onPressed: () => setState(() => _pwVisible = !_pwVisible),
|
||||
),
|
||||
),
|
||||
cursorColor: pt.danger,
|
||||
onSubmitted: (_) => _proceed(),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
Row(children: [
|
||||
Expanded(child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(foregroundColor: pt.fg, side: BorderSide(color: pt.border),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
|
||||
onPressed: () => setState(() => _step = _DeleteStep.confirm),
|
||||
child: const Text('Zurück'),
|
||||
)),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(backgroundColor: pt.danger, foregroundColor: Colors.white,
|
||||
elevation: 0, padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
|
||||
onPressed: _proceed,
|
||||
child: const Text('Konto endgültig löschen'),
|
||||
)),
|
||||
]),
|
||||
],
|
||||
);
|
||||
|
||||
Widget _buildError(PyramidTheme pt) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.error_outline_rounded, color: pt.danger, size: 48),
|
||||
const SizedBox(height: 12),
|
||||
Text(_error ?? 'Ein Fehler ist aufgetreten.',
|
||||
textAlign: TextAlign.center, style: TextStyle(color: pt.fgMuted, fontSize: 13)),
|
||||
const SizedBox(height: 20),
|
||||
SizedBox(width: double.infinity, child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(foregroundColor: pt.fg, side: BorderSide(color: pt.border),
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Schließen'),
|
||||
)),
|
||||
],
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user