chore: add more specific error messages

This commit is contained in:
ggurdin 2025-07-11 12:57:48 -04:00 committed by GitHub
parent e74c82acd5
commit f6f4c60287
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 135 additions and 192 deletions

View file

@ -5070,5 +5070,10 @@
"errorProcessAnalytics": "Failed to process analytics",
"errorDownloading": "Download failed",
"errorFetchingLevelSummary": "Failed to fetch level summary",
"errorLoadingSpaceChildren": "Failed to load chats within this space"
"errorLoadingSpaceChildren": "Failed to load chats within this space",
"unexpectedError": "Unexpected error.",
"pleaseReload": "Please reload and try again.",
"translationError": "Translation error",
"errorFetchingTranslation": "Failed to fetch translation",
"errorFetchingActivity": "Failed to fetch activity"
}

View file

@ -3,50 +3,17 @@ import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
import '../../common/utils/error_handler.dart';
enum ChoreoErrorType {
unknown,
classDisabled,
userDisabled,
}
class ChoreoError {
final ChoreoErrorType type;
final Object? raw;
ChoreoError({required this.type, this.raw});
ChoreoError({this.raw});
String title(BuildContext context) {
switch (type) {
case ChoreoErrorType.classDisabled:
return "Class Disabled";
case ChoreoErrorType.userDisabled:
return "User Disabled";
default:
return ErrorCopy(context, raw).title;
}
}
String title(BuildContext context) => ErrorCopy(context, error: raw).title;
String description(BuildContext context) {
switch (type) {
case ChoreoErrorType.classDisabled:
return "Class Disabled";
case ChoreoErrorType.userDisabled:
return "User Disabled";
default:
return ErrorCopy(context, raw).body;
}
}
String description(BuildContext context) =>
ErrorCopy(context, error: raw).body;
IconData get icon {
switch (type) {
case ChoreoErrorType.classDisabled:
return Icons.history_edu_outlined;
case ChoreoErrorType.userDisabled:
return Icons.history_edu_outlined;
default:
return Icons.error_outline;
}
}
IconData get icon => Icons.error_outline;
}
class ErrorService {

View file

@ -155,7 +155,7 @@ class IgcController {
} catch (err, stack) {
debugger(when: kDebugMode);
choreographer.errorService.setError(
ChoreoError(type: ChoreoErrorType.unknown, raw: err),
ChoreoError(raw: err),
);
ErrorHandler.logError(
e: err,

View file

@ -76,6 +76,7 @@ class ITController {
);
}
clear();
choreographer.errorService.resetError();
dismissed = true;
}
@ -181,7 +182,7 @@ class ITController {
);
}
choreographer.errorService.setErrorAndLock(
ChoreoError(type: ChoreoErrorType.unknown, raw: e),
ChoreoError(raw: e),
);
} finally {
choreographer.stopLoading();
@ -235,7 +236,7 @@ class ITController {
);
}
choreographer.errorService.setErrorAndLock(
ChoreoError(type: ChoreoErrorType.unknown, raw: e),
ChoreoError(raw: e),
);
} finally {
choreographer.stopLoading();
@ -269,7 +270,7 @@ class ITController {
);
}
choreographer.errorService.setErrorAndLock(
ChoreoError(type: ChoreoErrorType.unknown, raw: err),
ChoreoError(raw: err),
);
} finally {
choreographer.stopLoading();

View file

@ -113,6 +113,7 @@ class SpanDataController {
(await response).span;
} catch (err, s) {
ErrorHandler.logError(e: err, s: s, data: req.toJson());
_cache.remove(cacheKey);
}
choreographer.setState();

View file

@ -17,17 +17,15 @@ class ChoreographerHasErrorButton extends StatelessWidget {
Widget build(BuildContext context) {
return FloatingActionButton(
onPressed: () {
if (error.type == ChoreoErrorType.unknown) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
duration: const Duration(seconds: 5),
content: Text(
"${error.title(context)} ${error.description(context)}",
),
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
duration: const Duration(seconds: 5),
content: Text(
"${error.title(context)} ${error.description(context)}",
),
);
choreographer.errorService.resetError();
}
),
);
choreographer.errorService.resetError();
},
mini: true,
child: Icon(error.icon),

View file

