diff --git a/packages/neon/neon/lib/src/pages/home.dart b/packages/neon/neon/lib/src/pages/home.dart index a0e42a70..f54f6a7e 100644 --- a/packages/neon/neon/lib/src/pages/home.dart +++ b/packages/neon/neon/lib/src/pages/home.dart @@ -482,10 +482,10 @@ class _HomePageState extends State { const SizedBox( width: 8, ), - Icon( - Icons.error_outline, - size: 30, - color: Theme.of(context).colorScheme.onPrimary, + NeonException( + appImplementations.error, + onRetry: _appsBloc.refresh, + onlyIcon: true, ), ], if (appImplementations.loading) ...[ @@ -563,10 +563,6 @@ class _HomePageState extends State { Expanded( child: Column( children: [ - NeonException( - appImplementations.error, - onRetry: _appsBloc.refresh, - ), if (appImplementations.data != null) ...[ if (appImplementations.data!.isEmpty) ...[ Expanded( diff --git a/packages/neon/neon/lib/src/utils/request_manager.dart b/packages/neon/neon/lib/src/utils/request_manager.dart index 66f59fd7..7cc4d33b 100644 --- a/packages/neon/neon/lib/src/utils/request_manager.dart +++ b/packages/neon/neon/lib/src/utils/request_manager.dart @@ -85,6 +85,7 @@ class RequestManager { deserialize, emitEmptyCache, true, + null, ); try { @@ -117,6 +118,7 @@ class RequestManager { deserialize, emitEmptyCache, false, + e, ))) { subject.add(Result.error(e)); } @@ -130,6 +132,7 @@ class RequestManager { final R Function(String) deserialize, final bool emitEmptyCache, final bool loading, + final Object? error, ) async { T? cached; try { @@ -144,7 +147,7 @@ class RequestManager { subject.add( Result( cached, - null, + error, loading: loading, cached: true, ), diff --git a/packages/neon/neon/lib/src/widgets/account_avatar.dart b/packages/neon/neon/lib/src/widgets/account_avatar.dart index 8747c5f2..572e7fb8 100644 --- a/packages/neon/neon/lib/src/widgets/account_avatar.dart +++ b/packages/neon/neon/lib/src/widgets/account_avatar.dart @@ -14,6 +14,7 @@ class NeonAccountAvatar extends StatelessWidget { Widget build(final BuildContext context) { final isDark = Theme.of(context).brightness == Brightness.dark; final size = (kAvatarSize * MediaQuery.of(context).devicePixelRatio).toInt(); + final userStatusBloc = Provider.of(context, listen: false).getUserStatusBloc(account); return Stack( alignment: Alignment.center, children: [ @@ -40,7 +41,7 @@ class NeonAccountAvatar extends StatelessWidget { ), ), ResultBuilder( - stream: Provider.of(context, listen: false).getUserStatusBloc(account).userStatus, + stream: userStatusBloc.userStatus, builder: (final context, final userStatus) { final hasEmoji = userStatus.data?.icon != null; final factor = hasEmoji ? 2 : 3; @@ -66,10 +67,11 @@ class NeonAccountAvatar extends StatelessWidget { : userStatus.error != null && (userStatus.error is! NextcloudApiException || (userStatus.error! as NextcloudApiException).statusCode != 404) - ? Icon( - Icons.error_outline, - size: kAvatarSize / factor, - color: Colors.red, + ? NeonException( + userStatus.error, + onRetry: userStatusBloc.refresh, + onlyIcon: true, + iconSize: kAvatarSize / factor, ) : hasEmoji ? Text( diff --git a/packages/neon/neon/lib/src/widgets/cached_api_image.dart b/packages/neon/neon/lib/src/widgets/cached_api_image.dart index 6c91a4be..5e27e3ae 100644 --- a/packages/neon/neon/lib/src/widgets/cached_api_image.dart +++ b/packages/neon/neon/lib/src/widgets/cached_api_image.dart @@ -15,7 +15,7 @@ class NeonCachedApiImage extends NeonCachedImage { super.iconColor, super.key, }) : super( - future: () async { + getImageFile: () async { final realKey = '${account.id}-$cacheKey'; final cacheFile = await _cacheManager.getFileFromCache(realKey); if (cacheFile != null && cacheFile.validTill.isAfter(DateTime.now())) { @@ -28,6 +28,6 @@ class NeonCachedApiImage extends NeonCachedImage { maxAge: const Duration(days: 7), eTag: etag, ); - }(), + }, ); } diff --git a/packages/neon/neon/lib/src/widgets/cached_image.dart b/packages/neon/neon/lib/src/widgets/cached_image.dart index 20f9f06c..598c83ba 100644 --- a/packages/neon/neon/lib/src/widgets/cached_image.dart +++ b/packages/neon/neon/lib/src/widgets/cached_image.dart @@ -2,9 +2,9 @@ part of '../../neon.dart'; final _cacheManager = DefaultCacheManager(); -abstract class NeonCachedImage extends StatelessWidget { +abstract class NeonCachedImage extends StatefulWidget { const NeonCachedImage({ - required this.future, + required this.getImageFile, this.isSvgHint = false, this.height, this.width, @@ -14,7 +14,7 @@ abstract class NeonCachedImage extends StatelessWidget { super.key, }); - final Future future; + final Future Function() getImageFile; final bool isSvgHint; final double? height; @@ -25,48 +25,66 @@ abstract class NeonCachedImage extends StatelessWidget { final Color? iconColor; @override - Widget build(final BuildContext context) => FutureBuilder( - future: future, - builder: (final context, final fileSnapshot) { - if (fileSnapshot.hasData) { - final content = fileSnapshot.data!.readAsBytesSync(); + State createState() => _NeonCachedImageState(); +} + +class _NeonCachedImageState extends State { + late Future future = widget.getImageFile(); + + @override + Widget build(final BuildContext context) => Center( + child: FutureBuilder( + future: future, + builder: (final context, final fileSnapshot) { + if (fileSnapshot.hasData) { + final content = fileSnapshot.data!.readAsBytesSync(); - try { - // TODO: Is this safe enough? - if (isSvgHint || utf8.decode(content).contains(' _cacheManager.getSingleFile(url), isSvgHint: Uri.parse(url).path.endsWith('.svg'), ); } diff --git a/packages/neon/neon/lib/src/widgets/exception.dart b/packages/neon/neon/lib/src/widgets/exception.dart index 064078af..5da96df5 100644 --- a/packages/neon/neon/lib/src/widgets/exception.dart +++ b/packages/neon/neon/lib/src/widgets/exception.dart @@ -5,14 +5,16 @@ class NeonException extends StatelessWidget { this.exception, { required this.onRetry, this.onlyIcon = false, - this.iconSize = 30, + this.iconSize, + this.color, super.key, }); final dynamic exception; final Function() onRetry; final bool onlyIcon; - final double iconSize; + final double? iconSize; + final Color? color; static void showSnackbar(final BuildContext context, final dynamic exception) { final details = _getExceptionDetails(context, exception); @@ -41,8 +43,8 @@ class NeonException extends StatelessWidget { final errorIcon = Icon( Icons.error_outline, - size: iconSize, - color: Colors.red, + size: iconSize ?? 30, + color: color ?? Colors.red, ); if (onlyIcon) { @@ -71,8 +73,8 @@ class NeonException extends StatelessWidget { Flexible( child: Text( details.text, - style: const TextStyle( - color: Colors.red, + style: TextStyle( + color: color ?? Colors.red, ), ), ),