implemented chat long-polling and optimistic updates, centralized notification management, optimized avatar caching
This commit is contained in:
+57
-47
@@ -29,15 +29,18 @@ class _AvatarPayload {
|
||||
_AvatarPayload(this.bytes, this.isSvg);
|
||||
}
|
||||
|
||||
final Map<String, Future<_AvatarPayload?>> _avatarCache = {};
|
||||
// Resolved payloads are cached so re-mounts render synchronously; in-flight
|
||||
// requests are deduped so concurrent mounts share one HTTP call.
|
||||
final Map<String, _AvatarPayload?> _resolvedAvatars = {};
|
||||
final Map<String, Future<_AvatarPayload?>> _pendingAvatars = {};
|
||||
|
||||
class _UserAvatarState extends State<UserAvatar> {
|
||||
late Future<_AvatarPayload?> _payload;
|
||||
_AvatarPayload? _payload;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_payload = _load();
|
||||
_attach();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -46,7 +49,7 @@ class _UserAvatarState extends State<UserAvatar> {
|
||||
if (oldWidget.id != widget.id ||
|
||||
oldWidget.isGroup != widget.isGroup ||
|
||||
oldWidget.size != widget.size) {
|
||||
_payload = _load();
|
||||
_attach();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,9 +61,20 @@ class _UserAvatarState extends State<UserAvatar> {
|
||||
return 'https://$host/avatar/${widget.id}/${widget.size}';
|
||||
}
|
||||
|
||||
Future<_AvatarPayload?> _load() {
|
||||
void _attach() {
|
||||
final url = _url();
|
||||
return _avatarCache.putIfAbsent(url, () => _fetch(url));
|
||||
if (_resolvedAvatars.containsKey(url)) {
|
||||
_payload = _resolvedAvatars[url];
|
||||
return;
|
||||
}
|
||||
_payload = null;
|
||||
final pending = _pendingAvatars.putIfAbsent(url, () => _fetch(url));
|
||||
pending.then((p) {
|
||||
_resolvedAvatars[url] = p;
|
||||
_pendingAvatars.remove(url);
|
||||
if (!mounted || _url() != url) return;
|
||||
setState(() => _payload = p);
|
||||
});
|
||||
}
|
||||
|
||||
Future<_AvatarPayload?> _fetch(String url) async {
|
||||
@@ -97,49 +111,45 @@ class _UserAvatarState extends State<UserAvatar> {
|
||||
Widget build(BuildContext context) {
|
||||
final radius = widget.size.toDouble();
|
||||
final theme = Theme.of(context);
|
||||
final payload = _payload;
|
||||
|
||||
return FutureBuilder<_AvatarPayload?>(
|
||||
future: _payload,
|
||||
builder: (context, snapshot) {
|
||||
final payload = snapshot.data;
|
||||
|
||||
Widget content;
|
||||
if (payload == null) {
|
||||
content = Icon(
|
||||
widget.isGroup ? Icons.group : Icons.person,
|
||||
size: radius,
|
||||
color: Colors.white,
|
||||
);
|
||||
} else if (payload.isSvg) {
|
||||
content = SvgPicture.memory(
|
||||
payload.bytes,
|
||||
width: radius * 2,
|
||||
height: radius * 2,
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
} else {
|
||||
content = Image.memory(
|
||||
payload.bytes,
|
||||
width: radius * 2,
|
||||
height: radius * 2,
|
||||
fit: BoxFit.cover,
|
||||
gaplessPlayback: true,
|
||||
);
|
||||
}
|
||||
|
||||
return CircleAvatar(
|
||||
radius: radius,
|
||||
backgroundColor: theme.primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
child: ClipOval(
|
||||
child: SizedBox(
|
||||
width: radius * 2,
|
||||
height: radius * 2,
|
||||
child: content,
|
||||
),
|
||||
),
|
||||
Widget content;
|
||||
if (payload != null) {
|
||||
if (payload.isSvg) {
|
||||
content = SvgPicture.memory(
|
||||
payload.bytes,
|
||||
width: radius * 2,
|
||||
height: radius * 2,
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
},
|
||||
} else {
|
||||
content = Image.memory(
|
||||
payload.bytes,
|
||||
width: radius * 2,
|
||||
height: radius * 2,
|
||||
fit: BoxFit.cover,
|
||||
gaplessPlayback: true,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
content = Icon(
|
||||
widget.isGroup ? Icons.group : Icons.person,
|
||||
size: radius,
|
||||
color: Colors.white,
|
||||
);
|
||||
}
|
||||
|
||||
return CircleAvatar(
|
||||
radius: radius,
|
||||
backgroundColor: theme.primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
child: ClipOval(
|
||||
child: SizedBox(
|
||||
width: radius * 2,
|
||||
height: radius * 2,
|
||||
child: content,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user