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 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 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 executeActionWithNotification({ required BuildContext context, required Future 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 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 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 _showErrorDialog( BuildContext context, { required String title, required String message, }) async { if (!context.mounted) return; final loc = AppLocalizations.of(context)!; await showDialog( 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; }