From 86b1314c61f12f0ce102d98df7941d927de2653d Mon Sep 17 00:00:00 2001 From: Krille Date: Wed, 17 Jul 2024 10:27:56 +0200 Subject: [PATCH] refactor: Use cached network image for mxc image uris --- lib/pages/chat/events/html_message.dart | 1 - lib/widgets/avatar.dart | 16 ++-- lib/widgets/mxc_image.dart | 108 ++++++++---------------- pubspec.lock | 32 +++++++ pubspec.yaml | 1 + 5 files changed, 75 insertions(+), 83 deletions(-) diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index d6e6cbc44..4d5bcdc8c 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -280,7 +280,6 @@ class ImageExtension extends HtmlExtension { uri: mxcUrl, width: width ?? height ?? defaultDimension, height: height ?? width ?? defaultDimension, - cacheKey: mxcUrl.toString(), ), ), ); diff --git a/lib/widgets/avatar.dart b/lib/widgets/avatar.dart index 0c280a88b..cdb7b2e7a 100644 --- a/lib/widgets/avatar.dart +++ b/lib/widgets/avatar.dart @@ -47,29 +47,32 @@ class Avatar extends StatelessWidget { final noPic = mxContent == null || mxContent.toString().isEmpty || mxContent.toString() == 'null'; - final textWidget = Center( + final textWidget = Container( + color: name?.lightColorAvatar, + alignment: Alignment.center, child: Text( fallbackLetters, style: TextStyle( - color: noPic ? Colors.white : null, + color: Colors.white, fontSize: (size / 2.5).roundToDouble(), ), ), ); final borderRadius = this.borderRadius ?? BorderRadius.circular(size / 2); final presenceUserId = this.presenceUserId; - final color = - noPic ? name?.lightColorAvatar : Theme.of(context).secondaryHeaderColor; final container = Stack( children: [ SizedBox( width: size, height: size, child: Material( - color: color, shape: RoundedRectangleBorder( borderRadius: borderRadius, - side: border ?? BorderSide.none, + side: border ?? + BorderSide( + color: Theme.of(context).dividerColor, + width: 1, + ), ), clipBehavior: Clip.hardEdge, child: noPic @@ -81,7 +84,6 @@ class Avatar extends StatelessWidget { width: size, height: size, placeholder: (_) => textWidget, - cacheKey: mxContent.toString(), ), ), ), diff --git a/lib/widgets/mxc_image.dart b/lib/widgets/mxc_image.dart index 9290156bf..b12c9fc99 100644 --- a/lib/widgets/mxc_image.dart +++ b/lib/widgets/mxc_image.dart @@ -2,7 +2,7 @@ import 'dart:typed_data'; import 'package:flutter/material.dart'; -import 'package:http/http.dart' as http; +import 'package:cached_network_image/cached_network_image.dart'; import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/themes.dart'; @@ -18,11 +18,8 @@ class MxcImage extends StatefulWidget { final bool isThumbnail; final bool animated; final Duration retryDuration; - final Duration animationDuration; - final Curve animationCurve; final ThumbnailMethod thumbnailMethod; final Widget Function(BuildContext context)? placeholder; - final String? cacheKey; const MxcImage({ this.uri, @@ -33,11 +30,8 @@ class MxcImage extends StatefulWidget { this.placeholder, this.isThumbnail = true, this.animated = false, - this.animationDuration = FluffyThemes.animationDuration, this.retryDuration = const Duration(seconds: 2), - this.animationCurve = FluffyThemes.animationCurve, this.thumbnailMethod = ThumbnailMethod.scale, - this.cacheKey, super.key, }); @@ -46,76 +40,11 @@ class MxcImage extends StatefulWidget { } class _MxcImageState extends State { - static final Map _imageDataCache = {}; - Uint8List? _imageDataNoCache; - Uint8List? get _imageData { - final cacheKey = widget.cacheKey; - return cacheKey == null ? _imageDataNoCache : _imageDataCache[cacheKey]; - } - - set _imageData(Uint8List? data) { - if (data == null) return; - final cacheKey = widget.cacheKey; - cacheKey == null - ? _imageDataNoCache = data - : _imageDataCache[cacheKey] = data; - } - - bool? _isCached; + Uint8List? _imageData; Future _load() async { - final client = Matrix.of(context).client; - final uri = widget.uri; final event = widget.event; - if (uri != null) { - final devicePixelRatio = MediaQuery.of(context).devicePixelRatio; - final width = widget.width; - final realWidth = width == null ? null : width * devicePixelRatio; - final height = widget.height; - final realHeight = height == null ? null : height * devicePixelRatio; - - final httpUri = widget.isThumbnail - ? uri.getThumbnail( - client, - width: realWidth, - height: realHeight, - animated: widget.animated, - method: widget.thumbnailMethod, - ) - : uri.getDownloadLink(client); - - final storeKey = widget.isThumbnail ? httpUri : uri; - - if (_isCached == null) { - final cachedData = await client.database?.getFile(storeKey); - if (cachedData != null) { - if (!mounted) return; - setState(() { - _imageData = cachedData; - _isCached = true; - }); - return; - } - _isCached = false; - } - - final response = await http.get(httpUri); - if (response.statusCode != 200) { - if (response.statusCode == 404) { - return; - } - throw Exception(); - } - final remoteData = response.bodyBytes; - - if (!mounted) return; - setState(() { - _imageData = remoteData; - }); - await client.database?.storeFile(storeKey, remoteData, 0); - } - if (event != null) { final data = await event.downloadAndDecryptAttachment( getThumbnail: widget.isThumbnail, @@ -131,7 +60,7 @@ class _MxcImageState extends State { } void _tryLoad(_) async { - if (_imageData != null) { + if (_imageData != null || widget.event == null) { return; } try { @@ -160,6 +89,36 @@ class _MxcImageState extends State { @override Widget build(BuildContext context) { + final uri = widget.uri; + + if (uri != null) { + final client = Matrix.of(context).client; + final devicePixelRatio = MediaQuery.of(context).devicePixelRatio; + final width = widget.width; + final realWidth = width == null ? null : width * devicePixelRatio; + final height = widget.height; + final realHeight = height == null ? null : height * devicePixelRatio; + + final httpUri = widget.isThumbnail + ? uri.getThumbnail( + client, + width: realWidth, + height: realHeight, + animated: widget.animated, + method: widget.thumbnailMethod, + ) + : uri.getDownloadLink(client); + + return CachedNetworkImage( + imageUrl: httpUri.toString(), + width: width, + height: height, + fit: widget.fit, + placeholder: (context, _) => placeholder(context), + errorWidget: (context, _, __) => placeholder(context), + ); + } + final data = _imageData; final hasData = data != null && data.isNotEmpty; @@ -177,7 +136,6 @@ class _MxcImageState extends State { filterQuality: widget.isThumbnail ? FilterQuality.low : FilterQuality.medium, errorBuilder: (context, __, ___) { - _isCached = false; _imageData = null; WidgetsBinding.instance.addPostFrameCallback(_tryLoad); return placeholder(context); diff --git a/pubspec.lock b/pubspec.lock index 0ff035c1b..18e927b9f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -129,6 +129,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + cached_network_image: + dependency: "direct main" + description: + name: cached_network_image + sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" + url: "https://pub.dev" + source: hosted + version: "3.3.1" + cached_network_image_platform_interface: + dependency: transitive + description: + name: cached_network_image_platform_interface + sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f" + url: "https://pub.dev" + source: hosted + version: "4.0.0" + cached_network_image_web: + dependency: transitive + description: + name: cached_network_image_web + sha256: "205d6a9f1862de34b93184f22b9d2d94586b2f05c581d546695e3d8f6a805cd7" + url: "https://pub.dev" + source: hosted + version: "1.2.0" callkeep: dependency: "direct main" description: @@ -1270,6 +1294,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" + octo_image: + dependency: transitive + description: + name: octo_image + sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d" + url: "https://pub.dev" + source: hosted + version: "2.0.0" olm: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 36a37e8c4..4d9d028d2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,6 +14,7 @@ dependencies: async: ^2.11.0 badges: ^3.1.2 blurhash_dart: ^1.2.1 + cached_network_image: ^3.3.1 callkeep: ^0.3.2 chewie: ^1.8.1 collection: ^1.18.0