refactored broad range of the application, split files, modularized calendar and file views, centralized bottom sheets and clipboard handling, and implemented unit test coverage

This commit is contained in:
2026-05-08 19:05:16 +02:00
parent 3b1b0d0c19
commit c62a14645a
68 changed files with 4633 additions and 3141 deletions
@@ -0,0 +1,63 @@
part of '../async_action_button.dart';
typedef AsyncActionCallback = Future<void> Function();
typedef AsyncErrorBuilder = String Function(Object error);
/// Wraps [action] with a try/catch that pops up an [InfoDialog] on failure
/// (using [errorBuilder] or the default error mapper). Returns `true` on
/// success, `false` on caught failure.
Future<bool> runWithErrorDialog(
BuildContext context,
AsyncActionCallback action, {
AsyncErrorBuilder? errorBuilder,
}) async {
try {
await action();
return true;
} catch (e) {
if (!context.mounted) return false;
final message = errorBuilder != null ? errorBuilder(e) : errorToUserMessage(e);
final details = errorToTechnicalDetails(e);
final body = details != null && details != message ? '$message\n\n$details' : message;
InfoDialog.show(context, body, copyable: true, title: 'Fehler');
return false;
}
}
/// Reusable busy/error state for the async-button family. Multiple buttons
/// can share the same controller (e.g. a parent toolbar wanting to disable
/// while any one child is running).
class AsyncActionController extends ChangeNotifier {
bool _busy = false;
String? _error;
bool get busy => _busy;
String? get error => _error;
Future<bool> run(
AsyncActionCallback action, {
AsyncErrorBuilder? errorBuilder,
}) async {
if (_busy) return false;
_busy = true;
_error = null;
notifyListeners();
try {
await action();
_busy = false;
notifyListeners();
return true;
} catch (e) {
_busy = false;
_error = errorBuilder != null ? errorBuilder(e) : errorToUserMessage(e);
notifyListeners();
return false;
}
}
void clearError() {
if (_error == null) return;
_error = null;
notifyListeners();
}
}