part of '../settings_modal.dart'; class _AboutSection extends ConsumerWidget { final PyramidTheme pt; const _AboutSection({required this.pt}); @override Widget build(BuildContext context, WidgetRef ref) { final clientAsync = ref.watch(matrixClientProvider); final client = clientAsync.valueOrNull; return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _SectionTitle(title: 'Über Pyramid', subtitle: 'Version & Konto-Infos.', pt: pt), _SettingsGroup(title: 'Version', pt: pt, children: [ _SettingsRow( title: 'Pyramid', desc: '0.1.0 · Flutter · Matrix SDK', pt: pt, control: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), decoration: BoxDecoration( color: pt.accentSoft, borderRadius: BorderRadius.circular(6)), child: Text('Beta', style: TextStyle( color: pt.accent, fontSize: 10, fontWeight: FontWeight.w700)), )), _Divider(pt: pt), _SettingsRow( title: 'Matrix SDK', desc: 'matrix-dart-sdk ^6.2.0', pt: pt), ]), const SizedBox(height: 20), _UpdatesSettingsGroup(pt: pt), const SizedBox(height: 20), _NotificationsSettingsGroup(pt: pt), const SizedBox(height: 20), if (client != null) _SettingsGroup(title: 'Konto', pt: pt, children: [ _SettingsRow( title: 'Matrix-ID', desc: client.userID ?? '', pt: pt, control: GestureDetector( onTap: () => Clipboard.setData(ClipboardData(text: client.userID ?? '')), child: Tooltip( message: 'Kopieren', child: Icon(Icons.copy_rounded, size: 14, color: pt.fgDim), ), ), ), _Divider(pt: pt), _SettingsRow( title: 'Homeserver', desc: client.homeserver?.toString() ?? '', pt: pt, ), _Divider(pt: pt), _SettingsRow( title: 'Gerät', desc: client.deviceID ?? '', pt: pt, control: GestureDetector( onTap: () => Clipboard.setData(ClipboardData(text: client.deviceID ?? '')), child: Tooltip( message: 'Kopieren', child: Icon(Icons.copy_rounded, size: 14, color: pt.fgDim), ), ), ), ]), const SizedBox(height: 20), _SettingsGroup(title: 'Konto-Aktionen', pt: pt, children: [ _SettingsRow( title: 'Abmelden', desc: 'Auf diesem Gerät ausloggen', pt: pt, destructive: true, showArrow: true, onTap: () => _confirmLogout(context, ref), ), ]), ], ), ); } Future _confirmLogout(BuildContext context, WidgetRef ref) async { final client = await ref.read(matrixClientProvider.future); final backupMissing = await isKeyBackupMissing(client); if (!context.mounted) return; final confirm = await showDialog( context: context, builder: (ctx) => AlertDialog( backgroundColor: pt.bg2, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(pt.rXl), side: BorderSide(color: pt.border)), title: Text('Abmelden?', style: TextStyle(color: pt.fg, fontSize: 16, fontWeight: FontWeight.w700)), content: Text( backupMissing ? 'Du wirst auf diesem Gerät abgemeldet. Es ist kein Schlüssel-Backup eingerichtet — ' 'ältere verschlüsselte Nachrichten sind danach auf keinem Gerät mehr lesbar.' : 'Du wirst auf diesem Gerät abgemeldet. Deine Nachrichten bleiben erhalten.', style: TextStyle( color: backupMissing ? pt.danger : pt.fgMuted, fontSize: 13, height: 1.4), ), actions: [ TextButton( onPressed: () => Navigator.pop(ctx, false), child: Text('Abbrechen', style: TextStyle(color: pt.fgMuted)), ), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: pt.danger, foregroundColor: Colors.white, elevation: 0, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8))), onPressed: () => Navigator.pop(ctx, true), child: const Text('Abmelden'), ), ], ), ); if (confirm == true) { unawaited(AuthLog.write('User-initiated logout (Settings)')); await client.logout().catchError((_) {}); } } } class _UpdatesSettingsGroup extends ConsumerStatefulWidget { final PyramidTheme pt; const _UpdatesSettingsGroup({required this.pt}); @override ConsumerState<_UpdatesSettingsGroup> createState() => _UpdatesSettingsGroupState(); } class _UpdatesSettingsGroupState extends ConsumerState<_UpdatesSettingsGroup> { bool _checking = false; Future _checkNow() async { setState(() => _checking = true); await resetUpdateCheckTimer(); ref.invalidate(updateInfoProvider); // Give the async provider a moment to resolve before resetting the spinner. await Future.delayed(const Duration(seconds: 2)); if (mounted) setState(() => _checking = false); } @override Widget build(BuildContext context) { final pt = widget.pt; final updateAsync = ref.watch(updateInfoProvider); return _SettingsGroup(title: 'Updates', pt: pt, children: [ updateAsync.when( loading: () => _SettingsRow( title: 'Nach Updates suchen…', desc: 'Gitea Releases wird geprüft', pt: pt, control: SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, color: pt.accent, ), ), ), error: (e, _) => _SettingsRow( title: 'Update-Prüfung fehlgeschlagen', desc: 'Keine Verbindung oder Gitea nicht erreichbar (nur im Heimnetz).', pt: pt, control: _checking ? SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, color: pt.accent), ) : GestureDetector( onTap: _checkNow, child: Tooltip( message: 'Erneut prüfen', child: Icon(Icons.refresh_rounded, size: 16, color: pt.fgDim), ), ), ), data: (info) { if (info == null) { return _SettingsRow( title: 'Pyramid ist aktuell', desc: 'Du verwendest die neueste Version.', pt: pt, onTap: _checking ? null : _checkNow, control: _checking ? SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, color: pt.accent), ) : Icon(Icons.refresh_rounded, size: 16, color: pt.fgDim), ); } // New version available return Column( children: [ _SettingsRow( title: 'Update verfügbar: ${info.tagName}', desc: info.releaseName, pt: pt, showArrow: true, onTap: () async { if (info.canDownload) { await showUpdateDownloadDialog(context, info); } else { final uri = Uri.parse(info.htmlUrl); if (await canLaunchUrl(uri)) { await launchUrl(uri, mode: LaunchMode.externalApplication); } } }, control: Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3), decoration: BoxDecoration( color: pt.accent, borderRadius: BorderRadius.circular(6)), child: Text( 'Neu', style: TextStyle( color: pt.accentFg, fontSize: 10, fontWeight: FontWeight.w700), ), ), ), _Divider(pt: pt), _SettingsRow( title: 'Diese Version überspringen', desc: '${info.tagName} nicht mehr anzeigen', pt: pt, onTap: () async { await skipUpdateVersion(info.tagName); ref.invalidate(updateInfoProvider); }, ), ], ); }, ), ]); } }