You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
253 lines
8.1 KiB
Dart
253 lines
8.1 KiB
Dart
import 'dart:async';
|
|
import 'dart:developer';
|
|
|
|
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
|
|
import 'package:fluffychat/pangea/choreographer/controllers/error_service.dart';
|
|
import 'package:fluffychat/pangea/models/igc_text_data_model.dart';
|
|
import 'package:fluffychat/pangea/models/pangea_match_model.dart';
|
|
import 'package:fluffychat/pangea/models/span_data.dart';
|
|
import 'package:fluffychat/pangea/repo/igc_repo.dart';
|
|
import 'package:fluffychat/pangea/widgets/igc/span_card.dart';
|
|
import 'package:flutter/foundation.dart';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:sentry_flutter/sentry_flutter.dart';
|
|
|
|
import '../../../widgets/matrix.dart';
|
|
import '../../models/language_detection_model.dart';
|
|
import '../../models/span_card_model.dart';
|
|
import '../../repo/span_data_repo.dart';
|
|
import '../../repo/tokens_repo.dart';
|
|
import '../../utils/error_handler.dart';
|
|
import '../../utils/overlay.dart';
|
|
|
|
class IgcController {
|
|
Choreographer choreographer;
|
|
IGCTextData? igcTextData;
|
|
Object? igcError;
|
|
|
|
Completer<IGCTextData> igcCompleter = Completer();
|
|
|
|
IgcController(this.choreographer);
|
|
|
|
Future<void> getIGCTextData({required bool tokensOnly}) async {
|
|
try {
|
|
if (choreographer.currentText.isEmpty) return clear();
|
|
|
|
debugPrint('getIGCTextData called with ${choreographer.currentText}');
|
|
|
|
debugPrint('getIGCTextData called with tokensOnly = $tokensOnly');
|
|
|
|
final IGCRequestBody reqBody = IGCRequestBody(
|
|
fullText: choreographer.currentText,
|
|
userL1: choreographer.l1LangCode!,
|
|
userL2: choreographer.l2LangCode!,
|
|
enableIGC: choreographer.igcEnabled && !tokensOnly,
|
|
enableIT: choreographer.itEnabled && !tokensOnly,
|
|
tokensOnly: tokensOnly,
|
|
);
|
|
|
|
final IGCTextData igcTextDataResponse = await IgcRepo.getIGC(
|
|
await choreographer.accessToken,
|
|
igcRequest: reqBody,
|
|
);
|
|
// temp fix
|
|
igcTextDataResponse.originalInput = reqBody.fullText;
|
|
|
|
//this will happen when the user changes the input while igc is fetching results
|
|
if (igcTextDataResponse.originalInput != choreographer.currentText) {
|
|
// final current = choreographer.currentText;
|
|
// final igctext = igcTextDataResponse.originalInput;
|
|
// Sentry.addBreadcrumb(
|
|
// Breadcrumb(message: "igc return input does not match current text"),
|
|
// );
|
|
// debugger(when: kDebugMode);
|
|
return;
|
|
}
|
|
|
|
//TO-DO: in api call, specify turning off IT and/or grammar checking
|
|
if (!choreographer.igcEnabled) {
|
|
igcTextDataResponse.matches = igcTextDataResponse.matches
|
|
.where((match) => !match.isGrammarMatch)
|
|
.toList();
|
|
}
|
|
if (!choreographer.itEnabled) {
|
|
igcTextDataResponse.matches = igcTextDataResponse.matches
|
|
.where((match) => !match.isOutOfTargetMatch)
|
|
.toList();
|
|
}
|
|
if (!choreographer.itEnabled && !choreographer.igcEnabled) {
|
|
igcTextDataResponse.matches = [];
|
|
}
|
|
|
|
igcTextData = igcTextDataResponse;
|
|
|
|
debugPrint("igc text ${igcTextData.toString()}");
|
|
} catch (err, stack) {
|
|
debugger(when: kDebugMode);
|
|
choreographer.errorService.setError(
|
|
ChoreoError(type: ChoreoErrorType.unknown, raw: err),
|
|
);
|
|
ErrorHandler.logError(e: err, s: stack);
|
|
clear();
|
|
}
|
|
}
|
|
|
|
Future<void> getSpanDetails(int matchIndex) async {
|
|
if (igcTextData == null ||
|
|
igcTextData!.matches.isEmpty ||
|
|
matchIndex < 0 ||
|
|
matchIndex >= igcTextData!.matches.length) {
|
|
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,
|
|
),
|
|
);
|
|
|
|
igcTextData!.matches[matchIndex].match = response.span;
|
|
|
|
choreographer.setState();
|
|
}
|
|
|
|
Future<void> justGetTokensAndAddThemToIGCTextData() async {
|
|
try {
|
|
if (igcTextData == null) {
|
|
debugger(when: kDebugMode);
|
|
choreographer.getLanguageHelp();
|
|
return;
|
|
}
|
|
igcTextData!.loading = true;
|
|
choreographer.startLoading();
|
|
if (igcTextData!.originalInput != choreographer.textController.text) {
|
|
debugger(when: kDebugMode);
|
|
ErrorHandler.logError(
|
|
m: "igcTextData fullText does not match current text",
|
|
s: StackTrace.current,
|
|
data: igcTextData!.toJson(),
|
|
);
|
|
}
|
|
|
|
if (choreographer.l1LangCode == null ||
|
|
choreographer.l2LangCode == null) {
|
|
debugger(when: kDebugMode);
|
|
ErrorHandler.logError(
|
|
m: "l1LangCode and/or l2LangCode is null",
|
|
s: StackTrace.current,
|
|
data: {
|
|
"l1LangCode": choreographer.l1LangCode,
|
|
"l2LangCode": choreographer.l2LangCode,
|
|
},
|
|
);
|
|
return;
|
|
}
|
|
|
|
final TokensResponseModel res = await TokensRepo.tokenize(
|
|
await choreographer.pangeaController.userController.accessToken,
|
|
TokensRequestModel(
|
|
fullText: igcTextData!.originalInput,
|
|
userL1: choreographer.l1LangCode!,
|
|
userL2: choreographer.l2LangCode!,
|
|
),
|
|
);
|
|
igcTextData?.tokens = res.tokens;
|
|
} catch (err, stack) {
|
|
debugger(when: kDebugMode);
|
|
choreographer.errorService.setError(
|
|
ChoreoError(type: ChoreoErrorType.unknown, raw: err),
|
|
);
|
|
Sentry.addBreadcrumb(
|
|
Breadcrumb.fromJson({"igctextDdata": igcTextData?.toJson()}),
|
|
);
|
|
ErrorHandler.logError(e: err, s: stack);
|
|
} finally {
|
|
igcTextData?.loading = false;
|
|
choreographer.stopLoading();
|
|
}
|
|
}
|
|
|
|
void showFirstMatch(BuildContext context) {
|
|
if (igcTextData == null || igcTextData!.matches.isEmpty) {
|
|
debugger(when: kDebugMode);
|
|
ErrorHandler.logError(
|
|
m: "should not be calling showFirstMatch with this igcTextData ${igcTextData?.toJson().toString()}",
|
|
s: StackTrace.current,
|
|
);
|
|
return;
|
|
}
|
|
|
|
const int firstMatchIndex = 0;
|
|
final PangeaMatch match = igcTextData!.matches[firstMatchIndex];
|
|
|
|
OverlayUtil.showPositionedCard(
|
|
context: context,
|
|
cardToShow: SpanCard(
|
|
scm: SpanCardModel(
|
|
matchIndex: firstMatchIndex,
|
|
onReplacementSelect: choreographer.onReplacementSelect,
|
|
onSentenceRewrite: (value) async {},
|
|
onIgnore: () => choreographer.onIgnoreMatch(
|
|
cursorOffset: match.match.offset,
|
|
),
|
|
onITStart: () {
|
|
if (choreographer.itEnabled && igcTextData != null) {
|
|
choreographer.onITStart(igcTextData!.matches[firstMatchIndex]);
|
|
}
|
|
},
|
|
choreographer: choreographer,
|
|
),
|
|
roomId: choreographer.roomId,
|
|
),
|
|
cardSize: match.isITStart ? const Size(350, 220) : const Size(350, 400),
|
|
transformTargetId: choreographer.inputTransformTargetKey,
|
|
);
|
|
}
|
|
|
|
bool get hasRelevantIGCTextData {
|
|
if (igcTextData == null) return false;
|
|
|
|
if (igcTextData!.originalInput != choreographer.currentText) {
|
|
debugPrint(
|
|
"returning isIGCTextDataRelevant false because text has changed",
|
|
);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
String? get detectedLangCode {
|
|
if (!hasRelevantIGCTextData) return null;
|
|
|
|
final LanguageDetection first = igcTextData!.detections.first;
|
|
|
|
return first.langCode;
|
|
}
|
|
|
|
clear() {
|
|
igcTextData = null;
|
|
MatrixState.pAnyState.closeOverlay();
|
|
}
|
|
|
|
bool get canSendMessage {
|
|
if (choreographer.isFetching) return false;
|
|
if (igcTextData == null ||
|
|
choreographer.errorService.isError ||
|
|
igcTextData!.matches.isEmpty) {
|
|
return true;
|
|
}
|
|
|
|
return !((choreographer.itEnabled &&
|
|
igcTextData!.matches.any((match) => match.isOutOfTargetMatch)) ||
|
|
(choreographer.igcEnabled &&
|
|
igcTextData!.matches.any((match) => !match.isOutOfTargetMatch)));
|
|
}
|
|
}
|