fluffychat/lib/pangea/choreographer/controllers/alternative_translator.dart
sienna-sterling ddbc215252
2353-add-number-animation-xp-earned-in-it-bar (#2363)
* fix(IT Controller): fixed accuracy issues with star calcultion and point calculation for vocab + grammar. Added number animation. Staggered animations

* generated

* chore: redirect to new group page on click new chat button in space view (#2354)

* chore: disable custom message text sizing (#2355)

* feat: initial work to prevent giving points for copy-pasted text (#2345)

* feat: initial work to prevent giving points for copy-pasted text

* chore: replace tokenization with comparing token content with pasted content

* fix(emoji_activity_generator): ensure unique choices

* fix(intl_en): two copy edits

* fix(lemma_meaning_widget): fix text alignment

* chore(practice_selection): preferencing tokens without activities in selection

* 2364 on chat creation with activity if no room image set activity image (#2371)

* chore: formatting

* chore: on chat creation without activity, set avatar to activity image if no image set

* chore: in empty chat popup, listen for changes to participant count and close self if it increases (#2372)

* chore: constrain width of unsubscribed card (#2373)

* chore: fix dialogs in report offensive message flow (#2380)

* chore: fix off-center close button in level up notifications (#2382)

* chore: fix discrepency between original message and centered message border radius (#2383)

* chore: don't show presence indicator on small avatars (#2386)

* chore: give max height to image in activity suggestion dialog (#2403)

* chore: when navigating to space details, always open space view (#2405)

* chore: fix vertical alignment of tokens in HTML-formatted messages (#2406)

* added robot animation and message to instruct user to wait after too … (#2415)

* added robot animation and message to instruct user to wait after too many join with code attempts

* chore: formatting

* replaced hardcoded text with intl_en.arb

* Resolving missing import

* generated

* chore: formatting

---------

Co-authored-by: ggurdin <ggurdin@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* refactor: separate token and message reading assistance modes (#2416)

* refactor: separate token and message reading assistance modes

* chore: apply same token styling to HTML formatted messages

* chore: don't wait for lemma responses before showing reading assistance content

* 2421 reading assistance mode split feedback from will (#2422)

* chore: make input bar shorter in token mode

* chore: retry showing reading assistance content for initial token

* chore: make background lighter in token mode

* Added 'JoinByCode' button on new group view (#2417)

* Added 'JoinByCode' button on new group view

* generated

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: ggurdin <46800240+ggurdin@users.noreply.github.com>

* fix(IT): added chreo code back + added original feedback star class back

* generated

* chore: revert change to it controller, use choreo record to determine which constructs are new

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: ggurdin <46800240+ggurdin@users.noreply.github.com>
Co-authored-by: wcjord <32568597+wcjord@users.noreply.github.com>
Co-authored-by: Sofanyas Genene <123987957+Sofanyas@users.noreply.github.com>
Co-authored-by: ggurdin <ggurdin@gmail.com>
2025-04-14 11:54:32 -04:00

219 lines
7 KiB
Dart

import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:http/http.dart' as http;
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/constructs_model.dart';
import 'package:fluffychat/pangea/choreographer/constants/choreo_constants.dart';
import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart';
import 'package:fluffychat/pangea/choreographer/controllers/error_service.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
import 'package:fluffychat/widgets/matrix.dart';
import '../repo/similarity_repo.dart';
class AlternativeTranslator {
final Choreographer choreographer;
bool showAlternativeTranslations = false;
bool loadingAlternativeTranslations = false;
bool showTranslationFeedback = false;
String? userTranslation;
FeedbackKey? translationFeedbackKey;
List<String> translations = [];
SimilartyResponseModel? similarityResponse;
AlternativeTranslator(this.choreographer);
void clear() {
userTranslation = null;
showAlternativeTranslations = false;
loadingAlternativeTranslations = false;
showTranslationFeedback = false;
translationFeedbackKey = null;
translations = [];
similarityResponse = null;
}
double get _percentCorrectChoices {
final totalSteps = choreographer.choreoRecord.itSteps.length;
if (totalSteps == 0) return 0.0;
final int correctFirstAttempts = choreographer.itController.completedITSteps
.where(
(step) => !step.continuances.any(
(c) =>
c.level != ChoreoConstants.levelThresholdForGreen &&
c.wasClicked,
),
)
.length;
final double percentage = (correctFirstAttempts / totalSteps) * 100;
return percentage;
}
int get starRating {
final double percent = _percentCorrectChoices;
if (percent == 100) return 5;
if (percent >= 80) return 4;
if (percent >= 60) return 3;
if (percent >= 40) return 2;
if (percent > 0) return 1;
return 0;
}
Future<void> setTranslationFeedback() async {
try {
choreographer.startLoading();
translationFeedbackKey = FeedbackKey.loadingPleaseWait;
showTranslationFeedback = true;
userTranslation = choreographer.currentText;
final double percentCorrect = _percentCorrectChoices;
// Set feedback based on percentage
if (percentCorrect == 100) {
translationFeedbackKey = FeedbackKey.allCorrect;
} else if (percentCorrect >= 80) {
translationFeedbackKey = FeedbackKey.newWayAllGood;
} else {
translationFeedbackKey = FeedbackKey.othersAreBetter;
}
} catch (err, stack) {
if (err is! http.Response) {
ErrorHandler.logError(
e: err,
s: stack,
data: {
"sourceText": choreographer.itController.sourceText,
"currentText": choreographer.currentText,
"userL1": choreographer.l1LangCode,
"userL2": choreographer.l2LangCode,
"goldRouteTranslation":
choreographer.itController.goldRouteTracker.fullTranslation,
},
);
}
choreographer.errorService.setError(
ChoreoError(type: ChoreoErrorType.unknown, raw: err),
);
} finally {
choreographer.stopLoading();
}
}
List<OneConstructUse> get _itStepConstructs {
final metadata = ConstructUseMetaData(
roomId: choreographer.roomId,
timeStamp: DateTime.now(),
);
final List<OneConstructUse> constructs = [];
for (final step in choreographer.choreoRecord.itSteps) {
for (final continuance in step.continuances) {
final ConstructUseTypeEnum useType = continuance.wasClicked &&
continuance.level == ChoreoConstants.levelThresholdForGreen
? ConstructUseTypeEnum.corIt
: continuance.wasClicked
? ConstructUseTypeEnum.incIt
: ConstructUseTypeEnum.ignIt;
final tokens = continuance.tokens.where((t) => t.lemma.saveVocab);
constructs.addAll(
tokens.map(
(token) => OneConstructUse(
useType: useType,
lemma: token.lemma.text,
constructType: ConstructTypeEnum.vocab,
metadata: metadata,
category: token.pos,
form: token.text.content,
),
),
);
for (final token in tokens) {
constructs.add(
OneConstructUse(
useType: useType,
lemma: token.pos,
form: token.text.content,
category: "POS",
constructType: ConstructTypeEnum.morph,
metadata: metadata,
),
);
for (final entry in token.morph.entries) {
constructs.add(
OneConstructUse(
useType: useType,
lemma: entry.value,
form: token.text.content,
category: entry.key,
constructType: ConstructTypeEnum.morph,
metadata: metadata,
),
);
}
}
}
}
return constructs;
}
int countNewConstructs(ConstructTypeEnum type) {
final vocabUses = _itStepConstructs.where((c) => c.constructType == type);
final Map<ConstructIdentifier, int> constructPoints = {};
for (final use in vocabUses) {
constructPoints[use.identifier] ??= 0;
constructPoints[use.identifier] =
constructPoints[use.identifier]! + use.pointValue;
}
final constructListModel =
MatrixState.pangeaController.getAnalytics.constructListModel;
int newConstructCount = 0;
for (final entry in constructPoints.entries) {
final construct = constructListModel.getConstructUses(entry.key);
if (construct?.points == entry.value) {
newConstructCount++;
}
}
return newConstructCount;
}
String getDefaultFeedback(BuildContext context) {
final l10n = L10n.of(context);
switch (translationFeedbackKey) {
case FeedbackKey.allCorrect:
return l10n.perfectTranslation;
case FeedbackKey.newWayAllGood:
return l10n.greatJobTranslation;
case FeedbackKey.othersAreBetter:
if (_percentCorrectChoices >= 60) {
return l10n.goodJobTranslation;
}
if (_percentCorrectChoices >= 40) {
return l10n.makingProgress;
}
return l10n.keepPracticing;
case FeedbackKey.loadingPleaseWait:
return l10n.letMeThink;
case FeedbackKey.allDone:
return l10n.allDone;
default:
return l10n.loadingPleaseWait;
}
}
}
enum FeedbackKey {
allCorrect,
newWayAllGood,
othersAreBetter,
loadingPleaseWait,
allDone,
}
extension FeedbackKeyExtension on FeedbackKey {}