fluffychat/lib/pangea/choreographer/it/it_controller.dart

225 lines
6.3 KiB
Dart

import 'dart:async';
import 'dart:collection';
import 'package:flutter/foundation.dart';
import 'package:async/async.dart';
import 'package:fluffychat/pangea/choreographer/it/gold_route_tracker_model.dart';
import 'package:fluffychat/pangea/choreographer/it/it_repo.dart';
import 'package:fluffychat/pangea/choreographer/it/it_response_model.dart';
import 'package:fluffychat/pangea/choreographer/it/it_step_model.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'package:fluffychat/widgets/matrix.dart';
import 'completed_it_step_model.dart';
import 'it_request_model.dart';
class ITController {
final Function(Object) onError;
final Queue<Completer<ITStepModel>> _queue = Queue();
GoldRouteTrackerModel? _goldRouteTracker;
final ValueNotifier<String?> _sourceText = ValueNotifier(null);
final ValueNotifier<ITStepModel?> _currentITStep = ValueNotifier(null);
final ValueNotifier<bool> _open = ValueNotifier(false);
final ValueNotifier<bool> _editing = ValueNotifier(false);
ITController(this.onError);
ValueNotifier<bool> get open => _open;
ValueNotifier<bool> get editing => _editing;
ValueNotifier<ITStepModel?> get currentITStep => _currentITStep;
ValueNotifier<String?> get sourceText => _sourceText;
StreamController<CompletedITStepModel> acceptedContinuanceStream =
StreamController.broadcast();
bool _continuing = false;
bool dismissed = false;
ITRequestModel _request(String textInput) {
assert(_sourceText.value != null);
return ITRequestModel(
text: _sourceText.value!,
customInput: textInput,
sourceLangCode: MatrixState.pangeaController.userController.userL1Code!,
targetLangCode: MatrixState.pangeaController.userController.userL2Code!,
goldTranslation: _goldRouteTracker?.fullTranslation,
goldContinuances: _goldRouteTracker?.continuances,
);
}
Future<Result<ITResponseModel>> _safeRequest(String text) {
return ITRepo.get(_request(text)).timeout(
const Duration(seconds: 10),
onTimeout: () => Result.error(
TimeoutException("ITRepo.get timed out after 10 seconds"),
),
);
}
void clearSourceText() {
_sourceText.value = null;
}
void clearDissmissed() {
dismissed = false;
}
void dispose() {
acceptedContinuanceStream.close();
_open.dispose();
_editing.dispose();
_currentITStep.dispose();
_sourceText.dispose();
}
void openIT(String text) {
_sourceText.value = text;
_open.value = true;
_continueIT();
}
void closeIT({bool dismiss = false}) {
MatrixState.pAnyState.closeOverlay("it_feedback_card");
setEditingSourceText(false);
if (dismiss) {
dismissed = true;
}
_open.value = false;
_queue.clear();
_currentITStep.value = null;
_goldRouteTracker = null;
}
void setEditingSourceText(bool value) {
_editing.value = value;
}
void submitSourceTextEdits(String text) {
_queue.clear();
_currentITStep.value = null;
_goldRouteTracker = null;
_sourceText.value = text;
setEditingSourceText(false);
_continueIT();
}
ContinuanceModel selectContinuance(int index) {
if (_currentITStep.value == null) {
throw "onSelectContinuance called when _currentITStep is null";
}
if (index < 0 || index >= _currentITStep.value!.continuances.length) {
throw "onSelectContinuance called with invalid index $index";
}
final currentStep = _currentITStep.value!;
currentStep.continuances[index] = currentStep.continuances[index].copyWith(
wasClicked: true,
);
_currentITStep.value = _currentITStep.value!.copyWith(
continuances: currentStep.continuances,
);
return _currentITStep.value!.continuances[index];
}
void acceptContinuance(int chosenIndex) {
if (_currentITStep.value == null) {
throw "onAcceptContinuance called when _currentITStep is null";
}
if (chosenIndex < 0 ||
chosenIndex >= _currentITStep.value!.continuances.length) {
throw "onAcceptContinuance called with invalid index $chosenIndex";
}
acceptedContinuanceStream.add(
CompletedITStepModel(
_currentITStep.value!.continuances,
chosen: chosenIndex,
),
);
_continueIT();
}
Future<void> _continueIT() async {
if (_continuing) return;
_continuing = true;
try {
if (_currentITStep.value == null) {
await _initTranslationData();
} else if (_queue.isEmpty) {
closeIT();
} else {
final nextStepCompleter = _queue.removeFirst();
_currentITStep.value = await nextStepCompleter.future;
}
} catch (e) {
onError(e);
} finally {
_continuing = false;
}
}
Future<void> _initTranslationData() async {
final res = await _safeRequest("");
if (_sourceText.value == null || !_open.value) return;
if (res.isError || res.result?.goldContinuances == null) {
onError(res.asError!);
return;
}
final result = res.result!;
_goldRouteTracker = GoldRouteTrackerModel(
result.goldContinuances!,
_sourceText.value!,
);
_currentITStep.value = ITStepModel.fromResponse(
sourceText: _sourceText.value!,
currentText: "",
responseModel: res.result!,
storedGoldContinuances: _goldRouteTracker!.continuances,
);
_fillITStepQueue();
}
Future<void> _fillITStepQueue() async {
if (_sourceText.value == null ||
_goldRouteTracker!.continuances.length < 2) {
return;
}
final sourceText = _sourceText.value!;
final goldContinuances = _goldRouteTracker!.continuances;
String currentText = goldContinuances[0].text;
for (int i = 1; i < goldContinuances.length; i++) {
if (_sourceText.value == null || !_open.value) {
return;
}
final completer = Completer<ITStepModel>();
_queue.add(completer);
final resp = await _safeRequest(currentText);
if (resp.isError) {
completer.completeError(resp.asError!);
break;
} else {
final step = ITStepModel.fromResponse(
sourceText: sourceText,
currentText: currentText,
responseModel: resp.result!,
storedGoldContinuances: goldContinuances,
);
completer.complete(step);
}
currentText += goldContinuances[i].text;
}
}
}