mirror of
https://github.com/rzuasti/oott.git
synced 2026-07-08 19:21:54 +02:00
Catch uncaught async errors and surface response-shape failures
Wrap main in runZonedGuarded and install FlutterError.onError / PlatformDispatcher.onError so background async failures get logged instead of dying silently. Guard PrefUtil.init so a SharedPreferences platform error no longer aborts startup. Recognize TypeError and FormatException in dioErrorToUserMessage to give a clearer message when the backend returns an unexpected JSON shape or bad timestamp. Wrap Settings._save in try/catch so prefs write failures show a snackbar instead of leaving the Save button silently dead. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
e70676a193
commit
140ebab035
+29
-4
@@ -1,3 +1,6 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:frontend/theme/catppuccin_mocha_theme.dart';
|
import 'package:frontend/theme/catppuccin_mocha_theme.dart';
|
||||||
import 'package:frontend/utils/pref_utils.dart';
|
import 'package:frontend/utils/pref_utils.dart';
|
||||||
@@ -5,10 +8,32 @@ import 'package:provider/provider.dart';
|
|||||||
import 'navigation.dart';
|
import 'navigation.dart';
|
||||||
import 'theme/gruvbox_theme.dart';
|
import 'theme/gruvbox_theme.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
runZonedGuarded(
|
||||||
await PrefUtil.init();
|
() async {
|
||||||
runApp(const MainApp());
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
FlutterError.onError = (details) {
|
||||||
|
FlutterError.presentError(details);
|
||||||
|
debugPrint('FlutterError: ${details.exceptionAsString()}');
|
||||||
|
};
|
||||||
|
PlatformDispatcher.instance.onError = (error, stack) {
|
||||||
|
debugPrint('Uncaught platform error: $error\n$stack');
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await PrefUtil.init();
|
||||||
|
} catch (e, stack) {
|
||||||
|
debugPrint('PrefUtil.init failed, continuing with defaults: $e\n$stack');
|
||||||
|
}
|
||||||
|
|
||||||
|
runApp(const MainApp());
|
||||||
|
},
|
||||||
|
(error, stack) {
|
||||||
|
debugPrint('Uncaught zone error: $error\n$stack');
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final class MainApp extends StatelessWidget {
|
final class MainApp extends StatelessWidget {
|
||||||
|
|||||||
@@ -69,20 +69,28 @@ class _SettingsState extends State<Settings> {
|
|||||||
|
|
||||||
Future<void> _save() async {
|
Future<void> _save() async {
|
||||||
if (!_formKey.currentState!.validate()) return;
|
if (!_formKey.currentState!.validate()) return;
|
||||||
final urlOk = await PrefUtil.setValue('base_url', _baseUrlController.text);
|
try {
|
||||||
if (!mounted) return;
|
final urlOk = await PrefUtil.setValue(
|
||||||
final keyOk = await PrefUtil.setValue(
|
'base_url',
|
||||||
'api_key',
|
_baseUrlController.text,
|
||||||
XOR().xorEncode(_apiKeyController.text),
|
);
|
||||||
);
|
if (!mounted) return;
|
||||||
if (!mounted) return;
|
final keyOk = await PrefUtil.setValue(
|
||||||
final themeOk = await context.read<AppState>().setTheme(_selectedTheme);
|
'api_key',
|
||||||
if (!mounted) return;
|
XOR().xorEncode(_apiKeyController.text),
|
||||||
if (urlOk && keyOk && themeOk) {
|
);
|
||||||
BackendAPI.instance.reconfigureFromPrefs();
|
if (!mounted) return;
|
||||||
UISnackbars.showSuccess(context, 'Settings saved successfully');
|
final themeOk = await context.read<AppState>().setTheme(_selectedTheme);
|
||||||
} else {
|
if (!mounted) return;
|
||||||
UISnackbars.showError(context, 'Failed to save settings');
|
if (urlOk && keyOk && themeOk) {
|
||||||
|
BackendAPI.instance.reconfigureFromPrefs();
|
||||||
|
UISnackbars.showSuccess(context, 'Settings saved successfully');
|
||||||
|
} else {
|
||||||
|
UISnackbars.showError(context, 'Failed to save settings');
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (!mounted) return;
|
||||||
|
UISnackbars.showError(context, 'Failed to save settings: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -59,6 +59,10 @@ LogInterceptor _buildLogInterceptor() => LogInterceptor(
|
|||||||
);
|
);
|
||||||
|
|
||||||
String dioErrorToUserMessage(Object error) {
|
String dioErrorToUserMessage(Object error) {
|
||||||
|
if (error is TypeError || error is FormatException) {
|
||||||
|
debugPrint('Backend response shape error: $error');
|
||||||
|
return 'Unexpected response shape from backend.';
|
||||||
|
}
|
||||||
if (error is! DioException) {
|
if (error is! DioException) {
|
||||||
debugPrint('Non-Dio error from backend call: $error');
|
debugPrint('Non-Dio error from backend call: $error');
|
||||||
return 'Unexpected error.';
|
return 'Unexpected error.';
|
||||||
|
|||||||
Reference in New Issue
Block a user