spotube/lib/components/fallbacks/error_box.dart

139 lines
5.5 KiB
Dart

import 'package:auto_route/auto_route.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:shadcn_flutter/shadcn_flutter.dart';
import 'package:shadcn_flutter/shadcn_flutter_extension.dart';
import 'package:spotube/collections/spotube_icons.dart';
import 'package:spotube/extensions/context.dart';
class ErrorBox extends StatelessWidget {
final Object error;
final VoidCallback? onRetry;
const ErrorBox({
super.key,
required this.error,
this.onRetry,
});
@override
Widget build(BuildContext context) {
// Make a monospace error log view. Make sure it's only 4 lines
return ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 400),
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
spacing: 12,
children: [
Basic(
leading: const Icon(SpotubeIcons.error),
contentSpacing: 8,
title: Text(context.l10n.an_error_occurred),
),
Card(
padding: const EdgeInsets.all(8.0),
filled: true,
fillColor: context.theme.colorScheme.muted,
child: Text(
error.toString(),
style: TextStyle(
// Use monospace
fontFamily: 'Ubuntu Mono',
color: context.theme.colorScheme.mutedForeground,
fontSize: 14,
),
maxLines: 6,
overflow: TextOverflow.ellipsis,
),
),
// Show a dialog with full log and a retry button as well
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Button.text(
leading: const Icon(SpotubeIcons.logs),
onPressed: () {
showDialog(
context: context,
builder: (context) {
return ConstrainedBox(
constraints: BoxConstraints(
maxWidth: 480,
maxHeight:
MediaQuery.of(context).size.height * 0.8,
),
child: AlertDialog(
padding: const EdgeInsets.all(12),
title: Row(
spacing: 8,
children: [
const Icon(SpotubeIcons.logs),
Text(context.l10n.logs),
const Spacer(),
IconButton.ghost(
icon: const Icon(SpotubeIcons.close),
onPressed: () => context.maybePop(),
)
],
),
actions: [
HookBuilder(builder: (context) {
final copied = useState(false);
return Button.ghost(
leading: copied.value
? const Icon(SpotubeIcons.done)
: const Icon(SpotubeIcons.clipboard),
child: Text(context.l10n.copy_to_clipboard),
onPressed: () {
Clipboard.setData(
ClipboardData(text: error.toString()),
);
copied.value = true;
},
);
})
],
content: SingleChildScrollView(
child: Card(
padding: const EdgeInsets.all(8.0),
filled: true,
fillColor: context.theme.colorScheme.muted,
child: SelectableText(
error.toString(),
style: TextStyle(
// Use monospace
fontFamily: 'Ubuntu Mono',
color: context
.theme.colorScheme.mutedForeground,
fontSize: 16,
),
),
),
),
),
);
},
);
},
child: Text(context.l10n.view_logs),
),
if (onRetry != null)
Button.text(
leading: const Icon(SpotubeIcons.refresh),
onPressed: onRetry,
child: Text(context.l10n.retry),
),
],
),
],
),
),
),
);
}
}