part of '../../settings_modal.dart'; class _FingerprintGroup extends ConsumerWidget { final PyramidTheme pt; const _FingerprintGroup({required this.pt}); static String _fmt(String key) { final clean = key.replaceAll(RegExp(r'\s'), ''); final groups = []; for (var i = 0; i < clean.length; i += 4) { groups.add(clean.substring(i, (i + 4).clamp(0, clean.length))); } return groups.join(' '); } @override Widget build(BuildContext context, WidgetRef ref) { final clientAsync = ref.watch(matrixClientProvider); final client = clientAsync.valueOrNull; if (client == null) return const SizedBox.shrink(); final fingerprint = _fmt(client.fingerprintKey); final deviceId = client.deviceID ?? ''; return _SettingsGroup(title: 'Dieses Gerät', pt: pt, children: [ _SettingsRow( title: 'Geräte-ID', desc: deviceId, pt: pt, control: IconButton( icon: Icon(Icons.copy_rounded, size: 14, color: pt.fgDim), tooltip: 'Kopieren', padding: EdgeInsets.zero, constraints: const BoxConstraints(maxWidth: 28, maxHeight: 28), onPressed: () => Clipboard.setData(ClipboardData(text: deviceId)), ), ), _Divider(pt: pt), Padding( padding: const EdgeInsets.fromLTRB(14, 12, 14, 12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Text('Ed25519-Fingerabdruck', style: TextStyle(color: pt.fg, fontSize: 13, fontWeight: FontWeight.w500)), const Spacer(), IconButton( icon: Icon(Icons.copy_rounded, size: 14, color: pt.fgDim), tooltip: 'Fingerabdruck kopieren', padding: EdgeInsets.zero, constraints: const BoxConstraints(maxWidth: 28, maxHeight: 28), onPressed: () => Clipboard.setData(ClipboardData(text: client.fingerprintKey)), ), ], ), const SizedBox(height: 4), SelectableText( fingerprint, style: TextStyle( color: pt.fgMuted, fontSize: 11, fontFamily: 'monospace', letterSpacing: 0.5, height: 1.6, ), ), ], ), ), ]); } } // Passphrase dialog (for key export/import) class _PassphraseDialog extends StatefulWidget { final PyramidTheme pt; final bool forExport; const _PassphraseDialog({required this.pt, required this.forExport}); @override State<_PassphraseDialog> createState() => _PassphraseDialogState(); } class _PassphraseDialogState extends State<_PassphraseDialog> { final _ctrl1 = TextEditingController(); final _ctrl2 = TextEditingController(); bool _obscure = true; String? _error; @override void dispose() { _ctrl1.dispose(); _ctrl2.dispose(); super.dispose(); } void _submit() { final pass = _ctrl1.text.trim(); if (pass.isEmpty) { setState(() => _error = 'Bitte ein Passwort eingeben.'); return; } if (widget.forExport && pass != _ctrl2.text.trim()) { setState(() => _error = 'Passwörter stimmen nicht überein.'); return; } Navigator.pop(context, pass); } InputDecoration _inputDeco(String label, PyramidTheme pt) => InputDecoration( labelText: label, labelStyle: TextStyle(color: pt.fgMuted, fontSize: 13), errorText: _error, filled: true, fillColor: pt.bg3, prefixIcon: Icon(Icons.lock_outline_rounded, color: pt.fgDim, size: 18), border: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.border)), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.border)), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(8), borderSide: BorderSide(color: pt.accent)), isDense: true, errorMaxLines: 2, suffixIcon: IconButton( icon: Icon( _obscure ? Icons.visibility_off_outlined : Icons.visibility_outlined, size: 18, color: pt.fgDim, ), onPressed: () => setState(() => _obscure = !_obscure), ), ); @override Widget build(BuildContext context) { final pt = widget.pt; return AlertDialog( backgroundColor: pt.bg2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(pt.rXl), side: BorderSide(color: pt.border), ), title: Text( widget.forExport ? 'Schlüssel exportieren' : 'Schlüssel importieren', style: TextStyle(color: pt.fg, fontSize: 16, fontWeight: FontWeight.w700), ), content: SizedBox( width: 340, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.forExport ? 'Wähle ein Passwort zum Verschlüsseln der Schlüsseldatei.' : 'Gib das Passwort ein, mit dem die Datei verschlüsselt wurde.', style: TextStyle(color: pt.fgMuted, fontSize: 13, height: 1.5), ), const SizedBox(height: 16), TextField( controller: _ctrl1, obscureText: _obscure, autofocus: true, style: TextStyle(color: pt.fg, fontSize: 13), decoration: _inputDeco('Passwort', pt).copyWith( // Only show error on confirm field if export, else here errorText: widget.forExport ? null : _error, ), cursorColor: pt.accent, onSubmitted: widget.forExport ? null : (_) => _submit(), ), if (widget.forExport) ...[ const SizedBox(height: 12), TextField( controller: _ctrl2, obscureText: _obscure, style: TextStyle(color: pt.fg, fontSize: 13), decoration: _inputDeco('Passwort bestätigen', pt), cursorColor: pt.accent, onSubmitted: (_) => _submit(), ), ], ], ), ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text('Abbrechen', style: TextStyle(color: pt.fgMuted)), ), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: pt.accent, foregroundColor: pt.accentFg, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), ), onPressed: _submit, child: Text(widget.forExport ? 'Exportieren' : 'Importieren'), ), ], ); } }