skeleton of practice activities
This commit is contained in:
parent
c235842f35
commit
1dcd988be0
12 changed files with 615 additions and 49 deletions
|
|
@ -3963,5 +3963,6 @@
|
|||
"studentAnalyticsNotAvailable": "Student data not currently available",
|
||||
"roomDataMissing": "Some data may be missing from rooms in which you are not a member.",
|
||||
"updatePhoneOS": "You may need to update your device's OS version.",
|
||||
"wordsPerMinute": "Words per minute"
|
||||
"wordsPerMinute": "Words per minute",
|
||||
"practice": "Practice"
|
||||
}
|
||||
|
|
@ -23,4 +23,7 @@ class PangeaEventTypes {
|
|||
|
||||
static const String report = 'm.report';
|
||||
static const textToSpeechRule = "p.rule.text_to_speech";
|
||||
|
||||
static const activityResponse = "pangea.activity_res";
|
||||
static const acitivtyRequest = "pangea.activity_req";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,13 @@ import 'package:flutter_gen/gen_l10n/l10n.dart';
|
|||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
enum MessageMode { translation, definition, speechToText, textToSpeech }
|
||||
enum MessageMode {
|
||||
translation,
|
||||
definition,
|
||||
speechToText,
|
||||
textToSpeech,
|
||||
practiceActivity
|
||||
}
|
||||
|
||||
extension MessageModeExtension on MessageMode {
|
||||
IconData get icon {
|
||||
|
|
@ -17,6 +23,8 @@ extension MessageModeExtension on MessageMode {
|
|||
//TODO change icon for audio messages
|
||||
case MessageMode.definition:
|
||||
return Icons.book;
|
||||
case MessageMode.practiceActivity:
|
||||
return Symbols.fitness_center;
|
||||
default:
|
||||
return Icons.error; // Icon to indicate an error or unsupported mode
|
||||
}
|
||||
|
|
@ -32,6 +40,8 @@ extension MessageModeExtension on MessageMode {
|
|||
return L10n.of(context)!.speechToTextTooltip;
|
||||
case MessageMode.definition:
|
||||
return L10n.of(context)!.definitions;
|
||||
case MessageMode.practiceActivity:
|
||||
return L10n.of(context)!.practice;
|
||||
default:
|
||||
return L10n.of(context)!
|
||||
.oopsSomethingWentWrong; // Title to indicate an error or unsupported mode
|
||||
|
|
@ -48,6 +58,8 @@ extension MessageModeExtension on MessageMode {
|
|||
return L10n.of(context)!.speechToTextTooltip;
|
||||
case MessageMode.definition:
|
||||
return L10n.of(context)!.define;
|
||||
case MessageMode.practiceActivity:
|
||||
return L10n.of(context)!.practice;
|
||||
default:
|
||||
return L10n.of(context)!
|
||||
.oopsSomethingWentWrong; // Title to indicate an error or unsupported mode
|
||||
|
|
@ -58,6 +70,7 @@ extension MessageModeExtension on MessageMode {
|
|||
switch (this) {
|
||||
case MessageMode.translation:
|
||||
case MessageMode.textToSpeech:
|
||||
case MessageMode.practiceActivity:
|
||||
case MessageMode.definition:
|
||||
return event.messageType == MessageTypes.Text;
|
||||
case MessageMode.speechToText:
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ extension PangeaEvent on Event {
|
|||
return PangeaRepresentation.fromJson(json) as V;
|
||||
case PangeaEventTypes.choreoRecord:
|
||||
return ChoreoRecord.fromJson(json) as V;
|
||||
case PangeaEventTypes.activityResponse:
|
||||
return PangeaMessageTokens.fromJson(json) as V;
|
||||
default:
|
||||
throw Exception("$type events do not have pangea content");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,11 +4,15 @@ import 'package:collection/collection.dart';
|
|||
import 'package:fluffychat/pangea/constants/model_keys.dart';
|
||||
import 'package:fluffychat/pangea/controllers/text_to_speech_controller.dart';
|
||||
import 'package:fluffychat/pangea/enum/audio_encoding_enum.dart';
|
||||
import 'package:fluffychat/pangea/enum/construct_type_enum.dart';
|
||||
import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart';
|
||||
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_representation_event.dart';
|
||||
import 'package:fluffychat/pangea/matrix_event_wrappers/practice_activity_event.dart';
|
||||
import 'package:fluffychat/pangea/models/choreo_record.dart';
|
||||
import 'package:fluffychat/pangea/models/class_model.dart';
|
||||
import 'package:fluffychat/pangea/models/pangea_match_model.dart';
|
||||
import 'package:fluffychat/pangea/models/practice_activities.dart/multiple_choice_activity_model.dart';
|
||||
import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart';
|
||||
import 'package:fluffychat/pangea/models/representation_content_model.dart';
|
||||
import 'package:fluffychat/pangea/models/speech_to_text_models.dart';
|
||||
import 'package:fluffychat/pangea/models/tokens_event_content_model.dart';
|
||||
|
|
@ -601,6 +605,52 @@ class PangeaMessageEvent {
|
|||
return steps;
|
||||
}
|
||||
|
||||
List<PracticeActivityEvent> get _practiceActivityEvents => _latestEdit
|
||||
.aggregatedEvents(
|
||||
timeline,
|
||||
PangeaEventTypes.activityResponse,
|
||||
)
|
||||
.map(
|
||||
(e) => PracticeActivityEvent(
|
||||
event: e,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
||||
List<PracticeActivityModel> activities(String langCode) {
|
||||
// final List<PracticeActivityEvent> practiceActivityEvents = _practiceActivityEvents;
|
||||
|
||||
// final List<PracticeActivityModel> activities = _practiceActivityEvents
|
||||
// .map(
|
||||
// (e) => PracticeActivityModel.fromJson(
|
||||
// e.event.content,
|
||||
// ),
|
||||
// )
|
||||
// .where(
|
||||
// (element) => element.langCode == langCode,
|
||||
// )
|
||||
// .toList();
|
||||
|
||||
// return activities;
|
||||
|
||||
// for now, return a hard-coded activity
|
||||
final PracticeActivityModel activityModel = PracticeActivityModel(
|
||||
tgtConstructs: [
|
||||
ConstructIdentifier(lemma: "be", type: ConstructType.vocab.string),
|
||||
],
|
||||
activityType: ActivityType.multipleChoice,
|
||||
langCode: langCode,
|
||||
msgId: _event.eventId,
|
||||
multipleChoice: MultipleChoice(
|
||||
question: "What is a synonym for 'happy'?",
|
||||
choices: ["sad", "angry", "joyful", "tired"],
|
||||
correctAnswer: "joyful",
|
||||
),
|
||||
);
|
||||
|
||||
return [activityModel];
|
||||
}
|
||||
|
||||
// List<SpanData> get activities =>
|
||||
//each match is turned into an activity that other students can access
|
||||
//they're not told the answer but have to find it themselves
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
import 'package:fluffychat/pangea/extensions/pangea_event_extension.dart';
|
||||
import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart';
|
||||
import 'package:fluffychat/pangea/utils/error_handler.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import '../constants/pangea_event_types.dart';
|
||||
|
||||
class PracticeActivityEvent {
|
||||
Event event;
|
||||
PracticeActivityModel? _content;
|
||||
|
||||
PracticeActivityEvent({required this.event}) {
|
||||
if (event.type != PangeaEventTypes.activityResponse) {
|
||||
throw Exception(
|
||||
"${event.type} should not be used to make a PracticeActivityEvent",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PracticeActivityModel? get practiceActivity {
|
||||
try {
|
||||
_content ??= event.getPangeaContent<PracticeActivityModel>();
|
||||
return _content!;
|
||||
} catch (err, s) {
|
||||
ErrorHandler.logError(e: err, s: s);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
class MultipleChoice {
|
||||
final String question;
|
||||
final List<String> choices;
|
||||
final String correctAnswer;
|
||||
|
||||
MultipleChoice({
|
||||
required this.question,
|
||||
required this.choices,
|
||||
required this.correctAnswer,
|
||||
});
|
||||
|
||||
bool get isValidQuestion => choices.contains(correctAnswer);
|
||||
|
||||
int get correctAnswerIndex => choices.indexOf(correctAnswer);
|
||||
|
||||
factory MultipleChoice.fromJson(Map<String, dynamic> json) {
|
||||
return MultipleChoice(
|
||||
question: json['question'] as String,
|
||||
choices: (json['choices'] as List).map((e) => e as String).toList(),
|
||||
correctAnswer: json['correct_answer'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'question': question,
|
||||
'choices': choices,
|
||||
'correct_answer': correctAnswer,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// record the options that the user selected
|
||||
// note that this is not the same as the correct answer
|
||||
// the user might have selected multiple options before
|
||||
// finding the answer
|
||||
class MultipleChoiceActivityCompletionRecord {
|
||||
final String question;
|
||||
List<String> selectedOptions;
|
||||
|
||||
MultipleChoiceActivityCompletionRecord({
|
||||
required this.question,
|
||||
this.selectedOptions = const [],
|
||||
});
|
||||
|
||||
factory MultipleChoiceActivityCompletionRecord.fromJson(
|
||||
Map<String, dynamic> json,
|
||||
) {
|
||||
return MultipleChoiceActivityCompletionRecord(
|
||||
question: json['question'] as String,
|
||||
selectedOptions:
|
||||
(json['selected_options'] as List).map((e) => e as String).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'question': question,
|
||||
'selected_options': selectedOptions,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
import 'package:fluffychat/pangea/models/practice_activities.dart/multiple_choice_activity_model.dart';
|
||||
|
||||
class ConstructIdentifier {
|
||||
final String lemma;
|
||||
final String type;
|
||||
|
||||
ConstructIdentifier({required this.lemma, required this.type});
|
||||
|
||||
factory ConstructIdentifier.fromJson(Map<String, dynamic> json) {
|
||||
return ConstructIdentifier(
|
||||
lemma: json['lemma'] as String,
|
||||
type: json['type'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'lemma': lemma,
|
||||
'type': type,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
enum ActivityType { multipleChoice, freeResponse, listening, speaking }
|
||||
|
||||
class MessageInfo {
|
||||
final String msgId;
|
||||
final String roomId;
|
||||
final String text;
|
||||
|
||||
MessageInfo({required this.msgId, required this.roomId, required this.text});
|
||||
|
||||
factory MessageInfo.fromJson(Map<String, dynamic> json) {
|
||||
return MessageInfo(
|
||||
msgId: json['msg_id'] as String,
|
||||
roomId: json['room_id'] as String,
|
||||
text: json['text'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'msg_id': msgId,
|
||||
'room_id': roomId,
|
||||
'text': text,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class ActivityRequest {
|
||||
final String mode;
|
||||
final List<ConstructIdentifier>? targetConstructs;
|
||||
final List<MessageInfo>? candidateMessages;
|
||||
final List<String>? userIds;
|
||||
final ActivityType? activityType;
|
||||
final int numActivities;
|
||||
|
||||
ActivityRequest({
|
||||
required this.mode,
|
||||
this.targetConstructs,
|
||||
this.candidateMessages,
|
||||
this.userIds,
|
||||
this.activityType,
|
||||
this.numActivities = 10,
|
||||
});
|
||||
|
||||
factory ActivityRequest.fromJson(Map<String, dynamic> json) {
|
||||
return ActivityRequest(
|
||||
mode: json['mode'] as String,
|
||||
targetConstructs: (json['target_constructs'] as List?)
|
||||
?.map((e) => ConstructIdentifier.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
candidateMessages: (json['candidate_msgs'] as List)
|
||||
.map((e) => MessageInfo.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
userIds: (json['user_ids'] as List?)?.map((e) => e as String).toList(),
|
||||
activityType: ActivityType.values.firstWhere(
|
||||
(e) => e.toString().split('.').last == json['activity_type'],
|
||||
),
|
||||
numActivities: json['num_activities'] as int,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'mode': mode,
|
||||
'target_constructs': targetConstructs?.map((e) => e.toJson()).toList(),
|
||||
'candidate_msgs': candidateMessages?.map((e) => e.toJson()).toList(),
|
||||
'user_ids': userIds,
|
||||
'activity_type': activityType?.toString().split('.').last,
|
||||
'num_activities': numActivities,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class FreeResponse {
|
||||
final String question;
|
||||
final String correctAnswer;
|
||||
final String gradingGuide;
|
||||
|
||||
FreeResponse({
|
||||
required this.question,
|
||||
required this.correctAnswer,
|
||||
required this.gradingGuide,
|
||||
});
|
||||
|
||||
factory FreeResponse.fromJson(Map<String, dynamic> json) {
|
||||
return FreeResponse(
|
||||
question: json['question'] as String,
|
||||
correctAnswer: json['correct_answer'] as String,
|
||||
gradingGuide: json['grading_guide'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'question': question,
|
||||
'correct_answer': correctAnswer,
|
||||
'grading_guide': gradingGuide,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Listening {
|
||||
final String audioUrl;
|
||||
final String text;
|
||||
|
||||
Listening({required this.audioUrl, required this.text});
|
||||
|
||||
factory Listening.fromJson(Map<String, dynamic> json) {
|
||||
return Listening(
|
||||
audioUrl: json['audio_url'] as String,
|
||||
text: json['text'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'audio_url': audioUrl,
|
||||
'text': text,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class Speaking {
|
||||
final String text;
|
||||
|
||||
Speaking({required this.text});
|
||||
|
||||
factory Speaking.fromJson(Map<String, dynamic> json) {
|
||||
return Speaking(
|
||||
text: json['text'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'text': text,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
class PracticeActivityModel {
|
||||
final List<ConstructIdentifier> tgtConstructs;
|
||||
final String langCode;
|
||||
final String msgId;
|
||||
final ActivityType activityType;
|
||||
final MultipleChoice? multipleChoice;
|
||||
final Listening? listening;
|
||||
final Speaking? speaking;
|
||||
final FreeResponse? freeResponse;
|
||||
|
||||
PracticeActivityModel({
|
||||
required this.tgtConstructs,
|
||||
required this.langCode,
|
||||
required this.msgId,
|
||||
required this.activityType,
|
||||
this.multipleChoice,
|
||||
this.listening,
|
||||
this.speaking,
|
||||
this.freeResponse,
|
||||
});
|
||||
|
||||
factory PracticeActivityModel.fromJson(Map<String, dynamic> json) {
|
||||
return PracticeActivityModel(
|
||||
tgtConstructs: (json['tgt_constructs'] as List)
|
||||
.map((e) => ConstructIdentifier.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
langCode: json['lang_code'] as String,
|
||||
msgId: json['msg_id'] as String,
|
||||
activityType: ActivityType.values.firstWhere(
|
||||
(e) => e.toString().split('.').last == json['activity_type'],
|
||||
),
|
||||
multipleChoice: json['multiple_choice'] != null
|
||||
? MultipleChoice.fromJson(
|
||||
json['multiple_choice'] as Map<String, dynamic>,
|
||||
)
|
||||
: null,
|
||||
listening: json['listening'] != null
|
||||
? Listening.fromJson(json['listening'] as Map<String, dynamic>)
|
||||
: null,
|
||||
speaking: json['speaking'] != null
|
||||
? Speaking.fromJson(json['speaking'] as Map<String, dynamic>)
|
||||
: null,
|
||||
freeResponse: json['free_response'] != null
|
||||
? FreeResponse.fromJson(json['free_response'] as Map<String, dynamic>)
|
||||
: null,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'tgt_constructs': tgtConstructs.map((e) => e.toJson()).toList(),
|
||||
'lang_code': langCode,
|
||||
'msg_id': msgId,
|
||||
'activity_type': activityType.toString().split('.').last,
|
||||
'multiple_choice': multipleChoice?.toJson(),
|
||||
'listening': listening?.toJson(),
|
||||
'speaking': speaking?.toJson(),
|
||||
'free_response': freeResponse?.toJson(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ import 'package:fluffychat/pangea/widgets/chat/message_translation_card.dart';
|
|||
import 'package:fluffychat/pangea/widgets/chat/message_unsubscribed_card.dart';
|
||||
import 'package:fluffychat/pangea/widgets/chat/overlay_message.dart';
|
||||
import 'package:fluffychat/pangea/widgets/igc/word_data_card.dart';
|
||||
import 'package:fluffychat/pangea/widgets/practice_activity_card/message_practice_activity_card.dart';
|
||||
import 'package:fluffychat/pangea/widgets/user_settings/p_language_dialog.dart';
|
||||
import 'package:fluffychat/widgets/matrix.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
|
@ -215,6 +216,9 @@ class MessageToolbarState extends State<MessageToolbar> {
|
|||
case MessageMode.definition:
|
||||
showDefinition();
|
||||
break;
|
||||
case MessageMode.practiceActivity:
|
||||
showPracticeActivity();
|
||||
break;
|
||||
default:
|
||||
ErrorHandler.logError(
|
||||
e: "Invalid toolbar mode",
|
||||
|
|
@ -272,6 +276,15 @@ class MessageToolbarState extends State<MessageToolbar> {
|
|||
);
|
||||
}
|
||||
|
||||
void showPracticeActivity() {
|
||||
toolbarContent = PracticeActivityCard(
|
||||
practiceActivity: widget.pangeaMessageEvent
|
||||
// @ggurdin - is this the best way to get the l2 language here?
|
||||
.activities(widget.pangeaMessageEvent.messageDisplayLangCode)
|
||||
.first,
|
||||
);
|
||||
}
|
||||
|
||||
void showImage() {}
|
||||
|
||||
void spellCheck() {}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
//stateful widget that displays a card with a practice activity
|
||||
|
||||
import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart';
|
||||
import 'package:fluffychat/pangea/widgets/practice_activity_card/multiple_choice_activity.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class PracticeActivityCard extends StatefulWidget {
|
||||
final PracticeActivityModel practiceActivity;
|
||||
|
||||
const PracticeActivityCard({
|
||||
super.key,
|
||||
required this.practiceActivity,
|
||||
});
|
||||
|
||||
@override
|
||||
MessagePracticeActivityCardState createState() =>
|
||||
MessagePracticeActivityCardState();
|
||||
}
|
||||
|
||||
//parameters for the stateful widget
|
||||
// practiceActivity: the practice activity to display
|
||||
// use a switch statement based on the type of the practice activity to display the appropriate content
|
||||
// just use different widgets for the different types, don't define in this file
|
||||
// for multiple choice, use the MultipleChoiceActivity widget
|
||||
// for the rest, just return SizedBox.shrink() for now
|
||||
class MessagePracticeActivityCardState extends State<PracticeActivityCard> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
switch (widget.practiceActivity.activityType) {
|
||||
case ActivityType.multipleChoice:
|
||||
return MultipleChoiceActivity(
|
||||
practiceActivity: widget.practiceActivity,
|
||||
);
|
||||
default:
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
// stateful widget that displays a card with a practice activity of type multiple choice
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fluffychat/pangea/choreographer/widgets/choice_array.dart';
|
||||
import 'package:fluffychat/pangea/models/practice_activities.dart/multiple_choice_activity_model.dart';
|
||||
import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MultipleChoiceActivity extends StatefulWidget {
|
||||
final PracticeActivityModel practiceActivity;
|
||||
|
||||
const MultipleChoiceActivity({
|
||||
super.key,
|
||||
required this.practiceActivity,
|
||||
});
|
||||
|
||||
@override
|
||||
MultipleChoiceActivityState createState() => MultipleChoiceActivityState();
|
||||
}
|
||||
|
||||
//parameters for the stateful widget
|
||||
// practiceActivity: the practice activity to display
|
||||
// show the question text and choices
|
||||
// use the ChoiceArray widget to display the choices
|
||||
class MultipleChoiceActivityState extends State<MultipleChoiceActivity> {
|
||||
int? selectedChoiceIndex;
|
||||
|
||||
late MultipleChoiceActivityCompletionRecord? completionRecord;
|
||||
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
selectedChoiceIndex = null;
|
||||
completionRecord = MultipleChoiceActivityCompletionRecord(
|
||||
question: widget.practiceActivity.multipleChoice!.question,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
widget.practiceActivity.multipleChoice!.question,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
ChoicesArray(
|
||||
isLoading: false,
|
||||
uniqueKeyForLayerLink: (index) => "multiple_choice_$index",
|
||||
onLongPress: null,
|
||||
onPressed: (index) {
|
||||
selectedChoiceIndex = index;
|
||||
completionRecord!.selectedOptions
|
||||
.add(widget.practiceActivity.multipleChoice!.choices[index]);
|
||||
setState(() {});
|
||||
},
|
||||
originalSpan: "placeholder",
|
||||
selectedChoiceIndex: selectedChoiceIndex,
|
||||
choices: widget.practiceActivity.multipleChoice!.choices
|
||||
.mapIndexed(
|
||||
(int index, String value) => Choice(
|
||||
text: value,
|
||||
color: null,
|
||||
isGold:
|
||||
widget.practiceActivity.multipleChoice!.correctAnswer ==
|
||||
value,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -839,7 +839,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"be": [
|
||||
|
|
@ -2277,7 +2278,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"bn": [
|
||||
|
|
@ -3177,7 +3179,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"bo": [
|
||||
|
|
@ -4077,7 +4080,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"ca": [
|
||||
|
|
@ -4977,7 +4981,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"cs": [
|
||||
|
|
@ -5877,7 +5882,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"de": [
|
||||
|
|
@ -6724,7 +6730,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"el": [
|
||||
|
|
@ -7624,7 +7631,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"eo": [
|
||||
|
|
@ -8524,7 +8532,12 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"es": [
|
||||
"practice"
|
||||
],
|
||||
|
||||
"et": [
|
||||
|
|
@ -9367,7 +9380,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"eu": [
|
||||
|
|
@ -10210,7 +10224,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"fa": [
|
||||
|
|
@ -11110,7 +11125,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"fi": [
|
||||
|
|
@ -12010,7 +12026,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"fr": [
|
||||
|
|
@ -12910,7 +12927,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"ga": [
|
||||
|
|
@ -13810,7 +13828,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"gl": [
|
||||
|
|
@ -14653,7 +14672,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"he": [
|
||||
|
|
@ -15553,7 +15573,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"hi": [
|
||||
|
|
@ -16453,7 +16474,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"hr": [
|
||||
|
|
@ -17340,7 +17362,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"hu": [
|
||||
|
|
@ -18240,7 +18263,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"ia": [
|
||||
|
|
@ -19664,7 +19688,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"id": [
|
||||
|
|
@ -20564,7 +20589,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"ie": [
|
||||
|
|
@ -21464,7 +21490,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"it": [
|
||||
|
|
@ -22349,7 +22376,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"ja": [
|
||||
|
|
@ -23249,7 +23277,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"ko": [
|
||||
|
|
@ -24149,7 +24178,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"lt": [
|
||||
|
|
@ -25049,7 +25079,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"lv": [
|
||||
|
|
@ -25949,7 +25980,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"nb": [
|
||||
|
|
@ -26849,7 +26881,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"nl": [
|
||||
|
|
@ -27749,7 +27782,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"pl": [
|
||||
|
|
@ -28649,7 +28683,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"pt": [
|
||||
|
|
@ -29549,7 +29584,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"pt_BR": [
|
||||
|
|
@ -30418,7 +30454,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"pt_PT": [
|
||||
|
|
@ -31318,7 +31355,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"ro": [
|
||||
|
|
@ -32218,7 +32256,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"ru": [
|
||||
|
|
@ -33061,7 +33100,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"sk": [
|
||||
|
|
@ -33961,7 +34001,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"sl": [
|
||||
|
|
@ -34861,7 +34902,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"sr": [
|
||||
|
|
@ -35761,7 +35803,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"sv": [
|
||||
|
|
@ -36626,7 +36669,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"ta": [
|
||||
|
|
@ -37526,7 +37570,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"th": [
|
||||
|
|
@ -38426,7 +38471,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"tr": [
|
||||
|
|
@ -39311,7 +39357,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"uk": [
|
||||
|
|
@ -40154,7 +40201,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"vi": [
|
||||
|
|
@ -41054,7 +41102,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"zh": [
|
||||
|
|
@ -41897,7 +41946,8 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
],
|
||||
|
||||
"zh_Hant": [
|
||||
|
|
@ -42797,6 +42847,7 @@
|
|||
"studentAnalyticsNotAvailable",
|
||||
"roomDataMissing",
|
||||
"updatePhoneOS",
|
||||
"wordsPerMinute"
|
||||
"wordsPerMinute",
|
||||
"practice"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue