feat: add feedback flag to analytics activities (#5829)

This commit is contained in:
ggurdin 2026-02-26 15:27:23 -05:00 committed by GitHub
parent 774432ef49
commit aa2496dfee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 46 additions and 83 deletions

View file

@ -2,6 +2,7 @@ import 'dart:async';
import 'package:flutter/material.dart';
import 'package:fluffychat/l10n/l10n.dart';
import 'package:fluffychat/pangea/analytics_data/analytics_updater_mixin.dart';
import 'package:fluffychat/pangea/analytics_misc/construct_type_enum.dart';
import 'package:fluffychat/pangea/analytics_misc/example_message_util.dart';
@ -12,10 +13,12 @@ import 'package:fluffychat/pangea/analytics_practice/analytics_practice_session_
import 'package:fluffychat/pangea/analytics_practice/analytics_practice_ui_controller.dart';
import 'package:fluffychat/pangea/analytics_practice/analytics_practice_view.dart';
import 'package:fluffychat/pangea/common/utils/async_state.dart';
import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/common/widgets/feedback_dialog.dart';
import 'package:fluffychat/pangea/languages/language_model.dart';
import 'package:fluffychat/pangea/morphs/morph_features_enum.dart';
import 'package:fluffychat/pangea/practice_activities/message_activity_request.dart';
import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart';
import 'package:fluffychat/pangea/practice_activities/practice_target.dart';
import 'package:fluffychat/widgets/matrix.dart';
class SelectedMorphChoice {
@ -304,23 +307,44 @@ class AnalyticsPracticeState extends State<AnalyticsPractice>
Future<void> startNextActivity() async {
_sessionController.completeActivity();
progress.value = _sessionController.progress;
_sessionController.session?.isComplete == true
? await _completeSession()
: await _continueSession();
await _continueSession();
}
Future<void> skipActivity(MessageActivityRequest request) async {
Future<void> skipActivity(PracticeTarget target) async {
// Record a 0 XP use so that activity isn't chosen again soon
_sessionController.skipActivity();
progress.value = _sessionController.progress;
await _analyticsController.addSkippedActivityAnalytics(
request.target,
target,
_l2!.langCodeShort,
);
}
Future<void> flagActivity(
MultipleChoicePracticeActivityModel activity,
) async {
final feedback = await showDialog<String?>(
context: context,
builder: (context) {
return FeedbackDialog(
title: L10n.of(context).feedbackTitle,
onSubmit: Navigator.of(context).pop,
scrollable: false,
);
},
);
if (feedback == null || feedback.isEmpty) return;
ErrorHandler.logError(
e: 'Practice activity flagged',
data: {'activity': activity.toJson(), 'feedback': feedback},
);
await skipActivity(activity.practiceTarget);
await _continueSession();
}
@override
Widget build(BuildContext context) => AnalyticsPracticeView(this);
}

View file

@ -12,6 +12,7 @@ import 'package:fluffychat/pangea/common/utils/error_handler.dart';
import 'package:fluffychat/pangea/practice_activities/message_activity_request.dart';
import 'package:fluffychat/pangea/practice_activities/practice_activity_model.dart';
import 'package:fluffychat/pangea/practice_activities/practice_generation_repo.dart';
import 'package:fluffychat/pangea/practice_activities/practice_target.dart';
import 'package:fluffychat/widgets/future_loading_dialog.dart';
import 'package:fluffychat/widgets/matrix.dart';
@ -95,7 +96,7 @@ class PracticeSessionController {
}
Future<MultipleChoicePracticeActivityModel?> _initActivityData(
Future Function(MessageActivityRequest) onSkip,
Future Function(PracticeTarget) onSkip,
Future Function(MultipleChoicePracticeActivityModel) onFetch,
) async {
final requests = activityRequests;
@ -106,7 +107,7 @@ class PracticeSessionController {
_fillActivityQueue(requests.skip(i + 1).toList(), onSkip, onFetch);
return res;
} catch (e) {
await onSkip(requests[i]);
await onSkip(requests[i].target);
// Try next request
continue;
}
@ -116,7 +117,7 @@ class PracticeSessionController {
Future<void> _fillActivityQueue(
List<MessageActivityRequest> requests,
Future Function(MessageActivityRequest) onSkip,
Future Function(PracticeTarget) onSkip,
Future Function(MultipleChoicePracticeActivityModel) onFetch,
) async {
for (final request in requests) {
@ -127,7 +128,7 @@ class PracticeSessionController {
completer.complete(res);
} catch (e) {
completer.completeError(e);
await onSkip(request);
await onSkip(request.target);
}
}
}
@ -149,7 +150,7 @@ class PracticeSessionController {
}
Future<MultipleChoicePracticeActivityModel?> getNextActivity(
Future Function(MessageActivityRequest) onSkip,
Future Function(PracticeTarget) onSkip,
Future Function(MultipleChoicePracticeActivityModel) onFetch,
) async {
final session = this.session;

View file

@ -42,6 +42,15 @@ class OngoingActivitySessionView extends StatelessWidget {
Expanded(
child: ListView(
children: [
Align(
alignment: Alignment.centerRight,
child: IconButton(
icon: Icon(Icons.flag_outlined),
onPressed: activity != null
? () => controller.flagActivity(activity)
: null,
),
),
//Hints counter bar for grammar activities only
if (controller.widget.type == ConstructTypeEnum.morph)
Padding(

View file

@ -1,71 +0,0 @@
import 'package:flutter/material.dart';
import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/l10n/l10n.dart';
import '../pangea/bot/widgets/bot_face_svg.dart';
Future<dynamic> showFeedbackDialog(
BuildContext context,
Widget offendingContent,
void Function(String) submitFeedback,
) {
final TextEditingController feedbackController = TextEditingController();
return showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text(
L10n.of(context).reportContentIssueTitle,
textAlign: TextAlign.center,
),
content: SingleChildScrollView(
child: Container(
constraints: const BoxConstraints(maxWidth: 300),
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
const BotFace(width: 60, expression: BotExpression.addled),
const SizedBox(height: 10),
Text(L10n.of(context).reportContentIssueDescription),
const SizedBox(height: 10),
Container(
padding: const EdgeInsets.all(8.0),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8.0),
border: Border.all(color: AppConfig.warning),
),
child: offendingContent,
),
const SizedBox(height: 10),
TextField(
controller: feedbackController,
decoration: InputDecoration(
labelText: L10n.of(context).feedback,
border: const OutlineInputBorder(),
),
maxLines: 4,
),
],
),
),
),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop(); // Close the dialog
},
child: Text(L10n.of(context).cancel),
),
ElevatedButton(
onPressed: () {
// Call the additional callback function
submitFeedback(feedbackController.text);
Navigator.of(context).pop(); // Close the dialog
},
child: Text(L10n.of(context).submit),
),
],
);
},
);
}