refactor: move caching logic into repos
This commit is contained in:
parent
2637308891
commit
f020e02b20
7 changed files with 342 additions and 264 deletions
|
|
@ -609,7 +609,7 @@ class Choreographer {
|
|||
choreoRecord = null;
|
||||
translatedText = null;
|
||||
itController.clear();
|
||||
igc.dispose();
|
||||
igc.clear();
|
||||
_resetDebounceTimer();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import 'dart:developer';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:sentry_flutter/sentry_flutter.dart';
|
||||
import 'package:async/async.dart';
|
||||
import 'package:matrix/matrix.dart' hide Result;
|
||||
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/controllers/error_service.dart';
|
||||
|
|
@ -16,170 +16,96 @@ import 'package:fluffychat/pangea/choreographer/repo/igc_repo.dart';
|
|||
import 'package:fluffychat/pangea/choreographer/repo/igc_request_model.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/igc/span_card.dart';
|
||||
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import '../../common/utils/error_handler.dart';
|
||||
import '../../common/utils/overlay.dart';
|
||||
|
||||
class _IGCTextDataCacheItem {
|
||||
Future<IGCTextData> data;
|
||||
|
||||
_IGCTextDataCacheItem({required this.data});
|
||||
}
|
||||
|
||||
class _IgnoredMatchCacheItem {
|
||||
PangeaMatch match;
|
||||
|
||||
String get spanText => match.match.fullText.substring(
|
||||
match.match.offset,
|
||||
match.match.offset + match.match.length,
|
||||
);
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other is _IgnoredMatchCacheItem && other.spanText == spanText;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => spanText.hashCode;
|
||||
|
||||
_IgnoredMatchCacheItem({required this.match});
|
||||
}
|
||||
|
||||
class IgcController {
|
||||
Choreographer choreographer;
|
||||
IGCTextData? igcTextData;
|
||||
late SpanDataController spanDataController;
|
||||
|
||||
// cache for IGC data and prev message
|
||||
final Map<int, _IGCTextDataCacheItem> _igcTextDataCache = {};
|
||||
|
||||
final Map<int, _IgnoredMatchCacheItem> _ignoredMatchCache = {};
|
||||
|
||||
Timer? _cacheClearTimer;
|
||||
|
||||
IgcController(this.choreographer) {
|
||||
spanDataController = SpanDataController(choreographer);
|
||||
_initializeCacheClearing();
|
||||
}
|
||||
|
||||
void _initializeCacheClearing() {
|
||||
const duration = Duration(minutes: 2);
|
||||
_cacheClearTimer = Timer.periodic(duration, (Timer t) {
|
||||
_igcTextDataCache.clear();
|
||||
_ignoredMatchCache.clear();
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> getIGCTextData() async {
|
||||
try {
|
||||
if (choreographer.currentText.isEmpty) return clear();
|
||||
debugPrint('getIGCTextData called with ${choreographer.currentText}');
|
||||
if (choreographer.currentText.isEmpty) return clear();
|
||||
debugPrint('getIGCTextData called with ${choreographer.currentText}');
|
||||
|
||||
final IGCRequestModel reqBody = IGCRequestModel(
|
||||
fullText: choreographer.currentText,
|
||||
userId: choreographer.pangeaController.userController.userId!,
|
||||
userL1: choreographer.l1LangCode!,
|
||||
userL2: choreographer.l2LangCode!,
|
||||
enableIGC: choreographer.igcEnabled &&
|
||||
choreographer.choreoMode != ChoreoMode.it,
|
||||
enableIT: choreographer.itEnabled &&
|
||||
choreographer.choreoMode != ChoreoMode.it,
|
||||
prevMessages: _prevMessages(),
|
||||
);
|
||||
final IGCRequestModel reqBody = IGCRequestModel(
|
||||
fullText: choreographer.currentText,
|
||||
userId: choreographer.pangeaController.userController.userId!,
|
||||
userL1: choreographer.l1LangCode!,
|
||||
userL2: choreographer.l2LangCode!,
|
||||
enableIGC:
|
||||
choreographer.igcEnabled && choreographer.choreoMode != ChoreoMode.it,
|
||||
enableIT:
|
||||
choreographer.itEnabled && choreographer.choreoMode != ChoreoMode.it,
|
||||
prevMessages: _prevMessages(),
|
||||
);
|
||||
|
||||
if (_cacheClearTimer == null || !_cacheClearTimer!.isActive) {
|
||||
_initializeCacheClearing();
|
||||
}
|
||||
|
||||
// if the request is not in the cache, add it
|
||||
if (!_igcTextDataCache.containsKey(reqBody.hashCode)) {
|
||||
_igcTextDataCache[reqBody.hashCode] = _IGCTextDataCacheItem(
|
||||
data: IgcRepo.getIGC(
|
||||
choreographer.accessToken,
|
||||
igcRequest: reqBody,
|
||||
),
|
||||
final res = await IgcRepo.get(
|
||||
choreographer.accessToken,
|
||||
reqBody,
|
||||
).timeout(
|
||||
(const Duration(seconds: 10)),
|
||||
onTimeout: () {
|
||||
return Result.error(
|
||||
TimeoutException('IGC request timed out'),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
final IGCTextData igcTextDataResponse =
|
||||
await _igcTextDataCache[reqBody.hashCode]!
|
||||
.data
|
||||
.timeout((const Duration(seconds: 10)));
|
||||
|
||||
// this will happen when the user changes the input while igc is fetching results
|
||||
if (igcTextDataResponse.originalInput.trim() !=
|
||||
choreographer.currentText.trim()) {
|
||||
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;
|
||||
|
||||
final List<PangeaMatch> filteredMatches = List.from(igcTextData!.matches);
|
||||
for (final PangeaMatch match in igcTextData!.matches) {
|
||||
final _IgnoredMatchCacheItem cacheEntry =
|
||||
_IgnoredMatchCacheItem(match: match);
|
||||
|
||||
if (_ignoredMatchCache.containsKey(cacheEntry.hashCode)) {
|
||||
filteredMatches.remove(match);
|
||||
}
|
||||
}
|
||||
|
||||
igcTextData!.matches = filteredMatches;
|
||||
choreographer.acceptNormalizationMatches();
|
||||
|
||||
// TODO - for each new match,
|
||||
// check if existing igcTextData has one and only one match with the same error text and correction
|
||||
// if so, keep the original match and discard the new one
|
||||
// if not, add the new match to the existing igcTextData
|
||||
|
||||
// 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++) {
|
||||
if (!igcTextData!.matches[i].isITStart) {
|
||||
spanDataController.getSpanDetails(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint("igc text ${igcTextData.toString()}");
|
||||
} catch (err, stack) {
|
||||
debugger(when: kDebugMode);
|
||||
choreographer.errorService.setError(
|
||||
ChoreoError(raw: err),
|
||||
);
|
||||
ErrorHandler.logError(
|
||||
e: err,
|
||||
s: stack,
|
||||
data: {
|
||||
"currentText": choreographer.currentText,
|
||||
"userL1": choreographer.l1LangCode,
|
||||
"userL2": choreographer.l2LangCode,
|
||||
"igcEnabled": choreographer.igcEnabled,
|
||||
"itEnabled": choreographer.itEnabled,
|
||||
"matches": igcTextData?.matches.map((e) => e.toJson()),
|
||||
},
|
||||
level:
|
||||
err is TimeoutException ? SentryLevel.warning : SentryLevel.error,
|
||||
);
|
||||
if (res.isError) {
|
||||
choreographer.errorService.setError(ChoreoError(raw: res.error));
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// this will happen when the user changes the input while igc is fetching results
|
||||
if (res.result!.originalInput.trim() != choreographer.currentText.trim()) {
|
||||
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 = res.result!;
|
||||
final List<PangeaMatch> filteredMatches = List.from(igcTextData!.matches);
|
||||
for (final PangeaMatch match in igcTextData!.matches) {
|
||||
if (IgcRepo.isIgnored(match)) {
|
||||
filteredMatches.remove(match);
|
||||
}
|
||||
}
|
||||
|
||||
igcTextData!.matches = filteredMatches;
|
||||
choreographer.acceptNormalizationMatches();
|
||||
|
||||
// TODO - for each new match,
|
||||
// check if existing igcTextData has one and only one match with the same error text and correction
|
||||
// if so, keep the original match and discard the new one
|
||||
// if not, add the new match to the existing igcTextData
|
||||
|
||||
// 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++) {
|
||||
if (!igcTextData!.matches[i].isITStart) {
|
||||
spanDataController.getSpanDetails(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onIgnoreMatch(PangeaMatch match) {
|
||||
final cacheEntry = _IgnoredMatchCacheItem(match: match);
|
||||
if (!_ignoredMatchCache.containsKey(cacheEntry.hashCode)) {
|
||||
_ignoredMatchCache[cacheEntry.hashCode] = cacheEntry;
|
||||
}
|
||||
IgcRepo.ignore(match);
|
||||
}
|
||||
|
||||
void showFirstMatch(BuildContext context) {
|
||||
|
|
@ -282,11 +208,4 @@ class IgcController {
|
|||
filter: RegExp(r'span_card_overlay_\d+'),
|
||||
);
|
||||
}
|
||||
|
||||
dispose() {
|
||||
clear();
|
||||
_igcTextDataCache.clear();
|
||||
_ignoredMatchCache.clear();
|
||||
_cacheClearTimer?.cancel();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import 'package:fluffychat/pangea/choreographer/repo/it_response_model.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/models/it_step.dart';
|
||||
import 'package:fluffychat/pangea/common/constants/model_keys.dart';
|
||||
|
||||
class CustomInputRequestModel {
|
||||
|
|
|
|||
|
|
@ -3,71 +3,48 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:async/async.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_request_model.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_response_model.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import '../../common/config/environment.dart';
|
||||
import '../../common/network/requests.dart';
|
||||
import '../../common/network/urls.dart';
|
||||
|
||||
class _TranslateCacheItem {
|
||||
final Future<FullTextTranslationResponseModel> response;
|
||||
final DateTime timestamp;
|
||||
|
||||
_TranslateCacheItem({
|
||||
required this.response,
|
||||
required this.timestamp,
|
||||
});
|
||||
}
|
||||
|
||||
class FullTextTranslationRepo {
|
||||
static final Map<String, FullTextTranslationResponseModel> _cache = {};
|
||||
static Timer? _cacheTimer;
|
||||
static final Map<String, _TranslateCacheItem> _cache = {};
|
||||
static const Duration _cacheDuration = Duration(minutes: 10);
|
||||
|
||||
// start a timer to clear the cache
|
||||
static void startCacheTimer() {
|
||||
_cacheTimer = Timer.periodic(const Duration(minutes: 10), (timer) {
|
||||
clearCache();
|
||||
});
|
||||
}
|
||||
|
||||
// stop the cache time (optional)
|
||||
static void stopCacheTimer() {
|
||||
_cacheTimer?.cancel();
|
||||
}
|
||||
|
||||
// method to clear the cache
|
||||
static void clearCache() {
|
||||
_cache.clear();
|
||||
}
|
||||
|
||||
static String _generateCacheKey({
|
||||
required String text,
|
||||
required String srcLang,
|
||||
required String tgtLang,
|
||||
required int offset,
|
||||
required int length,
|
||||
bool? deepL,
|
||||
}) {
|
||||
return '${text.hashCode}-$srcLang-$tgtLang-$deepL-$offset-$length';
|
||||
}
|
||||
|
||||
static Future<FullTextTranslationResponseModel> translate({
|
||||
required String accessToken,
|
||||
required FullTextTranslationRequestModel request,
|
||||
}) async {
|
||||
// start cache timer when the first API call is made
|
||||
startCacheTimer();
|
||||
|
||||
final cacheKey = _generateCacheKey(
|
||||
text: request.text,
|
||||
srcLang: request.srcLang ?? '',
|
||||
tgtLang: request.tgtLang,
|
||||
offset: request.offset ?? 0,
|
||||
length: request.length ?? 0,
|
||||
deepL: request.deepL,
|
||||
);
|
||||
|
||||
// check cache first
|
||||
if (_cache.containsKey(cacheKey)) {
|
||||
if (_cache[cacheKey] == null) {
|
||||
_cache.remove(cacheKey);
|
||||
} else {
|
||||
return _cache[cacheKey]!;
|
||||
}
|
||||
static Future<Result<FullTextTranslationResponseModel>> get(
|
||||
String accessToken,
|
||||
FullTextTranslationRequestModel request,
|
||||
) {
|
||||
final cached = _getCached(request);
|
||||
if (cached != null) {
|
||||
return _getResult(request, cached);
|
||||
}
|
||||
|
||||
final future = _fetch(accessToken, request);
|
||||
_setCached(request, future);
|
||||
return _getResult(request, future);
|
||||
}
|
||||
|
||||
static Future<FullTextTranslationResponseModel> _fetch(
|
||||
String accessToken,
|
||||
FullTextTranslationRequestModel request,
|
||||
) async {
|
||||
final Requests req = Requests(
|
||||
choreoApiKey: Environment.choreoApiKey,
|
||||
accessToken: accessToken,
|
||||
|
|
@ -78,13 +55,57 @@ class FullTextTranslationRepo {
|
|||
body: request.toJson(),
|
||||
);
|
||||
|
||||
final responseModel = FullTextTranslationResponseModel.fromJson(
|
||||
if (res.statusCode != 200) {
|
||||
throw Exception(
|
||||
'Failed to translate text: ${res.statusCode} ${res.reasonPhrase}',
|
||||
);
|
||||
}
|
||||
|
||||
return FullTextTranslationResponseModel.fromJson(
|
||||
jsonDecode(utf8.decode(res.bodyBytes)),
|
||||
);
|
||||
|
||||
// store response in cache
|
||||
_cache[cacheKey] = responseModel;
|
||||
|
||||
return responseModel;
|
||||
}
|
||||
|
||||
static Future<Result<FullTextTranslationResponseModel>> _getResult(
|
||||
FullTextTranslationRequestModel request,
|
||||
Future<FullTextTranslationResponseModel> future,
|
||||
) async {
|
||||
try {
|
||||
final res = await future;
|
||||
return Result.value(res);
|
||||
} catch (e, s) {
|
||||
_cache.remove(request.hashCode.toString());
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: request.toJson(),
|
||||
);
|
||||
return Result.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<FullTextTranslationResponseModel>? _getCached(
|
||||
FullTextTranslationRequestModel request,
|
||||
) {
|
||||
final cached = _cache[request.hashCode.toString()];
|
||||
if (cached == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (DateTime.now().difference(cached.timestamp) < _cacheDuration) {
|
||||
return cached.response;
|
||||
}
|
||||
|
||||
_cache.remove(request.hashCode.toString());
|
||||
return null;
|
||||
}
|
||||
|
||||
static void _setCached(
|
||||
FullTextTranslationRequestModel request,
|
||||
Future<FullTextTranslationResponseModel> response,
|
||||
) =>
|
||||
_cache[request.hashCode.toString()] = _TranslateCacheItem(
|
||||
response: response,
|
||||
timestamp: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,75 @@
|
|||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:async/async.dart';
|
||||
import 'package:http/http.dart';
|
||||
|
||||
import 'package:fluffychat/pangea/choreographer/models/pangea_match_model.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/repo/igc_request_model.dart';
|
||||
import 'package:fluffychat/pangea/common/config/environment.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import '../../common/network/requests.dart';
|
||||
import '../../common/network/urls.dart';
|
||||
import '../models/igc_text_data_model.dart';
|
||||
|
||||
class _IgcCacheItem {
|
||||
final Future<IGCTextData> data;
|
||||
final DateTime timestamp;
|
||||
|
||||
_IgcCacheItem({
|
||||
required this.data,
|
||||
required this.timestamp,
|
||||
});
|
||||
}
|
||||
|
||||
class _IgnoredMatchCacheItem {
|
||||
final PangeaMatch match;
|
||||
final DateTime timestamp;
|
||||
|
||||
String get spanText => match.match.fullText.characters
|
||||
.skip(match.match.offset)
|
||||
.take(match.match.length)
|
||||
.toString();
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
return other is _IgnoredMatchCacheItem && other.spanText == spanText;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => spanText.hashCode;
|
||||
|
||||
_IgnoredMatchCacheItem({
|
||||
required this.match,
|
||||
required this.timestamp,
|
||||
});
|
||||
}
|
||||
|
||||
class IgcRepo {
|
||||
static Future<IGCTextData> getIGC(
|
||||
static final Map<String, _IgcCacheItem> _igcCache = {};
|
||||
static final Map<String, _IgnoredMatchCacheItem> _ignoredMatchCache = {};
|
||||
static const Duration _cacheDuration = Duration(minutes: 10);
|
||||
|
||||
static Future<Result<IGCTextData>> get(
|
||||
String? accessToken,
|
||||
IGCRequestModel igcRequest,
|
||||
) {
|
||||
final cached = _getCached(igcRequest);
|
||||
if (cached != null) {
|
||||
return _getResult(igcRequest, cached);
|
||||
}
|
||||
|
||||
final future = _fetch(
|
||||
accessToken,
|
||||
igcRequest: igcRequest,
|
||||
);
|
||||
_setCached(igcRequest, future);
|
||||
return _getResult(igcRequest, future);
|
||||
}
|
||||
|
||||
static Future<IGCTextData> _fetch(
|
||||
String? accessToken, {
|
||||
required IGCRequestModel igcRequest,
|
||||
}) async {
|
||||
|
|
@ -22,11 +82,98 @@ class IgcRepo {
|
|||
body: igcRequest.toJson(),
|
||||
);
|
||||
|
||||
if (res.statusCode != 200) {
|
||||
throw Exception(
|
||||
'Failed to fetch IGC data: ${res.statusCode} ${res.reasonPhrase}',
|
||||
);
|
||||
}
|
||||
|
||||
final Map<String, dynamic> json =
|
||||
jsonDecode(utf8.decode(res.bodyBytes).toString());
|
||||
|
||||
final IGCTextData response = IGCTextData.fromJson(json);
|
||||
return IGCTextData.fromJson(json);
|
||||
}
|
||||
|
||||
return response;
|
||||
static Future<Result<IGCTextData>> _getResult(
|
||||
IGCRequestModel request,
|
||||
Future<IGCTextData> future,
|
||||
) async {
|
||||
try {
|
||||
final res = await future;
|
||||
return Result.value(res);
|
||||
} catch (e, s) {
|
||||
_igcCache.remove(request.hashCode.toString());
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: request.toJson(),
|
||||
);
|
||||
return Result.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
static Future<IGCTextData>? _getCached(
|
||||
IGCRequestModel request,
|
||||
) {
|
||||
final cached = _igcCache[request.hashCode.toString()];
|
||||
if (cached == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (DateTime.now().difference(cached.timestamp) < _cacheDuration) {
|
||||
return cached.data;
|
||||
}
|
||||
|
||||
_igcCache.remove(request.hashCode.toString());
|
||||
return null;
|
||||
}
|
||||
|
||||
static void _setCached(
|
||||
IGCRequestModel request,
|
||||
Future<IGCTextData> response,
|
||||
) =>
|
||||
_igcCache[request.hashCode.toString()] = _IgcCacheItem(
|
||||
data: response,
|
||||
timestamp: DateTime.now(),
|
||||
);
|
||||
|
||||
static void ignore(PangeaMatch match) {
|
||||
_setCachedIgnoredSpan(match);
|
||||
}
|
||||
|
||||
static bool isIgnored(PangeaMatch match) {
|
||||
final cached = _getCachedIgnoredSpan(match);
|
||||
return cached != null;
|
||||
}
|
||||
|
||||
static PangeaMatch? _getCachedIgnoredSpan(
|
||||
PangeaMatch match,
|
||||
) {
|
||||
final cacheEntry = _IgnoredMatchCacheItem(
|
||||
match: match,
|
||||
timestamp: DateTime.now(),
|
||||
);
|
||||
|
||||
final cached = _ignoredMatchCache[cacheEntry.hashCode.toString()];
|
||||
if (cached == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (DateTime.now().difference(cached.timestamp) < _cacheDuration) {
|
||||
return cached.match;
|
||||
}
|
||||
|
||||
_ignoredMatchCache.remove(cacheEntry.hashCode.toString());
|
||||
return null;
|
||||
}
|
||||
|
||||
static void _setCachedIgnoredSpan(
|
||||
PangeaMatch match,
|
||||
) {
|
||||
final cacheEntry = _IgnoredMatchCacheItem(
|
||||
match: match,
|
||||
timestamp: DateTime.now(),
|
||||
);
|
||||
_ignoredMatchCache[cacheEntry.hashCode.toString()] = cacheEntry;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart';
|
|||
import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_repo.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_request_model.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_response_model.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
import '../../../widgets/matrix.dart';
|
||||
import '../../bot/utils/bot_style.dart';
|
||||
import '../../common/controllers/pangea_controller.dart';
|
||||
|
|
@ -53,27 +53,17 @@ class ITFeedbackCardController extends State<ITFeedbackCard> {
|
|||
isLoadingFeedback = true;
|
||||
});
|
||||
|
||||
try {
|
||||
res = await FullTextTranslationRepo.translate(
|
||||
accessToken: controller.userController.accessToken,
|
||||
request: widget.req,
|
||||
);
|
||||
} catch (e, s) {
|
||||
error = e;
|
||||
ErrorHandler.logError(
|
||||
e: e,
|
||||
s: s,
|
||||
data: {
|
||||
"req": widget.req.toJson(),
|
||||
"choiceFeedback": widget.choiceFeedback,
|
||||
},
|
||||
);
|
||||
} finally {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
isLoadingFeedback = false;
|
||||
});
|
||||
}
|
||||
final result = await FullTextTranslationRepo.get(
|
||||
controller.userController.accessToken,
|
||||
widget.req,
|
||||
);
|
||||
res = result.result;
|
||||
|
||||
if (result.isError) error = result.error;
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
isLoadingFeedback = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import 'package:matrix/matrix.dart';
|
|||
|
||||
import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_repo.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_request_model.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/repo/full_text_translation_response_model.dart';
|
||||
import 'package:fluffychat/pangea/common/controllers/base_controller.dart';
|
||||
import 'package:fluffychat/pangea/common/controllers/pangea_controller.dart';
|
||||
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
|
||||
|
|
@ -19,6 +18,7 @@ import 'package:fluffychat/pangea/events/models/tokens_event_content_model.dart'
|
|||
import 'package:fluffychat/pangea/events/repo/token_api_models.dart';
|
||||
import 'package:fluffychat/pangea/events/repo/tokens_repo.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
|
||||
// TODO - make this static and take it out of the _pangeaController
|
||||
// will need to pass accessToken to the requests
|
||||
|
|
@ -26,8 +26,6 @@ class MessageDataController extends BaseController {
|
|||
late PangeaController _pangeaController;
|
||||
|
||||
final Map<int, Future<TokensResponseModel>> _tokensCache = {};
|
||||
final Map<int, Future<PangeaRepresentation>> _representationCache = {};
|
||||
final Map<int, Future<SttTranslationModel>> _sttTranslationCache = {};
|
||||
late Timer _cacheTimer;
|
||||
|
||||
MessageDataController(PangeaController pangeaController) {
|
||||
|
|
@ -45,8 +43,6 @@ class MessageDataController extends BaseController {
|
|||
/// Clears the token and representation caches
|
||||
void _clearCache() {
|
||||
_tokensCache.clear();
|
||||
_representationCache.clear();
|
||||
_sttTranslationCache.clear();
|
||||
debugPrint("message data cache cleared.");
|
||||
}
|
||||
|
||||
|
|
@ -116,24 +112,25 @@ class MessageDataController extends BaseController {
|
|||
Future<PangeaRepresentation> getPangeaRepresentation({
|
||||
required FullTextTranslationRequestModel req,
|
||||
required Event messageEvent,
|
||||
}) async {
|
||||
return _representationCache[req.hashCode] ??=
|
||||
_getPangeaRepresentation(req: req, messageEvent: messageEvent);
|
||||
}
|
||||
}) =>
|
||||
_getPangeaRepresentation(req: req, messageEvent: messageEvent);
|
||||
|
||||
Future<PangeaRepresentation> _getPangeaRepresentation({
|
||||
required FullTextTranslationRequestModel req,
|
||||
required Event messageEvent,
|
||||
}) async {
|
||||
final FullTextTranslationResponseModel res =
|
||||
await FullTextTranslationRepo.translate(
|
||||
accessToken: _pangeaController.userController.accessToken,
|
||||
request: req,
|
||||
final res = await FullTextTranslationRepo.get(
|
||||
_pangeaController.userController.accessToken,
|
||||
req,
|
||||
);
|
||||
|
||||
if (res.isError) {
|
||||
throw res.error!;
|
||||
}
|
||||
|
||||
final rep = PangeaRepresentation(
|
||||
langCode: req.tgtLang,
|
||||
text: res.bestTranslation,
|
||||
text: res.result!.bestTranslation,
|
||||
originalSent: false,
|
||||
originalWritten: false,
|
||||
);
|
||||
|
|
@ -161,19 +158,22 @@ class MessageDataController extends BaseController {
|
|||
required PangeaMessageEvent messageEvent,
|
||||
bool originalSent = false,
|
||||
}) async {
|
||||
final FullTextTranslationResponseModel res =
|
||||
await FullTextTranslationRepo.translate(
|
||||
accessToken: _pangeaController.userController.accessToken,
|
||||
request: req,
|
||||
final res = await FullTextTranslationRepo.get(
|
||||
_pangeaController.userController.accessToken,
|
||||
req,
|
||||
);
|
||||
|
||||
if (res.isError) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (originalSent && messageEvent.originalSent != null) {
|
||||
originalSent = false;
|
||||
}
|
||||
|
||||
final rep = PangeaRepresentation(
|
||||
langCode: req.tgtLang,
|
||||
text: res.bestTranslation,
|
||||
text: res.result!.bestTranslation,
|
||||
originalSent: originalSent,
|
||||
originalWritten: false,
|
||||
);
|
||||
|
|
@ -230,27 +230,28 @@ class MessageDataController extends BaseController {
|
|||
required FullTextTranslationRequestModel req,
|
||||
required Room? room,
|
||||
}) =>
|
||||
_sttTranslationCache[req.hashCode] ??= _getSttTranslation(
|
||||
_getSttTranslation(
|
||||
repEventId: repEventId,
|
||||
req: req,
|
||||
room: room,
|
||||
).catchError((e, s) {
|
||||
_sttTranslationCache.remove(req.hashCode);
|
||||
return Future<SttTranslationModel>.error(e, s);
|
||||
});
|
||||
);
|
||||
|
||||
Future<SttTranslationModel> _getSttTranslation({
|
||||
required String? repEventId,
|
||||
required FullTextTranslationRequestModel req,
|
||||
required Room? room,
|
||||
}) async {
|
||||
final res = await FullTextTranslationRepo.translate(
|
||||
accessToken: _pangeaController.userController.accessToken,
|
||||
request: req,
|
||||
final res = await FullTextTranslationRepo.get(
|
||||
_pangeaController.userController.accessToken,
|
||||
req,
|
||||
);
|
||||
|
||||
if (res.isError) {
|
||||
throw res.error!;
|
||||
}
|
||||
|
||||
final translation = SttTranslationModel(
|
||||
translation: res.bestTranslation,
|
||||
translation: res.result!.bestTranslation,
|
||||
langCode: req.tgtLang,
|
||||
);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue