From 6400fb639ce531fa3291be76e44b50e2029f93e8 Mon Sep 17 00:00:00 2001 From: Bernd Steckmeister Date: Fri, 3 Jul 2026 10:29:54 +0200 Subject: [PATCH] =?UTF-8?q?refactor:=20toten=20Code=20entfernen,=20doppelt?= =?UTF-8?q?e=20Zeit-Formatierung=20zusammenf=C3=BChren?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit message_bubble.dart war vollständig unbenutzt (durch message_group.dart abgelöst). Die zwei identischen _formatTime-Methoden in message_group.dart zu einer Top-Level-Funktion zusammengeführt - reine Extraktion, keine Verhaltensänderung. flutter analyze bleibt sauber. --- CHANGES.md | 13 ++ PROGRESS.md | 31 +++++ ROADMAP.md | 3 + lib/features/chat/message_bubble.dart | 178 -------------------------- lib/features/chat/message_group.dart | 21 ++- 5 files changed, 55 insertions(+), 191 deletions(-) delete mode 100644 lib/features/chat/message_bubble.dart diff --git a/CHANGES.md b/CHANGES.md index c42f5fc..19b3a85 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1146,3 +1146,16 @@ 2026-07-03 09:58 [Edit] /home/steggi/pyramid/lib/widgets/settings_modal.dart 2026-07-03 09:59 [Edit] /home/steggi/pyramid/ROADMAP.md 2026-07-03 10:00 [Edit] /home/steggi/pyramid/PROGRESS.md +2026-07-03 10:24 [Edit] /home/steggi/pyramid/PROGRESS.md +2026-07-03 10:25 [Edit] /home/steggi/pyramid/ROADMAP.md +2026-07-03 10:26 [Write] /home/steggi/pyramid/docs/M2_MODULE_SCHNITT.md +2026-07-03 10:26 [Edit] /home/steggi/pyramid/PROGRESS.md +2026-07-03 10:27 [Edit] /home/steggi/pyramid/PROGRESS.md +2026-07-03 10:27 [Edit] /home/steggi/pyramid/ROADMAP.md +2026-07-03 10:28 [Edit] /home/steggi/pyramid/lib/features/chat/message_group.dart +2026-07-03 10:28 [Edit] /home/steggi/pyramid/lib/features/chat/message_group.dart +2026-07-03 10:28 [Edit] /home/steggi/pyramid/lib/features/chat/message_group.dart +2026-07-03 10:29 [Edit] /home/steggi/pyramid/lib/features/chat/message_group.dart +2026-07-03 10:29 [Edit] /home/steggi/pyramid/lib/features/chat/message_group.dart +2026-07-03 10:29 [Edit] /home/steggi/pyramid/PROGRESS.md +2026-07-03 10:29 [Edit] /home/steggi/pyramid/ROADMAP.md diff --git a/PROGRESS.md b/PROGRESS.md index e318e18..a8a5c7b 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -13,6 +13,37 @@ Schritt (und beim Abbruch mitten im Schritt den Zwischenstand). Format: --- +## 2026-07-03 – M2: Toten Code & Duplikate entfernt (Chat-Timeline) + +**Erledigt:** Vorgriff auf den ROADMAP-Punkt „Toten Code & Duplikate entfernen" +(technisch der 5. Punkt in M2, aber risikofrei und ohne GUI testbar – im +Gegensatz zum aktuell blockierten „Call-Schicht entwirren", siehe Frage an +Bernd im Eintrag darunter): +- `lib/features/chat/message_bubble.dart` (178 Zeilen) gelöscht – vollständig + toter Code, wurde nirgends importiert/instanziiert (durch `message_group.dart` + abgelöst), per `grep` doppelt bestätigt. +- Die zwei identischen `_formatTime`-Methoden in `message_group.dart` + (`_MessageGroupState` Zeile 322, `_ContinuationMessageState` Zeile 475 – + Vorher-Zeilennummern) zu einer gemeinsamen Top-Level-Funktion + `_formatMessageTime()` zusammengeführt, beide Aufrufstellen umgestellt. + Reine Extraktion, keine Verhaltensänderung (identischer Code vorher/nachher). + `pinned_messages_panel.dart:_formatTime` bewusst NICHT angefasst – hat + andere Logik (relatives Datum „vor Xd"), ist kein echtes Duplikat. +- `flutter analyze` weiterhin sauber: nur der eine bekannte, bewusst + zurückgestellte Hinweis (`chat_provider.dart:42`, `Timeline.onUpdate` + deprecated, hängt am E2EE-Redecrypt-Pfad, siehe M0-Eintrag). + +**Offen/Nächster Schritt:** UNGETESTET (Pi) im Sinne von „nicht in echter UI +gesehen" – kein GUI auf diesem Pi verfügbar. Da es sich aber um reine +Code-Verschiebung ohne Logikänderung handelt (identischer Funktionskörper), +ist das Risiko gering; trotzdem beim nächsten Windows-Lauf kurz einen +Chat mit mehreren aufeinanderfolgenden Nachrichten (Zeit-Anzeige) und eine +Fortsetzungsnachricht (Hover zeigt Uhrzeit) ansehen. + +**Stolperfallen:** Keine. + +--- + ## 2026-07-03 – M2: Modul-Schnitt definiert (docs/M2_MODULE_SCHNITT.md) **Erledigt:** Ausgehend von der Ist-Analyse (Eintrag darunter) die konkreten diff --git a/ROADMAP.md b/ROADMAP.md index 83efb0e..9b6ba90 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -76,6 +76,9 @@ lassen, ohne dass andere Module angefasst werden müssen. LiveKit-Verbindung / UI-State sauber getrennt) - [ ] Einheitliches State-Management (Riverpod konsequent, keine Misch-Patterns) - [ ] Toten Code & Duplikate entfernen, Ordnerstruktur vereinheitlichen + (angefangen, PROGRESS.md 2026-07-03: `message_bubble.dart` tot entfernt, + doppelte `_formatTime` in `message_group.dart` zusammengeführt – weitere + Fundstellen aus der Ist-Analyse noch offen, Ordnerstruktur noch nicht angefasst) - [ ] Nach JEDEM Schritt: App läuft, Kernflows getestet, Commit ## M3 – Calls: Logik, UI & Audio (Discord als Referenz) diff --git a/lib/features/chat/message_bubble.dart b/lib/features/chat/message_bubble.dart deleted file mode 100644 index 7e2f49a..0000000 --- a/lib/features/chat/message_bubble.dart +++ /dev/null @@ -1,178 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:matrix/matrix.dart'; - -class MessageBubble extends StatelessWidget { - final Event event; - final bool isOwn; - final Event? replyEvent; - - const MessageBubble({ - super.key, - required this.event, - required this.isOwn, - this.replyEvent, - }); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - final scheme = theme.colorScheme; - - final bgColor = isOwn ? scheme.primaryContainer : scheme.surfaceContainerHigh; - final textColor = isOwn ? scheme.onPrimaryContainer : scheme.onSurface; - - return Align( - alignment: isOwn ? Alignment.centerRight : Alignment.centerLeft, - child: ConstrainedBox( - constraints: BoxConstraints( - maxWidth: MediaQuery.sizeOf(context).width * 0.75, - ), - child: Container( - margin: EdgeInsets.only( - left: isOwn ? 48 : 8, - right: isOwn ? 8 : 48, - top: 2, - bottom: 2, - ), - padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), - decoration: BoxDecoration( - color: bgColor, - borderRadius: BorderRadius.only( - topLeft: const Radius.circular(16), - topRight: const Radius.circular(16), - bottomLeft: Radius.circular(isOwn ? 16 : 4), - bottomRight: Radius.circular(isOwn ? 4 : 16), - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (!isOwn) ...[ - Text( - event.senderFromMemoryOrFallback.calcDisplayname(), - style: theme.textTheme.labelSmall?.copyWith( - color: scheme.primary, - fontWeight: FontWeight.bold, - ), - ), - const SizedBox(height: 2), - ], - if (replyEvent != null) _ReplyPreview(event: replyEvent!), - _MessageContent(event: event, textColor: textColor), - const SizedBox(height: 2), - Align( - alignment: Alignment.bottomRight, - child: Text( - _formatTime(event.originServerTs), - style: theme.textTheme.labelSmall?.copyWith( - color: textColor.withAlpha(160), - fontSize: 10, - ), - ), - ), - ], - ), - ), - ), - ); - } - - String _formatTime(DateTime t) => - '${t.hour.toString().padLeft(2, '0')}:${t.minute.toString().padLeft(2, '0')}'; -} - -class _MessageContent extends StatelessWidget { - final Event event; - final Color textColor; - const _MessageContent({required this.event, required this.textColor}); - - @override - Widget build(BuildContext context) { - final body = event.body; - return Text( - body, - style: Theme.of(context).textTheme.bodyMedium?.copyWith(color: textColor), - ); - } -} - -class _ReplyPreview extends StatelessWidget { - final Event event; - const _ReplyPreview({required this.event}); - - @override - Widget build(BuildContext context) { - final theme = Theme.of(context); - return Container( - margin: const EdgeInsets.only(bottom: 6), - padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), - decoration: BoxDecoration( - border: Border( - left: BorderSide(color: theme.colorScheme.primary, width: 3), - ), - color: theme.colorScheme.surface.withAlpha(120), - borderRadius: const BorderRadius.only( - topRight: Radius.circular(4), - bottomRight: Radius.circular(4), - ), - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - event.senderFromMemoryOrFallback.calcDisplayname(), - style: theme.textTheme.labelSmall?.copyWith( - color: theme.colorScheme.primary, - fontWeight: FontWeight.bold, - ), - ), - Text( - event.body, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: theme.textTheme.bodySmall, - ), - ], - ), - ); - } -} - -class DateSeparator extends StatelessWidget { - final DateTime date; - const DateSeparator({super.key, required this.date}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8), - child: Row( - children: [ - const Expanded(child: Divider()), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 12), - child: Text( - _formatDate(date), - style: Theme.of(context).textTheme.labelSmall, - ), - ), - const Expanded(child: Divider()), - ], - ), - ); - } - - String _formatDate(DateTime d) { - final now = DateTime.now(); - if (d.year == now.year && d.month == now.month && d.day == now.day) { - return 'Heute'; - } - final yesterday = now.subtract(const Duration(days: 1)); - if (d.year == yesterday.year && - d.month == yesterday.month && - d.day == yesterday.day) { - return 'Gestern'; - } - return '${d.day.toString().padLeft(2, '0')}.${d.month.toString().padLeft(2, '0')}.${d.year}'; - } -} diff --git a/lib/features/chat/message_group.dart b/lib/features/chat/message_group.dart index c55de5d..1586e74 100644 --- a/lib/features/chat/message_group.dart +++ b/lib/features/chat/message_group.dart @@ -21,6 +21,12 @@ import 'package:pyramid/utils/gif_favorite_service.dart'; import 'package:pyramid/widgets/mxc_image.dart'; import 'package:url_launcher/url_launcher.dart'; +String _formatMessageTime(DateTime t) { + final h = t.hour.toString().padLeft(2, '0'); + final m = t.minute.toString().padLeft(2, '0'); + return '$h:$m'; +} + // Wraps a group of consecutive messages from the same sender class MessageGroup extends StatefulWidget { final List events; @@ -85,7 +91,7 @@ class _MessageGroupState extends State { final name = sender.calcDisplayname(); final initial = name.isNotEmpty ? name[0].toUpperCase() : '?'; final color = _colorFromId(firstEvent.senderId); - final time = _formatTime(firstEvent.originServerTs); + final time = _formatMessageTime(firstEvent.originServerTs); final avatarUri = sender.avatarUrl; // Build a FLAT column of rows — each message at the same indentation level. @@ -318,12 +324,6 @@ class _MessageGroupState extends State { if (id.isEmpty) return colors[0]; return colors[id.codeUnitAt(0) % colors.length]; } - - String _formatTime(DateTime t) { - final h = t.hour.toString().padLeft(2, '0'); - final m = t.minute.toString().padLeft(2, '0'); - return '$h:$m'; - } } // Shown under a message whose send failed (e.g. offline) — keeps the message @@ -439,7 +439,7 @@ class _ContinuationMessageState extends State<_ContinuationMessage> { opacity: _hovered ? 1 : 0, duration: const Duration(milliseconds: 120), child: Text( - _formatTime(widget.event.originServerTs), + _formatMessageTime(widget.event.originServerTs), textAlign: TextAlign.center, style: TextStyle( color: widget.pt.fgDim, @@ -472,11 +472,6 @@ class _ContinuationMessageState extends State<_ContinuationMessage> { ); } - String _formatTime(DateTime t) { - final h = t.hour.toString().padLeft(2, '0'); - final m = t.minute.toString().padLeft(2, '0'); - return '$h:$m'; - } } class _MessageContent extends StatelessWidget {