@ -1,12 +1,13 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/bot/utils/bot_style.dart';
import 'package:fluffychat/pangea/bot/widgets/bot_face_svg.dart';
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
class CardErrorWidget extends StatelessWidget {
final Object error;
final String error;
final Choreographer? choreographer;
final int? offset;
final double maxWidth;
@ -23,7 +24,11 @@ class CardErrorWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
final ErrorCopy errorCopy = ErrorCopy(context, error);
final ErrorCopy errorCopy = ErrorCopy(
context,
title: L10n.of(context).oopsSomethingWentWrong,
body: error,
);
return Container(
padding: EdgeInsets.all(padding),

View file

@ -188,7 +188,7 @@ class PangeaTextController extends TextEditingController {
);
} catch (e) {
choreographer.errorService.setError(
ChoreoError(type: ChoreoErrorType.unknown, raw: e),
ChoreoError(raw: e),
);
inlineSpans = [TextSpan(text: text, style: style)];
choreographer.igc.clear();

View file

@ -8,7 +8,6 @@ import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/bot/utils/bot_style.dart';
import 'package:fluffychat/pangea/choreographer/enums/span_data_type.dart';
import 'package:fluffychat/pangea/choreographer/models/span_data.dart';
import 'package:fluffychat/pangea/choreographer/widgets/igc/card_error_widget.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/toolbar/controllers/tts_controller.dart';
import '../../../../widgets/matrix.dart';
@ -42,7 +41,6 @@ class SpanCard extends StatefulWidget {
}
class SpanCardState extends State<SpanCard> {
Object? error;
bool fetchingData = false;
int? selectedChoiceIndex;
@ -109,37 +107,20 @@ class SpanCardState extends State<SpanCard> {
}
Future<void> getSpanDetails({bool force = false}) async {
try {
if (widget.scm.pangeaMatch?.isITStart ?? false) return;
if (widget.scm.pangeaMatch?.isITStart ?? false) return;
if (!mounted) return;
setState(() {
fetchingData = true;
});
if (!mounted) return;
setState(() {
fetchingData = true;
});
await widget.scm.choreographer.igc.spanDataController.getSpanDetails(
widget.scm.matchIndex,
force: force,
);
await widget.scm.choreographer.igc.spanDataController.getSpanDetails(
widget.scm.matchIndex,
force: force,
);
if (mounted) {
setState(() => fetchingData = false);
}
} catch (e, s) {
// debugger(when: kDebugMode);
ErrorHandler.logError(
e: e,
s: s,
data: {
"matchIndex": widget.scm.matchIndex,
},
);
if (mounted) {
setState(() {
error = e;
fetchingData = false;
});
}
if (mounted) {
setState(() => fetchingData = false);
}
}
@ -203,13 +184,6 @@ class WordMatchContent extends StatelessWidget {
if (controller.widget.scm.pangeaMatch == null) {
return const SizedBox();
}
if (controller.error != null) {
return CardErrorWidget(
error: controller.error!,
choreographer: controller.widget.scm.choreographer,
offset: controller.widget.scm.pangeaMatch?.match.offset,
);
}
final ScrollController scrollController = ScrollController();

View file

@ -174,7 +174,7 @@ class WordDataCardView extends StatelessWidget {
Widget build(BuildContext context) {
if (controller.wordNetError != null) {
return CardErrorWidget(
error: controller.wordNetError!,
error: controller.wordNetError!.toString(),
maxWidth: AppConfig.toolbarMinWidth,
);
}
@ -187,7 +187,7 @@ class WordDataCardView extends StatelessWidget {
},
);
return CardErrorWidget(
error: controller.noLanguages,
error: L10n.of(context).noLanguagesSet,
maxWidth: AppConfig.toolbarMinWidth,
);
}

View file

@ -4,12 +4,14 @@ import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/choreographer/constants/choreo_constants.dart';
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
import 'package:fluffychat/pangea/choreographer/controllers/it_controller.dart';
import 'package:fluffychat/pangea/choreographer/widgets/igc/word_data_card.dart';
import 'package:fluffychat/pangea/choreographer/widgets/it_feedback_card.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
import 'package:fluffychat/pangea/instructions/instructions_enum.dart';
import 'package:fluffychat/pangea/instructions/instructions_inline_tooltip.dart';
import 'package:fluffychat/pangea/learning_settings/pages/settings_learning.dart';
@ -222,11 +224,7 @@ class ITBarState extends State<ITBar> with SingleTickerProviderStateMixin {
duration: itController.animationSpeed,
child: Center(
child: itController.choreographer.errorService.isError
? ITError(
error: itController
.choreographer.errorService.error!,
controller: itController,
)
? ITError(controller: itController)
: itController.showChoiceFeedback
? ChoiceFeedbackText(
controller: itController,
@ -439,46 +437,34 @@ class ITChoices extends StatelessWidget {
class ITError extends StatelessWidget {
final ITController controller;
final Object error;
const ITError({super.key, required this.error, required this.controller});
const ITError({super.key, required this.controller});
@override
Widget build(BuildContext context) {
final ErrorCopy errorCopy = ErrorCopy(context, error);
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 8),
child: RichText(
text: TextSpan(
children: [
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(
Icons.error_outline,
size: 20,
color: Theme.of(context).colorScheme.error,
),
child: Row(
spacing: 8.0,
mainAxisSize: MainAxisSize.min,
children: [
ErrorIndicator(
message: L10n.of(context).translationError,
style: TextStyle(
fontStyle: FontStyle.italic,
color: Theme.of(context).colorScheme.error,
),
TextSpan(text: " ${errorCopy.title} "),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: IconButton(
onPressed: () {
controller.closeIT();
controller.choreographer.errorService.resetError();
},
icon: const Icon(
Icons.close,
size: 20,
),
),
),
],
style: TextStyle(
fontStyle: FontStyle.italic,
color: Theme.of(context).colorScheme.error,
),
),
textAlign: TextAlign.center,
IconButton(
onPressed: () {
controller.closeIT();
controller.choreographer.errorService.resetError();
},
icon: const Icon(
Icons.close,
size: 20,
),
),
],
),
);
}

View file

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:http/http.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart';
import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_repo.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
@ -87,7 +88,9 @@ class ITFeedbackCardController extends State<ITFeedbackCard> {
@override
Widget build(BuildContext context) => error == null
? ITFeedbackCardView(controller: this)
: CardErrorWidget(error: error!);
: CardErrorWidget(
error: L10n.of(context).errorFetchingDefinition,
);
}
class ITFeedbackCardView extends StatelessWidget {

View file

@ -90,13 +90,20 @@ class ErrorCopy {
late String body;
int? errorCode;
ErrorCopy(this.context, this.error) {
setCopy();
ErrorCopy(
this.context, {
this.error,
String? title,
String? body,
}) {
if (title != null) this.title = title;
if (body != null) this.body = body;
if (title == null || body == null) setCopy();
}
void _setDefaults() {
title = "Unexpected error.";
body = "Please reload and try again.";
title = L10n.of(context).unexpectedError;
body = L10n.of(context).pleaseReload;
errorCode = 400;
}
@ -105,11 +112,6 @@ class ErrorCopy {
if (error is http.Response) {
errorCode = (error as http.Response).statusCode;
} else {
ErrorHandler.logError(
e: error,
s: StackTrace.current,
data: {},
);
errorCode = null;
}
final L10n l10n = L10n.of(context);

View file

@ -18,7 +18,7 @@ class FindYourPeople extends StatefulWidget {
class FindYourPeopleState extends State<FindYourPeople> {
final TextEditingController searchController = TextEditingController();
String? error;
Object? error;
bool loading = true;
Timer? _coolDown;
@ -85,7 +85,7 @@ class FindYourPeopleState extends State<FindYourPeople> {
'searchText': searchController.text,
},
);
error = e.toString();
error = e;
} finally {
if (mounted) {
setState(() {

View file

@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
import 'package:fluffychat/pangea/find_your_people/find_your_people.dart';
import 'package:fluffychat/pangea/find_your_people/public_space_tile.dart';
import 'package:fluffychat/pangea/spaces/utils/space_code.dart';
@ -194,12 +195,12 @@ class FindYourPeopleView extends StatelessWidget {
),
),
controller.error != null
? Row(
? Column(
spacing: 8.0,
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Text(
L10n.of(context).oopsSomethingWentWrong,
ErrorIndicator(
message: L10n.of(context).oopsSomethingWentWrong,
),
IconButton(
onPressed: controller.setSpaceItems,

View file

@ -179,7 +179,7 @@ class _PhoneticTranscriptionWidgetState
else
Flexible(
child: Text(
"/$_transcription/",
_transcription!,
textScaler: TextScaler.noScaling,
style: widget.style ??
Theme.of(context).textTheme.bodyMedium,

View file

@ -15,6 +15,7 @@ import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
import 'package:fluffychat/pangea/chat_settings/utils/download_file.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
import 'package:fluffychat/widgets/matrix.dart';
@ -37,7 +38,7 @@ class DownloadAnalyticsDialogState extends State<DownloadAnalyticsDialog> {
bool get _loading => _downloading || !_initialized;
String? _error;
Object? _error;
Map<String, int> _downloadStatuses = {};
@ -157,7 +158,7 @@ class DownloadAnalyticsDialogState extends State<DownloadAnalyticsDialog> {
);
_clean();
_error = e.toString();
_error = e;
if (mounted) setState(() {});
}
}
@ -460,7 +461,9 @@ class DownloadAnalyticsDialogState extends State<DownloadAnalyticsDialog> {
child: _error != null
? Padding(
padding: const EdgeInsets.all(8.0),
child: Text(L10n.of(context).oopsSomethingWentWrong),
child: ErrorIndicator(
message: L10n.of(context).errorDownloading,
),
)
: const SizedBox(),
),

View file

@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/choreographer/widgets/igc/card_error_widget.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
@ -86,8 +87,8 @@ class MessageTranslationCardState extends State<MessageTranslationCard> {
Widget build(BuildContext context) {
debugPrint('MessageTranslationCard build');
if (!_fetchingTranslation && repEvent == null) {
return const CardErrorWidget(
error: "No translation found",
return CardErrorWidget(
error: L10n.of(context).errorFetchingTranslation,
maxWidth: AppConfig.toolbarMinWidth,
);
}

View file

@ -4,6 +4,7 @@ import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/choreographer/widgets/igc/card_error_widget.dart';
import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';
@ -293,19 +294,10 @@ class PracticeActivityCardState extends State<PracticeActivityCard> {
@override
Widget build(BuildContext context) {
if (_error != null) {
if (_error != null || (!fetchingActivity && currentActivity == null)) {
debugger(when: kDebugMode);
return CardErrorWidget(
error: _error!,
maxWidth: 500,
);
}
if (!fetchingActivity && currentActivity == null) {
debugPrint("don't think we should be here");
debugger(when: kDebugMode);
return CardErrorWidget(
error: _error!,
error: L10n.of(context).errorFetchingActivity,
maxWidth: 500,
);
}

View file

@ -32,7 +32,7 @@ class LemmaMeaningBuilderState extends State<LemmaMeaningBuilder> {
bool editMode = false;
LemmaInfoResponse? lemmaInfo;
bool isLoading = true;
String? error;
Object? error;
TextEditingController controller = TextEditingController();
@ -77,7 +77,7 @@ class LemmaMeaningBuilderState extends State<LemmaMeaningBuilder> {
lemmaInfo = resp;
controller.text = resp.meaning;
} catch (e) {
error = e.toString();
error = e;
} finally {
if (mounted) setState(() => isLoading = false);
}

View file

@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart';
import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart';
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
import 'package:fluffychat/pangea/toolbar/widgets/word_zoom/lemma_meaning_builder.dart';
class LemmaMeaningWidget extends StatelessWidget {
@ -34,9 +35,9 @@ class LemmaMeaningWidget extends StatelessWidget {
if (controller.error != null) {
debugger(when: kDebugMode);
return Text(
L10n.of(context).oopsSomethingWentWrong,
textAlign: TextAlign.center,
return ErrorIndicator(
message: L10n.of(context).errorFetchingDefinition,
style: style,
);
}

View file

@ -4,6 +4,7 @@ import 'package:go_router/go_router.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/learning_settings/models/language_model.dart';
@ -205,9 +206,9 @@ class WordZoomWidget extends StatelessWidget {
controller: overlayController.widget.chatController,
),
if (controller.error != null)
Text(
L10n.of(context).oopsSomethingWentWrong,
textAlign: TextAlign.center,
ErrorIndicator(
message: L10n.of(context).errorFetchingDefinition,
style: const TextStyle(fontSize: 14.0),
)
else if (controller.isLoading ||
controller.lemmaInfo == null)

View file

@ -146,16 +146,17 @@ class UserSettings {
}
@override
int get hashCode =>
dateOfBirth.hashCode ^
createdAt.hashCode ^
autoPlayMessages.hashCode ^
publicProfile.hashCode ^
targetLanguage.hashCode ^
sourceLanguage.hashCode ^
country.hashCode ^
hasJoinedHelpSpace.hashCode ^
cefrLevel.hashCode;
int get hashCode => Object.hashAll([
dateOfBirth.hashCode,
createdAt.hashCode,
autoPlayMessages.hashCode,
publicProfile.hashCode,
targetLanguage.hashCode,
sourceLanguage.hashCode,
country.hashCode,
hasJoinedHelpSpace.hashCode,
cefrLevel.hashCode,
]);
}
/// The user's language tool settings.
@ -254,14 +255,15 @@ class UserToolSettings {
}
@override
int get hashCode =>
interactiveTranslator.hashCode ^
interactiveGrammar.hashCode ^
immersionMode.hashCode ^
definitions.hashCode ^
autoIGC.hashCode ^
enableTTS.hashCode ^
enableAutocorrect.hashCode;
int get hashCode => Object.hashAll([
interactiveTranslator.hashCode,
interactiveGrammar.hashCode,
immersionMode.hashCode,
definitions.hashCode,
autoIGC.hashCode,
enableTTS.hashCode,
enableAutocorrect.hashCode,
]);
}
/// A wrapper around the matrix account data for the user profile.