diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart index 395628bbb..c6e2dac7f 100644 --- a/lib/pangea/choreographer/controllers/choreographer.dart +++ b/lib/pangea/choreographer/controllers/choreographer.dart @@ -457,7 +457,7 @@ class Choreographer { isFetching = false; choreoRecord = ChoreoRecord.newRecord; itController.clear(); - igc.clear(); + igc.dispose(); //@ggurdin - why is this commented out? // errorService.clear(); _resetDebounceTimer(); diff --git a/lib/pangea/choreographer/controllers/igc_controller.dart b/lib/pangea/choreographer/controllers/igc_controller.dart index 6d0e848aa..a539be048 100644 --- a/lib/pangea/choreographer/controllers/igc_controller.dart +++ b/lib/pangea/choreographer/controllers/igc_controller.dart @@ -17,6 +17,12 @@ import '../../models/span_card_model.dart'; import '../../utils/error_handler.dart'; import '../../utils/overlay.dart'; +class _IGCTextDataCacheItem { + Future data; + + _IGCTextDataCacheItem({required this.data}); +} + class IgcController { Choreographer choreographer; IGCTextData? igcTextData; @@ -24,8 +30,20 @@ class IgcController { Completer igcCompleter = Completer(); late SpanDataController spanDataController; + // cache for IGC data and prev message + final Map _igcTextDataCache = {}; + + Timer? _igcCacheClearTimer; + IgcController(this.choreographer) { spanDataController = SpanDataController(choreographer); + _initializeCacheClearing(); + } + + void _initializeCacheClearing() { + const duration = Duration(minutes: 1); + _igcCacheClearTimer = + Timer.periodic(duration, (Timer t) => _igcTextDataCache.clear()); } Future getIGCTextData({ @@ -49,15 +67,37 @@ class IgcController { prevMessages: prevMessages(), ); - final IGCTextData igcTextDataResponse = await IgcRepo.getIGC( + if (_igcCacheClearTimer == null || !_igcCacheClearTimer!.isActive) { + _initializeCacheClearing(); + } + + // Check if cached data exists + if (_igcTextDataCache.containsKey(reqBody.hashCode)) { + igcTextData = await _igcTextDataCache[reqBody.hashCode]!.data; + return; + } + + final igcFuture = IgcRepo.getIGC( choreographer.accessToken, igcRequest: reqBody, ); + _igcTextDataCache[reqBody.hashCode] = + _IGCTextDataCacheItem(data: igcFuture); + final IGCTextData igcTextDataResponse = + await _igcTextDataCache[reqBody.hashCode]!.data; // this will happen when the user changes the input while igc is fetching results if (igcTextDataResponse.originalInput != choreographer.currentText) { return; } + // get ignored matches from the original igcTextData + // if the new matches are the same as the original match + // could possibly change the status of the new match + // thing is the same if the text we are trying to change is the smae + // as the new text we are trying to change (suggestion is the same) + + // Check for duplicate or minor text changes that shouldn't trigger suggestions + // checks for duplicate input igcTextData = igcTextDataResponse; @@ -158,7 +198,8 @@ class IgcController { choreographer.l2LangCode, ) ?.transcript - .text; + .text + .trim(); // trim whitespace if (content == null) continue; messages.add( PreviousMessage( @@ -189,7 +230,12 @@ class IgcController { clear() { igcTextData = null; spanDataController.clearCache(); - // Not sure why this is here - // MatrixState.pAnyState.closeOverlay(); + spanDataController.dispose(); + } + + dispose() { + clear(); + _igcTextDataCache.clear(); + _igcCacheClearTimer?.cancel(); } } diff --git a/lib/pangea/repo/igc_repo.dart b/lib/pangea/repo/igc_repo.dart index bb5c27062..af4156582 100644 --- a/lib/pangea/repo/igc_repo.dart +++ b/lib/pangea/repo/igc_repo.dart @@ -121,6 +121,26 @@ class PreviousMessage { ModelKey.prevSender: sender, ModelKey.prevTimestamp: timestamp.toIso8601String(), }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + if (other is! PreviousMessage) return false; + + return content == other.content && + sender == other.sender && + timestamp == other.timestamp; + } + + @override + int get hashCode { + return Object.hash( + content, + sender, + timestamp, + ); + } } class IGCRequestBody { @@ -152,4 +172,29 @@ class IGCRequestBody { ModelKey.prevMessages: jsonEncode(prevMessages.map((x) => x.toJson()).toList()), }; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + if (other is! IGCRequestBody) return false; + + return fullText.trim() == other.fullText.trim() && + fullText == other.fullText && + userL1 == other.userL1 && + userL2 == other.userL2 && + enableIT == other.enableIT && + userId == other.userId; + } + + @override + int get hashCode => Object.hash( + fullText.trim(), + userL1, + userL2, + enableIT, + enableIGC, + userId, + Object.hashAll(prevMessages), + ); }