Initial commit: watch-together phases 0-4 + presence hardening, internet-tested

This commit is contained in:
foxyz committed 2026-09-12 00:13:43 +03:00
commit 427d851a5b
101 files changed
+11326

No files matched your search

@@ -0,0 +1,65 @@
import 'dart:convert';
import 'package:flutter/material.dart';
class MemberAvatar extends StatelessWidget {
final String avatarBase64;
final String name;
final double size;
static final Map<String, MemoryImage> _images = {};
const MemberAvatar({
super.key,
required this.avatarBase64,
required this.name,
this.size = 40,
});
static MemoryImage? _providerFor(String raw) {
if (raw.isEmpty) return null;
var hit = _images[raw];
if (hit != null) return hit;
try {
hit = MemoryImage(base64Decode(raw));
} catch (_) {
return null;
}
if (_images.length > 64) _images.clear();
_images[raw] = hit;
return hit;
}
@override
Widget build(BuildContext context) {
final image = _providerFor(avatarBase64);
final decoration = BoxDecoration(
shape: BoxShape.circle,
);
if (image != null) {
return Container(
decoration: decoration,
child: ClipOval(
child: Image(
image: ResizeImage(height: (size * MediaQuery.of(context).devicePixelRatio).round(), image),
width: size,
height: size,
fit: BoxFit.cover,
gaplessPlayback: true,
errorBuilder: (_, __, ___) => _fallback(),
),
),
);
}
return Container(decoration: decoration, child: _fallback());
}
Widget _fallback() => CircleAvatar(
radius: size / 2,
backgroundColor: Colors.grey.shade800,
child: Text(
name.isEmpty ? '?' : name[0].toUpperCase(),
style: TextStyle(fontSize: size * 0.4, color: Colors.white),
),
);
}