5d772389d6
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>
266 lines
9.3 KiB
Dart
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);
|
|
},
|
|
),
|
|
],
|
|
);
|
|
},
|
|
),
|
|
]);
|
|
}
|
|
}
|