From 9b72bd8a09605d0572bf40cb965f58bd5b74ecee Mon Sep 17 00:00:00 2001
From: ggurdin <46800240+ggurdin@users.noreply.github.com>
Date: Thu, 7 Aug 2025 14:25:07 -0400
Subject: [PATCH] Sentry (#3655)
* chore: render newlines in html message properly
* chore: log timeouts as warnings
* chore: update log level
---
lib/pages/chat/events/html_message.dart | 4 ++
.../activity_suggestions_area.dart | 13 ++++
.../controllers/choreographer.dart | 1 +
.../controllers/igc_controller.dart | 3 +
.../controllers/it_controller.dart | 2 +
.../pangea_representation_event.dart | 59 +------------------
.../events/models/pangea_token_model.dart | 41 -------------
lib/pangea/morphs/get_grammar_copy.dart | 3 +
.../user/controllers/user_controller.dart | 12 ++++
9 files changed, 40 insertions(+), 98 deletions(-)
diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart
index 59f0f5e1f..1489f6485 100644
--- a/lib/pages/chat/events/html_message.dart
+++ b/lib/pages/chat/events/html_message.dart
@@ -239,6 +239,10 @@ class HtmlMessage extends StatelessWidget {
position = substringIndex;
}
+ for (int i = 0; i < result.length; i++) {
+ if (result[i] == '\n') result[i] = '
';
+ }
+
if (pangeaMessageEvent?.textDirection == TextDirection.rtl) {
for (int i = 0; i < result.length; i++) {
final tag = result[i];
diff --git a/lib/pangea/activity_suggestions/activity_suggestions_area.dart b/lib/pangea/activity_suggestions/activity_suggestions_area.dart
index 01983e281..1071f3368 100644
--- a/lib/pangea/activity_suggestions/activity_suggestions_area.dart
+++ b/lib/pangea/activity_suggestions/activity_suggestions_area.dart
@@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
import 'package:collection/collection.dart';
import 'package:matrix/matrix.dart';
+import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:shimmer/shimmer.dart';
import 'package:fluffychat/config/themes.dart';
@@ -19,6 +20,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/utils/error_handler.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';
@@ -131,6 +133,17 @@ class ActivitySuggestionsAreaState extends State {
);
_activityItems.addAll(resp.activityPlans);
_timeout = false;
+ } catch (e, s) {
+ if (e is! TimeoutException) rethrow;
+ ErrorHandler.logError(
+ e: e,
+ s: s,
+ data: {
+ 'retries': retries,
+ 'request': _request.toJson(),
+ },
+ level: SentryLevel.warning,
+ );
} finally {
if (mounted) setState(() => _loading = false);
}
diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart
index f4ebb045e..74992dc97 100644
--- a/lib/pangea/choreographer/controllers/choreographer.dart
+++ b/lib/pangea/choreographer/controllers/choreographer.dart
@@ -197,6 +197,7 @@ class Choreographer {
"l2LangCode": l2LangCode,
"choreoRecord": choreoRecord?.toJson(),
},
+ level: e is TimeoutException ? SentryLevel.warning : SentryLevel.error,
);
} finally {
chatController.send(
diff --git a/lib/pangea/choreographer/controllers/igc_controller.dart b/lib/pangea/choreographer/controllers/igc_controller.dart
index 163f4f8d1..6d3e0e070 100644
--- a/lib/pangea/choreographer/controllers/igc_controller.dart
+++ b/lib/pangea/choreographer/controllers/igc_controller.dart
@@ -5,6 +5,7 @@ import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:matrix/matrix.dart';
+import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
import 'package:fluffychat/pangea/choreographer/controllers/error_service.dart';
@@ -168,6 +169,8 @@ class IgcController {
"itEnabled": choreographer.itEnabled,
"matches": igcTextData?.matches.map((e) => e.toJson()),
},
+ level:
+ err is TimeoutException ? SentryLevel.warning : SentryLevel.error,
);
clear();
}
diff --git a/lib/pangea/choreographer/controllers/it_controller.dart b/lib/pangea/choreographer/controllers/it_controller.dart
index c39d4e9db..c2b4f3cd0 100644
--- a/lib/pangea/choreographer/controllers/it_controller.dart
+++ b/lib/pangea/choreographer/controllers/it_controller.dart
@@ -180,6 +180,8 @@ class ITController {
"sourceText": sourceText,
"currentITStepPayloadID": currentITStep?.payloadId,
},
+ level:
+ e is TimeoutException ? SentryLevel.warning : SentryLevel.error,
);
}
choreographer.errorService.setErrorAndLock(
diff --git a/lib/pangea/events/event_wrappers/pangea_representation_event.dart b/lib/pangea/events/event_wrappers/pangea_representation_event.dart
index 12b41b83d..21e20e652 100644
--- a/lib/pangea/events/event_wrappers/pangea_representation_event.dart
+++ b/lib/pangea/events/event_wrappers/pangea_representation_event.dart
@@ -76,15 +76,7 @@ class RepresentationEvent {
List? get tokens {
if (_tokens != null) return _tokens!.tokens;
-
- if (_event == null) {
- // debugger(when: kDebugMode);
- // ErrorHandler.logError(
- // m: '_event and _tokens both null',
- // s: StackTrace.current,
- // );
- return null;
- }
+ if (_event == null) return null;
final Set tokenEvents = _event?.aggregatedEvents(
timeline,
@@ -93,54 +85,7 @@ class RepresentationEvent {
{};
if (tokenEvents.isEmpty) return null;
-
- if (tokenEvents.length > 1) {
- // debugger(when: kDebugMode);
- Sentry.addBreadcrumb(
- Breadcrumb(
- message:
- 'should not have more than one tokenEvent per representation ${_event?.eventId}',
- data: {
- "eventID": _event?.eventId,
- "content": tokenEvents.map((e) => e.content).toString(),
- "type": tokenEvents.map((e) => e.type).toString(),
- },
- ),
- );
- }
-
- PangeaMessageTokens? storedTokens;
- for (final tokenEvent in tokenEvents) {
- final tokenPangeaEvent =
- tokenEvent.getPangeaContent();
- if (PangeaToken.reconstructText(tokenPangeaEvent.tokens) != text) {
- Sentry.addBreadcrumb(
- Breadcrumb(
- message: 'Stored tokens do not match text for representation',
- data: {
- 'text': text,
- 'tokens': tokenPangeaEvent.tokens,
- },
- ),
- );
- continue;
- }
- storedTokens = tokenPangeaEvent;
- break;
- }
-
- if (storedTokens == null) {
- ErrorHandler.logError(
- e: "No tokens found for representation",
- data: {
- "event": _event?.toJson(),
- },
- );
- return null;
- }
-
- _tokens = storedTokens;
-
+ _tokens = tokenEvents.last.getPangeaContent();
return _tokens?.tokens;
}
diff --git a/lib/pangea/events/models/pangea_token_model.dart b/lib/pangea/events/models/pangea_token_model.dart
index 05799feb8..c96d9626f 100644
--- a/lib/pangea/events/models/pangea_token_model.dart
+++ b/lib/pangea/events/models/pangea_token_model.dart
@@ -74,47 +74,6 @@ class PangeaToken {
return morphWithPos;
}
- /// reconstructs the text from the tokens
- /// [tokens] - the tokens to reconstruct
- /// [debugWalkThrough] - if true, will start the debugger
- static String reconstructText(
- List tokens, {
- bool debugWalkThrough = false,
- int startTokenIndex = 0,
- int endTokenIndex = -1,
- }) {
- debugger(when: kDebugMode && debugWalkThrough);
-
- if (endTokenIndex == -1) {
- endTokenIndex = tokens.length;
- }
-
- final List subset =
- tokens.sublist(startTokenIndex, endTokenIndex);
-
- if (subset.isEmpty) {
- debugger(when: kDebugMode);
- return '';
- }
-
- if (subset.length == 1) {
- return subset.first.text.content;
- }
-
- String reconstruction = "";
- for (int i = 0; i < subset.length; i++) {
- int whitespace = subset[i].text.offset -
- (i > 0 ? (subset[i - 1].text.offset + subset[i - 1].text.length) : 0);
-
- if (whitespace < 0) {
- whitespace = 0;
- }
- reconstruction += ' ' * whitespace + subset[i].text.content;
- }
-
- return reconstruction;
- }
-
static Lemma _getLemmas(String text, dynamic json) {
if (json != null) {
// July 24, 2024 - we're changing from a list to a single lemma and this is for backwards compatibility
diff --git a/lib/pangea/morphs/get_grammar_copy.dart b/lib/pangea/morphs/get_grammar_copy.dart
index cc2a141e6..ab23c75f9 100644
--- a/lib/pangea/morphs/get_grammar_copy.dart
+++ b/lib/pangea/morphs/get_grammar_copy.dart
@@ -2,6 +2,8 @@
import 'package:flutter/material.dart';
+import 'package:sentry_flutter/sentry_flutter.dart';
+
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
@@ -511,6 +513,7 @@ String? getGrammarCopy({
'tag': key,
'context': context,
},
+ level: SentryLevel.warning,
);
return lemma; // Fallback to the lemma itself if no match is found
}
diff --git a/lib/pangea/user/controllers/user_controller.dart b/lib/pangea/user/controllers/user_controller.dart
index 460fbac6a..3c5b191c5 100644
--- a/lib/pangea/user/controllers/user_controller.dart
+++ b/lib/pangea/user/controllers/user_controller.dart
@@ -3,6 +3,7 @@ import 'dart:async';
import 'package:collection/collection.dart';
import 'package:jwt_decode/jwt_decode.dart';
import 'package:matrix/matrix.dart' as matrix;
+import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:fluffychat/pangea/analytics_misc/client_analytics_extension.dart';
import 'package:fluffychat/pangea/bot/utils/bot_name.dart';
@@ -200,6 +201,17 @@ class UserController {
updatePublicProfile(
level: _pangeaController.getAnalytics.constructListModel.level,
);
+ }).catchError((e, s) {
+ ErrorHandler.logError(
+ e: e,
+ s: s,
+ data: {
+ "publicProfile": publicProfile?.toJson(),
+ "userId": client.userID,
+ },
+ level:
+ e is TimeoutException ? SentryLevel.warning : SentryLevel.error,
+ );
});
}
}