feat: add error_handler and email verification request
This commit is contained in:
parent
defecf958a
commit
cdc5e44bb3
8 changed files with 110 additions and 26 deletions
|
|
@ -383,5 +383,13 @@
|
||||||
"lobbyStatusEntering": "Betrete das Schlachtfeld...",
|
"lobbyStatusEntering": "Betrete das Schlachtfeld...",
|
||||||
|
|
||||||
"connectivityError": "Keine Internetverbindung verfügbar.",
|
"connectivityError": "Keine Internetverbindung verfügbar.",
|
||||||
"connectivityMultiplayerError": "Für Multiplayer wird eine Internetverbindung benötigt."
|
"connectivityMultiplayerError": "Für Multiplayer wird eine Internetverbindung benötigt.",
|
||||||
|
|
||||||
|
"errorNoInternet": "Keine Internetverbindung",
|
||||||
|
"errorGeneric": "Etwas ist schiefgelaufen.",
|
||||||
|
"errorUnauthorized": "Zugriff verweigert. Bitte neu einloggen.",
|
||||||
|
"errorNotFound": "Daten konnten nicht gefunden werden.",
|
||||||
|
"errorEntryNotUnique": "Dieser Eintrag ist bereits vergeben.",
|
||||||
|
"errorAuthenticationFailed": "E-Mail oder Passwort falsch.",
|
||||||
|
"errorIllegalRequest": "Ungültige Anfrage."
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -397,5 +397,13 @@
|
||||||
"lobbyStatusEntering": "Entering Battle...",
|
"lobbyStatusEntering": "Entering Battle...",
|
||||||
|
|
||||||
"connectivityError": "No internet connection available.",
|
"connectivityError": "No internet connection available.",
|
||||||
"connectivityMultiplayerError": "Active internet connection required for multiplayer."
|
"connectivityMultiplayerError": "Active internet connection required for multiplayer.",
|
||||||
|
|
||||||
|
"errorNoInternet": "No internet connection",
|
||||||
|
"errorGeneric": "Something went wrong",
|
||||||
|
"errorUnauthorized": "Access denied. Please relogin.",
|
||||||
|
"errorNotFound": "Data not found.",
|
||||||
|
"errorEntryNotUnique": "Entry already exists.",
|
||||||
|
"errorAuthenticationFailed": "E-Mail or Passwort wrong.",
|
||||||
|
"errorIllegalRequest": "Illegal Request."
|
||||||
}
|
}
|
||||||
|
|
|
||||||
56
lib/src/core/utils/error_handler.dart
Normal file
56
lib/src/core/utils/error_handler.dart
Normal file
|
|
@ -0,0 +1,56 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:slrpg_app/l10n/app_localizations.dart';
|
||||||
|
import 'package:slrpg_app/src/core/theme/app_theme.dart';
|
||||||
|
|
||||||
|
class ErrorHandler {
|
||||||
|
static String getReadableError(BuildContext context, Object error) {
|
||||||
|
final l10n = AppLocalizations.of(context);
|
||||||
|
if (l10n == null) return error.toString();
|
||||||
|
|
||||||
|
final e = error.toString();
|
||||||
|
|
||||||
|
if (e.contains('SocketException') ||
|
||||||
|
e.contains('Connection refused') ||
|
||||||
|
e.contains('ClientException') ||
|
||||||
|
e.contains('HandshakeException')) {
|
||||||
|
return l10n.errorNoInternet;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.contains('401') || e.contains('403')) {
|
||||||
|
return l10n.errorUnauthorized;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.contains('404')) {
|
||||||
|
return l10n.errorNotFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.contains('400')) {
|
||||||
|
if (e.contains('validation_not_unique')) {
|
||||||
|
return l10n.errorEntryNotUnique;
|
||||||
|
}
|
||||||
|
if (e.contains('Failed to authenticate')) {
|
||||||
|
return l10n.errorAuthenticationFailed;
|
||||||
|
}
|
||||||
|
return l10n.errorIllegalRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
return l10n.errorGeneric;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void showErrorSnackBar(BuildContext context, Object error) {
|
||||||
|
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(getReadableError(context, error)),
|
||||||
|
backgroundColor: AppTheme.errorColor,
|
||||||
|
behavior: SnackBarBehavior.floating,
|
||||||
|
duration: const Duration(seconds: 4),
|
||||||
|
action: SnackBarAction(
|
||||||
|
label: 'OK',
|
||||||
|
textColor: Colors.white,
|
||||||
|
onPressed: () => ScaffoldMessenger.of(context).hideCurrentSnackBar(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:slrpg_app/l10n/app_localizations.dart';
|
import 'package:slrpg_app/l10n/app_localizations.dart';
|
||||||
|
import 'package:slrpg_app/src/core/utils/error_handler.dart';
|
||||||
|
|
||||||
import '../../../../shared/data/repositories/user_repository.dart';
|
import '../../../../shared/data/repositories/user_repository.dart';
|
||||||
import '../../../../core/theme/app_theme.dart';
|
import '../../../../core/theme/app_theme.dart';
|
||||||
|
|
@ -60,6 +61,7 @@ class _LoginScreenState extends ConsumerState<LoginScreen> {
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
_errorMessage = _parseErrorMessage(e.toString());
|
_errorMessage = _parseErrorMessage(e.toString());
|
||||||
});
|
});
|
||||||
|
ErrorHandler.showErrorSnackBar(context, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:slrpg_app/l10n/app_localizations.dart';
|
import 'package:slrpg_app/l10n/app_localizations.dart';
|
||||||
|
import 'package:slrpg_app/src/core/utils/error_handler.dart';
|
||||||
import 'package:slrpg_app/src/features/multiplayer/data/repositories/party_repository.dart';
|
import 'package:slrpg_app/src/features/multiplayer/data/repositories/party_repository.dart';
|
||||||
|
|
||||||
import '../../../../core/constants/app_constants.dart';
|
import '../../../../core/constants/app_constants.dart';
|
||||||
|
|
@ -159,9 +160,7 @@ class _HubScreenState extends ConsumerState<HubScreen> {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
log('Failed to start workout: $e');
|
log('Failed to start workout: $e');
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
if (mounted) ErrorHandler.showErrorSnackBar(context, e);
|
||||||
SnackBar(content: Text('Error: $e')),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -276,8 +275,7 @@ class _HubScreenState extends ConsumerState<HubScreen> {
|
||||||
if (mounted) context.go('/lobby/${party.id}');
|
if (mounted) context.go('/lobby/${party.id}');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context)
|
if (mounted) ErrorHandler.showErrorSnackBar(context, e);
|
||||||
.showSnackBar(SnackBar(content: Text('Error: $e')));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -321,8 +319,7 @@ class _HubScreenState extends ConsumerState<HubScreen> {
|
||||||
if (mounted) context.go('/lobby/${party.id}');
|
if (mounted) context.go('/lobby/${party.id}');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
ScaffoldMessenger.of(context)
|
if (mounted) ErrorHandler.showErrorSnackBar(context, e);
|
||||||
.showSnackBar(SnackBar(content: Text('Error: $e')));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -497,21 +494,21 @@ class _HubScreenState extends ConsumerState<HubScreen> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 24),
|
const SizedBox(height: 24),
|
||||||
if (cycle != null)
|
// if (cycle != null)
|
||||||
Padding(
|
// Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 32),
|
// padding: const EdgeInsets.symmetric(horizontal: 32),
|
||||||
child: Row(
|
// child: Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
// mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
children: [
|
// children: [
|
||||||
_StatBox(
|
// _StatBox(
|
||||||
label: l10n.hubCycleLabel,
|
// label: l10n.hubCycleLabel,
|
||||||
value: '#${cycle.cycleNumber}'),
|
// value: '#${cycle.cycleNumber}'),
|
||||||
_StatBox(
|
// _StatBox(
|
||||||
label: l10n.hubActiveLabel,
|
// label: l10n.hubActiveLabel,
|
||||||
value: l10n.hubActiveYes),
|
// value: l10n.hubActiveYes),
|
||||||
],
|
// ],
|
||||||
),
|
// ),
|
||||||
),
|
// ),
|
||||||
const Spacer(flex: 1),
|
const Spacer(flex: 1),
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(
|
padding: const EdgeInsets.symmetric(
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ class _AvatarSetupScreenState extends ConsumerState<AvatarSetupScreen> {
|
||||||
);
|
);
|
||||||
await Future.delayed(const Duration(milliseconds: 100));
|
await Future.delayed(const Duration(milliseconds: 100));
|
||||||
user = await userRepo.getLocalUser();
|
user = await userRepo.getLocalUser();
|
||||||
|
await ref.read(apiClientProvider).requestVerification(email);
|
||||||
|
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
throw Exception(
|
throw Exception(
|
||||||
|
|
|
||||||
|
|
@ -169,11 +169,10 @@ class _BattleScreenState extends ConsumerState<BattleScreen> {
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
|
_showCompletionDialog(result.xpEarned);
|
||||||
if (result.hasLevelUp) {
|
if (result.hasLevelUp) {
|
||||||
_showLevelUpDialog(result.oldLevel!, result.newLevel!);
|
_showLevelUpDialog(result.oldLevel!, result.newLevel!);
|
||||||
}
|
}
|
||||||
|
|
||||||
_showCompletionDialog(result.xpEarned);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showLevelUpDialog(int oldLevel, int newLevel) {
|
void _showLevelUpDialog(int oldLevel, int newLevel) {
|
||||||
|
|
|
||||||
|
|
@ -236,6 +236,19 @@ class ApiClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> requestVerification(String email) async {
|
||||||
|
try {
|
||||||
|
await _dio.post(
|
||||||
|
'/api/collections/users/request-verification',
|
||||||
|
data: {'email': email},
|
||||||
|
);
|
||||||
|
_logger.i('Verification email requested for $email');
|
||||||
|
} catch (e) {
|
||||||
|
_logger.e('Request verification failed', error: e);
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> logout() async {
|
Future<void> logout() async {
|
||||||
await _storage.delete(key: AppConstants.keyAuthToken);
|
await _storage.delete(key: AppConstants.keyAuthToken);
|
||||||
await _storage.delete(key: AppConstants.keyUserId);
|
await _storage.delete(key: AppConstants.keyUserId);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue