Critical: - Fix MXC URI resolution: all avatars/images now resolve mxc:// to HTTP - Sync persistence: only write changed rooms, batch message upserts - lastActivityAt uses room.lastEvent.originServerTs, not creation time High: - Shared MatrixAvatar widget replaces 6 duplicate implementations - CallScreen decodes roomId before LiveKit JWT fetch - Decline button actually dismisses incoming call overlay - EventTypes constants replace raw string literals - LiveKitService uses lazy auth reads, onDispose disconnects Medium: - CallController is keepAlive with timer/room cleanup - authRepository is keepAlive (used from keepAlive notifier) - StreamController not closed in stopListening (crash fix) - Index on messages.roomId for query performance - 400ms debounce on user search - Static DateFormat in MessageBubble - Hardcoded strings replaced with AppConfig refs - Duplicate isDirectMessage field removed from RoomModel - E2EE profile claim corrected to Phase 3 Shared utilities: - lib/shared/widgets/matrix_avatar.dart - lib/shared/utils/mxc_url.dart - lib/shared/utils/room_preview.dart - lib/shared/utils/matrix_id.dart rawJson column removed (unused, caused main-thread jsonEncode) Schema migrated to v2 with roomId index. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
55 lines
1.5 KiB
Dart
55 lines
1.5 KiB
Dart
// Version: 1.0.0 | Created: 2026-04-02
|
|
// Shared avatar widget used throughout the app. Displays a cached network
|
|
// image when an HTTP avatar URL is available, or falls back to a coloured
|
|
// circle with the first letter of the display name.
|
|
//
|
|
// The [avatarUrl] MUST be a resolved HTTP URL — never pass an mxc:// URI.
|
|
|
|
import 'package:cached_network_image/cached_network_image.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
class MatrixAvatar extends StatelessWidget {
|
|
const MatrixAvatar({
|
|
super.key,
|
|
required this.name,
|
|
this.avatarUrl,
|
|
this.radius = 20,
|
|
});
|
|
|
|
/// Display name used for the initials fallback.
|
|
final String name;
|
|
|
|
/// Resolved HTTP URL for the avatar image. Must NOT be an mxc:// URI.
|
|
final String? avatarUrl;
|
|
|
|
/// Radius of the [CircleAvatar].
|
|
final double radius;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
final initials = name.isNotEmpty ? name[0].toUpperCase() : '?';
|
|
|
|
if (avatarUrl != null) {
|
|
return CircleAvatar(
|
|
radius: radius,
|
|
backgroundImage: CachedNetworkImageProvider(avatarUrl!),
|
|
backgroundColor: theme.colorScheme.surfaceContainerHighest,
|
|
);
|
|
}
|
|
|
|
return CircleAvatar(
|
|
radius: radius,
|
|
backgroundColor: theme.colorScheme.primary.withAlpha(51),
|
|
child: Text(
|
|
initials,
|
|
style: TextStyle(
|
|
fontSize: radius * 0.7,
|
|
fontWeight: FontWeight.bold,
|
|
color: theme.colorScheme.primary,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|