return domain-specific models from choreo-related repos, use future builders on async data when possbile

This commit is contained in:
ggurdin 2025-11-12 10:18:58 -05:00
parent 4b9fd02baf
commit 4eebc4b820
No known key found for this signature in database
GPG key ID: A01CB41737CBB478
12 changed files with 185 additions and 247 deletions

View file

@ -108,11 +108,7 @@ class IgcController {
}
if (!_isFetching) return;
final response = res.result!;
_igcTextData = IGCTextData(
originalInput: response.originalInput,
matches: response.matches,
);
_igcTextData = res.result!;
_isFetching = false;
}
@ -147,7 +143,7 @@ class IgcController {
throw response.error!;
}
_igcTextData?.setSpanData(match, response.result!.span);
_igcTextData?.setSpanData(match, response.result!);
}
Future<void> fetchAllSpanDetails() async {

View file

@ -7,6 +7,7 @@ import 'package:http/http.dart';
import 'package:fluffychat/pangea/choreographer/igc/igc_request_model.dart';
import 'package:fluffychat/pangea/choreographer/igc/igc_response_model.dart';
import 'package:fluffychat/pangea/choreographer/igc/igc_text_data_model.dart';
import 'package:fluffychat/pangea/choreographer/igc/pangea_match_model.dart';
import 'package:fluffychat/pangea/common/config/environment.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
@ -14,7 +15,7 @@ import '../../common/network/requests.dart';
import '../../common/network/urls.dart';
class _IgcCacheItem {
final Future<IGCResponseModel> data;
final Future<IGCTextData> data;
final DateTime timestamp;
const _IgcCacheItem({
@ -52,7 +53,7 @@ class IgcRepo {
static final Map<String, _IgnoredMatchCacheItem> _ignoredMatchCache = {};
static const Duration _cacheDuration = Duration(minutes: 10);
static Future<Result<IGCResponseModel>> get(
static Future<Result<IGCTextData>> get(
String? accessToken,
IGCRequestModel igcRequest,
) {
@ -69,7 +70,7 @@ class IgcRepo {
return _getResult(igcRequest, future);
}
static Future<IGCResponseModel> _fetch(
static Future<IGCTextData> _fetch(
String? accessToken, {
required IGCRequestModel igcRequest,
}) async {
@ -91,12 +92,16 @@ class IgcRepo {
final Map<String, dynamic> json =
jsonDecode(utf8.decode(res.bodyBytes).toString());
return IGCResponseModel.fromJson(json);
final respModel = IGCResponseModel.fromJson(json);
return IGCTextData(
originalInput: respModel.originalInput,
matches: respModel.matches,
);
}
static Future<Result<IGCResponseModel>> _getResult(
static Future<Result<IGCTextData>> _getResult(
IGCRequestModel request,
Future<IGCResponseModel> future,
Future<IGCTextData> future,
) async {
try {
final res = await future;
@ -112,7 +117,7 @@ class IgcRepo {
}
}
static Future<IGCResponseModel>? _getCached(
static Future<IGCTextData>? _getCached(
IGCRequestModel request,
) {
final cacheKeys = [..._igcCache.keys];
@ -129,7 +134,7 @@ class IgcRepo {
static void _setCached(
IGCRequestModel request,
Future<IGCResponseModel> response,
Future<IGCTextData> response,
) =>
_igcCache[request.hashCode.toString()] = _IgcCacheItem(
data: response,

View file

@ -8,8 +8,8 @@ import 'package:fluffychat/pangea/choreographer/choreographer.dart';
import 'package:fluffychat/pangea/choreographer/igc/pangea_match_state_model.dart';
import 'package:fluffychat/pangea/choreographer/igc/span_choice_type_enum.dart';
import 'package:fluffychat/pangea/choreographer/igc/span_data_model.dart';
import 'package:fluffychat/pangea/common/utils/async_state.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/common/utils/feedback_model.dart';
import 'package:fluffychat/pangea/common/widgets/error_indicator.dart';
import '../../../widgets/matrix.dart';
import '../../common/widgets/choice_array.dart';
@ -32,7 +32,8 @@ class SpanCard extends StatefulWidget {
class SpanCardState extends State<SpanCard> {
bool _loadingChoices = true;
final _feedbackModel = FeedbackModel<String>();
final ValueNotifier<AsyncState<String>> _feedbackState =
ValueNotifier<AsyncState<String>>(const AsyncIdle<String>());
final ScrollController scrollController = ScrollController();
@ -44,7 +45,7 @@ class SpanCardState extends State<SpanCard> {
@override
void dispose() {
_feedbackModel.dispose();
_feedbackState.dispose();
scrollController.dispose();
super.dispose();
}
@ -62,19 +63,21 @@ class SpanCardState extends State<SpanCard> {
return;
}
setState(() => _loadingChoices = true);
try {
setState(() => _loadingChoices = true);
await widget.choreographer.igcController.fetchSpanDetails(
match: widget.match,
);
} catch (e) {
widget.choreographer.clearMatches(e);
} finally {
if (_choices == null || _choices!.isEmpty) {
widget.choreographer.clearMatches(
'No choices available for span ${widget.match.updatedMatch.match.message}',
);
}
} catch (e) {
widget.choreographer.clearMatches(e);
} finally {
if (mounted) {
setState(() => _loadingChoices = false);
}
@ -83,29 +86,28 @@ class SpanCardState extends State<SpanCard> {
Future<void> _fetchFeedback() async {
if (_selectedFeedback != null) {
_feedbackModel.setState(FeedbackLoaded<String>(_selectedFeedback!));
_feedbackState.value = AsyncLoaded<String>(_selectedFeedback!);
return;
}
try {
_feedbackModel.setState(FeedbackLoading<String>());
_feedbackState.value = const AsyncLoading<String>();
await widget.choreographer.igcController.fetchSpanDetails(
match: widget.match,
force: true,
);
} finally {
if (!mounted) return;
if (_selectedFeedback != null) {
_feedbackState.value = AsyncLoaded<String>(_selectedFeedback!);
} else {
_feedbackState.value = AsyncError<String>(
L10n.of(context).failedToLoadFeedback,
);
}
} catch (e) {
if (mounted) {
if (_selectedFeedback == null) {
_feedbackModel.setState(
FeedbackError<String>(
L10n.of(context).failedToLoadFeedback,
),
);
} else {
_feedbackModel.setState(
FeedbackLoaded<String>(_selectedFeedback!),
);
}
_feedbackState.value = AsyncError<String>(e);
}
}
}
@ -113,11 +115,11 @@ class SpanCardState extends State<SpanCard> {
void _onChoiceSelect(int index) {
final selected = _choices![index];
widget.match.selectChoice(index);
_feedbackModel.setState(
selected.feedback != null
? FeedbackLoaded<String>(selected.feedback!)
: FeedbackIdle<String>(),
);
_feedbackState.value = selected.feedback != null
? AsyncLoaded<String>(selected.feedback!)
: const AsyncIdle<String>();
setState(() {});
}
@ -185,7 +187,7 @@ class SpanCardState extends State<SpanCard> {
_SpanCardFeedback(
_selectedChoice != null,
_fetchFeedback,
_feedbackModel,
_feedbackState,
),
],
),
@ -207,12 +209,12 @@ class SpanCardState extends State<SpanCard> {
class _SpanCardFeedback extends StatelessWidget {
final bool hasSelectedChoice;
final VoidCallback fetchFeedback;
final FeedbackModel<String> feedbackModel;
final ValueNotifier<AsyncState<String>> feedbackState;
const _SpanCardFeedback(
this.hasSelectedChoice,
this.fetchFeedback,
this.feedbackModel,
this.feedbackState,
);
@override
@ -224,12 +226,11 @@ class _SpanCardFeedback extends StatelessWidget {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ListenableBuilder(
listenable: feedbackModel,
builder: (context, _) {
final state = feedbackModel.state;
ValueListenableBuilder(
valueListenable: feedbackState,
builder: (context, state, __) {
return switch (state) {
FeedbackIdle<String>() => hasSelectedChoice
AsyncIdle<String>() => hasSelectedChoice
? IconButton(
onPressed: fetchFeedback,
icon: const Icon(Icons.lightbulb_outline, size: 24),
@ -240,14 +241,14 @@ class _SpanCardFeedback extends StatelessWidget {
fontStyle: FontStyle.italic,
),
),
FeedbackLoading<String>() => const SizedBox(
AsyncLoading<String>() => const SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(),
),
FeedbackError<String>(:final error) =>
AsyncError<String>(:final error) =>
ErrorIndicator(message: error.toString()),
FeedbackLoaded<String>(:final value) =>
AsyncLoaded<String>(:final value) =>
Text(value, style: BotStyle.text(context)),
};
},

View file

@ -3,6 +3,7 @@ import 'dart:convert';
import 'package:async/async.dart';
import 'package:http/http.dart';
import 'package:fluffychat/pangea/choreographer/igc/span_data_model.dart';
import 'package:fluffychat/pangea/choreographer/igc/span_data_request.dart';
import 'package:fluffychat/pangea/choreographer/igc/span_data_response.dart';
import 'package:fluffychat/pangea/common/config/environment.dart';
@ -11,7 +12,7 @@ import '../../common/network/requests.dart';
import '../../common/network/urls.dart';
class _SpanDetailsCacheItem {
final Future<SpanDetailsResponse> data;
final Future<SpanData> data;
final DateTime timestamp;
const _SpanDetailsCacheItem({
@ -24,7 +25,7 @@ class SpanDataRepo {
static final Map<String, _SpanDetailsCacheItem> _cache = {};
static const Duration _cacheDuration = Duration(minutes: 10);
static Future<Result<SpanDetailsResponse>> get(
static Future<Result<SpanData>> get(
String? accessToken, {
required SpanDetailsRequest request,
}) async {
@ -41,7 +42,7 @@ class SpanDataRepo {
return _getResult(request, future);
}
static Future<SpanDetailsResponse> _fetch(
static Future<SpanData> _fetch(
String? accessToken, {
required SpanDetailsRequest request,
}) async {
@ -59,14 +60,15 @@ class SpanDataRepo {
throw Exception('Failed to load span details');
}
return SpanDetailsResponse.fromJson(
final respModel = SpanDetailsResponse.fromJson(
jsonDecode(utf8.decode(res.bodyBytes)),
);
return respModel.span;
}
static Future<Result<SpanDetailsResponse>> _getResult(
static Future<Result<SpanData>> _getResult(
SpanDetailsRequest request,
Future<SpanDetailsResponse> future,
Future<SpanData> future,
) async {
try {
final res = await future;
@ -82,7 +84,7 @@ class SpanDataRepo {
}
}
static Future<SpanDetailsResponse>? _getCached(
static Future<SpanData>? _getCached(
SpanDetailsRequest request,
) {
final cacheKeys = [..._cache.keys];
@ -96,7 +98,7 @@ class SpanDataRepo {
static void _setCached(
SpanDetailsRequest request,
Future<SpanDetailsResponse> response,
Future<SpanData> response,
) {
_cache[request.hashCode.toString()] = _SpanDetailsCacheItem(
data: response,

View file

@ -11,10 +11,9 @@ import '../../common/network/requests.dart';
import '../../common/network/urls.dart';
class ContextualDefinitionRepo {
static final Map<String, Future<ContextualDefinitionResponseModel>> _cache =
{};
static final Map<String, Future<String>> _cache = {};
static Future<Result<ContextualDefinitionResponseModel>> get(
static Future<Result<String>> get(
String accessToken,
ContextualDefinitionRequestModel request,
) async {
@ -38,7 +37,7 @@ class ContextualDefinitionRepo {
return _getResult(request, future);
}
static Future<ContextualDefinitionResponseModel> _fetch(
static Future<String> _fetch(
String accessToken,
ContextualDefinitionRequestModel request,
) async {
@ -77,12 +76,12 @@ class ContextualDefinitionRepo {
);
}
return response;
return response.text;
}
static Future<Result<ContextualDefinitionResponseModel>> _getResult(
static Future<Result<String>> _getResult(
ContextualDefinitionRequestModel request,
Future<ContextualDefinitionResponseModel> future,
Future<String> future,
) async {
try {
final res = await future;
@ -98,14 +97,14 @@ class ContextualDefinitionRepo {
}
}
static Future<ContextualDefinitionResponseModel>? _getCached(
static Future<String>? _getCached(
ContextualDefinitionRequestModel request,
) =>
_cache[request.hashCode.toString()];
static void _setCached(
ContextualDefinitionRequestModel request,
Future<ContextualDefinitionResponseModel> response,
Future<String> response,
) =>
_cache[request.hashCode.toString()] = response;
}

View file

@ -113,9 +113,8 @@ class ITBarState extends State<ITBar> with SingleTickerProviderStateMixin {
cardToShow: choiceFeedback == null
? WordDataCard(
word: text,
wordLang: l2Code,
langCode: l2Code,
fullText: _sourceText.value ?? widget.choreographer.currentText,
fullTextLang: l2Code,
)
: ITFeedbackCard(
FullTextTranslationRequestModel(
@ -129,7 +128,7 @@ class ITBarState extends State<ITBar> with SingleTickerProviderStateMixin {
maxWidth: 300,
borderColor: borderColor,
transformTargetId: 'it_bar',
isScrollable: choiceFeedback == null,
isScrollable: false,
overlayKey: "it_feedback_card",
ignorePointer: true,
);

View file

@ -6,7 +6,6 @@ import 'package:async/async.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart';
import 'package:fluffychat/pangea/common/utils/feedback_model.dart';
import 'package:fluffychat/pangea/translation/full_text_translation_repo.dart';
import 'package:fluffychat/pangea/translation/full_text_translation_request_model.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
@ -14,7 +13,7 @@ import '../../../widgets/matrix.dart';
import '../../bot/utils/bot_style.dart';
import '../../common/widgets/card_error_widget.dart';
class ITFeedbackCard extends StatefulWidget {
class ITFeedbackCard extends StatelessWidget {
final FullTextTranslationRequestModel req;
const ITFeedbackCard(
@ -22,56 +21,22 @@ class ITFeedbackCard extends StatefulWidget {
super.key,
});
@override
State<ITFeedbackCard> createState() => ITFeedbackCardController();
}
class ITFeedbackCardController extends State<ITFeedbackCard> {
final FeedbackModel<String> _feedbackModel = FeedbackModel<String>();
@override
void initState() {
super.initState();
_getFeedback();
}
@override
void dispose() {
_feedbackModel.dispose();
super.dispose();
}
Future<void> _getFeedback() async {
_feedbackModel.setState(FeedbackLoading());
final result = await FullTextTranslationRepo.get(
Future<Result<String>> _getFeedback() {
return FullTextTranslationRepo.get(
MatrixState.pangeaController.userController.accessToken,
widget.req,
req,
).timeout(
const Duration(seconds: 10),
onTimeout: () {
return Result.error("Timeout getting translation");
},
onTimeout: () => Result.error("Timeout getting translation"),
);
if (!mounted) return;
if (result.isError) {
_feedbackModel.setState(
FeedbackError<String>(result.error.toString()),
);
} else {
_feedbackModel.setState(
FeedbackLoaded<String>(result.result!.bestTranslation),
);
}
}
@override
Widget build(BuildContext context) {
return ListenableBuilder(
listenable: _feedbackModel,
builder: (context, _) {
final state = _feedbackModel.state;
if (state is FeedbackError) {
return FutureBuilder<Result<String>>(
future: _getFeedback(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return CardErrorWidget(L10n.of(context).errorFetchingDefinition);
}
@ -83,22 +48,22 @@ class ITFeedbackCardController extends State<ITFeedbackCard> {
alignment: WrapAlignment.center,
children: [
Text(
widget.req.text,
req.text,
style: BotStyle.text(context),
),
Text(
"",
style: BotStyle.text(context),
),
_feedbackModel.state is FeedbackLoaded
snapshot.hasData
? Text(
(state as FeedbackLoaded<String>).value,
snapshot.data!.result!,
style: BotStyle.text(context),
)
: TextLoadingShimmer(
width: min(
140,
10.0 * widget.req.text.length,
10.0 * req.text.length,
),
),
],

View file

@ -2,120 +2,65 @@ import 'package:flutter/material.dart';
import 'package:async/async.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/bot/utils/bot_style.dart';
import 'package:fluffychat/pangea/choreographer/it/contextual_definition_repo.dart';
import 'package:fluffychat/pangea/choreographer/it/contextual_definition_request_model.dart';
import 'package:fluffychat/pangea/common/utils/feedback_model.dart';
import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart';
import 'package:fluffychat/pangea/toolbar/widgets/toolbar_content_loading_indicator.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'package:fluffychat/widgets/matrix.dart';
class WordDataCard extends StatefulWidget {
class WordDataCard extends StatelessWidget {
final String word;
final String fullText;
final String wordLang;
final String fullTextLang;
final String langCode;
const WordDataCard({
super.key,
required this.word,
required this.fullText,
required this.wordLang,
required this.fullTextLang,
required this.langCode,
});
@override
State<WordDataCard> createState() => WordDataCardController();
}
class WordDataCardController extends State<WordDataCard> {
final FeedbackModel<String> _feedbackModel = FeedbackModel<String>();
@override
void initState() {
super.initState();
_getContextualDefinition();
}
@override
void didUpdateWidget(covariant WordDataCard oldWidget) {
if (oldWidget.word != widget.word) {
_getContextualDefinition();
}
super.didUpdateWidget(oldWidget);
}
@override
void dispose() {
_feedbackModel.dispose();
super.dispose();
}
ContextualDefinitionRequestModel get _request =>
ContextualDefinitionRequestModel(
fullText: widget.fullText,
word: widget.word,
fullTextLang: widget.fullTextLang,
wordLang: widget.wordLang,
feedbackLang:
MatrixState.pangeaController.languageController.activeL1Code() ??
LanguageKeys.defaultLanguage,
);
Future<void> _getContextualDefinition() async {
_feedbackModel.setState(FeedbackLoading<String>());
final resp = await ContextualDefinitionRepo.get(
Future<Result<String>> _fetchDefinition() {
final request = ContextualDefinitionRequestModel(
fullText: fullText,
word: word,
fullTextLang: langCode,
wordLang: langCode,
feedbackLang:
MatrixState.pangeaController.languageController.activeL1Code() ??
LanguageKeys.defaultLanguage,
);
return ContextualDefinitionRepo.get(
MatrixState.pangeaController.userController.accessToken,
_request,
request,
).timeout(
const Duration(seconds: 10),
onTimeout: () {
return Result.error("Timeout getting definition");
},
onTimeout: () => Result.error("Timeout getting definition"),
);
if (!mounted) return;
if (resp.isError) {
_feedbackModel.setState(
const FeedbackError<String>("Error getting definition"),
);
} else {
_feedbackModel.setState(FeedbackLoaded<String>(resp.result!.text));
}
}
@override
Widget build(BuildContext context) {
return ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: AppConfig.toolbarMinWidth,
maxHeight: AppConfig.toolbarMaxHeight,
),
child: SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.all(20.0),
child: ListenableBuilder(
listenable: _feedbackModel,
builder: (context, _) {
final state = _feedbackModel.state;
return switch (state) {
FeedbackIdle<String>() => const SizedBox.shrink(),
FeedbackLoading<String>() =>
const ToolbarContentLoadingIndicator(),
FeedbackError<String>() => Text(
L10n.of(context).sorryNoResults,
style: BotStyle.text(context),
textAlign: TextAlign.center,
),
FeedbackLoaded<String>(:final value) =>
Text(value, style: BotStyle.text(context)),
};
},
),
),
return Center(
child: FutureBuilder<Result<String>>(
future: _fetchDefinition(),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const ToolbarContentLoadingIndicator();
}
final result = snapshot.data!;
if (result.isError) {
return Text(
L10n.of(context).sorryNoResults,
style: BotStyle.text(context),
textAlign: TextAlign.center,
);
}
return Text(result.result!, style: BotStyle.text(context));
},
),
);
}

View file

@ -0,0 +1,55 @@
/// A generic sealed class that represents the state of an asynchronous operation.
sealed class AsyncState<T> {
/// Base constructor for all asynchronous state variants.
const AsyncState();
/// Represents an idle state before any asynchronous work has begun.
const factory AsyncState.idle() = AsyncIdle<T>;
/// Represents an in-progress loading state.
const factory AsyncState.loading() = AsyncLoading<T>;
/// Represents a completed asynchronous operation with a successful [value].
const factory AsyncState.loaded(T value) = AsyncLoaded<T>;
/// Represents a failed asynchronous operation with an [error].
const factory AsyncState.error(Object error) = AsyncError<T>;
}
/// The idle state of an [AsyncState], indicating no active or completed work.
///
/// Use this as the initial state before triggering an async operation.
class AsyncIdle<T> extends AsyncState<T> {
/// Creates an idle [AsyncState].
const AsyncIdle();
}
/// The loading state of an [AsyncState], indicating that work is in progress.
///
/// This state is typically used to show a loading spinner or progress indicator.
class AsyncLoading<T> extends AsyncState<T> {
/// Creates a loading [AsyncState].
const AsyncLoading();
}
/// The success state of an [AsyncState], containing a completed [value].
///
/// This state indicates that the asynchronous work finished successfully.
class AsyncLoaded<T> extends AsyncState<T> {
/// The result of the successful asynchronous operation.
final T value;
/// Creates a loaded [AsyncState] with a [value].
const AsyncLoaded(this.value);
}
/// The error state of an [AsyncState], containing an [error].
///
/// This state indicates that the asynchronous work failed.
class AsyncError<T> extends AsyncState<T> {
/// The error produced during the asynchronous operation.
final Object error;
/// Creates an error [AsyncState] with an [error].
const AsyncError(this.error);
}

View file

@ -1,29 +0,0 @@
import 'package:flutter/material.dart';
sealed class FeedbackState<T> {
const FeedbackState();
}
class FeedbackIdle<T> extends FeedbackState<T> {}
class FeedbackLoading<T> extends FeedbackState<T> {}
class FeedbackLoaded<T> extends FeedbackState<T> {
final T value;
const FeedbackLoaded(this.value);
}
class FeedbackError<T> extends FeedbackState<T> {
final Object error;
const FeedbackError(this.error);
}
class FeedbackModel<T> extends ChangeNotifier {
FeedbackState<T> _state = FeedbackIdle<T>();
FeedbackState<T> get state => _state;
void setState(FeedbackState<T> newState) {
_state = newState;
notifyListeners();
}
}

View file

@ -88,7 +88,7 @@ class MessageDataController extends BaseController {
final rep = PangeaRepresentation(
langCode: req.tgtLang,
text: res.result!.bestTranslation,
text: res.result!,
originalSent: false,
originalWritten: false,
);
@ -131,7 +131,7 @@ class MessageDataController extends BaseController {
final rep = PangeaRepresentation(
langCode: req.tgtLang,
text: res.result!.bestTranslation,
text: res.result!,
originalSent: originalSent,
originalWritten: false,
);
@ -180,7 +180,7 @@ class MessageDataController extends BaseController {
}
final translation = SttTranslationModel(
translation: res.result!.bestTranslation,
translation: res.result!,
langCode: req.tgtLang,
);

View file

@ -14,7 +14,7 @@ import '../common/network/requests.dart';
import '../common/network/urls.dart';
class _TranslateCacheItem {
final Future<FullTextTranslationResponseModel> response;
final Future<String> response;
final DateTime timestamp;
const _TranslateCacheItem({
@ -27,7 +27,7 @@ class FullTextTranslationRepo {
static final Map<String, _TranslateCacheItem> _cache = {};
static const Duration _cacheDuration = Duration(minutes: 10);
static Future<Result<FullTextTranslationResponseModel>> get(
static Future<Result<String>> get(
String accessToken,
FullTextTranslationRequestModel request,
) {
@ -41,7 +41,7 @@ class FullTextTranslationRepo {
return _getResult(request, future);
}
static Future<FullTextTranslationResponseModel> _fetch(
static Future<String> _fetch(
String accessToken,
FullTextTranslationRequestModel request,
) async {
@ -63,12 +63,12 @@ class FullTextTranslationRepo {
return FullTextTranslationResponseModel.fromJson(
jsonDecode(utf8.decode(res.bodyBytes)),
);
).bestTranslation;
}
static Future<Result<FullTextTranslationResponseModel>> _getResult(
static Future<Result<String>> _getResult(
FullTextTranslationRequestModel request,
Future<FullTextTranslationResponseModel> future,
Future<String> future,
) async {
try {
final res = await future;
@ -84,7 +84,7 @@ class FullTextTranslationRepo {
}
}
static Future<FullTextTranslationResponseModel>? _getCached(
static Future<String>? _getCached(
FullTextTranslationRequestModel request,
) {
final cacheKeys = [..._cache.keys];
@ -99,7 +99,7 @@ class FullTextTranslationRepo {
static void _setCached(
FullTextTranslationRequestModel request,
Future<FullTextTranslationResponseModel> response,
Future<String> response,
) =>
_cache[request.hashCode.toString()] = _TranslateCacheItem(
response: response,