Files
sendico/frontend/pweb/lib/utils/error/snackbar.dart
2026-02-11 02:48:30 +03:00

216 lines
5.9 KiB
Dart

import 'package:flutter/material.dart';
import 'package:pweb/utils/error/handler.dart';
import 'package:pweb/utils/snackbar.dart';
import 'package:pweb/widgets/error/content.dart';
import 'package:pweb/generated/i18n/app_localizations.dart';
Future<void> notifyUserOfErrorX({
required BuildContext context,
required String errorSituation,
required Object exception,
required AppLocalizations appLocalizations,
int delaySeconds = 3,
}) async {
if (!context.mounted) return;
if (!_shouldShowError(errorSituation, exception)) {
return;
}
final localizedError = ErrorHandler.handleErrorLocs(appLocalizations, exception);
final technicalDetails = exception.toString();
final scaffoldMessenger = ScaffoldMessenger.maybeOf(context);
if (scaffoldMessenger != null) {
final durationSeconds = _normalizeDelaySeconds(delaySeconds);
scaffoldMessenger.clearSnackBars();
final snackBar = _buildMainErrorSnackBar(
errorSituation: errorSituation,
localizedError: localizedError,
technicalDetails: technicalDetails,
loc: appLocalizations,
scaffoldMessenger: scaffoldMessenger,
delaySeconds: durationSeconds,
);
scaffoldMessenger.showSnackBar(snackBar);
return;
}
await _showErrorDialog(
context,
title: errorSituation,
message: localizedError,
);
}
void showErrorSnackBar({
required ScaffoldMessengerState scaffoldMessenger,
required String errorSituation,
required Object exception,
required AppLocalizations appLocalizations,
int delaySeconds = 3,
}) {
if (!_shouldShowError(errorSituation, exception)) {
return;
}
final localizedError = ErrorHandler.handleErrorLocs(appLocalizations, exception);
final technicalDetails = exception.toString();
final durationSeconds = _normalizeDelaySeconds(delaySeconds);
scaffoldMessenger.clearSnackBars();
final snackBar = _buildMainErrorSnackBar(
errorSituation: errorSituation,
localizedError: localizedError,
technicalDetails: technicalDetails,
loc: appLocalizations,
scaffoldMessenger: scaffoldMessenger,
delaySeconds: durationSeconds,
);
scaffoldMessenger.showSnackBar(snackBar);
}
Future<void> notifyUserOfError({
required BuildContext context,
required String errorSituation,
required Object exception,
int delaySeconds = 3,
}) =>
notifyUserOfErrorX(
context: context,
errorSituation: errorSituation,
exception: exception,
appLocalizations: AppLocalizations.of(context)!,
delaySeconds: delaySeconds,
);
Future<T?> executeActionWithNotification<T>({
required BuildContext context,
required Future<T> Function() action,
required String errorMessage,
String? successMessage,
int delaySeconds = 3,
}) async {
final localizations = AppLocalizations.of(context)!;
try {
final result = await action();
if (successMessage != null) {
await notifyUser(context, successMessage, delaySeconds: delaySeconds);
}
return result;
} catch (e) {
await notifyUserOfErrorX(
context: context,
errorSituation: errorMessage,
exception: e,
appLocalizations: localizations,
delaySeconds: delaySeconds,
);
}
return null;
}
Future<void> postNotifyUserOfError({
required BuildContext context,
required String errorSituation,
required Object exception,
required AppLocalizations appLocalizations,
int delaySeconds = 3,
}) =>
notifyUserOfErrorX(
context: context,
errorSituation: errorSituation,
exception: exception,
appLocalizations: appLocalizations,
delaySeconds: delaySeconds,
);
Future<void> postNotifyUserOfErrorX({
required BuildContext context,
required String errorSituation,
required Object exception,
int delaySeconds = 3,
}) =>
postNotifyUserOfError(
context: context,
errorSituation: errorSituation,
exception: exception,
appLocalizations: AppLocalizations.of(context)!,
delaySeconds: delaySeconds,
);
SnackBar _buildMainErrorSnackBar({
required String errorSituation,
required String localizedError,
required String technicalDetails,
required AppLocalizations loc,
required ScaffoldMessengerState scaffoldMessenger,
int delaySeconds = 3,
}) =>
SnackBar(
duration: Duration(seconds: _normalizeDelaySeconds(delaySeconds)),
content: ErrorSnackBarContent(
situation: errorSituation,
localizedError: localizedError,
),
action: SnackBarAction(
label: loc.showDetailsAction,
onPressed: () {
scaffoldMessenger.hideCurrentSnackBar();
scaffoldMessenger.showSnackBar(
SnackBar(
content: Text(technicalDetails),
duration: const Duration(seconds: 6),
),
);
},
),
);
Future<void> _showErrorDialog(
BuildContext context, {
required String title,
required String message,
}) async {
if (!context.mounted) return;
final loc = AppLocalizations.of(context)!;
await showDialog<void>(
context: context,
builder: (dialogContext) => AlertDialog(
title: Text(title),
content: Text(message),
actions: [
TextButton(
onPressed: () => Navigator.of(dialogContext).pop(),
child: Text(loc.ok),
),
],
),
);
}
int _normalizeDelaySeconds(int delaySeconds) =>
delaySeconds <= 0 ? 3 : delaySeconds;
String? _lastErrorSignature;
DateTime? _lastErrorShownAt;
const int _errorCooldownSeconds = 60;
bool _shouldShowError(String errorSituation, Object exception) {
final signature = '$errorSituation|${exception.runtimeType}|${exception.toString()}';
final now = DateTime.now();
if (_lastErrorSignature == signature) {
final lastShownAt = _lastErrorShownAt;
if (lastShownAt != null &&
now.difference(lastShownAt).inSeconds < _errorCooldownSeconds) {
return false;
}
}
_lastErrorSignature = signature;
_lastErrorShownAt = now;
return true;
}