refactor: Gott-Datei message_group.dart in 7 Dateien aufgeteilt
Gleiche Technik wie bei settings_modal.dart: reine Verschiebung per Dart part/part-of, keine Logikänderung. message_group.dart schrumpft von 2775 auf 30 Zeilen (nur noch Bibliothekskopf). Verifiziert per automatisiertem Blockvergleich gegen das Original und flutter analyze (Baseline unverändert). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
part of '../message_group.dart';
|
||||
|
||||
class _Avatar extends ConsumerStatefulWidget {
|
||||
final String initial;
|
||||
final Color color;
|
||||
final PyramidTheme pt;
|
||||
final double size;
|
||||
final Uri? avatarUri;
|
||||
|
||||
const _Avatar({
|
||||
required this.initial,
|
||||
required this.color,
|
||||
required this.pt,
|
||||
required this.size,
|
||||
this.avatarUri,
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<_Avatar> createState() => _AvatarState();
|
||||
}
|
||||
|
||||
class _AvatarState extends ConsumerState<_Avatar> {
|
||||
bool _hovered = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final client = ref.watch(matrixClientProvider).valueOrNull;
|
||||
final radius = BorderRadius.circular(widget.size / 2);
|
||||
|
||||
Widget child;
|
||||
if (client != null && widget.avatarUri != null) {
|
||||
child = MxcAvatar(
|
||||
mxcUri: widget.avatarUri,
|
||||
client: client,
|
||||
size: widget.size,
|
||||
borderRadius: radius,
|
||||
placeholder: (_) => _InitialBox(
|
||||
initial: widget.initial,
|
||||
color: widget.color,
|
||||
size: widget.size,
|
||||
radius: radius,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
child = _InitialBox(
|
||||
initial: widget.initial,
|
||||
color: widget.color,
|
||||
size: widget.size,
|
||||
radius: radius,
|
||||
);
|
||||
}
|
||||
|
||||
return MouseRegion(
|
||||
cursor: SystemMouseCursors.click,
|
||||
onEnter: (_) => setState(() => _hovered = true),
|
||||
onExit: (_) => setState(() => _hovered = false),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 180),
|
||||
curve: Curves.easeOutBack,
|
||||
transform: _hovered
|
||||
? Matrix4.diagonal3Values(1.06, 1.06, 1.0)
|
||||
: Matrix4.identity(),
|
||||
transformAlignment: Alignment.center,
|
||||
width: widget.size,
|
||||
height: widget.size,
|
||||
child: child,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _InitialBox extends StatelessWidget {
|
||||
final String initial;
|
||||
final Color color;
|
||||
final double size;
|
||||
final BorderRadius radius;
|
||||
const _InitialBox({required this.initial, required this.color, required this.size, required this.radius});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
width: size,
|
||||
height: size,
|
||||
decoration: BoxDecoration(color: color, borderRadius: radius),
|
||||
child: Center(
|
||||
child: Text(initial, style: const TextStyle(color: Colors.white, fontSize: 14, fontWeight: FontWeight.w600)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _InlineUnreadDivider extends StatelessWidget {
|
||||
final PyramidTheme pt;
|
||||
const _InlineUnreadDivider({required this.pt});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(0, 8, 0, 4),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(child: Divider(color: pt.accent.withAlpha(160), height: 1)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10),
|
||||
child: Text(
|
||||
'Neue Nachrichten',
|
||||
style: TextStyle(color: pt.accent, fontSize: 11, fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
Expanded(child: Divider(color: pt.accent.withAlpha(160), height: 1)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class DateDivider extends StatelessWidget {
|
||||
final DateTime date;
|
||||
|
||||
const DateDivider({super.key, required this.date});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final pt = PyramidTheme.of(context);
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(20, 16, 20, 8),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(child: Divider(color: pt.border, height: 1)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12),
|
||||
child: Text(
|
||||
_formatDate(date),
|
||||
style: TextStyle(
|
||||
color: pt.fgDim,
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
letterSpacing: 0.06 * 11,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(child: Divider(color: pt.border, height: 1)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _formatDate(DateTime d) {
|
||||
final now = DateTime.now();
|
||||
if (d.year == now.year && d.month == now.month && d.day == now.day) {
|
||||
return 'Heute · ${d.day}. ${_monthName(d.month)}';
|
||||
}
|
||||
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}. ${_monthName(d.month)} ${d.year}';
|
||||
}
|
||||
|
||||
String _monthName(int m) => const [
|
||||
'', 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni',
|
||||
'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'
|
||||
][m];
|
||||
}
|
||||
|
||||
class _MessageStatus extends StatelessWidget {
|
||||
final Event lastEvent;
|
||||
final PyramidTheme pt;
|
||||
|
||||
const _MessageStatus({required this.lastEvent, required this.pt});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final status = lastEvent.status;
|
||||
if (status == EventStatus.error) {
|
||||
return Icon(Icons.error_outline_rounded, size: 12, color: pt.danger);
|
||||
}
|
||||
if (status == EventStatus.sending) {
|
||||
return Icon(Icons.access_time_rounded, size: 12, color: pt.fgDim);
|
||||
}
|
||||
// Sent (delivered). "Read" is shown by the per-event ✓✓ marker.
|
||||
return Icon(Icons.done_rounded, size: 13, color: pt.fgDim);
|
||||
}
|
||||
}
|
||||
|
||||
class _ReadMarker extends StatelessWidget {
|
||||
final PyramidTheme pt;
|
||||
const _ReadMarker({required this.pt});
|
||||
|
||||
// Goldener Doppelhaken — bewusst nicht die Akzentfarbe, damit "gelesen"
|
||||
// unabhängig vom Theme sofort erkennbar ist.
|
||||
static const _gold = Color(0xFFE6B422);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.only(right: 24, top: 1, bottom: 1),
|
||||
child: Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: Icon(Icons.done_all_rounded, size: 13, color: _gold),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user