Surface/lib/widgets/dialog.dart

171 lines
4.8 KiB
Dart

import 'dart:math' as math;
import 'package:dio/dio.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher_string.dart';
extension AppPromptExtension on BuildContext {
void showSnackbar(String content, {SnackBarAction? action}) {
ScaffoldMessenger.of(this).showSnackBar(SnackBar(
content: Text(content),
action: action,
));
}
void clearSnackbar() {
ScaffoldMessenger.of(this).clearSnackBars();
}
Future<void> showModalDialog(String title, desc) {
return showDialog<void>(
useRootNavigator: true,
context: this,
builder: (ctx) => AlertDialog(
title: Text(title),
content: Text(desc),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: Text('dialogDismiss').tr(),
)
],
),
);
}
Future<void> showInfoDialog(String title, body) {
return showDialog<void>(
useRootNavigator: true,
context: this,
builder: (ctx) => AlertDialog(
title: Text(title),
content: Text(body),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: Text('dialogDismiss').tr(),
)
],
),
);
}
Future<bool> showConfirmDialog(String title, body) async {
return await showDialog<bool>(
useRootNavigator: true,
context: this,
builder: (ctx) => AlertDialog(
title: Text(title),
content: Text(body),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx, false),
child: Text('dialogCancel').tr(),
),
TextButton(
onPressed: () => Navigator.pop(ctx, true),
child: Text('dialogConfirm').tr(),
)
],
),
) ??
false;
}
Future<void> showErrorDialog(dynamic exception) {
Widget content = Text(exception.toString().capitalize());
if (exception is DioException) {
String preview;
switch (exception.response?.statusCode) {
case 400:
preview = 'errorRequestBad'.tr();
break;
case 401:
preview = 'errorRequestUnauthorized'.tr();
break;
case 403:
preview = 'errorRequestForbidden'.tr();
break;
case 404:
preview = 'errorRequestNotFound'.tr();
break;
case null:
preview = 'errorRequestConnection'.tr();
break;
default:
preview = 'errorRequestUnknown'.tr();
break;
}
if (exception.response != null) {
content = Text(
'$preview\n\n${exception.requestOptions.uri.path}\n(${exception.response?.statusCode}) ${exception.response?.data}',
);
} else {
content = Text(preview);
}
}
return showDialog<void>(
useRootNavigator: true,
context: this,
builder: (ctx) => AlertDialog(
title: Text('dialogError').tr(),
content: Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
spacing: 20,
children: [
content,
Text.rich(
TextSpan(
text: 'needHelp'.tr(),
children: [
TextSpan(text: ' '),
TextSpan(
text: 'needHelpLaunch'.tr(),
style: TextStyle(
color: Theme.of(ctx).colorScheme.primary,
decoration: TextDecoration.underline,
decorationColor: Theme.of(ctx).colorScheme.primary,
),
recognizer: TapGestureRecognizer()
..onTap = () {
launchUrlString('https://kb.solsynth.dev/solar-network');
},
),
],
),
),
],
),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: Text('dialogDismiss').tr(),
)
],
),
);
}
}
extension ByteFormatter on int {
String formatBytes({int decimals = 2}) {
if (this == 0) return '0 Bytes';
const k = 1024;
final dm = decimals < 0 ? 0 : decimals;
final sizes = ['Bytes', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];
final i = (math.log(this) / math.log(k)).floor().toInt();
return '${(this / math.pow(k, i)).toStringAsFixed(dm)} ${sizes[i]}';
}
}
extension StringFormatter on String {
String capitalize() {
return "${this[0].toUpperCase()}${substring(1)}";
}
}