diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 056c6a347..647697673 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4058,5 +4058,6 @@ "suggestToSpaceDesc": "Suggested spaces will appear in the chat lists for their parent spaces", "practice": "Practice", "noLanguagesSet": "No languages set", - "noActivitiesFound": "No practice activities found for this message" + "noActivitiesFound": "No practice activities found for this message", + "previous": "Previous" } \ No newline at end of file diff --git a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart index 951b77dfc..306376df8 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart @@ -569,15 +569,7 @@ class PangeaMessageEvent { if (l2Code == null) return false; final List activities = practiceActivities(l2Code!); if (activities.isEmpty) return false; - - // for now, only show the button if the event has no completed activities - // TODO - revert this after adding logic to show next activity - for (final activity in activities) { - if (activity.isComplete) return false; - } - return true; - // if (activities.isEmpty) return false; - // return !activities.every((activity) => activity.isComplete); + return activities.any((activity) => !(activity.isComplete)); } String? get l2Code => diff --git a/lib/pangea/models/practice_activities.dart/multiple_choice_activity_model.dart b/lib/pangea/models/practice_activities.dart/multiple_choice_activity_model.dart index 3cd78a66a..68376d410 100644 --- a/lib/pangea/models/practice_activities.dart/multiple_choice_activity_model.dart +++ b/lib/pangea/models/practice_activities.dart/multiple_choice_activity_model.dart @@ -27,7 +27,7 @@ class MultipleChoice { return MultipleChoice( question: json['question'] as String, choices: (json['choices'] as List).map((e) => e as String).toList(), - answer: json['answer'] as String, + answer: json['answer'] ?? json['correct_answer'] as String, ); } diff --git a/lib/pangea/models/practice_activities.dart/practice_activity_model.dart b/lib/pangea/models/practice_activities.dart/practice_activity_model.dart index ae8455c7f..f5f4348c6 100644 --- a/lib/pangea/models/practice_activities.dart/practice_activity_model.dart +++ b/lib/pangea/models/practice_activities.dart/practice_activity_model.dart @@ -243,9 +243,11 @@ class PracticeActivityModel { .toList(), langCode: json['lang_code'] as String, msgId: json['msg_id'] as String, - activityType: ActivityTypeEnum.values.firstWhere( - (e) => e.string == json['activity_type'], - ), + activityType: json['activity_type'] == "multipleChoice" + ? ActivityTypeEnum.multipleChoice + : ActivityTypeEnum.values.firstWhere( + (e) => e.string == json['activity_type'], + ), multipleChoice: json['multiple_choice'] != null ? MultipleChoice.fromJson( json['multiple_choice'] as Map, diff --git a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart b/lib/pangea/widgets/practice_activity/multiple_choice_activity_view.dart similarity index 75% rename from lib/pangea/widgets/practice_activity/multiple_choice_activity.dart rename to lib/pangea/widgets/practice_activity/multiple_choice_activity_view.dart index c2861ffe0..100da3456 100644 --- a/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart +++ b/lib/pangea/widgets/practice_activity/multiple_choice_activity_view.dart @@ -2,26 +2,24 @@ import 'package:collection/collection.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/models/practice_activities.dart/practice_activity_model.dart'; -import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_content.dart'; +import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity.dart'; import 'package:flutter/material.dart'; -class MultipleChoiceActivity extends StatelessWidget { - final MessagePracticeActivityContentState card; +class MultipleChoiceActivityView extends StatelessWidget { + final PracticeActivityContentState controller; final Function(int) updateChoice; final bool isActive; - const MultipleChoiceActivity({ + const MultipleChoiceActivityView({ super.key, - required this.card, + required this.controller, required this.updateChoice, required this.isActive, }); - PracticeActivityEvent get practiceEvent => card.practiceEvent; + PracticeActivityEvent get practiceEvent => controller.practiceEvent; - int? get selectedChoiceIndex => card.selectedChoiceIndex; - - bool get submitted => card.recordSubmittedThisSession; + int? get selectedChoiceIndex => controller.selectedChoiceIndex; @override Widget build(BuildContext context) { @@ -50,10 +48,7 @@ class MultipleChoiceActivity extends StatelessWidget { .mapIndexed( (index, value) => Choice( text: value, - color: (selectedChoiceIndex == index || - practiceActivity.multipleChoice! - .isCorrect(index)) && - submitted + color: selectedChoiceIndex == index ? practiceActivity.multipleChoice!.choiceColor(index) : null, isGold: practiceActivity.multipleChoice!.isCorrect(index), diff --git a/lib/pangea/widgets/practice_activity/practice_activity.dart b/lib/pangea/widgets/practice_activity/practice_activity.dart new file mode 100644 index 000000000..5606aceff --- /dev/null +++ b/lib/pangea/widgets/practice_activity/practice_activity.dart @@ -0,0 +1,104 @@ +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/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/practice_activity_card.dart'; +import 'package:flutter/material.dart'; + +class PracticeActivity extends StatefulWidget { + final PracticeActivityEvent practiceEvent; + final PangeaMessageEvent pangeaMessageEvent; + final MessagePracticeActivityCardState controller; + + const PracticeActivity({ + super.key, + required this.practiceEvent, + required this.pangeaMessageEvent, + required this.controller, + }); + + @override + PracticeActivityContentState createState() => PracticeActivityContentState(); +} + +class PracticeActivityContentState extends State { + 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 { + switch (widget.practiceEvent.practiceActivity.activityType) { + case ActivityTypeEnum.multipleChoice: + return MultipleChoiceActivityView( + controller: this, + updateChoice: updateChoice, + isActive: !isSubmitted, + ); + default: + return const SizedBox.shrink(); + } + } + + @override + Widget build(BuildContext context) { + debugPrint( + "MessagePracticeActivityContentState.build with selectedChoiceIndex: $selectedChoiceIndex", + ); + return Column( + children: [ + activityWidget, + const SizedBox(height: 8), + ], + ); + } +} diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index 17c528b22..e28dabe9d 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -1,15 +1,20 @@ import 'dart:developer'; -import 'package:fluffychat/pangea/enum/message_mode_enum.dart'; +import 'package:collection/collection.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/practice_acitivity_record_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/utils/bot_style.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_content.dart'; +import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:matrix/matrix.dart'; class PracticeActivityCard extends StatefulWidget { final PangeaMessageEvent pangeaMessageEvent; @@ -27,12 +32,32 @@ class PracticeActivityCard extends StatefulWidget { } class MessagePracticeActivityCardState extends State { + List practiceActivities = []; PracticeActivityEvent? practiceEvent; + PracticeActivityRecordModel? recordModel; + bool sending = false; + + int get practiceEventIndex => practiceActivities.indexWhere( + (activity) => activity.event.eventId == practiceEvent?.event.eventId, + ); + + bool get isPrevEnabled => + practiceEventIndex > 0 && + practiceActivities.length > (practiceEventIndex - 1); + + bool get isNextEnabled => + practiceEventIndex >= 0 && + practiceEventIndex < practiceActivities.length - 1; + + // the first record for this practice activity + // assosiated with the current user + PracticeActivityRecordEvent? get recordEvent => + practiceEvent?.userRecords.firstOrNull; @override void initState() { super.initState(); - loadInitialData(); + setPracticeActivities(); } String? get langCode { @@ -50,46 +75,106 @@ class MessagePracticeActivityCardState extends State { return langCode; } - void loadInitialData() { + /// Initalizes the practice activities for the current language + /// and sets the first activity as the current activity + void setPracticeActivities() { if (langCode == null) return; - updatePracticeActivity(); - if (practiceEvent == null) { - debugger(when: kDebugMode); - } - } - - void updatePracticeActivity() { - if (langCode == null) return; - final List activities = + practiceActivities = widget.pangeaMessageEvent.practiceActivities(langCode!); - if (activities.isEmpty) return; - final List incompleteActivities = - activities.where((element) => !element.isComplete).toList(); - debugPrint("total events: ${activities.length}"); - debugPrint("incomplete practice events: ${incompleteActivities.length}"); + if (practiceActivities.isEmpty) return; - // TODO update to show next activity - practiceEvent = activities.first; - // // if an incomplete activity is found, show that - // if (incompleteActivities.isNotEmpty) { - // practiceEvent = incompleteActivities.first; - // } - // // if no incomplete activity is found, show the last activity - // else if (activities.isNotEmpty) { - // practiceEvent = activities.last; - // } + 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 incompleteActivities = + practiceActivities.where((element) => !element.isComplete).toList(); + practiceEvent ??= incompleteActivities.isNotEmpty + ? incompleteActivities.first + : practiceActivities.first; setState(() {}); } - void showNextActivity() { - if (langCode == null) return; - updatePracticeActivity(); - widget.controller.updateMode(MessageMode.practiceActivity); + void navigateActivities({Direction? direction, int? index}) { + final bool enableNavigation = (direction == Direction.f && isNextEnabled) || + (direction == Direction.b && isPrevEnabled) || + (index != null && index >= 0 && index < practiceActivities.length); + if (enableNavigation) { + final int newIndex = index ?? + (direction == Direction.f + ? practiceEventIndex + 1 + : practiceEventIndex - 1); + practiceEvent = practiceActivities[newIndex]; + setState(() {}); + } + } + + void sendRecord() { + if (recordModel == null || practiceEvent == null) return; + setState(() => sending = true); + MatrixState.pangeaController.activityRecordController + .send(recordModel!, practiceEvent!) + .catchError((error) { + ErrorHandler.logError( + e: error, + s: StackTrace.current, + data: { + 'recordModel': recordModel?.toJson(), + 'practiceEvent': practiceEvent?.event.toJson(), + }, + ); + return null; + }).whenComplete(() => setState(() => sending = false)); } @override Widget build(BuildContext context) { - if (practiceEvent == null) { + final Widget navigationButtons = Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Opacity( + opacity: isPrevEnabled ? 1.0 : 0, + child: IconButton( + onPressed: isPrevEnabled + ? () => navigateActivities(direction: Direction.b) + : null, + icon: const Icon(Icons.keyboard_arrow_left_outlined), + tooltip: L10n.of(context)!.previous, + ), + ), + Expanded( + child: Opacity( + opacity: recordEvent == null ? 1.0 : 0.5, + child: sending + ? const CircularProgressIndicator.adaptive() + : TextButton( + onPressed: recordEvent == null ? sendRecord : null, + style: ButtonStyle( + backgroundColor: WidgetStateProperty.all( + AppConfig.primaryColor, + ), + ), + child: Text(L10n.of(context)!.submit), + ), + ), + ), + Opacity( + opacity: isNextEnabled ? 1.0 : 0, + child: IconButton( + onPressed: isNextEnabled + ? () => navigateActivities(direction: Direction.f) + : null, + icon: const Icon(Icons.keyboard_arrow_right_outlined), + tooltip: L10n.of(context)!.next, + ), + ), + ], + ); + + if (practiceEvent == null || practiceActivities.isEmpty) { return Text( L10n.of(context)!.noActivitiesFound, style: BotStyle.text(context), @@ -99,10 +184,15 @@ class MessagePracticeActivityCardState extends State { // onActivityGenerated: updatePracticeActivity, // ); } - return PracticeActivityContent( - practiceEvent: practiceEvent!, - pangeaMessageEvent: widget.pangeaMessageEvent, - controller: this, + return Column( + children: [ + PracticeActivity( + pangeaMessageEvent: widget.pangeaMessageEvent, + practiceEvent: practiceEvent!, + controller: this, + ), + navigationButtons, + ], ); } } diff --git a/lib/pangea/widgets/practice_activity/practice_activity_content.dart b/lib/pangea/widgets/practice_activity/practice_activity_content.dart deleted file mode 100644 index 8080c27ee..000000000 --- a/lib/pangea/widgets/practice_activity/practice_activity_content.dart +++ /dev/null @@ -1,165 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:fluffychat/config/app_config.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_acitivity_record_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/utils/error_handler.dart'; -import 'package:fluffychat/pangea/widgets/practice_activity/multiple_choice_activity.dart'; -import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_card.dart'; -import 'package:fluffychat/widgets/matrix.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/l10n.dart'; - -class PracticeActivityContent extends StatefulWidget { - final PracticeActivityEvent practiceEvent; - final PangeaMessageEvent pangeaMessageEvent; - final MessagePracticeActivityCardState controller; - - const PracticeActivityContent({ - super.key, - required this.practiceEvent, - required this.pangeaMessageEvent, - required this.controller, - }); - - @override - MessagePracticeActivityContentState createState() => - MessagePracticeActivityContentState(); -} - -class MessagePracticeActivityContentState - extends State { - int? selectedChoiceIndex; - PracticeActivityRecordModel? recordModel; - bool recordSubmittedThisSession = false; - bool recordSubmittedPreviousSession = false; - - PracticeActivityEvent get practiceEvent => widget.practiceEvent; - - @override - void initState() { - super.initState(); - initalizeActivity(); - } - - @override - void didUpdateWidget(covariant PracticeActivityContent oldWidget) { - super.didUpdateWidget(oldWidget); - if (oldWidget.practiceEvent.event.eventId != - widget.practiceEvent.event.eventId) { - initalizeActivity(); - } - } - - void initalizeActivity() { - final PracticeActivityRecordEvent? recordEvent = - widget.practiceEvent.userRecords.firstOrNull; - if (recordEvent?.record == null) { - recordModel = PracticeActivityRecordModel( - question: - widget.practiceEvent.practiceActivity.multipleChoice!.question, - ); - } else { - recordModel = recordEvent!.record; - - //Note that only MultipleChoice activities will have this so we probably should move this logic to the MultipleChoiceActivity widget - selectedChoiceIndex = recordModel?.latestResponse != null - ? widget.practiceEvent.practiceActivity.multipleChoice - ?.choiceIndex(recordModel!.latestResponse!) - : null; - - recordSubmittedPreviousSession = true; - recordSubmittedThisSession = true; - } - setState(() {}); - } - - void updateChoice(int index) { - setState(() { - selectedChoiceIndex = index; - recordModel!.addResponse( - text: widget - .practiceEvent.practiceActivity.multipleChoice!.choices[index], - ); - }); - } - - Widget get activityWidget { - switch (widget.practiceEvent.practiceActivity.activityType) { - case ActivityTypeEnum.multipleChoice: - return MultipleChoiceActivity( - card: this, - updateChoice: updateChoice, - isActive: - !recordSubmittedPreviousSession && !recordSubmittedThisSession, - ); - default: - return const SizedBox.shrink(); - } - } - - void sendRecord() { - MatrixState.pangeaController.activityRecordController - .send( - recordModel!, - widget.practiceEvent, - ) - .catchError((error) { - ErrorHandler.logError( - e: error, - s: StackTrace.current, - data: { - 'recordModel': recordModel?.toJson(), - 'practiceEvent': widget.practiceEvent.event.toJson(), - }, - ); - return null; - }).then((_) => widget.controller.showNextActivity()); - - setState(() { - recordSubmittedThisSession = true; - }); - } - - @override - Widget build(BuildContext context) { - debugPrint( - "MessagePracticeActivityContentState.build with selectedChoiceIndex: $selectedChoiceIndex", - ); - return Column( - children: [ - activityWidget, - const SizedBox(height: 8), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Opacity( - opacity: selectedChoiceIndex != null && - !recordSubmittedThisSession && - !recordSubmittedPreviousSession - ? 1.0 - : 0.5, - child: TextButton( - onPressed: () { - if (recordSubmittedThisSession || - recordSubmittedPreviousSession) { - return; - } - selectedChoiceIndex != null ? sendRecord() : null; - }, - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all( - AppConfig.primaryColor, - ), - ), - child: Text(L10n.of(context)!.submit), - ), - ), - ], - ), - ], - ); - } -} diff --git a/needed-translations.txt b/needed-translations.txt index 1355bb368..ec54ac359 100644 --- a/needed-translations.txt +++ b/needed-translations.txt @@ -863,7 +863,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "be": [ @@ -2363,7 +2364,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "bn": [ @@ -3859,7 +3861,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "bo": [ @@ -5359,7 +5362,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "ca": [ @@ -6261,7 +6265,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "cs": [ @@ -7245,7 +7250,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "de": [ @@ -8112,7 +8118,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "el": [ @@ -9563,7 +9570,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "eo": [ @@ -10712,7 +10720,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "es": [ @@ -10727,7 +10736,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "et": [ @@ -11594,7 +11604,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "eu": [ @@ -12463,7 +12474,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "fa": [ @@ -13469,7 +13481,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "fi": [ @@ -14439,7 +14452,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "fil": [ @@ -15765,7 +15779,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "fr": [ @@ -16770,7 +16785,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "ga": [ @@ -17904,7 +17920,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "gl": [ @@ -18771,7 +18788,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "he": [ @@ -20024,7 +20042,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "hi": [ @@ -21517,7 +21536,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "hr": [ @@ -22463,7 +22483,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "hu": [ @@ -23346,7 +23367,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "ia": [ @@ -24832,7 +24854,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "id": [ @@ -25705,7 +25728,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "ie": [ @@ -26962,7 +26986,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "it": [ @@ -27886,7 +27911,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "ja": [ @@ -28921,7 +28947,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "ka": [ @@ -30275,7 +30302,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "ko": [ @@ -31144,7 +31172,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "lt": [ @@ -32179,7 +32208,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "lv": [ @@ -33054,7 +33084,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "nb": [ @@ -34253,7 +34284,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "nl": [ @@ -35216,7 +35248,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "pl": [ @@ -36188,7 +36221,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "pt": [ @@ -37666,7 +37700,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "pt_BR": [ @@ -38539,7 +38574,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "pt_PT": [ @@ -39739,7 +39775,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "ro": [ @@ -40746,7 +40783,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "ru": [ @@ -41619,7 +41657,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "sk": [ @@ -42885,7 +42924,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "sl": [ @@ -44281,7 +44321,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "sr": [ @@ -45451,7 +45492,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "sv": [ @@ -46355,7 +46397,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "ta": [ @@ -47852,7 +47895,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "th": [ @@ -49303,7 +49347,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "tr": [ @@ -50170,7 +50215,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "uk": [ @@ -51074,7 +51120,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "vi": [ @@ -52426,7 +52473,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "zh": [ @@ -53293,7 +53341,8 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ], "zh_Hant": [ @@ -54441,6 +54490,7 @@ "suggestToSpaceDesc", "practice", "noLanguagesSet", - "noActivitiesFound" + "noActivitiesFound", + "previous" ] }