seperating practice activity-specific logic and functionality from navigation / event sending logic

pull/1384/head
ggurdin 1 year ago
parent 167b8819e4
commit d0e03aea97

@ -26,7 +26,13 @@ class PangeaEventTypes {
static const String report = 'm.report'; static const String report = 'm.report';
static const textToSpeechRule = "p.rule.text_to_speech"; static const textToSpeechRule = "p.rule.text_to_speech";
static const pangeaActivityRes = "pangea.activity_res"; /// A request to the server to generate activities
static const acitivtyRequest = "pangea.activity_req"; static const activityRequest = "pangea.activity_req";
/// A practice activity that is related to a message
static const pangeaActivity = "pangea.activity_res";
/// A record of completion of an activity. There
/// can be one per user per activity.
static const activityRecord = "pangea.activity_completion"; static const activityRecord = "pangea.activity_completion";
} }

@ -51,7 +51,7 @@ class PracticeGenerationController {
final Event? activityEvent = await pangeaMessageEvent.room.sendPangeaEvent( final Event? activityEvent = await pangeaMessageEvent.room.sendPangeaEvent(
content: model.toJson(), content: model.toJson(),
parentEventId: pangeaMessageEvent.eventId, parentEventId: pangeaMessageEvent.eventId,
type: PangeaEventTypes.pangeaActivityRes, type: PangeaEventTypes.pangeaActivity,
); );
if (activityEvent == null) { if (activityEvent == null) {

@ -28,7 +28,7 @@ extension PangeaEvent on Event {
return PangeaRepresentation.fromJson(json) as V; return PangeaRepresentation.fromJson(json) as V;
case PangeaEventTypes.choreoRecord: case PangeaEventTypes.choreoRecord:
return ChoreoRecord.fromJson(json) as V; return ChoreoRecord.fromJson(json) as V;
case PangeaEventTypes.pangeaActivityRes: case PangeaEventTypes.pangeaActivity:
return PracticeActivityModel.fromJson(json) as V; return PracticeActivityModel.fromJson(json) as V;
case PangeaEventTypes.activityRecord: case PangeaEventTypes.activityRecord:
return PracticeActivityRecordModel.fromJson(json) as V; return PracticeActivityRecordModel.fromJson(json) as V;

@ -566,10 +566,8 @@ class PangeaMessageEvent {
/// If any activity is not complete, it returns true, indicating that the activity icon should be shown. /// If any activity is not complete, it returns true, indicating that the activity icon should be shown.
/// Otherwise, it returns false. /// Otherwise, it returns false.
bool get hasUncompletedActivity { bool get hasUncompletedActivity {
if (l2Code == null) return false; if (practiceActivities.isEmpty) return false;
final List<PracticeActivityEvent> activities = practiceActivities(l2Code!); return practiceActivities.any((activity) => !(activity.isComplete));
if (activities.isEmpty) return false;
return activities.any((activity) => !(activity.isComplete));
} }
String? get l2Code => String? get l2Code =>
@ -603,34 +601,36 @@ class PangeaMessageEvent {
return steps; return steps;
} }
List<PracticeActivityEvent> get _practiceActivityEvents => _latestEdit /// Returns a list of all [PracticeActivityEvent] objects
.aggregatedEvents( /// associated with this message event.
timeline, List<PracticeActivityEvent> get _practiceActivityEvents {
PangeaEventTypes.pangeaActivityRes, return _latestEdit
) .aggregatedEvents(
.map( timeline,
(e) => PracticeActivityEvent( PangeaEventTypes.pangeaActivity,
timeline: timeline, )
event: e, .map(
), (e) => PracticeActivityEvent(
) timeline: timeline,
.toList(); event: e,
),
)
.toList();
}
/// Returns a boolean value indicating whether there are any
/// activities associated with this message event for the user's active l2
bool get hasActivities { bool get hasActivities {
try { try {
final String? l2code = return practiceActivities.isNotEmpty;
MatrixState.pangeaController.languageController.activeL2Code();
if (l2code == null) return false;
return practiceActivities(l2code).isNotEmpty;
} catch (e, s) { } catch (e, s) {
ErrorHandler.logError(e: e, s: s); ErrorHandler.logError(e: e, s: s);
return false; return false;
} }
} }
List<PracticeActivityEvent> practiceActivities( /// Returns a list of [PracticeActivityEvent] objects for the given [langCode].
List<PracticeActivityEvent> practiceActivitiesByLangCode(
String langCode, { String langCode, {
bool debug = false, bool debug = false,
}) { }) {
@ -650,6 +650,14 @@ class PangeaMessageEvent {
} }
} }
/// Returns a list of [PracticeActivityEvent] for the user's active l2.
List<PracticeActivityEvent> get practiceActivities {
final String? l2code =
MatrixState.pangeaController.languageController.activeL2Code();
if (l2code == null) return [];
return practiceActivitiesByLangCode(l2code);
}
// List<SpanData> get activities => // List<SpanData> get activities =>
//each match is turned into an activity that other students can access //each match is turned into an activity that other students can access
//they're not told the answer but have to find it themselves //they're not told the answer but have to find it themselves

@ -27,7 +27,7 @@ class PracticeActivityEvent {
_content = content; _content = content;
} }
} }
if (event.type != PangeaEventTypes.pangeaActivityRes) { if (event.type != PangeaEventTypes.pangeaActivity) {
throw Exception( throw Exception(
"${event.type} should not be used to make a PracticeActivityEvent", "${event.type} should not be used to make a PracticeActivityEvent",
); );
@ -39,7 +39,7 @@ class PracticeActivityEvent {
return _content!; return _content!;
} }
//in aggregatedEvents for the event, find all practiceActivityRecordEvents whose sender matches the client's userId /// All completion records assosiated with this activity
List<PracticeActivityRecordEvent> get allRecords { List<PracticeActivityRecordEvent> get allRecords {
if (timeline == null) { if (timeline == null) {
debugger(when: kDebugMode); debugger(when: kDebugMode);
@ -54,14 +54,24 @@ class PracticeActivityEvent {
.toList(); .toList();
} }
List<PracticeActivityRecordEvent> get userRecords => allRecords /// Completion record assosiated with this activity
.where( /// for the logged in user, null if there is none
(recordEvent) => PracticeActivityRecordEvent? get userRecord {
recordEvent.event.senderId == recordEvent.event.room.client.userID, final List<PracticeActivityRecordEvent> records = allRecords
) .where(
.toList(); (recordEvent) =>
recordEvent.event.senderId ==
recordEvent.event.room.client.userID,
)
.toList();
if (records.length > 1) {
debugPrint("There should only be one record per user per activity");
debugger(when: kDebugMode);
}
return records.firstOrNull;
}
/// Checks if there are any user records in the list for this activity, /// Checks if there is a user record for this activity,
/// and, if so, then the activity is complete /// and, if so, then the activity is complete
bool get isComplete => userRecords.isNotEmpty; bool get isComplete => userRecord != null;
} }

@ -302,7 +302,6 @@ class MessageToolbarState extends State<MessageToolbar> {
void showPracticeActivity() { void showPracticeActivity() {
toolbarContent = PracticeActivityCard( toolbarContent = PracticeActivityCard(
pangeaMessageEvent: widget.pangeaMessageEvent, pangeaMessageEvent: widget.pangeaMessageEvent,
controller: this,
); );
} }

@ -2,29 +2,89 @@ import 'package:collection/collection.dart';
import 'package:fluffychat/pangea/choreographer/widgets/choice_array.dart'; import 'package:fluffychat/pangea/choreographer/widgets/choice_array.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/practice_activity_event.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/practice_activity_event.dart';
import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart';
import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_record_model.dart';
import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class MultipleChoiceActivityView extends StatelessWidget { /// The multiple choice activity view
final PracticeActivityContentState controller; class MultipleChoiceActivity extends StatefulWidget {
final Function(int) updateChoice; final MessagePracticeActivityCardState controller;
final bool isActive; final PracticeActivityEvent? currentActivity;
const MultipleChoiceActivityView({ const MultipleChoiceActivity({
super.key, super.key,
required this.controller, required this.controller,
required this.updateChoice, required this.currentActivity,
required this.isActive,
}); });
PracticeActivityEvent get practiceEvent => controller.practiceEvent; @override
MultipleChoiceActivityState createState() => MultipleChoiceActivityState();
}
class MultipleChoiceActivityState extends State<MultipleChoiceActivity> {
int? selectedChoiceIndex;
PracticeActivityRecordModel? get currentRecordModel =>
widget.controller.currentRecordModel;
bool get isSubmitted =>
widget.currentActivity?.userRecord?.record?.latestResponse != null;
@override
void initState() {
super.initState();
setCompletionRecord();
}
int? get selectedChoiceIndex => controller.selectedChoiceIndex; @override
void didUpdateWidget(covariant MultipleChoiceActivity oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.currentActivity?.event.eventId !=
widget.currentActivity?.event.eventId) {
setCompletionRecord();
}
}
/// Sets the completion record for the multiple choice activity.
/// If the user record is null, it creates a new record model with the question
/// from the current activity and sets the selected choice index to null.
/// Otherwise, it sets the current model to the user record's record and
/// determines the selected choice index.
void setCompletionRecord() {
if (widget.currentActivity?.userRecord?.record == null) {
widget.controller.setCurrentModel(
PracticeActivityRecordModel(
question:
widget.currentActivity?.practiceActivity.multipleChoice!.question,
),
);
selectedChoiceIndex = null;
} else {
widget.controller
.setCurrentModel(widget.currentActivity!.userRecord!.record);
selectedChoiceIndex = widget
.currentActivity?.practiceActivity.multipleChoice!
.choiceIndex(currentRecordModel!.latestResponse!);
}
setState(() {});
}
void updateChoice(int index) {
currentRecordModel?.addResponse(
text: widget.controller.currentActivity?.practiceActivity.multipleChoice!
.choices[index],
);
setState(() => selectedChoiceIndex = index);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final PracticeActivityModel practiceActivity = final PracticeActivityModel? practiceActivity =
practiceEvent.practiceActivity; widget.currentActivity?.practiceActivity;
if (practiceActivity == null) {
return const SizedBox();
}
return Container( return Container(
padding: const EdgeInsets.all(8), padding: const EdgeInsets.all(8),
@ -55,7 +115,7 @@ class MultipleChoiceActivityView extends StatelessWidget {
), ),
) )
.toList(), .toList(),
isActive: isActive, isActive: !isSubmitted,
), ),
], ],
), ),

