Files
pyramid/lib/widgets/settings/settings_about.dart
T
Bernd Steckmeister 5d772389d6 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>
2026-07-03 10:56:48 +02:00

266 lines
9.3 KiB
Dart

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<void> _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<bool>(
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<void> _checkNow() async {
setState(() => _checking = true);
await resetUpdateCheckTimer();
ref.invalidate(updateInfoProvider);
// Give the async provider a moment to resolve before resetting the spinner.
await Future<void>.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);
},
),
],
);
},
),
]);
}
}