More specific error messages (#3418)

* chore: add more specific error messages

* chore: more specific error messages

* chore: more specific error messages

* chore: more specific error messages
This commit is contained in:
ggurdin 2025-07-11 09:51:49 -04:00 committed by GitHub
parent 7934fc8b15
commit a41b2d2dde
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 194 additions and 195 deletions

View file

@ -5061,5 +5061,14 @@
}
}
},
"saved": "Saved"
"saved": "Saved",
"reset": "Reset",
"errorGenerateActivityMessage": "Failed to generate activity",
"errorRegenerateActivityMessage": "Failed to regenerate activity",
"errorFetchingActivitiesMessage": "Failed to fetch activities",
"errorFetchingDefinition": "Failed to fetch definition",
"errorProcessAnalytics": "Failed to process analytics",
"errorDownloading": "Download failed",
"errorFetchingLevelSummary": "Failed to fetch level summary",
"errorLoadingSpaceChildren": "Failed to load chats within this space"
}

View file

@ -33,7 +33,7 @@ class ActivityGenerator extends StatefulWidget {
class ActivityGeneratorState extends State<ActivityGenerator> {
bool loading = false;
String? error;
Object? error;
List<ActivityPlanModel>? activities;
final formKey = GlobalKey<FormState>();
@ -219,7 +219,7 @@ class ActivityGeneratorState extends State<ActivityGenerator> {
activities = resp.activityPlans;
await _setModeImageURL();
} catch (e, s) {
error = e.toString();
error = e;
ErrorHandler.logError(
e: e,
s: s,

View file

@ -11,6 +11,7 @@ import 'package:fluffychat/pangea/activity_planner/activity_planner_builder.dart
import 'package:fluffychat/pangea/activity_planner/suggestion_form_field.dart';
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestions_constants.dart';
import 'package:fluffychat/pangea/chat_settings/widgets/language_level_dropdown.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/utils/p_language_store.dart';
@ -38,10 +39,12 @@ class ActivityGeneratorView extends StatelessWidget {
} else if (controller.error != null) {
body = Center(
child: Column(
spacing: 16.0,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(l10n.oopsSomethingWentWrong),
const SizedBox(height: 16),
ErrorIndicator(
message: l10n.errorGenerateActivityMessage,
),
ElevatedButton(
onPressed: controller.generate,
child: Text(l10n.tryAgain),

View file

@ -56,7 +56,7 @@ class ActivityPlannerBuilderState extends State<ActivityPlannerBuilder> {
@override
void initState() {
super.initState();
_resetActivity();
resetActivity();
}
@override
@ -91,7 +91,7 @@ class ActivityPlannerBuilderState extends State<ActivityPlannerBuilder> {
);
}
Future<void> _resetActivity() async {
Future<void> resetActivity() async {
avatar = null;
filename = null;
imageURL = null;
@ -230,7 +230,7 @@ class ActivityPlannerBuilderState extends State<ActivityPlannerBuilder> {
}
Future<void> clearEdits() async {
await _resetActivity();
await resetActivity();
if (mounted) {
setState(() {
isEditing = false;

View file

@ -14,6 +14,7 @@ import 'package:fluffychat/pangea/activity_suggestions/activity_room_selection.d
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_card_row.dart';
import 'package:fluffychat/pangea/chat_settings/widgets/language_level_dropdown.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart';
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
@ -117,9 +118,26 @@ class ActivitySuggestionDialogState extends State<ActivitySuggestionDialog> {
}
}
void _resetActivity() {
widget.controller.resetActivity();
setState(() {
_pageMode = _PageMode.activity;
_loading = false;
_error = null;
});
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final buttonStyle = ElevatedButton.styleFrom(
backgroundColor: theme.colorScheme.primaryContainer,
foregroundColor: theme.colorScheme.onPrimaryContainer,
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
),
);
final body = Stack(
alignment: Alignment.topCenter,
children: [
@ -132,12 +150,30 @@ class ActivitySuggestionDialogState extends State<ActivitySuggestionDialog> {
if (_pageMode == _PageMode.activity) {
if (_error != null) {
return Center(
child: Row(
spacing: 8.0,
child: Column(
spacing: 16.0,
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.error, color: theme.colorScheme.error),
Text(L10n.of(context).oopsSomethingWentWrong),
ErrorIndicator(
message:
L10n.of(context).errorRegenerateActivityMessage,
),
Row(
spacing: 8.0,
mainAxisSize: MainAxisSize.min,
children: [
ElevatedButton(
onPressed: _onRegenerate,
style: buttonStyle,
child: Text(L10n.of(context).tryAgain),
),
ElevatedButton(
onPressed: _resetActivity,
style: buttonStyle,
child: Text(L10n.of(context).reset),
),
],
),
],
),
);
@ -547,15 +583,7 @@ class ActivitySuggestionDialogState extends State<ActivitySuggestionDialog> {
children: [
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
theme.colorScheme.primaryContainer,
foregroundColor: theme
.colorScheme.onPrimaryContainer,
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
),
),
style: buttonStyle,
onPressed: widget.controller.saveEdits,
child: Row(
children: [
@ -572,15 +600,7 @@ class ActivitySuggestionDialogState extends State<ActivitySuggestionDialog> {
),
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor:
theme.colorScheme.primaryContainer,
foregroundColor: theme
.colorScheme.onPrimaryContainer,
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
),
),
style: buttonStyle,
onPressed: widget.controller.clearEdits,
child: Row(
children: [
@ -605,15 +625,7 @@ class ActivitySuggestionDialogState extends State<ActivitySuggestionDialog> {
children: [
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: theme
.colorScheme.primaryContainer,
foregroundColor: theme
.colorScheme.onPrimaryContainer,
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
),
),
style: buttonStyle,
child: Row(
children: [
const Icon(Icons.edit),
@ -632,16 +644,7 @@ class ActivitySuggestionDialogState extends State<ActivitySuggestionDialog> {
if (widget.replaceActivity != null)
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: theme
.colorScheme.primaryContainer,
foregroundColor: theme.colorScheme
.onPrimaryContainer,
padding:
const EdgeInsets.symmetric(
horizontal: 12.0,
),
),
style: buttonStyle,
onPressed: _onRegenerate,
child: Row(
children: [
@ -664,15 +667,7 @@ class ActivitySuggestionDialogState extends State<ActivitySuggestionDialog> {
children: [
Expanded(
child: ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: theme
.colorScheme.primaryContainer,
foregroundColor: theme
.colorScheme.onPrimaryContainer,
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
),
),
style: buttonStyle,
onPressed: _launchActivity,
child: Row(
children: [

View file

@ -19,6 +19,7 @@ import 'package:fluffychat/pangea/activity_planner/media_enum.dart';
import 'package:fluffychat/pangea/activity_suggestions/activity_plan_search_repo.dart';
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_card.dart';
import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_dialog.dart';
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart';
import 'package:fluffychat/pangea/learning_settings/enums/language_level_type_enum.dart';
import 'package:fluffychat/widgets/matrix.dart';
@ -206,25 +207,27 @@ class ActivitySuggestionsAreaState extends State<ActivitySuggestionsArea> {
child: (_timeout || !_loading && cards.isEmpty)
? Padding(
padding: const EdgeInsets.all(8.0),
child: RichText(
text: TextSpan(
style: DefaultTextStyle.of(context).style,
children: [
const WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: Icon(
Icons.info_outline,
child: Column(
spacing: 16.0,
mainAxisSize: MainAxisSize.min,
children: [
ErrorIndicator(
message: _timeout
? L10n.of(context).activitySuggestionTimeoutMessage
: L10n.of(context).errorFetchingActivitiesMessage,
),
ElevatedButton(
onPressed: _setActivityItems,
style: ElevatedButton.styleFrom(
backgroundColor: theme.colorScheme.primaryContainer,
foregroundColor: theme.colorScheme.onPrimaryContainer,
padding: const EdgeInsets.symmetric(
horizontal: 12.0,
),
),
const TextSpan(text: " "),
TextSpan(
text: _timeout
? L10n.of(context)
.activitySuggestionTimeoutMessage
: L10n.of(context).oopsSomethingWentWrong,
),
],
),
child: Text(L10n.of(context).tryAgain),
),
],
),
)
: Container(

View file

@ -1,16 +1,11 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:material_symbols_icons/symbols.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics_details_popup/morph_analytics_list_view.dart';
import 'package:fluffychat/pangea/analytics_details_popup/morph_details_view.dart';
import 'package:fluffychat/pangea/analytics_details_popup/vocab_analytics_details_view.dart';
import 'package:fluffychat/pangea/analytics_details_popup/vocab_analytics_list_view.dart';
import 'package:fluffychat/pangea/analytics_downloads/analytics_download_button.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_indicators_enum.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
import 'package:fluffychat/pangea/constructs/construct_level_enum.dart';
@ -25,13 +20,11 @@ class AnalyticsPopupWrapper extends StatefulWidget {
this.constructZoom,
required this.view,
this.backButtonOverride,
this.showAppBar = true,
});
final ConstructTypeEnum view;
final ConstructIdentifier? constructZoom;
final Widget? backButtonOverride;
final bool showAppBar;
@override
AnalyticsPopupWrapperState createState() => AnalyticsPopupWrapperState();
@ -124,84 +117,31 @@ class AnalyticsPopupWrapperState extends State<AnalyticsPopupWrapper> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: widget.showAppBar
? AppBar(
title: kIsWeb
? Text(
localView == ConstructTypeEnum.morph
? ConstructTypeEnum.morph.indicator.tooltip(context)
: ConstructTypeEnum.vocab.indicator.tooltip(context),
)
: null,
leading: widget.backButtonOverride ??
IconButton(
icon: localConstructZoom == null
? const Icon(Icons.close)
: const Icon(Icons.arrow_back),
onPressed: localConstructZoom == null
? () => Navigator.of(context).pop()
: () => setConstructZoom(null),
),
actions: [
TextButton.icon(
style: TextButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
backgroundColor: localView == ConstructTypeEnum.vocab
? Theme.of(context).colorScheme.primary.withAlpha(50)
: Theme.of(context).colorScheme.surface,
),
label: Text(L10n.of(context).vocab),
icon: const Icon(Symbols.dictionary),
onPressed: () => setState(() {
localView = ConstructTypeEnum.vocab;
localConstructZoom = null;
}),
),
const SizedBox(width: 4.0),
TextButton.icon(
style: TextButton.styleFrom(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10.0),
),
backgroundColor: localView == ConstructTypeEnum.morph
? Theme.of(context).colorScheme.primary.withAlpha(50)
: Theme.of(context).colorScheme.surface,
),
label: Text(L10n.of(context).grammar),
icon: const Icon(Symbols.toys_and_games),
onPressed: () => setState(() {
localView = ConstructTypeEnum.morph;
localConstructZoom = null;
}),
),
const SizedBox(width: 4.0),
if (kIsWeb) const DownloadAnalyticsButton(),
if (kIsWeb) const SizedBox(width: 4.0),
],
)
: null,
body: Stack(
body: Column(
children: [
localView == ConstructTypeEnum.morph
? localConstructZoom == null
? MorphAnalyticsListView(controller: this)
: MorphDetailsView(constructId: localConstructZoom!)
: localConstructZoom == null
? VocabAnalyticsListView(controller: this)
: VocabDetailsView(constructId: localConstructZoom!),
if (localConstructZoom != null)
Positioned(
top: 0,
left: 0,
child: IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
setConstructZoom(null);
},
),
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
localConstructZoom != null
? IconButton(
icon: const Icon(Icons.arrow_back),
onPressed: () {
setConstructZoom(null);
},
)
: const SizedBox(),
const DownloadAnalyticsButton(),
],
),
Expanded(
child: localView == ConstructTypeEnum.morph
? localConstructZoom == null
? MorphAnalyticsListView(controller: this)
: MorphDetailsView(constructId: localConstructZoom!)
: localConstructZoom == null
? VocabAnalyticsListView(controller: this)
: VocabDetailsView(constructId: localConstructZoom!),
),
],
),
);

View file

@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart';
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
import 'package:fluffychat/pangea/morphs/morph_meaning/morph_info_repo.dart';
@ -32,7 +33,7 @@ class MorphMeaningWidgetState extends State<MorphMeaningWidget> {
static const int maxCharacters = 140;
String? _cachedResponse;
bool _isLoading = true;
String? _error;
Object? _error;
@override
void didUpdateWidget(covariant MorphMeaningWidget oldWidget) {
@ -62,7 +63,8 @@ class MorphMeaningWidgetState extends State<MorphMeaningWidget> {
final response = await _morphMeaning();
_setMeaningText(response);
} catch (e) {
_error = e.toString();
debugger(when: kDebugMode);
_error = e;
} finally {
if (mounted) setState(() => _isLoading = false);
}
@ -114,11 +116,10 @@ class MorphMeaningWidgetState extends State<MorphMeaningWidget> {
}
if (_error != null) {
debugger(when: kDebugMode);
return Text(
L10n.of(context).oopsSomethingWentWrong,
textAlign: TextAlign.center,
style: widget.style,
return Center(
child: ErrorIndicator(
message: L10n.of(context).errorFetchingDefinition,
),
);
}

View file

@ -15,6 +15,7 @@ import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/analytics_misc/learning_skills_enum.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/constructs/construct_identifier.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/morphs/get_grammar_copy.dart';
@ -49,6 +50,9 @@ class AnalyticsDownloadDialogState extends State<AnalyticsDownloadDialog> {
}
Future<void> _downloadAnalytics() async {
List<AnalyticsSummaryModel> vocabSummary;
List<AnalyticsSummaryModel> morphSummary;
try {
setState(() {
_downloading = true;
@ -56,9 +60,27 @@ class AnalyticsDownloadDialogState extends State<AnalyticsDownloadDialog> {
_error = null;
});
final vocabSummary = await _getVocabAnalytics();
final morphSummary = await _getMorphAnalytics();
vocabSummary = await _getVocabAnalytics();
morphSummary = await _getMorphAnalytics();
} catch (e, s) {
ErrorHandler.logError(
e: e,
s: s,
data: {
"downloadType": _downloadType,
},
);
if (mounted) {
setState(() {
_downloading = false;
_error = L10n.of(context).errorProcessAnalytics;
});
}
return;
}
try {
final content = _getExcelFileContent({
ConstructTypeEnum.vocab: vocabSummary,
ConstructTypeEnum.morph: morphSummary,
@ -72,6 +94,7 @@ class AnalyticsDownloadDialogState extends State<AnalyticsDownloadDialog> {
fileName,
_downloadType,
);
_downloaded = true;
} catch (e, s) {
ErrorHandler.logError(
e: e,
@ -80,12 +103,9 @@ class AnalyticsDownloadDialogState extends State<AnalyticsDownloadDialog> {
"downloadType": _downloadType,
},
);
_error = e.toString();
_error = L10n.of(context).errorDownloading;
} finally {
setState(() {
_downloading = false;
_downloaded = true;
});
if (mounted) setState(() => _downloading = false);
}
}
@ -376,7 +396,9 @@ class AnalyticsDownloadDialogState extends State<AnalyticsDownloadDialog> {
child: _error != null
? Padding(
padding: const EdgeInsets.all(8.0),
child: Text(L10n.of(context).oopsSomethingWentWrong),
child: ErrorIndicator(
message: _error!,
),
)
: const SizedBox(),
),

View file

@ -18,6 +18,7 @@ import 'package:fluffychat/pangea/analytics_misc/level_up/level_up_manager.dart'
import 'package:fluffychat/pangea/analytics_misc/level_up/rain_confetti.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_bar/level_bar.dart';
import 'package:fluffychat/pangea/analytics_summary/progress_bar/progress_bar_details.dart';
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
import 'package:fluffychat/pangea/common/widgets/full_width_dialog.dart';
import 'package:fluffychat/pangea/constructs/construct_repo.dart';
import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart';
@ -386,12 +387,8 @@ class _LevelUpPopupContentState extends State<LevelUpPopupContent>
else if (_error != null)
Padding(
padding: const EdgeInsets.all(16.0),
child: Text(
L10n.of(context).oopsSomethingWentWrong,
style: TextStyle(
color: Theme.of(context).colorScheme.error,
fontSize: 16,
),
child: ErrorIndicator(
message: L10n.of(context).errorFetchingLevelSummary,
),
)
else if (_constructSummary != null)

View file

@ -58,14 +58,12 @@ class AnalyticsPageView extends StatelessWidget {
return AnalyticsPopupWrapper(
constructZoom: controller.widget.constructZoom,
view: ConstructTypeEnum.morph,
showAppBar: false,
);
} else if (controller.selectedIndicator ==
ProgressIndicatorEnum.wordsUsed) {
return AnalyticsPopupWrapper(
constructZoom: controller.widget.constructZoom,
view: ConstructTypeEnum.vocab,
showAppBar: false,
);
}

View file

@ -6,6 +6,7 @@ import 'package:fluffychat/config/themes.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/chat_settings/utils/delete_room.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';
class DeleteSpaceDialog extends StatefulWidget {
@ -32,7 +33,9 @@ class DeleteSpaceDialogState extends State<DeleteSpaceDialog> {
@override
void initState() {
super.initState();
_getSpaceChildrenToDelete();
WidgetsBinding.instance.addPostFrameCallback(
(_) => _getSpaceChildrenToDelete(),
);
}
Future<void> _getSpaceChildrenToDelete() async {
@ -44,7 +47,7 @@ class DeleteSpaceDialogState extends State<DeleteSpaceDialog> {
try {
_rooms = await widget.space.getSpaceChildrenToDelete();
} catch (e, s) {
_roomLoadError = L10n.of(context).oopsSomethingWentWrong;
_roomLoadError = L10n.of(context).errorLoadingSpaceChildren;
ErrorHandler.logError(
e: e,
s: s,
@ -162,16 +165,9 @@ class DeleteSpaceDialogState extends State<DeleteSpaceDialog> {
if (_roomLoadError != null) {
return Center(
child: Column(
spacing: 8.0,
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.error_outline,
color: Theme.of(context).colorScheme.error,
),
Text(L10n.of(context).oopsSomethingWentWrong),
],
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: ErrorIndicator(message: _roomLoadError!),
),
);
}
@ -252,7 +248,7 @@ class DeleteSpaceDialogState extends State<DeleteSpaceDialog> {
child: _deleteError != null
? Padding(
padding: const EdgeInsets.all(8.0),
child: Text(L10n.of(context).oopsSomethingWentWrong),
child: Text(_deleteError!),
)
: const SizedBox(),
),

View file

@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
class ErrorIndicator extends StatelessWidget {
final String message;
final double? iconSize;
final TextStyle? style;
const ErrorIndicator({
super.key,
required this.message,
this.iconSize,
this.style,
});
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.error,
color: Theme.of(context).colorScheme.error,
size: iconSize ?? 24.0,
),
const SizedBox(width: 8),
Text(
message,
style: style,
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
);
}
}