@ -1,20 +1,17 @@
import 'package:fluffychat/pangea/enum/activity_type_enum.dart'; import 'package:fluffychat/pangea/enum/activity_type_enum.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/practice_activity_event.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/practice_activity_event.dart';
import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_record_model.dart';
import 'package:fluffychat/pangea/widgets/practice_activity/multiple_choice_activity_view.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/multiple_choice_activity_view.dart';
import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
/// Practice activity content
class PracticeActivity extends StatefulWidget { class PracticeActivity extends StatefulWidget {
final PracticeActivityEvent practiceEvent; final PracticeActivityEvent practiceEvent;
final PangeaMessageEvent pangeaMessageEvent;
final MessagePracticeActivityCardState controller; final MessagePracticeActivityCardState controller;
const PracticeActivity({ const PracticeActivity({
super.key, super.key,
required this.practiceEvent, required this.practiceEvent,
required this.pangeaMessageEvent,
required this.controller, required this.controller,
}); });
@ -23,66 +20,12 @@ class PracticeActivity extends StatefulWidget {
} }
class PracticeActivityContentState extends State<PracticeActivity> { class PracticeActivityContentState extends State<PracticeActivity> {
PracticeActivityEvent get practiceEvent => widget.practiceEvent;
int? selectedChoiceIndex;
bool isSubmitted = false;
@override
void initState() {
super.initState();
setRecord();
}
@override
void didUpdateWidget(covariant PracticeActivity oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.practiceEvent.event.eventId !=
widget.practiceEvent.event.eventId) {
setRecord();
}
}
// sets the record model for the activity
// either a new record model that will be sent after submitting the
// activity or the record model from the user's previous response
void setRecord() {
if (widget.controller.recordEvent?.record == null) {
final String question =
practiceEvent.practiceActivity.multipleChoice!.question;
widget.controller.recordModel =
PracticeActivityRecordModel(question: question);
} else {
widget.controller.recordModel = widget.controller.recordEvent!.record;
// Note that only MultipleChoice activities will have this so we
// probably should move this logic to the MultipleChoiceActivity widget
selectedChoiceIndex =
widget.controller.recordModel?.latestResponse != null
? widget.practiceEvent.practiceActivity.multipleChoice
?.choiceIndex(widget.controller.recordModel!.latestResponse!)
: null;
isSubmitted = widget.controller.recordModel?.latestResponse != null;
}
setState(() {});
}
void updateChoice(int index) {
setState(() {
selectedChoiceIndex = index;
widget.controller.recordModel!.addResponse(
text: widget
.practiceEvent.practiceActivity.multipleChoice!.choices[index],
);
});
}
Widget get activityWidget { Widget get activityWidget {
switch (widget.practiceEvent.practiceActivity.activityType) { switch (widget.practiceEvent.practiceActivity.activityType) {
case ActivityTypeEnum.multipleChoice: case ActivityTypeEnum.multipleChoice:
return MultipleChoiceActivityView( return MultipleChoiceActivity(
controller: this, controller: widget.controller,
updateChoice: updateChoice, currentActivity: widget.practiceEvent,
isActive: !isSubmitted,
); );
default: default:
return const SizedBox.shrink(); return const SizedBox.shrink();
@ -91,9 +34,6 @@ class PracticeActivityContentState extends State<PracticeActivity> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
debugPrint(
"MessagePracticeActivityContentState.build with selectedChoiceIndex: $selectedChoiceIndex",
);
return Column( return Column(
children: [ children: [
activityWidget, activityWidget,

@ -1,29 +1,24 @@
import 'dart:developer';
import 'package:collection/collection.dart';
import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/app_config.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/practice_acitivity_record_event.dart';
import 'package:fluffychat/pangea/matrix_event_wrappers/practice_activity_event.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/practice_activity_event.dart';
import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_record_model.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_record_model.dart';
import 'package:fluffychat/pangea/utils/bot_style.dart'; import 'package:fluffychat/pangea/utils/bot_style.dart';
import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart';
import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart';
import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity.dart';
import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/matrix.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:matrix/matrix.dart'; import 'package:matrix/matrix.dart';
/// The wrapper for practice activity content.
/// Handles the activities assosiated with a message,
/// their navigation, and the management of completion records
class PracticeActivityCard extends StatefulWidget { class PracticeActivityCard extends StatefulWidget {
final PangeaMessageEvent pangeaMessageEvent; final PangeaMessageEvent pangeaMessageEvent;
final MessageToolbarState controller;
const PracticeActivityCard({ const PracticeActivityCard({
super.key, super.key,
required this.pangeaMessageEvent, required this.pangeaMessageEvent,
required this.controller,
}); });
@override @override
@ -32,13 +27,15 @@ class PracticeActivityCard extends StatefulWidget {
} }
class MessagePracticeActivityCardState extends State<PracticeActivityCard> { class MessagePracticeActivityCardState extends State<PracticeActivityCard> {
List<PracticeActivityEvent> practiceActivities = []; PracticeActivityEvent? currentActivity;
PracticeActivityEvent? practiceEvent; PracticeActivityRecordModel? currentRecordModel;
PracticeActivityRecordModel? recordModel;
bool sending = false; bool sending = false;
List<PracticeActivityEvent> get practiceActivities =>
widget.pangeaMessageEvent.practiceActivities;
int get practiceEventIndex => practiceActivities.indexWhere( int get practiceEventIndex => practiceActivities.indexWhere(
(activity) => activity.event.eventId == practiceEvent?.event.eventId, (activity) => activity.event.eventId == currentActivity?.event.eventId,
); );
bool get isPrevEnabled => bool get isPrevEnabled =>
@ -49,80 +46,59 @@ class MessagePracticeActivityCardState extends State<PracticeActivityCard> {
practiceEventIndex >= 0 && practiceEventIndex >= 0 &&
practiceEventIndex < practiceActivities.length - 1; practiceEventIndex < practiceActivities.length - 1;
// the first record for this practice activity
// assosiated with the current user
PracticeActivityRecordEvent? get recordEvent =>
practiceEvent?.userRecords.firstOrNull;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
setPracticeActivities(); setCurrentActivity();
}
String? get langCode {
final String? langCode = MatrixState.pangeaController.languageController
.activeL2Model()
?.langCode;
if (langCode == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(L10n.of(context)!.noLanguagesSet)),
);
debugger(when: kDebugMode);
return null;
}
return langCode;
} }
/// Initalizes the practice activities for the current language /// Initalizes the current activity.
/// and sets the first activity as the current activity /// If the current activity hasn't been set yet, show the first
void setPracticeActivities() { /// uncompleted activity if there is one.
if (langCode == null) return; /// If not, show the first activity
practiceActivities = void setCurrentActivity() {
widget.pangeaMessageEvent.practiceActivities(langCode!);
if (practiceActivities.isEmpty) return; if (practiceActivities.isEmpty) return;
practiceActivities.sort(
(a, b) => a.event.originServerTs.compareTo(b.event.originServerTs),
);
// if the current activity hasn't been set yet, show the first uncompleted activity
// if there is one. If not, show the first activity
final List<PracticeActivityEvent> incompleteActivities = final List<PracticeActivityEvent> incompleteActivities =
practiceActivities.where((element) => !element.isComplete).toList(); practiceActivities.where((element) => !element.isComplete).toList();
practiceEvent ??= incompleteActivities.isNotEmpty currentActivity ??= incompleteActivities.isNotEmpty
? incompleteActivities.first ? incompleteActivities.first
: practiceActivities.first; : practiceActivities.first;
setState(() {}); setState(() {});
} }
void navigateActivities({Direction? direction, int? index}) { void setCurrentModel(PracticeActivityRecordModel? recordModel) {
currentRecordModel = recordModel;
}
/// Sets the current acitivity based on the given [direction].
void navigateActivities(Direction direction) {
final bool enableNavigation = (direction == Direction.f && isNextEnabled) || final bool enableNavigation = (direction == Direction.f && isNextEnabled) ||
(direction == Direction.b && isPrevEnabled) || (direction == Direction.b && isPrevEnabled);
(index != null && index >= 0 && index < practiceActivities.length);
if (enableNavigation) { if (enableNavigation) {
final int newIndex = index ?? currentActivity = practiceActivities[direction == Direction.f
(direction == Direction.f ? practiceEventIndex + 1
? practiceEventIndex + 1 : practiceEventIndex - 1];
: practiceEventIndex - 1);
practiceEvent = practiceActivities[newIndex];
setState(() {}); setState(() {});
} }
} }
/// Sends the current record model and activity to the server.
/// If either the currentRecordModel or currentActivity is null, the method returns early.
/// Sets the [sending] flag to true before sending the record and activity.
/// Logs any errors that occur during the send operation.
/// Sets the [sending] flag to false when the send operation is complete.
void sendRecord() { void sendRecord() {
if (recordModel == null || practiceEvent == null) return; if (currentRecordModel == null || currentActivity == null) return;
setState(() => sending = true); setState(() => sending = true);
MatrixState.pangeaController.activityRecordController MatrixState.pangeaController.activityRecordController
.send(recordModel!, practiceEvent!) .send(currentRecordModel!, currentActivity!)
.catchError((error) { .catchError((error) {
ErrorHandler.logError( ErrorHandler.logError(
e: error, e: error,
s: StackTrace.current, s: StackTrace.current,
data: { data: {
'recordModel': recordModel?.toJson(), 'recordModel': currentRecordModel?.toJson(),
'practiceEvent': practiceEvent?.event.toJson(), 'practiceEvent': currentActivity?.event.toJson(),
}, },
); );
return null; return null;
@ -138,20 +114,20 @@ class MessagePracticeActivityCardState extends State<PracticeActivityCard> {
Opacity( Opacity(
opacity: isPrevEnabled ? 1.0 : 0, opacity: isPrevEnabled ? 1.0 : 0,
child: IconButton( child: IconButton(
onPressed: isPrevEnabled onPressed:
? () => navigateActivities(direction: Direction.b) isPrevEnabled ? () => navigateActivities(Direction.b) : null,
: null,
icon: const Icon(Icons.keyboard_arrow_left_outlined), icon: const Icon(Icons.keyboard_arrow_left_outlined),
tooltip: L10n.of(context)!.previous, tooltip: L10n.of(context)!.previous,
), ),
), ),
Expanded( Expanded(
child: Opacity( child: Opacity(
opacity: recordEvent == null ? 1.0 : 0.5, opacity: currentActivity?.userRecord == null ? 1.0 : 0.5,
child: sending child: sending
? const CircularProgressIndicator.adaptive() ? const CircularProgressIndicator.adaptive()
: TextButton( : TextButton(
onPressed: recordEvent == null ? sendRecord : null, onPressed:
currentActivity?.userRecord == null ? sendRecord : null,
style: ButtonStyle( style: ButtonStyle(
backgroundColor: WidgetStateProperty.all<Color>( backgroundColor: WidgetStateProperty.all<Color>(
AppConfig.primaryColor, AppConfig.primaryColor,
@ -164,9 +140,8 @@ class MessagePracticeActivityCardState extends State<PracticeActivityCard> {
Opacity( Opacity(
opacity: isNextEnabled ? 1.0 : 0, opacity: isNextEnabled ? 1.0 : 0,
child: IconButton( child: IconButton(
onPressed: isNextEnabled onPressed:
? () => navigateActivities(direction: Direction.f) isNextEnabled ? () => navigateActivities(Direction.f) : null,
: null,
icon: const Icon(Icons.keyboard_arrow_right_outlined), icon: const Icon(Icons.keyboard_arrow_right_outlined),
tooltip: L10n.of(context)!.next, tooltip: L10n.of(context)!.next,
), ),
@ -174,7 +149,7 @@ class MessagePracticeActivityCardState extends State<PracticeActivityCard> {
], ],
); );
if (practiceEvent == null || practiceActivities.isEmpty) { if (currentActivity == null || practiceActivities.isEmpty) {
return Text( return Text(
L10n.of(context)!.noActivitiesFound, L10n.of(context)!.noActivitiesFound,
style: BotStyle.text(context), style: BotStyle.text(context),
@ -187,8 +162,7 @@ class MessagePracticeActivityCardState extends State<PracticeActivityCard> {
return Column( return Column(
children: [ children: [
PracticeActivity( PracticeActivity(
pangeaMessageEvent: widget.pangeaMessageEvent, practiceEvent: currentActivity!,
practiceEvent: practiceEvent!,
controller: this, controller: this,
), ),
navigationButtons, navigationButtons,

Loading…
Cancel
Save