Browse Source

neon: Always display error messages

pull/280/head
jld3103 2 years ago
parent
commit
91a7af1550
No known key found for this signature in database
GPG Key ID: 9062417B9E8EB7B3
  1. 12
      packages/neon/neon/lib/src/pages/home.dart
  2. 5
      packages/neon/neon/lib/src/utils/request_manager.dart
  3. 12
      packages/neon/neon/lib/src/widgets/account_avatar.dart
  4. 4
      packages/neon/neon/lib/src/widgets/cached_api_image.dart
  5. 102
      packages/neon/neon/lib/src/widgets/cached_image.dart
  6. 2
      packages/neon/neon/lib/src/widgets/cached_url_image.dart
  7. 14
      packages/neon/neon/lib/src/widgets/exception.dart

12
packages/neon/neon/lib/src/pages/home.dart

@ -482,10 +482,10 @@ class _HomePageState extends State<HomePage> {
const SizedBox( const SizedBox(
width: 8, width: 8,
), ),
Icon( NeonException(
Icons.error_outline, appImplementations.error,
size: 30, onRetry: _appsBloc.refresh,
color: Theme.of(context).colorScheme.onPrimary, onlyIcon: true,
), ),
], ],
if (appImplementations.loading) ...[ if (appImplementations.loading) ...[
@ -563,10 +563,6 @@ class _HomePageState extends State<HomePage> {
Expanded( Expanded(
child: Column( child: Column(
children: [ children: [
NeonException(
appImplementations.error,
onRetry: _appsBloc.refresh,
),
if (appImplementations.data != null) ...[ if (appImplementations.data != null) ...[
if (appImplementations.data!.isEmpty) ...[ if (appImplementations.data!.isEmpty) ...[
Expanded( Expanded(

5
packages/neon/neon/lib/src/utils/request_manager.dart

@ -85,6 +85,7 @@ class RequestManager {
deserialize, deserialize,
emitEmptyCache, emitEmptyCache,
true, true,
null,
); );
try { try {
@ -117,6 +118,7 @@ class RequestManager {
deserialize, deserialize,
emitEmptyCache, emitEmptyCache,
false, false,
e,
))) { ))) {
subject.add(Result.error(e)); subject.add(Result.error(e));
} }
@ -130,6 +132,7 @@ class RequestManager {
final R Function(String) deserialize, final R Function(String) deserialize,
final bool emitEmptyCache, final bool emitEmptyCache,
final bool loading, final bool loading,
final Object? error,
) async { ) async {
T? cached; T? cached;
try { try {
@ -144,7 +147,7 @@ class RequestManager {
subject.add( subject.add(
Result( Result(
cached, cached,
null, error,
loading: loading, loading: loading,
cached: true, cached: true,
), ),

12
packages/neon/neon/lib/src/widgets/account_avatar.dart

@ -14,6 +14,7 @@ class NeonAccountAvatar extends StatelessWidget {
Widget build(final BuildContext context) { Widget build(final BuildContext context) {
final isDark = Theme.of(context).brightness == Brightness.dark; final isDark = Theme.of(context).brightness == Brightness.dark;
final size = (kAvatarSize * MediaQuery.of(context).devicePixelRatio).toInt(); final size = (kAvatarSize * MediaQuery.of(context).devicePixelRatio).toInt();
final userStatusBloc = Provider.of<AccountsBloc>(context, listen: false).getUserStatusBloc(account);
return Stack( return Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
@ -40,7 +41,7 @@ class NeonAccountAvatar extends StatelessWidget {
), ),
), ),
ResultBuilder<UserStatusBloc, NextcloudUserStatusStatus?>( ResultBuilder<UserStatusBloc, NextcloudUserStatusStatus?>(
stream: Provider.of<AccountsBloc>(context, listen: false).getUserStatusBloc(account).userStatus, stream: userStatusBloc.userStatus,
builder: (final context, final userStatus) { builder: (final context, final userStatus) {
final hasEmoji = userStatus.data?.icon != null; final hasEmoji = userStatus.data?.icon != null;
final factor = hasEmoji ? 2 : 3; final factor = hasEmoji ? 2 : 3;
@ -66,10 +67,11 @@ class NeonAccountAvatar extends StatelessWidget {
: userStatus.error != null && : userStatus.error != null &&
(userStatus.error is! NextcloudApiException || (userStatus.error is! NextcloudApiException ||
(userStatus.error! as NextcloudApiException).statusCode != 404) (userStatus.error! as NextcloudApiException).statusCode != 404)
? Icon( ? NeonException(
Icons.error_outline, userStatus.error,
size: kAvatarSize / factor, onRetry: userStatusBloc.refresh,
color: Colors.red, onlyIcon: true,
iconSize: kAvatarSize / factor,
) )
: hasEmoji : hasEmoji
? Text( ? Text(

4
packages/neon/neon/lib/src/widgets/cached_api_image.dart

@ -15,7 +15,7 @@ class NeonCachedApiImage extends NeonCachedImage {
super.iconColor, super.iconColor,
super.key, super.key,
}) : super( }) : super(
future: () async { getImageFile: () async {
final realKey = '${account.id}-$cacheKey'; final realKey = '${account.id}-$cacheKey';
final cacheFile = await _cacheManager.getFileFromCache(realKey); final cacheFile = await _cacheManager.getFileFromCache(realKey);
if (cacheFile != null && cacheFile.validTill.isAfter(DateTime.now())) { if (cacheFile != null && cacheFile.validTill.isAfter(DateTime.now())) {
@ -28,6 +28,6 @@ class NeonCachedApiImage extends NeonCachedImage {
maxAge: const Duration(days: 7), maxAge: const Duration(days: 7),
eTag: etag, eTag: etag,
); );
}(), },
); );
} }

102
packages/neon/neon/lib/src/widgets/cached_image.dart

@ -2,9 +2,9 @@ part of '../../neon.dart';
final _cacheManager = DefaultCacheManager(); final _cacheManager = DefaultCacheManager();
abstract class NeonCachedImage extends StatelessWidget { abstract class NeonCachedImage extends StatefulWidget {
const NeonCachedImage({ const NeonCachedImage({
required this.future, required this.getImageFile,
this.isSvgHint = false, this.isSvgHint = false,
this.height, this.height,
this.width, this.width,
@ -14,7 +14,7 @@ abstract class NeonCachedImage extends StatelessWidget {
super.key, super.key,
}); });
final Future<File> future; final Future<File> Function() getImageFile;
final bool isSvgHint; final bool isSvgHint;
final double? height; final double? height;
@ -25,48 +25,66 @@ abstract class NeonCachedImage extends StatelessWidget {
final Color? iconColor; final Color? iconColor;
@override @override
Widget build(final BuildContext context) => FutureBuilder<File>( State<NeonCachedImage> createState() => _NeonCachedImageState();
future: future, }
builder: (final context, final fileSnapshot) {
if (fileSnapshot.hasData) { class _NeonCachedImageState extends State<NeonCachedImage> {
final content = fileSnapshot.data!.readAsBytesSync(); late Future<File> future = widget.getImageFile();
@override
Widget build(final BuildContext context) => Center(
child: FutureBuilder<File>(
future: future,
builder: (final context, final fileSnapshot) {
if (fileSnapshot.hasData) {
final content = fileSnapshot.data!.readAsBytesSync();
try { try {
// TODO: Is this safe enough? // TODO: Is this safe enough?
if (isSvgHint || utf8.decode(content).contains('<svg')) { if (widget.isSvgHint || utf8.decode(content).contains('<svg')) {
return SvgPicture.memory( return SvgPicture.memory(
content, content,
height: height, height: widget.height,
width: width, width: widget.width,
fit: fit ?? BoxFit.contain, fit: widget.fit ?? BoxFit.contain,
color: svgColor, color: widget.svgColor,
); );
}
} catch (_) {
// If the data is not UTF-8
} }
} catch (_) {
// If the data is not UTF-8
}
return Image.memory( return Image.memory(
content, content,
height: height, height: widget.height,
width: width, width: widget.width,
fit: fit, fit: widget.fit,
gaplessPlayback: true, gaplessPlayback: true,
); );
} }
if (fileSnapshot.hasError) { if (fileSnapshot.hasError) {
return Icon( return NeonException(
Icons.error_outline, fileSnapshot.error,
size: height != null && width != null ? min(height!, width!) : height ?? width, onRetry: () {
color: iconColor, setState(() {
// ignore: discarded_futures
future = widget.getImageFile();
});
},
onlyIcon: true,
iconSize: widget.height != null && widget.width != null
? min(widget.height!, widget.width!)
: widget.height ?? widget.width,
color: widget.iconColor ?? Colors.red,
);
}
return SizedBox(
width: widget.width,
child: NeonLinearProgressIndicator(
color: widget.iconColor,
),
); );
} },
return SizedBox( ),
width: width,
child: NeonLinearProgressIndicator(
color: iconColor,
),
);
},
); );
} }

2
packages/neon/neon/lib/src/widgets/cached_url_image.dart

@ -10,7 +10,7 @@ class NeonCachedUrlImage extends NeonCachedImage {
super.iconColor, super.iconColor,
super.key, super.key,
}) : super( }) : super(
future: _cacheManager.getSingleFile(url), getImageFile: () => _cacheManager.getSingleFile(url),
isSvgHint: Uri.parse(url).path.endsWith('.svg'), isSvgHint: Uri.parse(url).path.endsWith('.svg'),
); );
} }

14
packages/neon/neon/lib/src/widgets/exception.dart

@ -5,14 +5,16 @@ class NeonException extends StatelessWidget {
this.exception, { this.exception, {
required this.onRetry, required this.onRetry,
this.onlyIcon = false, this.onlyIcon = false,
this.iconSize = 30, this.iconSize,
this.color,
super.key, super.key,
}); });
final dynamic exception; final dynamic exception;
final Function() onRetry; final Function() onRetry;
final bool onlyIcon; final bool onlyIcon;
final double iconSize; final double? iconSize;
final Color? color;
static void showSnackbar(final BuildContext context, final dynamic exception) { static void showSnackbar(final BuildContext context, final dynamic exception) {
final details = _getExceptionDetails(context, exception); final details = _getExceptionDetails(context, exception);
@ -41,8 +43,8 @@ class NeonException extends StatelessWidget {
final errorIcon = Icon( final errorIcon = Icon(
Icons.error_outline, Icons.error_outline,
size: iconSize, size: iconSize ?? 30,
color: Colors.red, color: color ?? Colors.red,
); );
if (onlyIcon) { if (onlyIcon) {
@ -71,8 +73,8 @@ class NeonException extends StatelessWidget {
Flexible( Flexible(
child: Text( child: Text(
details.text, details.text,
style: const TextStyle( style: TextStyle(
color: Colors.red, color: color ?? Colors.red,
), ),
), ),
), ),

Loading…
Cancel
Save