diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 7fb15a581..2d0a3c823 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4493,5 +4493,6 @@ "enterLanguageLevel": "Please enter a language level", "enterDiscussionTopic": "Please enter a discussion topic", "selectBotChatMode": "Select chat mode", - "messageNotInTargetLang": "Message not in target language" + "messageNotInTargetLang": "Message not in target language", + "other": "Other" } \ No newline at end of file diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 8b8969181..e8aa4e3f9 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -1167,6 +1167,13 @@ class ChatController extends State } void sendAgainAction() { + // #Pangea + if (selectedEvents.isEmpty) { + ErrorHandler.logError(e: "No selected events in send again action"); + clearSelectedEvents(); + return; + } + // Pangea# final event = selectedEvents.first; if (event.status.isError) { event.sendAgain(); diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index 0167d5d21..9b0e5883f 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -96,43 +96,67 @@ class ChatInputRow extends StatelessWidget { else // Pangea# controller.selectedEvents.length == 1 - ? controller.selectedEvents.first - .getDisplayEvent(controller.timeline!) - .status - .isSent - ? SizedBox( - height: height, - child: TextButton( - onPressed: controller.replyAction, - child: Row( - children: [ - // #Pangea - // Text(L10n.of(context)!.reply), - // const Icon(Icons.keyboard_arrow_right), - const Icon(Symbols.reply), - const SizedBox(width: 6), - Text(L10n.of(context)!.reply), - // Pangea# - ], - ), - ), - ) - : SizedBox( - height: height, - child: TextButton( - onPressed: controller.sendAgainAction, - child: Row( - children: [ - Text(L10n.of(context)!.tryToSendAgain), - const SizedBox(width: 4), - const Icon(Icons.send_outlined, size: 16), - ], - ), - ), - ) + ? + // #Pangea + // controller.selectedEvents.first + // .getDisplayEvent(controller.timeline!) + // .status + // .isSent + // ? + // Pangea# + SizedBox( + height: height, + child: TextButton( + onPressed: controller.replyAction, + child: Row( + children: [ + // #Pangea + // Text(L10n.of(context)!.reply), + // const Icon(Icons.keyboard_arrow_right), + const Icon(Symbols.reply), + const SizedBox(width: 6), + Text(L10n.of(context)!.reply), + // Pangea# + ], + ), + ), + ) + // #Pangea + // : SizedBox( + // height: height, + // child: TextButton( + // onPressed: controller.sendAgainAction, + // child: Row( + // children: [ + // Text(L10n.of(context)!.tryToSendAgain), + // const SizedBox(width: 4), + // const Icon(Icons.send_outlined, size: 16), + // ], + // ), + // ), + // ) + // Pangea# : const SizedBox.shrink(), // #Pangea PangeaReactionsPicker(controller), + if (controller.selectedEvents.length == 1 && + !controller.selectedEvents.first + .getDisplayEvent(controller.timeline!) + .status + .isSent) + SizedBox( + height: height, + child: TextButton( + onPressed: controller.sendAgainAction, + child: Row( + children: [ + Text(L10n.of(context)!.tryToSendAgain), + const SizedBox(width: 4), + const Icon(Icons.send_outlined, size: 16), + ], + ), + ), + ), // Pangea# ] : [ diff --git a/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart b/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart index 5d4819de9..da6c798cb 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart @@ -130,7 +130,7 @@ class RepresentationEvent { data: { 'content': content.toJson(), 'event': _event?.toJson(), - 'timestamp': timestamp, + 'timestamp': timestamp.toIso8601String(), 'senderID': senderID, }, ); diff --git a/lib/pangea/models/analytics/construct_list_model.dart b/lib/pangea/models/analytics/construct_list_model.dart index 0f29d4cab..12bd7fbe3 100644 --- a/lib/pangea/models/analytics/construct_list_model.dart +++ b/lib/pangea/models/analytics/construct_list_model.dart @@ -4,6 +4,7 @@ import 'package:fluffychat/pangea/enum/construct_type_enum.dart'; import 'package:fluffychat/pangea/enum/construct_use_type_enum.dart'; import 'package:fluffychat/pangea/models/analytics/constructs_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; +import 'package:fluffychat/pangea/utils/error_handler.dart'; /// A wrapper around a list of [OneConstructUse]s, used to simplify /// the process of filtering / sorting / displaying the events. @@ -104,7 +105,23 @@ class ConstructListModel { 0, (total, construct) => total + construct.points, ); - level = 1 + sqrt((1 + 8 * totalXP / 100) / 2).floor(); + + // Don't call .floor() if NaN or Infinity + // https://pangea-chat.sentry.io/issues/6052871310 + final double levelCalculation = 1 + sqrt((1 + 8 * totalXP / 100) / 2); + if (!levelCalculation.isNaN && levelCalculation.isFinite) { + level = levelCalculation.floor(); + } else { + level = 0; + ErrorHandler.logError( + e: "Calculated level in Nan or Infinity", + data: { + "totalXP": totalXP, + "prevXP": prevXP, + "level": levelCalculation, + }, + ); + } } ConstructUses? getConstructUses(ConstructIdentifier identifier) { diff --git a/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_popup.dart b/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_popup.dart index 38605e53e..ef8f456ce 100644 --- a/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_popup.dart +++ b/lib/pangea/widgets/chat_list/analytics_summary/analytics_popup/analytics_popup.dart @@ -55,12 +55,17 @@ class AnalyticsPopupState extends State { selectedCategory = category; }); - String categoryCopy(category) => - widget.type.getDisplayCopy( - category, - context, - ) ?? - category; + String categoryCopy(category) { + if (category.toLowerCase() == "other") { + return L10n.of(context)!.other; + } + + return widget.type.getDisplayCopy( + category, + context, + ) ?? + category; + } @override Widget build(BuildContext context) { diff --git a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart index e8112ea79..f795099b7 100644 --- a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart +++ b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart @@ -7,6 +7,7 @@ import 'package:fluffychat/pangea/enum/activity_type_enum.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_record_model.dart'; import 'package:fluffychat/pangea/utils/bot_style.dart'; +import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/widgets/chat/tts_controller.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/word_audio_button.dart'; @@ -68,7 +69,16 @@ class MultipleChoiceActivityState extends State { ); if (currentRecordModel == null || - currentRecordModel!.latestResponse == null) { + currentRecordModel?.latestResponse == null || + widget.practiceCardController.currentActivity == null) { + ErrorHandler.logError( + e: "Missing necessary information to send analytics in multiple choice activity", + data: { + "currentRecordModel": currentRecordModel, + "latestResponse": currentRecordModel?.latestResponse, + "currentActivity": widget.practiceCardController.currentActivity, + }, + ); debugger(when: kDebugMode); return; } diff --git a/lib/widgets/error_widget.dart b/lib/widgets/error_widget.dart index e21e5e4e8..436262945 100644 --- a/lib/widgets/error_widget.dart +++ b/lib/widgets/error_widget.dart @@ -1,6 +1,5 @@ -import 'package:flutter/material.dart'; - import 'package:fluffychat/utils/error_reporter.dart'; +import 'package:flutter/material.dart'; class FluffyChatErrorWidget extends StatefulWidget { final FlutterErrorDetails details; @@ -21,6 +20,10 @@ class _FluffyChatErrorWidgetState extends State { } knownExceptions.add(widget.details.exception.toString()); WidgetsBinding.instance.addPostFrameCallback((_) { + // #Pangea + // related sentry issue: https://pangea-chat.sentry.io/issues/5970490357 + if (!context.mounted) return; + // Pangea# ErrorReporter(context, 'Error Widget').onErrorCallback( widget.details.exception, widget.details.stack,