diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart
index 393a1fa46..67263af7b 100644
--- a/lib/pages/chat/events/html_message.dart
+++ b/lib/pages/chat/events/html_message.dart
@@ -14,7 +14,7 @@ import 'package:fluffychat/pages/chat/chat.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/message_token_text/message_token_button.dart';
-import 'package:fluffychat/pangea/message_token_text/token_position_model.dart';
+import 'package:fluffychat/pangea/message_token_text/tokens_util.dart';
import 'package:fluffychat/pangea/toolbar/enums/reading_assistance_mode_enum.dart';
import 'package:fluffychat/pangea/toolbar/utils/token_rendering_util.dart';
import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart';
@@ -396,6 +396,9 @@ class HtmlMessage extends StatelessWidget {
);
final fontSize = renderer.fontSize(context) ?? this.fontSize;
+ final newTokens = pangeaMessageEvent != null
+ ? TokensUtil.getNewTokens(pangeaMessageEvent!)
+ : [];
// Pangea#
switch (node.localName) {
@@ -415,10 +418,7 @@ class HtmlMessage extends StatelessWidget {
? isHighlighted!.call(token)
: false;
- final isNew = token != null &&
- overlayController != null &&
- overlayController!.isNewToken(token);
-
+ final isNew = token != null && newTokens.contains(token.text);
final tokenWidth = renderer.tokenTextWidthForContainer(
context,
node.text,
diff --git a/lib/pangea/course_plans/course_plan_builder.dart b/lib/pangea/course_plans/course_plan_builder.dart
index 8618dcaa1..e83847b02 100644
--- a/lib/pangea/course_plans/course_plan_builder.dart
+++ b/lib/pangea/course_plans/course_plan_builder.dart
@@ -68,9 +68,7 @@ class CoursePlanController extends State {
widget.onNotFound?.call();
error = e;
} finally {
- setState(() {
- loading = false;
- });
+ if (mounted) setState(() => loading = false);
}
}
diff --git a/lib/pangea/message_token_text/token_position_model.dart b/lib/pangea/message_token_text/tokens_util.dart
similarity index 69%
rename from lib/pangea/message_token_text/token_position_model.dart
rename to lib/pangea/message_token_text/tokens_util.dart
index cd5a47355..1922f656d 100644
--- a/lib/pangea/message_token_text/token_position_model.dart
+++ b/lib/pangea/message_token_text/tokens_util.dart
@@ -1,4 +1,27 @@
+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/events/models/pangea_token_text_model.dart';
+import 'package:fluffychat/widgets/matrix.dart';
+
+class _TokenPositionCacheItem {
+ final List positions;
+ final DateTime timestamp;
+
+ _TokenPositionCacheItem(
+ this.positions,
+ this.timestamp,
+ );
+}
+
+class _NewTokenCacheItem {
+ final List tokens;
+ final DateTime timestamp;
+
+ _NewTokenCacheItem(
+ this.tokens,
+ this.timestamp,
+ );
+}
class TokenPosition {
final PangeaToken? token;
@@ -15,9 +38,74 @@ class TokenPosition {
class TokensUtil {
/// A cache of calculated adjacent token positions
static final Map _tokenPositionCache = {};
+ static final Map _newTokenCache = {};
static const Duration _cacheDuration = Duration(minutes: 1);
+ static List? _getCachedNewTokens(String eventID) {
+ final cacheItem = _newTokenCache[eventID];
+ if (cacheItem == null) return null;
+ if (cacheItem.timestamp.isBefore(DateTime.now().subtract(_cacheDuration))) {
+ _newTokenCache.remove(eventID);
+ return null;
+ }
+
+ return cacheItem.tokens;
+ }
+
+ static void _setCachedNewTokens(
+ String eventID,
+ List tokens,
+ ) {
+ _newTokenCache[eventID] = _NewTokenCacheItem(
+ tokens,
+ DateTime.now(),
+ );
+ }
+
+ static List getNewTokens(
+ PangeaMessageEvent event,
+ ) {
+ final messageInUserL2 = event.messageDisplayLangCode.split("-")[0] ==
+ MatrixState.pangeaController.languageController.userL2?.langCodeShort;
+
+ final cached = _getCachedNewTokens(event.eventId);
+ if (cached != null) {
+ if (!messageInUserL2) {
+ _newTokenCache.remove(event.eventId);
+ return [];
+ }
+ return cached;
+ }
+
+ final tokens = event.messageDisplayRepresentation?.tokens;
+ if (!messageInUserL2 || tokens == null || tokens.isEmpty) {
+ return [];
+ }
+
+ final List newTokens = [];
+ for (final token in tokens) {
+ if (!token.lemma.saveVocab || !token.isContentWord) continue;
+ if (token.vocabConstruct.uses.isNotEmpty) continue;
+ if (newTokens.any((t) => t == token.text)) continue;
+
+ newTokens.add(token.text);
+ if (newTokens.length >= 3) break;
+ }
+
+ _setCachedNewTokens(event.eventId, newTokens);
+ return newTokens;
+ }
+
+ static bool isNewToken(PangeaToken token, PangeaMessageEvent event) {
+ final newTokens = getNewTokens(event);
+ return newTokens.any((t) => t == token.text);
+ }
+
+ static clearNewTokenCache(String eventID) {
+ _newTokenCache.remove(eventID);
+ }
+
static List? _getCachedTokenPositions(String eventID) {
final cacheItem = _tokenPositionCache[eventID];
if (cacheItem == null) return null;
@@ -158,13 +246,3 @@ class TokensUtil {
return tokenPositions;
}
}
-
-class _TokenPositionCacheItem {
- final List positions;
- final DateTime timestamp;
-
- _TokenPositionCacheItem(
- this.positions,
- this.timestamp,
- );
-}
diff --git a/lib/pangea/toolbar/widgets/message_selection_overlay.dart b/lib/pangea/toolbar/widgets/message_selection_overlay.dart
index 43bcbb1dd..ddd0f5c64 100644
--- a/lib/pangea/toolbar/widgets/message_selection_overlay.dart
+++ b/lib/pangea/toolbar/widgets/message_selection_overlay.dart
@@ -19,6 +19,7 @@ import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dar
import 'package:fluffychat/pangea/events/event_wrappers/pangea_representation_event.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_text_model.dart';
+import 'package:fluffychat/pangea/message_token_text/tokens_util.dart';
import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart';
import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart';
import 'package:fluffychat/pangea/practice_activities/practice_choice.dart';
@@ -102,8 +103,6 @@ class MessageOverlayController extends State
double maxWidth = AppConfig.toolbarMinWidth;
- List newTokens = [];
-
/////////////////////////////////////
/// Lifecycle
/////////////////////////////////////
@@ -148,14 +147,6 @@ class MessageOverlayController extends State
MatrixState.pangeaController.languageController.userL2!.langCode,
);
}
-
- newTokens = pangeaMessageEvent?.messageDisplayRepresentation?.tokens
- ?.where((token) {
- return token.lemma.saveVocab == true &&
- token.vocabConstruct.uses.isEmpty &&
- messageInUserL2;
- }).toList() ??
- [];
} catch (e, s) {
debugger(when: kDebugMode);
ErrorHandler.logError(
@@ -554,14 +545,8 @@ class MessageOverlayController extends State
);
if (mounted) {
- setState(() {
- newTokens.removeWhere(
- (t) =>
- t.text.offset == token.text.offset &&
- t.text.length == token.text.length &&
- t.lemma.text.equals(token.lemma.text),
- );
- });
+ TokensUtil.clearNewTokenCache(event.eventId);
+ setState(() {});
}
}
@@ -579,14 +564,8 @@ class MessageOverlayController extends State
return isSelected;
}
- bool isNewToken(PangeaToken token) {
- if (newTokens.isEmpty) return false;
- return newTokens.any(
- (t) =>
- t.text.offset == token.text.offset &&
- t.text.length == token.text.length,
- );
- }
+ bool isNewToken(PangeaToken token) =>
+ TokensUtil.isNewToken(token, pangeaMessageEvent!);
bool isTokenHighlighted(PangeaToken token) {
if (_highlightedTokens == null) return false;
diff --git a/lib/pangea/toolbar/widgets/stt_transcript_tokens.dart b/lib/pangea/toolbar/widgets/stt_transcript_tokens.dart
index e63bcb7df..03247a35f 100644
--- a/lib/pangea/toolbar/widgets/stt_transcript_tokens.dart
+++ b/lib/pangea/toolbar/widgets/stt_transcript_tokens.dart
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
-import 'package:fluffychat/pangea/message_token_text/token_position_model.dart';
+import 'package:fluffychat/pangea/message_token_text/tokens_util.dart';
import 'package:fluffychat/pangea/toolbar/models/speech_to_text_models.dart';
class SttTranscriptTokens extends StatelessWidget {