diff --git a/lib/pangea/choreographer/controllers/igc_controller.dart b/lib/pangea/choreographer/controllers/igc_controller.dart index 646543f22..b0c8dd462 100644 --- a/lib/pangea/choreographer/controllers/igc_controller.dart +++ b/lib/pangea/choreographer/controllers/igc_controller.dart @@ -19,14 +19,37 @@ import '../../repo/tokens_repo.dart'; import '../../utils/error_handler.dart'; import '../../utils/overlay.dart'; +class _SpanDetailsCacheItem { + SpanDetailsRepoReqAndRes data; + + _SpanDetailsCacheItem({required this.data}); +} + class IgcController { Choreographer choreographer; IGCTextData? igcTextData; Object? igcError; Completer igcCompleter = Completer(); + final Map _cache = {}; + Timer? _cacheClearTimer; + + IgcController(this.choreographer) { + _initializeCacheClearing(); + } + + void _initializeCacheClearing() { + const duration = Duration(minutes: 2); + _cacheClearTimer = Timer.periodic(duration, (Timer t) => _clearCache()); + } + + void _clearCache() { + _cache.clear(); + } - IgcController(this.choreographer); + void dispose() { + _cacheClearTimer?.cancel(); + } Future getIGCTextData({required bool tokensOnly}) async { try { @@ -80,6 +103,14 @@ class IgcController { igcTextData = igcTextDataResponse; + // After fetching igc data, pre-call span details for each match optimistically. + // This will make the loading of span details faster for the user + if (igcTextData?.matches.isNotEmpty ?? false) { + for (int i = 0; i < igcTextData!.matches.length; i++) { + getSpanDetails(i); + } + } + debugPrint("igc text ${igcTextData.toString()}"); } catch (err, stack) { debugger(when: kDebugMode); @@ -99,18 +130,38 @@ class IgcController { debugger(when: kDebugMode); return; } - final SpanData span = igcTextData!.matches[matchIndex].match; - final SpanDetailsRepoReqAndRes response = await SpanDataRepo.getSpanDetails( - await choreographer.accessToken, - request: SpanDetailsRepoReqAndRes( - userL1: choreographer.l1LangCode!, - userL2: choreographer.l2LangCode!, - enableIGC: choreographer.igcEnabled, - enableIT: choreographer.itEnabled, - span: span, - ), + /// Retrieves the span data from the `igcTextData` matches at the specified `matchIndex`. + /// Creates a `SpanDetailsRepoReqAndRes` object with the retrieved span data and other parameters. + /// Generates a cache key based on the created `SpanDetailsRepoReqAndRes` object. + final SpanData span = igcTextData!.matches[matchIndex].match; + final req = SpanDetailsRepoReqAndRes( + userL1: choreographer.l1LangCode!, + userL2: choreographer.l2LangCode!, + enableIGC: choreographer.igcEnabled, + enableIT: choreographer.itEnabled, + span: span, ); + final int cacheKey = req.hashCode; + + /// Retrieves the [SpanDetailsRepoReqAndRes] response from the cache if it exists, + /// otherwise makes an API call to get the response and stores it in the cache. + SpanDetailsRepoReqAndRes response; + if (_cache.containsKey(cacheKey)) { + response = _cache[cacheKey]!.data; + } else { + response = await SpanDataRepo.getSpanDetails( + await choreographer.accessToken, + request: SpanDetailsRepoReqAndRes( + userL1: choreographer.l1LangCode!, + userL2: choreographer.l2LangCode!, + enableIGC: choreographer.igcEnabled, + enableIT: choreographer.itEnabled, + span: span, + ), + ); + _cache[cacheKey] = _SpanDetailsCacheItem(data: response); + } try { igcTextData!.matches[matchIndex].match = response.span; diff --git a/lib/pangea/repo/span_data_repo.dart b/lib/pangea/repo/span_data_repo.dart index c6025af6c..e253bb1d0 100644 --- a/lib/pangea/repo/span_data_repo.dart +++ b/lib/pangea/repo/span_data_repo.dart @@ -72,6 +72,24 @@ class SpanDetailsRepoReqAndRes { enableIGC: json['enable_igc'] as bool, span: SpanData.fromJson(json['span']), ); + + /// Overrides the equality operator to compare two [SpanDetailsRepoReqAndRes] objects. + /// Returns true if the objects are identical or have the same property + /// values (based on the results of the toJson function), false otherwise. + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other is! SpanDetailsRepoReqAndRes) return false; + + return toJson().toString() == other.toJson().toString(); + } + + /// Overrides the hashCode getter to generate a hash code for the [SpanDetailsRepoReqAndRes] object. + /// Used as keys in response cache in igc_controller. + @override + int get hashCode { + return toJson().toString().hashCode; + } } final spanDataRepomockSpan = SpanData(