fetch translation on activity target generation

This commit is contained in:
ggurdin 2026-01-23 10:30:26 -05:00
parent 5938b15820
commit 558d8fdc50
No known key found for this signature in database
GPG key ID: A01CB41737CBB478
6 changed files with 75 additions and 97 deletions

View file

@ -508,14 +508,4 @@ class AnalyticsPracticeState extends State<AnalyticsPractice>
@override
Widget build(BuildContext context) => AnalyticsPracticeView(this);
Future<String> requestTranslation() async {
final request = activityTarget.value;
if (request?.grammarErrorInfo == null) {
throw L10n.of(context).oopsSomethingWentWrong;
}
final event = request!.grammarErrorInfo!.event!;
return await event.requestRespresentationByL1();
}
}

View file

@ -5,6 +5,7 @@ import 'package:fluffychat/pangea/analytics_misc/construct_use_type_enum.dart';
import 'package:fluffychat/pangea/analytics_practice/analytics_practice_constants.dart';
import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_model.dart';
import 'package:fluffychat/pangea/common/network/requests.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/constructs/construct_identifier.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/events/models/pangea_token_model.dart';
@ -256,6 +257,22 @@ class AnalyticsPracticeSessionRepo {
// Skip if no valid tokens found for this grammar error
if (choiceTokens.isEmpty) continue;
String? translation;
try {
translation = await event.requestRespresentationByL1();
} catch (e, s) {
ErrorHandler.logError(
e: e,
s: s,
data: {
'context': 'AnalyticsPracticeSessionRepo._fetchErrors',
'message': 'Failed to fetch translation for analytics practice',
'event_id': event.eventId,
},
);
}
if (translation == null) continue;
targets.add(
AnalyticsActivityTarget(
target: PracticeTarget(
@ -267,7 +284,7 @@ class AnalyticsPracticeSessionRepo {
choreo: choreo,
stepIndex: i,
eventID: event.eventId,
event: event,
translation: translation,
),
),
);

View file

@ -253,7 +253,7 @@ class _AnalyticsPracticeCenterContent extends StatelessWidget {
key: ValueKey(
'${activity.eventID}_${activity.errorOffset}_${activity.errorLength}',
),
controller: controller,
translation: activity.translation,
),
],
),
@ -557,11 +557,11 @@ class _ChoiceCard extends StatelessWidget {
}
class _GrammarErrorTranslationButton extends StatefulWidget {
final AnalyticsPracticeState controller;
final String translation;
const _GrammarErrorTranslationButton({
super.key,
required this.controller,
required this.translation,
});
@override
@ -571,19 +571,16 @@ class _GrammarErrorTranslationButton extends StatefulWidget {
class _GrammarErrorTranslationButtonState
extends State<_GrammarErrorTranslationButton> {
Future<String>? _translationFuture;
bool _showTranslation = false;
void _toggleTranslation() {
if (_showTranslation) {
setState(() {
_showTranslation = false;
_translationFuture = null;
});
} else {
setState(() {
_showTranslation = true;
_translationFuture = widget.controller.requestTranslation();
});
}
}
@ -599,83 +596,27 @@ class _GrammarErrorTranslationButtonState
children: [
if (_showTranslation)
Flexible(
child: FutureBuilder<String>(
future: _translationFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
decoration: BoxDecoration(
color: Color.alphaBlend(
Colors.white.withAlpha(180),
ThemeData.dark().colorScheme.primary,
),
borderRadius: BorderRadius.circular(16),
),
child: const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator.adaptive(
strokeWidth: 2,
),
),
);
}
if (snapshot.hasError) {
return Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
decoration: BoxDecoration(
color: Color.alphaBlend(
Colors.white.withAlpha(180),
ThemeData.dark().colorScheme.primary,
),
borderRadius: BorderRadius.circular(16),
),
child: Text(
L10n.of(context).oopsSomethingWentWrong,
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimaryFixed,
fontSize: AppConfig.fontSizeFactor *
AppConfig.messageFontSize,
),
),
);
}
if (snapshot.hasData) {
return Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
decoration: BoxDecoration(
color: Color.alphaBlend(
Colors.white.withAlpha(180),
ThemeData.dark().colorScheme.primary,
),
borderRadius: BorderRadius.circular(16),
),
child: Text(
snapshot.data!,
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimaryFixed,
fontSize: AppConfig.fontSizeFactor *
AppConfig.messageFontSize,
),
textAlign: TextAlign.center,
),
);
}
return const SizedBox();
},
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 8,
),
decoration: BoxDecoration(
color: Color.alphaBlend(
Colors.white.withAlpha(180),
ThemeData.dark().colorScheme.primary,
),
borderRadius: BorderRadius.circular(16),
),
child: Text(
widget.translation,
style: TextStyle(
color: Theme.of(context).colorScheme.onPrimaryFixed,
fontSize:
AppConfig.fontSizeFactor * AppConfig.messageFontSize,
),
textAlign: TextAlign.center,
),
),
),
if (!_showTranslation)

View file

@ -45,6 +45,7 @@ class GrammarErrorPracticeGenerator {
errorOffset: igcMatch.offset,
errorLength: igcMatch.length,
eventID: eventID,
translation: req.grammarErrorInfo!.translation,
),
);
}

View file

@ -4,7 +4,6 @@ import 'package:sentry_flutter/sentry_flutter.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/choreographer/choreo_record_model.dart';
import 'package:fluffychat/pangea/events/event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart';
import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart';
@ -46,13 +45,13 @@ class GrammarErrorRequestInfo {
final ChoreoRecordModel choreo;
final int stepIndex;
final String eventID;
final PangeaMessageEvent? event;
final String translation;
const GrammarErrorRequestInfo({
required this.choreo,
required this.stepIndex,
required this.eventID,
this.event,
required this.translation,
});
Map<String, dynamic> toJson() {
@ -60,6 +59,7 @@ class GrammarErrorRequestInfo {
'choreo': choreo.toJson(),
'step_index': stepIndex,
'event_id': eventID,
'translation': translation,
};
}
@ -68,6 +68,7 @@ class GrammarErrorRequestInfo {
choreo: ChoreoRecordModel.fromJson(json['choreo']),
stepIndex: json['step_index'] as int,
eventID: json['event_id'] as String,
translation: json['translation'] as String,
);
}
}

View file

@ -187,6 +187,21 @@ sealed class PracticeActivityModel {
tokens: tokens,
matchContent: matchContent!,
);
case ActivityTypeEnum.grammarError:
assert(
multipleChoiceContent != null,
"multipleChoiceContent is null in PracticeActivityModel.fromJson for grammarError",
);
return GrammarErrorPracticeActivityModel(
langCode: langCode,
tokens: tokens,
multipleChoiceContent: multipleChoiceContent!,
text: json['text'] as String,
errorOffset: json['error_offset'] as int,
errorLength: json['error_length'] as int,
eventID: json['event_id'] as String,
translation: json['translation'] as String,
);
default:
throw ("Unsupported activity type in PracticeActivityModel.fromJson: $type");
}
@ -358,6 +373,7 @@ class GrammarErrorPracticeActivityModel
final int errorOffset;
final int errorLength;
final String eventID;
final String translation;
GrammarErrorPracticeActivityModel({
required super.tokens,
@ -367,7 +383,19 @@ class GrammarErrorPracticeActivityModel
required this.errorOffset,
required this.errorLength,
required this.eventID,
required this.translation,
});
@override
Map<String, dynamic> toJson() {
final json = super.toJson();
json['text'] = text;
json['error_offset'] = errorOffset;
json['error_length'] = errorLength;
json['event_id'] = eventID;
json['translation'] = translation;
return json;
}
}
class EmojiPracticeActivityModel extends MatchPracticeActivityModel {