From 1dcd988be0e2920330b81b240f88afe2ef743de5 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Thu, 6 Jun 2024 18:05:16 -0400 Subject: [PATCH 01/31] skeleton of practice activities --- assets/l10n/intl_en.arb | 3 +- lib/pangea/constants/pangea_event_types.dart | 3 + lib/pangea/enum/message_mode_enum.dart | 15 +- .../extensions/pangea_event_extension.dart | 2 + .../pangea_message_event.dart | 50 ++++ .../practice_activity_event.dart | 29 +++ .../multiple_choice_activity_model.dart | 62 +++++ .../practice_activity_model.dart | 223 ++++++++++++++++++ lib/pangea/widgets/chat/message_toolbar.dart | 13 + .../message_practice_activity_card.dart | 38 +++ .../multiple_choice_activity.dart | 81 +++++++ needed-translations.txt | 145 ++++++++---- 12 files changed, 615 insertions(+), 49 deletions(-) create mode 100644 lib/pangea/matrix_event_wrappers/practice_activity_event.dart create mode 100644 lib/pangea/models/practice_activities.dart/multiple_choice_activity_model.dart create mode 100644 lib/pangea/models/practice_activities.dart/practice_activity_model.dart create mode 100644 lib/pangea/widgets/practice_activity_card/message_practice_activity_card.dart create mode 100644 lib/pangea/widgets/practice_activity_card/multiple_choice_activity.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 1d5ff4218..0bb035da0 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -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" } \ No newline at end of file diff --git a/lib/pangea/constants/pangea_event_types.dart b/lib/pangea/constants/pangea_event_types.dart index cfdb7f0d7..df37724b3 100644 --- a/lib/pangea/constants/pangea_event_types.dart +++ b/lib/pangea/constants/pangea_event_types.dart @@ -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"; } diff --git a/lib/pangea/enum/message_mode_enum.dart b/lib/pangea/enum/message_mode_enum.dart index 25948d23b..f25140a9c 100644 --- a/lib/pangea/enum/message_mode_enum.dart +++ b/lib/pangea/enum/message_mode_enum.dart @@ -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: diff --git a/lib/pangea/extensions/pangea_event_extension.dart b/lib/pangea/extensions/pangea_event_extension.dart index f62f27925..dcc3a8bec 100644 --- a/lib/pangea/extensions/pangea_event_extension.dart +++ b/lib/pangea/extensions/pangea_event_extension.dart @@ -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"); } diff --git a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart index 5fa2e2659..c874dc93c 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart @@ -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 get _practiceActivityEvents => _latestEdit + .aggregatedEvents( + timeline, + PangeaEventTypes.activityResponse, + ) + .map( + (e) => PracticeActivityEvent( + event: e, + ), + ) + .toList(); + + List activities(String langCode) { + // final List practiceActivityEvents = _practiceActivityEvents; + + // final List 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 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 diff --git a/lib/pangea/matrix_event_wrappers/practice_activity_event.dart b/lib/pangea/matrix_event_wrappers/practice_activity_event.dart new file mode 100644 index 000000000..3e5fa5f16 --- /dev/null +++ b/lib/pangea/matrix_event_wrappers/practice_activity_event.dart @@ -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(); + return _content!; + } catch (err, s) { + ErrorHandler.logError(e: err, s: s); + return null; + } + } +} 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 new file mode 100644 index 000000000..a0007e754 --- /dev/null +++ b/lib/pangea/models/practice_activities.dart/multiple_choice_activity_model.dart @@ -0,0 +1,62 @@ +class MultipleChoice { + final String question; + final List 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 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 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 selectedOptions; + + MultipleChoiceActivityCompletionRecord({ + required this.question, + this.selectedOptions = const [], + }); + + factory MultipleChoiceActivityCompletionRecord.fromJson( + Map json, + ) { + return MultipleChoiceActivityCompletionRecord( + question: json['question'] as String, + selectedOptions: + (json['selected_options'] as List).map((e) => e as String).toList(), + ); + } + + Map toJson() { + return { + 'question': question, + 'selected_options': selectedOptions, + }; + } +} diff --git a/lib/pangea/models/practice_activities.dart/practice_activity_model.dart b/lib/pangea/models/practice_activities.dart/practice_activity_model.dart new file mode 100644 index 000000000..10aaefa87 --- /dev/null +++ b/lib/pangea/models/practice_activities.dart/practice_activity_model.dart @@ -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 json) { + return ConstructIdentifier( + lemma: json['lemma'] as String, + type: json['type'] as String, + ); + } + + Map 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 json) { + return MessageInfo( + msgId: json['msg_id'] as String, + roomId: json['room_id'] as String, + text: json['text'] as String, + ); + } + + Map toJson() { + return { + 'msg_id': msgId, + 'room_id': roomId, + 'text': text, + }; + } +} + +class ActivityRequest { + final String mode; + final List? targetConstructs; + final List? candidateMessages; + final List? 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 json) { + return ActivityRequest( + mode: json['mode'] as String, + targetConstructs: (json['target_constructs'] as List?) + ?.map((e) => ConstructIdentifier.fromJson(e as Map)) + .toList(), + candidateMessages: (json['candidate_msgs'] as List) + .map((e) => MessageInfo.fromJson(e as Map)) + .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 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 json) { + return FreeResponse( + question: json['question'] as String, + correctAnswer: json['correct_answer'] as String, + gradingGuide: json['grading_guide'] as String, + ); + } + + Map 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 json) { + return Listening( + audioUrl: json['audio_url'] as String, + text: json['text'] as String, + ); + } + + Map toJson() { + return { + 'audio_url': audioUrl, + 'text': text, + }; + } +} + +class Speaking { + final String text; + + Speaking({required this.text}); + + factory Speaking.fromJson(Map json) { + return Speaking( + text: json['text'] as String, + ); + } + + Map toJson() { + return { + 'text': text, + }; + } +} + +class PracticeActivityModel { + final List 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 json) { + return PracticeActivityModel( + tgtConstructs: (json['tgt_constructs'] as List) + .map((e) => ConstructIdentifier.fromJson(e as Map)) + .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, + ) + : null, + listening: json['listening'] != null + ? Listening.fromJson(json['listening'] as Map) + : null, + speaking: json['speaking'] != null + ? Speaking.fromJson(json['speaking'] as Map) + : null, + freeResponse: json['free_response'] != null + ? FreeResponse.fromJson(json['free_response'] as Map) + : null, + ); + } + + Map 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(), + }; + } +} diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 142a27227..523637b37 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -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 { 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 { ); } + 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() {} diff --git a/lib/pangea/widgets/practice_activity_card/message_practice_activity_card.dart b/lib/pangea/widgets/practice_activity_card/message_practice_activity_card.dart new file mode 100644 index 000000000..c5bb5dbfa --- /dev/null +++ b/lib/pangea/widgets/practice_activity_card/message_practice_activity_card.dart @@ -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 { + @override + Widget build(BuildContext context) { + switch (widget.practiceActivity.activityType) { + case ActivityType.multipleChoice: + return MultipleChoiceActivity( + practiceActivity: widget.practiceActivity, + ); + default: + return const SizedBox.shrink(); + } + } +} diff --git a/lib/pangea/widgets/practice_activity_card/multiple_choice_activity.dart b/lib/pangea/widgets/practice_activity_card/multiple_choice_activity.dart new file mode 100644 index 000000000..f8bec5436 --- /dev/null +++ b/lib/pangea/widgets/practice_activity_card/multiple_choice_activity.dart @@ -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 { + 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(), + ), + ], + ), + ); + } +} diff --git a/needed-translations.txt b/needed-translations.txt index bb967d011..6a6224756 100644 --- a/needed-translations.txt +++ b/needed-translations.txt @@ -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" ] } From 91d7600c5de9422fa64d8bb51a9f9a312ed549b4 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Wed, 12 Jun 2024 17:34:42 -0400 Subject: [PATCH 02/31] display, interactivity, saving/fetching of record, and dummy generation all done --- README.md | 2 +- assets/l10n/intl_en.arb | 3 +- lib/pages/chat/events/message.dart | 40 +++-- .../choreographer/widgets/choice_array.dart | 93 +++++------ lib/pangea/constants/pangea_event_types.dart | 3 +- lib/pangea/controllers/pangea_controller.dart | 7 + ...actice_activity_generation_controller.dart | 101 ++++++++++++ .../practice_activity_record_controller.dart | 94 ++++++++++++ .../extensions/pangea_event_extension.dart | 9 +- .../pangea_audio_events.dart | 9 -- .../pangea_choreo_event.dart | 4 +- .../pangea_message_event.dart | 77 +++++----- .../pangea_tokens_event.dart | 4 + .../practice_acitivity_record_event.dart | 24 +++ .../practice_activity_event.dart | 60 ++++++-- .../multiple_choice_activity_model.dart | 39 +---- .../practice_activity_model.dart | 72 +++++++-- .../practice_activity_record_model.dart | 127 +++++++++++++++ lib/pangea/widgets/chat/message_buttons.dart | 96 ++++++++++++ lib/pangea/widgets/chat/message_toolbar.dart | 8 +- .../generate_practice_activity.dart | 60 ++++++++ .../message_practice_activity_card.dart | 67 +++++--- .../message_practice_activity_content.dart | 141 +++++++++++++++++ .../multiple_choice_activity.dart | 66 ++++---- .../user_settings/p_language_dialog.dart | 1 - needed-translations.txt | 144 ++++++++++++------ .../fcm_shared_isolate/pubspec.lock | 56 +++++-- 27 files changed, 1115 insertions(+), 292 deletions(-) create mode 100644 lib/pangea/controllers/practice_activity_generation_controller.dart create mode 100644 lib/pangea/controllers/practice_activity_record_controller.dart delete mode 100644 lib/pangea/matrix_event_wrappers/pangea_audio_events.dart create mode 100644 lib/pangea/matrix_event_wrappers/practice_acitivity_record_event.dart create mode 100644 lib/pangea/models/practice_activities.dart/practice_activity_record_model.dart create mode 100644 lib/pangea/widgets/chat/message_buttons.dart create mode 100644 lib/pangea/widgets/practice_activity_card/generate_practice_activity.dart create mode 100644 lib/pangea/widgets/practice_activity_card/message_practice_activity_content.dart diff --git a/README.md b/README.md index 7c27b6e2e..a1ad9f2b7 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ # Special thanks -* Pangea Chat is a fork of [FluffyChat](https://fluffychat.im), is an open source, nonprofit and cute [[matrix](https://matrix.org)] client written in [Flutter](https://flutter.dev). The goal of FluffyChat is to create an easy to use instant messenger which is open source and accessible for everyone. You can [support the primary maker of FluffyChat directly here.](https://ko-fi.com/C1C86VN53) +* Pangea Chat is a fork of [FluffyChat](https://fluffychat.im) which is a [[matrix](https://matrix.org)] client written in [Flutter](https://flutter.dev). You can [support the primary maker of FluffyChat directly here.](https://ko-fi.com/C1C86VN53) * Fabiyamada is a graphics designer and has made the fluffychat logo and the banner. Big thanks for her great designs. diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 0bb035da0..bf9a112b6 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -3964,5 +3964,6 @@ "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", - "practice": "Practice" + "practice": "Practice", + "noLanguagesSet": "Please set your target language and try again." } \ No newline at end of file diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 472ef4eb4..c604d9c19 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -3,6 +3,7 @@ import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pangea/enum/use_type.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/language_model.dart'; +import 'package:fluffychat/pangea/widgets/chat/message_buttons.dart'; import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; import 'package:fluffychat/utils/date_time_extension.dart'; import 'package:fluffychat/utils/string_color.dart'; @@ -108,7 +109,7 @@ class Message extends StatelessWidget { final client = Matrix.of(context).client; final ownMessage = event.senderId == client.userID; final alignment = ownMessage ? Alignment.topRight : Alignment.topLeft; - var color = Theme.of(context).colorScheme.surfaceVariant; + var color = Theme.of(context).colorScheme.surfaceContainerHighest; final displayTime = event.type == EventTypes.RoomCreate || nextEvent == null || !event.originServerTs.sameEnvironment(nextEvent!.originServerTs); @@ -132,7 +133,7 @@ class Message extends StatelessWidget { final textColor = ownMessage ? Theme.of(context).colorScheme.onPrimary - : Theme.of(context).colorScheme.onBackground; + : Theme.of(context).colorScheme.onSurface; final rowMainAxisAlignment = ownMessage ? MainAxisAlignment.end : MainAxisAlignment.start; @@ -458,7 +459,14 @@ class Message extends StatelessWidget { Widget container; final showReceiptsRow = event.hasAggregatedEvents(timeline, RelationshipTypes.reaction); - if (showReceiptsRow || displayTime || selected || displayReadMarker) { + // #Pangea + // if (showReceiptsRow || displayTime || selected || displayReadMarker) { + if (showReceiptsRow || + displayTime || + selected || + displayReadMarker || + (pangeaMessageEvent?.showMessageButtons ?? false)) { + // Pangea# container = Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: @@ -472,11 +480,8 @@ class Message extends StatelessWidget { child: Center( child: Material( color: displayTime - ? Theme.of(context).colorScheme.background - : Theme.of(context) - .colorScheme - .background - .withOpacity(0.33), + ? Theme.of(context).colorScheme.surface + : Theme.of(context).colorScheme.surface.withOpacity(0.33), borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), clipBehavior: Clip.antiAlias, @@ -498,7 +503,11 @@ class Message extends StatelessWidget { AnimatedSize( duration: FluffyThemes.animationDuration, curve: FluffyThemes.animationCurve, - child: !showReceiptsRow + // #Pangea + child: !showReceiptsRow && + !(pangeaMessageEvent?.showMessageButtons ?? false) + // child: !showReceiptsRow + // Pangea# ? const SizedBox.shrink() : Padding( padding: EdgeInsets.only( @@ -506,7 +515,18 @@ class Message extends StatelessWidget { left: (ownMessage ? 0 : Avatar.defaultSize) + 12.0, right: ownMessage ? 0 : 12.0, ), - child: MessageReactions(event, timeline), + // #Pangea + child: Row( + mainAxisAlignment: ownMessage + ? MainAxisAlignment.end + : MainAxisAlignment.start, + children: [ + MessageButtons(toolbarController: toolbarController), + MessageReactions(event, timeline), + ], + ), + // child: MessageReactions(event, timeline), + // Pangea# ), ), if (displayReadMarker) diff --git a/lib/pangea/choreographer/widgets/choice_array.dart b/lib/pangea/choreographer/widgets/choice_array.dart index 54fd601b9..c26fd706d 100644 --- a/lib/pangea/choreographer/widgets/choice_array.dart +++ b/lib/pangea/choreographer/widgets/choice_array.dart @@ -3,9 +3,7 @@ import 'dart:math'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; - import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:matrix/matrix.dart'; import '../../utils/bot_style.dart'; import 'it_shimmer.dart'; @@ -18,6 +16,10 @@ class ChoicesArray extends StatelessWidget { final int? selectedChoiceIndex; final String originalSpan; final String Function(int) uniqueKeyForLayerLink; + + /// some uses of this widget want to disable the choices + final bool isActive; + const ChoicesArray({ super.key, required this.isLoading, @@ -26,6 +28,7 @@ class ChoicesArray extends StatelessWidget { required this.originalSpan, required this.uniqueKeyForLayerLink, required this.selectedChoiceIndex, + this.isActive = true, this.onLongPress, }); @@ -42,8 +45,8 @@ class ChoicesArray extends StatelessWidget { .map( (entry) => ChoiceItem( theme: theme, - onLongPress: onLongPress, - onPressed: onPressed, + onLongPress: isActive ? onLongPress : null, + onPressed: isActive ? onPressed : (_) {}, entry: entry, isSelected: selectedChoiceIndex == entry.key, ), @@ -109,19 +112,19 @@ class ChoiceItem extends StatelessWidget { : null, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all( + padding: WidgetStateProperty.all( const EdgeInsets.symmetric(horizontal: 7), ), //if index is selected, then give the background a slight primary color - backgroundColor: MaterialStateProperty.all( + backgroundColor: WidgetStateProperty.all( entry.value.color != null ? entry.value.color!.withOpacity(0.2) : theme.colorScheme.primary.withOpacity(0.1), ), - textStyle: MaterialStateProperty.all( + textStyle: WidgetStateProperty.all( BotStyle.text(context), ), - shape: MaterialStateProperty.all( + shape: WidgetStateProperty.all( RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), @@ -177,21 +180,21 @@ class ChoiceAnimationWidgetState extends State ); _animation = widget.isGold - ? Tween(begin: 1.0, end: 1.2).animate(_controller) - : TweenSequence([ - TweenSequenceItem( - tween: Tween(begin: 0, end: -8 * pi / 180), - weight: 1.0, - ), - TweenSequenceItem( - tween: Tween(begin: -8 * pi / 180, end: 16 * pi / 180), - weight: 2.0, - ), - TweenSequenceItem( - tween: Tween(begin: 16 * pi / 180, end: 0), - weight: 1.0, - ), - ]).animate(_controller); + ? Tween(begin: 1.0, end: 1.2).animate(_controller) + : TweenSequence([ + TweenSequenceItem( + tween: Tween(begin: 0, end: -8 * pi / 180), + weight: 1.0, + ), + TweenSequenceItem( + tween: Tween(begin: -8 * pi / 180, end: 16 * pi / 180), + weight: 2.0, + ), + TweenSequenceItem( + tween: Tween(begin: 16 * pi / 180, end: 0), + weight: 1.0, + ), + ]).animate(_controller); if (widget.selected && !animationPlayed) { _controller.forward(); @@ -221,28 +224,28 @@ class ChoiceAnimationWidgetState extends State @override Widget build(BuildContext context) { return widget.isGold - ? AnimatedBuilder( - key: UniqueKey(), - animation: _animation, - builder: (context, child) { - return Transform.scale( - scale: _animation.value, - child: child, - ); - }, - child: widget.child, - ) - : AnimatedBuilder( - key: UniqueKey(), - animation: _animation, - builder: (context, child) { - return Transform.rotate( - angle: _animation.value, - child: child, - ); - }, - child: widget.child, - ); + ? AnimatedBuilder( + key: UniqueKey(), + animation: _animation, + builder: (context, child) { + return Transform.scale( + scale: _animation.value, + child: child, + ); + }, + child: widget.child, + ) + : AnimatedBuilder( + key: UniqueKey(), + animation: _animation, + builder: (context, child) { + return Transform.rotate( + angle: _animation.value, + child: child, + ); + }, + child: widget.child, + ); } @override diff --git a/lib/pangea/constants/pangea_event_types.dart b/lib/pangea/constants/pangea_event_types.dart index df37724b3..344bd3122 100644 --- a/lib/pangea/constants/pangea_event_types.dart +++ b/lib/pangea/constants/pangea_event_types.dart @@ -24,6 +24,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 pangeaActivityRes = "pangea.activity_res"; static const acitivtyRequest = "pangea.activity_req"; + static const activityRecord = "pangea.activity_completion"; } diff --git a/lib/pangea/controllers/pangea_controller.dart b/lib/pangea/controllers/pangea_controller.dart index ad2a27145..b0f65505f 100644 --- a/lib/pangea/controllers/pangea_controller.dart +++ b/lib/pangea/controllers/pangea_controller.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:developer'; import 'dart:math'; @@ -12,6 +13,8 @@ import 'package:fluffychat/pangea/controllers/local_settings.dart'; import 'package:fluffychat/pangea/controllers/message_data_controller.dart'; import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart'; import 'package:fluffychat/pangea/controllers/permissions_controller.dart'; +import 'package:fluffychat/pangea/controllers/practice_activity_generation_controller.dart'; +import 'package:fluffychat/pangea/controllers/practice_activity_record_controller.dart'; import 'package:fluffychat/pangea/controllers/speech_to_text_controller.dart'; import 'package:fluffychat/pangea/controllers/subscription_controller.dart'; import 'package:fluffychat/pangea/controllers/text_to_speech_controller.dart'; @@ -53,6 +56,8 @@ class PangeaController { late TextToSpeechController textToSpeech; late SpeechToTextController speechToText; late LanguageDetectionController languageDetection; + late PracticeActivityRecordController activityRecordController; + late PracticeGenerationController practiceGenerationController; ///store Services late PLocalStore pStoreService; @@ -101,6 +106,8 @@ class PangeaController { textToSpeech = TextToSpeechController(this); speechToText = SpeechToTextController(this); languageDetection = LanguageDetectionController(this); + activityRecordController = PracticeActivityRecordController(this); + practiceGenerationController = PracticeGenerationController(); PAuthGaurd.pController = this; } diff --git a/lib/pangea/controllers/practice_activity_generation_controller.dart b/lib/pangea/controllers/practice_activity_generation_controller.dart new file mode 100644 index 000000000..403e22d4f --- /dev/null +++ b/lib/pangea/controllers/practice_activity_generation_controller.dart @@ -0,0 +1,101 @@ +import 'dart:async'; + +import 'package:fluffychat/pangea/constants/pangea_event_types.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_message_event.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/practice_activity_event.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:matrix/matrix.dart'; + +/// Represents an item in the completion cache. +class _RequestCacheItem { + PracticeActivityRequest req; + + Future practiceActivityEvent; + + _RequestCacheItem({ + required this.req, + required this.practiceActivityEvent, + }); +} + +/// Controller for handling activity completions. +class PracticeGenerationController { + static final Map _cache = {}; + Timer? _cacheClearTimer; + + PracticeGenerationController() { + _initializeCacheClearing(); + } + + void _initializeCacheClearing() { + const duration = Duration(minutes: 2); + _cacheClearTimer = Timer.periodic(duration, (Timer t) => _clearCache()); + } + + void _clearCache() { + _cache.clear(); + } + + void dispose() { + _cacheClearTimer?.cancel(); + } + + Future _sendAndPackageEvent( + PracticeActivityModel model, + PangeaMessageEvent pangeaMessageEvent, + ) async { + final Event? activityEvent = await pangeaMessageEvent.room.sendPangeaEvent( + content: model.toJson(), + parentEventId: pangeaMessageEvent.eventId, + type: PangeaEventTypes.pangeaActivityRes, + ); + + if (activityEvent == null) { + return null; + } + + return PracticeActivityEvent( + event: activityEvent, + timeline: pangeaMessageEvent.timeline, + ); + } + + Future getPracticeActivity( + PracticeActivityRequest req, + PangeaMessageEvent event, + ) async { + final int cacheKey = req.hashCode; + + if (_cache.containsKey(cacheKey)) { + return _cache[cacheKey]!.practiceActivityEvent; + } else { + //TODO - send request to server/bot, either via API or via event of type pangeaActivityReq + // for now, just make and send the event from the client + final Future eventFuture = + _sendAndPackageEvent(dummyModel(event), event); + + _cache[cacheKey] = + _RequestCacheItem(req: req, practiceActivityEvent: eventFuture); + + return _cache[cacheKey]!.practiceActivityEvent; + } + } + + PracticeActivityModel dummyModel(PangeaMessageEvent event) => + PracticeActivityModel( + tgtConstructs: [ + ConstructIdentifier(lemma: "be", type: ConstructType.vocab.string), + ], + activityType: ActivityType.multipleChoice, + langCode: event.messageDisplayLangCode, + msgId: event.eventId, + multipleChoice: MultipleChoice( + question: "What is a synonym for 'happy'?", + choices: ["sad", "angry", "joyful", "tired"], + correctAnswer: "joyful", + ), + ); +} diff --git a/lib/pangea/controllers/practice_activity_record_controller.dart b/lib/pangea/controllers/practice_activity_record_controller.dart new file mode 100644 index 000000000..b075fa553 --- /dev/null +++ b/lib/pangea/controllers/practice_activity_record_controller.dart @@ -0,0 +1,94 @@ +import 'dart:async'; +import 'dart:developer'; + +import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; +import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; +import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.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:flutter/foundation.dart'; +import 'package:matrix/matrix.dart'; + +/// Represents an item in the completion cache. +class _RecordCacheItem { + PracticeActivityRecordModel data; + + Future recordEvent; + + _RecordCacheItem({required this.data, required this.recordEvent}); +} + +/// Controller for handling activity completions. +class PracticeActivityRecordController { + static final Map _cache = {}; + late final PangeaController _pangeaController; + Timer? _cacheClearTimer; + + PracticeActivityRecordController(this._pangeaController) { + _initializeCacheClearing(); + } + + void _initializeCacheClearing() { + const duration = Duration(minutes: 2); + _cacheClearTimer = Timer.periodic(duration, (Timer t) => _clearCache()); + } + + void _clearCache() { + _cache.clear(); + } + + void dispose() { + _cacheClearTimer?.cancel(); + } + + /// Sends a practice activity record to the server and returns the corresponding event. + /// + /// The [recordModel] parameter is the model representing the practice activity record. + /// The [practiceActivityEvent] parameter is the event associated with the practice activity. + /// Note that the system will send a new event if the model has changed in any way ie it is + /// a new completion of the practice activity. However, it will cache previous sends to ensure + /// that opening and closing of the widget does not result in multiple sends of the same data. + /// It allows checks the data to make sure that it contains responses to the practice activity + /// and does not represent a blank record with no actual completion to be saved. + /// + /// Returns a [Future] that completes with the corresponding [Event] object. + Future send( + PracticeActivityRecordModel recordModel, + PracticeActivityEvent practiceActivityEvent, + ) async { + final int cacheKey = recordModel.hashCode; + + if (recordModel.responses.isEmpty) { + return null; + } + + if (_cache.containsKey(cacheKey)) { + return _cache[cacheKey]!.recordEvent; + } else { + final Future eventFuture = practiceActivityEvent.event.room + .sendPangeaEvent( + content: recordModel.toJson(), + parentEventId: practiceActivityEvent.event.eventId, + type: PangeaEventTypes.activityRecord, + ) + .catchError((e) { + debugger(when: kDebugMode); + ErrorHandler.logError( + e: e, + s: StackTrace.current, + data: { + 'recordModel': recordModel.toJson(), + 'practiceActivityEvent': practiceActivityEvent.event.toJson(), + }, + ); + return null; + }); + + _cache[cacheKey] = + _RecordCacheItem(data: recordModel, recordEvent: eventFuture); + + return _cache[cacheKey]!.recordEvent; + } + } +} diff --git a/lib/pangea/extensions/pangea_event_extension.dart b/lib/pangea/extensions/pangea_event_extension.dart index dcc3a8bec..17e14ed86 100644 --- a/lib/pangea/extensions/pangea_event_extension.dart +++ b/lib/pangea/extensions/pangea_event_extension.dart @@ -2,6 +2,8 @@ import 'dart:developer'; import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/models/choreo_record.dart'; +import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; +import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_record_model.dart'; import 'package:fluffychat/pangea/models/representation_content_model.dart'; import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:flutter/foundation.dart'; @@ -26,9 +28,12 @@ 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; + case PangeaEventTypes.pangeaActivityRes: + return PracticeActivityModel.fromJson(json) as V; + case PangeaEventTypes.activityRecord: + return PracticeActivityRecordModel.fromJson(json) as V; default: + debugger(when: kDebugMode); throw Exception("$type events do not have pangea content"); } } diff --git a/lib/pangea/matrix_event_wrappers/pangea_audio_events.dart b/lib/pangea/matrix_event_wrappers/pangea_audio_events.dart deleted file mode 100644 index 3583d021e..000000000 --- a/lib/pangea/matrix_event_wrappers/pangea_audio_events.dart +++ /dev/null @@ -1,9 +0,0 @@ -// relates to a pangea representation event -// the matrix even fits the form of a regular matrix audio event -// but with something to distinguish it as a pangea audio event - -import 'package:matrix/matrix.dart'; - -class PangeaAudioEvent { - Event? _event; -} diff --git a/lib/pangea/matrix_event_wrappers/pangea_choreo_event.dart b/lib/pangea/matrix_event_wrappers/pangea_choreo_event.dart index 47a6b688f..a6a79fd4f 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_choreo_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_choreo_event.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:fluffychat/pangea/extensions/pangea_event_extension.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/foundation.dart'; @@ -23,7 +25,7 @@ class ChoreoEvent { _content ??= event.getPangeaContent(); return _content; } catch (err, s) { - if (kDebugMode) rethrow; + debugger(when: kDebugMode); ErrorHandler.logError(e: err, s: s); return null; } diff --git a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart index c874dc93c..66c81bb47 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart @@ -4,15 +4,12 @@ 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'; @@ -576,12 +573,35 @@ class PangeaMessageEvent { _event.messageType != PangeaEventTypes.report && _event.messageType == MessageTypes.Text; + // this is just showActivityIcon now but will include + // logic for showing + bool get showMessageButtons => showActivityIcon; + + /// Returns a boolean value indicating whether to show an activity icon for this message event. + /// + /// The [showActivityIcon] getter checks if the [l2Code] is null, and if so, returns false. + /// Otherwise, it retrieves a list of [PracticeActivityEvent] objects using the [practiceActivities] function + /// with the [l2Code] as an argument. + /// If the list is empty, it returns false. + /// Otherwise, it checks if every activity in the list is complete using the [isComplete] property. + /// If any activity is not complete, it returns true, indicating that the activity icon should be shown. + /// Otherwise, it returns false. + bool get showActivityIcon { + if (l2Code == null) return false; + final List activities = practiceActivities(l2Code!); + + if (activities.isEmpty) return false; + + return !activities.every((activity) => activity.isComplete); + } + + String? get l2Code => MatrixState.pangeaController.languageController + .activeL2Code(roomID: room.id); + String get messageDisplayLangCode { final bool immersionMode = MatrixState .pangeaController.permissionsController .isToolEnabled(ToolSetting.immersionMode, room); - final String? l2Code = MatrixState.pangeaController.languageController - .activeL2Code(roomID: room.id); final String? originalLangCode = (originalWritten ?? originalSent)?.langCode; @@ -608,47 +628,34 @@ class PangeaMessageEvent { List get _practiceActivityEvents => _latestEdit .aggregatedEvents( timeline, - PangeaEventTypes.activityResponse, + PangeaEventTypes.pangeaActivityRes, ) .map( (e) => PracticeActivityEvent( + timeline: timeline, event: e, ), ) .toList(); - List activities(String langCode) { - // final List practiceActivityEvents = _practiceActivityEvents; + bool get hasActivities { + try { + final String? l2code = MatrixState.pangeaController.languageController + .activeL2Code(roomID: room.id); - // final List activities = _practiceActivityEvents - // .map( - // (e) => PracticeActivityModel.fromJson( - // e.event.content, - // ), - // ) - // .where( - // (element) => element.langCode == langCode, - // ) - // .toList(); + if (l2code == null) return false; - // return activities; + return practiceActivities(l2code).isNotEmpty; + } catch (e, s) { + ErrorHandler.logError(e: e, s: s); + return false; + } + } - // 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 practiceActivities(String langCode) { + return _practiceActivityEvents + .where((ev) => ev.practiceActivity.langCode == langCode) + .toList(); } // List get activities => diff --git a/lib/pangea/matrix_event_wrappers/pangea_tokens_event.dart b/lib/pangea/matrix_event_wrappers/pangea_tokens_event.dart index 0c138c637..f617b8dae 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_tokens_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_tokens_event.dart @@ -1,6 +1,9 @@ +import 'dart:developer'; + import 'package:fluffychat/pangea/extensions/pangea_event_extension.dart'; import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:flutter/foundation.dart'; import 'package:matrix/matrix.dart'; import '../constants/pangea_event_types.dart'; @@ -22,6 +25,7 @@ class TokensEvent { _content ??= event.getPangeaContent(); return _content!; } catch (err, s) { + debugger(when: kDebugMode); ErrorHandler.logError(e: err, s: s); return null; } diff --git a/lib/pangea/matrix_event_wrappers/practice_acitivity_record_event.dart b/lib/pangea/matrix_event_wrappers/practice_acitivity_record_event.dart new file mode 100644 index 000000000..d4b9cde23 --- /dev/null +++ b/lib/pangea/matrix_event_wrappers/practice_acitivity_record_event.dart @@ -0,0 +1,24 @@ +import 'package:fluffychat/pangea/extensions/pangea_event_extension.dart'; +import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_record_model.dart'; +import 'package:matrix/matrix.dart'; + +import '../constants/pangea_event_types.dart'; + +class PracticeActivityRecordEvent { + Event event; + + PracticeActivityRecordModel? _content; + + PracticeActivityRecordEvent({required this.event}) { + if (event.type != PangeaEventTypes.activityRecord) { + throw Exception( + "${event.type} should not be used to make a PracticeActivityRecordEvent", + ); + } + } + + PracticeActivityRecordModel? get record { + _content ??= event.getPangeaContent(); + return _content!; + } +} diff --git a/lib/pangea/matrix_event_wrappers/practice_activity_event.dart b/lib/pangea/matrix_event_wrappers/practice_activity_event.dart index 3e5fa5f16..c5f35be91 100644 --- a/lib/pangea/matrix_event_wrappers/practice_activity_event.dart +++ b/lib/pangea/matrix_event_wrappers/practice_activity_event.dart @@ -1,29 +1,67 @@ +import 'dart:developer'; + import 'package:fluffychat/pangea/extensions/pangea_event_extension.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/practice_acitivity_record_event.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/practice_activity_model.dart'; -import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:flutter/foundation.dart'; import 'package:matrix/matrix.dart'; import '../constants/pangea_event_types.dart'; class PracticeActivityEvent { Event event; + Timeline? timeline; PracticeActivityModel? _content; - PracticeActivityEvent({required this.event}) { - if (event.type != PangeaEventTypes.activityResponse) { + PracticeActivityEvent({ + required this.event, + required this.timeline, + content, + }) { + if (content != null) { + if (!kDebugMode) { + throw Exception( + "content should not be set on product, just a dev placeholder", + ); + } else { + _content = content; + } + } + if (event.type != PangeaEventTypes.pangeaActivityRes) { throw Exception( "${event.type} should not be used to make a PracticeActivityEvent", ); } } - PracticeActivityModel? get practiceActivity { - try { - _content ??= event.getPangeaContent(); - return _content!; - } catch (err, s) { - ErrorHandler.logError(e: err, s: s); - return null; - } + PracticeActivityModel get practiceActivity { + _content ??= event.getPangeaContent(); + return _content!; } + + //in aggregatedEvents for the event, find all practiceActivityRecordEvents whose sender matches the client's userId + List get allRecords { + if (timeline == null) { + debugger(when: kDebugMode); + return []; + } + final List records = event + .aggregatedEvents(timeline!, PangeaEventTypes.activityRecord) + .toList(); + + return records + .map((event) => PracticeActivityRecordEvent(event: event)) + .toList(); + } + + List get userRecords => allRecords + .where( + (recordEvent) => + recordEvent.event.senderId == recordEvent.event.room.client.userID, + ) + .toList(); + + /// Checks if there are any user records in the list for this activity, + /// and, if so, then the activity is complete + bool get isComplete => userRecords.isNotEmpty; } 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 a0007e754..0cd6aac05 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 @@ -1,3 +1,6 @@ +import 'package:fluffychat/config/app_config.dart'; +import 'package:flutter/material.dart'; + class MultipleChoice { final String question; final List choices; @@ -9,10 +12,15 @@ class MultipleChoice { required this.correctAnswer, }); + bool isCorrect(int index) => index == correctAnswerIndex; + bool get isValidQuestion => choices.contains(correctAnswer); int get correctAnswerIndex => choices.indexOf(correctAnswer); + Color choiceColor(int index) => + index == correctAnswerIndex ? AppConfig.success : AppConfig.warning; + factory MultipleChoice.fromJson(Map json) { return MultipleChoice( question: json['question'] as String, @@ -29,34 +37,3 @@ class MultipleChoice { }; } } - -// 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 selectedOptions; - - MultipleChoiceActivityCompletionRecord({ - required this.question, - this.selectedOptions = const [], - }); - - factory MultipleChoiceActivityCompletionRecord.fromJson( - Map json, - ) { - return MultipleChoiceActivityCompletionRecord( - question: json['question'] as String, - selectedOptions: - (json['selected_options'] as List).map((e) => e as String).toList(), - ); - } - - Map toJson() { - return { - 'question': question, - 'selected_options': selectedOptions, - }; - } -} 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 10aaefa87..ebfd68f37 100644 --- a/lib/pangea/models/practice_activities.dart/practice_activity_model.dart +++ b/lib/pangea/models/practice_activities.dart/practice_activity_model.dart @@ -23,15 +23,16 @@ class ConstructIdentifier { enum ActivityType { multipleChoice, freeResponse, listening, speaking } -class MessageInfo { +class CandidateMessage { final String msgId; final String roomId; final String text; - MessageInfo({required this.msgId, required this.roomId, required this.text}); + CandidateMessage( + {required this.msgId, required this.roomId, required this.text}); - factory MessageInfo.fromJson(Map json) { - return MessageInfo( + factory CandidateMessage.fromJson(Map json) { + return CandidateMessage( msgId: json['msg_id'] as String, roomId: json['room_id'] as String, text: json['text'] as String, @@ -47,31 +48,46 @@ class MessageInfo { } } -class ActivityRequest { - final String mode; +enum PracticeActivityMode { focus, srs } + +extension on PracticeActivityMode { + String get value { + switch (this) { + case PracticeActivityMode.focus: + return 'focus'; + case PracticeActivityMode.srs: + return 'srs'; + } + } +} + +class PracticeActivityRequest { + final PracticeActivityMode? mode; final List? targetConstructs; - final List? candidateMessages; + final List? candidateMessages; final List? userIds; final ActivityType? activityType; - final int numActivities; + final int? numActivities; - ActivityRequest({ - required this.mode, + PracticeActivityRequest({ + this.mode, this.targetConstructs, this.candidateMessages, this.userIds, this.activityType, - this.numActivities = 10, + this.numActivities, }); - factory ActivityRequest.fromJson(Map json) { - return ActivityRequest( - mode: json['mode'] as String, + factory PracticeActivityRequest.fromJson(Map json) { + return PracticeActivityRequest( + mode: PracticeActivityMode.values.firstWhere( + (e) => e.value == json['mode'], + ), targetConstructs: (json['target_constructs'] as List?) ?.map((e) => ConstructIdentifier.fromJson(e as Map)) .toList(), candidateMessages: (json['candidate_msgs'] as List) - .map((e) => MessageInfo.fromJson(e as Map)) + .map((e) => CandidateMessage.fromJson(e as Map)) .toList(), userIds: (json['user_ids'] as List?)?.map((e) => e as String).toList(), activityType: ActivityType.values.firstWhere( @@ -83,7 +99,7 @@ class ActivityRequest { Map toJson() { return { - 'mode': mode, + 'mode': mode?.value, 'target_constructs': targetConstructs?.map((e) => e.toJson()).toList(), 'candidate_msgs': candidateMessages?.map((e) => e.toJson()).toList(), 'user_ids': userIds, @@ -91,6 +107,30 @@ class ActivityRequest { 'num_activities': numActivities, }; } + + // override operator == and hashCode + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is PracticeActivityRequest && + other.mode == mode && + other.targetConstructs == targetConstructs && + other.candidateMessages == candidateMessages && + other.userIds == userIds && + other.activityType == activityType && + other.numActivities == numActivities; + } + + @override + int get hashCode { + return mode.hashCode ^ + targetConstructs.hashCode ^ + candidateMessages.hashCode ^ + userIds.hashCode ^ + activityType.hashCode ^ + numActivities.hashCode; + } } class FreeResponse { diff --git a/lib/pangea/models/practice_activities.dart/practice_activity_record_model.dart b/lib/pangea/models/practice_activities.dart/practice_activity_record_model.dart new file mode 100644 index 000000000..7aa7f6b88 --- /dev/null +++ b/lib/pangea/models/practice_activities.dart/practice_activity_record_model.dart @@ -0,0 +1,127 @@ +// 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 +import 'dart:developer'; +import 'dart:typed_data'; + +class PracticeActivityRecordModel { + final String? question; + late List responses; + + PracticeActivityRecordModel({ + required this.question, + List? responses, + }) { + if (responses == null) { + this.responses = List.empty(growable: true); + } else { + this.responses = responses; + } + } + + factory PracticeActivityRecordModel.fromJson( + Map json, + ) { + return PracticeActivityRecordModel( + question: json['question'] as String, + responses: (json['responses'] as List) + .map((e) => ActivityResponse.fromJson(e as Map)) + .toList(), + ); + } + + Map toJson() { + return { + 'question': question, + 'responses': responses.map((e) => e.toJson()).toList(), + }; + } + + void addResponse({ + String? text, + Uint8List? audioBytes, + Uint8List? imageBytes, + }) { + try { + responses.add( + ActivityResponse( + text: text, + audioBytes: audioBytes, + imageBytes: imageBytes, + timestamp: DateTime.now(), + ), + ); + } catch (e) { + debugger(); + } + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is PracticeActivityRecordModel && + other.question == question && + other.responses.length == responses.length && + List.generate( + responses.length, + (index) => responses[index] == other.responses[index], + ).every((element) => element); + } + + @override + int get hashCode => question.hashCode ^ responses.hashCode; +} + +class ActivityResponse { + // the user's response + // has nullable string, nullable audio bytes, nullable image bytes, and timestamp + final String? text; + final Uint8List? audioBytes; + final Uint8List? imageBytes; + final DateTime timestamp; + + ActivityResponse({ + this.text, + this.audioBytes, + this.imageBytes, + required this.timestamp, + }); + + factory ActivityResponse.fromJson(Map json) { + return ActivityResponse( + text: json['text'] as String?, + audioBytes: json['audio'] as Uint8List?, + imageBytes: json['image'] as Uint8List?, + timestamp: DateTime.parse(json['timestamp'] as String), + ); + } + + Map toJson() { + return { + 'text': text, + 'audio': audioBytes, + 'image': imageBytes, + 'timestamp': timestamp.toIso8601String(), + }; + } + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is ActivityResponse && + other.text == text && + other.audioBytes == audioBytes && + other.imageBytes == imageBytes && + other.timestamp == timestamp; + } + + @override + int get hashCode => + text.hashCode ^ + audioBytes.hashCode ^ + imageBytes.hashCode ^ + timestamp.hashCode; +} diff --git a/lib/pangea/widgets/chat/message_buttons.dart b/lib/pangea/widgets/chat/message_buttons.dart new file mode 100644 index 000000000..f7748675f --- /dev/null +++ b/lib/pangea/widgets/chat/message_buttons.dart @@ -0,0 +1,96 @@ +import 'package:fluffychat/pangea/enum/message_mode_enum.dart'; +import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; +import 'package:flutter/material.dart'; + +class MessageButtons extends StatelessWidget { + final ToolbarDisplayController? toolbarController; + + const MessageButtons({ + super.key, + this.toolbarController, + }); + + void showActivity(BuildContext context) { + toolbarController?.showToolbar( + context, + mode: MessageMode.practiceActivity, + ); + } + + @override + Widget build(BuildContext context) { + if (toolbarController == null) { + return const SizedBox.shrink(); + } + return Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Row( + children: [ + HoverIconButton( + icon: MessageMode.practiceActivity.icon, + onTap: () => showActivity(context), + primaryColor: Theme.of(context).colorScheme.primary, + tooltip: MessageMode.practiceActivity.tooltip(context), + ), + + // Additional buttons can be added here in the future + ], + ), + ); + } +} + +class HoverIconButton extends StatefulWidget { + final IconData icon; + final VoidCallback onTap; + final Color primaryColor; + final String tooltip; + + const HoverIconButton({ + super.key, + required this.icon, + required this.onTap, + required this.primaryColor, + required this.tooltip, + }); + + @override + _HoverIconButtonState createState() => _HoverIconButtonState(); +} + +class _HoverIconButtonState extends State { + bool _isHovered = false; + + @override + Widget build(BuildContext context) { + return Tooltip( + message: widget.tooltip, + child: InkWell( + onTap: widget.onTap, + onHover: (hovering) { + setState(() => _isHovered = hovering); + }, + borderRadius: BorderRadius.circular(100), + child: Container( + decoration: BoxDecoration( + color: _isHovered ? widget.primaryColor : null, + borderRadius: BorderRadius.circular(100), + border: Border.all( + width: 1, + color: widget.primaryColor, + ), + ), + padding: const EdgeInsets.all(2), + child: Icon( + widget.icon, + size: 18, + // when hovered, use themeData to get background color, otherwise use primary + color: _isHovered + ? Theme.of(context).scaffoldBackgroundColor + : widget.primaryColor, + ), + ), + ), + ); + } +} diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 523637b37..ff7b73b72 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -277,12 +277,8 @@ class MessageToolbarState extends State { } void showPracticeActivity() { - toolbarContent = PracticeActivityCard( - practiceActivity: widget.pangeaMessageEvent - // @ggurdin - is this the best way to get the l2 language here? - .activities(widget.pangeaMessageEvent.messageDisplayLangCode) - .first, - ); + toolbarContent = + PracticeActivityCard(pangeaMessageEvent: widget.pangeaMessageEvent); } void showImage() {} diff --git a/lib/pangea/widgets/practice_activity_card/generate_practice_activity.dart b/lib/pangea/widgets/practice_activity_card/generate_practice_activity.dart new file mode 100644 index 000000000..02d7e90cd --- /dev/null +++ b/lib/pangea/widgets/practice_activity_card/generate_practice_activity.dart @@ -0,0 +1,60 @@ +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_model.dart'; +import 'package:fluffychat/widgets/matrix.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +class GeneratePracticeActivityButton extends StatelessWidget { + final PangeaMessageEvent pangeaMessageEvent; + final Function(PracticeActivityEvent?) onActivityGenerated; + + const GeneratePracticeActivityButton({ + super.key, + required this.pangeaMessageEvent, + required this.onActivityGenerated, + }); + + @override + Widget build(BuildContext context) { + return ElevatedButton( + onPressed: () async { + final String? l2Code = MatrixState.pangeaController.languageController + .activeL1Model(roomID: pangeaMessageEvent.room.id) + ?.langCode; + + if (l2Code == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(L10n.of(context)!.noLanguagesSet), + ), + ); + return; + } + + final PracticeActivityEvent? practiceActivityEvent = await MatrixState + .pangeaController.practiceGenerationController + .getPracticeActivity( + PracticeActivityRequest( + candidateMessages: [ + CandidateMessage( + msgId: pangeaMessageEvent.eventId, + roomId: pangeaMessageEvent.room.id, + text: + pangeaMessageEvent.representationByLanguage(l2Code)?.text ?? + pangeaMessageEvent.body, + ), + ], + userIds: pangeaMessageEvent.room.client.userID != null + ? [pangeaMessageEvent.room.client.userID!] + : null, + ), + pangeaMessageEvent, + ); + + onActivityGenerated(practiceActivityEvent); + }, + child: Text(L10n.of(context)!.practice), + ); + } +} diff --git a/lib/pangea/widgets/practice_activity_card/message_practice_activity_card.dart b/lib/pangea/widgets/practice_activity_card/message_practice_activity_card.dart index c5bb5dbfa..d69627fcb 100644 --- a/lib/pangea/widgets/practice_activity_card/message_practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity_card/message_practice_activity_card.dart @@ -1,15 +1,17 @@ -//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:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/practice_activity_event.dart'; +import 'package:fluffychat/pangea/widgets/practice_activity_card/generate_practice_activity.dart'; +import 'package:fluffychat/pangea/widgets/practice_activity_card/message_practice_activity_content.dart'; +import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; class PracticeActivityCard extends StatefulWidget { - final PracticeActivityModel practiceActivity; + final PangeaMessageEvent pangeaMessageEvent; const PracticeActivityCard({ super.key, - required this.practiceActivity, + required this.pangeaMessageEvent, }); @override @@ -17,22 +19,49 @@ class PracticeActivityCard extends StatefulWidget { 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 { + PracticeActivityEvent? practiceEvent; + + @override + void initState() { + super.initState(); + loadInitialData(); + } + + void loadInitialData() { + final String? langCode = MatrixState.pangeaController.languageController + .activeL2Model(roomID: widget.pangeaMessageEvent.room.id) + ?.langCode; + + if (langCode == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(L10n.of(context)!.noLanguagesSet)), + ); + return; + } + + practiceEvent = + widget.pangeaMessageEvent.practiceActivities(langCode).firstOrNull; + setState(() {}); + } + + void updatePracticeActivity(PracticeActivityEvent? newEvent) { + setState(() { + practiceEvent = newEvent; + }); + } + @override Widget build(BuildContext context) { - switch (widget.practiceActivity.activityType) { - case ActivityType.multipleChoice: - return MultipleChoiceActivity( - practiceActivity: widget.practiceActivity, - ); - default: - return const SizedBox.shrink(); + if (practiceEvent == null) { + return GeneratePracticeActivityButton( + pangeaMessageEvent: widget.pangeaMessageEvent, + onActivityGenerated: updatePracticeActivity, + ); } + return PracticeActivityContent( + practiceEvent: practiceEvent!, + pangeaMessageEvent: widget.pangeaMessageEvent, + ); } } diff --git a/lib/pangea/widgets/practice_activity_card/message_practice_activity_content.dart b/lib/pangea/widgets/practice_activity_card/message_practice_activity_content.dart new file mode 100644 index 000000000..fc0119012 --- /dev/null +++ b/lib/pangea/widgets/practice_activity_card/message_practice_activity_content.dart @@ -0,0 +1,141 @@ +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_model.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_card/multiple_choice_activity.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; + + const PracticeActivityContent({ + super.key, + required this.practiceEvent, + required this.pangeaMessageEvent, + }); + + @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(); + final PracticeActivityRecordEvent? recordEvent = + widget.practiceEvent.userRecords.firstOrNull; + if (recordEvent?.record == null) { + recordModel = PracticeActivityRecordModel( + question: + widget.practiceEvent.practiceActivity.multipleChoice!.question, + ); + } else { + recordModel = recordEvent!.record; + recordSubmittedPreviousSession = true; + recordSubmittedThisSession = true; + } + } + + 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 ActivityType.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; + }); + + 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/lib/pangea/widgets/practice_activity_card/multiple_choice_activity.dart b/lib/pangea/widgets/practice_activity_card/multiple_choice_activity.dart index f8bec5436..f74dc03ce 100644 --- a/lib/pangea/widgets/practice_activity_card/multiple_choice_activity.dart +++ b/lib/pangea/widgets/practice_activity_card/multiple_choice_activity.dart @@ -1,49 +1,39 @@ -// 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/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_card/message_practice_activity_content.dart'; import 'package:flutter/material.dart'; -class MultipleChoiceActivity extends StatefulWidget { - final PracticeActivityModel practiceActivity; +class MultipleChoiceActivity extends StatelessWidget { + final MessagePracticeActivityContentState card; + final Function(int) updateChoice; + final bool isActive; const MultipleChoiceActivity({ super.key, - required this.practiceActivity, + required this.card, + required this.updateChoice, + required this.isActive, }); - @override - MultipleChoiceActivityState createState() => MultipleChoiceActivityState(); -} + PracticeActivityEvent get practiceEvent => card.practiceEvent; -//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 { - int? selectedChoiceIndex; + int? get selectedChoiceIndex => card.selectedChoiceIndex; - late MultipleChoiceActivityCompletionRecord? completionRecord; - - @override - initState() { - super.initState(); - selectedChoiceIndex = null; - completionRecord = MultipleChoiceActivityCompletionRecord( - question: widget.practiceActivity.multipleChoice!.question, - ); - } + bool get submitted => card.recordSubmittedThisSession; @override Widget build(BuildContext context) { + final PracticeActivityModel practiceActivity = + practiceEvent.practiceActivity; + return Container( padding: const EdgeInsets.all(8), child: Column( children: [ Text( - widget.practiceActivity.multipleChoice!.question, + practiceActivity.multipleChoice!.question, style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, @@ -53,26 +43,24 @@ class MultipleChoiceActivityState extends State { 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", + onPressed: updateChoice, selectedChoiceIndex: selectedChoiceIndex, - choices: widget.practiceActivity.multipleChoice!.choices + choices: practiceActivity.multipleChoice!.choices .mapIndexed( - (int index, String value) => Choice( + (index, value) => Choice( text: value, - color: null, - isGold: - widget.practiceActivity.multipleChoice!.correctAnswer == - value, + color: (selectedChoiceIndex == index || + practiceActivity.multipleChoice! + .isCorrect(index)) && + submitted + ? practiceActivity.multipleChoice!.choiceColor(index) + : null, + isGold: practiceActivity.multipleChoice!.isCorrect(index), ), ) .toList(), + isActive: isActive, ), ], ), diff --git a/lib/pangea/widgets/user_settings/p_language_dialog.dart b/lib/pangea/widgets/user_settings/p_language_dialog.dart index 4d09b506e..51082a7e6 100644 --- a/lib/pangea/widgets/user_settings/p_language_dialog.dart +++ b/lib/pangea/widgets/user_settings/p_language_dialog.dart @@ -98,7 +98,6 @@ pLanguageDialog(BuildContext parentContext, Function callback) async { Navigator.pop(context); } catch (err, s) { debugger(when: kDebugMode); - //PTODO-Lala add standard error message ErrorHandler.logError(e: err, s: s); rethrow; } finally { diff --git a/needed-translations.txt b/needed-translations.txt index 6a6224756..50198bb38 100644 --- a/needed-translations.txt +++ b/needed-translations.txt @@ -840,7 +840,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "be": [ @@ -2279,7 +2280,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "bn": [ @@ -3180,7 +3182,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "bo": [ @@ -4081,7 +4084,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "ca": [ @@ -4982,7 +4986,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "cs": [ @@ -5883,7 +5888,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "de": [ @@ -6731,7 +6737,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "el": [ @@ -7632,7 +7639,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "eo": [ @@ -8533,11 +8541,13 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "es": [ - "practice" + "practice", + "noLanguagesSet" ], "et": [ @@ -9381,7 +9391,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "eu": [ @@ -10225,7 +10236,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "fa": [ @@ -11126,7 +11138,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "fi": [ @@ -12027,7 +12040,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "fr": [ @@ -12928,7 +12942,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "ga": [ @@ -13829,7 +13844,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "gl": [ @@ -14673,7 +14689,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "he": [ @@ -15574,7 +15591,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "hi": [ @@ -16475,7 +16493,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "hr": [ @@ -17363,7 +17382,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "hu": [ @@ -18264,7 +18284,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "ia": [ @@ -19689,7 +19710,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "id": [ @@ -20590,7 +20612,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "ie": [ @@ -21491,7 +21514,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "it": [ @@ -22377,7 +22401,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "ja": [ @@ -23278,7 +23303,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "ko": [ @@ -24179,7 +24205,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "lt": [ @@ -25080,7 +25107,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "lv": [ @@ -25981,7 +26009,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "nb": [ @@ -26882,7 +26911,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "nl": [ @@ -27783,7 +27813,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "pl": [ @@ -28684,7 +28715,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "pt": [ @@ -29585,7 +29617,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "pt_BR": [ @@ -30455,7 +30488,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "pt_PT": [ @@ -31356,7 +31390,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "ro": [ @@ -32257,7 +32292,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "ru": [ @@ -33101,7 +33137,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "sk": [ @@ -34002,7 +34039,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "sl": [ @@ -34903,7 +34941,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "sr": [ @@ -35804,7 +35843,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "sv": [ @@ -36670,7 +36710,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "ta": [ @@ -37571,7 +37612,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "th": [ @@ -38472,7 +38514,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "tr": [ @@ -39358,7 +39401,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "uk": [ @@ -40202,7 +40246,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "vi": [ @@ -41103,7 +41148,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "zh": [ @@ -41947,7 +41993,8 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ], "zh_Hant": [ @@ -42848,6 +42895,7 @@ "roomDataMissing", "updatePhoneOS", "wordsPerMinute", - "practice" + "practice", + "noLanguagesSet" ] } diff --git a/pangea_packages/fcm_shared_isolate/pubspec.lock b/pangea_packages/fcm_shared_isolate/pubspec.lock index e18c1dba1..4d8bacbed 100644 --- a/pangea_packages/fcm_shared_isolate/pubspec.lock +++ b/pangea_packages/fcm_shared_isolate/pubspec.lock @@ -128,38 +128,62 @@ packages: url: "https://pub.dev" source: hosted version: "0.6.7" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.dev" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.dev" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.dev" + source: hosted + version: "3.0.1" matcher: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.dev" source: hosted - version: "0.5.0" + version: "0.8.0" meta: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.12.0" path: dependency: transitive description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted - version: "1.8.3" + version: "1.9.0" pedantic: dependency: "direct dev" description: @@ -225,10 +249,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" vector_math: dependency: transitive description: @@ -237,14 +261,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" - web: + vm_service: dependency: transitive description: - name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "0.3.0" + version: "14.2.1" sdks: - dart: ">=3.2.0-194.0.dev <4.0.0" - flutter: ">=1.20.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.18.0-18.0.pre.54" From 5d5b35b4eb93aac536b4f7591f42e0749a107680 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Thu, 13 Jun 2024 12:43:29 -0400 Subject: [PATCH 03/31] switch to enum for construct type --- .../practice_activity_generation_controller.dart | 2 +- .../practice_activity_model.dart | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/pangea/controllers/practice_activity_generation_controller.dart b/lib/pangea/controllers/practice_activity_generation_controller.dart index 403e22d4f..fdd6da4fa 100644 --- a/lib/pangea/controllers/practice_activity_generation_controller.dart +++ b/lib/pangea/controllers/practice_activity_generation_controller.dart @@ -87,7 +87,7 @@ class PracticeGenerationController { PracticeActivityModel dummyModel(PangeaMessageEvent event) => PracticeActivityModel( tgtConstructs: [ - ConstructIdentifier(lemma: "be", type: ConstructType.vocab.string), + ConstructIdentifier(lemma: "be", type: ConstructType.vocab), ], activityType: ActivityType.multipleChoice, langCode: event.messageDisplayLangCode, 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 ebfd68f37..909406430 100644 --- a/lib/pangea/models/practice_activities.dart/practice_activity_model.dart +++ b/lib/pangea/models/practice_activities.dart/practice_activity_model.dart @@ -1,22 +1,25 @@ +import 'package:fluffychat/pangea/enum/construct_type_enum.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/multiple_choice_activity_model.dart'; class ConstructIdentifier { final String lemma; - final String type; + final ConstructType type; ConstructIdentifier({required this.lemma, required this.type}); factory ConstructIdentifier.fromJson(Map json) { return ConstructIdentifier( lemma: json['lemma'] as String, - type: json['type'] as String, + type: ConstructType.values.firstWhere( + (e) => e.string == json['type'], + ), ); } Map toJson() { return { 'lemma': lemma, - 'type': type, + 'type': type.string, }; } } @@ -28,8 +31,11 @@ class CandidateMessage { final String roomId; final String text; - CandidateMessage( - {required this.msgId, required this.roomId, required this.text}); + CandidateMessage({ + required this.msgId, + required this.roomId, + required this.text, + }); factory CandidateMessage.fromJson(Map json) { return CandidateMessage( From a2fb240c6e1900ba63ed46d92946cc3812a1baef Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 19 Jun 2024 12:18:18 -0400 Subject: [PATCH 04/31] inital work to combine class and exchanges into generalized spaces --- assets/l10n/intl_en.arb | 116 +- assets/l10n/intl_es.arb | 143 +- lib/config/routes.dart | 33 +- lib/pages/chat/chat.dart | 8 +- lib/pages/chat_details/chat_details_view.dart | 25 +- lib/pages/chat_list/chat_list.dart | 9 +- .../chat_list/client_chooser_button.dart | 28 +- lib/pages/new_group/new_group.dart | 2 +- lib/pages/new_group/new_group_view.dart | 2 - lib/pages/new_space/new_space.dart | 79 +- lib/pages/new_space/new_space_view.dart | 64 +- .../controllers/choreographer.dart | 16 +- .../controllers/it_controller.dart | 2 - .../language_permissions_warning_buttons.dart | 2 +- lib/pangea/constants/model_keys.dart | 4 - lib/pangea/constants/pangea_event_types.dart | 3 +- lib/pangea/constants/pangea_room_types.dart | 1 - lib/pangea/controllers/class_controller.dart | 24 +- .../controllers/language_controller.dart | 78 +- lib/pangea/controllers/local_settings.dart | 2 +- .../message_analytics_controller.dart | 105 +- .../controllers/message_data_controller.dart | 14 +- .../controllers/my_analytics_controller.dart | 12 +- .../controllers/permissions_controller.dart | 25 +- .../space_rules_edit_controller.dart | 2 +- .../classes_and_exchanges_extension.dart | 83 - .../client_analytics_extension.dart | 2 +- .../client_extension/client_extension.dart | 22 +- .../general_info_extension.dart | 4 +- .../client_extension/space_extension.dart | 64 + .../children_and_parents_extension.dart | 25 +- .../pangea_room_extension.dart | 29 +- .../room_analytics_extension.dart | 12 +- .../room_information_extension.dart | 7 - .../room_settings_extension.dart | 3 +- ...ion.dart => space_settings_extension.dart} | 91 +- .../user_permissions_extension.dart | 43 +- .../pangea_message_event.dart | 6 +- .../models/analytics/constructs_model.dart | 5 +- .../custom_input_translation_model.dart | 4 - lib/pangea/models/exchange_model.dart | 40 - lib/pangea/models/language_model.dart | 4 +- .../{class_model.dart => space_model.dart} | 46 +- lib/pangea/models/user_model.dart | 2 +- .../analytics/analytics_language_button.dart | 38 + .../pages/analytics/analytics_list_tile.dart | 119 +- .../pages/analytics/base_analytics.dart | 7 + .../pages/analytics/base_analytics_view.dart | 10 + .../space_analytics.dart} | 51 +- .../space_analytics_view.dart} | 22 +- .../space_list.dart} | 16 +- .../space_list_view.dart} | 10 +- .../analytics/time_span_menu_button.dart | 1 - .../class_analytics_button.dart | 35 - .../p_class_widgets/room_rules_editor.dart | 2 +- .../pages/exchange/add_exchange_to_class.dart | 59 - .../settings_learning_view.dart | 2 +- lib/pangea/repo/class_analytics_repo.dart | 76 - lib/pangea/repo/exchange_repo.dart | 34 - .../utils/chat_list_handle_space_tap.dart | 2 +- lib/pangea/utils/download_chat.dart | 11 +- lib/pangea/utils/firebase_analytics.dart | 28 - .../utils/get_chat_list_item_subtitle.dart | 6 +- lib/pangea/utils/report_message.dart | 2 +- .../{class_code.dart => space_code.dart} | 16 +- .../chat/message_speech_to_text_card.dart | 8 +- .../chat/message_translation_card.dart | 8 +- .../widgets/class/add_class_and_invite.dart | 298 --- .../widgets/class/add_space_toggles.dart | 32 +- .../class/invite_students_from_class.dart | 342 --- .../conversation_bot_settings.dart | 5 - lib/pangea/widgets/igc/pangea_rich_text.dart | 9 +- lib/pangea/widgets/igc/word_data_card.dart | 3 +- lib/pangea/widgets/space/class_settings.dart | 183 -- .../widgets/space/language_settings.dart | 184 ++ lib/utils/client_manager.dart | 2 +- lib/widgets/chat_settings_popup_menu.dart | 91 +- needed-translations.txt | 2073 +++++++---------- 78 files changed, 1826 insertions(+), 3250 deletions(-) delete mode 100644 lib/pangea/extensions/client_extension/classes_and_exchanges_extension.dart create mode 100644 lib/pangea/extensions/client_extension/space_extension.dart rename lib/pangea/extensions/pangea_room_extension/{class_and_exchange_settings_extension.dart => space_settings_extension.dart} (66%) delete mode 100644 lib/pangea/models/exchange_model.dart rename lib/pangea/models/{class_model.dart => space_model.dart} (88%) create mode 100644 lib/pangea/pages/analytics/analytics_language_button.dart rename lib/pangea/pages/analytics/{class_analytics/class_analytics.dart => space_analytics/space_analytics.dart} (70%) rename lib/pangea/pages/analytics/{class_analytics/class_analytics_view.dart => space_analytics/space_analytics_view.dart} (72%) rename lib/pangea/pages/analytics/{class_list/class_list.dart => space_list/space_list.dart} (79%) rename lib/pangea/pages/analytics/{class_list/class_list_view.dart => space_list/space_list_view.dart} (90%) delete mode 100644 lib/pangea/pages/class_settings/p_class_widgets/class_analytics_button.dart delete mode 100644 lib/pangea/pages/exchange/add_exchange_to_class.dart delete mode 100644 lib/pangea/repo/class_analytics_repo.dart delete mode 100644 lib/pangea/repo/exchange_repo.dart rename lib/pangea/utils/{class_code.dart => space_code.dart} (81%) delete mode 100644 lib/pangea/widgets/class/add_class_and_invite.dart delete mode 100644 lib/pangea/widgets/class/invite_students_from_class.dart delete mode 100644 lib/pangea/widgets/space/class_settings.dart create mode 100644 lib/pangea/widgets/space/language_settings.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index b17ad9f16..4201c4827 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -410,7 +410,6 @@ "type": "text", "placeholders": {} }, - "classes": "Classes", "chooseAStrongPassword": "Choose a strong password", "@chooseAStrongPassword": { "type": "text", @@ -616,16 +615,11 @@ } }, "createGroup": "Create group", - "createNewSpace": "Create an exchange space", "createNewGroup": "Create a new chat", "@createNewGroup": { "type": "text", "placeholders": {} }, - "@createNewSpace": { - "type": "text", - "placeholders": {} - }, "currentlyActive": "Currently active", "@currentlyActive": { "type": "text", @@ -2401,7 +2395,7 @@ "@noKeyForThisMessage": {}, "newGroup": "New chat", "@newGroup": {}, - "newSpace": "New class", + "newSpace": "New space", "@newSpace": {}, "enterSpace": "Enter space", "@enterSpace": {}, @@ -2540,7 +2534,7 @@ "type": "text", "placeholders": {} }, - "interactiveTranslatorNotAllowedDesc": "Translation assistance is disabled in space group chats for all participants. This restriction does not apply to Class/Exchange Admin or direct chats.", + "interactiveTranslatorNotAllowedDesc": "Translation assistance is disabled in space group chats for all participants. This restriction does not apply to space admins or direct chats.", "@interactiveTranslatorNotAllowedDesc": { "type": "text", "placeholders": {} @@ -2550,7 +2544,7 @@ "type": "text", "placeholders": {} }, - "interactiveTranslatorRequiredDesc": "Students cannot turn off translation assistance. They can choose not to accept the translation suggestions. This restriction does not apply to Class/Exchange or direct chats.", + "interactiveTranslatorRequiredDesc": "Students cannot turn off translation assistance. They can choose not to accept the translation suggestions. This restriction does not apply to spaces or direct chats.", "@interactiveTranslatorRequiredDesc": { "type": "text", "placeholders": {} @@ -2560,12 +2554,7 @@ "type": "text", "placeholders": {} }, - "multiLingualClass": "Multilingual Class", - "classAnalytics": "Class Analytics", - "@classAnalytics": { - "type": "text", - "placeholders": {} - }, + "multiLingualSpace": "Multilingual Space", "allClasses": "All Classes", "@allClasses": { "type": "text", @@ -2601,18 +2590,8 @@ "type": "text", "placeholders": {} }, - "requestAnExchange": "Request an Exchange", - "@requestAnExchange": { - "type": "text", - "placeholders": {} - }, - "findLanguageExchange": "Find a class exchange partner", - "@findLanguageExchange": { - "type": "text", - "placeholders": {} - }, - "classAnalyticsDesc": "Detailed information on student engagement and language use", - "@classAnalyticsDesc": { + "spaceAnalyticsDesc": "Detailed information on student engagement and language use", + "@spaceAnalyticsDesc": { "type": "text", "placeholders": {} }, @@ -2629,28 +2608,28 @@ "type": "text", "placeholders": {} }, - "classSettings": "Class Settings", - "@classSettings": { + "languageSettings": "Language Settings", + "@languageSettings": { "type": "text", "placeholders": {} }, - "classSettingsDesc": "Edit class languages and proficiency level.", - "@classSettingsDesc": { + "languageSettingsDesc": "Edit space languages and proficiency level.", + "@languageSettingsDesc": { "type": "text", "placeholders": {} }, - "selectClassRoomDominantLanguage": "What is the base language of your class?", - "@selectClassRoomDominantLanguage": { + "selectSpaceDominantLanguage": "What's the most common language of space members?", + "@selectSpaceDominantLanguage": { "type": "text", "placeholders": {} }, - "selectTargetLanguage": "What language are you teaching?", - "@selectTargetLanguage": { + "selectSpaceTargetLanguage": "What is the most common target language of the space?", + "@selectSpaceTargetLanguage": { "type": "text", "placeholders": {} }, - "whatIsYourClassLanguageLevel": "What is the average language level of your class?", - "@whatIsYourClassLanguageLevel": { + "whatIsYourSpaceLanguageLevel": "What is the average language level of the space?", + "@whatIsYourSpaceLanguageLevel": { "type": "text", "placeholders": {} }, @@ -2679,7 +2658,7 @@ "type": "text", "placeholders": {} }, - "createGroupChatsDesc": "Toggle this on to allow students to create group chats within the class/exchange space.", + "createGroupChatsDesc": "Toggle this on to allow students to create group chats within the space.", "@createGroupChatsDesc": { "type": "text", "placeholders": {} @@ -2794,12 +2773,12 @@ "type": "text", "placeholders": {} }, - "joinWithClassCode": "Join class or exchange", + "joinWithClassCode": "Join space", "@joinWithClassCode": { "type": "text", "placeholders": {} }, - "joinWithClassCodeDesc": "Connect to a class or exchange space with the 6-digit invite code provided by the space administrator.", + "joinWithClassCodeDesc": "Connect to a space with the 6-digit invite code provided by the space administrator.", "@joinWithClassCodeDesc": { "type": "text", "placeholders": {} @@ -2809,7 +2788,7 @@ "type": "text", "placeholders": {} }, - "unableToFindClass": "We are unable to find the class or exchange. Please double-check the information with the space administrator. If you are still experiencing an issue, please contact support@pangea.chat.", + "unableToFindClass": "We are unable to find the space. Please double-check the information with the space administrator. If you are still experiencing an issue, please contact support@pangea.chat.", "@unableToFindClass": { "type": "text", "placeholders": {} @@ -2869,12 +2848,12 @@ "type": "text", "placeholders": {} }, - "welcomeToPangea18Plus": "Welcome to Pangea Chat! 🙂\nWhat's next?\nCreate or join a class!\nOr search for a conversation partner!", + "welcomeToPangea18Plus": "Welcome to Pangea Chat! 🙂\nWhat's next?\nCreate or join a space!\nOr search for a conversation partner!", "@welcomeToPangea18Plus": { "type": "text", "placeholders": {} }, - "welcomeToPangeaMinor": "Welcome to Pangea Chat! 🙂\nWhat's next?\nJoin a class!\nAsk your teacher for an invite code.", + "welcomeToPangeaMinor": "Welcome to Pangea Chat! 🙂\nWhat's next?\nJoin a space!\nAsk your teacher for an invite code.", "@welcomeToPangeaMinor": { "type": "text", "placeholders": {} @@ -3174,7 +3153,7 @@ "igcToggleDescription": "This language learning tool will identify common spelling, grammar and punctuation errors in your message and suggest corrections. Though rare, the AI can make correction errors.", "sendOnEnterDescription": "Turn this off to be able to add line spaces in messages. When the toggle is off on the browser app, you can press Shift + Enter to start a new line. When the toggle is off on mobile apps, just Enter will start a new line.", "alreadyInClass": "You are already in this space.", - "pleaseLoginFirst": "Please login or sign up first and then you will be added to your class/exchange space.", + "pleaseLoginFirst": "Please login or sign up first and then you will be added to your space.", "originalMessage": "Original Message", "sentMessage": "Sent Message", "useType": "Use Type", @@ -3185,16 +3164,13 @@ "definitionsToolDescription": "When enabled, words underlined in blue can be clicked for definitions. Click messages to access definitions.", "translationsToolDescrption": "When enabled, click a message and the translation icon to see a message in your base language.", "welcomeBack": "Welcome back! If you were part of the 2023-2024 pilot, please contact us for your special pilot subscription. If you are a teacher who has (or whose institution has) purchased licenses for your class, contact us for your teacher subscription.", - "classExchanges": "Exchanges", "createNewClass": "New class space", - "newExchange": "New exchange space", "kickAllStudents": "Kick All Students", "kickAllStudentsConfirmation": "Are you sure you want to kick all students?", "inviteAllStudents": "Invite All Students", "inviteAllStudentsConfirmation": "Are you sure you want to invite all students?", "inviteStudentsFromOtherClasses": "Invite students from other spaces", "inviteUsersFromPangea": "Add teachers", - "allExchanges": "All Exchanges", "redeemPromoCode": "Redeem Promo Code", "enterPromoCode": "Enter Promo Code", "downloadTxtFile": "Download Text File", @@ -3652,22 +3628,24 @@ "pay": "Pay", "allPrivateChats": "Direct chats", "unknownPrivateChat": "Unknown private chat", - "copyClassCodeDesc": "Students who are already in the app can 'Join class or exchange' via the main menu.", - "addToClass": "Add exchange to class", - "addToClassDesc": "Adding an exchange to a class will make the exchange appear within the class for students and give them access to all chats within the exchange.", - "addToClassOrExchange": "Add chat to class or exchange", - "addToClassOrExchangeDesc": "Adding a chat to a class or exchange will make the chat appear within the class or exchange for students and give them access.", - "invitedToClassOrExchange": "{user} has invited you to join a space: {classOrExchange}! Do you wish to accept?", - "@invitedToClassOrExchange": { + "copyClassCodeDesc": "Students who are already in the app can 'Join space' via the main menu.", + "addToSpaceDesc": "Adding a chat to a space will make the chat appear within the space for students and give them access.", + "@addToSpaceDesc": { "placeholders": { - "classOrExchange": {}, + "roomtype": {} + } + }, + "invitedToSpace": "{user} has invited you to join a space: {space}! Do you wish to accept?", + "@invitedToSpace": { + "placeholders": { + "space": {}, "user": {} } }, "declinedInvitation": "Declined invitation", "acceptedInvitation": "Accepted invitation", "youreInvited": "📩 You're invited!", - "studentPermissionsDesc": "Set permissions for this space. They will only apply to the class/exchange space. They will override individual user settings.", + "studentPermissionsDesc": "Set permissions for this space. They will only apply to the space. They will override individual user settings.", "noEligibleSpaces": "There are no eligible spaces to add this to.", "youAddedToSpace": "You added {child} to {space}", "@youAddedToSpace": { @@ -3705,9 +3683,9 @@ }, "emptyChatNameWarning": "Please enter a name for this chat", "emptyClassNameWarning": "Please enter a name for this class", - "emptyExchangeNameWarning": "Please enter a name for this exchange", + "emptySpaceNameWarning": "Please enter a name for this space", "blurMeansTranslateTitle": "Why is the message blurred?", - "blurMeansTranslateBody": "While Immersion Mode is on, messages that are sent in your base language will be blurred while Pangea Bot translates them to your target language. Immersion Mode can be toggled in individual and class settings.", + "blurMeansTranslateBody": "While Immersion Mode is on, messages that are sent in your base language will be blurred while Pangea Bot translates them to your target language. Immersion Mode can be toggled in individual and space settings.", "someErrorTitle": "Hm, something's not right", "someErrorBody": "It could be an error or something in your base language.", "bestCorrectionFeedback": "That's correct!", @@ -3717,7 +3695,7 @@ "practiceDefaultPrompt": "What is the best answer?", "correctionDefaultPrompt": "What is the best replacement?", "itStartDefaultPrompt": "Do you want help translating?", - "languageLevelWarning": "Please select a class language level", + "languageLevelWarning": "Please select a space language level", "lockedChatWarning": "🔒 This chat has been locked", "lockSpace": "Lock Space", "lockChat": "Lock Chat", @@ -3730,7 +3708,6 @@ "why": "Why?", "definition": "Definition", "exampleSentence": "Example Sentence", - "addToClassTitle": "Add Exchange to Class", "reportToTeacher": "Who do you want to report this message to?", "reportMessageTitle": "{reportingUserId} has reported a message from {reportedUserId} in the chat {roomName}", "@reportMessageTitle": { @@ -3777,7 +3754,6 @@ }, "searchChatsRooms": "Search for #chats, @users...", "createClass": "Create class", - "createExchange": "Create exchange", "viewArchive": "View Archive", "trialExpiration": "Your free trial expires on {expiration}", "@trialExpiration": { @@ -3787,10 +3763,10 @@ }, "freeTrialDesc": "New users recieve a one week free trial of Pangea Chat", "activateTrial": "Activate Free Trial", - "inNoSpaces": "You are not a member of any classes or exchanges", + "inNoSpaces": "You are not a member of any spaces", "successfullySubscribed": "You have successfully subscribed!", "clickToManageSubscription": "Click here to manage your subscription.", - "emptyInviteWarning": "Add this chat to a class or exchange to invite other users.", + "emptyInviteWarning": "Add this chat to a space to invite other users.", "errorGettingAudio": "Error getting audio. Please refresh and try again.", "nothingFound": "Nothing found...", "groupName": "Group name", @@ -4059,5 +4035,17 @@ "restricted": "Restricted", "@restricted": {}, "knockRestricted": "Knock restricted", - "@knockRestricted": {} + "@knockRestricted": {}, + "nonexistentSelection": "Selection no longer exists.", + "cantAddSpaceChild": "You do not have permission to add a child to this space.", + "roomAddedToSpace": "Room(s) have been added to the selected space.", + "createNewSpace": "New space", + "@createNewSpace": { + "type": "text", + "placeholders": {} + }, + "addChatToSpaceDesc": "Adding a chat to a space will make the chat appear within the space for students and give them access.", + "addSpaceToSpaceDesc": "Adding a space to another space will make the child space appear within the parent space for students and give them access.", + "spaceAnalytics": "Space Analytics", + "changeAnalyticsLanguage": "Change Analytics Language" } \ No newline at end of file diff --git a/assets/l10n/intl_es.arb b/assets/l10n/intl_es.arb index c8eba31b8..905248dd1 100644 --- a/assets/l10n/intl_es.arb +++ b/assets/l10n/intl_es.arb @@ -2752,31 +2752,11 @@ "type": "text", "placeholders": {} }, - "openToExchanges": "¿Abierto a intercambios?", - "@openToExchanges": { - "type": "text", - "placeholders": {} - }, - "oneToOneChatsWithinExchanges": "Chats Privados dentro de Intercambios", - "@oneToOneChatsWithinExchanges": { - "type": "text", - "placeholders": {} - }, "createGroupChats": "Crear Chats Grupales", "@createGroupChats": { "type": "text", "placeholders": {} }, - "createGroupChatsInExchanges": "Crear Chats Grupales en Intercambios", - "@createGroupChatsInExchanges": { - "type": "text", - "placeholders": {} - }, - "createGroupChatsInExchangesDesc": "Active esta opción para permitir que los estudiantes creen chats grupales dentro de los intercambios.", - "@createGroupChatsInExchangesDesc": { - "type": "text", - "placeholders": {} - }, "shareStories": "Subir historias", "@shareStories": { "type": "text", @@ -2917,21 +2897,11 @@ "type": "text", "placeholders": {} }, - "findLanguageExchange": "Encuentre una clase de intercambio", - "@findLanguageExchange": { - "type": "text", - "placeholders": {} - }, "requestToEnroll": "Solicitar inscripción", "@requestToEnroll": { "type": "text", "placeholders": {} }, - "requestAnExchange": "Solicitar un intercambio", - "@requestAnExchange": { - "type": "text", - "placeholders": {} - }, "targetLanguage": "Idioma a aprender", "@targetLanguage": { "type": "text", @@ -2982,11 +2952,6 @@ "type": "text", "placeholders": {} }, - "classSettings": "Ajustes de clase", - "@classSettings": { - "type": "text", - "placeholders": {} - }, "suggestToClass": "Sugiera el chat a", "@suggestToClass": { "type": "text", @@ -3032,8 +2997,8 @@ "type": "text", "placeholders": {} }, - "classAnalyticsDesc": "Información detallada sobre la participación de los estudiantes y el uso del idioma", - "@classAnalyticsDesc": { + "spaceAnalyticsDesc": "Información detallada sobre la participación de los estudiantes y el uso del idioma", + "@spaceAnalyticsDesc": { "type": "text", "placeholders": {} }, @@ -3815,7 +3780,6 @@ "sentMessage": "Mensaje enviado", "useType": "Tipo de Uso", "notAvailable": "No disponible", - "classExchanges": "Intercambios", "kickAllStudents": "Patear a todos los estudiantes", "kickAllStudentsConfirmation": "¿Estás seguro de que quieres echar a todos los estudiantes?", "inviteAllStudents": "Invitar a todos los estudiantes", @@ -4296,7 +4260,6 @@ "copyClassLink": "Copiar enlace de invitación", "copyClassLinkDesc": "Al hacer clic en este enlace, los usuarios accederán a la aplicación, se abrirán una cuenta y se unirán automáticamente a este espacio.", "copyClassCode": "Copiar código de invitación", - "classSettingsDesc": "Editar idiomas de clase y nivel", "createGroupChatsDesc": "Active esta opción para permitir a los estudiantes crear chats de grupo dentro del espacio de clase/intercambio.", "joinWithClassCode": "Únete a una clase o a un intercambio", "joinWithClassCodeDesc": "Conéctese a una clase o espacio de intercambio con el código de invitación de 6 dígitos proporcionado por el administrador del espacio.", @@ -4304,8 +4267,6 @@ "unableToFindClass": "No podemos encontrar la clase o el intercambio. Por favor, vuelva a comprobar la información con el administrador del espacio. Si sigue teniendo problemas, póngase en contacto con support@pangea.chat.", "welcomeToYourNewClass": "Bienvenido 🙂", "welcomeToClass": "Bienvenido! 🙂\n- ¡Prueba a unirte a un chat!\n- ¡Diviértete chateando!", - "welcomeToPangea18Plus": "Bienvenido al chat de Pangea 🙂 .\n¿Qué es lo siguiente?\n¡Crea una clase o únete a ella!\n¡O busca un compañero de conversación!", - "welcomeToPangeaMinor": "Bienvenido al chat de Pangea 🙂 .\n¿Qué es lo siguiente?\n¡Únete a una clase!\nPide a tu profesor un código de invitación.", "unableToFindClassCode": "No se puede encontrar el código.", "errorDisableITClassDesc": "La ayuda a la traducción está desactivada para el espacio en el que se encuentra este chat.", "errorDisableIGCClassDesc": "La asistencia gramatical está desactivada para el espacio en el que se encuentra este chat.", @@ -4317,26 +4278,14 @@ "pleaseLoginFirst": "Inicie sesión o regístrese primero y, a continuación, se le añadirá a su clase/espacio de intercambio.", "welcomeBack": "¡Bienvenido de nuevo! Si formó parte del piloto 2023-2024, póngase en contacto con nosotros para obtener su suscripción especial de piloto. Si es usted un profesor que ha adquirido (o cuya institución ha adquirido) licencias para su clase, póngase en contacto con nosotros para obtener su suscripción de profesor.", "createNewClass": "Nuevo espacio para clases", - "newExchange": "Nuevo espacio de intercambio", "inviteStudentsFromOtherClasses": "Invitar a estudiantes de otros espacios", - "allExchanges": "Todos los intercambios", "creatingSpacePleaseWait": "Creando espacio. Por favor, espere...", "pay": "Pagar", "copyClassCodeDesc": "Los estudiantes que ya están en la aplicación pueden 'Unirse a una clase o a un intercambio' a través del menú principal.", "inviteUsersFromPangea": "Añadir profesores", "addToClass": "Añadir intercambio a la clase", - "addToClassOrExchange": "Añadir chat a la clase o al intercambio", - "classAnalytics": "Análisis de clase", "myLearning": "Mis análisis", "addToClassDesc": "Añadir un intercambio a una clase hará que el intercambio aparezca dentro de la clase para los estudiantes y les dará acceso a todos los chats dentro del intercambio.", - "addToClassOrExchangeDesc": "Añadir un chat a una clase o intercambio hará que el chat aparezca dentro de la clase o intercambio para los estudiantes y les dará acceso.", - "invitedToClassOrExchange": "{user} te ha invitado a unirte a ¡{classOrExchange}! ¿Deseas aceptar?", - "@invitedToClassOrExchange": { - "placeholders": { - "classOrExchange": {}, - "user": {} - } - }, "decline": "Disminución", "declinedInvitation": "Invitación rechazada", "acceptedInvitation": "Invitación aceptada", @@ -4369,14 +4318,11 @@ }, "emptyChatNameWarning": "Introduzca un nombre para este chat", "emptyClassNameWarning": "Introduzca un nombre para esta clase", - "emptyExchangeNameWarning": "Introduzca un nombre para este intercambio", "blurMeansTranslateTitle": "¿Por qué está borroso el mensaje?", - "blurMeansTranslateBody": "Mientras el Modo Inmersión esté activado, los mensajes que se envíen en su idioma base aparecerán borrosos mientras Pangea Bot los traduce a su idioma de destino. El modo inmersión puede activarse en los ajustes individuales y de clase.", "monthlySubscription": "Mensualmente", "yearlySubscription": "Anualmente", "defaultSubscription": "Suscripción al chat de Pangea", "freeTrial": "Prueba gratuita", - "languageLevelWarning": "Seleccione un nivel de idioma de clase", "lockedChatWarning": "🔒 Este chat ha sido bloqueado", "lockSpace": "Espacio de bloqueo", "lockChat": "Chat de bloqueo", @@ -4393,7 +4339,6 @@ "itStartDefaultPrompt": "¿Quiere ayuda para traducir?", "suggestTo": "Sugerir a {spaceName}", "suggestChatDesc": "Los chats sugeridos aparecerán en la lista de chats de {spaceName}.", - "suggestExchangeDesc": "Los intercambios sugeridos aparecerán en la lista de chat de {spaceName}.", "acceptSelection": "Aceptar corrección", "acceptSelectionAnyway": "Use esto de todos modos", "makingActivity": "Actividad de fabricación", @@ -4460,7 +4405,6 @@ "groupCanBeFoundViaSearch": "El grupo se puede encontrar a través de la búsqueda", "inNoSpaces": "No es miembro de ninguna clase o bolsa", "createClass": "Crear clase", - "createExchange": "Crear intercambio", "viewArchive": "Ver archivo", "trialExpiration": "Su prueba gratuita caduca el {expiration}.", "@trialExpiration": { @@ -4675,5 +4619,86 @@ "tooltipInstructionsTitle": "¿No sabes para qué sirve?", "tooltipInstructionsMobileBody": "Mantenga pulsados los elementos para ver la información sobre herramientas.", "tooltipInstructionsBrowserBody": "Pase el ratón sobre los elementos para ver información sobre herramientas.", - "buildTranslation": "Construye tu traducción a partir de las opciones anteriores" + "buildTranslation": "Construye tu traducción a partir de las opciones anteriores", + "appLockDescription": "Bloquea la aplicación cuando no la uses con un código pin", + "swipeRightToLeftToReply": "Desliza el dedo de derecha a izquierda para responder", + "globalChatId": "ID global del chat", + "accessAndVisibility": "Acceso y visibilidad", + "accessAndVisibilityDescription": "Quién puede participar en este chat y cómo se puede descubrir el chat.", + "calls": "Llamadas", + "customEmojisAndStickers": "Emojis y pegatinas personalizados", + "customEmojisAndStickersBody": "Añade o comparte emojis o stickers personalizados que podrás utilizar en cualquier chat.", + "hideRedactedMessages": "Ocultar mensajes redactados", + "hideRedactedMessagesBody": "Si alguien borra un mensaje, éste ya no será visible en el chat.", + "hideInvalidOrUnknownMessageFormats": "Ocultar formatos de mensaje no válidos o desconocidos", + "hideMemberChangesInPublicChats": "Ocultar los cambios de los miembros en los chats públicos", + "hideMemberChangesInPublicChatsBody": "No mostrar en la línea de tiempo del chat si alguien se une o abandona un chat público para mejorar la legibilidad.", + "overview": "Visión general", + "notifyMeFor": "Notificarme para", + "passwordRecoverySettings": "Configuración de recuperación de contraseña", + "usersMustKnock": "Los usuarios deben golpear", + "userWouldLikeToChangeTheChat": "{user} desea unirse al chat.", + "@userWouldLikeToChangeTheChat": { + "placeholders": { + "user": {} + } + }, + "noPublicLinkHasBeenCreatedYet": "Aún no se ha creado ningún enlace público", + "knock": "Knock", + "multiLingualSpace": "Espacio multilingüe", + "languageSettings": "Ajustes de idioma", + "languageSettingsDesc": "Editar idiomas espaciales y nivel de competencia.", + "selectSpaceDominantLanguage": "¿Cuál es la lengua más común de los miembros del espacio?", + "selectSpaceTargetLanguage": "¿Cuál es la lengua de destino más común del espacio?", + "whatIsYourSpaceLanguageLevel": "¿Cuál es el nivel lingüístico medio del espacio?", + "welcomeToPangea18Plus": "Bienvenido al chat de Pangea 🙂 .\n¿Qué es lo siguiente?\n¡Crea o únete a un espacio!\n¡O busca un compañero de conversación!", + "welcomeToPangeaMinor": "Bienvenido al chat de Pangea 🙂 .\n¿Qué es lo siguiente?\n¡Únete a un espacio!\nPide a tu profesor un código de invitación.", + "addToSpaceDesc": "Añadir un chat a un espacio hará que el chat aparezca dentro del espacio para los estudiantes y les dará acceso.", + "invitedToSpace": "{user} te ha invitado a unirte a un espacio: ¡{space}! ¿Deseas aceptar?", + "@invitedToSpace": { + "placeholders": { + "space": {}, + "user": {} + } + }, + "emptySpaceNameWarning": "Introduzca un nombre para este espacio", + "blurMeansTranslateBody": "Mientras el Modo Inmersión esté activado, los mensajes que se envíen en tu idioma base aparecerán borrosos mientras Pangea Bot los traduce a tu idioma de destino. El Modo Inmersión puede activarse en los ajustes individuales y espaciales.", + "languageLevelWarning": "Seleccione un nivel de lengua espacial", + "knocking": "Golpeando", + "chatCanBeDiscoveredViaSearchOnServer": "El chat puede descubrirse mediante la búsqueda en {server}.", + "@chatCanBeDiscoveredViaSearchOnServer": { + "type": "text", + "placeholders": { + "server": {} + } + }, + "publicChatAddresses": "Direcciones de chat públicas", + "createNewAddress": "Crear una nueva dirección", + "userRole": "Función del usuario", + "minimumPowerLevel": "{level} es el nivel mínimo de potencia.", + "@minimumPowerLevel": { + "type": "text", + "placeholders": { + "level": {} + } + }, + "searchMore": "Buscar más...", + "gallery": "Galería", + "files": "Archivos", + "addSpaceToSpaceDescription": "Seleccione un espacio para añadir como padre", + "noDatabaseEncryption": "La encriptación de la base de datos no es compatible con esta plataforma", + "thereAreCountUsersBlocked": "Ahora mismo hay {count} usuarios bloqueados.", + "@thereAreCountUsersBlocked": { + "type": "text", + "count": {} + }, + "restricted": "Restringido", + "knockRestricted": "Golpe restringido", + "nonexistentSelection": "La selección ya no existe.", + "cantAddSpaceChild": "No tiene permiso para añadir un niño a este espacio.", + "roomAddedToSpace": "Se han añadido habitaciones al espacio seleccionado.", + "addChatToSpaceDesc": "Añadir un chat a un espacio hará que el chat aparezca dentro del espacio para los estudiantes y les dará acceso.", + "addSpaceToSpaceDesc": "Añadir un espacio a otro espacio hará que el espacio hijo aparezca dentro del espacio padre para los estudiantes y les dará acceso.", + "spaceAnalytics": "Analítica espacial", + "changeAnalyticsLanguage": "Cambiar el lenguaje analítico" } \ No newline at end of file diff --git a/lib/config/routes.dart b/lib/config/routes.dart index d4eaa5d85..407b78c1f 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -29,7 +29,6 @@ import 'package:fluffychat/pages/settings_style/settings_style.dart'; import 'package:fluffychat/pangea/enum/bar_chart_view_enum.dart'; import 'package:fluffychat/pangea/guard/p_vguard.dart'; import 'package:fluffychat/pangea/pages/analytics/student_analytics/student_analytics.dart'; -import 'package:fluffychat/pangea/pages/exchange/add_exchange_to_class.dart'; import 'package:fluffychat/pangea/pages/find_partner/find_partner.dart'; import 'package:fluffychat/pangea/pages/p_user_age/p_user_age.dart'; import 'package:fluffychat/pangea/pages/settings_learning/settings_learning.dart'; @@ -43,8 +42,8 @@ import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import '../pangea/pages/analytics/class_analytics/class_analytics.dart'; -import '../pangea/pages/analytics/class_list/class_list.dart'; +import '../pangea/pages/analytics/space_analytics/space_analytics.dart'; +import '../pangea/pages/analytics/space_list/space_list.dart'; abstract class AppRoutes { static FutureOr loggedInRedirect( @@ -202,17 +201,17 @@ abstract class AppRoutes { pageBuilder: (context, state) => defaultPageBuilder( context, state, - const AnalyticsClassList(), + const AnalyticsSpaceList(), ), redirect: loggedOutRedirect, routes: [ GoRoute( - path: ':classid', + path: ':spaceid', redirect: loggedOutRedirect, pageBuilder: (context, state) => defaultPageBuilder( context, state, - const ClassAnalyticsPage(), + const SpaceAnalyticsPage(), ), routes: [ GoRoute( @@ -220,7 +219,7 @@ abstract class AppRoutes { pageBuilder: (context, state) => defaultPageBuilder( context, state, - ClassAnalyticsPage( + SpaceAnalyticsPage( // when going to sub-space from within a parent space's analytics, the // analytics list tiles do not properly update. Adding a unique key to this page is the best fix // I can find at the moment @@ -234,7 +233,7 @@ abstract class AppRoutes { pageBuilder: (context, state) => defaultPageBuilder( context, state, - ClassAnalyticsPage( + SpaceAnalyticsPage( // when going to sub-space from within a parent space's analytics, the // analytics list tiles do not properly update. Adding a unique key to this page is the best fix // I can find at the moment @@ -319,24 +318,6 @@ abstract class AppRoutes { redirect: loggedOutRedirect, ), // #Pangea - GoRoute( - path: 'newspace/:newexchange', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const NewSpace(), - ), - redirect: loggedOutRedirect, - ), - GoRoute( - path: 'join_exchange/:exchangeid', - pageBuilder: (context, state) => defaultPageBuilder( - context, - state, - const AddExchangeToClass(), - ), - redirect: loggedOutRedirect, - ), GoRoute( path: 'partner', pageBuilder: (context, state) => defaultPageBuilder( diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 6d018bbf6..2552e3f13 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -20,8 +20,8 @@ import 'package:fluffychat/pangea/enum/use_type.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/choreo_record.dart'; -import 'package:fluffychat/pangea/models/class_model.dart'; import 'package:fluffychat/pangea/models/representation_content_model.dart'; +import 'package:fluffychat/pangea/models/space_model.dart'; import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/firebase_analytics.dart'; @@ -317,10 +317,10 @@ class ChatController extends State Future.delayed(const Duration(seconds: 1), () async { if (!mounted) return; debugPrint( - "chat.dart l1 ${pangeaController.languageController.activeL1Code(roomID: roomId)}", + "chat.dart l1 ${pangeaController.languageController.userL1?.langCode}", ); debugPrint( - "chat.dart l2 ${pangeaController.languageController.activeL2Code(roomID: roomId)}", + "chat.dart l2 ${pangeaController.languageController.userL2?.langCode}", ); if (mounted) { pangeaController.languageController.showDialogOnEmptyLanguage( @@ -654,8 +654,6 @@ class ChatController extends State ); return; } - // ensure that analytics room exists / is created for the active langCode - await room.ensureAnalyticsRoomExists(); }, onError: (err, stack) => ErrorHandler.logError(e: err, s: stack), ); diff --git a/lib/pages/chat_details/chat_details_view.dart b/lib/pages/chat_details/chat_details_view.dart index f100208e2..b19c2612c 100644 --- a/lib/pages/chat_details/chat_details_view.dart +++ b/lib/pages/chat_details/chat_details_view.dart @@ -11,10 +11,8 @@ import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/class_nam import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart'; import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_rules_editor.dart'; import 'package:fluffychat/pangea/utils/lock_room.dart'; -import 'package:fluffychat/pangea/widgets/class/add_class_and_invite.dart'; import 'package:fluffychat/pangea/widgets/class/add_space_toggles.dart'; import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_settings.dart'; -import 'package:fluffychat/pangea/widgets/space/class_settings.dart'; import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/widgets/avatar.dart'; @@ -257,11 +255,10 @@ class ChatDetailsView extends StatelessWidget { controller: controller, ), // Pangea# - if ((room.isPangeaClass || room.isExchange) && - room.isRoomAdmin) + if (room.isSpace && room.isRoomAdmin) ListTile( title: Text( - L10n.of(context)!.classAnalytics, + L10n.of(context)!.spaceAnalytics, style: TextStyle( color: Theme.of(context).colorScheme.secondary, fontWeight: FontWeight.bold, @@ -279,11 +276,12 @@ class ChatDetailsView extends StatelessWidget { '/rooms/analytics/${room.id}', ), ), - if (room.classSettings != null && room.isRoomAdmin) - ClassSettings( - roomId: controller.roomId, - startOpen: false, - ), + // commenting out language settings in spaces for now + // if (room.languageSettings != null && room.isRoomAdmin) + // LanguageSettings( + // roomId: controller.roomId, + // startOpen: false, + // ), if (room.pangeaRoomRules != null && room.isRoomAdmin) RoomRulesEditor( roomId: controller.roomId, @@ -459,16 +457,11 @@ class ChatDetailsView extends StatelessWidget { room: room, ), const Divider(height: 1), - if (!room.isPangeaClass && - !room.isDirectChat && - room.isRoomAdmin) + if (!room.isDirectChat && room.isRoomAdmin) AddToSpaceToggles( roomId: room.id, key: controller.addToSpaceKey, startOpen: false, - mode: room.isExchange - ? AddToClassMode.exchange - : AddToClassMode.chat, ), const Divider(height: 1), if (!room.isDirectChat) diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index c7a3ee706..25182bb7e 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -183,10 +183,9 @@ class ChatListController extends State bool Function(Room) getRoomFilterByActiveFilter(ActiveFilter activeFilter) { switch (activeFilter) { case ActiveFilter.allChats: - return (room) => - !room.isSpace // #Pangea - && - !room.isAnalyticsRoom; + return (room) => !room.isSpace; // #Pangea + // && + // !room.isAnalyticsRoom; // Pangea#; case ActiveFilter.groups: return (room) => @@ -818,7 +817,7 @@ class ChatListController extends State && selectedRoomIds .map((id) => Matrix.of(context).client.getRoomById(id)) - .where((e) => !(e?.isPangeaClass ?? true)) + .where((e) => !(e?.isSpace ?? false)) .every((e) => r.canIAddSpaceChild(e)), //Pangea# ) diff --git a/lib/pages/chat_list/client_chooser_button.dart b/lib/pages/chat_list/client_chooser_button.dart index 56aa568c0..e45b38c33 100644 --- a/lib/pages/chat_list/client_chooser_button.dart +++ b/lib/pages/chat_list/client_chooser_button.dart @@ -1,8 +1,8 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:fluffychat/pangea/constants/class_default_values.dart'; -import 'package:fluffychat/pangea/utils/class_code.dart'; import 'package:fluffychat/pangea/utils/find_conversation_partner_dialog.dart'; import 'package:fluffychat/pangea/utils/logout.dart'; +import 'package:fluffychat/pangea/utils/space_code.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -58,12 +58,12 @@ class ClientChooserButton extends StatelessWidget { room.isSpace && room.ownPowerLevel >= ClassDefaultValues.powerLevelOfAdmin, ), - value: SettingsAction.classAnalytics, + value: SettingsAction.spaceAnalytics, child: Row( children: [ const Icon(Icons.analytics_outlined), const SizedBox(width: 18), - Expanded(child: Text(L10n.of(context)!.classAnalytics)), + Expanded(child: Text(L10n.of(context)!.spaceAnalytics)), ], ), ), @@ -84,7 +84,7 @@ class ClientChooserButton extends StatelessWidget { children: [ const Icon(Icons.school), const SizedBox(width: 18), - Expanded(child: Text(L10n.of(context)!.createNewClass)), + Expanded(child: Text(L10n.of(context)!.createNewSpace)), ], ), ), @@ -98,16 +98,6 @@ class ClientChooserButton extends StatelessWidget { // ], // ), // ), - PopupMenuItem( - value: SettingsAction.newExchange, - child: Row( - children: [ - const Icon(Icons.connecting_airports), - const SizedBox(width: 18), - Expanded(child: Text(L10n.of(context)!.newExchange)), - ], - ), - ), if (controller.pangeaController.permissionsController.isUser18()) PopupMenuItem( value: SettingsAction.findAConversationPartner, @@ -397,11 +387,8 @@ class ClientChooserButton extends StatelessWidget { case SettingsAction.newClass: context.go('/rooms/newspace'); break; - case SettingsAction.newExchange: - context.go('/rooms/newspace/exchange'); - break; case SettingsAction.joinWithClassCode: - ClassCodeUtil.joinWithClassCodeDialog( + SpaceCodeUtil.joinWithSpaceCodeDialog( context, controller.pangeaController, ); @@ -412,7 +399,7 @@ class ClientChooserButton extends StatelessWidget { controller.pangeaController, ); break; - case SettingsAction.classAnalytics: + case SettingsAction.spaceAnalytics: context.go('/rooms/analytics'); break; case SettingsAction.myAnalytics: @@ -507,11 +494,10 @@ enum SettingsAction { // #Pangea learning, joinWithClassCode, - classAnalytics, + spaceAnalytics, myAnalytics, findAConversationPartner, logout, newClass, - newExchange // Pangea# } diff --git a/lib/pages/new_group/new_group.dart b/lib/pages/new_group/new_group.dart index b3a1703af..7e77efe56 100644 --- a/lib/pages/new_group/new_group.dart +++ b/lib/pages/new_group/new_group.dart @@ -174,7 +174,7 @@ class NewGroupController extends State { void initState() { Future.delayed(Duration.zero, () { chatTopic.langCode = - pangeaController.languageController.activeL2Code(roomID: null) ?? + pangeaController.languageController.userL2?.langCode ?? pangeaController.pLanguageStore.targetOptions.first.langCode; setState(() {}); }); diff --git a/lib/pages/new_group/new_group_view.dart b/lib/pages/new_group/new_group_view.dart index 277a7402f..b8b43c3f1 100644 --- a/lib/pages/new_group/new_group_view.dart +++ b/lib/pages/new_group/new_group_view.dart @@ -1,7 +1,6 @@ import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/new_group/new_group.dart'; import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart'; -import 'package:fluffychat/pangea/widgets/class/add_class_and_invite.dart'; import 'package:fluffychat/pangea/widgets/class/add_space_toggles.dart'; import 'package:fluffychat/pangea/widgets/conversation_bot/conversation_bot_settings.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; @@ -100,7 +99,6 @@ class NewGroupView extends StatelessWidget { key: controller.addToSpaceKey, startOpen: true, activeSpaceId: controller.activeSpaceId, - mode: AddToClassMode.chat, ), // const SizedBox(height: 16), // SwitchListTile.adaptive( diff --git a/lib/pages/new_space/new_space.dart b/lib/pages/new_space/new_space.dart index 59818d3b2..7a7759349 100644 --- a/lib/pages/new_space/new_space.dart +++ b/lib/pages/new_space/new_space.dart @@ -8,16 +8,14 @@ import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_capa import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_rules_editor.dart'; import 'package:fluffychat/pangea/utils/bot_name.dart'; import 'package:fluffychat/pangea/utils/class_chat_power_levels.dart'; -import 'package:fluffychat/pangea/utils/class_code.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/firebase_analytics.dart'; +import 'package:fluffychat/pangea/utils/space_code.dart'; import 'package:fluffychat/pangea/widgets/class/add_space_toggles.dart'; -import 'package:fluffychat/pangea/widgets/space/class_settings.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:go_router/go_router.dart'; import 'package:matrix/matrix.dart' as sdk; import 'package:matrix/matrix.dart'; @@ -36,8 +34,9 @@ class NewSpaceController extends State { bool publicGroup = true; final GlobalKey rulesEditorKey = GlobalKey(); final GlobalKey addToSpaceKey = GlobalKey(); - final GlobalKey classSettingsKey = - GlobalKey(); + // commenting out language settings in spaces for now + // final GlobalKey languageSettingsKey = + // GlobalKey(); final GlobalKey addCapacityKey = GlobalKey(); @@ -68,8 +67,6 @@ class NewSpaceController extends State { void setPublicGroup(bool b) => setState(() => publicGroup = b); // #Pangea - bool newClassMode = true; - List get initialState { final events = []; @@ -95,11 +92,11 @@ class NewSpaceController extends State { } else { debugger(when: kDebugMode); } - if (classSettingsKey.currentState != null) { - events.add(classSettingsKey.currentState!.classSettings.toStateEvent); - } else { - debugger(when: kDebugMode && newClassMode); - } + // commenting out language settings in spaces for now + // if (languageSettingsKey.currentState != null) { + // events + // .add(languageSettingsKey.currentState!.languageSettings.toStateEvent); + // } return events; } @@ -117,33 +114,30 @@ class NewSpaceController extends State { debugger(when: kDebugMode); return; } - if (classSettingsKey.currentState != null && - classSettingsKey.currentState!.sameLanguages) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text(L10n.of(context)!.noIdenticalLanguages), - ), - ); - return; - } - if (newClassMode) { - final int? languageLevel = - classSettingsKey.currentState!.classSettings.languageLevel; - if (languageLevel == null) { - ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(L10n.of(context)!.languageLevelWarning)), - ); - return; - } - } + // commenting out language settings in spaces for now + // if (languageSettingsKey.currentState != null && + // languageSettingsKey.currentState!.sameLanguages) { + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar( + // content: Text(L10n.of(context)!.noIdenticalLanguages), + // ), + // ); + // return; + // } + // final int? languageLevel = + // languageSettingsKey.currentState!.languageSettings.languageLevel; + // if (languageLevel == null) { + // ScaffoldMessenger.of(context).showSnackBar( + // SnackBar(content: Text(L10n.of(context)!.languageLevelWarning)), + // ); + // return; + // } // Pangea# if (nameController.text.isEmpty) { setState(() { // #Pangea // nameError = L10n.of(context)!.pleaseChoose; - final String warning = newClassMode - ? L10n.of(context)!.emptyClassNameWarning - : L10n.of(context)!.emptyExchangeNameWarning; + final String warning = L10n.of(context)!.emptySpaceNameWarning; ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(warning)), ); @@ -168,7 +162,7 @@ class NewSpaceController extends State { // roomAliasName: publicGroup // ? nameController.text.trim().toLowerCase().replaceAll(' ', '_') // : null, - roomAliasName: ClassCodeUtil.generateClassCode(), + roomAliasName: SpaceCodeUtil.generateSpaceCode(), // Pangea# name: nameController.text.trim(), topic: topicController.text.isEmpty ? null : topicController.text, @@ -224,15 +218,10 @@ class NewSpaceController extends State { } room.setSpaceChild(newChatRoomId, suggested: true); - newClassMode - ? GoogleAnalytics.addParent( - newChatRoomId, - room.classCode, - ) - : GoogleAnalytics.addChatToExchange( - newChatRoomId, - room.classCode, - ); + GoogleAnalytics.addParent( + newChatRoomId, + room.classCode, + ); GoogleAnalytics.createClass(room.name, room.classCode); try { @@ -267,8 +256,6 @@ class NewSpaceController extends State { // #Pangea // Widget build(BuildContext context) => NewSpaceView(this); Widget build(BuildContext context) { - newClassMode = - GoRouterState.of(context).pathParameters['newexchange'] != 'exchange'; return NewSpaceView(this); } // Pangea# diff --git a/lib/pages/new_space/new_space_view.dart b/lib/pages/new_space/new_space_view.dart index 09abb7066..469a3326d 100644 --- a/lib/pages/new_space/new_space_view.dart +++ b/lib/pages/new_space/new_space_view.dart @@ -1,17 +1,14 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/extensions/client_extension/client_extension.dart'; -import 'package:fluffychat/pangea/models/class_model.dart'; +import 'package:fluffychat/pangea/models/space_model.dart'; import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_capacity_button.dart'; import 'package:fluffychat/pangea/pages/class_settings/p_class_widgets/room_rules_editor.dart'; -import 'package:fluffychat/pangea/widgets/class/add_class_and_invite.dart'; import 'package:fluffychat/pangea/widgets/class/add_space_toggles.dart'; -import 'package:fluffychat/pangea/widgets/space/class_settings.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:go_router/go_router.dart'; import 'new_space.dart'; @@ -32,29 +29,8 @@ class NewSpaceView extends StatelessWidget { appBar: AppBar( // #Pangea centerTitle: true, - title: Text( - controller.newClassMode - ? L10n.of(context)!.createNewClass - : L10n.of(context)!.newExchange, - ), - actions: [ - IconButton( - icon: const Icon(Icons.class_outlined), - selectedIcon: const Icon(Icons.class_), - color: controller.newClassMode ? activeColor : null, - isSelected: controller.newClassMode, - onPressed: () => context.go('/rooms/newspace'), - ), - IconButton( - icon: const Icon(Icons.connecting_airports), - selectedIcon: const Icon(Icons.connecting_airports), - color: !controller.newClassMode ? activeColor : null, - isSelected: !controller.newClassMode, - onPressed: () => context.go('/rooms/newspace/exchange'), - ), - ], - // title: Text(L10n.of(context)!.createNewSpace), // Pangea# + title: Text(L10n.of(context)!.createNewSpace), ), body: MaxWidthBody( child: Column( @@ -135,22 +111,19 @@ class NewSpaceView extends StatelessWidget { RoomCapacityButton( key: controller.addCapacityKey, ), - if (controller.newClassMode) - ClassSettings( - key: controller.classSettingsKey, - roomId: null, - startOpen: true, - initialSettings: - Matrix.of(context).client.lastUpdatedClassSettings, - ), - if (!controller.newClassMode) - AddToSpaceToggles( - key: controller.addToSpaceKey, - startOpen: true, - mode: !controller.newClassMode - ? AddToClassMode.exchange - : AddToClassMode.chat, - ), + // commenting out language settings in spaces for now + // LanguageSettings( + // key: controller.languageSettingsKey, + // roomId: null, + // startOpen: true, + // initialSettings: + // Matrix.of(context).client.lastUpdatedLanguageSettings, + // ), + AddToSpaceToggles( + key: controller.addToSpaceKey, + startOpen: true, + spaceMode: true, + ), FutureBuilder( future: Matrix.of(context).client.lastUpdatedRoomRules, builder: (context, snapshot) { @@ -194,12 +167,7 @@ class NewSpaceView extends StatelessWidget { children: [ Expanded( child: Text( - // #Pangea - // L10n.of(context)!.createNewSpace, - controller.newClassMode - ? L10n.of(context)!.createClass - : L10n.of(context)!.createExchange, - // Pangea# + L10n.of(context)!.createNewSpace, ), ), Icon(Icons.adaptive.arrow_forward_outlined), diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart index 3d84b0e1d..61a9489b6 100644 --- a/lib/pangea/choreographer/controllers/choreographer.dart +++ b/lib/pangea/choreographer/controllers/choreographer.dart @@ -6,15 +6,13 @@ import 'package:fluffychat/pangea/choreographer/controllers/alternative_translat import 'package:fluffychat/pangea/choreographer/controllers/igc_controller.dart'; import 'package:fluffychat/pangea/choreographer/controllers/message_options.dart'; import 'package:fluffychat/pangea/constants/language_keys.dart'; -import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/controllers/subscription_controller.dart'; import 'package:fluffychat/pangea/enum/edit_type.dart'; -import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/models/class_model.dart'; import 'package:fluffychat/pangea/models/it_step.dart'; import 'package:fluffychat/pangea/models/language_detection_model.dart'; import 'package:fluffychat/pangea/models/representation_content_model.dart'; +import 'package:fluffychat/pangea/models/space_model.dart'; import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/utils/any_state_holder.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; @@ -451,17 +449,9 @@ class Choreographer { String? get l2LangCode => l2Lang?.langCode; LanguageModel? get l1Lang => - pangeaController.languageController.activeL1Model( - roomID: roomId, - ); - String? get l1LangCode => l1Lang?.langCode; + pangeaController.languageController.activeL1Model(); - String? get classId => roomId != null - ? pangeaController.matrixState.client - .getRoomById(roomId) - ?.firstParentWithState(PangeaEventTypes.classSettings) - ?.id - : null; + String? get l1LangCode => l1Lang?.langCode; String? get userId => pangeaController.userController.userId; diff --git a/lib/pangea/choreographer/controllers/it_controller.dart b/lib/pangea/choreographer/controllers/it_controller.dart index 61c8c2312..632db7e9b 100644 --- a/lib/pangea/choreographer/controllers/it_controller.dart +++ b/lib/pangea/choreographer/controllers/it_controller.dart @@ -265,7 +265,6 @@ class ITController { targetLangCode: targetLangCode, userId: choreographer.userId!, roomId: choreographer.roomId!, - classId: choreographer.classId, goldTranslation: goldRouteTracker.fullTranslation, goldContinuances: goldRouteTracker.continuances, ), @@ -283,7 +282,6 @@ class ITController { translationId: translationId, targetLangCode: targetLangCode, sourceLangCode: sourceLangCode, - classId: choreographer.classId, ), ); diff --git a/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart b/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart index 9d3302910..0abe90925 100644 --- a/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart +++ b/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart @@ -2,7 +2,7 @@ import 'dart:developer'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; -import 'package:fluffychat/pangea/models/class_model.dart'; +import 'package:fluffychat/pangea/models/space_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; diff --git a/lib/pangea/constants/model_keys.dart b/lib/pangea/constants/model_keys.dart index 0cd14e5a4..c85c8a7bd 100644 --- a/lib/pangea/constants/model_keys.dart +++ b/lib/pangea/constants/model_keys.dart @@ -27,11 +27,8 @@ class ModelKey { static const String clientIsPublic = "isPublic"; static const String clientIsOpenEnrollment = 'isOpenEnrollment'; - static const String clientIsOpenExchange = 'isOpenExchange'; static const String clientIsOneToOneChatClass = 'oneToOneChatClass'; - static const String clientIsOneToOneChatExchange = 'oneToOneChatExchange'; static const String clientIsCreateRooms = 'isCreateRooms'; - static const String clientIsCreateRoomsExchange = 'isCreateRoomsExchange'; static const String clientIsShareVideo = 'isShareVideo'; static const String clientIsSharePhoto = 'isSharePhoto'; static const String clientIsShareFiles = 'isShareFiles'; @@ -39,7 +36,6 @@ class ModelKey { static const String clientIsCreateStories = 'isCreateStories'; static const String clientIsVoiceNotes = 'isVoiceNotes'; static const String clientIsInviteOnlyStudents = 'isInviteOnlyStudents'; - static const String clientIsInviteOnlyExchanges = 'isInviteOnlyExchanges'; static const String userL1 = "user_l1"; static const String userL2 = "user_l2"; diff --git a/lib/pangea/constants/pangea_event_types.dart b/lib/pangea/constants/pangea_event_types.dart index a182728dd..7451a2a70 100644 --- a/lib/pangea/constants/pangea_event_types.dart +++ b/lib/pangea/constants/pangea_event_types.dart @@ -1,6 +1,5 @@ class PangeaEventTypes { - static const classSettings = "pangea.class"; - static const pangeaExchange = "p.exchange"; + static const languageSettings = "pangea.class"; static const transcript = "pangea.transcript"; diff --git a/lib/pangea/constants/pangea_room_types.dart b/lib/pangea/constants/pangea_room_types.dart index dcceadd36..804d2be86 100644 --- a/lib/pangea/constants/pangea_room_types.dart +++ b/lib/pangea/constants/pangea_room_types.dart @@ -1,4 +1,3 @@ class PangeaRoomTypes { static const analytics = 'p.analytics'; - static const exchange = 'p.exchange'; } diff --git a/lib/pangea/controllers/class_controller.dart b/lib/pangea/controllers/class_controller.dart index 1e2febf21..c9f2246fd 100644 --- a/lib/pangea/controllers/class_controller.dart +++ b/lib/pangea/controllers/class_controller.dart @@ -7,9 +7,9 @@ import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/extensions/client_extension/client_extension.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/models/class_model.dart'; -import 'package:fluffychat/pangea/utils/class_code.dart'; +import 'package:fluffychat/pangea/models/space_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:fluffychat/pangea/utils/space_code.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -35,8 +35,8 @@ class ClassController extends BaseController { Future fixClassPowerLevels() async { try { final List> classFixes = []; - final teacherSpaces = await _pangeaController - .matrixState.client.classesAndExchangesImTeaching; + final teacherSpaces = + await _pangeaController.matrixState.client.spacesImTeaching; for (final room in teacherSpaces) { classFixes.add(room.setClassPowerLevels()); } @@ -65,7 +65,7 @@ class ClassController extends BaseController { classCode, ).onError( (error, stackTrace) => - ClassCodeUtil.messageSnack(context, ErrorCopy(context, error).body), + SpaceCodeUtil.messageSnack(context, ErrorCopy(context, error).body), ); } } @@ -78,8 +78,7 @@ class ClassController extends BaseController { if (!room.isDirectChat) return []; final List existingParentsIds = room.pangeaSpaceParents.map((e) => e.id).toList(); - final List spaces = - _pangeaController.matrixState.client.classesAndExchangesImIn; + final List spaces = _pangeaController.matrixState.client.spacesImIn; //make sure we have the latest participants await Future.wait(spaces.map((e) => e.requestParticipants())); @@ -121,7 +120,7 @@ class ClassController extends BaseController { }); if (classChunk == null) { - ClassCodeUtil.messageSnack( + SpaceCodeUtil.messageSnack( context, L10n.of(context)!.unableToFindClass, ); @@ -131,7 +130,7 @@ class ClassController extends BaseController { if (_pangeaController.matrixState.client.rooms .any((room) => room.id == classChunk.roomId)) { setActiveSpaceIdInChatListController(classChunk.roomId); - ClassCodeUtil.messageSnack(context, L10n.of(context)!.alreadyInClass); + SpaceCodeUtil.messageSnack(context, L10n.of(context)!.alreadyInClass); return; } @@ -170,9 +169,6 @@ class ClassController extends BaseController { final Room? joinedSpace = _pangeaController.matrixState.client.getRoomById(classChunk.roomId); - // ensure that the user has an analytics room for this space's language - await joinedSpace?.ensureAnalyticsRoomExists(); - // when possible, add user's analytics room the to space they joined await joinedSpace?.addAnalyticsRoomsToSpace(); @@ -181,7 +177,7 @@ class ClassController extends BaseController { GoogleAnalytics.joinClass(classCode); return; } catch (err) { - ClassCodeUtil.messageSnack( + SpaceCodeUtil.messageSnack( context, ErrorCopy(context, err).body, ); @@ -199,7 +195,7 @@ class ClassController extends BaseController { final Room? room = _pangeaController.matrixState.client.getRoomById(roomId); if (room == null) return; - if (room.classSettings != null && room.pangeaRoomRules == null) { + if (room.pangeaRoomRules == null) { try { await _pangeaController.matrixState.client.setRoomStateWithKey( roomId, diff --git a/lib/pangea/controllers/language_controller.dart b/lib/pangea/controllers/language_controller.dart index e11053fe2..d070ac914 100644 --- a/lib/pangea/controllers/language_controller.dart +++ b/lib/pangea/controllers/language_controller.dart @@ -3,12 +3,9 @@ import 'dart:developer'; import 'package:fluffychat/pangea/constants/language_keys.dart'; import 'package:fluffychat/pangea/controllers/language_list_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; -import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/models/class_model.dart'; import 'package:fluffychat/pangea/models/language_model.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:matrix/matrix.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import '../widgets/user_settings/p_language_dialog.dart'; @@ -63,50 +60,51 @@ class LanguageController { return _userL2Code != null ? PangeaLanguage.byLangCode(_userL2Code!) : null; } - String? activeL1Code({String? roomID}) { - final String? activeL2 = activeL2Code(roomID: roomID); - if (roomID == null || activeL2 != _userL1Code) { - return _userL1Code; - } - final ClassSettingsModel? classContext = _pangeaController - .matrixState.client - .getRoomById(roomID) - ?.firstLanguageSettings; - final String? classL1 = classContext?.dominantLanguage; - if (classL1 == LanguageKeys.mixedLanguage || - classL1 == LanguageKeys.multiLanguage || - classL1 == null) { - if (_userL2Code != _userL1Code) { - return _userL2Code; - } - return LanguageKeys.unknownLanguage; - } - return classL1; + String? activeL1Code() { + return _userL1Code; + // final String? activeL2 = activeL2Code(roomID: roomID); + // if (roomID == null || activeL2 != _userL1Code) { + // return _userL1Code; + // } + // final LanguageSettingsModel? classContext = _pangeaController + // .matrixState.client + // .getRoomById(roomID) + // ?.firstLanguageSettings; + // final String? classL1 = classContext?.dominantLanguage; + // if (classL1 == LanguageKeys.mixedLanguage || + // classL1 == LanguageKeys.multiLanguage || + // classL1 == null) { + // if (_userL2Code != _userL1Code) { + // return _userL2Code; + // } + // return LanguageKeys.unknownLanguage; + // } + // return classL1; } /// Class languages override user languages within a class context - String? activeL2Code({String? roomID}) { - if (roomID == null) { - return _userL2Code; - } - final ClassSettingsModel? classContext = _pangeaController - .matrixState.client - .getRoomById(roomID) - ?.firstLanguageSettings; - return classContext?.targetLanguage ?? _userL2Code; + String? activeL2Code() { + return _userL2Code; + // if (roomID == null) { + // return _userL2Code; + // } + // final LanguageSettingsModel? classContext = _pangeaController + // .matrixState.client + // .getRoomById(roomID) + // ?.firstLanguageSettings; + // return classContext?.targetLanguage ?? _userL2Code; } - LanguageModel? activeL1Model({String? roomID}) { - final activeL1 = activeL1Code(roomID: roomID); - return activeL1 != null ? PangeaLanguage.byLangCode(activeL1) : null; + LanguageModel? activeL1Model() { + return userL1; + // final activeL1 = activeL1Code(roomID: roomID); + // return activeL1 != null ? PangeaLanguage.byLangCode(activeL1) : null; } LanguageModel? activeL2Model({String? roomID}) { - final activeL2 = activeL2Code(roomID: roomID); - final model = activeL2 != null ? PangeaLanguage.byLangCode(activeL2) : null; - return model; + return userL2; + // final activeL2 = activeL2Code(roomID: roomID); + // final model = activeL2 != null ? PangeaLanguage.byLangCode(activeL2) : null; + // return model; } - - bool equalActiveL1AndActiveL2({Room? room}) => - activeL1Code() == activeL2Code(roomID: room?.id); } diff --git a/lib/pangea/controllers/local_settings.dart b/lib/pangea/controllers/local_settings.dart index 2dc30cfe7..95b88ae14 100644 --- a/lib/pangea/controllers/local_settings.dart +++ b/lib/pangea/controllers/local_settings.dart @@ -1,5 +1,5 @@ import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; -import 'package:fluffychat/pangea/models/class_model.dart'; +import 'package:fluffychat/pangea/models/space_model.dart'; class LocalSettings { late PangeaController _pangeaController; diff --git a/lib/pangea/controllers/message_analytics_controller.dart b/lib/pangea/controllers/message_analytics_controller.dart index c0c8ecd1a..25dcaf2d5 100644 --- a/lib/pangea/controllers/message_analytics_controller.dart +++ b/lib/pangea/controllers/message_analytics_controller.dart @@ -4,11 +4,13 @@ import 'dart:developer'; import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/constants/match_rule_ids.dart'; import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; +import 'package:fluffychat/pangea/controllers/language_list_controller.dart'; import 'package:fluffychat/pangea/enum/construct_type_enum.dart'; import 'package:fluffychat/pangea/enum/time_span.dart'; import 'package:fluffychat/pangea/models/analytics/analytics_event.dart'; import 'package:fluffychat/pangea/models/analytics/constructs_event.dart'; import 'package:fluffychat/pangea/models/analytics/summary_analytics_event.dart'; +import 'package:fluffychat/pangea/models/language_model.dart'; import 'package:fluffychat/pangea/pages/analytics/base_analytics.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/foundation.dart'; @@ -62,6 +64,33 @@ class AnalyticsController extends BaseController { ); } + ///////// SPACE ANALYTICS LANGUAGES ////////// + String get _analyticsSpaceLangKey => "ANALYTICS_SPACE_LANG_KEY"; + + LanguageModel get currentAnalyticsSpaceLang { + try { + final String? str = _pangeaController.pStoreService.read( + _analyticsSpaceLangKey, + local: true, + ); + return str != null + ? PangeaLanguage.byLangCode(str) + : _pangeaController.languageController.userL2 ?? + _pangeaController.pLanguageStore.targetOptions.first; + } catch (err) { + debugger(when: kDebugMode); + return _pangeaController.pLanguageStore.targetOptions.first; + } + } + + Future setCurrentAnalyticsSpaceLang(LanguageModel lang) async { + await _pangeaController.pStoreService.save( + _analyticsSpaceLangKey, + lang.langCode, + local: true, + ); + } + Future myAnalyticsLastUpdated(String type) async { // given an analytics event type, get the last updated times // for each of the user's analytics rooms and return the most recent @@ -96,7 +125,6 @@ class AnalyticsController extends BaseController { Future spaceAnalyticsLastUpdated( String type, Room space, - String langCode, ) async { // check if any students have recently updated their analytics // if any have, then the cache needs to be updated @@ -106,7 +134,7 @@ class AnalyticsController extends BaseController { final List> lastUpdatedFutures = []; for (final student in space.students) { final Room? analyticsRoom = _pangeaController.matrixState.client - .analyticsRoomLocal(langCode, student.id); + .analyticsRoomLocal(currentAnalyticsSpaceLang.langCode, student.id); if (analyticsRoom == null) continue; lastUpdatedFutures.add( analyticsRoom.analyticsLastUpdated( @@ -168,9 +196,6 @@ class AnalyticsController extends BaseController { ) async { // gets all the summary analytics events for the students // in a space since the current timespace's cut off date - final langCode = _pangeaController.languageController.activeL2Code( - roomID: space.id, - ); // ensure that all the space's events are loaded (mainly the for langCode) // and that the participants are loaded @@ -181,7 +206,7 @@ class AnalyticsController extends BaseController { final List analyticsEvents = []; for (final student in space.students) { final Room? analyticsRoom = _pangeaController.matrixState.client - .analyticsRoomLocal(langCode, student.id); + .analyticsRoomLocal(currentAnalyticsSpaceLang.langCode, student.id); if (analyticsRoom != null) { final List? roomEvents = @@ -225,7 +250,8 @@ class AnalyticsController extends BaseController { (e.defaultSelected.id == defaultSelected.id) && (e.defaultSelected.type == defaultSelected.type) && (e.selected?.id == selected?.id) && - (e.selected?.type == selected?.type), + (e.selected?.type == selected?.type) && + (e.langCode == currentAnalyticsSpaceLang.langCode), ); if (index != -1) { @@ -253,6 +279,7 @@ class AnalyticsController extends BaseController { chartAnalyticsModel: chartAnalyticsModel, defaultSelected: defaultSelected, selected: selected, + langCode: currentAnalyticsSpaceLang.langCode, ), ); } @@ -401,11 +428,11 @@ class AnalyticsController extends BaseController { bool forceUpdate = false, }) async { try { + debugPrint("getting analytics"); await _pangeaController.matrixState.client.roomsLoading; // if the user is looking at space analytics, then fetch the space Room? space; - String? langCode; if (defaultSelected.type == AnalyticsEntryType.space) { space = _pangeaController.matrixState.client.getRoomById( defaultSelected.id, @@ -423,23 +450,7 @@ class AnalyticsController extends BaseController { timeSpan: currentAnalyticsTimeSpan, ); } - await space.postLoad(); - langCode = _pangeaController.languageController.activeL2Code( - roomID: space.id, - ); - if (langCode == null) { - ErrorHandler.logError( - m: "langCode missing in getAnalytics", - data: { - "space": space, - }, - ); - return ChartAnalyticsModel( - msgs: [], - timeSpan: currentAnalyticsTimeSpan, - ); - } } DateTime? lastUpdated; @@ -456,7 +467,6 @@ class AnalyticsController extends BaseController { lastUpdated = await spaceAnalyticsLastUpdated( PangeaEventTypes.summaryAnalytics, space!, - langCode!, ); } @@ -467,8 +477,10 @@ class AnalyticsController extends BaseController { lastUpdated: lastUpdated, ); if (local != null && !forceUpdate) { + debugPrint("returning local analytics"); return local; } + debugPrint("fetching new analytics"); // get all the relevant summary analytics events for the current timespan final List summaryEvents = @@ -548,24 +560,10 @@ class AnalyticsController extends BaseController { ) async { await space.postLoad(); await space.requestParticipants(); - final String? langCode = _pangeaController.languageController.activeL2Code( - roomID: space.id, - ); - - if (langCode == null) { - ErrorHandler.logError( - m: "langCode missing in allSpaceMemberConstructs", - data: { - "space": space, - }, - ); - return []; - } - final List constructEvents = []; for (final student in space.students) { final Room? analyticsRoom = _pangeaController.matrixState.client - .analyticsRoomLocal(langCode, student.id); + .analyticsRoomLocal(currentAnalyticsSpaceLang.langCode, student.id); if (analyticsRoom != null) { final List? roomEvents = (await analyticsRoom.getAnalyticsEvents( @@ -665,7 +663,8 @@ class AnalyticsController extends BaseController { e.defaultSelected.id == defaultSelected.id && e.defaultSelected.type == defaultSelected.type && e.selected?.id == selected?.id && - e.selected?.type == selected?.type, + e.selected?.type == selected?.type && + e.langCode == currentAnalyticsSpaceLang.langCode, ); if (index > -1) { @@ -691,6 +690,7 @@ class AnalyticsController extends BaseController { events: List.from(events), defaultSelected: defaultSelected, selected: selected, + langCode: currentAnalyticsSpaceLang.langCode, ); _cachedConstructs.add(entry); } @@ -784,10 +784,10 @@ class AnalyticsController extends BaseController { bool removeIT = true, bool forceUpdate = false, }) async { + debugPrint("getting constructs"); await _pangeaController.matrixState.client.roomsLoading; Room? space; - String? langCode; if (defaultSelected.type == AnalyticsEntryType.space) { space = _pangeaController.matrixState.client.getRoomById( defaultSelected.id, @@ -803,18 +803,6 @@ class AnalyticsController extends BaseController { return []; } await space.postLoad(); - langCode = _pangeaController.languageController.activeL2Code( - roomID: space.id, - ); - if (langCode == null) { - ErrorHandler.logError( - m: "langCode missing in setConstructs", - data: { - "space": space, - }, - ); - return []; - } } DateTime? lastUpdated; @@ -831,7 +819,6 @@ class AnalyticsController extends BaseController { lastUpdated = await spaceAnalyticsLastUpdated( PangeaEventTypes.construct, space!, - langCode!, ); } @@ -843,8 +830,10 @@ class AnalyticsController extends BaseController { lastUpdated: lastUpdated, ); if (local != null && !forceUpdate) { + debugPrint("returning local constructs"); return local; } + debugPrint("fetching new constructs"); final filteredConstructs = space == null ? await getMyConstructs( @@ -884,6 +873,7 @@ class AnalyticsController extends BaseController { } abstract class CacheEntry { + final String langCode; final TimeSpan timeSpan; final AnalyticsSelected defaultSelected; AnalyticsSelected? selected; @@ -892,6 +882,7 @@ abstract class CacheEntry { CacheEntry({ required this.timeSpan, required this.defaultSelected, + required this.langCode, this.selected, }) { _createdAt = DateTime.now(); @@ -924,17 +915,19 @@ class ConstructCacheEntry extends CacheEntry { required this.type, required this.events, required super.timeSpan, + required super.langCode, required super.defaultSelected, super.selected, }); } class AnalyticsCacheModel extends CacheEntry { - ChartAnalyticsModel chartAnalyticsModel; + final ChartAnalyticsModel chartAnalyticsModel; AnalyticsCacheModel({ required this.chartAnalyticsModel, required super.timeSpan, + required super.langCode, required super.defaultSelected, super.selected, }); diff --git a/lib/pangea/controllers/message_data_controller.dart b/lib/pangea/controllers/message_data_controller.dart index 4a0668880..7b3a3e6d2 100644 --- a/lib/pangea/controllers/message_data_controller.dart +++ b/lib/pangea/controllers/message_data_controller.dart @@ -176,14 +176,20 @@ class MessageDataController extends BaseController { required String target, required Room room, }) async { + if (_pangeaController.languageController.userL2 == null || + _pangeaController.languageController.userL1 == null) { + ErrorHandler.logError( + e: "userL1 or userL2 is null in _getPangeaRepresentation", + s: StackTrace.current, + ); + return null; + } final req = FullTextTranslationRequestModel( text: text, tgtLang: target, srcLang: source, - userL2: - _pangeaController.languageController.activeL2Code(roomID: room.id)!, - userL1: - _pangeaController.languageController.activeL1Code(roomID: room.id)!, + userL2: _pangeaController.languageController.userL2!.langCode, + userL1: _pangeaController.languageController.userL1!.langCode, ); try { diff --git a/lib/pangea/controllers/my_analytics_controller.dart b/lib/pangea/controllers/my_analytics_controller.dart index cd95dd002..f2aeb0410 100644 --- a/lib/pangea/controllers/my_analytics_controller.dart +++ b/lib/pangea/controllers/my_analytics_controller.dart @@ -207,8 +207,9 @@ class MyAnalyticsController extends BaseController { for (final Room chat in _studentChats) { // for each chat the student studies in, check if the langCode // matches the langCode of the analytics room + // TODO gabby - replace this final String? chatLangCode = - _pangeaController.languageController.activeL2Code(roomID: chat.id); + _pangeaController.languageController.activeL2Code(); if (chatLangCode != langCode) continue; // get messages the logged in user has sent in all chats @@ -251,11 +252,10 @@ class MyAnalyticsController extends BaseController { List targetLangs = []; final String? userL2 = _pangeaController.languageController.activeL2Code(); if (userL2 != null) targetLangs.add(userL2); + // TODO gabby - replace this final List spaceL2s = studentSpaces .map( - (space) => _pangeaController.languageController.activeL2Code( - roomID: space.id, - ), + (space) => _pangeaController.languageController.activeL2Code(), ) .toList(); targetLangs.addAll(spaceL2s.where((l2) => l2 != null).cast()); @@ -296,8 +296,8 @@ class MyAnalyticsController extends BaseController { List _studentSpaces = []; Future setStudentSpaces() async { - _studentSpaces = await _pangeaController - .matrixState.client.classesAndExchangesImStudyingIn; + _studentSpaces = + await _pangeaController.matrixState.client.spacesImStudyingIn; } List get studentSpaces { diff --git a/lib/pangea/controllers/permissions_controller.dart b/lib/pangea/controllers/permissions_controller.dart index f83903473..363accf8e 100644 --- a/lib/pangea/controllers/permissions_controller.dart +++ b/lib/pangea/controllers/permissions_controller.dart @@ -3,7 +3,7 @@ import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/controllers/base_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/models/class_model.dart'; +import 'package:fluffychat/pangea/models/space_model.dart'; import 'package:fluffychat/pangea/models/user_model.dart'; import 'package:fluffychat/pangea/utils/p_extension.dart'; import 'package:matrix/matrix.dart'; @@ -117,27 +117,4 @@ class PermissionsController extends BaseController { return isToolEnabled(ToolSetting.interactiveTranslator, room) && isToolEnabled(ToolSetting.interactiveGrammar, room); } - - // bool get showChatListStartChatFloatingActionButton { - // //for now, I'm turning off chat button when not in the context of a clas - // //it will still be possible to private chat outside of a class - // //need to investigate if private chats can be put in a space. i suppose they can - // //if so, do we want that? - // try { - // if (_pangeaController.classController.activeClass == null) return false; - - // // final isExchange = - // // (_pangeaController.classController.activeClass?.isExchange ?? false); - // const isExchange = false; - // final regular = (canUserPrivateChat() || canUserGroupChat()); - // final inExchange = - // (canUserPrivateChatExchanges() || canUserGroupChatExchanges()); - // final theAnswer = isExchange ? inExchange : regular; - // // debugger(when: kDebugMode && !theAnswer); - // return theAnswer; - // } catch (e, s) { - // ErrorHandler.logError(e: e, s: s); - // return false; - // } - // } } diff --git a/lib/pangea/controllers/space_rules_edit_controller.dart b/lib/pangea/controllers/space_rules_edit_controller.dart index a13fd9219..6142e273f 100644 --- a/lib/pangea/controllers/space_rules_edit_controller.dart +++ b/lib/pangea/controllers/space_rules_edit_controller.dart @@ -2,7 +2,7 @@ import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:matrix/matrix.dart'; import '../extensions/pangea_room_extension/pangea_room_extension.dart'; -import '../models/class_model.dart'; +import '../models/space_model.dart'; class RoomRulesEditController { final Room? room; diff --git a/lib/pangea/extensions/client_extension/classes_and_exchanges_extension.dart b/lib/pangea/extensions/client_extension/classes_and_exchanges_extension.dart deleted file mode 100644 index af1df62a0..000000000 --- a/lib/pangea/extensions/client_extension/classes_and_exchanges_extension.dart +++ /dev/null @@ -1,83 +0,0 @@ -part of "client_extension.dart"; - -extension ClassesAndExchangesClientExtension on Client { - List get _classes => rooms.where((e) => e.isPangeaClass).toList(); - - List get _classesImTeaching => rooms - .where( - (e) => - e.isPangeaClass && - e.ownPowerLevel == ClassDefaultValues.powerLevelOfAdmin, - ) - .toList(); - - Future> get _classesAndExchangesImTeaching async { - final allSpaces = rooms.where((room) => room.isSpace); - for (final Room space in allSpaces) { - if (space.getState(EventTypes.RoomPowerLevels) == null) { - await space.postLoad(); - } - } - - final spaces = rooms - .where( - (e) => - (e.isPangeaClass || e.isExchange) && - e.ownPowerLevel == ClassDefaultValues.powerLevelOfAdmin, - ) - .toList(); - return spaces; - } - - List get _classesImIn => rooms - .where( - (e) => - e.isPangeaClass && - e.ownPowerLevel < ClassDefaultValues.powerLevelOfAdmin, - ) - .toList(); - - Future> get _classesAndExchangesImStudyingIn async { - final List joinedSpaces = rooms - .where( - (room) => room.isSpace && room.membership == Membership.join, - ) - .toList(); - - for (final Room space in joinedSpaces) { - if (space.getState(EventTypes.RoomPowerLevels) == null) { - await space.postLoad(); - } - } - - final spaces = rooms - .where( - (e) => - (e.isPangeaClass || e.isExchange) && - e.ownPowerLevel < ClassDefaultValues.powerLevelOfAdmin, - ) - .toList(); - return spaces; - } - - List get _classesAndExchangesImIn => - rooms.where((e) => e.isPangeaClass || e.isExchange).toList(); - - Future get _lastUpdatedRoomRules async => - (await _classesAndExchangesImTeaching) - .where((space) => space.rulesUpdatedAt != null) - .sorted( - (a, b) => b.rulesUpdatedAt!.compareTo(a.rulesUpdatedAt!), - ) - .firstOrNull - ?.pangeaRoomRules; - - ClassSettingsModel? get _lastUpdatedClassSettings => classesImTeaching - .where((space) => space.classSettingsUpdatedAt != null) - .sorted( - (a, b) => - b.classSettingsUpdatedAt!.compareTo(a.classSettingsUpdatedAt!), - ) - .firstOrNull - ?.classSettings; -} diff --git a/lib/pangea/extensions/client_extension/client_analytics_extension.dart b/lib/pangea/extensions/client_extension/client_analytics_extension.dart index 6057b5a87..d2423131f 100644 --- a/lib/pangea/extensions/client_extension/client_analytics_extension.dart +++ b/lib/pangea/extensions/client_extension/client_analytics_extension.dart @@ -123,7 +123,7 @@ extension AnalyticsClientExtension on Client { // Allows teachers to join analytics rooms without being invited Future _joinAnalyticsRoomsInAllSpaces() async { final List joinFutures = []; - for (final Room space in (await _classesAndExchangesImTeaching)) { + for (final Room space in (await _spacesImTeaching)) { joinFutures.add(space.joinAnalyticsRoomsInSpace()); } await Future.wait(joinFutures); diff --git a/lib/pangea/extensions/client_extension/client_extension.dart b/lib/pangea/extensions/client_extension/client_extension.dart index d23caa5de..498081a8c 100644 --- a/lib/pangea/extensions/client_extension/client_extension.dart +++ b/lib/pangea/extensions/client_extension/client_extension.dart @@ -5,15 +5,15 @@ import 'package:fluffychat/pangea/constants/class_default_values.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/constants/pangea_room_types.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/models/class_model.dart'; +import 'package:fluffychat/pangea/models/space_model.dart'; import 'package:fluffychat/pangea/utils/bot_name.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/foundation.dart'; import 'package:matrix/matrix.dart'; -part "classes_and_exchanges_extension.dart"; part "client_analytics_extension.dart"; part "general_info_extension.dart"; +part "space_extension.dart"; extension PangeaClient on Client { // analytics @@ -43,27 +43,17 @@ extension PangeaClient on Client { Future migrateAnalyticsRooms() async => await _migrateAnalyticsRooms(); - // classes_and_exchanges + // spaces - List get classes => _classes; + Future> get spacesImTeaching async => await _spacesImTeaching; - List get classesImTeaching => _classesImTeaching; + Future> get spacesImStudyingIn async => await _spacesImStudyingIn; - Future> get classesAndExchangesImTeaching async => - await _classesAndExchangesImTeaching; - - List get classesImIn => _classesImIn; - - Future> get classesAndExchangesImStudyingIn async => - await _classesAndExchangesImStudyingIn; - - List get classesAndExchangesImIn => _classesAndExchangesImIn; + List get spacesImIn => _spacesImIn; Future get lastUpdatedRoomRules async => await _lastUpdatedRoomRules; - ClassSettingsModel? get lastUpdatedClassSettings => _lastUpdatedClassSettings; - // general_info Future> get teacherRoomIds async => await _teacherRoomIds; diff --git a/lib/pangea/extensions/client_extension/general_info_extension.dart b/lib/pangea/extensions/client_extension/general_info_extension.dart index 058b6f695..26b87b533 100644 --- a/lib/pangea/extensions/client_extension/general_info_extension.dart +++ b/lib/pangea/extensions/client_extension/general_info_extension.dart @@ -3,7 +3,7 @@ part of "client_extension.dart"; extension GeneralInfoClientExtension on Client { Future> get _teacherRoomIds async { final List adminRoomIds = []; - for (final Room adminSpace in (await _classesAndExchangesImTeaching)) { + for (final Room adminSpace in (await _spacesImTeaching)) { adminRoomIds.add(adminSpace.id); final List adminSpaceRooms = adminSpace.allSpaceChildRoomIds; adminRoomIds.addAll(adminSpaceRooms); @@ -13,7 +13,7 @@ extension GeneralInfoClientExtension on Client { Future> get _myTeachers async { final List teachers = []; - for (final classRoom in classesAndExchangesImIn) { + for (final classRoom in spacesImIn) { for (final teacher in await classRoom.teachers) { // If person requesting list of teachers is a teacher in another classroom, don't add them to the list if (!teachers.any((e) => e.id == teacher.id) && userID != teacher.id) { diff --git a/lib/pangea/extensions/client_extension/space_extension.dart b/lib/pangea/extensions/client_extension/space_extension.dart new file mode 100644 index 000000000..3bef46e45 --- /dev/null +++ b/lib/pangea/extensions/client_extension/space_extension.dart @@ -0,0 +1,64 @@ +part of "client_extension.dart"; + +extension SpaceClientExtension on Client { + Future> get _spacesImTeaching async { + final allSpaces = rooms.where((room) => room.isSpace); + for (final Room space in allSpaces) { + if (space.getState(EventTypes.RoomPowerLevels) == null) { + await space.postLoad(); + } + } + + final spaces = rooms + .where( + (e) => + (e.isSpace) && + e.ownPowerLevel == ClassDefaultValues.powerLevelOfAdmin, + ) + .toList(); + return spaces; + } + + Future> get _spacesImStudyingIn async { + final List joinedSpaces = rooms + .where( + (room) => room.isSpace && room.membership == Membership.join, + ) + .toList(); + + for (final Room space in joinedSpaces) { + if (space.getState(EventTypes.RoomPowerLevels) == null) { + await space.postLoad(); + } + } + + final spaces = rooms + .where( + (e) => + e.isSpace && + e.ownPowerLevel < ClassDefaultValues.powerLevelOfAdmin, + ) + .toList(); + return spaces; + } + + List get _spacesImIn => rooms.where((e) => e.isSpace).toList(); + + Future get _lastUpdatedRoomRules async => + (await _spacesImTeaching) + .where((space) => space.rulesUpdatedAt != null) + .sorted( + (a, b) => b.rulesUpdatedAt!.compareTo(a.rulesUpdatedAt!), + ) + .firstOrNull + ?.pangeaRoomRules; + + // LanguageSettingsModel? get _lastUpdatedLanguageSettings => rooms + // .where((room) => room.isSpace && room.languageSettingsUpdatedAt != null) + // .sorted( + // (a, b) => b.languageSettingsUpdatedAt! + // .compareTo(a.languageSettingsUpdatedAt!), + // ) + // .firstOrNull + // ?.languageSettings; +} diff --git a/lib/pangea/extensions/pangea_room_extension/children_and_parents_extension.dart b/lib/pangea/extensions/pangea_room_extension/children_and_parents_extension.dart index 78ab91cc8..8afdaefa1 100644 --- a/lib/pangea/extensions/pangea_room_extension/children_and_parents_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/children_and_parents_extension.dart @@ -60,7 +60,7 @@ extension ChildrenAndParentsRoomExtension on Room { //resolve somehow if multiple rooms have the state? //check logic Room? _firstParentWithState(String stateType) { - if (![PangeaEventTypes.classSettings, PangeaEventTypes.rules] + if (![PangeaEventTypes.languageSettings, PangeaEventTypes.rules] .contains(stateType)) { return null; } @@ -77,13 +77,6 @@ extension ChildrenAndParentsRoomExtension on Room { return null; } - /// find any parents and return the rooms - List get _immediateClassParents => pangeaSpaceParents - .where( - (element) => element.isPangeaClass, - ) - .toList(); - List get _pangeaSpaceParents => client.rooms .where( (r) => r.isSpace, @@ -98,16 +91,16 @@ extension ChildrenAndParentsRoomExtension on Room { String _nameIncludingParents(BuildContext context) { String nameSoFar = getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)); Room currentRoom = this; - if (currentRoom.immediateClassParents.isEmpty) { + if (currentRoom.pangeaSpaceParents.isEmpty) { return nameSoFar; } - currentRoom = currentRoom.immediateClassParents.first; + currentRoom = currentRoom.pangeaSpaceParents.first; var nameToAdd = currentRoom.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)); nameToAdd = nameToAdd.length <= 10 ? nameToAdd : "${nameToAdd.substring(0, 10)}..."; nameSoFar = '$nameToAdd > $nameSoFar'; - if (currentRoom.immediateClassParents.isEmpty) { + if (currentRoom.pangeaSpaceParents.isEmpty) { return nameSoFar; } return "... > $nameSoFar"; @@ -127,4 +120,14 @@ extension ChildrenAndParentsRoomExtension on Room { } return childIds; } + + // Checks if has permissions to add child chat + // Or whether potential child space is ancestor of this + bool _canAddAsParentOf(Room? child, {bool spaceMode = false}) { + if (child == null || !child.isSpace) { + return _canIAddSpaceChild(child, spaceMode: spaceMode); + } + if (id == child.id) return false; + return !child._allSpaceChildRoomIds.contains(id); + } } diff --git a/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart b/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart index edcd80b04..e27b890e9 100644 --- a/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart @@ -12,7 +12,7 @@ import 'package:fluffychat/pangea/models/analytics/constructs_event.dart'; import 'package:fluffychat/pangea/models/analytics/summary_analytics_event.dart'; import 'package:fluffychat/pangea/models/analytics/summary_analytics_model.dart'; import 'package:fluffychat/pangea/models/bot_options_model.dart'; -import 'package:fluffychat/pangea/models/class_model.dart'; +import 'package:fluffychat/pangea/models/space_model.dart'; import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/utils/bot_name.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; @@ -36,11 +36,11 @@ import '../../models/representation_content_model.dart'; import '../client_extension/client_extension.dart'; part "children_and_parents_extension.dart"; -part "class_and_exchange_settings_extension.dart"; part "events_extension.dart"; part "room_analytics_extension.dart"; part "room_information_extension.dart"; part "room_settings_extension.dart"; +part "space_settings_extension.dart"; part "user_permissions_extension.dart"; extension PangeaRoom on Room { @@ -49,9 +49,6 @@ extension PangeaRoom on Room { Future joinAnalyticsRoomsInSpace() async => await _joinAnalyticsRoomsInSpace(); - Future ensureAnalyticsRoomExists() async => - await _ensureAnalyticsRoomExists(); - Future addAnalyticsRoomToSpace(Room analyticsRoom) async => await _addAnalyticsRoomToSpace(analyticsRoom); @@ -105,8 +102,6 @@ extension PangeaRoom on Room { Room? firstParentWithState(String stateType) => _firstParentWithState(stateType); - List get immediateClassParents => _immediateClassParents; - List get pangeaSpaceParents => _pangeaSpaceParents; String nameIncludingParents(BuildContext context) => @@ -114,7 +109,10 @@ extension PangeaRoom on Room { List get allSpaceChildRoomIds => _allSpaceChildRoomIds; -// class_and_exchange_settings + bool canAddAsParentOf(Room? child, {spaceMode = false}) => + _canAddAsParentOf(child, spaceMode: spaceMode); + +// space settings DateTime? get rulesUpdatedAt => _rulesUpdatedAt; @@ -128,16 +126,8 @@ extension PangeaRoom on Room { Future setClassPowerLevels() async => await _setClassPowerLevels(); - DateTime? get classSettingsUpdatedAt => _classSettingsUpdatedAt; - - ClassSettingsModel? get classSettings => _classSettings; - - Event? get languageSettingsStateEvent => _languageSettingsStateEvent; - Event? get pangeaRoomRulesStateEvent => _pangeaRoomRulesStateEvent; - ClassSettingsModel? get firstLanguageSettings => _firstLanguageSettings; - // events Future leaveIfFull() async => await _leaveIfFull(); @@ -216,8 +206,6 @@ extension PangeaRoom on Room { bool isFirstOrSecondChild(String roomId) => _isFirstOrSecondChild(roomId); - bool get isExchange => _isExchange; - bool get isDirectChatWithoutMe => _isDirectChatWithoutMe; // bool isMadeForLang(String langCode) => _isMadeForLang(langCode); @@ -228,8 +216,6 @@ extension PangeaRoom on Room { bool get isLocked => _isLocked; - bool get isPangeaClass => _isPangeaClass; - bool isAnalyticsRoomOfUser(String userId) => _isAnalyticsRoomOfUser(userId); bool get isAnalyticsRoom => _isAnalyticsRoom; @@ -277,7 +263,8 @@ extension PangeaRoom on Room { bool get canDelete => _canDelete; - bool canIAddSpaceChild(Room? room) => _canIAddSpaceChild(room); + bool canIAddSpaceChild(Room? room, {bool spaceMode = false}) => + _canIAddSpaceChild(room, spaceMode: spaceMode); bool get canIAddSpaceParents => _canIAddSpaceParents; diff --git a/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart b/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart index 756f83adf..e3d00f8a8 100644 --- a/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart @@ -55,14 +55,6 @@ extension AnalyticsRoomExtension on Room { } } - // check if analytics room exists for a given language code - // and if not, create it - Future _ensureAnalyticsRoomExists() async { - await postLoad(); - if (firstLanguageSettings?.targetLanguage == null) return; - await client.getMyAnalyticsRoom(firstLanguageSettings!.targetLanguage); - } - // add 1 analytics room to 1 space Future _addAnalyticsRoomToSpace(Room analyticsRoom) async { if (!isSpace) { @@ -107,7 +99,7 @@ extension AnalyticsRoomExtension on Room { return; } - for (final Room space in (await client.classesAndExchangesImStudyingIn)) { + for (final Room space in (await client.spacesImStudyingIn)) { if (space.spaceChildren.any((sc) => sc.roomId == id)) continue; await space.addAnalyticsRoomToSpace(this); } @@ -183,7 +175,7 @@ extension AnalyticsRoomExtension on Room { return; } - for (final Room space in (await client.classesAndExchangesImStudyingIn)) { + for (final Room space in (await client.spacesImStudyingIn)) { await space.inviteSpaceTeachersToAnalyticsRoom(this); } } diff --git a/lib/pangea/extensions/pangea_room_extension/room_information_extension.dart b/lib/pangea/extensions/pangea_room_extension/room_information_extension.dart index e204e3f83..a33722fad 100644 --- a/lib/pangea/extensions/pangea_room_extension/room_information_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/room_information_extension.dart @@ -40,11 +40,6 @@ extension RoomInformationRoomExtension on Room { )); } - bool get _isExchange => - isSpace && - languageSettingsStateEvent == null && - pangeaRoomRulesStateEvent != null; - bool get _isDirectChatWithoutMe => isDirectChat && !getParticipants().any((e) => e.id == client.userID); @@ -85,8 +80,6 @@ extension RoomInformationRoomExtension on Room { return joinedRooms > 0 ? true : false; } - bool get _isPangeaClass => isSpace && languageSettingsStateEvent != null; - bool _isAnalyticsRoomOfUser(String userId) => isAnalyticsRoom && isMadeByUser(userId); diff --git a/lib/pangea/extensions/pangea_room_extension/room_settings_extension.dart b/lib/pangea/extensions/pangea_room_extension/room_settings_extension.dart index 0abeab942..399382f1c 100644 --- a/lib/pangea/extensions/pangea_room_extension/room_settings_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/room_settings_extension.dart @@ -40,8 +40,7 @@ extension RoomSettingsRoomExtension on Room { IconData? get _roomTypeIcon { if (membership == Membership.invite) return Icons.add; - if (isPangeaClass) return Icons.school; - if (isExchange) return Icons.connecting_airports; + if (isSpace) return Icons.school; if (isAnalyticsRoom) return Icons.analytics; if (isDirectChat) return Icons.forum; return Icons.group; diff --git a/lib/pangea/extensions/pangea_room_extension/class_and_exchange_settings_extension.dart b/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart similarity index 66% rename from lib/pangea/extensions/pangea_room_extension/class_and_exchange_settings_extension.dart rename to lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart index 44423d90f..00efb6773 100644 --- a/lib/pangea/extensions/pangea_room_extension/class_and_exchange_settings_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart @@ -1,6 +1,6 @@ part of "pangea_room_extension.dart"; -extension ClassAndExchangeSettingsRoomExtension on Room { +extension SpaceRoomExtension on Room { DateTime? get _rulesUpdatedAt { if (!isSpace) return null; return pangeaRoomRulesStateEvent?.originServerTs ?? creationTime; @@ -9,7 +9,7 @@ extension ClassAndExchangeSettingsRoomExtension on Room { String get _classCode { if (!isSpace) { for (final Room potentialClassRoom in pangeaSpaceParents) { - if (potentialClassRoom.isPangeaClass) { + if (potentialClassRoom.isSpace) { return potentialClassRoom.classCode; } } @@ -84,46 +84,6 @@ extension ClassAndExchangeSettingsRoomExtension on Room { } } - DateTime? get _classSettingsUpdatedAt { - if (!isSpace) return null; - return languageSettingsStateEvent?.originServerTs ?? creationTime; - } - - /// the pangeaClass event is listed an importantStateEvent so, if event exists, - /// it's already local. If it's an old class and doesn't, then the class_controller - /// should automatically migrate during this same session, when the space is first loaded - ClassSettingsModel? get _classSettings { - try { - if (!isSpace) { - return null; - } - final Map? content = languageSettingsStateEvent?.content; - if (content != null) { - final ClassSettingsModel classSettings = - ClassSettingsModel.fromJson(content); - return classSettings; - } - return null; - } catch (err, s) { - Sentry.addBreadcrumb( - Breadcrumb( - message: "Error in classSettings", - data: {"room": toJson()}, - ), - ); - ErrorHandler.logError(e: err, s: s); - return null; - } - } - - Event? get _languageSettingsStateEvent { - final dynamic classSettings = getState(PangeaEventTypes.classSettings); - if (classSettings is Event) { - return classSettings; - } - return null; - } - Event? get _pangeaRoomRulesStateEvent { final dynamic roomRules = getState(PangeaEventTypes.rules); if (roomRules is Event) { @@ -132,7 +92,48 @@ extension ClassAndExchangeSettingsRoomExtension on Room { return null; } - ClassSettingsModel? get _firstLanguageSettings => - classSettings ?? - firstParentWithState(PangeaEventTypes.classSettings)?.classSettings; + // DateTime? get _languageSettingsUpdatedAt { + // if (!isSpace) return null; + // return languageSettingsStateEvent?.originServerTs ?? creationTime; + // } + + /// the pangeaClass event is listed an importantStateEvent so, if event exists, + /// it's already local. If it's an old class and doesn't, then the class_controller + /// should automatically migrate during this same session, when the space is first loaded + // LanguageSettingsModel? get _languageSettings { + // try { + // if (!isSpace) { + // return null; + // } + // final Map? content = languageSettingsStateEvent?.content; + // if (content != null) { + // final LanguageSettingsModel languageSettings = + // LanguageSettingsModel.fromJson(content); + // return languageSettings; + // } + // return null; + // } catch (err, s) { + // Sentry.addBreadcrumb( + // Breadcrumb( + // message: "Error in languageSettings", + // data: {"room": toJson()}, + // ), + // ); + // ErrorHandler.logError(e: err, s: s); + // return null; + // } + // } + + // Event? get _languageSettingsStateEvent { + // final dynamic languageSettings = + // getState(PangeaEventTypes.languageSettings); + // if (languageSettings is Event) { + // return languageSettings; + // } + // return null; + // } + + // LanguageSettingsModel? get _firstLanguageSettings => + // languageSettings ?? + // firstParentWithState(PangeaEventTypes.languageSettings)?.languageSettings; } diff --git a/lib/pangea/extensions/pangea_room_extension/user_permissions_extension.dart b/lib/pangea/extensions/pangea_room_extension/user_permissions_extension.dart index eef684f0d..b94db5c57 100644 --- a/lib/pangea/extensions/pangea_room_extension/user_permissions_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/user_permissions_extension.dart @@ -78,39 +78,30 @@ extension UserPermissionsRoomExtension on Room { bool get _canDelete => isSpaceAdmin; - bool _canIAddSpaceChild(Room? room) { + bool _canIAddSpaceChild(Room? room, {bool spaceMode = false}) { if (!isSpace) { ErrorHandler.logError( - m: "should not call canIAddSpaceChildren on non-space room", + m: "should not call canIAddSpaceChildren on non-space room. Room id: $id", data: toJson(), s: StackTrace.current, ); return false; } - if (room != null && !room._isRoomAdmin) { - return false; - } - if (!pangeaCanSendEvent(EventTypes.SpaceChild) && !_isRoomAdmin) { - return false; - } - if (room == null) { - return isRoomAdmin || (pangeaRoomRules?.isCreateRooms ?? false); - } - if (room.isExchange) { - return isRoomAdmin; - } - if (!room.isSpace) { - return pangeaRoomRules?.isCreateRooms ?? false; - } - if (room.isPangeaClass) { - ErrorHandler.logError( - m: "should not call canIAddSpaceChild with class", - data: room.toJson(), - s: StackTrace.current, - ); - return false; - } - return false; + + final isSpaceAdmin = isRoomAdmin; + final isChildRoomAdmin = room?.isRoomAdmin ?? true; + + // if user is not admin of child room, return false + if (!isChildRoomAdmin) return false; + + // if the child room is a space, or will be a space, + // then the user must be an admin of the parent space + if (room?.isSpace ?? spaceMode) return isSpaceAdmin; + + // otherwise, the user can add the child room to the parent + // if they're the admin of the parent or if the parent creation + // of group chats + return isSpaceAdmin || (pangeaRoomRules?.isCreateRooms ?? false); } bool get _canIAddSpaceParents => diff --git a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart index ff8696989..b22aa7491 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart @@ -7,9 +7,9 @@ import 'package:fluffychat/pangea/enum/audio_encoding_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/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/representation_content_model.dart'; +import 'package:fluffychat/pangea/models/space_model.dart'; import 'package:fluffychat/pangea/models/speech_to_text_models.dart'; import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/utils/bot_name.dart'; @@ -553,8 +553,8 @@ class PangeaMessageEvent { final bool immersionMode = MatrixState .pangeaController.permissionsController .isToolEnabled(ToolSetting.immersionMode, room); - final String? l2Code = MatrixState.pangeaController.languageController - .activeL2Code(roomID: room.id); + final String? l2Code = + MatrixState.pangeaController.languageController.activeL2Code(); final String? originalLangCode = (originalWritten ?? originalSent)?.langCode; diff --git a/lib/pangea/models/analytics/constructs_model.dart b/lib/pangea/models/analytics/constructs_model.dart index 18c6d3d5a..6e6bad1b6 100644 --- a/lib/pangea/models/analytics/constructs_model.dart +++ b/lib/pangea/models/analytics/constructs_model.dart @@ -1,10 +1,7 @@ -import 'dart:developer'; - import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/analytics/analytics_model.dart'; import 'package:fluffychat/pangea/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; @@ -57,7 +54,7 @@ class ConstructAnalyticsModel extends AnalyticsModel { s: s, m: "Error parsing ConstructAnalyticsModel", ); - debugger(when: kDebugMode); + // debugger(when: kDebugMode); } } return ConstructAnalyticsModel( diff --git a/lib/pangea/models/custom_input_translation_model.dart b/lib/pangea/models/custom_input_translation_model.dart index 37d6c7a88..ff0c339dc 100644 --- a/lib/pangea/models/custom_input_translation_model.dart +++ b/lib/pangea/models/custom_input_translation_model.dart @@ -8,7 +8,6 @@ class CustomInputRequestModel { String targetLangCode; String userId; String roomId; - String? classId; String? goldTranslation; List? goldContinuances; @@ -20,7 +19,6 @@ class CustomInputRequestModel { required this.targetLangCode, required this.userId, required this.roomId, - required this.classId, required this.goldTranslation, required this.goldContinuances, }); @@ -32,7 +30,6 @@ class CustomInputRequestModel { targetLangCode: json[ModelKey.tgtLang], userId: json['user_id'], roomId: json['room_id'], - classId: json['class_id'], goldTranslation: json['gold_translation'], goldContinuances: json['gold_continuances'] != null ? List.from(json['gold_continuances']) @@ -48,7 +45,6 @@ class CustomInputRequestModel { ModelKey.tgtLang: targetLangCode, 'user_id': userId, 'room_id': roomId, - 'class_id': classId, 'gold_translation': goldTranslation, 'gold_continuances': goldContinuances != null ? List.from(goldContinuances!.map((e) => e.toJson())) diff --git a/lib/pangea/models/exchange_model.dart b/lib/pangea/models/exchange_model.dart deleted file mode 100644 index 42ce2a175..000000000 --- a/lib/pangea/models/exchange_model.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'dart:developer'; - -import 'package:flutter/foundation.dart'; - -import 'package:fluffychat/pangea/constants/model_keys.dart'; -import 'package:fluffychat/pangea/utils/error_handler.dart'; -import 'class_model.dart'; - -class ExchangeModel { - PangeaRoomRules permissions; - - ExchangeModel({ - required this.permissions, - }); - - factory ExchangeModel.fromJson(Map json) { - return ExchangeModel( - permissions: PangeaRoomRules.fromJson(json[ModelKey.permissions]), - ); - } - - Map toJson() { - final data = {}; - try { - data[ModelKey.permissions] = permissions.toJson(); - return data; - } catch (e, s) { - debugger(when: kDebugMode); - ErrorHandler.logError(e: e, s: s); - return data; - } - } - - updateEditableClassField(String key, dynamic value) { - switch (key) { - default: - throw Exception('Invalid key for setting permissions - $key'); - } - } -} diff --git a/lib/pangea/models/language_model.dart b/lib/pangea/models/language_model.dart index 2505d1d2c..ae960b212 100644 --- a/lib/pangea/models/language_model.dart +++ b/lib/pangea/models/language_model.dart @@ -79,8 +79,8 @@ class LanguageModel { static LanguageModel multiLingual([BuildContext? context]) => LanguageModel( displayName: context != null - ? L10n.of(context)!.multiLingualClass - : "Multilingual Class", + ? L10n.of(context)!.multiLingualSpace + : "Multilingual Space", l2: false, l1: false, langCode: LanguageKeys.multiLanguage, diff --git a/lib/pangea/models/class_model.dart b/lib/pangea/models/space_model.dart similarity index 88% rename from lib/pangea/models/class_model.dart rename to lib/pangea/models/space_model.dart index 7e95c9d26..f17507fa8 100644 --- a/lib/pangea/models/class_model.dart +++ b/lib/pangea/models/space_model.dart @@ -1,6 +1,5 @@ import 'dart:developer'; -import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -12,7 +11,7 @@ import '../constants/language_keys.dart'; import '../constants/pangea_event_types.dart'; import 'language_model.dart'; -class ClassSettingsModel { +class LanguageSettingsModel { String? city; String? country; String? schoolName; @@ -20,7 +19,7 @@ class ClassSettingsModel { String dominantLanguage; String targetLanguage; - ClassSettingsModel({ + LanguageSettingsModel({ this.dominantLanguage = ClassDefaultValues.defaultDominantLanguage, this.targetLanguage = ClassDefaultValues.defaultTargetLanguage, this.languageLevel, @@ -29,17 +28,8 @@ class ClassSettingsModel { this.schoolName, }); - static ClassSettingsModel get newClass => ClassSettingsModel( - city: null, - country: null, - dominantLanguage: ClassDefaultValues.defaultDominantLanguage, - languageLevel: null, - schoolName: null, - targetLanguage: ClassDefaultValues.defaultTargetLanguage, - ); - - factory ClassSettingsModel.fromJson(Map json) { - return ClassSettingsModel( + factory LanguageSettingsModel.fromJson(Map json) { + return LanguageSettingsModel( city: json['city'], country: json['country'], dominantLanguage: LanguageModel.codeFromNameOrCode( @@ -72,35 +62,9 @@ class ClassSettingsModel { } } - //TODO: define enum with all possible values - updateEditableClassField(String key, dynamic value) { - switch (key) { - case ModelKey.clientClassCity: - city = value; - break; - case ModelKey.clientClassCountry: - country = value; - break; - case ModelKey.clientClassDominantLanguage: - dominantLanguage = value; - break; - case ModelKey.clientClassTargetLanguage: - targetLanguage = value; - break; - case ModelKey.clientLanguageLevel: - languageLevel = value; - break; - case ModelKey.clientSchool: - schoolName = value; - break; - default: - throw Exception('Invalid key for setting permissions - $key'); - } - } - StateEvent get toStateEvent => StateEvent( content: toJson(), - type: PangeaEventTypes.classSettings, + type: PangeaEventTypes.languageSettings, ); } diff --git a/lib/pangea/models/user_model.dart b/lib/pangea/models/user_model.dart index a6ae5730f..f8e929a13 100644 --- a/lib/pangea/models/user_model.dart +++ b/lib/pangea/models/user_model.dart @@ -4,7 +4,7 @@ import 'package:country_picker/country_picker.dart'; import 'package:fluffychat/pangea/constants/local.key.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; -import 'package:fluffychat/pangea/models/class_model.dart'; +import 'package:fluffychat/pangea/models/space_model.dart'; import 'package:fluffychat/pangea/utils/instructions.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; diff --git a/lib/pangea/pages/analytics/analytics_language_button.dart b/lib/pangea/pages/analytics/analytics_language_button.dart new file mode 100644 index 000000000..d74e07be1 --- /dev/null +++ b/lib/pangea/pages/analytics/analytics_language_button.dart @@ -0,0 +1,38 @@ +import 'package:fluffychat/pangea/models/language_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +class AnalyticsLanguageButton extends StatelessWidget { + final List languages; + final LanguageModel value; + final void Function(LanguageModel) onChange; + const AnalyticsLanguageButton({ + super.key, + required this.value, + required this.onChange, + required this.languages, + }); + + @override + Widget build(BuildContext context) { + return PopupMenuButton( + icon: const Icon(Icons.language_outlined), + tooltip: L10n.of(context)!.changeAnalyticsLanguage, + initialValue: value, + onSelected: (LanguageModel? lang) { + if (lang == null) { + debugPrint("when is lang null?"); + return; + } + onChange(lang); + }, + itemBuilder: (BuildContext context) => + languages.map((LanguageModel lang) { + return PopupMenuItem( + value: lang, + child: Text(lang.getDisplayName(context) ?? lang.langCode), + ); + }).toList(), + ); + } +} diff --git a/lib/pangea/pages/analytics/analytics_list_tile.dart b/lib/pangea/pages/analytics/analytics_list_tile.dart index 53bd72922..bfc19eb43 100644 --- a/lib/pangea/pages/analytics/analytics_list_tile.dart +++ b/lib/pangea/pages/analytics/analytics_list_tile.dart @@ -87,73 +87,68 @@ class AnalyticsListTileState extends State { color: widget.isSelected ? Theme.of(context).colorScheme.secondaryContainer : Colors.transparent, - child: Tooltip( - message: widget.selected.type == AnalyticsEntryType.room - ? L10n.of(context)!.joinToView - : L10n.of(context)!.studentAnalyticsNotAvailable, - child: ListTile( - leading: widget.selected.type == AnalyticsEntryType.privateChats - ? CircleAvatar( - backgroundColor: Theme.of(context).primaryColor, - foregroundColor: Colors.white, - radius: Avatar.defaultSize / 2, - child: const Icon(Icons.forum), - ) - : Avatar( - mxContent: widget.avatar, - name: widget.selected.displayName, - littleIcon: room?.roomTypeIcon, - ), - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Expanded( - child: Text( - widget.selected.displayName, - maxLines: 1, - overflow: TextOverflow.ellipsis, - softWrap: false, - style: TextStyle( - fontWeight: FontWeight.bold, - color: Theme.of(context).textTheme.bodyLarge!.color, - ), + child: ListTile( + leading: widget.selected.type == AnalyticsEntryType.privateChats + ? CircleAvatar( + backgroundColor: Theme.of(context).primaryColor, + foregroundColor: Colors.white, + radius: Avatar.defaultSize / 2, + child: const Icon(Icons.forum), + ) + : Avatar( + mxContent: widget.avatar, + name: widget.selected.displayName, + littleIcon: room?.roomTypeIcon, + ), + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + widget.selected.displayName, + maxLines: 1, + overflow: TextOverflow.ellipsis, + softWrap: false, + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).textTheme.bodyLarge!.color, ), ), - Tooltip( - message: L10n.of(context)!.timeOfLastMessage, - child: Text( - tileData?.lastMessageTime?.localizedTimeShort(context) ?? "", - style: TextStyle( - fontSize: 13, - color: Theme.of(context).textTheme.bodyMedium!.color, - ), + ), + Tooltip( + message: L10n.of(context)!.timeOfLastMessage, + child: Text( + tileData?.lastMessageTime?.localizedTimeShort(context) ?? "", + style: TextStyle( + fontSize: 13, + color: Theme.of(context).textTheme.bodyMedium!.color, ), ), - ], - ), - subtitle: ListSummaryAnalytics( - chartAnalytics: tileData, - ), - selected: widget.isSelected, - onTap: () { - if (widget.controller?.widget.selectedView == null) { - widget.onTap(widget.selected); - return; - } - if ((room?.isSpace ?? false) && widget.allowNavigateOnSelect) { - final String selectedView = - widget.controller!.widget.selectedView!.route; - context.go('/rooms/analytics/${room!.id}/$selectedView'); - return; - } - widget.onTap(widget.selected); - }, - trailing: (room?.isSpace ?? false) && - widget.selected.type != AnalyticsEntryType.privateChats && - widget.allowNavigateOnSelect - ? const Icon(Icons.chevron_right) - : null, + ), + ], ), + subtitle: ListSummaryAnalytics( + chartAnalytics: tileData, + ), + selected: widget.isSelected, + onTap: () { + if (widget.controller?.widget.selectedView == null) { + widget.onTap(widget.selected); + return; + } + if ((room?.isSpace ?? false) && widget.allowNavigateOnSelect) { + final String selectedView = + widget.controller!.widget.selectedView!.route; + context.go('/rooms/analytics/${room!.id}/$selectedView'); + return; + } + widget.onTap(widget.selected); + }, + trailing: (room?.isSpace ?? false) && + widget.selected.type != AnalyticsEntryType.privateChats && + widget.allowNavigateOnSelect + ? const Icon(Icons.chevron_right) + : null, ), ); } diff --git a/lib/pangea/pages/analytics/base_analytics.dart b/lib/pangea/pages/analytics/base_analytics.dart index 2b548226b..f62e5f6b5 100644 --- a/lib/pangea/pages/analytics/base_analytics.dart +++ b/lib/pangea/pages/analytics/base_analytics.dart @@ -4,6 +4,7 @@ import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/extensions/client_extension/client_extension.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/models/analytics/analytics_event.dart'; +import 'package:fluffychat/pangea/models/language_model.dart'; import 'package:fluffychat/pangea/pages/analytics/base_analytics_view.dart'; import 'package:fluffychat/pangea/pages/analytics/student_analytics/student_analytics.dart'; import 'package:flutter/material.dart'; @@ -157,6 +158,12 @@ class BaseAnalyticsController extends State { refreshStream.add(false); } + Future toggleSpaceLang(LanguageModel lang) async { + await pangeaController.analytics.setCurrentAnalyticsSpaceLang(lang); + await setChartData(); + refreshStream.add(false); + } + void setCurrentLemma(String? lemma) { currentLemma = lemma; setState(() {}); diff --git a/lib/pangea/pages/analytics/base_analytics_view.dart b/lib/pangea/pages/analytics/base_analytics_view.dart index 09805ef71..1c0445d5a 100644 --- a/lib/pangea/pages/analytics/base_analytics_view.dart +++ b/lib/pangea/pages/analytics/base_analytics_view.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:fluffychat/pangea/enum/bar_chart_view_enum.dart'; import 'package:fluffychat/pangea/enum/construct_type_enum.dart'; import 'package:fluffychat/pangea/enum/time_span.dart'; +import 'package:fluffychat/pangea/pages/analytics/analytics_language_button.dart'; import 'package:fluffychat/pangea/pages/analytics/analytics_list_tile.dart'; import 'package:fluffychat/pangea/pages/analytics/base_analytics.dart'; import 'package:fluffychat/pangea/pages/analytics/construct_list.dart'; @@ -121,6 +122,15 @@ class BaseAnalyticsView extends StatelessWidget { onChange: (TimeSpan value) => controller.toggleTimeSpan(context, value), ), + if (controller.widget.defaultSelected.type == + AnalyticsEntryType.space) + AnalyticsLanguageButton( + value: controller.pangeaController.analytics + .currentAnalyticsSpaceLang, + onChange: (lang) => controller.toggleSpaceLang(lang), + languages: controller + .pangeaController.pLanguageStore.targetOptions, + ), ], ), Expanded( diff --git a/lib/pangea/pages/analytics/class_analytics/class_analytics.dart b/lib/pangea/pages/analytics/space_analytics/space_analytics.dart similarity index 70% rename from lib/pangea/pages/analytics/class_analytics/class_analytics.dart rename to lib/pangea/pages/analytics/space_analytics/space_analytics.dart index 9bf5ac7a3..64875bfba 100644 --- a/lib/pangea/pages/analytics/class_analytics/class_analytics.dart +++ b/lib/pangea/pages/analytics/space_analytics/space_analytics.dart @@ -14,48 +14,47 @@ import 'package:matrix/matrix.dart'; import '../../../../widgets/matrix.dart'; import '../../../utils/sync_status_util_v2.dart'; -import 'class_analytics_view.dart'; +import 'space_analytics_view.dart'; -enum AnalyticsPageType { classList, student, classDetails } - -class ClassAnalyticsPage extends StatefulWidget { +class SpaceAnalyticsPage extends StatefulWidget { final BarChartViewSelection? selectedView; - const ClassAnalyticsPage({super.key, this.selectedView}); + const SpaceAnalyticsPage({super.key, this.selectedView}); @override - State createState() => ClassAnalyticsV2Controller(); + State createState() => SpaceAnalyticsV2Controller(); } -class ClassAnalyticsV2Controller extends State { +class SpaceAnalyticsV2Controller extends State { bool _initialized = false; // StreamSubscription? stateSub; // Timer? refreshTimer; List chats = []; List students = []; - String? get classId => GoRouterState.of(context).pathParameters['classid']; - Room? _classRoom; + String? get spaceId => GoRouterState.of(context).pathParameters['spaceid']; + Room? _spaceRoom; - Room? get classRoom { - if (_classRoom == null || _classRoom!.id != classId) { - debugPrint("updating _classRoom"); - _classRoom = classId != null - ? Matrix.of(context).client.getRoomById(classId!) + Room? get spaceRoom { + if (_spaceRoom == null || _spaceRoom!.id != spaceId) { + debugPrint("updating _spaceRoom"); + _spaceRoom = spaceId != null + ? Matrix.of(context).client.getRoomById(spaceId!) : null; - if (_classRoom == null) { + if (_spaceRoom == null) { context.go('/rooms/analytics'); + return null; } getChatAndStudents(); } - return _classRoom; + return _spaceRoom; } @override void initState() { super.initState(); - debugPrint("init class analytics"); + debugPrint("init space analytics"); Future.delayed(Duration.zero, () async { - if (classRoom == null || (!(classRoom?.isSpace ?? false))) { + if (spaceRoom == null || (!(spaceRoom?.isSpace ?? false))) { context.go('/rooms'); } getChatAndStudents(); @@ -64,25 +63,25 @@ class ClassAnalyticsV2Controller extends State { Future getChatAndStudents() async { try { - await classRoom?.postLoad(); - await classRoom?.requestParticipants(); + await spaceRoom?.postLoad(); + await spaceRoom?.requestParticipants(); - if (classRoom != null) { + if (spaceRoom != null) { final response = await Matrix.of(context).client.getSpaceHierarchy( - classRoom!.id, + spaceRoom!.id, ); // set the latest fetched full hierarchy in message analytics controller // we want to avoid calling this endpoint again and again, so whenever the // data is made available, set it in the controller MatrixState.pangeaController.analytics - .setLatestHierarchy(_classRoom!.id, response); + .setLatestHierarchy(_spaceRoom!.id, response); - students = classRoom!.students; + students = spaceRoom!.students; chats = response.rooms .where( (room) => - room.roomId != classRoom!.id && + room.roomId != spaceRoom!.id && room.roomType != PangeaRoomTypes.analytics, ) .toList(); @@ -116,7 +115,7 @@ class ClassAnalyticsV2Controller extends State { // onFinish: () { // getChatAndStudentAnalytics(context); // }, - child: ClassAnalyticsView(this), + child: SpaceAnalyticsView(this), ); } } diff --git a/lib/pangea/pages/analytics/class_analytics/class_analytics_view.dart b/lib/pangea/pages/analytics/space_analytics/space_analytics_view.dart similarity index 72% rename from lib/pangea/pages/analytics/class_analytics/class_analytics_view.dart rename to lib/pangea/pages/analytics/space_analytics/space_analytics_view.dart index 5f609437b..5e0008555 100644 --- a/lib/pangea/pages/analytics/class_analytics/class_analytics_view.dart +++ b/lib/pangea/pages/analytics/space_analytics/space_analytics_view.dart @@ -3,17 +3,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import '../base_analytics.dart'; -import 'class_analytics.dart'; +import 'space_analytics.dart'; -class ClassAnalyticsView extends StatelessWidget { - final ClassAnalyticsV2Controller controller; - const ClassAnalyticsView(this.controller, {super.key}); +class SpaceAnalyticsView extends StatelessWidget { + final SpaceAnalyticsV2Controller controller; + const SpaceAnalyticsView(this.controller, {super.key}); @override Widget build(BuildContext context) { - // final String pageTitle = - // "${L10n.of(context)!.classAnalytics}: ${controller.className(context)}"; - final String pageTitle = L10n.of(context)!.classAnalytics; + final String pageTitle = L10n.of(context)!.spaceAnalytics; final TabData tab1 = TabData( type: AnalyticsEntryType.room, icon: Icons.chat_bubble_outline, @@ -46,20 +44,20 @@ class ClassAnalyticsView extends StatelessWidget { .toList(), ); - return controller.classId != null + return controller.spaceId != null ? BaseAnalyticsPage( selectedView: controller.widget.selectedView, pageTitle: pageTitle, tabs: [tab1, tab2], alwaysSelected: AnalyticsSelected( - controller.classId!, + controller.spaceId!, AnalyticsEntryType.space, - controller.classRoom?.name ?? "", + controller.spaceRoom?.name ?? "", ), defaultSelected: AnalyticsSelected( - controller.classId!, + controller.spaceId!, AnalyticsEntryType.space, - controller.classRoom?.name ?? "", + controller.spaceRoom?.name ?? "", ), ) : const SizedBox(); diff --git a/lib/pangea/pages/analytics/class_list/class_list.dart b/lib/pangea/pages/analytics/space_list/space_list.dart similarity index 79% rename from lib/pangea/pages/analytics/class_list/class_list.dart rename to lib/pangea/pages/analytics/space_list/space_list.dart index 45aa6bb88..e6158525d 100644 --- a/lib/pangea/pages/analytics/class_list/class_list.dart +++ b/lib/pangea/pages/analytics/space_list/space_list.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:fluffychat/pangea/enum/time_span.dart'; import 'package:fluffychat/pangea/extensions/client_extension/client_extension.dart'; import 'package:fluffychat/pangea/pages/analytics/base_analytics.dart'; -import 'package:fluffychat/pangea/pages/analytics/class_list/class_list_view.dart'; +import 'package:fluffychat/pangea/pages/analytics/space_list/space_list_view.dart'; import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; @@ -13,14 +13,14 @@ import '../../../models/analytics/chart_analytics_model.dart'; import '../../../utils/sync_status_util_v2.dart'; import '../../../widgets/common/list_placeholder.dart'; -class AnalyticsClassList extends StatefulWidget { - const AnalyticsClassList({super.key}); +class AnalyticsSpaceList extends StatefulWidget { + const AnalyticsSpaceList({super.key}); @override - State createState() => AnalyticsClassListController(); + State createState() => AnalyticsSpaceListController(); } -class AnalyticsClassListController extends State { +class AnalyticsSpaceListController extends State { PangeaController pangeaController = MatrixState.pangeaController; List models = []; List spaces = []; @@ -28,7 +28,7 @@ class AnalyticsClassListController extends State { @override void initState() { super.initState(); - Matrix.of(context).client.classesAndExchangesImTeaching.then((spaceList) { + Matrix.of(context).client.spacesImTeaching.then((spaceList) { spaceList = spaceList .where( (space) => !spaceList.any( @@ -46,14 +46,14 @@ class AnalyticsClassListController extends State { Widget build(BuildContext context) { return PLoadingStatusV2( shimmerChild: const ListPlaceholder(), - child: AnalyticsClassListView(this), + child: AnalyticsSpaceListView(this), onFinish: () { // getAllClassAnalytics(context); }, ); } - Future updateClassAnalytics( + Future updateSpaceAnalytics( Room? space, ) async { if (space == null) { diff --git a/lib/pangea/pages/analytics/class_list/class_list_view.dart b/lib/pangea/pages/analytics/space_list/space_list_view.dart similarity index 90% rename from lib/pangea/pages/analytics/class_list/class_list_view.dart rename to lib/pangea/pages/analytics/space_list/space_list_view.dart index fe63d7675..4c6f72b71 100644 --- a/lib/pangea/pages/analytics/class_list/class_list_view.dart +++ b/lib/pangea/pages/analytics/space_list/space_list_view.dart @@ -6,11 +6,11 @@ import 'package:go_router/go_router.dart'; import '../../../enum/time_span.dart'; import '../base_analytics.dart'; -import 'class_list.dart'; +import 'space_list.dart'; -class AnalyticsClassListView extends StatelessWidget { - final AnalyticsClassListController controller; - const AnalyticsClassListView(this.controller, {super.key}); +class AnalyticsSpaceListView extends StatelessWidget { + final AnalyticsSpaceListController controller; + const AnalyticsSpaceListView(this.controller, {super.key}); @override Widget build(BuildContext context) { @@ -18,7 +18,7 @@ class AnalyticsClassListView extends StatelessWidget { appBar: AppBar( centerTitle: true, title: Text( - L10n.of(context)!.classAnalytics, + L10n.of(context)!.spaceAnalytics, style: TextStyle( color: Theme.of(context).textTheme.bodyLarge!.color, fontSize: 18, diff --git a/lib/pangea/pages/analytics/time_span_menu_button.dart b/lib/pangea/pages/analytics/time_span_menu_button.dart index 97a2ede4b..23d2ad0c8 100644 --- a/lib/pangea/pages/analytics/time_span_menu_button.dart +++ b/lib/pangea/pages/analytics/time_span_menu_button.dart @@ -15,7 +15,6 @@ class TimeSpanMenuButton extends StatelessWidget { @override Widget build(BuildContext context) { return PopupMenuButton( - offset: const Offset(0, 100), icon: const Icon(Icons.calendar_month_outlined), tooltip: L10n.of(context)!.changeDateRange, initialValue: value, diff --git a/lib/pangea/pages/class_settings/p_class_widgets/class_analytics_button.dart b/lib/pangea/pages/class_settings/p_class_widgets/class_analytics_button.dart deleted file mode 100644 index 1ac293494..000000000 --- a/lib/pangea/pages/class_settings/p_class_widgets/class_analytics_button.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'package:flutter/material.dart'; - -import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:go_router/go_router.dart'; - -class ClassAnalyticsButton extends StatelessWidget { - const ClassAnalyticsButton({super.key}); - - @override - Widget build(BuildContext context) { - final roomId = GoRouterState.of(context).pathParameters['roomid']; - final iconColor = Theme.of(context).textTheme.bodyLarge!.color; - - return Column( - children: [ - ListTile( - title: Text( - L10n.of(context)!.classAnalytics, - style: TextStyle( - color: Theme.of(context).colorScheme.secondary, - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text(L10n.of(context)!.classAnalyticsDesc), - leading: CircleAvatar( - backgroundColor: Theme.of(context).scaffoldBackgroundColor, - foregroundColor: iconColor, - child: const Icon(Icons.analytics_outlined), - ), - onTap: () => context.go('/rooms/analytics/$roomId'), - ), - ], - ); - } -} diff --git a/lib/pangea/pages/class_settings/p_class_widgets/room_rules_editor.dart b/lib/pangea/pages/class_settings/p_class_widgets/room_rules_editor.dart index 5a6616f37..350acb0b6 100644 --- a/lib/pangea/pages/class_settings/p_class_widgets/room_rules_editor.dart +++ b/lib/pangea/pages/class_settings/p_class_widgets/room_rules_editor.dart @@ -1,4 +1,4 @@ -import 'package:fluffychat/pangea/models/class_model.dart'; +import 'package:fluffychat/pangea/models/space_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:future_loading_dialog/future_loading_dialog.dart'; diff --git a/lib/pangea/pages/exchange/add_exchange_to_class.dart b/lib/pangea/pages/exchange/add_exchange_to_class.dart deleted file mode 100644 index 25b448214..000000000 --- a/lib/pangea/pages/exchange/add_exchange_to_class.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:fluffychat/pangea/widgets/class/add_class_and_invite.dart'; -import 'package:fluffychat/pangea/widgets/class/add_space_toggles.dart'; -import 'package:fluffychat/widgets/matrix.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:go_router/go_router.dart'; - -class AddExchangeToClass extends StatefulWidget { - const AddExchangeToClass({super.key}); - - @override - AddExchangeToClassState createState() => AddExchangeToClassState(); -} - -class AddExchangeToClassState extends State { - final GlobalKey addToSpaceKey = GlobalKey(); - - @override - Widget build(BuildContext context) { - final String? spaceId = - GoRouterState.of(context).pathParameters['exchangeid']; - if (spaceId == null) { - return const SizedBox(); - } - - return Scaffold( - appBar: AppBar( - centerTitle: true, - title: Text(L10n.of(context)!.addToClassTitle), - ), - body: FutureBuilder( - future: - Matrix.of(context).client.waitForRoomInSync(spaceId, join: true), - builder: (context, snapshot) { - if (snapshot.hasData) { - return ListView( - children: [ - const SizedBox(height: 40), - AddToSpaceToggles( - roomId: - GoRouterState.of(context).pathParameters['exchangeid'], - key: addToSpaceKey, - startOpen: true, - mode: AddToClassMode.exchange, - ), - ], - ); - } else { - return const Center(child: CircularProgressIndicator()); - } - }, - ), - floatingActionButton: FloatingActionButton( - onPressed: () => context.go("/rooms"), - child: const Icon(Icons.arrow_forward_outlined), - ), - ); - } -} diff --git a/lib/pangea/pages/settings_learning/settings_learning_view.dart b/lib/pangea/pages/settings_learning/settings_learning_view.dart index 6c3a87f00..910cc611f 100644 --- a/lib/pangea/pages/settings_learning/settings_learning_view.dart +++ b/lib/pangea/pages/settings_learning/settings_learning_view.dart @@ -1,5 +1,5 @@ import 'package:fluffychat/pangea/constants/local.key.dart'; -import 'package:fluffychat/pangea/models/class_model.dart'; +import 'package:fluffychat/pangea/models/space_model.dart'; import 'package:fluffychat/pangea/pages/settings_learning/settings_learning.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/widgets/user_settings/country_picker_tile.dart'; diff --git a/lib/pangea/repo/class_analytics_repo.dart b/lib/pangea/repo/class_analytics_repo.dart deleted file mode 100644 index fae8fe3b5..000000000 --- a/lib/pangea/repo/class_analytics_repo.dart +++ /dev/null @@ -1,76 +0,0 @@ -// import 'dart:convert'; -// import 'dart:developer'; - -// import 'package:fluffychat/pangea/network/requests.dart'; -// import 'package:fluffychat/pangea/utils/analytics_util.dart'; -// import 'package:flutter/foundation.dart'; -// import 'package:http/http.dart'; - -// import '../../config/environment.dart'; -// import '../models/analytics_model_oldest.dart'; -// import '../network/urls.dart'; - -class PClassAnalyticsRepo { - /// deprecated in favor of new analytics - static Future repoGetAnalyticsByIds( - String accessToken, - String timeSpan, { - List? classIds, - List? userIds, - List? chatIds, - }) async { - // if (!AnalyticsUtil.isValidSpan(timeSpan)) throw "Invalid span"; - - // final Requests req = Requests( - // accessToken: accessToken, choreoApiKey: Environment.choreoApiKey); - - // final body = {}; - // body["timespan"] = timeSpan; - // if (classIds != null) body["class_ids"] = classIds; - // if (chatIds != null) body["chat_ids"] = chatIds; - // if (userIds != null) body["user_ids"] = userIds; - - // final Response res = - // await req.post(url: PApiUrls.classAnalytics, body: body); - // final json = jsonDecode(res.body); - - // final Iterable? classJson = json["class_analytics"]; - // final Iterable? chatJson = json["chat_analytics"]; - // final Iterable? userJson = json["user_analytics"]; - - // final classInfo = classJson != null - // ? (classJson) - // .map((e) { - // e["timespan"] = timeSpan; - // return chartAnalytics(e); - // }) - // .toList() - // .cast() - // : []; - // final chatInfo = chatJson != null - // ? (chatJson) - // .map((e) { - // e["timespan"] = timeSpan; - // return chartAnalytics(e); - // }) - // .toList() - // .cast() - // : []; - // final userInfo = userJson != null - // ? (userJson) - // .map((e) { - // e["timespan"] = timeSpan; - // return chartAnalytics(e); - // }) - // .toList() - // .cast() - // : []; - - // final List allAnalytics = [ - // ...classInfo, - // ...chatInfo, - // ...userInfo - // ]; - // return allAnalytics; - } -} diff --git a/lib/pangea/repo/exchange_repo.dart b/lib/pangea/repo/exchange_repo.dart deleted file mode 100644 index a4b78112c..000000000 --- a/lib/pangea/repo/exchange_repo.dart +++ /dev/null @@ -1,34 +0,0 @@ -import 'package:flutter/material.dart'; - -class PExchangeRepo { - static fetchExchangeClassInfo(String exchangePangeaId) async {} - - static saveExchangeRecord( - String requestFromClass, - String requestToClass, - String requestTeacher, - String requestToClassAuthor, - String exchangePangeaId, - ) async {} - - static exchangeRejectRequest(String roomId, String teacherName) async {} - - static validateExchange({ - required String requestFromClass, - required String requestToClass, - required BuildContext context, - }) async {} - - static createExchangeRequest({ - required String roomId, - required String teacherID, - required String toClass, - required BuildContext context, - }) async {} - - static isExchange( - BuildContext context, - String accessToken, - String exchangeId, - ) async {} -} diff --git a/lib/pangea/utils/chat_list_handle_space_tap.dart b/lib/pangea/utils/chat_list_handle_space_tap.dart index 4930f7ecc..9b950e15b 100644 --- a/lib/pangea/utils/chat_list_handle_space_tap.dart +++ b/lib/pangea/utils/chat_list_handle_space_tap.dart @@ -56,7 +56,7 @@ void chatListHandleSpaceTap( title: L10n.of(context)!.youreInvited, message: space.isSpace ? L10n.of(context)! - .invitedToClassOrExchange(space.name, space.creatorId ?? "???") + .invitedToSpace(space.name, space.creatorId ?? "???") : L10n.of(context)! .invitedToChat(space.name, space.creatorId ?? "???"), okLabel: L10n.of(context)!.accept, diff --git a/lib/pangea/utils/download_chat.dart b/lib/pangea/utils/download_chat.dart index a73cfa129..ab0fee8a2 100644 --- a/lib/pangea/utils/download_chat.dart +++ b/lib/pangea/utils/download_chat.dart @@ -1,10 +1,11 @@ import 'dart:async'; import 'dart:io'; +import 'package:csv/csv.dart'; +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; +import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; - -import 'package:csv/csv.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:intl/intl.dart'; import 'package:matrix/matrix.dart'; @@ -15,16 +16,12 @@ import 'package:permission_handler/permission_handler.dart'; import 'package:syncfusion_flutter_xlsio/xlsio.dart'; import 'package:universal_html/html.dart' as webFile; -import 'package:fluffychat/pangea/models/class_model.dart'; -import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; -import 'package:fluffychat/pangea/utils/error_handler.dart'; import '../models/choreo_record.dart'; enum DownloadType { txt, csv, xlsx } Future downloadChat( Room room, - ClassSettingsModel classSettings, DownloadType type, Client client, BuildContext context, @@ -43,7 +40,6 @@ Future downloadChat( allEvents, timeline, room, - classSettings.targetLanguage, ); } catch (err) { ErrorHandler.logError( @@ -123,7 +119,6 @@ List getPangeaMessageEvents( List events, Timeline timeline, Room room, - String? targetLang, ) { final List allPangeaMessages = events .where( diff --git a/lib/pangea/utils/firebase_analytics.dart b/lib/pangea/utils/firebase_analytics.dart index 374242d7b..59485d35b 100644 --- a/lib/pangea/utils/firebase_analytics.dart +++ b/lib/pangea/utils/firebase_analytics.dart @@ -68,13 +68,6 @@ class GoogleAnalytics { ); } - static createExchange(String exchangeName, String classCode) { - logEvent( - 'create_exchange', - parameters: {'name': exchangeName, 'group_id': classCode}, - ); - } - static createChat(String newChatRoomId) { logEvent('create_chat', parameters: {"chat_id": newChatRoomId}); } @@ -93,27 +86,6 @@ class GoogleAnalytics { ); } - static addChatToExchange(String chatRoomId, String classCode) { - logEvent( - 'add_chat_to_exchange', - parameters: {"chat_id": chatRoomId, 'group_id': classCode}, - ); - } - - static inviteClassToExchange(String classId, String exchangeId) { - logEvent( - 'invite_class_to_exchange', - parameters: {'group_id': classId, 'exchange_id': exchangeId}, - ); - } - - static kickClassFromExchange(String classId, String exchangeId) { - logEvent( - 'kick_class_from_exchange', - parameters: {'group_id': classId, 'exchange_id': exchangeId}, - ); - } - static joinClass(String classCode) { logEvent('join_group', parameters: {'group_id': classCode}); } diff --git a/lib/pangea/utils/get_chat_list_item_subtitle.dart b/lib/pangea/utils/get_chat_list_item_subtitle.dart index 9d1e45005..5adc0040c 100644 --- a/lib/pangea/utils/get_chat_list_item_subtitle.dart +++ b/lib/pangea/utils/get_chat_list_item_subtitle.dart @@ -3,7 +3,7 @@ import 'package:fluffychat/pangea/constants/language_keys.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; -import 'package:fluffychat/pangea/models/class_model.dart'; +import 'package:fluffychat/pangea/models/space_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; @@ -69,9 +69,7 @@ class GetChatListItemSubtitle { timeline: timeline, ownMessage: false, ); - final l2Code = pangeaController.languageController - .activeL2Code(roomID: event.roomId); - + final l2Code = pangeaController.languageController.activeL2Code(); if (l2Code == null || l2Code == LanguageKeys.unknownLanguage) { return event.body; } diff --git a/lib/pangea/utils/report_message.dart b/lib/pangea/utils/report_message.dart index e1dc2d553..2fd218ea9 100644 --- a/lib/pangea/utils/report_message.dart +++ b/lib/pangea/utils/report_message.dart @@ -101,7 +101,7 @@ Future> getReportTeachers( final List otherSpaces = Matrix.of(context) .client - .classesAndExchangesImIn + .spacesImIn .where((space) => !reportRoomParentSpaces.contains(space)) .toList(); diff --git a/lib/pangea/utils/class_code.dart b/lib/pangea/utils/space_code.dart similarity index 81% rename from lib/pangea/utils/class_code.dart rename to lib/pangea/utils/space_code.dart index ca783f755..9b1a44525 100644 --- a/lib/pangea/utils/class_code.dart +++ b/lib/pangea/utils/space_code.dart @@ -6,25 +6,25 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; import '../controllers/pangea_controller.dart'; -class ClassCodeUtil { +class SpaceCodeUtil { static const codeLength = 6; - static bool isValidCode(String? classcode) { - return classcode == null || classcode.length > 4; + static bool isValidCode(String? spacecode) { + return spacecode == null || spacecode.length > 4; } - static String generateClassCode() { + static String generateSpaceCode() { final r = Random(); const chars = 'AaBbCcDdEeFfGgHhiJjKkLMmNnoPpQqRrSsTtUuVvWwXxYyZz1234567890'; return List.generate(codeLength, (index) => chars[r.nextInt(chars.length)]) .join(); } - static Future joinWithClassCodeDialog( + static Future joinWithSpaceCodeDialog( BuildContext context, PangeaController pangeaController, ) async { - final List? classCode = await showTextInputDialog( + final List? spaceCode = await showTextInputDialog( context: context, title: L10n.of(context)!.joinWithClassCode, okLabel: L10n.of(context)!.ok, @@ -33,10 +33,10 @@ class ClassCodeUtil { DialogTextField(hintText: L10n.of(context)!.joinWithClassCodeHint), ], ); - if (classCode == null || classCode.single.isEmpty) return; + if (spaceCode == null || spaceCode.single.isEmpty) return; await pangeaController.classController.joinClasswithCode( context, - classCode.first, + spaceCode.first, ); } diff --git a/lib/pangea/widgets/chat/message_speech_to_text_card.dart b/lib/pangea/widgets/chat/message_speech_to_text_card.dart index 663e39ffe..9099b9b95 100644 --- a/lib/pangea/widgets/chat/message_speech_to_text_card.dart +++ b/lib/pangea/widgets/chat/message_speech_to_text_card.dart @@ -35,13 +35,9 @@ class MessageSpeechToTextCardState extends State { STTToken? selectedToken; String? get l1Code => - MatrixState.pangeaController.languageController.activeL1Code( - roomID: widget.messageEvent.room.id, - ); + MatrixState.pangeaController.languageController.activeL1Code(); String? get l2Code => - MatrixState.pangeaController.languageController.activeL2Code( - roomID: widget.messageEvent.room.id, - ); + MatrixState.pangeaController.languageController.activeL2Code(); // look for transcription in message event // if not found, call API to transcribe audio diff --git a/lib/pangea/widgets/chat/message_translation_card.dart b/lib/pangea/widgets/chat/message_translation_card.dart index b1bae20e5..45853df61 100644 --- a/lib/pangea/widgets/chat/message_translation_card.dart +++ b/lib/pangea/widgets/chat/message_translation_card.dart @@ -107,12 +107,8 @@ class MessageTranslationCardState extends State { @override void initState() { super.initState(); - l1Code = MatrixState.pangeaController.languageController.activeL1Code( - roomID: widget.messageEvent.room.id, - ); - l2Code = MatrixState.pangeaController.languageController.activeL2Code( - roomID: widget.messageEvent.room.id, - ); + l1Code = MatrixState.pangeaController.languageController.activeL1Code(); + l2Code = MatrixState.pangeaController.languageController.activeL2Code(); if (mounted) { setState(() {}); } diff --git a/lib/pangea/widgets/class/add_class_and_invite.dart b/lib/pangea/widgets/class/add_class_and_invite.dart deleted file mode 100644 index 45c84c506..000000000 --- a/lib/pangea/widgets/class/add_class_and_invite.dart +++ /dev/null @@ -1,298 +0,0 @@ -import 'dart:developer'; - -import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:future_loading_dialog/future_loading_dialog.dart'; -import 'package:go_router/go_router.dart'; -import 'package:matrix/matrix.dart'; - -import '../../../widgets/matrix.dart'; -import '../../utils/error_handler.dart'; -import '../../utils/firebase_analytics.dart'; - -enum AddToClassMode { exchange, chat } - -class AddToClassAndInviteToggles extends StatefulWidget { - final String? roomId; - final bool startOpen; - final Function? setParentState; - final AddToClassMode mode; - - const AddToClassAndInviteToggles({ - super.key, - this.roomId, - this.startOpen = false, - this.setParentState, - required this.mode, - }); - - @override - AddToClassAndInviteState createState() => AddToClassAndInviteState(); -} - -class AddToClassAndInviteState extends State { - late Room? room; - late List parents; - late List possibleParents; - late bool isOpen; - - AddToClassAndInviteState({Key? key}); - - @override - void initState() { - room = widget.roomId != null - ? Matrix.of(context).client.getRoomById(widget.roomId!) - : null; - - if (room != null && room!.isPangeaClass) { - debugger(when: kDebugMode); - ErrorHandler.logError( - m: "should not be able to add class to space, not yet at least", - ); - context.go('/rooms'); - } - - possibleParents = Matrix.of(context) - .client - .rooms - .where( - widget.mode == AddToClassMode.exchange - ? (Room r) => r.isPangeaClass && widget.roomId != r.id - : (Room r) => - (r.isPangeaClass || r.isExchange) && widget.roomId != r.id, - ) - .toList(); - - parents = widget.roomId != null - ? possibleParents - .where( - (r) => - r.spaceChildren.any((room) => room.roomId == widget.roomId), - ) - .toList() - : []; - - isOpen = widget.startOpen; - - super.initState(); - } - - Future addParents(String roomToAddId) async { - final List> addFutures = []; - for (final Room newParent in parents) { - addFutures.add(_addSingleParent(roomToAddId, newParent)); - } - await addFutures.wait; - } - - Future _addSingleParent(String roomToAddId, Room newParent) async { - GoogleAnalytics.addParent(roomToAddId, newParent.classCode); - final List> existingMembers = await Future.wait([ - room?.requestParticipants() ?? Future.value([]), - newParent.requestParticipants(), - ]); - final List roomMembers = existingMembers[0]; - final List spaceMembers = existingMembers[1]; - - final List> inviteFutures = [ - newParent.setSpaceChild(roomToAddId, suggested: true), - ]; - for (final spaceMember - in spaceMembers.where((element) => element.id != room?.client.userID)) { - if (!roomMembers.any( - (m) => m.id == spaceMember.id && m.membership == Membership.join, - )) { - inviteFutures.add(_inviteSpaceMember(spaceMember)); - } else { - debugPrint('User ${spaceMember.id} is already in the room'); - } - } - await Future.wait(inviteFutures); - return; - } - - //function for kicking single student and haandling error - Future _kickSpaceMember(User spaceMember) async { - try { - await room?.kick(spaceMember.id); - debugPrint('Kicked ${spaceMember.id}'); - } catch (e) { - debugger(when: kDebugMode); - ErrorHandler.logError(e: e); - } - } - - //function for adding single student and haandling error - Future _inviteSpaceMember(User spaceMember) async { - try { - await room?.invite(spaceMember.id); - debugPrint('added ${spaceMember.id}'); - } catch (e) { - debugger(when: kDebugMode); - ErrorHandler.logError(e: e); - } - } - - //remove single class - Future _removeSingleSpaceFromParents( - String roomToRemoveId, - Room spaceToRemove, - ) async { - GoogleAnalytics.removeChatFromClass( - roomToRemoveId, - spaceToRemove.classCode, - ); - - if (room == null) { - ErrorHandler.logError(m: 'Room is null in kickSpaceMembers'); - debugger(when: kDebugMode); - return; - } - final List> roomsMembers = await Future.wait([ - room?.requestParticipants() ?? Future.value([]), - spaceToRemove.requestParticipants(), - ]); - - final List toKick = roomsMembers[1] - .where( - (element) => - element.id != room?.client.userID && - roomsMembers[0].any((m) => m.id == element.id), - ) - .toList(); - - final List> kickFutures = [ - spaceToRemove.removeSpaceChild(roomToRemoveId), - ]; - for (final spaceMember in toKick) { - kickFutures.add(_kickSpaceMember(spaceMember)); - } - await Future.wait(kickFutures); - - // if (widget.setParentState != null) { - // widget.setParentState!(); - // } - await room?.requestParticipants(); - - if (room != null) { - GoogleAnalytics.kickClassFromExchange(room!.id, spaceToRemove.id); - } - return; - } - - // ignore: curly_braces_in_flow_control_structures - Future _handleAdd(bool add, Room possibleParent) async { - //in this case, the room has already been made so we handle adding as it happens - if (room != null) { - await showFutureLoadingDialog( - context: context, - future: () async { - await (add - ? _addSingleParent(room!.id, possibleParent) - : _removeSingleSpaceFromParents(room!.id, possibleParent)); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - add - ? L10n.of(context)!.youAddedToSpace( - room!.name, - possibleParent.name, - ) - : L10n.of(context)!.youRemovedFromSpace( - room!.name, - possibleParent.name, - ), - ), - ), - ); - }, - ); - } - setState( - () => add - ? parents.add(possibleParent) - : parents.removeWhere((r) => r.id == possibleParent.id), - ); - } - - @override - Widget build(BuildContext context) { - return Column( - children: [ - if (!widget.startOpen) - ListTile( - enableFeedback: !widget.startOpen, - title: Text( - widget.mode == AddToClassMode.exchange - ? L10n.of(context)!.addToClass - : L10n.of(context)!.addToClassOrExchange, - style: TextStyle( - color: Theme.of(context).colorScheme.secondary, - fontWeight: FontWeight.bold, - ), - ), - leading: CircleAvatar( - backgroundColor: Theme.of(context).scaffoldBackgroundColor, - foregroundColor: Theme.of(context).textTheme.bodyLarge!.color, - child: const Icon( - Icons.workspaces_outline, - ), - ), - trailing: !widget.startOpen - ? Icon( - isOpen - ? Icons.keyboard_arrow_down_outlined - : Icons.keyboard_arrow_right_outlined, - ) - : null, - onTap: () => setState(() => isOpen = !isOpen), - ), - if (isOpen) - Padding( - padding: const EdgeInsets.fromLTRB(16.0, 0, 16.0, 0), - child: Column( - children: [ - if (possibleParents.isEmpty) - ListTile( - title: Text(L10n.of(context)!.noEligibleSpaces), - ), - ListView.builder( - shrinkWrap: true, - itemCount: possibleParents.length, - itemBuilder: (BuildContext context, int i) { - final bool canIAddSpaceChildren = - possibleParents[i].canIAddSpaceChild(room) && - (room?.canIAddSpaceParents ?? true); - return Column( - children: [ - Opacity( - opacity: canIAddSpaceChildren ? 1 : 0.5, - child: SwitchListTile.adaptive( - title: possibleParents[i].nameAndRoomTypeIcon(), - activeColor: AppConfig.activeToggleColor, - value: parents - .any((r) => r.id == possibleParents[i].id), - onChanged: (bool add) => canIAddSpaceChildren - ? _handleAdd(add, possibleParents[i]) - : ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: - Text(L10n.of(context)!.noPermission), - ), - ), - ), - ), - ], - ); - }, - ), - ], - ), - ), - ], - ); - } -} diff --git a/lib/pangea/widgets/class/add_space_toggles.dart b/lib/pangea/widgets/class/add_space_toggles.dart index cfea4dd7b..d2085be2f 100644 --- a/lib/pangea/widgets/class/add_space_toggles.dart +++ b/lib/pangea/widgets/class/add_space_toggles.dart @@ -10,21 +10,20 @@ import 'package:matrix/matrix.dart'; import '../../../widgets/matrix.dart'; import '../../utils/firebase_analytics.dart'; -import 'add_class_and_invite.dart'; //PTODO - auto invite students when you add a space and delete the add_class_and_invite.dart file class AddToSpaceToggles extends StatefulWidget { final String? roomId; final bool startOpen; final String? activeSpaceId; - final AddToClassMode mode; + final bool spaceMode; const AddToSpaceToggles({ super.key, this.roomId, this.startOpen = false, this.activeSpaceId, - required this.mode, + this.spaceMode = false, }); @override @@ -54,10 +53,7 @@ class AddToSpaceState extends State { .client .rooms .where( - widget.mode == AddToClassMode.exchange - ? (Room r) => r.isPangeaClass && widget.roomId != r.id - : (Room r) => - (r.isPangeaClass || r.isExchange) && widget.roomId != r.id, + (Room r) => r.isSpace && widget.roomId != r.id, ) .toList(); @@ -144,9 +140,10 @@ class AddToSpaceState extends State { Widget getAddToSpaceToggleItem(int index) { final Room possibleParent = possibleParents[index]; - final bool canAdd = !(!possibleParent.isRoomAdmin && - widget.mode == AddToClassMode.exchange) && - possibleParent.canIAddSpaceChild(room); + final bool canAdd = possibleParent.canAddAsParentOf( + room, + spaceMode: widget.spaceMode, + ); return Opacity( opacity: canAdd ? 1 : 0.5, @@ -185,24 +182,21 @@ class AddToSpaceState extends State { @override Widget build(BuildContext context) { - final String title = widget.mode == AddToClassMode.exchange - ? L10n.of(context)!.addToClass - : L10n.of(context)!.addToClassOrExchange; - final String subtitle = widget.mode == AddToClassMode.exchange - ? L10n.of(context)!.addToClassDesc - : L10n.of(context)!.addToClassOrExchangeDesc; - return Column( children: [ ListTile( title: Text( - title, + L10n.of(context)!.addToSpace, style: TextStyle( color: Theme.of(context).colorScheme.secondary, fontWeight: FontWeight.bold, ), ), - subtitle: Text(subtitle), + subtitle: Text( + widget.spaceMode || (room?.isSpace ?? false) + ? L10n.of(context)!.addSpaceToSpaceDesc + : L10n.of(context)!.addChatToSpaceDesc, + ), leading: CircleAvatar( backgroundColor: Theme.of(context).scaffoldBackgroundColor, foregroundColor: Theme.of(context).textTheme.bodyLarge!.color, diff --git a/lib/pangea/widgets/class/invite_students_from_class.dart b/lib/pangea/widgets/class/invite_students_from_class.dart deleted file mode 100644 index 70bafeb7d..000000000 --- a/lib/pangea/widgets/class/invite_students_from_class.dart +++ /dev/null @@ -1,342 +0,0 @@ -// import 'dart:developer'; - -// import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; -// import 'package:flutter/foundation.dart'; -// import 'package:flutter/material.dart'; -// import 'package:flutter_gen/gen_l10n/l10n.dart'; -// import 'package:future_loading_dialog/future_loading_dialog.dart'; -// import 'package:matrix/matrix.dart'; - -// import '../../../utils/matrix_sdk_extensions/matrix_locals.dart'; -// import '../../../widgets/avatar.dart'; -// import '../../../widgets/matrix.dart'; -// import '../../utils/error_handler.dart'; -// import '../../utils/firebase_analytics.dart'; - -// class InviteStudentsFromClass extends StatefulWidget { -// final String? roomId; -// final bool startOpen; -// final Function setParentState; - -// const InviteStudentsFromClass({ -// Key? key, -// this.roomId, -// this.startOpen = false, -// required this.setParentState, -// }) : super(key: key); - -// @override -// InviteStudentsFromClassState createState() => InviteStudentsFromClassState(); -// } - -// class InviteStudentsFromClassState extends State { -// late Room? room; -// late List otherSpaces; -// late bool isOpen; -// final List invitedSpaces = []; -// final List kickedSpaces = []; - -// InviteStudentsFromClassState({Key? key, cont}); - -// @override -// void initState() { -// room = widget.roomId != null -// ? Matrix.of(context).client.getRoomById(widget.roomId!) -// : null; - -// otherSpaces = Matrix.of(context) -// .client -// .rooms -// .where((r) => r.isPangeaClass && r.id != widget.roomId) -// .toList(); - -// isOpen = widget.startOpen; - -// super.initState(); -// } - -// Future inviteSpaceMembers(BuildContext context, Room spaceToInvite) => -// showFutureLoadingDialog( -// context: context, -// future: () async { -// if (room == null) { -// ErrorHandler.logError(m: 'Room is null in inviteSpaceMembers'); -// debugger(when: kDebugMode); -// return; -// } -// final List> existingMembers = await Future.wait([ -// room!.requestParticipants(), -// spaceToInvite.requestParticipants(), -// ]); -// final List roomMembers = existingMembers[0]; -// final List spaceMembers = existingMembers[1]; -// final List> inviteFutures = []; -// for (final spaceMember in spaceMembers -// .where((element) => element.id != room!.client.userID)) { -// if (!roomMembers.any((m) => -// m.id == spaceMember.id && m.membership == Membership.join)) { -// inviteFutures.add(inviteSpaceMember(spaceMember)); -// //add to invitedSpaces -// invitedSpaces.add(spaceToInvite.id); -// //if in kickedSpaces, remove -// kickedSpaces.remove(spaceToInvite.id); -// } else { -// debugPrint('User ${spaceMember.id} is already in the room'); -// } -// } -// await Future.wait(inviteFutures); -// debugPrint('Invited ${spaceMembers.length} members'); -// GoogleAnalytics.inviteClassToExchange(room!.id, spaceToInvite.id); -// // setState(() { -// // widget.setParentState(); -// // }); -// }, -// onError: handleError, -// ); - -// //function for kicking single student and haandling error -// Future kickSpaceMember(User spaceMember) async { -// try { -// await room!.kick(spaceMember.id); -// debugPrint('Kicked ${spaceMember.id}'); -// } catch (e) { -// debugger(when: kDebugMode); -// ErrorHandler.logError(e: e); -// } -// } - -// //function for adding single student and haandling error -// Future inviteSpaceMember(User spaceMember) async { -// try { -// await room!.invite(spaceMember.id); -// debugPrint('added ${spaceMember.id}'); -// } catch (e) { -// debugger(when: kDebugMode); -// ErrorHandler.logError(e: e); -// } -// } - -// Future kickSpaceMembers(BuildContext context, Room spaceToKick) => -// showFutureLoadingDialog( -// context: context, -// future: () async { -// if (room == null) { -// ErrorHandler.logError(m: 'Room is null in kickSpaceMembers'); -// debugger(when: kDebugMode); -// return; -// } -// final List> existingMembers = await Future.wait([ -// room!.requestParticipants(), -// spaceToKick.requestParticipants(), -// ]); -// final List roomMembers = existingMembers[0]; -// final List spaceMembers = existingMembers[1]; -// final List toKick = spaceMembers -// .where((element) => -// element.id != room!.client.userID && -// roomMembers.any((m) => m.id == element.id)) -// .toList(); -// //add to kickedSpaces -// kickedSpaces.add(spaceToKick.id); -// //if in invitedSpaces, remove from invitedSpaces -// invitedSpaces.remove(spaceToKick.id); -// final List> kickFutures = []; - -// for (final spaceMember in toKick) { -// kickFutures.add(kickSpaceMember(spaceMember)); -// } -// await Future.wait(kickFutures); -// GoogleAnalytics.kickClassFromExchange(room!.id, spaceToKick.id); -// setState(() { -// widget.setParentState(); -// }); -// return; -// }, -// onError: handleError, -// ); - -// String handleError(dynamic exception) { -// ErrorHandler.logError( -// e: exception, m: 'Error inviting or kicking students'); -// ScaffoldMessenger.of(context).showSnackBar( -// SnackBar( -// content: Text(exception.toString()), -// ), -// ); -// return exception.toString(); -// } - -// @override -// Widget build(BuildContext context) { -// if (room == null) return Container(); -// return Column( -// children: [ -// ListTile( -// title: Text( -// L10n.of(context)!.inviteStudentsFromOtherClasses, -// style: TextStyle( -// color: Theme.of(context).colorScheme.secondary, -// fontWeight: FontWeight.bold, -// ), -// ), -// leading: CircleAvatar( -// backgroundColor: Theme.of(context).primaryColor, -// foregroundColor: Colors.white, -// radius: Avatar.defaultSize / 2, -// child: const Icon(Icons.workspaces_outlined), -// ), -// trailing: Icon( -// isOpen -// ? Icons.keyboard_arrow_down_outlined -// : Icons.keyboard_arrow_right_outlined, -// ), -// onTap: () => setState(() => isOpen = !isOpen), -// ), -// if (isOpen) -// Padding( -// padding: const EdgeInsets.fromLTRB(16.0, 0, 16.0, 0), -// child: Column( -// children: [ -// if (otherSpaces.isEmpty) -// ListTile(title: Text(L10n.of(context)!.noEligibleSpaces)), -// ListView.builder( -// shrinkWrap: true, -// itemCount: otherSpaces.length, -// itemBuilder: (BuildContext context, int i) { -// final bool canIAddSpaceChildren = -// otherSpaces[i].canIAddSpaceChild(room); -// return Column( -// children: [ -// Opacity( -// opacity: canIAddSpaceChildren ? 1 : 0.5, -// child: InviteKickClass( -// room: otherSpaces[i], -// inviteCallback: (Room room) => -// inviteSpaceMembers(context, otherSpaces[i]), -// kickCallback: (Room room) => -// kickSpaceMembers(context, otherSpaces[i]), -// controller: this, -// ), -// ), -// ], -// ); -// }, -// ), -// ], -// ), -// ), -// // END: ed8c6549bwf9 -// ], -// ); -// } -// } - -// //listTile with two buttons - one to invite all students in the class and the other to kick them all out -// // parameters -// // 1. Room -// // 2. invite callback -// // 3. kick callback -// // when the user clicks either button, a dialog pops up asking for confirmation -// // after the dialog is confirmed, the callback is executed -// class InviteKickClass extends StatelessWidget { -// final Room room; -// final Function inviteCallback; -// final Function kickCallback; -// final InviteStudentsFromClassState controller; - -// const InviteKickClass({ -// Key? key, -// required this.room, -// required this.inviteCallback, -// required this.kickCallback, -// required this.controller, -// }) : super(key: key); - -// @override -// Widget build(BuildContext context) { -// return ListTile( -// title: Text( -// room.getLocalizedDisplayname( -// MatrixLocals(L10n.of(context)!), -// ), -// ), -// leading: const SizedBox( -// height: Avatar.defaultSize, -// width: Avatar.defaultSize, -// ), -// trailing: Row( -// mainAxisSize: MainAxisSize.min, -// children: [ -// IconButton( -// icon: const Icon(Icons.add), -// isSelected: controller.invitedSpaces.contains(room.id), -// onPressed: () async { -// final bool? result = await showDialog( -// context: context, -// builder: (BuildContext context) => AlertDialog( -// title: Text( -// L10n.of(context)!.inviteAllStudents, -// style: TextStyle( -// color: Theme.of(context).colorScheme.secondary, -// fontWeight: FontWeight.bold, -// ), -// ), -// content: Text( -// L10n.of(context)!.inviteAllStudentsConfirmation, -// ), -// actions: [ -// TextButton( -// onPressed: () => Navigator.of(context).pop(false), -// child: Text(L10n.of(context)!.cancel), -// ), -// TextButton( -// onPressed: () => Navigator.of(context).pop(true), -// child: Text(L10n.of(context)!.ok), -// ), -// ], -// ), -// ); -// if (result != null && result) { -// inviteCallback(room); -// } -// }, -// ), -// IconButton( -// icon: const Icon(Icons.remove), -// isSelected: controller.kickedSpaces.contains(room.id), -// onPressed: () async { -// final bool? result = await showDialog( -// context: context, -// builder: (BuildContext context) => AlertDialog( -// title: Text( -// L10n.of(context)!.kickAllStudents, -// style: TextStyle( -// color: Theme.of(context).colorScheme.secondary, -// fontWeight: FontWeight.bold, -// ), -// ), -// content: Text( -// L10n.of(context)!.kickAllStudentsConfirmation, -// ), -// actions: [ -// TextButton( -// onPressed: () => Navigator.of(context).pop(false), -// child: Text(L10n.of(context)!.cancel), -// ), -// TextButton( -// onPressed: () => Navigator.of(context).pop(true), -// child: Text(L10n.of(context)!.ok), -// ), -// ], -// ), -// ); -// if (result != null && result) { -// kickCallback(room); -// } -// }, -// ), -// ], -// ), -// ); -// } -// } diff --git a/lib/pangea/widgets/conversation_bot/conversation_bot_settings.dart b/lib/pangea/widgets/conversation_bot/conversation_bot_settings.dart index 1ed7b549d..506760d17 100644 --- a/lib/pangea/widgets/conversation_bot/conversation_bot_settings.dart +++ b/lib/pangea/widgets/conversation_bot/conversation_bot_settings.dart @@ -21,14 +21,12 @@ class ConversationBotSettings extends StatefulWidget { final Room? room; final bool startOpen; final String? activeSpaceId; - // final ClassSettingsModel? initialSettings; const ConversationBotSettings({ super.key, this.room, this.startOpen = false, this.activeSpaceId, - // this.initialSettings, }); @override @@ -56,9 +54,6 @@ class ConversationBotSettingsState extends State { parentSpace = widget.activeSpaceId != null ? Matrix.of(context).client.getRoomById(widget.activeSpaceId!) : null; - if (parentSpace != null && botOptions.languageLevel == null) { - botOptions.languageLevel = parentSpace?.classSettings?.languageLevel; - } } Future updateBotOption(void Function() makeLocalChange) async { diff --git a/lib/pangea/widgets/igc/pangea_rich_text.dart b/lib/pangea/widgets/igc/pangea_rich_text.dart index 262f37824..fe3f23f05 100644 --- a/lib/pangea/widgets/igc/pangea_rich_text.dart +++ b/lib/pangea/widgets/igc/pangea_rich_text.dart @@ -203,14 +203,7 @@ class PangeaRichTextState extends State { userL2LangCode != null && userL2LangCode != LanguageKeys.unknownLanguage; String? get userL2LangCode => - pangeaController.languageController.activeL2Code( - roomID: widget.pangeaMessageEvent.room.id, - ); - - String? get userL1LangCode => - pangeaController.languageController.activeL1Code( - roomID: widget.pangeaMessageEvent.room.id, - ); + pangeaController.languageController.activeL2Code(); Future onIgnore() async { debugPrint("PTODO implement onIgnore"); diff --git a/lib/pangea/widgets/igc/word_data_card.dart b/lib/pangea/widgets/igc/word_data_card.dart index 4b8445417..eae1c2b8d 100644 --- a/lib/pangea/widgets/igc/word_data_card.dart +++ b/lib/pangea/widgets/igc/word_data_card.dart @@ -61,8 +61,7 @@ class WordDataCardController extends State { @override void initState() { if (!mounted) return; - activeL1 = - controller.languageController.activeL1Model(roomID: widget.room.id)!; + activeL1 = controller.languageController.activeL1Model()!; activeL2 = controller.languageController.activeL2Model(roomID: widget.room.id)!; if (activeL1 == null || activeL2 == null) { diff --git a/lib/pangea/widgets/space/class_settings.dart b/lib/pangea/widgets/space/class_settings.dart deleted file mode 100644 index c9743befd..000000000 --- a/lib/pangea/widgets/space/class_settings.dart +++ /dev/null @@ -1,183 +0,0 @@ -import 'dart:developer'; - -import 'package:fluffychat/pangea/models/class_model.dart'; -import 'package:fluffychat/pangea/widgets/space/language_level_dropdown.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/l10n.dart'; -import 'package:future_loading_dialog/future_loading_dialog.dart'; -import 'package:matrix/matrix.dart'; - -import '../../../widgets/matrix.dart'; -import '../../constants/language_keys.dart'; -import '../../constants/pangea_event_types.dart'; -import '../../controllers/language_list_controller.dart'; -import '../../controllers/pangea_controller.dart'; -import '../../extensions/pangea_room_extension/pangea_room_extension.dart'; -import '../../models/language_model.dart'; -import '../../utils/error_handler.dart'; -import '../user_settings/p_language_dropdown.dart'; -import '../user_settings/p_question_container.dart'; - -class ClassSettings extends StatefulWidget { - final String? roomId; - final bool startOpen; - final ClassSettingsModel? initialSettings; - - const ClassSettings({ - super.key, - this.roomId, - this.startOpen = false, - this.initialSettings, - }); - - @override - ClassSettingsState createState() => ClassSettingsState(); -} - -class ClassSettingsState extends State { - Room? room; - late ClassSettingsModel classSettings; - late bool isOpen; - final PangeaController pangeaController = MatrixState.pangeaController; - - final cityController = TextEditingController(); - final countryController = TextEditingController(); - final schoolController = TextEditingController(); - - ClassSettingsState({Key? key}); - - @override - void initState() { - room = widget.roomId != null - ? Matrix.of(context).client.getRoomById(widget.roomId!) - : null; - - classSettings = - room?.classSettings ?? widget.initialSettings ?? ClassSettingsModel(); - - isOpen = widget.startOpen; - - super.initState(); - } - - bool get sameLanguages => - classSettings.targetLanguage == classSettings.dominantLanguage; - - LanguageModel getLanguage({required bool isBase, required String? langCode}) { - final LanguageModel backup = isBase - ? pangeaController.pLanguageStore.baseOptions.first - : pangeaController.pLanguageStore.targetOptions.first; - if (langCode == null) return backup; - final LanguageModel byCode = PangeaLanguage.byLangCode(langCode); - return byCode.langCode != LanguageKeys.unknownLanguage ? byCode : backup; - } - - Future updatePermission(void Function() makeLocalRuleChange) async { - makeLocalRuleChange(); - if (room != null) { - await showFutureLoadingDialog( - context: context, - future: () => setClassSettings(room!.id), - ); - } - setState(() {}); - } - - void setTextControllerValues() { - classSettings.city = cityController.text; - classSettings.country = countryController.text; - classSettings.schoolName = schoolController.text; - } - - Future setClassSettings(String roomId) async { - try { - setTextControllerValues(); - - await Matrix.of(context).client.setRoomStateWithKey( - roomId, - PangeaEventTypes.classSettings, - '', - classSettings.toJson(), - ); - } catch (err, stack) { - debugger(when: kDebugMode); - ErrorHandler.logError(e: err, s: stack); - } - } - - @override - Widget build(BuildContext context) => Column( - children: [ - ListTile( - title: Text( - L10n.of(context)!.classSettings, - style: TextStyle( - color: Theme.of(context).colorScheme.secondary, - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text(L10n.of(context)!.classSettingsDesc), - leading: CircleAvatar( - backgroundColor: Theme.of(context).scaffoldBackgroundColor, - foregroundColor: Theme.of(context).textTheme.bodyLarge!.color, - child: const Icon(Icons.language), - ), - trailing: Icon( - isOpen - ? Icons.keyboard_arrow_down_outlined - : Icons.keyboard_arrow_right_outlined, - ), - onTap: () => setState(() => isOpen = !isOpen), - ), - if (isOpen) - AnimatedContainer( - duration: const Duration(milliseconds: 300), - height: isOpen ? null : 0, - child: Padding( - padding: const EdgeInsets.fromLTRB(16.0, 0, 16.0, 8.0), - child: Column( - children: [ - PQuestionContainer( - title: L10n.of(context)!.selectClassRoomDominantLanguage, - ), - PLanguageDropdown( - onChange: (p0) => updatePermission(() { - classSettings.dominantLanguage = p0.langCode; - }), - initialLanguage: getLanguage( - isBase: true, - langCode: classSettings.dominantLanguage, - ), - languages: pangeaController.pLanguageStore.baseOptions, - showMultilingual: true, - ), - PQuestionContainer( - title: L10n.of(context)!.selectTargetLanguage, - ), - PLanguageDropdown( - onChange: (p0) => updatePermission(() { - classSettings.targetLanguage = p0.langCode; - }), - initialLanguage: getLanguage( - isBase: false, - langCode: classSettings.targetLanguage, - ), - languages: pangeaController.pLanguageStore.targetOptions, - ), - PQuestionContainer( - title: L10n.of(context)!.whatIsYourClassLanguageLevel, - ), - LanguageLevelDropdown( - initialLevel: classSettings.languageLevel, - onChanged: (int? newValue) => updatePermission(() { - classSettings.languageLevel = newValue!; - }), - ), - ], - ), - ), - ), - ], - ); -} diff --git a/lib/pangea/widgets/space/language_settings.dart b/lib/pangea/widgets/space/language_settings.dart new file mode 100644 index 000000000..97af7b34d --- /dev/null +++ b/lib/pangea/widgets/space/language_settings.dart @@ -0,0 +1,184 @@ +// import 'dart:developer'; + +// import 'package:fluffychat/pangea/models/space_model.dart'; +// import 'package:fluffychat/pangea/widgets/space/language_level_dropdown.dart'; +// import 'package:flutter/foundation.dart'; +// import 'package:flutter/material.dart'; +// import 'package:flutter_gen/gen_l10n/l10n.dart'; +// import 'package:future_loading_dialog/future_loading_dialog.dart'; +// import 'package:matrix/matrix.dart'; + +// import '../../../widgets/matrix.dart'; +// import '../../constants/language_keys.dart'; +// import '../../constants/pangea_event_types.dart'; +// import '../../controllers/language_list_controller.dart'; +// import '../../controllers/pangea_controller.dart'; +// import '../../extensions/pangea_room_extension/pangea_room_extension.dart'; +// import '../../models/language_model.dart'; +// import '../../utils/error_handler.dart'; +// import '../user_settings/p_language_dropdown.dart'; +// import '../user_settings/p_question_container.dart'; + +// class LanguageSettings extends StatefulWidget { +// final String? roomId; +// final bool startOpen; +// final LanguageSettingsModel? initialSettings; + +// const LanguageSettings({ +// super.key, +// this.roomId, +// this.startOpen = false, +// this.initialSettings, +// }); + +// @override +// LanguageSettingsState createState() => LanguageSettingsState(); +// } + +// class LanguageSettingsState extends State { +// Room? room; +// late LanguageSettingsModel languageSettings; +// late bool isOpen; +// final PangeaController pangeaController = MatrixState.pangeaController; + +// final cityController = TextEditingController(); +// final countryController = TextEditingController(); +// final schoolController = TextEditingController(); + +// LanguageSettingsState({Key? key}); + +// @override +// void initState() { +// room = widget.roomId != null +// ? Matrix.of(context).client.getRoomById(widget.roomId!) +// : null; + +// languageSettings = room?.languageSettings ?? +// widget.initialSettings ?? +// LanguageSettingsModel(); + +// isOpen = widget.startOpen; + +// super.initState(); +// } + +// bool get sameLanguages => +// languageSettings.targetLanguage == languageSettings.dominantLanguage; + +// LanguageModel getLanguage({required bool isBase, required String? langCode}) { +// final LanguageModel backup = isBase +// ? pangeaController.pLanguageStore.baseOptions.first +// : pangeaController.pLanguageStore.targetOptions.first; +// if (langCode == null) return backup; +// final LanguageModel byCode = PangeaLanguage.byLangCode(langCode); +// return byCode.langCode != LanguageKeys.unknownLanguage ? byCode : backup; +// } + +// Future updatePermission(void Function() makeLocalRuleChange) async { +// makeLocalRuleChange(); +// if (room != null) { +// await showFutureLoadingDialog( +// context: context, +// future: () => setLanguageSettings(room!.id), +// ); +// } +// setState(() {}); +// } + +// void setTextControllerValues() { +// languageSettings.city = cityController.text; +// languageSettings.country = countryController.text; +// languageSettings.schoolName = schoolController.text; +// } + +// Future setLanguageSettings(String roomId) async { +// try { +// setTextControllerValues(); + +// await Matrix.of(context).client.setRoomStateWithKey( +// roomId, +// PangeaEventTypes.languageSettings, +// '', +// languageSettings.toJson(), +// ); +// } catch (err, stack) { +// debugger(when: kDebugMode); +// ErrorHandler.logError(e: err, s: stack); +// } +// } + +// @override +// Widget build(BuildContext context) => Column( +// children: [ +// ListTile( +// title: Text( +// L10n.of(context)!.languageSettings, +// style: TextStyle( +// color: Theme.of(context).colorScheme.secondary, +// fontWeight: FontWeight.bold, +// ), +// ), +// subtitle: Text(L10n.of(context)!.languageSettingsDesc), +// leading: CircleAvatar( +// backgroundColor: Theme.of(context).scaffoldBackgroundColor, +// foregroundColor: Theme.of(context).textTheme.bodyLarge!.color, +// child: const Icon(Icons.language), +// ), +// trailing: Icon( +// isOpen +// ? Icons.keyboard_arrow_down_outlined +// : Icons.keyboard_arrow_right_outlined, +// ), +// onTap: () => setState(() => isOpen = !isOpen), +// ), +// if (isOpen) +// AnimatedContainer( +// duration: const Duration(milliseconds: 300), +// height: isOpen ? null : 0, +// child: Padding( +// padding: const EdgeInsets.fromLTRB(16.0, 0, 16.0, 8.0), +// child: Column( +// children: [ +// PQuestionContainer( +// title: L10n.of(context)!.selectSpaceDominantLanguage, +// ), +// PLanguageDropdown( +// onChange: (p0) => updatePermission(() { +// languageSettings.dominantLanguage = p0.langCode; +// }), +// initialLanguage: getLanguage( +// isBase: true, +// langCode: languageSettings.dominantLanguage, +// ), +// languages: pangeaController.pLanguageStore.baseOptions, +// showMultilingual: true, +// ), +// PQuestionContainer( +// title: L10n.of(context)!.selectSpaceTargetLanguage, +// ), +// PLanguageDropdown( +// onChange: (p0) => updatePermission(() { +// languageSettings.targetLanguage = p0.langCode; +// }), +// initialLanguage: getLanguage( +// isBase: false, +// langCode: languageSettings.targetLanguage, +// ), +// languages: pangeaController.pLanguageStore.targetOptions, +// ), +// PQuestionContainer( +// title: L10n.of(context)!.whatIsYourSpaceLanguageLevel, +// ), +// LanguageLevelDropdown( +// initialLevel: languageSettings.languageLevel, +// onChanged: (int? newValue) => updatePermission(() { +// languageSettings.languageLevel = newValue!; +// }), +// ), +// ], +// ), +// ), +// ), +// ], +// ); +// } diff --git a/lib/utils/client_manager.dart b/lib/utils/client_manager.dart index 1422c0dc1..1d5e1fe66 100644 --- a/lib/utils/client_manager.dart +++ b/lib/utils/client_manager.dart @@ -111,7 +111,7 @@ abstract class ClientManager { // To make room emotes work 'im.ponies.room_emotes', // #Pangea - PangeaEventTypes.classSettings, + PangeaEventTypes.languageSettings, PangeaEventTypes.rules, PangeaEventTypes.botOptions, EventTypes.RoomTopic, diff --git a/lib/widgets/chat_settings_popup_menu.dart b/lib/widgets/chat_settings_popup_menu.dart index e9bcf6bd8..bd4cf0a66 100644 --- a/lib/widgets/chat_settings_popup_menu.dart +++ b/lib/widgets/chat_settings_popup_menu.dart @@ -1,9 +1,7 @@ import 'dart:async'; import 'package:adaptive_dialog/adaptive_dialog.dart'; -import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; -import 'package:fluffychat/pangea/models/class_model.dart'; import 'package:fluffychat/pangea/utils/download_chat.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -49,13 +47,6 @@ class ChatSettingsPopupMenuState extends State { @override Widget build(BuildContext context) { - // #Pangea - final PangeaController pangeaController = MatrixState.pangeaController; - final ClassSettingsModel? classSettings = pangeaController - .matrixState.client - .getRoomById(widget.room.id) - ?.firstLanguageSettings; - // Pangea# notificationChangeSub ??= Matrix.of(context) .client .onSync @@ -151,7 +142,6 @@ class ChatSettingsPopupMenuState extends State { context: context, future: () => downloadChat( widget.room, - classSettings!, DownloadType.txt, Matrix.of(context).client, context, @@ -163,7 +153,6 @@ class ChatSettingsPopupMenuState extends State { context: context, future: () => downloadChat( widget.room, - classSettings!, DownloadType.csv, Matrix.of(context).client, context, @@ -175,7 +164,6 @@ class ChatSettingsPopupMenuState extends State { context: context, future: () => downloadChat( widget.room, - classSettings!, DownloadType.xlsx, Matrix.of(context).client, context, @@ -189,6 +177,17 @@ class ChatSettingsPopupMenuState extends State { } }, itemBuilder: (BuildContext context) => [ + if (widget.displayChatDetails) + PopupMenuItem( + value: ChatPopupMenuActions.details, + child: Row( + children: [ + const Icon(Icons.info_outline_rounded), + const SizedBox(width: 12), + Text(L10n.of(context)!.chatDetails), + ], + ), + ), // #Pangea PopupMenuItem( value: ChatPopupMenuActions.learningSettings, @@ -201,17 +200,6 @@ class ChatSettingsPopupMenuState extends State { ), ), // Pangea# - if (widget.displayChatDetails) - PopupMenuItem( - value: ChatPopupMenuActions.details, - child: Row( - children: [ - const Icon(Icons.info_outline_rounded), - const SizedBox(width: 12), - Text(L10n.of(context)!.chatDetails), - ], - ), - ), if (widget.room.pushRuleState == PushRuleState.notify) PopupMenuItem( value: ChatPopupMenuActions.mute, @@ -270,39 +258,36 @@ class ChatSettingsPopupMenuState extends State { ], ), ), - if (classSettings != null) - PopupMenuItem( - value: ChatPopupMenuActions.downloadTxt, - child: Row( - children: [ - const Icon(Icons.download_outlined), - const SizedBox(width: 12), - Text(L10n.of(context)!.downloadTxtFile), - ], - ), + PopupMenuItem( + value: ChatPopupMenuActions.downloadTxt, + child: Row( + children: [ + const Icon(Icons.download_outlined), + const SizedBox(width: 12), + Text(L10n.of(context)!.downloadTxtFile), + ], ), - if (classSettings != null) - PopupMenuItem( - value: ChatPopupMenuActions.downloadCsv, - child: Row( - children: [ - const Icon(Icons.download_outlined), - const SizedBox(width: 12), - Text(L10n.of(context)!.downloadCSVFile), - ], - ), + ), + PopupMenuItem( + value: ChatPopupMenuActions.downloadCsv, + child: Row( + children: [ + const Icon(Icons.download_outlined), + const SizedBox(width: 12), + Text(L10n.of(context)!.downloadCSVFile), + ], ), - if (classSettings != null) - PopupMenuItem( - value: ChatPopupMenuActions.downloadXlsx, - child: Row( - children: [ - const Icon(Icons.download_outlined), - const SizedBox(width: 12), - Text(L10n.of(context)!.downloadXLSXFile), - ], - ), + ), + PopupMenuItem( + value: ChatPopupMenuActions.downloadXlsx, + child: Row( + children: [ + const Icon(Icons.download_outlined), + const SizedBox(width: 12), + Text(L10n.of(context)!.downloadXLSXFile), + ], ), + ), // Pangea# ], ), diff --git a/needed-translations.txt b/needed-translations.txt index 359b812db..928fa5052 100644 --- a/needed-translations.txt +++ b/needed-translations.txt @@ -5,7 +5,6 @@ "addNewFriend", "alreadyHaveAnAccount", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "enterAGroupName", @@ -53,8 +52,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -66,19 +64,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -243,16 +239,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -706,11 +699,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -729,7 +719,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -754,13 +744,11 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", "noTeachersFound", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -857,7 +845,14 @@ "capacitySetTooLow", "roomCapacityExplanation", "enterNumber", - "buildTranslation" + "buildTranslation", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "be": [ @@ -944,7 +939,6 @@ "chatDetails", "chatHasBeenAddedToThisSpace", "chats", - "classes", "chooseAStrongPassword", "clearArchive", "close", @@ -987,7 +981,6 @@ "create", "createdTheChat", "createGroup", - "createNewSpace", "createNewGroup", "currentlyActive", "darkTheme", @@ -1469,8 +1462,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -1482,19 +1474,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -1659,16 +1649,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -2122,11 +2109,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -2145,7 +2129,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -2170,7 +2154,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -2191,7 +2174,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -2351,7 +2333,15 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "createNewSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "bn": [ @@ -2434,7 +2424,6 @@ "chatDetails", "chatHasBeenAddedToThisSpace", "chats", - "classes", "chooseAStrongPassword", "clearArchive", "close", @@ -2477,7 +2466,6 @@ "create", "createdTheChat", "createGroup", - "createNewSpace", "createNewGroup", "currentlyActive", "darkTheme", @@ -2959,8 +2947,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -2972,19 +2959,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -3149,16 +3134,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -3612,11 +3594,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -3635,7 +3614,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -3660,7 +3639,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -3681,7 +3659,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -3841,7 +3818,15 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "createNewSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "bo": [ @@ -3928,7 +3913,6 @@ "chatDetails", "chatHasBeenAddedToThisSpace", "chats", - "classes", "chooseAStrongPassword", "clearArchive", "close", @@ -3971,7 +3955,6 @@ "create", "createdTheChat", "createGroup", - "createNewSpace", "createNewGroup", "currentlyActive", "darkTheme", @@ -4453,8 +4436,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -4466,19 +4448,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -4643,16 +4623,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -5106,11 +5083,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -5129,7 +5103,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -5154,7 +5128,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -5175,7 +5148,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -5335,7 +5307,15 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "createNewSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "ca": [ @@ -5345,7 +5325,6 @@ "alreadyHaveAnAccount", "appLockDescription", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "globalChatId", @@ -5412,8 +5391,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -5425,19 +5403,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -5602,16 +5578,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -6065,11 +6038,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -6088,7 +6058,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -6113,7 +6083,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -6121,7 +6090,6 @@ "knocking", "chatCanBeDiscoveredViaSearchOnServer", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -6231,7 +6199,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "cs": [ @@ -6240,7 +6215,6 @@ "addNewFriend", "alreadyHaveAnAccount", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "enterAGroupName", @@ -6327,8 +6301,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -6340,19 +6313,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -6517,16 +6488,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -6980,11 +6948,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -7003,7 +6968,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -7028,7 +6993,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -7049,7 +7013,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -7209,7 +7172,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "de": [ @@ -7218,7 +7188,6 @@ "addNewFriend", "alreadyHaveAnAccount", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "enterAGroupName", @@ -7266,8 +7235,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -7279,19 +7247,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -7456,16 +7422,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -7919,11 +7882,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -7942,7 +7902,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -7967,13 +7927,11 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", "noTeachersFound", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -8070,7 +8028,14 @@ "capacitySetTooLow", "roomCapacityExplanation", "enterNumber", - "buildTranslation" + "buildTranslation", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "el": [ @@ -8108,7 +8073,6 @@ "chatDetails", "chatHasBeenAddedToThisSpace", "chats", - "classes", "chooseAStrongPassword", "clearArchive", "close", @@ -8151,7 +8115,6 @@ "create", "createdTheChat", "createGroup", - "createNewSpace", "createNewGroup", "currentlyActive", "darkTheme", @@ -8633,8 +8596,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -8646,19 +8608,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -8823,16 +8783,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -9286,11 +9243,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -9309,7 +9263,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -9334,7 +9288,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -9355,7 +9308,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -9515,7 +9467,15 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "createNewSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "eo": [ @@ -9542,7 +9502,6 @@ "sendTypingNotifications", "swipeRightToLeftToReply", "yourChatBackupHasBeenSetUp", - "classes", "commandHint_markasdm", "commandHint_markasgroup", "commandHint_clearcache", @@ -9776,8 +9735,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -9789,19 +9747,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -9966,16 +9922,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -10429,11 +10382,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -10452,7 +10402,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -10477,7 +10427,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -10498,7 +10447,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -10658,45 +10606,18 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "es": [ - "appLockDescription", - "swipeRightToLeftToReply", - "globalChatId", - "accessAndVisibility", - "accessAndVisibilityDescription", - "calls", - "customEmojisAndStickers", - "customEmojisAndStickersBody", - "hideRedactedMessages", - "hideRedactedMessagesBody", - "hideInvalidOrUnknownMessageFormats", - "hideMemberChangesInPublicChats", - "hideMemberChangesInPublicChatsBody", - "overview", - "notifyMeFor", - "passwordRecoverySettings", - "usersMustKnock", - "userWouldLikeToChangeTheChat", - "noPublicLinkHasBeenCreatedYet", - "knock", - "knocking", - "chatCanBeDiscoveredViaSearchOnServer", - "publicChatAddresses", - "createNewAddress", - "userRole", - "minimumPowerLevel", - "searchIn", - "searchMore", - "gallery", - "files", - "addSpaceToSpaceDescription", - "noDatabaseEncryption", - "thereAreCountUsersBlocked", - "restricted", - "knockRestricted" + "searchIn" ], "et": [ @@ -10705,7 +10626,6 @@ "addNewFriend", "alreadyHaveAnAccount", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "enterAGroupName", @@ -10753,8 +10673,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -10766,19 +10685,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -10943,16 +10860,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -11406,11 +11320,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -11429,7 +11340,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -11454,13 +11365,11 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", "noTeachersFound", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -11557,7 +11466,14 @@ "capacitySetTooLow", "roomCapacityExplanation", "enterNumber", - "buildTranslation" + "buildTranslation", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "eu": [ @@ -11566,7 +11482,6 @@ "addNewFriend", "alreadyHaveAnAccount", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "enterAGroupName", @@ -11614,8 +11529,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -11627,19 +11541,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -11804,16 +11716,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -12267,11 +12176,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -12290,7 +12196,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -12315,13 +12221,11 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", "noTeachersFound", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -12420,7 +12324,14 @@ "enterNumber", "buildTranslation", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "fa": [ @@ -12436,7 +12347,6 @@ "appLockDescription", "sendTypingNotifications", "swipeRightToLeftToReply", - "classes", "createGroup", "createNewGroup", "chatPermissions", @@ -12538,8 +12448,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -12551,19 +12460,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -12728,16 +12635,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -13191,11 +13095,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -13214,7 +13115,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -13239,7 +13140,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -13260,7 +13160,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -13420,7 +13319,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "fi": [ @@ -13430,7 +13336,6 @@ "alreadyHaveAnAccount", "appLockDescription", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "globalChatId", @@ -13505,8 +13410,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -13518,19 +13422,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -13695,16 +13597,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -14158,11 +14057,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -14181,7 +14077,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -14206,7 +14102,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -14224,7 +14119,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -14384,7 +14278,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "fil": [ @@ -14393,7 +14294,6 @@ "addNewFriend", "alreadyHaveAnAccount", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "enterAGroupName", @@ -14822,8 +14722,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -14835,19 +14734,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -15012,16 +14909,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -15475,11 +15369,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -15498,7 +15389,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -15523,7 +15414,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -15544,7 +15434,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -15704,7 +15593,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "fr": [ @@ -15717,7 +15613,6 @@ "appLockDescription", "sendTypingNotifications", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "emoteKeyboardNoRecents", @@ -15821,8 +15716,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -15834,19 +15728,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -16011,16 +15903,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -16474,11 +16363,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -16497,7 +16383,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -16522,7 +16408,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -16543,7 +16428,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -16703,7 +16587,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "ga": [ @@ -16728,7 +16619,6 @@ "appLockDescription", "sendTypingNotifications", "swipeRightToLeftToReply", - "classes", "commandHint_markasdm", "commandHint_markasgroup", "commandHint_discardsession", @@ -16949,8 +16839,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -16962,19 +16851,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -17139,16 +17026,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -17602,11 +17486,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -17625,7 +17506,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -17650,7 +17531,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -17671,7 +17551,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -17831,7 +17710,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "gl": [ @@ -17840,7 +17726,6 @@ "addNewFriend", "alreadyHaveAnAccount", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "enterAGroupName", @@ -17888,8 +17773,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -17901,19 +17785,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -18078,16 +17960,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -18541,11 +18420,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -18564,7 +18440,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -18589,13 +18465,11 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", "noTeachersFound", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -18692,7 +18566,14 @@ "capacitySetTooLow", "roomCapacityExplanation", "enterNumber", - "buildTranslation" + "buildTranslation", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "he": [ @@ -18718,7 +18599,6 @@ "appLockDescription", "sendTypingNotifications", "swipeRightToLeftToReply", - "classes", "commandHint_markasdm", "commandHint_markasgroup", "createGroup", @@ -19057,8 +18937,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -19070,19 +18949,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -19247,16 +19124,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -19710,11 +19584,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -19733,7 +19604,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -19758,7 +19629,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -19779,7 +19649,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -19939,7 +19808,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "hi": [ @@ -20019,7 +19895,6 @@ "chatDetails", "chatHasBeenAddedToThisSpace", "chats", - "classes", "chooseAStrongPassword", "clearArchive", "close", @@ -20062,7 +19937,6 @@ "create", "createdTheChat", "createGroup", - "createNewSpace", "createNewGroup", "currentlyActive", "darkTheme", @@ -20544,8 +20418,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -20557,19 +20430,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -20734,16 +20605,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -21197,11 +21065,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -21220,7 +21085,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -21245,7 +21110,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -21266,7 +21130,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -21426,7 +21289,15 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "createNewSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "hr": [ @@ -21436,7 +21307,6 @@ "alreadyHaveAnAccount", "appLockDescription", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "globalChatId", @@ -21504,8 +21374,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -21517,19 +21386,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -21694,16 +21561,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -22157,11 +22021,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -22180,7 +22041,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -22205,7 +22066,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -22221,7 +22081,6 @@ "knocking", "chatCanBeDiscoveredViaSearchOnServer", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -22366,7 +22225,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "hu": [ @@ -22375,7 +22241,6 @@ "addNewFriend", "alreadyHaveAnAccount", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "globalChatId", @@ -22429,8 +22294,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -22442,19 +22306,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -22619,16 +22481,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -23082,11 +22941,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -23105,7 +22961,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -23130,14 +22986,12 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", "noTeachersFound", "knocking", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -23243,7 +23097,14 @@ "enterNumber", "buildTranslation", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "ia": [ @@ -23316,7 +23177,6 @@ "chatDetails", "chatHasBeenAddedToThisSpace", "chats", - "classes", "chooseAStrongPassword", "clearArchive", "close", @@ -23359,7 +23219,6 @@ "create", "createdTheChat", "createGroup", - "createNewSpace", "createNewGroup", "currentlyActive", "darkTheme", @@ -23841,8 +23700,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -23854,19 +23712,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -24031,16 +23887,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -24494,11 +24347,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -24517,7 +24367,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -24542,7 +24392,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -24563,7 +24412,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -24723,7 +24571,15 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "createNewSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "id": [ @@ -24732,7 +24588,6 @@ "addNewFriend", "alreadyHaveAnAccount", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "enterAGroupName", @@ -24780,8 +24635,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -24793,19 +24647,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -24970,16 +24822,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -25433,11 +25282,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -25456,7 +25302,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -25481,13 +25327,11 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", "noTeachersFound", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -25590,7 +25434,14 @@ "enterNumber", "buildTranslation", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "ie": [ @@ -25637,7 +25488,6 @@ "yourChatBackupHasBeenSetUp", "chatBackupDescription", "chatHasBeenAddedToThisSpace", - "classes", "chooseAStrongPassword", "commandHint_markasdm", "commandHint_ban", @@ -25959,8 +25809,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -25972,19 +25821,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -26149,16 +25996,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -26612,11 +26456,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -26635,7 +26476,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -26660,7 +26501,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -26681,7 +26521,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -26841,7 +26680,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "it": [ @@ -26851,7 +26697,6 @@ "alreadyHaveAnAccount", "appLockDescription", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "globalChatId", @@ -26919,8 +26764,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -26932,19 +26776,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -27109,16 +26951,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -27572,11 +27411,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -27595,7 +27431,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -27620,7 +27456,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -27628,7 +27463,6 @@ "knocking", "chatCanBeDiscoveredViaSearchOnServer", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -27759,7 +27593,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "ja": [ @@ -27781,7 +27622,6 @@ "appLockDescription", "sendTypingNotifications", "swipeRightToLeftToReply", - "classes", "commandHint_kick", "commandHint_me", "commandHint_op", @@ -27906,8 +27746,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -27919,19 +27758,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -28096,16 +27933,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -28559,11 +28393,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -28582,7 +28413,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -28607,7 +28438,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -28628,7 +28458,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -28788,7 +28617,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "ka": [ @@ -28798,7 +28634,6 @@ "alreadyHaveAnAccount", "swipeRightToLeftToReply", "badServerVersionsException", - "classes", "commandHint_markasdm", "createNewGroup", "editChatPermissions", @@ -29254,8 +29089,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -29267,19 +29101,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -29444,16 +29276,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -29907,11 +29736,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -29930,7 +29756,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -29955,7 +29781,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -29976,7 +29801,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -30136,7 +29960,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "ko": [ @@ -30145,7 +29976,6 @@ "addNewFriend", "alreadyHaveAnAccount", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "enterAGroupName", @@ -30193,8 +30023,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -30206,19 +30035,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -30383,16 +30210,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -30846,11 +30670,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -30869,7 +30690,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -30894,13 +30715,11 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", "noTeachersFound", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -30999,7 +30818,14 @@ "enterNumber", "buildTranslation", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "lt": [ @@ -31022,7 +30848,6 @@ "appLockDescription", "sendTypingNotifications", "swipeRightToLeftToReply", - "classes", "createGroup", "createNewGroup", "allRooms", @@ -31146,8 +30971,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -31159,19 +30983,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -31336,16 +31158,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -31799,11 +31618,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -31822,7 +31638,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -31847,7 +31663,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -31868,7 +31683,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -32028,7 +31842,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "lv": [ @@ -32037,7 +31858,6 @@ "addNewFriend", "alreadyHaveAnAccount", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "enterAGroupName", @@ -32087,8 +31907,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -32100,19 +31919,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -32277,16 +32094,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -32740,11 +32554,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -32763,7 +32574,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -32788,13 +32599,11 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", "noTeachersFound", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -32897,7 +32706,14 @@ "enterNumber", "buildTranslation", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "nb": [ @@ -32924,7 +32740,6 @@ "yourChatBackupHasBeenSetUp", "chatHasBeenAddedToThisSpace", "chats", - "classes", "clearArchive", "commandHint_markasdm", "commandHint_markasgroup", @@ -32949,7 +32764,6 @@ "commandInvalid", "commandMissing", "createGroup", - "createNewSpace", "createNewGroup", "allRooms", "chatPermissions", @@ -33208,8 +33022,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -33221,19 +33034,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -33398,16 +33209,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -33861,11 +33669,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -33884,7 +33689,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -33909,7 +33714,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -33930,7 +33734,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -34090,7 +33893,15 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "createNewSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "nl": [ @@ -34100,7 +33911,6 @@ "alreadyHaveAnAccount", "appLockDescription", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "globalChatId", @@ -34175,8 +33985,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -34188,19 +33997,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -34365,16 +34172,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -34828,11 +34632,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -34851,7 +34652,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -34876,7 +34677,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -34887,7 +34687,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -35047,7 +34846,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "pl": [ @@ -35057,7 +34863,6 @@ "alreadyHaveAnAccount", "appLockDescription", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "globalChatId", @@ -35131,8 +34936,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -35144,19 +34948,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -35321,16 +35123,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -35784,11 +35583,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -35807,7 +35603,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -35832,7 +35628,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -35853,7 +35648,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -36013,7 +35807,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "pt": [ @@ -36095,7 +35896,6 @@ "chatDetails", "chatHasBeenAddedToThisSpace", "chats", - "classes", "chooseAStrongPassword", "clearArchive", "commandHint_markasdm", @@ -36136,7 +35936,6 @@ "create", "createdTheChat", "createGroup", - "createNewSpace", "createNewGroup", "currentlyActive", "darkTheme", @@ -36603,8 +36402,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -36616,19 +36414,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -36793,16 +36589,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -37256,11 +37049,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -37279,7 +37069,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -37304,7 +37094,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -37325,7 +37114,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -37485,7 +37273,15 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "createNewSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "pt_BR": [ @@ -37494,7 +37290,6 @@ "addNewFriend", "alreadyHaveAnAccount", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "enterAGroupName", @@ -37542,8 +37337,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -37555,19 +37349,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -37732,16 +37524,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -38195,11 +37984,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -38218,7 +38004,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -38243,13 +38029,11 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", "noTeachersFound", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -38352,7 +38136,14 @@ "enterNumber", "buildTranslation", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "pt_PT": [ @@ -38377,7 +38168,6 @@ "appLockDescription", "sendTypingNotifications", "swipeRightToLeftToReply", - "classes", "commandHint_markasdm", "commandHint_markasgroup", "createGroup", @@ -38664,8 +38454,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -38677,19 +38466,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -38854,16 +38641,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -39317,11 +39101,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -39340,7 +39121,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -39365,7 +39146,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -39386,7 +39166,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -39546,7 +39325,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "ro": [ @@ -39563,7 +39349,6 @@ "appLockDescription", "sendTypingNotifications", "swipeRightToLeftToReply", - "classes", "createGroup", "createNewGroup", "chatPermissions", @@ -39665,8 +39450,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -39678,19 +39462,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -39855,16 +39637,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -40318,11 +40097,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -40341,7 +40117,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -40366,7 +40142,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -40387,7 +40162,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -40547,7 +40321,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "ru": [ @@ -40556,7 +40337,6 @@ "addNewFriend", "alreadyHaveAnAccount", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "enterAGroupName", @@ -40604,8 +40384,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -40617,19 +40396,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -40794,16 +40571,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -41257,11 +41031,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -41280,7 +41051,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -41305,13 +41076,11 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", "noTeachersFound", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -41414,7 +41183,14 @@ "enterNumber", "buildTranslation", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "sk": [ @@ -41444,7 +41220,6 @@ "changeYourAvatar", "chatBackupDescription", "chatHasBeenAddedToThisSpace", - "classes", "clearArchive", "commandHint_markasdm", "commandHint_markasgroup", @@ -41474,7 +41249,6 @@ "contentHasBeenReported", "copyToClipboard", "createGroup", - "createNewSpace", "createNewGroup", "deactivateAccountWarning", "defaultPermissionLevel", @@ -41792,8 +41566,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -41805,19 +41578,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -41982,16 +41753,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -42445,11 +42213,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -42468,7 +42233,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -42493,7 +42258,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -42514,7 +42278,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -42674,7 +42437,15 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "createNewSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "sl": [ @@ -42700,7 +42471,6 @@ "appLockDescription", "sendTypingNotifications", "swipeRightToLeftToReply", - "classes", "commandHint_markasdm", "commandHint_markasgroup", "commandHint_clearcache", @@ -43182,8 +42952,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -43195,19 +42964,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -43372,16 +43139,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -43835,11 +43599,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -43858,7 +43619,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -43883,7 +43644,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -43904,7 +43664,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -44064,7 +43823,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "sr": [ @@ -44097,7 +43863,6 @@ "cantOpenUri", "yourChatBackupHasBeenSetUp", "chatHasBeenAddedToThisSpace", - "classes", "commandHint_markasdm", "commandHint_markasgroup", "commandHint_clearcache", @@ -44108,7 +43873,6 @@ "commandInvalid", "commandMissing", "createGroup", - "createNewSpace", "createNewGroup", "allRooms", "chatPermissions", @@ -44346,8 +44110,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -44359,19 +44122,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -44536,16 +44297,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -44999,11 +44757,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -45022,7 +44777,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -45047,7 +44802,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -45068,7 +44822,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -45228,7 +44981,15 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "createNewSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "sv": [ @@ -45238,7 +44999,6 @@ "alreadyHaveAnAccount", "appLockDescription", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "globalChatId", @@ -45305,8 +45065,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -45318,19 +45077,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -45495,16 +45252,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -45958,11 +45712,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -45981,7 +45732,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -46006,7 +45757,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -46014,7 +45764,6 @@ "knocking", "chatCanBeDiscoveredViaSearchOnServer", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -46126,7 +45875,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "ta": [ @@ -46210,7 +45966,6 @@ "chatDetails", "chatHasBeenAddedToThisSpace", "chats", - "classes", "chooseAStrongPassword", "clearArchive", "close", @@ -46253,7 +46008,6 @@ "create", "createdTheChat", "createGroup", - "createNewSpace", "createNewGroup", "currentlyActive", "darkTheme", @@ -46735,8 +46489,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -46748,19 +46501,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -46925,16 +46676,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -47388,11 +47136,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -47411,7 +47156,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -47436,7 +47181,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -47457,7 +47201,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -47617,7 +47360,15 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "createNewSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "th": [ @@ -47662,7 +47413,6 @@ "chatBackupDescription", "chatHasBeenAddedToThisSpace", "chats", - "classes", "chooseAStrongPassword", "clearArchive", "close", @@ -47704,7 +47454,6 @@ "create", "createdTheChat", "createGroup", - "createNewSpace", "createNewGroup", "currentlyActive", "darkTheme", @@ -48180,8 +47929,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -48193,19 +47941,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -48370,16 +48116,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -48833,11 +48576,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -48856,7 +48596,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -48881,7 +48621,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -48902,7 +48641,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -49062,7 +48800,15 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "createNewSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "tr": [ @@ -49071,7 +48817,6 @@ "addNewFriend", "alreadyHaveAnAccount", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "enterAGroupName", @@ -49119,8 +48864,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -49132,19 +48876,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -49309,16 +49051,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -49772,11 +49511,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -49795,7 +49531,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -49820,13 +49556,11 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", "noTeachersFound", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -49923,7 +49657,14 @@ "capacitySetTooLow", "roomCapacityExplanation", "enterNumber", - "buildTranslation" + "buildTranslation", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "uk": [ @@ -49933,7 +49674,6 @@ "alreadyHaveAnAccount", "appLockDescription", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "globalChatId", @@ -50000,8 +49740,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -50013,19 +49752,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -50190,16 +49927,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -50653,11 +50387,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -50676,7 +50407,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -50701,7 +50432,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -50709,7 +50439,6 @@ "knocking", "chatCanBeDiscoveredViaSearchOnServer", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -50821,7 +50550,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "vi": [ @@ -50853,7 +50589,6 @@ "yourChatBackupHasBeenSetUp", "chatHasBeenAddedToThisSpace", "chats", - "classes", "clearArchive", "commandHint_markasdm", "commandHint_markasgroup", @@ -50884,7 +50619,6 @@ "copiedToClipboard", "copyToClipboard", "createGroup", - "createNewSpace", "createNewGroup", "darkTheme", "defaultPermissionLevel", @@ -51302,8 +51036,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -51315,19 +51048,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -51492,16 +51223,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -51955,11 +51683,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -51978,7 +51703,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -52003,7 +51728,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -52013,7 +51737,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -52167,7 +51890,15 @@ "buildTranslation", "noDatabaseEncryption", "thereAreCountUsersBlocked", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "createNewSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "zh": [ @@ -52176,7 +51907,6 @@ "addNewFriend", "alreadyHaveAnAccount", "swipeRightToLeftToReply", - "classes", "createNewGroup", "editChatPermissions", "enterAGroupName", @@ -52224,8 +51954,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -52237,19 +51966,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -52414,16 +52141,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -52877,11 +52601,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -52900,7 +52621,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -52925,13 +52646,11 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", "noTeachersFound", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -53028,7 +52747,14 @@ "capacitySetTooLow", "roomCapacityExplanation", "enterNumber", - "buildTranslation" + "buildTranslation", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ], "zh_Hant": [ @@ -53038,7 +52764,6 @@ "alreadyHaveAnAccount", "appLockDescription", "swipeRightToLeftToReply", - "classes", "commandHint_markasdm", "commandHint_markasgroup", "commandHint_html", @@ -53048,7 +52773,6 @@ "commandHint_react", "commandHint_send", "createGroup", - "createNewSpace", "createNewGroup", "allRooms", "chatPermissions", @@ -53289,8 +53013,7 @@ "interactiveTranslatorAllowedDesc", "interactiveTranslatorRequiredDesc", "notYetSet", - "multiLingualClass", - "classAnalytics", + "multiLingualSpace", "allClasses", "myLearning", "allChatsAndClasses", @@ -53302,19 +53025,17 @@ "classDescription", "classDescriptionDesc", "requestToEnroll", - "requestAnExchange", - "findLanguageExchange", - "classAnalyticsDesc", + "spaceAnalyticsDesc", "addStudents", "copyClassLink", "copyClassLinkDesc", "copyClassCode", "inviteStudentByUserName", - "classSettings", - "classSettingsDesc", - "selectClassRoomDominantLanguage", - "selectTargetLanguage", - "whatIsYourClassLanguageLevel", + "languageSettings", + "languageSettingsDesc", + "selectSpaceDominantLanguage", + "selectSpaceTargetLanguage", + "whatIsYourSpaceLanguageLevel", "studentPermissions", "interactiveTranslator", "oneToOneChatsWithinClass", @@ -53479,16 +53200,13 @@ "definitionsToolDescription", "translationsToolDescrption", "welcomeBack", - "classExchanges", "createNewClass", - "newExchange", "kickAllStudents", "kickAllStudentsConfirmation", "inviteAllStudents", "inviteAllStudentsConfirmation", "inviteStudentsFromOtherClasses", "inviteUsersFromPangea", - "allExchanges", "redeemPromoCode", "enterPromoCode", "downloadTxtFile", @@ -53942,11 +53660,8 @@ "allPrivateChats", "unknownPrivateChat", "copyClassCodeDesc", - "addToClass", - "addToClassDesc", - "addToClassOrExchange", - "addToClassOrExchangeDesc", - "invitedToClassOrExchange", + "addToSpaceDesc", + "invitedToSpace", "declinedInvitation", "acceptedInvitation", "youreInvited", @@ -53965,7 +53680,7 @@ "promoSubscriptionExpirationDesc", "emptyChatNameWarning", "emptyClassNameWarning", - "emptyExchangeNameWarning", + "emptySpaceNameWarning", "blurMeansTranslateTitle", "blurMeansTranslateBody", "someErrorTitle", @@ -53990,7 +53705,6 @@ "why", "definition", "exampleSentence", - "addToClassTitle", "reportToTeacher", "reportMessageTitle", "reportMessageBody", @@ -54011,7 +53725,6 @@ "chatCanBeDiscoveredViaSearchOnServer", "searchChatsRooms", "createClass", - "createExchange", "viewArchive", "trialExpiration", "freeTrialDesc", @@ -54170,6 +53883,14 @@ "noDatabaseEncryption", "thereAreCountUsersBlocked", "restricted", - "knockRestricted" + "knockRestricted", + "nonexistentSelection", + "cantAddSpaceChild", + "roomAddedToSpace", + "createNewSpace", + "addChatToSpaceDesc", + "addSpaceToSpaceDesc", + "spaceAnalytics", + "changeAnalyticsLanguage" ] } From 11575b1a9aa7fbbe48e3b583101b6b226d247648 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 19 Jun 2024 16:03:20 -0400 Subject: [PATCH 05/31] some updates to add to space toggles --- lib/pages/settings/settings_view.dart | 6 +++++ .../children_and_parents_extension.dart | 20 ++++++++++----- .../pangea_room_extension.dart | 9 ++++--- .../widgets/class/add_space_toggles.dart | 25 +++++++++++++------ 4 files changed, 43 insertions(+), 17 deletions(-) diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart index c4a57a267..9ff495328 100644 --- a/lib/pages/settings/settings_view.dart +++ b/lib/pages/settings/settings_view.dart @@ -194,6 +194,12 @@ class SettingsView extends StatelessWidget { Icons.chevron_right_outlined, ), ), + ListTile( + leading: const Icon(Icons.psychology_outlined), + title: Text(L10n.of(context)!.learningSettings), + onTap: () => context.go('/rooms/settings/learning'), + trailing: const Icon(Icons.chevron_right_outlined), + ), // Pangea# ListTile( leading: const Icon(Icons.shield_outlined), diff --git a/lib/pangea/extensions/pangea_room_extension/children_and_parents_extension.dart b/lib/pangea/extensions/pangea_room_extension/children_and_parents_extension.dart index 6952a1dfb..2dc3f6f59 100644 --- a/lib/pangea/extensions/pangea_room_extension/children_and_parents_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/children_and_parents_extension.dart @@ -123,11 +123,19 @@ extension ChildrenAndParentsRoomExtension on Room { // Checks if has permissions to add child chat // Or whether potential child space is ancestor of this - bool _canAddAsParentOf(Room? child) { - if (child == null || !child.isSpace) { - return _canIAddSpaceChild(child); - } - if (id == child.id) return false; - return !child._allSpaceChildRoomIds.contains(id); + bool _canAddAsParentOf(Room? child, {bool spaceMode = false}) { + // don't add a room to itself + if (id == child?.id) return false; + spaceMode = child?.isSpace ?? spaceMode; + + // get the bool for adding chats to spaces + final bool canAddChild = _canIAddSpaceChild(child, spaceMode: spaceMode); + if (!spaceMode) return canAddChild; + + // if adding space to a space, check if the child is an ancestor + // to prevent cycles + final bool isCycle = + child != null ? child._allSpaceChildRoomIds.contains(id) : false; + return canAddChild && !isCycle; } } diff --git a/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart b/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart index 74230e212..0b303d7bc 100644 --- a/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart @@ -109,7 +109,9 @@ extension PangeaRoom on Room { List get allSpaceChildRoomIds => _allSpaceChildRoomIds; - bool canAddAsParentOf(Room? child) => _canAddAsParentOf(child); + bool canAddAsParentOf(Room? child, {bool spaceMode = false}) { + return _canAddAsParentOf(child, spaceMode: spaceMode); + } // class_and_exchange_settings @@ -262,8 +264,9 @@ extension PangeaRoom on Room { bool get canDelete => _canDelete; - bool canIAddSpaceChild(Room? room, {bool spaceMode = false}) => - _canIAddSpaceChild(room, spaceMode: spaceMode); + bool canIAddSpaceChild(Room? room, {bool spaceMode = false}) { + return _canIAddSpaceChild(room, spaceMode: spaceMode); + } bool get canIAddSpaceParents => _canIAddSpaceParents; diff --git a/lib/pangea/widgets/class/add_space_toggles.dart b/lib/pangea/widgets/class/add_space_toggles.dart index 4410cd6d5..08fac33d9 100644 --- a/lib/pangea/widgets/class/add_space_toggles.dart +++ b/lib/pangea/widgets/class/add_space_toggles.dart @@ -41,6 +41,19 @@ class AddToSpaceState extends State { @override void initState() { + initialize(); + super.initState(); + } + + @override + void didUpdateWidget(AddToSpaceToggles oldWidget) { + if (oldWidget.roomId != widget.roomId) { + initialize(); + } + super.didUpdateWidget(oldWidget); + } + + void initialize() { //if roomId is null, it means this widget is being used in the creation flow room = widget.roomId != null ? Matrix.of(context).client.getRoomById(widget.roomId!) @@ -92,7 +105,6 @@ class AddToSpaceState extends State { }); isOpen = widget.startOpen; - super.initState(); } Future _addSingleSpace(String roomToAddId, Room newParent) async { @@ -140,13 +152,10 @@ class AddToSpaceState extends State { Widget getAddToSpaceToggleItem(int index) { final Room possibleParent = possibleParents[index]; - final bool canAdd = (room?.isSpace ?? false) - // Room is space - ? possibleParent.isRoomAdmin && - room!.isRoomAdmin && - possibleParent.canAddAsParentOf(room) - // Room is null or chat - : possibleParent.canAddAsParentOf(room); + final bool canAdd = possibleParent.canAddAsParentOf( + room, + spaceMode: widget.spaceMode, + ); return Opacity( opacity: canAdd ? 1 : 0.5, From c1a06b8e3d7cbc8de688352da91329093267ab4d Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 20 Jun 2024 10:41:52 -0400 Subject: [PATCH 06/31] updated addMissingRoomRules function for combined spaces --- lib/pangea/controllers/class_controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pangea/controllers/class_controller.dart b/lib/pangea/controllers/class_controller.dart index c9f2246fd..5bab8af35 100644 --- a/lib/pangea/controllers/class_controller.dart +++ b/lib/pangea/controllers/class_controller.dart @@ -195,7 +195,7 @@ class ClassController extends BaseController { final Room? room = _pangeaController.matrixState.client.getRoomById(roomId); if (room == null) return; - if (room.pangeaRoomRules == null) { + if (room.isSpace && room.isRoomAdmin && room.pangeaRoomRules == null) { try { await _pangeaController.matrixState.client.setRoomStateWithKey( roomId, From fcca323b693e60ffcaa720d95f18dca3b0daa88e Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 20 Jun 2024 13:15:32 -0400 Subject: [PATCH 07/31] =?UTF-8?q?Updated=20how=20analytics=20are=20saved?= =?UTF-8?q?=20to=20work=20better=20with=20the=20switch=20over=20to=20using?= =?UTF-8?q?=20user=E2=80=99s=20language=20settings=20instead=20of=20class-?= =?UTF-8?q?level=20language=20settings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/l10n/intl_en.arb | 4 +- .../controllers/choreographer.dart | 4 +- .../controllers/language_controller.dart | 2 +- .../controllers/my_analytics_controller.dart | 189 +++++++++-------- .../client_analytics_extension.dart | 13 ++ .../client_extension/client_extension.dart | 4 + .../widgets/class/add_space_toggles.dart | 12 +- lib/pangea/widgets/igc/word_data_card.dart | 3 +- needed-translations.txt | 200 +++++++++++++----- 9 files changed, 283 insertions(+), 148 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 4201c4827..e971e6215 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4047,5 +4047,7 @@ "addChatToSpaceDesc": "Adding a chat to a space will make the chat appear within the space for students and give them access.", "addSpaceToSpaceDesc": "Adding a space to another space will make the child space appear within the parent space for students and give them access.", "spaceAnalytics": "Space Analytics", - "changeAnalyticsLanguage": "Change Analytics Language" + "changeAnalyticsLanguage": "Change Analytics Language", + "suggestToSpace": "Suggest this space", + "suggestToSpaceDesc": "Suggested spaces will appear in the chat lists for their parent spaces" } \ No newline at end of file diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart index 61a9489b6..6ea029a6f 100644 --- a/lib/pangea/choreographer/controllers/choreographer.dart +++ b/lib/pangea/choreographer/controllers/choreographer.dart @@ -441,9 +441,7 @@ class Choreographer { } LanguageModel? get l2Lang { - return pangeaController.languageController.activeL2Model( - roomID: roomId, - ); + return pangeaController.languageController.activeL2Model(); } String? get l2LangCode => l2Lang?.langCode; diff --git a/lib/pangea/controllers/language_controller.dart b/lib/pangea/controllers/language_controller.dart index d070ac914..c906e6b4e 100644 --- a/lib/pangea/controllers/language_controller.dart +++ b/lib/pangea/controllers/language_controller.dart @@ -101,7 +101,7 @@ class LanguageController { // return activeL1 != null ? PangeaLanguage.byLangCode(activeL1) : null; } - LanguageModel? activeL2Model({String? roomID}) { + LanguageModel? activeL2Model() { return userL2; // final activeL2 = activeL2Code(roomID: roomID); // final model = activeL2 != null ? PangeaLanguage.byLangCode(activeL2) : null; diff --git a/lib/pangea/controllers/my_analytics_controller.dart b/lib/pangea/controllers/my_analytics_controller.dart index f00e601a8..f96f4096a 100644 --- a/lib/pangea/controllers/my_analytics_controller.dart +++ b/lib/pangea/controllers/my_analytics_controller.dart @@ -6,8 +6,10 @@ import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/controllers/base_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; -import 'package:fluffychat/pangea/models/analytics/analytics_event.dart'; -import 'package:fluffychat/pangea/models/analytics/analytics_model.dart'; +import 'package:fluffychat/pangea/models/analytics/constructs_event.dart'; +import 'package:fluffychat/pangea/models/analytics/constructs_model.dart'; +import 'package:fluffychat/pangea/models/analytics/summary_analytics_event.dart'; +import 'package:fluffychat/pangea/models/analytics/summary_analytics_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:flutter/foundation.dart'; import 'package:matrix/matrix.dart'; @@ -170,6 +172,12 @@ class MyAnalyticsController extends BaseController { } Future _updateAnalytics() async { + // if the user's l2 is not sent, don't send analytics + final String? userL2 = _pangeaController.languageController.activeL2Code(); + if (userL2 == null) { + return; + } + // top level analytics sending function. Send analytics // for each type of analytics event // to each of the applicable analytics rooms @@ -180,115 +188,118 @@ class MyAnalyticsController extends BaseController { await setStudentChats(); await setStudentSpaces(); - // get all the analytics rooms that the user has - // and create any missing analytics rooms (if the user is studying - // in a class but doesn't have an analytics room for that class's L2) - final List analyticsRooms = - _pangeaController.matrixState.client.allMyAnalyticsRooms; - analyticsRooms.addAll(await createMissingAnalyticsRooms()); + // get the last updated time for each analytics room + // and the least recent update, which will be used to determine + // how far to go back in the chat history to get messages + final Map lastUpdatedMap = await _pangeaController + .matrixState.client + .allAnalyticsRoomsLastUpdated(); + final List lastUpdates = lastUpdatedMap.values + .where((lastUpdate) => lastUpdate != null) + .cast() + .toList(); + lastUpdates.sort((a, b) => a.compareTo(b)); + final DateTime? leastRecentUpdate = + lastUpdates.isNotEmpty ? lastUpdates.first : null; - // finally, send an analytics event for each analytics room and - // each type of analytics event - for (final Room analyticsRoom in analyticsRooms) { - for (final String type in AnalyticsEvent.analyticsEventTypes) { - await sendAnalyticsEvent(analyticsRoom, type); - } + // for each chat the user is studying in, get all the messages + // since the least recent update analytics update, and sort them + // by their langCodes + final Map> langCodeToMsgs = + await getLangCodesToMsgs( + userL2, + leastRecentUpdate, + ); + + final List langCodes = langCodeToMsgs.keys.toList(); + for (final String langCode in langCodes) { + // for each of the langs that the user has sent message in, get + // the corresponding analytics room (or create it) + final Room analyticsRoom = await _pangeaController.matrixState.client + .getMyAnalyticsRoom(langCode); + + // if there is no analytics room for this langCode, then user hadn't sent + // message in this language at the time of the last analytics update + // so fallback to the least recent update time + final DateTime? lastUpdated = + lastUpdatedMap[analyticsRoom.id] ?? leastRecentUpdate; + + // get the corresponding list of recent messages for this langCode + final List recentMsgs = + langCodeToMsgs[langCode] ?? []; + + // finally, send the analytics events to the analytics room + await sendAnalyticsEvents( + analyticsRoom, + recentMsgs, + lastUpdated, + ); } } - Future sendAnalyticsEvent( - Room analyticsRoom, - String type, + Future>> getLangCodesToMsgs( + String userL2, + DateTime? since, ) async { - // given an analytics room for a language and a type of analytics event - // gathers all the relevant data and sends it to the analytics room - - // get the language code for the analytics room - final String? langCode = analyticsRoom.madeForLang; - if (langCode == null) { - ErrorHandler.logError( - e: "no lang code found for analytics room: ${analyticsRoom.id}", - s: StackTrace.current, - ); - return; - } - - // get the last time an analytics event of this type was sent to this room - final DateTime? lastUpdated = await analyticsRoom.analyticsLastUpdated( - type, - _pangeaController.matrixState.client.userID!, - ); - - // each type of analytics event has a format for storing per-message data - // for SummaryAnalytics events, this is RecentMessageRecord - // for Construct events, this is OneConstructUse - // analyticsContent is a list of these formatted data - final List analyticsContent = []; - + // get a map of langCodes to messages for each chat the user is studying in + final Map> langCodeToMsgs = {}; for (final Room chat in _studentChats) { - // for each chat the student studies in, check if the langCode - // matches the langCode of the analytics room - // TODO gabby - replace this - final String? chatLangCode = - _pangeaController.languageController.activeL2Code(); - if (chatLangCode != langCode) continue; - - // get messages the logged in user has sent in all chats - // since the last analytics event was sent List? recentMsgs; try { recentMsgs = await chat.myMessageEventsInChat( - since: lastUpdated, + since: since, ); } catch (err) { debugPrint("failed to fetch messages for chat ${chat.id}"); continue; } - if (lastUpdated != null) { - recentMsgs.removeWhere( - (msg) => msg.event.originServerTs.isBefore(lastUpdated), - ); + // sort those messages by their langCode + // langCode is hopefully based on the original sent rep, but if that + // is null, it will be based on the user's current l2 + for (final msg in recentMsgs) { + final String msgLangCode = msg.originalSent?.langCode ?? userL2; + langCodeToMsgs[msgLangCode] ??= []; + langCodeToMsgs[msgLangCode]!.add(msg); } + } + return langCodeToMsgs; + } - // then format that data into analytics data and add the formatted - // data to the list of analyticsContent - analyticsContent.addAll( - AnalyticsModel.formatAnalyticsContent(recentMsgs, type), + Future sendAnalyticsEvents( + Room analyticsRoom, + List recentMsgs, + DateTime? lastUpdated, + ) async { + // remove messages that were sent before the last update + if (recentMsgs.isEmpty) return; + if (lastUpdated != null) { + recentMsgs.removeWhere( + (msg) => msg.event.originServerTs.isBefore(lastUpdated), ); } - // send the analytics data to the analytics room - // if there is no data to send, don't send an event, - // unless no events have been sent yet. In that case, send an event - // with no data to indicate that the the system checked for data - // and found none, so the system doesn't repeatedly check for data - if (analyticsContent.isEmpty && lastUpdated != null) return; - await AnalyticsEvent.sendEvent( - analyticsRoom, - type, - analyticsContent, - ); - } + // format the analytics data + final List summaryContent = + SummaryAnalyticsModel.formatSummaryContent(recentMsgs); + final List constructContent = + ConstructAnalyticsModel.formatConstructsContent(recentMsgs); - // on the off chance that the user is in a class but doesn't have an analytics - // room for the target language of that class, create the analytics room(s) - Future> createMissingAnalyticsRooms() async { - List targetLangs = []; - final String? userL2 = _pangeaController.languageController.activeL2Code(); - if (userL2 != null) targetLangs.add(userL2); - // TODO gabby - replace this - final List spaceL2s = studentSpaces - .map( - (space) => _pangeaController.languageController.activeL2Code(), - ) - .toList(); - targetLangs.addAll(spaceL2s.where((l2) => l2 != null).cast()); - targetLangs = targetLangs.toSet().toList(); - for (final String langCode in targetLangs) { - await _pangeaController.matrixState.client.getMyAnalyticsRoom(langCode); + // if there's new content to be sent, or if lastUpdated hasn't been + // set yet for this room, send the analytics events + if (summaryContent.isNotEmpty || lastUpdated == null) { + await SummaryAnalyticsEvent.sendSummaryAnalyticsEvent( + analyticsRoom, + summaryContent, + ); + } + + if (constructContent.isNotEmpty) { + await ConstructAnalyticsEvent.sendConstructsEvent( + analyticsRoom, + constructContent, + ); } - return _pangeaController.matrixState.client.allMyAnalyticsRooms; } List _studentChats = []; diff --git a/lib/pangea/extensions/client_extension/client_analytics_extension.dart b/lib/pangea/extensions/client_extension/client_analytics_extension.dart index d2423131f..9956359a0 100644 --- a/lib/pangea/extensions/client_extension/client_analytics_extension.dart +++ b/lib/pangea/extensions/client_extension/client_analytics_extension.dart @@ -153,4 +153,17 @@ extension AnalyticsClientExtension on Client { await _joinInvitedAnalyticsRooms(); await _joinAnalyticsRoomsInAllSpaces(); } + + Future> _allAnalyticsRoomsLastUpdated() async { + // get the last updated time for each analytics room + final Map lastUpdatedMap = {}; + for (final analyticsRoom in allMyAnalyticsRooms) { + final DateTime? lastUpdated = await analyticsRoom.analyticsLastUpdated( + PangeaEventTypes.summaryAnalytics, + userID!, + ); + lastUpdatedMap[analyticsRoom.id] = lastUpdated; + } + return lastUpdatedMap; + } } diff --git a/lib/pangea/extensions/client_extension/client_extension.dart b/lib/pangea/extensions/client_extension/client_extension.dart index 498081a8c..779f8ee0a 100644 --- a/lib/pangea/extensions/client_extension/client_extension.dart +++ b/lib/pangea/extensions/client_extension/client_extension.dart @@ -3,6 +3,7 @@ import 'dart:developer'; import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/constants/class_default_values.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; +import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/constants/pangea_room_types.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/models/space_model.dart'; @@ -43,6 +44,9 @@ extension PangeaClient on Client { Future migrateAnalyticsRooms() async => await _migrateAnalyticsRooms(); + Future> allAnalyticsRoomsLastUpdated() async => + await _allAnalyticsRoomsLastUpdated(); + // spaces Future> get spacesImTeaching async => await _spacesImTeaching; diff --git a/lib/pangea/widgets/class/add_space_toggles.dart b/lib/pangea/widgets/class/add_space_toggles.dart index 08fac33d9..abbef7746 100644 --- a/lib/pangea/widgets/class/add_space_toggles.dart +++ b/lib/pangea/widgets/class/add_space_toggles.dart @@ -229,13 +229,21 @@ class AddToSpaceState extends State { ? Column( children: [ SwitchListTile.adaptive( - title: Text(L10n.of(context)!.suggestToChat), + title: Text( + widget.spaceMode || (room?.isSpace ?? false) + ? L10n.of(context)!.suggestToSpace + : L10n.of(context)!.suggestToChat, + ), secondary: Icon( isSuggested ? Icons.visibility_outlined : Icons.visibility_off_outlined, ), - subtitle: Text(L10n.of(context)!.suggestToChatDesc), + subtitle: Text( + widget.spaceMode || (room?.isSpace ?? false) + ? L10n.of(context)!.suggestToSpaceDesc + : L10n.of(context)!.suggestToChatDesc, + ), activeColor: AppConfig.activeToggleColor, value: isSuggested, onChanged: (bool add) => setSuggested(add), diff --git a/lib/pangea/widgets/igc/word_data_card.dart b/lib/pangea/widgets/igc/word_data_card.dart index eae1c2b8d..5a785c795 100644 --- a/lib/pangea/widgets/igc/word_data_card.dart +++ b/lib/pangea/widgets/igc/word_data_card.dart @@ -62,8 +62,7 @@ class WordDataCardController extends State { void initState() { if (!mounted) return; activeL1 = controller.languageController.activeL1Model()!; - activeL2 = - controller.languageController.activeL2Model(roomID: widget.room.id)!; + activeL2 = controller.languageController.activeL2Model()!; if (activeL1 == null || activeL2 == null) { wordNetError = noLanguages; definitionError = noLanguages; diff --git a/needed-translations.txt b/needed-translations.txt index 928fa5052..a2d9d0330 100644 --- a/needed-translations.txt +++ b/needed-translations.txt @@ -852,7 +852,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "be": [ @@ -2341,7 +2343,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "bn": [ @@ -3826,7 +3830,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "bo": [ @@ -5315,7 +5321,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "ca": [ @@ -6206,7 +6214,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "cs": [ @@ -7179,7 +7189,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "de": [ @@ -8035,7 +8047,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "el": [ @@ -9475,7 +9489,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "eo": [ @@ -10613,11 +10629,15 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "es": [ - "searchIn" + "searchIn", + "suggestToSpace", + "suggestToSpaceDesc" ], "et": [ @@ -11473,7 +11493,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "eu": [ @@ -12331,7 +12353,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "fa": [ @@ -13326,7 +13350,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "fi": [ @@ -14285,7 +14311,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "fil": [ @@ -15600,7 +15628,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "fr": [ @@ -16594,7 +16624,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "ga": [ @@ -17717,7 +17749,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "gl": [ @@ -18573,7 +18607,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "he": [ @@ -19815,7 +19851,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "hi": [ @@ -21297,7 +21335,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "hr": [ @@ -22232,7 +22272,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "hu": [ @@ -23104,7 +23146,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "ia": [ @@ -24579,7 +24623,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "id": [ @@ -25441,7 +25487,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "ie": [ @@ -26687,7 +26735,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "it": [ @@ -27600,7 +27650,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "ja": [ @@ -28624,7 +28676,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "ka": [ @@ -29967,7 +30021,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "ko": [ @@ -30825,7 +30881,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "lt": [ @@ -31849,7 +31907,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "lv": [ @@ -32713,7 +32773,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "nb": [ @@ -33901,7 +33963,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "nl": [ @@ -34853,7 +34917,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "pl": [ @@ -35814,7 +35880,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "pt": [ @@ -37281,7 +37349,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "pt_BR": [ @@ -38143,7 +38213,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "pt_PT": [ @@ -39332,7 +39404,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "ro": [ @@ -40328,7 +40402,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "ru": [ @@ -41190,7 +41266,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "sk": [ @@ -42445,7 +42523,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "sl": [ @@ -43830,7 +43910,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "sr": [ @@ -44989,7 +45071,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "sv": [ @@ -45882,7 +45966,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "ta": [ @@ -47368,7 +47454,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "th": [ @@ -48808,7 +48896,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "tr": [ @@ -49664,7 +49754,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "uk": [ @@ -50557,7 +50649,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "vi": [ @@ -51898,7 +51992,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "zh": [ @@ -52754,7 +52850,9 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ], "zh_Hant": [ @@ -53891,6 +53989,8 @@ "addChatToSpaceDesc", "addSpaceToSpaceDesc", "spaceAnalytics", - "changeAnalyticsLanguage" + "changeAnalyticsLanguage", + "suggestToSpace", + "suggestToSpaceDesc" ] } From d6a56cbd43914aa83e1279d7d5fefd376c9f899e Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Sat, 22 Jun 2024 13:26:12 -0400 Subject: [PATCH 08/31] successfully received/completed bot-generated practice activity --- ...actice_activity_generation_controller.dart | 5 ++- lib/pangea/enum/activity_type_enum.dart | 16 ++++++++ .../pangea_message_event.dart | 14 +++++-- .../multiple_choice_activity_model.dart | 12 +++--- .../practice_activity_model.dart | 39 +++++++++++------- .../message_practice_activity_content.dart | 4 +- pubspec.lock | 40 +++++++++---------- 7 files changed, 83 insertions(+), 47 deletions(-) create mode 100644 lib/pangea/enum/activity_type_enum.dart diff --git a/lib/pangea/controllers/practice_activity_generation_controller.dart b/lib/pangea/controllers/practice_activity_generation_controller.dart index fdd6da4fa..29047d0c4 100644 --- a/lib/pangea/controllers/practice_activity_generation_controller.dart +++ b/lib/pangea/controllers/practice_activity_generation_controller.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; +import 'package:fluffychat/pangea/enum/activity_type_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_message_event.dart'; @@ -89,13 +90,13 @@ class PracticeGenerationController { tgtConstructs: [ ConstructIdentifier(lemma: "be", type: ConstructType.vocab), ], - activityType: ActivityType.multipleChoice, + activityType: ActivityTypeEnum.multipleChoice, langCode: event.messageDisplayLangCode, msgId: event.eventId, multipleChoice: MultipleChoice( question: "What is a synonym for 'happy'?", choices: ["sad", "angry", "joyful", "tired"], - correctAnswer: "joyful", + answer: "joyful", ), ); } diff --git a/lib/pangea/enum/activity_type_enum.dart b/lib/pangea/enum/activity_type_enum.dart new file mode 100644 index 000000000..d429aa038 --- /dev/null +++ b/lib/pangea/enum/activity_type_enum.dart @@ -0,0 +1,16 @@ +enum ActivityTypeEnum { multipleChoice, freeResponse, listening, speaking } + +extension ActivityTypeExtension on ActivityTypeEnum { + String get string { + switch (this) { + case ActivityTypeEnum.multipleChoice: + return 'multiple_choice'; + case ActivityTypeEnum.freeResponse: + return 'free_response'; + case ActivityTypeEnum.listening: + return 'listening'; + case ActivityTypeEnum.speaking: + return 'speaking'; + } + } +} diff --git a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart index 66c81bb47..32b8c1a29 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart @@ -1,4 +1,5 @@ import 'dart:convert'; +import 'dart:developer'; import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; @@ -15,6 +16,7 @@ import 'package:fluffychat/pangea/models/speech_to_text_models.dart'; import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/utils/bot_name.dart'; import 'package:fluffychat/pangea/widgets/chat/message_audio_card.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; @@ -653,9 +655,15 @@ class PangeaMessageEvent { } List practiceActivities(String langCode) { - return _practiceActivityEvents - .where((ev) => ev.practiceActivity.langCode == langCode) - .toList(); + try { + return _practiceActivityEvents + .where((ev) => ev.practiceActivity.langCode == langCode) + .toList(); + } catch (e, s) { + debugger(when: kDebugMode); + ErrorHandler.logError(e: e, s: s, data: event.toJson()); + return []; + } } // List get activities => 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 0cd6aac05..e152c18a2 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 @@ -4,19 +4,19 @@ import 'package:flutter/material.dart'; class MultipleChoice { final String question; final List choices; - final String correctAnswer; + final String answer; MultipleChoice({ required this.question, required this.choices, - required this.correctAnswer, + required this.answer, }); bool isCorrect(int index) => index == correctAnswerIndex; - bool get isValidQuestion => choices.contains(correctAnswer); + bool get isValidQuestion => choices.contains(answer); - int get correctAnswerIndex => choices.indexOf(correctAnswer); + int get correctAnswerIndex => choices.indexOf(answer); Color choiceColor(int index) => index == correctAnswerIndex ? AppConfig.success : AppConfig.warning; @@ -25,7 +25,7 @@ class MultipleChoice { return MultipleChoice( question: json['question'] as String, choices: (json['choices'] as List).map((e) => e as String).toList(), - correctAnswer: json['correct_answer'] as String, + answer: json['answer'] as String, ); } @@ -33,7 +33,7 @@ class MultipleChoice { return { 'question': question, 'choices': choices, - 'correct_answer': correctAnswer, + 'answer': answer, }; } } 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 909406430..ae8455c7f 100644 --- a/lib/pangea/models/practice_activities.dart/practice_activity_model.dart +++ b/lib/pangea/models/practice_activities.dart/practice_activity_model.dart @@ -1,5 +1,10 @@ +import 'dart:developer'; + +import 'package:fluffychat/pangea/enum/activity_type_enum.dart'; import 'package:fluffychat/pangea/enum/construct_type_enum.dart'; import 'package:fluffychat/pangea/models/practice_activities.dart/multiple_choice_activity_model.dart'; +import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:flutter/foundation.dart'; class ConstructIdentifier { final String lemma; @@ -8,12 +13,18 @@ class ConstructIdentifier { ConstructIdentifier({required this.lemma, required this.type}); factory ConstructIdentifier.fromJson(Map json) { - return ConstructIdentifier( - lemma: json['lemma'] as String, - type: ConstructType.values.firstWhere( - (e) => e.string == json['type'], - ), - ); + try { + return ConstructIdentifier( + lemma: json['lemma'] as String, + type: ConstructType.values.firstWhere( + (e) => e.string == json['type'], + ), + ); + } catch (e, s) { + debugger(when: kDebugMode); + ErrorHandler.logError(e: e, s: s, data: json); + rethrow; + } } Map toJson() { @@ -24,8 +35,6 @@ class ConstructIdentifier { } } -enum ActivityType { multipleChoice, freeResponse, listening, speaking } - class CandidateMessage { final String msgId; final String roomId; @@ -72,7 +81,7 @@ class PracticeActivityRequest { final List? targetConstructs; final List? candidateMessages; final List? userIds; - final ActivityType? activityType; + final ActivityTypeEnum? activityType; final int? numActivities; PracticeActivityRequest({ @@ -96,7 +105,7 @@ class PracticeActivityRequest { .map((e) => CandidateMessage.fromJson(e as Map)) .toList(), userIds: (json['user_ids'] as List?)?.map((e) => e as String).toList(), - activityType: ActivityType.values.firstWhere( + activityType: ActivityTypeEnum.values.firstWhere( (e) => e.toString().split('.').last == json['activity_type'], ), numActivities: json['num_activities'] as int, @@ -210,7 +219,7 @@ class PracticeActivityModel { final List tgtConstructs; final String langCode; final String msgId; - final ActivityType activityType; + final ActivityTypeEnum activityType; final MultipleChoice? multipleChoice; final Listening? listening; final Speaking? speaking; @@ -234,8 +243,8 @@ class PracticeActivityModel { .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'], + activityType: ActivityTypeEnum.values.firstWhere( + (e) => e.string == json['activity_type'], ), multipleChoice: json['multiple_choice'] != null ? MultipleChoice.fromJson( @@ -249,7 +258,9 @@ class PracticeActivityModel { ? Speaking.fromJson(json['speaking'] as Map) : null, freeResponse: json['free_response'] != null - ? FreeResponse.fromJson(json['free_response'] as Map) + ? FreeResponse.fromJson( + json['free_response'] as Map, + ) : null, ); } diff --git a/lib/pangea/widgets/practice_activity_card/message_practice_activity_content.dart b/lib/pangea/widgets/practice_activity_card/message_practice_activity_content.dart index fc0119012..8f234bc8d 100644 --- a/lib/pangea/widgets/practice_activity_card/message_practice_activity_content.dart +++ b/lib/pangea/widgets/practice_activity_card/message_practice_activity_content.dart @@ -1,9 +1,9 @@ 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_model.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_card/multiple_choice_activity.dart'; @@ -64,7 +64,7 @@ class MessagePracticeActivityContentState Widget get activityWidget { switch (widget.practiceEvent.practiceActivity.activityType) { - case ActivityType.multipleChoice: + case ActivityTypeEnum.multipleChoice: return MultipleChoiceActivity( card: this, updateChoice: updateChoice, diff --git a/pubspec.lock b/pubspec.lock index 8932ea070..cdc07b8fa 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1184,10 +1184,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" io: dependency: transitive description: @@ -1200,10 +1200,10 @@ packages: dependency: transitive description: name: jiffy - sha256: cc1d4b75016a9156c29b5d61f0c9176c3e0fb0580cc5a0e0422b5d2cab3fbfff + sha256: "3497caaa36d36a29033e66803c9739ce6bccbc7e241ca46070f76ee9e6f6eb0c" url: "https://pub.dev" source: hosted - version: "6.2.1" + version: "6.3.1" js: dependency: transitive description: @@ -1281,26 +1281,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" license_checker: dependency: "direct dev" description: @@ -1425,10 +1425,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" mgrs_dart: dependency: transitive description: @@ -2254,26 +2254,26 @@ packages: dependency: transitive description: name: test - sha256: a1f7595805820fcc05e5c52e3a231aedd0b72972cb333e8c738a8b1239448b6f + sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073" url: "https://pub.dev" source: hosted - version: "1.24.9" + version: "1.25.2" test_api: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" test_core: dependency: transitive description: name: test_core - sha256: a757b14fc47507060a162cc2530d9a4a2f92f5100a952c7443b5cad5ef5b106a + sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4" url: "https://pub.dev" source: hosted - version: "0.5.9" + version: "0.6.0" timezone: dependency: transitive description: @@ -2566,10 +2566,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" wakelock_plus: dependency: "direct main" description: From 15f493709f78b78c6be105de80d25ad6febee924 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Sun, 23 Jun 2024 14:09:26 -0400 Subject: [PATCH 09/31] client set up to recieve and display multiple choice questions with some display tweaks --- .../matrix_event_wrappers/pangea_message_event.dart | 6 +++--- .../practice_activity_record_model.dart | 10 ++++++++++ lib/pangea/widgets/chat/message_toolbar.dart | 6 ++++++ .../generate_practice_activity.dart | 1 + .../message_practice_activity_content.dart | 1 + 5 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart index 32b8c1a29..c9c05aca1 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart @@ -577,18 +577,18 @@ class PangeaMessageEvent { // this is just showActivityIcon now but will include // logic for showing - bool get showMessageButtons => showActivityIcon; + bool get showMessageButtons => hasUncompletedActivity; /// Returns a boolean value indicating whether to show an activity icon for this message event. /// - /// The [showActivityIcon] getter checks if the [l2Code] is null, and if so, returns false. + /// The [hasUncompletedActivity] getter checks if the [l2Code] is null, and if so, returns false. /// Otherwise, it retrieves a list of [PracticeActivityEvent] objects using the [practiceActivities] function /// with the [l2Code] as an argument. /// If the list is empty, it returns false. /// Otherwise, it checks if every activity in the list is complete using the [isComplete] property. /// If any activity is not complete, it returns true, indicating that the activity icon should be shown. /// Otherwise, it returns false. - bool get showActivityIcon { + bool get hasUncompletedActivity { if (l2Code == null) return false; final List activities = practiceActivities(l2Code!); diff --git a/lib/pangea/models/practice_activities.dart/practice_activity_record_model.dart b/lib/pangea/models/practice_activities.dart/practice_activity_record_model.dart index 7aa7f6b88..93ec8e478 100644 --- a/lib/pangea/models/practice_activities.dart/practice_activity_record_model.dart +++ b/lib/pangea/models/practice_activities.dart/practice_activity_record_model.dart @@ -38,6 +38,16 @@ class PracticeActivityRecordModel { }; } + /// get the latest response index according to the response timeStamp + /// sort the responses by timestamp and get the index of the last response + int? get latestResponseIndex { + if (responses.isEmpty) { + return null; + } + responses.sort((a, b) => a.timestamp.compareTo(b.timestamp)); + return responses.length - 1; + } + void addResponse({ String? text, Uint8List? audioBytes, diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index ff7b73b72..9bb5aacbe 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -189,6 +189,12 @@ class MessageToolbarState extends State { return; } + // if there is an uncompleted activity, then show that + // we don't want the user to user the tools to get the answer :P + if (widget.pangeaMessageEvent.hasUncompletedActivity) { + newMode = MessageMode.practiceActivity; + } + if (mounted) { setState(() { currentMode = newMode; diff --git a/lib/pangea/widgets/practice_activity_card/generate_practice_activity.dart b/lib/pangea/widgets/practice_activity_card/generate_practice_activity.dart index 02d7e90cd..7d01d8b4c 100644 --- a/lib/pangea/widgets/practice_activity_card/generate_practice_activity.dart +++ b/lib/pangea/widgets/practice_activity_card/generate_practice_activity.dart @@ -15,6 +15,7 @@ class GeneratePracticeActivityButton extends StatelessWidget { required this.onActivityGenerated, }); + //TODO - probably disable the generation of activities for specific messages @override Widget build(BuildContext context) { return ElevatedButton( diff --git a/lib/pangea/widgets/practice_activity_card/message_practice_activity_content.dart b/lib/pangea/widgets/practice_activity_card/message_practice_activity_content.dart index 8f234bc8d..6b7ebf7d9 100644 --- a/lib/pangea/widgets/practice_activity_card/message_practice_activity_content.dart +++ b/lib/pangea/widgets/practice_activity_card/message_practice_activity_content.dart @@ -47,6 +47,7 @@ class MessagePracticeActivityContentState ); } else { recordModel = recordEvent!.record; + selectedChoiceIndex = recordModel!.latestResponseIndex; recordSubmittedPreviousSession = true; recordSubmittedThisSession = true; } From a4cd5d702b1bdcf1c35ab0f0c05c047d4677d805 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Mon, 24 Jun 2024 12:05:57 -0400 Subject: [PATCH 10/31] Let multiple overlays coexist --- .../controllers/language_list_controller.dart | 4 ++-- lib/pangea/utils/any_state_holder.dart | 24 ++++++++++++------- lib/pangea/utils/instructions.dart | 1 + lib/pangea/utils/overlay.dart | 12 +++++++--- lib/pangea/widgets/chat/message_toolbar.dart | 11 +++++---- 5 files changed, 34 insertions(+), 18 deletions(-) diff --git a/lib/pangea/controllers/language_list_controller.dart b/lib/pangea/controllers/language_list_controller.dart index 345d5b5e7..59c3cce88 100644 --- a/lib/pangea/controllers/language_list_controller.dart +++ b/lib/pangea/controllers/language_list_controller.dart @@ -27,7 +27,7 @@ class PangeaLanguage { static Future initialize() async { try { - _langList = await _getCahedFlags(); + _langList = await _getCachedFlags(); if (await _shouldFetch || _langList.isEmpty) { _langList = await LanguageRepo.fetchLanguages(); @@ -77,7 +77,7 @@ class PangeaLanguage { await MyShared.saveJson(PrefKey.flags, flagMap); } - static Future> _getCahedFlags() async { + static Future> _getCachedFlags() async { final Map? flagsMap = await MyShared.readJson(PrefKey.flags); if (flagsMap == null) { diff --git a/lib/pangea/utils/any_state_holder.dart b/lib/pangea/utils/any_state_holder.dart index bd09c7131..9705a9ca1 100644 --- a/lib/pangea/utils/any_state_holder.dart +++ b/lib/pangea/utils/any_state_holder.dart @@ -4,7 +4,7 @@ import 'package:sentry_flutter/sentry_flutter.dart'; class PangeaAnyState { final Map _layerLinkAndKeys = {}; - OverlayEntry? overlay; + List entries = []; dispose() { closeOverlay(); @@ -32,26 +32,32 @@ class PangeaAnyState { _layerLinkAndKeys.remove(transformTargetId); } - void openOverlay(OverlayEntry entry, BuildContext context) { - closeOverlay(); - overlay = entry; - Overlay.of(context).insert(overlay!); + void openOverlay( + OverlayEntry entry, + BuildContext context, { + bool closePrevOverlay = true, + }) { + if (closePrevOverlay) { + closeOverlay(); + } + entries.add(entry); + Overlay.of(context).insert(entry); } void closeOverlay() { - if (overlay != null) { + if (entries.isNotEmpty) { try { - overlay?.remove(); + entries.last.remove(); } catch (err, s) { ErrorHandler.logError( e: err, s: s, data: { - "overlay": overlay, + "overlay": entries.last, }, ); } - overlay = null; + entries.removeLast(); } } diff --git a/lib/pangea/utils/instructions.dart b/lib/pangea/utils/instructions.dart index 43350f518..b9eecd799 100644 --- a/lib/pangea/utils/instructions.dart +++ b/lib/pangea/utils/instructions.dart @@ -94,6 +94,7 @@ class InstructionsController { ), cardSize: const Size(300.0, 300.0), transformTargetId: transformTargetKey, + closePrevOverlay: false, ), ); } diff --git a/lib/pangea/utils/overlay.dart b/lib/pangea/utils/overlay.dart index 07eab5133..b0ab82025 100644 --- a/lib/pangea/utils/overlay.dart +++ b/lib/pangea/utils/overlay.dart @@ -25,9 +25,12 @@ class OverlayUtil { Color? backgroundColor, Alignment? targetAnchor, Alignment? followerAnchor, + bool closePrevOverlay = true, }) { try { - MatrixState.pAnyState.closeOverlay(); + if (closePrevOverlay) { + MatrixState.pAnyState.closeOverlay(); + } final LayerLinkAndKey layerLinkAndKey = MatrixState.pAnyState.layerLinkAndKey(transformTargetId); @@ -58,7 +61,8 @@ class OverlayUtil { ), ); - MatrixState.pAnyState.openOverlay(entry, context); + MatrixState.pAnyState + .openOverlay(entry, context, closePrevOverlay: closePrevOverlay); } catch (err, stack) { debugger(when: kDebugMode); ErrorHandler.logError(e: err, s: stack); @@ -72,6 +76,7 @@ class OverlayUtil { required String transformTargetId, backDropToDismiss = true, Color? borderColor, + bool closePrevOverlay = true, }) { try { final LayerLinkAndKey layerLinkAndKey = @@ -101,6 +106,7 @@ class OverlayUtil { offset: cardOffset, backDropToDismiss: backDropToDismiss, borderColor: borderColor, + closePrevOverlay: closePrevOverlay, ); } catch (err, stack) { debugger(when: kDebugMode); @@ -176,7 +182,7 @@ class OverlayUtil { return Offset(dx, dy); } - static bool get isOverlayOpen => MatrixState.pAnyState.overlay != null; + static bool get isOverlayOpen => MatrixState.pAnyState.entries.isNotEmpty; } class TransparentBackdrop extends StatelessWidget { diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index ba508906b..a120669ab 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -135,8 +135,8 @@ class ToolbarDisplayController { backgroundColor: const Color.fromRGBO(0, 0, 0, 1).withAlpha(100), ); - if (MatrixState.pAnyState.overlay != null) { - overlayId = MatrixState.pAnyState.overlay.hashCode.toString(); + if (MatrixState.pAnyState.entries.isNotEmpty) { + overlayId = MatrixState.pAnyState.entries.last.hashCode.toString(); } if (mode != null) { @@ -150,8 +150,11 @@ class ToolbarDisplayController { bool get highlighted { if (overlayId == null) return false; - if (MatrixState.pAnyState.overlay == null) overlayId = null; - return MatrixState.pAnyState.overlay.hashCode.toString() == overlayId; + if (MatrixState.pAnyState.entries.isEmpty) { + overlayId = null; + return false; + } + return MatrixState.pAnyState.entries.last.hashCode.toString() == overlayId; } } From e29951d5a4a2244140fd5be0d3b8ad8ab04fb016 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Mon, 24 Jun 2024 12:49:12 -0400 Subject: [PATCH 11/31] fix iOS version error after updating firebase packages --- ios/Podfile | 2 +- ios/Runner.xcodeproj/project.pbxproj | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ios/Podfile b/ios/Podfile index c8df069d3..fac4d8176 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -platform :ios, '12.0' +platform :ios, '13.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 4d1ab7555..98f0adda2 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -475,7 +475,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -568,7 +568,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -617,7 +617,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; From 6a18967baf0f6060ef24fb67665091c64bacf05b Mon Sep 17 00:00:00 2001 From: ggurdin Date: Mon, 24 Jun 2024 15:29:26 -0400 Subject: [PATCH 12/31] don't have unk langCode to analytics room, fallback to user's target language --- lib/pangea/controllers/my_analytics_controller.dart | 8 ++++++-- lib/pangea/widgets/igc/pangea_rich_text.dart | 7 ------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/lib/pangea/controllers/my_analytics_controller.dart b/lib/pangea/controllers/my_analytics_controller.dart index f96f4096a..ea0d06c56 100644 --- a/lib/pangea/controllers/my_analytics_controller.dart +++ b/lib/pangea/controllers/my_analytics_controller.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:developer'; +import 'package:fluffychat/pangea/constants/language_keys.dart'; import 'package:fluffychat/pangea/constants/local.key.dart'; import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/controllers/base_controller.dart'; @@ -256,9 +257,12 @@ class MyAnalyticsController extends BaseController { // sort those messages by their langCode // langCode is hopefully based on the original sent rep, but if that - // is null, it will be based on the user's current l2 + // is null or unk, it will be based on the user's current l2 for (final msg in recentMsgs) { - final String msgLangCode = msg.originalSent?.langCode ?? userL2; + final String msgLangCode = (msg.originalSent?.langCode != null && + msg.originalSent?.langCode != LanguageKeys.unknownLanguage) + ? msg.originalSent!.langCode + : userL2; langCodeToMsgs[msgLangCode] ??= []; langCodeToMsgs[msgLangCode]!.add(msg); } diff --git a/lib/pangea/widgets/igc/pangea_rich_text.dart b/lib/pangea/widgets/igc/pangea_rich_text.dart index d1a6c205b..bbd6868bf 100644 --- a/lib/pangea/widgets/igc/pangea_rich_text.dart +++ b/lib/pangea/widgets/igc/pangea_rich_text.dart @@ -2,7 +2,6 @@ import 'dart:developer'; import 'dart:ui'; import 'package:fluffychat/config/app_config.dart'; -import 'package:fluffychat/pangea/constants/language_keys.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/representation_content_model.dart'; @@ -191,12 +190,6 @@ class PangeaRichTextState extends State { : richText; } - bool get areLanguagesSet => - userL2LangCode != null && userL2LangCode != LanguageKeys.unknownLanguage; - - String? get userL2LangCode => - pangeaController.languageController.activeL2Code(); - Future onIgnore() async { debugPrint("PTODO implement onIgnore"); } From 817f45709f0ecfaf46f10ea41d8b8fd944a81dce Mon Sep 17 00:00:00 2001 From: ggurdin Date: Mon, 24 Jun 2024 16:16:24 -0400 Subject: [PATCH 13/31] uncommented debug statement in construct model --- lib/pangea/models/analytics/constructs_model.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pangea/models/analytics/constructs_model.dart b/lib/pangea/models/analytics/constructs_model.dart index 6e6bad1b6..18c6d3d5a 100644 --- a/lib/pangea/models/analytics/constructs_model.dart +++ b/lib/pangea/models/analytics/constructs_model.dart @@ -1,7 +1,10 @@ +import 'dart:developer'; + import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/analytics/analytics_model.dart'; import 'package:fluffychat/pangea/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; @@ -54,7 +57,7 @@ class ConstructAnalyticsModel extends AnalyticsModel { s: s, m: "Error parsing ConstructAnalyticsModel", ); - // debugger(when: kDebugMode); + debugger(when: kDebugMode); } } return ConstructAnalyticsModel( From f6572d31272f01521bed416ec4e5f16d5806dc08 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 25 Jun 2024 09:25:46 -0400 Subject: [PATCH 14/31] on igc button long press, show user language dialog --- .../widgets/start_igc_button.dart | 111 +++++++++--------- 1 file changed, 58 insertions(+), 53 deletions(-) diff --git a/lib/pangea/choreographer/widgets/start_igc_button.dart b/lib/pangea/choreographer/widgets/start_igc_button.dart index ceb5af193..49e4b078d 100644 --- a/lib/pangea/choreographer/widgets/start_igc_button.dart +++ b/lib/pangea/choreographer/widgets/start_igc_button.dart @@ -5,6 +5,7 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; import 'package:fluffychat/pangea/constants/colors.dart'; import 'package:fluffychat/pangea/controllers/subscription_controller.dart'; +import 'package:fluffychat/pangea/widgets/user_settings/p_language_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -77,61 +78,65 @@ class StartIGCButtonState extends State return SizedBox( height: 50, width: 50, - child: FloatingActionButton( - tooltip: assistanceState.tooltip( - L10n.of(context)!, - ), - backgroundColor: Theme.of(context).scaffoldBackgroundColor, - disabledElevation: 0, - shape: const CircleBorder(), - onPressed: () { - if (assistanceState != AssistanceState.complete) { - widget.controller.choreographer - .getLanguageHelp( - false, - true, - ) - .then((_) { - if (widget.controller.choreographer.igc.igcTextData != null && - widget.controller.choreographer.igc.igcTextData!.matches - .isNotEmpty) { - widget.controller.choreographer.igc.showFirstMatch(context); - } - }); - } - }, - child: Stack( - alignment: Alignment.center, - children: [ - _controller != null - ? RotationTransition( - turns: Tween(begin: 0.0, end: math.pi * 2) - .animate(_controller!), - child: icon, - ) - : icon, - Container( - width: 26, - height: 26, - decoration: BoxDecoration( - shape: BoxShape.circle, + child: InkWell( + customBorder: const CircleBorder(), + onLongPress: () => pLanguageDialog(context, () {}), + child: FloatingActionButton( + tooltip: assistanceState.tooltip( + L10n.of(context)!, + ), + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + disabledElevation: 0, + shape: const CircleBorder(), + onPressed: () { + if (assistanceState != AssistanceState.complete) { + widget.controller.choreographer + .getLanguageHelp( + false, + true, + ) + .then((_) { + if (widget.controller.choreographer.igc.igcTextData != null && + widget.controller.choreographer.igc.igcTextData!.matches + .isNotEmpty) { + widget.controller.choreographer.igc.showFirstMatch(context); + } + }); + } + }, + child: Stack( + alignment: Alignment.center, + children: [ + _controller != null + ? RotationTransition( + turns: Tween(begin: 0.0, end: math.pi * 2) + .animate(_controller!), + child: icon, + ) + : icon, + Container( + width: 26, + height: 26, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Theme.of(context).scaffoldBackgroundColor, + ), + ), + Container( + width: 20, + height: 20, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: assistanceState.stateColor(context), + ), + ), + Icon( + size: 16, + Icons.check, color: Theme.of(context).scaffoldBackgroundColor, ), - ), - Container( - width: 20, - height: 20, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: assistanceState.stateColor(context), - ), - ), - Icon( - size: 16, - Icons.check, - color: Theme.of(context).scaffoldBackgroundColor, - ), - ], + ], + ), ), ), ); From 16a8383e72044e04a7364e4affe9c9a784cf1e22 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 25 Jun 2024 09:28:31 -0400 Subject: [PATCH 15/31] added language dropdown to space analytics list and made timespan dropdown functional, updated fetching of space analytics --- .../message_analytics_controller.dart | 77 +++++++++---------- .../pages/analytics/analytics_list_tile.dart | 12 ++- .../analytics/space_list/space_list.dart | 42 ++++------ .../analytics/space_list/space_list_view.dart | 16 +++- 4 files changed, 73 insertions(+), 74 deletions(-) diff --git a/lib/pangea/controllers/message_analytics_controller.dart b/lib/pangea/controllers/message_analytics_controller.dart index 25dcaf2d5..7083efbfd 100644 --- a/lib/pangea/controllers/message_analytics_controller.dart +++ b/lib/pangea/controllers/message_analytics_controller.dart @@ -160,11 +160,21 @@ class AnalyticsController extends BaseController { // private chat analytics to determine which children are already visible // in the chat list final Map> _lastFetchedHierarchies = {}; + void setLatestHierarchy(String spaceId, GetSpaceHierarchyResponse resp) { final List roomIds = resp.rooms.map((room) => room.roomId).toList(); _lastFetchedHierarchies[spaceId] = roomIds; } + Future> getLatestSpaceHierarchy(String spaceId) async { + if (!_lastFetchedHierarchies.containsKey(spaceId)) { + final resp = + await _pangeaController.matrixState.client.getSpaceHierarchy(spaceId); + setLatestHierarchy(spaceId, resp); + } + return _lastFetchedHierarchies[spaceId] ?? []; + } + //////////////////////////// MESSAGE SUMMARY ANALYTICS //////////////////////////// Future> mySummaryAnalytics() async { @@ -294,16 +304,16 @@ class AnalyticsController extends BaseController { return filtered; } - List filterRoomAnalytics( + Future> filterRoomAnalytics( List unfiltered, String? roomID, - ) { + ) async { List filtered = [...unfiltered]; Room? room; if (roomID != null) { room = _pangeaController.matrixState.client.getRoomById(roomID); if (room?.isSpace == true) { - return filterSpaceAnalytics(unfiltered, roomID); + return await filterSpaceAnalytics(unfiltered, roomID); } } @@ -322,16 +332,10 @@ class AnalyticsController extends BaseController { Future> filterPrivateChatAnalytics( List unfiltered, - Room? space, + Room space, ) async { - if (space != null && !_lastFetchedHierarchies.containsKey(space.id)) { - final resp = await _pangeaController.matrixState.client - .getSpaceHierarchy(space.id); - setLatestHierarchy(space.id, resp); - } - - final List privateChatIds = space?.allSpaceChildRoomIds ?? []; - final List lastFetched = _lastFetchedHierarchies[space!.id] ?? []; + final List privateChatIds = space.allSpaceChildRoomIds; + final List lastFetched = await getLatestSpaceHierarchy(space.id); for (final id in lastFetched) { privateChatIds.removeWhere((e) => e == id); } @@ -351,19 +355,11 @@ class AnalyticsController extends BaseController { return filtered; } - List filterSpaceAnalytics( + Future> filterSpaceAnalytics( List unfiltered, String spaceId, - ) { - final selectedSpace = - _pangeaController.matrixState.client.getRoomById(spaceId); - final List chatIds = selectedSpace?.spaceChildren - .map((e) => e.roomId) - .where((e) => e != null) - .cast() - .toList() ?? - []; - + ) async { + final List chatIds = await getLatestSpaceHierarchy(spaceId); List filtered = List.from(unfiltered); @@ -411,12 +407,15 @@ class AnalyticsController extends BaseController { if (defaultSelected.type == AnalyticsEntryType.student) { throw "private chat filtering not available for my analytics"; } + if (space == null) { + throw "space is null in filterAnalytics with selected type privateChats"; + } return await filterPrivateChatAnalytics( unfilteredAnalytics, space, ); case AnalyticsEntryType.space: - return filterSpaceAnalytics(unfilteredAnalytics, selected!.id); + return await filterSpaceAnalytics(unfilteredAnalytics, selected!.id); default: throw Exception("invalid filter type - ${selected?.type}"); } @@ -428,7 +427,6 @@ class AnalyticsController extends BaseController { bool forceUpdate = false, }) async { try { - debugPrint("getting analytics"); await _pangeaController.matrixState.client.roomsLoading; // if the user is looking at space analytics, then fetch the space @@ -612,31 +610,30 @@ class AnalyticsController extends BaseController { return filtered; } - List filterPrivateChatConstructs( + Future> filterPrivateChatConstructs( List unfilteredConstructs, - Room parentSpace, - ) { - final List directChatIds = []; + Room space, + ) async { + final List privateChatIds = space.allSpaceChildRoomIds; + final List lastFetched = await getLatestSpaceHierarchy(space.id); + for (final id in lastFetched) { + privateChatIds.removeWhere((e) => e == id); + } final List filtered = List.from(unfilteredConstructs); for (final construct in filtered) { construct.content.uses.removeWhere( - (use) => !directChatIds.contains(use.chatId), + (use) => !privateChatIds.contains(use.chatId), ); } return filtered; } - List filterSpaceConstructs( + Future> filterSpaceConstructs( List unfilteredConstructs, Room space, - ) { - final List chatIds = space.spaceChildren - .map((e) => e.roomId) - .where((e) => e != null) - .cast() - .toList(); - + ) async { + final List chatIds = await getLatestSpaceHierarchy(space.id); final List filtered = List.from(unfilteredConstructs); @@ -769,9 +766,9 @@ class AnalyticsController extends BaseController { case AnalyticsEntryType.privateChats: return defaultSelected.type == AnalyticsEntryType.student ? throw "private chat filtering not available for my analytics" - : filterPrivateChatConstructs(unfilteredConstructs, space!); + : await filterPrivateChatConstructs(unfilteredConstructs, space!); case AnalyticsEntryType.space: - return filterSpaceConstructs(unfilteredConstructs, space!); + return await filterSpaceConstructs(unfilteredConstructs, space!); default: throw Exception("invalid filter type - ${selected?.type}"); } diff --git a/lib/pangea/pages/analytics/analytics_list_tile.dart b/lib/pangea/pages/analytics/analytics_list_tile.dart index bfc19eb43..8b1e1ba49 100644 --- a/lib/pangea/pages/analytics/analytics_list_tile.dart +++ b/lib/pangea/pages/analytics/analytics_list_tile.dart @@ -26,8 +26,6 @@ class AnalyticsListTile extends StatefulWidget { required this.onTap, required this.pangeaController, this.controller, - // this.isEnabled = true, - // this.showSpaceAnalytics = true, this.refreshStream, }); @@ -40,8 +38,6 @@ class AnalyticsListTile extends StatefulWidget { final bool allowNavigateOnSelect; final bool isSelected; - // final bool isEnabled; - // final bool showSpaceAnalytics; final PangeaController pangeaController; final BaseAnalyticsController? controller; @@ -64,6 +60,14 @@ class AnalyticsListTileState extends State { }); } + @override + void didUpdateWidget(covariant AnalyticsListTile oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.selected != widget.selected) { + setTileData(); + } + } + @override void dispose() { refreshSubscription?.cancel(); diff --git a/lib/pangea/pages/analytics/space_list/space_list.dart b/lib/pangea/pages/analytics/space_list/space_list.dart index e6158525d..058d54e63 100644 --- a/lib/pangea/pages/analytics/space_list/space_list.dart +++ b/lib/pangea/pages/analytics/space_list/space_list.dart @@ -2,14 +2,13 @@ import 'dart:async'; import 'package:fluffychat/pangea/enum/time_span.dart'; import 'package:fluffychat/pangea/extensions/client_extension/client_extension.dart'; -import 'package:fluffychat/pangea/pages/analytics/base_analytics.dart'; +import 'package:fluffychat/pangea/models/language_model.dart'; import 'package:fluffychat/pangea/pages/analytics/space_list/space_list_view.dart'; import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; import '../../../../widgets/matrix.dart'; import '../../../controllers/pangea_controller.dart'; -import '../../../models/analytics/chart_analytics_model.dart'; import '../../../utils/sync_status_util_v2.dart'; import '../../../widgets/common/list_placeholder.dart'; @@ -22,7 +21,6 @@ class AnalyticsSpaceList extends StatefulWidget { class AnalyticsSpaceListController extends State { PangeaController pangeaController = MatrixState.pangeaController; - List models = []; List spaces = []; @override @@ -42,6 +40,20 @@ class AnalyticsSpaceListController extends State { }); } + StreamController refreshStream = StreamController.broadcast(); + + void toggleTimeSpan(BuildContext context, TimeSpan timeSpan) { + pangeaController.analytics.setCurrentAnalyticsTimeSpan(timeSpan); + refreshStream.add(false); + setState(() {}); + } + + Future toggleSpaceLang(LanguageModel lang) async { + await pangeaController.analytics.setCurrentAnalyticsSpaceLang(lang); + refreshStream.add(false); + setState(() {}); + } + @override Widget build(BuildContext context) { return PLoadingStatusV2( @@ -52,28 +64,4 @@ class AnalyticsSpaceListController extends State { }, ); } - - Future updateSpaceAnalytics( - Room? space, - ) async { - if (space == null) { - return null; - } - - final data = await pangeaController.analytics.getAnalytics( - defaultSelected: AnalyticsSelected( - space.id, - AnalyticsEntryType.space, - space.name, - ), - forceUpdate: true, - ); - setState(() {}); - return data; - } - - void toggleTimeSpan(BuildContext context, TimeSpan timeSpan) { - pangeaController.analytics.setCurrentAnalyticsTimeSpan(timeSpan); - setState(() {}); - } } diff --git a/lib/pangea/pages/analytics/space_list/space_list_view.dart b/lib/pangea/pages/analytics/space_list/space_list_view.dart index 4c6f72b71..5f5bf22da 100644 --- a/lib/pangea/pages/analytics/space_list/space_list_view.dart +++ b/lib/pangea/pages/analytics/space_list/space_list_view.dart @@ -1,3 +1,4 @@ +import 'package:fluffychat/pangea/pages/analytics/analytics_language_button.dart'; import 'package:fluffychat/pangea/pages/analytics/analytics_list_tile.dart'; import 'package:fluffychat/pangea/pages/analytics/time_span_menu_button.dart'; import 'package:flutter/material.dart'; @@ -35,8 +36,16 @@ class AnalyticsSpaceListView extends StatelessWidget { TimeSpanMenuButton( value: controller.pangeaController.analytics.currentAnalyticsTimeSpan, - onChange: (TimeSpan value) => - controller.toggleTimeSpan(context, value), + onChange: (TimeSpan value) => controller.toggleTimeSpan( + context, + value, + ), + ), + AnalyticsLanguageButton( + value: + controller.pangeaController.analytics.currentAnalyticsSpaceLang, + onChange: (lang) => controller.toggleSpaceLang(lang), + languages: controller.pangeaController.pLanguageStore.targetOptions, ), ], ), @@ -49,7 +58,7 @@ class AnalyticsSpaceListView extends StatelessWidget { defaultSelected: AnalyticsSelected( controller.spaces[i].id, AnalyticsEntryType.space, - "", + controller.spaces[i].name, ), avatar: controller.spaces[i].avatar, selected: AnalyticsSelected( @@ -65,6 +74,7 @@ class AnalyticsSpaceListView extends StatelessWidget { allowNavigateOnSelect: true, isSelected: false, pangeaController: controller.pangeaController, + refreshStream: controller.refreshStream, ), ), ), From 244c4b78dd69b482f5bb60594f8f1f98573013dd Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 25 Jun 2024 09:52:41 -0400 Subject: [PATCH 16/31] if room rules editor key has state, use that to set the rules editor inital rules in new space page --- lib/pages/new_space/new_space_view.dart | 49 +++++++++++++++---------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/lib/pages/new_space/new_space_view.dart b/lib/pages/new_space/new_space_view.dart index 469a3326d..f8c7baf4e 100644 --- a/lib/pages/new_space/new_space_view.dart +++ b/lib/pages/new_space/new_space_view.dart @@ -124,26 +124,35 @@ class NewSpaceView extends StatelessWidget { startOpen: true, spaceMode: true, ), - FutureBuilder( - future: Matrix.of(context).client.lastUpdatedRoomRules, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - return RoomRulesEditor( - key: controller.rulesEditorKey, - roomId: null, - startOpen: false, - initialRules: snapshot.data, - ); - } else { - return const Padding( - padding: EdgeInsets.all(16.0), - child: Center( - child: CircularProgressIndicator.adaptive(strokeWidth: 2), - ), - ); - } - }, - ), + if (controller.rulesEditorKey.currentState != null) + RoomRulesEditor( + key: controller.rulesEditorKey, + roomId: null, + startOpen: false, + initialRules: controller.rulesEditorKey.currentState!.rules, + ), + if (controller.rulesEditorKey.currentState == null) + FutureBuilder( + future: Matrix.of(context).client.lastUpdatedRoomRules, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + return RoomRulesEditor( + key: controller.rulesEditorKey, + roomId: null, + startOpen: false, + initialRules: snapshot.data, + ); + } else { + return const Padding( + padding: EdgeInsets.all(16.0), + child: Center( + child: + CircularProgressIndicator.adaptive(strokeWidth: 2), + ), + ); + } + }, + ), // SwitchListTile.adaptive( // title: Text(L10n.of(context)!.spaceIsPublic), // value: controller.publicGroup, From 9c75d4e2d53362e83e2f450b982415c0a25983ef Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Tue, 25 Jun 2024 10:52:16 -0400 Subject: [PATCH 17/31] debugging why certain activities would not show --- .../pangea_message_event.dart | 14 ++++++++++---- lib/pangea/widgets/chat/message_toolbar.dart | 2 +- .../generate_practice_activity.dart | 0 .../multiple_choice_activity.dart | 2 +- .../practice_activity_card.dart} | 12 ++++++++++-- .../practice_activity_content.dart} | 2 +- 6 files changed, 23 insertions(+), 9 deletions(-) rename lib/pangea/widgets/{practice_activity_card => practice_activity}/generate_practice_activity.dart (100%) rename lib/pangea/widgets/{practice_activity_card => practice_activity}/multiple_choice_activity.dart (95%) rename lib/pangea/widgets/{practice_activity_card/message_practice_activity_card.dart => practice_activity/practice_activity_card.dart} (83%) rename lib/pangea/widgets/{practice_activity_card/message_practice_activity_content.dart => practice_activity/practice_activity_content.dart} (97%) diff --git a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart index c9c05aca1..2932aae47 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart @@ -654,11 +654,17 @@ class PangeaMessageEvent { } } - List practiceActivities(String langCode) { + List practiceActivities(String langCode, + {bool debug = false}) { try { - return _practiceActivityEvents - .where((ev) => ev.practiceActivity.langCode == langCode) - .toList(); + debugger(when: debug); + final List activities = []; + for (final event in _practiceActivityEvents) { + if (event.practiceActivity.langCode == langCode) { + activities.add(event); + } + } + return activities; } catch (e, s) { debugger(when: kDebugMode); ErrorHandler.logError(e: e, s: s, data: event.toJson()); diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 9bb5aacbe..39729aa7c 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -16,7 +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/practice_activity/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'; diff --git a/lib/pangea/widgets/practice_activity_card/generate_practice_activity.dart b/lib/pangea/widgets/practice_activity/generate_practice_activity.dart similarity index 100% rename from lib/pangea/widgets/practice_activity_card/generate_practice_activity.dart rename to lib/pangea/widgets/practice_activity/generate_practice_activity.dart diff --git a/lib/pangea/widgets/practice_activity_card/multiple_choice_activity.dart b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart similarity index 95% rename from lib/pangea/widgets/practice_activity_card/multiple_choice_activity.dart rename to lib/pangea/widgets/practice_activity/multiple_choice_activity.dart index f74dc03ce..c2861ffe0 100644 --- a/lib/pangea/widgets/practice_activity_card/multiple_choice_activity.dart +++ b/lib/pangea/widgets/practice_activity/multiple_choice_activity.dart @@ -2,7 +2,7 @@ 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_card/message_practice_activity_content.dart'; +import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_content.dart'; import 'package:flutter/material.dart'; class MultipleChoiceActivity extends StatelessWidget { diff --git a/lib/pangea/widgets/practice_activity_card/message_practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart similarity index 83% rename from lib/pangea/widgets/practice_activity_card/message_practice_activity_card.dart rename to lib/pangea/widgets/practice_activity/practice_activity_card.dart index d69627fcb..f4312b8fd 100644 --- a/lib/pangea/widgets/practice_activity_card/message_practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -1,8 +1,11 @@ +import 'dart:developer'; + 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/widgets/practice_activity_card/generate_practice_activity.dart'; -import 'package:fluffychat/pangea/widgets/practice_activity_card/message_practice_activity_content.dart'; +import 'package:fluffychat/pangea/widgets/practice_activity/generate_practice_activity.dart'; +import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_content.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'; @@ -37,11 +40,16 @@ class MessagePracticeActivityCardState extends State { ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(L10n.of(context)!.noLanguagesSet)), ); + debugger(when: kDebugMode); return; } practiceEvent = widget.pangeaMessageEvent.practiceActivities(langCode).firstOrNull; + + if (practiceEvent == null) { + debugger(when: kDebugMode); + } setState(() {}); } diff --git a/lib/pangea/widgets/practice_activity_card/message_practice_activity_content.dart b/lib/pangea/widgets/practice_activity/practice_activity_content.dart similarity index 97% rename from lib/pangea/widgets/practice_activity_card/message_practice_activity_content.dart rename to lib/pangea/widgets/practice_activity/practice_activity_content.dart index 6b7ebf7d9..d5f387cfd 100644 --- a/lib/pangea/widgets/practice_activity_card/message_practice_activity_content.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_content.dart @@ -6,7 +6,7 @@ import 'package:fluffychat/pangea/matrix_event_wrappers/practice_acitivity_recor 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_card/multiple_choice_activity.dart'; +import 'package:fluffychat/pangea/widgets/practice_activity/multiple_choice_activity.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; From f667a35dce8fe4b2778c59358efb8d46cd190694 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Tue, 25 Jun 2024 12:19:27 -0400 Subject: [PATCH 18/31] hide generation button --- assets/l10n/intl_en.arb | 4 +- .../pangea_message_event.dart | 4 +- .../generate_practice_activity.dart | 2 +- .../practice_activity_card.dart | 12 +- needed-translations.txt | 200 +++++++++++++----- 5 files changed, 162 insertions(+), 60 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 4a27fd6da..b83a6ff08 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4055,5 +4055,7 @@ "spaceAnalytics": "Space Analytics", "changeAnalyticsLanguage": "Change Analytics Language", "suggestToSpace": "Suggest this space", - "suggestToSpaceDesc": "Suggested spaces will appear in the chat lists for their parent spaces" + "suggestToSpaceDesc": "Suggested spaces will appear in the chat lists for their parent spaces", + "practice": "Practice", + "noLanguagesSet": "No languages set" } \ 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 cf3ee492d..870d305a1 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart @@ -620,8 +620,8 @@ class PangeaMessageEvent { bool get hasActivities { try { - final String? l2code = MatrixState.pangeaController.languageController - .activeL2Code(roomID: room.id); + final String? l2code = + MatrixState.pangeaController.languageController.activeL2Code(); if (l2code == null) return false; diff --git a/lib/pangea/widgets/practice_activity/generate_practice_activity.dart b/lib/pangea/widgets/practice_activity/generate_practice_activity.dart index 7d01d8b4c..1eae97d63 100644 --- a/lib/pangea/widgets/practice_activity/generate_practice_activity.dart +++ b/lib/pangea/widgets/practice_activity/generate_practice_activity.dart @@ -21,7 +21,7 @@ class GeneratePracticeActivityButton extends StatelessWidget { return ElevatedButton( onPressed: () async { final String? l2Code = MatrixState.pangeaController.languageController - .activeL1Model(roomID: pangeaMessageEvent.room.id) + .activeL1Model() ?.langCode; if (l2Code == null) { diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index f4312b8fd..18713c2c9 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -2,7 +2,6 @@ import 'dart:developer'; 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/widgets/practice_activity/generate_practice_activity.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_content.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; @@ -33,7 +32,7 @@ class MessagePracticeActivityCardState extends State { void loadInitialData() { final String? langCode = MatrixState.pangeaController.languageController - .activeL2Model(roomID: widget.pangeaMessageEvent.room.id) + .activeL2Model() ?.langCode; if (langCode == null) { @@ -62,10 +61,11 @@ class MessagePracticeActivityCardState extends State { @override Widget build(BuildContext context) { if (practiceEvent == null) { - return GeneratePracticeActivityButton( - pangeaMessageEvent: widget.pangeaMessageEvent, - onActivityGenerated: updatePracticeActivity, - ); + return const Text('No practice activities found for this message'); + // return GeneratePracticeActivityButton( + // pangeaMessageEvent: widget.pangeaMessageEvent, + // onActivityGenerated: updatePracticeActivity, + // ); } return PracticeActivityContent( practiceEvent: practiceEvent!, diff --git a/needed-translations.txt b/needed-translations.txt index 6dae1ed19..d7704d03e 100644 --- a/needed-translations.txt +++ b/needed-translations.txt @@ -860,7 +860,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "be": [ @@ -2357,7 +2359,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "bn": [ @@ -3850,7 +3854,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "bo": [ @@ -5347,7 +5353,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "ca": [ @@ -6246,7 +6254,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "cs": [ @@ -7227,7 +7237,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "de": [ @@ -8091,7 +8103,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "el": [ @@ -9539,7 +9553,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "eo": [ @@ -10685,7 +10701,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "es": [ @@ -10697,7 +10715,9 @@ "addConversationBotButtonRemove", "addConversationBotDialogRemoveConfirmation", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "et": [ @@ -11561,7 +11581,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "eu": [ @@ -12427,7 +12449,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "fa": [ @@ -13430,7 +13454,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "fi": [ @@ -14397,7 +14423,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "fil": [ @@ -15720,7 +15748,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "fr": [ @@ -16722,7 +16752,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "ga": [ @@ -17853,7 +17885,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "gl": [ @@ -18717,7 +18751,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "he": [ @@ -19967,7 +20003,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "hi": [ @@ -21457,7 +21495,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "hr": [ @@ -22400,7 +22440,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "hu": [ @@ -23280,7 +23322,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "ia": [ @@ -24763,7 +24807,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "id": [ @@ -25633,7 +25679,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "ie": [ @@ -26887,7 +26935,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "it": [ @@ -27808,7 +27858,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "ja": [ @@ -28840,7 +28892,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "ka": [ @@ -30191,7 +30245,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "ko": [ @@ -31057,7 +31113,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "lt": [ @@ -32089,7 +32147,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "lv": [ @@ -32961,7 +33021,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "nb": [ @@ -34157,7 +34219,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "nl": [ @@ -35117,7 +35181,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "pl": [ @@ -36086,7 +36152,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "pt": [ @@ -37561,7 +37629,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "pt_BR": [ @@ -38431,7 +38501,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "pt_PT": [ @@ -39628,7 +39700,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "ro": [ @@ -40632,7 +40706,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "ru": [ @@ -41502,7 +41578,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "sk": [ @@ -42765,7 +42843,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "sl": [ @@ -44158,7 +44238,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "sr": [ @@ -45325,7 +45407,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "sv": [ @@ -46226,7 +46310,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "ta": [ @@ -47720,7 +47806,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "th": [ @@ -49168,7 +49256,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "tr": [ @@ -50032,7 +50122,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "uk": [ @@ -50933,7 +51025,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "vi": [ @@ -52282,7 +52376,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "zh": [ @@ -53146,7 +53242,9 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ], "zh_Hant": [ @@ -54291,6 +54389,8 @@ "spaceAnalytics", "changeAnalyticsLanguage", "suggestToSpace", - "suggestToSpaceDesc" + "suggestToSpaceDesc", + "practice", + "noLanguagesSet" ] } From 401d8522f8fda3184d823f58517867b19d2ae6d2 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 25 Jun 2024 12:53:54 -0400 Subject: [PATCH 19/31] only show practice activity button if showMessageButtons is enabled --- lib/pages/chat/events/message.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 086b5fbbf..c5756438c 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -587,7 +587,8 @@ class Message extends StatelessWidget { ? MainAxisAlignment.end : MainAxisAlignment.start, children: [ - MessageButtons(toolbarController: toolbarController), + if (pangeaMessageEvent?.showMessageButtons ?? false) + MessageButtons(toolbarController: toolbarController), MessageReactions(event, timeline), ], ), From 50af1e55085941290367235245b022af85dba97e Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 25 Jun 2024 13:20:58 -0400 Subject: [PATCH 20/31] make practice card text bot style, change opacity of icon buttons based on if they're available / enabled --- assets/l10n/intl_en.arb | 3 +- lib/pangea/enum/message_mode_enum.dart | 26 +++ lib/pangea/widgets/chat/message_toolbar.dart | 8 +- .../practice_activity_card.dart | 6 +- needed-translations.txt | 150 ++++++++++++------ 5 files changed, 138 insertions(+), 55 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index b83a6ff08..056c6a347 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4057,5 +4057,6 @@ "suggestToSpace": "Suggest this space", "suggestToSpaceDesc": "Suggested spaces will appear in the chat lists for their parent spaces", "practice": "Practice", - "noLanguagesSet": "No languages set" + "noLanguagesSet": "No languages set", + "noActivitiesFound": "No practice activities found for this message" } \ No newline at end of file diff --git a/lib/pangea/enum/message_mode_enum.dart b/lib/pangea/enum/message_mode_enum.dart index f25140a9c..58753e5b5 100644 --- a/lib/pangea/enum/message_mode_enum.dart +++ b/lib/pangea/enum/message_mode_enum.dart @@ -1,3 +1,4 @@ +import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:material_symbols_icons/symbols.dart'; @@ -79,4 +80,29 @@ extension MessageModeExtension on MessageMode { return true; } } + + Color? iconColor( + PangeaMessageEvent event, + MessageMode? currentMode, + BuildContext context, + ) { + final bool isPracticeActivity = this == MessageMode.practiceActivity; + final bool practicing = currentMode == MessageMode.practiceActivity; + final bool practiceEnabled = event.hasUncompletedActivity; + + // if this is the practice activity icon, and there's no practice activities available, + // and the current mode is not practice, return lower opacity color. + if (isPracticeActivity && !practicing && !practiceEnabled) { + return Theme.of(context).iconTheme.color?.withOpacity(0.5); + } + + // if this is not a practice activity icon, and practice activities are available, + // then return lower opacity color if the current mode is practice. + if (!isPracticeActivity && practicing && practiceEnabled) { + return Theme.of(context).iconTheme.color?.withOpacity(0.5); + } + + // if this is the current mode, return primary color. + return currentMode == this ? Theme.of(context).colorScheme.primary : null; + } } diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 9205a4246..1ce5f2f3d 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -418,9 +418,11 @@ class MessageToolbarState extends State { message: mode.tooltip(context), child: IconButton( icon: Icon(mode.icon), - color: currentMode == mode - ? Theme.of(context).colorScheme.primary - : null, + color: mode.iconColor( + widget.pangeaMessageEvent, + currentMode, + context, + ), onPressed: () => updateMode(mode), ), ); diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index 18713c2c9..ae8028702 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -2,6 +2,7 @@ import 'dart:developer'; 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/utils/bot_style.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_content.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; @@ -61,7 +62,10 @@ class MessagePracticeActivityCardState extends State { @override Widget build(BuildContext context) { if (practiceEvent == null) { - return const Text('No practice activities found for this message'); + return Text( + L10n.of(context)!.noActivitiesFound, + style: BotStyle.text(context), + ); // return GeneratePracticeActivityButton( // pangeaMessageEvent: widget.pangeaMessageEvent, // onActivityGenerated: updatePracticeActivity, diff --git a/needed-translations.txt b/needed-translations.txt index d7704d03e..1355bb368 100644 --- a/needed-translations.txt +++ b/needed-translations.txt @@ -862,7 +862,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "be": [ @@ -2361,7 +2362,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "bn": [ @@ -3856,7 +3858,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "bo": [ @@ -5355,7 +5358,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "ca": [ @@ -6256,7 +6260,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "cs": [ @@ -7239,7 +7244,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "de": [ @@ -8105,7 +8111,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "el": [ @@ -9555,7 +9562,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "eo": [ @@ -10703,7 +10711,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "es": [ @@ -10717,7 +10726,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "et": [ @@ -11583,7 +11593,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "eu": [ @@ -12451,7 +12462,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "fa": [ @@ -13456,7 +13468,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "fi": [ @@ -14425,7 +14438,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "fil": [ @@ -15750,7 +15764,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "fr": [ @@ -16754,7 +16769,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "ga": [ @@ -17887,7 +17903,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "gl": [ @@ -18753,7 +18770,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "he": [ @@ -20005,7 +20023,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "hi": [ @@ -21497,7 +21516,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "hr": [ @@ -22442,7 +22462,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "hu": [ @@ -23324,7 +23345,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "ia": [ @@ -24809,7 +24831,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "id": [ @@ -25681,7 +25704,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "ie": [ @@ -26937,7 +26961,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "it": [ @@ -27860,7 +27885,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "ja": [ @@ -28894,7 +28920,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "ka": [ @@ -30247,7 +30274,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "ko": [ @@ -31115,7 +31143,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "lt": [ @@ -32149,7 +32178,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "lv": [ @@ -33023,7 +33053,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "nb": [ @@ -34221,7 +34252,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "nl": [ @@ -35183,7 +35215,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "pl": [ @@ -36154,7 +36187,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "pt": [ @@ -37631,7 +37665,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "pt_BR": [ @@ -38503,7 +38538,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "pt_PT": [ @@ -39702,7 +39738,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "ro": [ @@ -40708,7 +40745,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "ru": [ @@ -41580,7 +41618,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "sk": [ @@ -42845,7 +42884,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "sl": [ @@ -44240,7 +44280,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "sr": [ @@ -45409,7 +45450,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "sv": [ @@ -46312,7 +46354,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "ta": [ @@ -47808,7 +47851,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "th": [ @@ -49258,7 +49302,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "tr": [ @@ -50124,7 +50169,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "uk": [ @@ -51027,7 +51073,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "vi": [ @@ -52378,7 +52425,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "zh": [ @@ -53244,7 +53292,8 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ], "zh_Hant": [ @@ -54391,6 +54440,7 @@ "suggestToSpace", "suggestToSpaceDesc", "practice", - "noLanguagesSet" + "noLanguagesSet", + "noActivitiesFound" ] } From 965308d6289987d5d6e66026bc7c4f1c3eb1bd9c Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 25 Jun 2024 14:12:33 -0400 Subject: [PATCH 21/31] show next activity after completing activity --- lib/pangea/widgets/chat/message_toolbar.dart | 6 ++- .../practice_activity_card.dart | 44 ++++++++++++++++--- .../practice_activity_content.dart | 5 ++- 3 files changed, 45 insertions(+), 10 deletions(-) diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 1ce5f2f3d..434128b1e 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -297,8 +297,10 @@ class MessageToolbarState extends State { } void showPracticeActivity() { - toolbarContent = - PracticeActivityCard(pangeaMessageEvent: widget.pangeaMessageEvent); + toolbarContent = PracticeActivityCard( + pangeaMessageEvent: widget.pangeaMessageEvent, + controller: this, + ); } void showImage() {} diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index ae8028702..c5ac1e6d1 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -1,8 +1,11 @@ import 'dart:developer'; +import 'package:collection/collection.dart'; +import 'package:fluffychat/pangea/enum/message_mode_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/utils/bot_style.dart'; +import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; import 'package:fluffychat/pangea/widgets/practice_activity/practice_activity_content.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; @@ -11,10 +14,12 @@ import 'package:flutter_gen/gen_l10n/l10n.dart'; class PracticeActivityCard extends StatefulWidget { final PangeaMessageEvent pangeaMessageEvent; + final MessageToolbarState controller; const PracticeActivityCard({ super.key, required this.pangeaMessageEvent, + required this.controller, }); @override @@ -31,7 +36,7 @@ class MessagePracticeActivityCardState extends State { loadInitialData(); } - void loadInitialData() { + String? get langCode { final String? langCode = MatrixState.pangeaController.languageController .activeL2Model() ?.langCode; @@ -41,24 +46,48 @@ class MessagePracticeActivityCardState extends State { SnackBar(content: Text(L10n.of(context)!.noLanguagesSet)), ); debugger(when: kDebugMode); - return; + return null; } + return langCode; + } - practiceEvent = - widget.pangeaMessageEvent.practiceActivities(langCode).firstOrNull; + void loadInitialData() { + if (langCode == null) return; + debugPrint( + "total events: ${widget.pangeaMessageEvent.practiceActivities(langCode!).length}", + ); + debugPrint( + "incomplete practice events: ${widget.pangeaMessageEvent.practiceActivities(langCode!).where((element) => !element.isComplete).length}", + ); + updatePracticeActivity(); + // practiceEvent = widget.pangeaMessageEvent + // .practiceActivities(langCode) + // .firstWhereOrNull((activity) => !activity.isComplete); if (practiceEvent == null) { debugger(when: kDebugMode); } - setState(() {}); } - void updatePracticeActivity(PracticeActivityEvent? newEvent) { + void updatePracticeActivity() { + if (langCode == null) return; setState(() { - practiceEvent = newEvent; + practiceEvent = widget.pangeaMessageEvent + .practiceActivities(langCode!) + .firstWhereOrNull( + (activity) => + activity.event.eventId != practiceEvent?.event.eventId && + !activity.isComplete, + ); }); } + void showNextActivity() { + if (langCode == null) return; + updatePracticeActivity(); + widget.controller.updateMode(MessageMode.practiceActivity); + } + @override Widget build(BuildContext context) { if (practiceEvent == null) { @@ -74,6 +103,7 @@ class MessagePracticeActivityCardState extends State { return PracticeActivityContent( practiceEvent: practiceEvent!, pangeaMessageEvent: widget.pangeaMessageEvent, + controller: this, ); } } diff --git a/lib/pangea/widgets/practice_activity/practice_activity_content.dart b/lib/pangea/widgets/practice_activity/practice_activity_content.dart index d5f387cfd..6f77741ed 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_content.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_content.dart @@ -7,6 +7,7 @@ import 'package:fluffychat/pangea/matrix_event_wrappers/practice_activity_event. 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'; @@ -14,11 +15,13 @@ 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 @@ -93,7 +96,7 @@ class MessagePracticeActivityContentState }, ); return null; - }); + }).then((_) => widget.controller.showNextActivity()); setState(() { recordSubmittedThisSession = true; From d959fdcc7bd6fa2c6eb1dc35f2ab5e0336fc2363 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Tue, 25 Jun 2024 14:24:13 -0400 Subject: [PATCH 22/31] Checks if visible rooms for my analytics --- lib/pages/chat_list/client_chooser_button.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pages/chat_list/client_chooser_button.dart b/lib/pages/chat_list/client_chooser_button.dart index e45b38c33..4ad107f6b 100644 --- a/lib/pages/chat_list/client_chooser_button.dart +++ b/lib/pages/chat_list/client_chooser_button.dart @@ -1,5 +1,6 @@ import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:fluffychat/pangea/constants/class_default_values.dart'; +import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/utils/find_conversation_partner_dialog.dart'; import 'package:fluffychat/pangea/utils/logout.dart'; import 'package:fluffychat/pangea/utils/space_code.dart'; @@ -68,7 +69,9 @@ class ClientChooserButton extends StatelessWidget { ), ), PopupMenuItem( - enabled: matrix.client.rooms.isNotEmpty, + enabled: matrix.client.rooms.any( + (room) => !room.isSpace && !room.isArchived && !room.isAnalyticsRoom, + ), value: SettingsAction.myAnalytics, child: Row( children: [ From 2227c9d22d9f25c3de7aa5546649b013bc614f95 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 25 Jun 2024 14:41:45 -0400 Subject: [PATCH 23/31] update activity data when activity event id changes, show last activity if there are no incomplete activities --- .../practice_activity_card.dart | 36 +++++++++---------- .../practice_activity_content.dart | 14 ++++++++ 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index c5ac1e6d1..ce888f0fb 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -1,6 +1,5 @@ import 'dart:developer'; -import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/enum/message_mode_enum.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/practice_activity_event.dart'; @@ -53,17 +52,7 @@ class MessagePracticeActivityCardState extends State { void loadInitialData() { if (langCode == null) return; - debugPrint( - "total events: ${widget.pangeaMessageEvent.practiceActivities(langCode!).length}", - ); - debugPrint( - "incomplete practice events: ${widget.pangeaMessageEvent.practiceActivities(langCode!).where((element) => !element.isComplete).length}", - ); updatePracticeActivity(); - // practiceEvent = widget.pangeaMessageEvent - // .practiceActivities(langCode) - // .firstWhereOrNull((activity) => !activity.isComplete); - if (practiceEvent == null) { debugger(when: kDebugMode); } @@ -71,15 +60,22 @@ class MessagePracticeActivityCardState extends State { void updatePracticeActivity() { if (langCode == null) return; - setState(() { - practiceEvent = widget.pangeaMessageEvent - .practiceActivities(langCode!) - .firstWhereOrNull( - (activity) => - activity.event.eventId != practiceEvent?.event.eventId && - !activity.isComplete, - ); - }); + final List activities = + widget.pangeaMessageEvent.practiceActivities(langCode!); + final List incompleteActivities = + activities.where((element) => !element.isComplete).toList(); + debugPrint("total events: ${activities.length}"); + debugPrint("incomplete practice events: ${incompleteActivities.length}"); + + // 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; + } + setState(() {}); } void showNextActivity() { diff --git a/lib/pangea/widgets/practice_activity/practice_activity_content.dart b/lib/pangea/widgets/practice_activity/practice_activity_content.dart index 6f77741ed..563904e42 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_content.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_content.dart @@ -41,6 +41,19 @@ class MessagePracticeActivityContentState @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) { @@ -54,6 +67,7 @@ class MessagePracticeActivityContentState recordSubmittedPreviousSession = true; recordSubmittedThisSession = true; } + setState(() {}); } void updateChoice(int index) { From 2cee0d374fb14726e46b1fc022896d0385070646 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Tue, 25 Jun 2024 14:51:47 -0400 Subject: [PATCH 24/31] Remove chat sorting edits --- lib/pages/chat_list/chat_list.dart | 33 ++++++------------------------ 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index 9d7ff2440..569eba880 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:io'; import 'package:adaptive_dialog/adaptive_dialog.dart'; -import 'package:collection/collection.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat_list/chat_list_view.dart'; @@ -213,32 +212,12 @@ class ChatListController extends State } List get filteredRooms => Matrix.of(context) - .client - .rooms - .where( - getRoomFilterByActiveFilter(activeFilter), - ) - // #Pangea - .sorted((roomA, roomB) { - // put rooms with unread messages at the top of the list - if (roomA.membership == Membership.invite && - roomB.membership != Membership.invite) { - return -1; - } - if (roomA.membership != Membership.invite && - roomB.membership == Membership.invite) { - return 1; - } - - final bool aUnread = roomA.notificationCount > 0 || roomA.markedUnread; - final bool bUnread = roomB.notificationCount > 0 || roomB.markedUnread; - if (aUnread && !bUnread) return -1; - if (!aUnread && bUnread) return 1; - - return 0; - }) - // Pangea# - .toList(); + .client + .rooms + .where( + getRoomFilterByActiveFilter(activeFilter), + ) + .toList(); bool isSearchMode = false; Future? publicRoomsResponse; From 76d26f1c0ccf2d533144e491c2d2f3c88803d019 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 25 Jun 2024 15:19:27 -0400 Subject: [PATCH 25/31] populate space analytics language dropdown from space student's analytics rooms --- .../pangea_room_extension.dart | 6 +++ .../space_settings_extension.dart | 28 +++++++++++ .../pages/analytics/base_analytics.dart | 8 +++- .../pages/analytics/base_analytics_view.dart | 3 +- .../space_analytics/space_analytics.dart | 39 ++++++++-------- .../space_analytics/space_analytics_view.dart | 1 + .../analytics/space_list/space_list.dart | 46 ++++++++++++++----- .../analytics/space_list/space_list_view.dart | 4 +- 8 files changed, 99 insertions(+), 36 deletions(-) diff --git a/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart b/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart index 6219c62d2..b8dae7ab3 100644 --- a/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart @@ -4,14 +4,17 @@ import 'dart:developer'; import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/constants/class_default_values.dart'; +import 'package:fluffychat/pangea/constants/language_keys.dart'; import 'package:fluffychat/pangea/constants/model_keys.dart'; import 'package:fluffychat/pangea/constants/pangea_room_types.dart'; +import 'package:fluffychat/pangea/controllers/language_list_controller.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/models/analytics/analytics_event.dart'; import 'package:fluffychat/pangea/models/analytics/constructs_event.dart'; import 'package:fluffychat/pangea/models/analytics/summary_analytics_event.dart'; import 'package:fluffychat/pangea/models/analytics/summary_analytics_model.dart'; import 'package:fluffychat/pangea/models/bot_options_model.dart'; +import 'package:fluffychat/pangea/models/language_model.dart'; import 'package:fluffychat/pangea/models/space_model.dart'; import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/utils/bot_name.dart'; @@ -129,6 +132,9 @@ extension PangeaRoom on Room { Event? get pangeaRoomRulesStateEvent => _pangeaRoomRulesStateEvent; + Future> targetLanguages() async => + await _targetLanguages(); + // events Future leaveIfFull() async => await _leaveIfFull(); diff --git a/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart b/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart index 00efb6773..5799631b1 100644 --- a/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/space_settings_extension.dart @@ -92,6 +92,34 @@ extension SpaceRoomExtension on Room { return null; } + Future> _targetLanguages() async { + await requestParticipants(); + final students = _students; + + final Map langCounts = {}; + final List allRooms = client.rooms; + for (final User student in students) { + for (final Room room in allRooms) { + if (!room.isAnalyticsRoomOfUser(student.id)) continue; + final String? langCode = room.madeForLang; + if (langCode == null || + langCode.isEmpty || + langCode == LanguageKeys.unknownLanguage) { + continue; + } + final LanguageModel lang = PangeaLanguage.byLangCode(langCode); + langCounts[lang] ??= 0; + langCounts[lang] = langCounts[lang]! + 1; + } + } + // get a list of language models, sorted + // by the number of students who are learning that language + return langCounts.entries.map((entry) => entry.key).toList() + ..sort( + (a, b) => langCounts[b]!.compareTo(langCounts[a]!), + ); + } + // DateTime? get _languageSettingsUpdatedAt { // if (!isSpace) return null; // return languageSettingsStateEvent?.originServerTs ?? creationTime; diff --git a/lib/pangea/pages/analytics/base_analytics.dart b/lib/pangea/pages/analytics/base_analytics.dart index f62e5f6b5..3ab8fa0f2 100644 --- a/lib/pangea/pages/analytics/base_analytics.dart +++ b/lib/pangea/pages/analytics/base_analytics.dart @@ -25,8 +25,9 @@ class BaseAnalyticsPage extends StatefulWidget { final AnalyticsSelected defaultSelected; final AnalyticsSelected? alwaysSelected; final StudentAnalyticsController? myAnalyticsController; + final List targetLanguages; - const BaseAnalyticsPage({ + BaseAnalyticsPage({ super.key, required this.pageTitle, required this.tabs, @@ -34,7 +35,10 @@ class BaseAnalyticsPage extends StatefulWidget { required this.defaultSelected, this.selectedView, this.myAnalyticsController, - }); + targetLanguages, + }) : targetLanguages = (targetLanguages?.isNotEmpty ?? false) + ? targetLanguages + : MatrixState.pangeaController.pLanguageStore.targetOptions; @override State createState() => BaseAnalyticsController(); diff --git a/lib/pangea/pages/analytics/base_analytics_view.dart b/lib/pangea/pages/analytics/base_analytics_view.dart index 1c0445d5a..8cab95396 100644 --- a/lib/pangea/pages/analytics/base_analytics_view.dart +++ b/lib/pangea/pages/analytics/base_analytics_view.dart @@ -128,8 +128,7 @@ class BaseAnalyticsView extends StatelessWidget { value: controller.pangeaController.analytics .currentAnalyticsSpaceLang, onChange: (lang) => controller.toggleSpaceLang(lang), - languages: controller - .pangeaController.pLanguageStore.targetOptions, + languages: controller.widget.targetLanguages, ), ], ), diff --git a/lib/pangea/pages/analytics/space_analytics/space_analytics.dart b/lib/pangea/pages/analytics/space_analytics/space_analytics.dart index 64875bfba..b32780761 100644 --- a/lib/pangea/pages/analytics/space_analytics/space_analytics.dart +++ b/lib/pangea/pages/analytics/space_analytics/space_analytics.dart @@ -4,6 +4,7 @@ import 'dart:developer'; import 'package:fluffychat/pangea/constants/pangea_room_types.dart'; import 'package:fluffychat/pangea/enum/bar_chart_view_enum.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; +import 'package:fluffychat/pangea/models/language_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/widgets/common/list_placeholder.dart'; import 'package:fluffychat/pangea/widgets/common/p_circular_loader.dart'; @@ -33,6 +34,18 @@ class SpaceAnalyticsV2Controller extends State { List students = []; String? get spaceId => GoRouterState.of(context).pathParameters['spaceid']; Room? _spaceRoom; + List targetLanguages = []; + + @override + void initState() { + super.initState(); + Future.delayed(Duration.zero, () async { + if (spaceRoom == null || (!(spaceRoom?.isSpace ?? false))) { + context.go('/rooms'); + } + getChatAndStudents(); + }); + } Room? get spaceRoom { if (_spaceRoom == null || _spaceRoom!.id != spaceId) { @@ -44,23 +57,11 @@ class SpaceAnalyticsV2Controller extends State { context.go('/rooms/analytics'); return null; } - getChatAndStudents(); + getChatAndStudents().then((_) => setTargetLanguages()); } return _spaceRoom; } - @override - void initState() { - super.initState(); - debugPrint("init space analytics"); - Future.delayed(Duration.zero, () async { - if (spaceRoom == null || (!(spaceRoom?.isSpace ?? false))) { - context.go('/rooms'); - } - getChatAndStudents(); - }); - } - Future getChatAndStudents() async { try { await spaceRoom?.postLoad(); @@ -97,12 +98,12 @@ class SpaceAnalyticsV2Controller extends State { } } - // @override - // void dispose() { - // super.dispose(); - // refreshTimer?.cancel(); - // stateSub?.cancel(); - // } + Future setTargetLanguages() async { + // get a list of language models, sorted by the + // number of students who are learning that language + targetLanguages = await spaceRoom?.targetLanguages() ?? []; + setState(() {}); + } @override Widget build(BuildContext context) { diff --git a/lib/pangea/pages/analytics/space_analytics/space_analytics_view.dart b/lib/pangea/pages/analytics/space_analytics/space_analytics_view.dart index 5e0008555..c72ec3c26 100644 --- a/lib/pangea/pages/analytics/space_analytics/space_analytics_view.dart +++ b/lib/pangea/pages/analytics/space_analytics/space_analytics_view.dart @@ -59,6 +59,7 @@ class SpaceAnalyticsView extends StatelessWidget { AnalyticsEntryType.space, controller.spaceRoom?.name ?? "", ), + targetLanguages: controller.targetLanguages, ) : const SizedBox(); } diff --git a/lib/pangea/pages/analytics/space_list/space_list.dart b/lib/pangea/pages/analytics/space_list/space_list.dart index 058d54e63..e325e1e94 100644 --- a/lib/pangea/pages/analytics/space_list/space_list.dart +++ b/lib/pangea/pages/analytics/space_list/space_list.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:fluffychat/pangea/enum/time_span.dart'; import 'package:fluffychat/pangea/extensions/client_extension/client_extension.dart'; +import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/models/language_model.dart'; import 'package:fluffychat/pangea/pages/analytics/space_list/space_list_view.dart'; import 'package:flutter/material.dart'; @@ -22,26 +23,47 @@ class AnalyticsSpaceList extends StatefulWidget { class AnalyticsSpaceListController extends State { PangeaController pangeaController = MatrixState.pangeaController; List spaces = []; + List targetLanguages = []; @override void initState() { super.initState(); - Matrix.of(context).client.spacesImTeaching.then((spaceList) { - spaceList = spaceList - .where( - (space) => !spaceList.any( - (parentSpace) => parentSpace.spaceChildren - .any((child) => child.roomId == space.id), - ), - ) - .toList(); - spaces = spaceList; - setState(() {}); - }); + + setSpaceList().then((_) => setTargetLanguages()); } StreamController refreshStream = StreamController.broadcast(); + Future setSpaceList() async { + final spaceList = await Matrix.of(context).client.spacesImTeaching; + spaces = spaceList + .where( + (space) => !spaceList.any( + (parentSpace) => parentSpace.spaceChildren + .any((child) => child.roomId == space.id), + ), + ) + .toList(); + setState(() {}); + } + + Future setTargetLanguages() async { + if (spaces.isEmpty) return; + final Map langCounts = {}; + for (final Room space in spaces) { + final List targetLangs = await space.targetLanguages(); + for (final LanguageModel lang in targetLangs) { + langCounts[lang] ??= 0; + langCounts[lang] = langCounts[lang]! + 1; + } + } + targetLanguages = langCounts.entries.map((entry) => entry.key).toList() + ..sort( + (a, b) => langCounts[b]!.compareTo(langCounts[a]!), + ); + setState(() {}); + } + void toggleTimeSpan(BuildContext context, TimeSpan timeSpan) { pangeaController.analytics.setCurrentAnalyticsTimeSpan(timeSpan); refreshStream.add(false); diff --git a/lib/pangea/pages/analytics/space_list/space_list_view.dart b/lib/pangea/pages/analytics/space_list/space_list_view.dart index 5f5bf22da..1837d35f2 100644 --- a/lib/pangea/pages/analytics/space_list/space_list_view.dart +++ b/lib/pangea/pages/analytics/space_list/space_list_view.dart @@ -45,7 +45,9 @@ class AnalyticsSpaceListView extends StatelessWidget { value: controller.pangeaController.analytics.currentAnalyticsSpaceLang, onChange: (lang) => controller.toggleSpaceLang(lang), - languages: controller.pangeaController.pLanguageStore.targetOptions, + languages: controller.targetLanguages.isEmpty + ? controller.pangeaController.pLanguageStore.targetOptions + : controller.targetLanguages, ), ], ), From a9ee9061b3ee178d1dabe9455129639be4b31c9d Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Tue, 25 Jun 2024 15:24:58 -0400 Subject: [PATCH 26/31] fixing latestResponse --- .../multiple_choice_activity_model.dart | 2 ++ .../practice_activity_record_model.dart | 4 ++-- .../widgets/practice_activity/practice_activity_card.dart | 6 ++++++ .../practice_activity/practice_activity_content.dart | 8 +++++++- 4 files changed, 17 insertions(+), 3 deletions(-) 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 e152c18a2..3cd78a66a 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 @@ -18,6 +18,8 @@ class MultipleChoice { int get correctAnswerIndex => choices.indexOf(answer); + int choiceIndex(String choice) => choices.indexOf(choice); + Color choiceColor(int index) => index == correctAnswerIndex ? AppConfig.success : AppConfig.warning; diff --git a/lib/pangea/models/practice_activities.dart/practice_activity_record_model.dart b/lib/pangea/models/practice_activities.dart/practice_activity_record_model.dart index 93ec8e478..3fe3e859d 100644 --- a/lib/pangea/models/practice_activities.dart/practice_activity_record_model.dart +++ b/lib/pangea/models/practice_activities.dart/practice_activity_record_model.dart @@ -40,12 +40,12 @@ class PracticeActivityRecordModel { /// get the latest response index according to the response timeStamp /// sort the responses by timestamp and get the index of the last response - int? get latestResponseIndex { + String? get latestResponse { if (responses.isEmpty) { return null; } responses.sort((a, b) => a.timestamp.compareTo(b.timestamp)); - return responses.length - 1; + return responses[responses.length - 1].text; } void addResponse({ diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index ce888f0fb..f772bdfc8 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -50,6 +50,7 @@ class MessagePracticeActivityCardState extends State { return langCode; } +<<<<<<< Updated upstream void loadInitialData() { if (langCode == null) return; updatePracticeActivity(); @@ -75,6 +76,11 @@ class MessagePracticeActivityCardState extends State { else if (activities.isNotEmpty) { practiceEvent = activities.last; } +======= + practiceEvent = + widget.pangeaMessageEvent.practiceActivities(langCode).firstOrNull; + +>>>>>>> Stashed changes setState(() {}); } diff --git a/lib/pangea/widgets/practice_activity/practice_activity_content.dart b/lib/pangea/widgets/practice_activity/practice_activity_content.dart index 563904e42..8080c27ee 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_content.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_content.dart @@ -63,7 +63,13 @@ class MessagePracticeActivityContentState ); } else { recordModel = recordEvent!.record; - selectedChoiceIndex = recordModel!.latestResponseIndex; + + //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; } From 22af3d096a02dc5d284076a5a72e217fc7eb3887 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Tue, 25 Jun 2024 15:33:30 -0400 Subject: [PATCH 27/31] merged and resolved conflicy --- .../widgets/practice_activity/practice_activity_card.dart | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index f772bdfc8..ce888f0fb 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -50,7 +50,6 @@ class MessagePracticeActivityCardState extends State { return langCode; } -<<<<<<< Updated upstream void loadInitialData() { if (langCode == null) return; updatePracticeActivity(); @@ -76,11 +75,6 @@ class MessagePracticeActivityCardState extends State { else if (activities.isNotEmpty) { practiceEvent = activities.last; } -======= - practiceEvent = - widget.pangeaMessageEvent.practiceActivities(langCode).firstOrNull; - ->>>>>>> Stashed changes setState(() {}); } From 19bf01421fbbe93ada8e5fef5733917ac158842f Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 25 Jun 2024 17:14:51 -0400 Subject: [PATCH 28/31] for now, don't show the next activity after submitting an activity --- .../pangea_message_event.dart | 10 ++++++++-- .../practice_activity_card.dart | 19 +++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart index 870d305a1..951b77dfc 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart @@ -568,10 +568,16 @@ class PangeaMessageEvent { bool get hasUncompletedActivity { if (l2Code == null) return false; final List activities = practiceActivities(l2Code!); - if (activities.isEmpty) return false; - return !activities.every((activity) => activity.isComplete); + // 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); } String? get l2Code => diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index ce888f0fb..17c528b22 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -62,19 +62,22 @@ class MessagePracticeActivityCardState extends State { if (langCode == null) return; final List activities = 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 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; - } + // 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; + // } setState(() {}); } From c9d1b72773857611d8fe4848d0984b2b514ff018 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Wed, 26 Jun 2024 11:34:41 -0400 Subject: [PATCH 29/31] igc tweaks --- assets/l10n/intl_en.arb | 4 +- assets/l10n/intl_es.arb | 4 +- .../controllers/choreographer.dart | 11 +---- .../widgets/start_igc_button.dart | 48 ++++--------------- lib/pangea/enum/assistance_state_enum.dart | 43 +++++++++++++++++ 5 files changed, 57 insertions(+), 53 deletions(-) create mode 100644 lib/pangea/enum/assistance_state_enum.dart diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 056c6a347..cb319c9c7 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4010,9 +4010,9 @@ "wordsPerMinute": "Words per minute", "autoIGCToolName": "Run Language Assistance Automatically", "autoIGCToolDescription": "Automatically run language assistance after typing messages", - "runGrammarCorrection": "Run grammar correction", + "runGrammarCorrection": "Check message", "grammarCorrectionFailed": "Issues to address", - "grammarCorrectionComplete": "Grammar correction complete", + "grammarCorrectionComplete": "Looks good!", "leaveRoomDescription": "The chat will be moved to the archive. Other users will be able to see that you have left the chat.", "archiveSpaceDescription": "All chats within this space will be moved to the archive for yourself and other non-admin users.", "leaveSpaceDescription": "All chats within this space will be moved to the archive. Other users will be able to see that you have left the space.", diff --git a/assets/l10n/intl_es.arb b/assets/l10n/intl_es.arb index 0658bff50..dc328294a 100644 --- a/assets/l10n/intl_es.arb +++ b/assets/l10n/intl_es.arb @@ -4609,9 +4609,9 @@ "enterNumber": "Introduzca un valor numérico entero.", "autoIGCToolName": "Ejecutar automáticamente la asistencia lingüística", "autoIGCToolDescription": "Ejecutar automáticamente la asistencia lingüística después de escribir mensajes", - "runGrammarCorrection": "Corregir la gramática", + "runGrammarCorrection": "Comprobar mensaje", "grammarCorrectionFailed": "Cuestiones a tratar", - "grammarCorrectionComplete": "Corrección gramatical completa", + "grammarCorrectionComplete": "¡Se ve bien!", "leaveRoomDescription": "El chat se moverá al archivo. Los demás usuarios podrán ver que has abandonado el chat.", "archiveSpaceDescription": "Todos los chats de este espacio se moverán al archivo para ti y otros usuarios que no sean administradores.", "leaveSpaceDescription": "Todos los chats dentro de este espacio se moverán al archivo. Los demás usuarios podrán ver que has abandonado el espacio.", diff --git a/lib/pangea/choreographer/controllers/choreographer.dart b/lib/pangea/choreographer/controllers/choreographer.dart index 6ea029a6f..a0c6a5f6b 100644 --- a/lib/pangea/choreographer/controllers/choreographer.dart +++ b/lib/pangea/choreographer/controllers/choreographer.dart @@ -8,6 +8,7 @@ import 'package:fluffychat/pangea/choreographer/controllers/message_options.dart import 'package:fluffychat/pangea/constants/language_keys.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/controllers/subscription_controller.dart'; +import 'package:fluffychat/pangea/enum/assistance_state_enum.dart'; import 'package:fluffychat/pangea/enum/edit_type.dart'; import 'package:fluffychat/pangea/models/it_step.dart'; import 'package:fluffychat/pangea/models/language_detection_model.dart'; @@ -570,13 +571,3 @@ class Choreographer { return AssistanceState.complete; } } - -// assistance state is, user has not typed a message, user has typed a message and IGC has not run, -// IGC is running, IGC has run and there are remaining steps (either IT or IGC), or all steps are done -enum AssistanceState { - noMessage, - notFetched, - fetching, - fetched, - complete, -} diff --git a/lib/pangea/choreographer/widgets/start_igc_button.dart b/lib/pangea/choreographer/widgets/start_igc_button.dart index 49e4b078d..877158dd2 100644 --- a/lib/pangea/choreographer/widgets/start_igc_button.dart +++ b/lib/pangea/choreographer/widgets/start_igc_button.dart @@ -1,10 +1,9 @@ import 'dart:async'; import 'dart:math' as math; -import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pangea/choreographer/controllers/choreographer.dart'; -import 'package:fluffychat/pangea/constants/colors.dart'; import 'package:fluffychat/pangea/controllers/subscription_controller.dart'; +import 'package:fluffychat/pangea/enum/assistance_state_enum.dart'; import 'package:fluffychat/pangea/widgets/user_settings/p_language_dialog.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -54,15 +53,15 @@ class StartIGCButtonState extends State setState(() => prevState = assistanceState); } + bool get itEnabled => widget.controller.choreographer.itEnabled; + bool get igcEnabled => widget.controller.choreographer.igcEnabled; + CanSendStatus get canSendStatus => + widget.controller.pangeaController.subscriptionController.canSendStatus; + bool get grammarCorrectionEnabled => + (itEnabled || igcEnabled) && canSendStatus == CanSendStatus.subscribed; + @override Widget build(BuildContext context) { - final bool itEnabled = widget.controller.choreographer.itEnabled; - final bool igcEnabled = widget.controller.choreographer.igcEnabled; - final CanSendStatus canSendStatus = - widget.controller.pangeaController.subscriptionController.canSendStatus; - final bool grammarCorrectionEnabled = - (itEnabled || igcEnabled) && canSendStatus == CanSendStatus.subscribed; - if (!grammarCorrectionEnabled || widget.controller.choreographer.isAutoIGCEnabled || widget.controller.choreographer.choreoMode == ChoreoMode.it) { @@ -89,7 +88,7 @@ class StartIGCButtonState extends State disabledElevation: 0, shape: const CircleBorder(), onPressed: () { - if (assistanceState != AssistanceState.complete) { + if (assistanceState != AssistanceState.fetching) { widget.controller.choreographer .getLanguageHelp( false, @@ -142,32 +141,3 @@ class StartIGCButtonState extends State ); } } - -extension AssistanceStateExtension on AssistanceState { - Color stateColor(context) { - switch (this) { - case AssistanceState.noMessage: - case AssistanceState.notFetched: - case AssistanceState.fetching: - return Theme.of(context).colorScheme.primary; - case AssistanceState.fetched: - return PangeaColors.igcError; - case AssistanceState.complete: - return AppConfig.success; - } - } - - String tooltip(L10n l10n) { - switch (this) { - case AssistanceState.noMessage: - case AssistanceState.notFetched: - return l10n.runGrammarCorrection; - case AssistanceState.fetching: - return ""; - case AssistanceState.fetched: - return l10n.grammarCorrectionFailed; - case AssistanceState.complete: - return l10n.grammarCorrectionComplete; - } - } -} diff --git a/lib/pangea/enum/assistance_state_enum.dart b/lib/pangea/enum/assistance_state_enum.dart new file mode 100644 index 000000000..6d3a853da --- /dev/null +++ b/lib/pangea/enum/assistance_state_enum.dart @@ -0,0 +1,43 @@ +// assistance state is, user has not typed a message, user has typed a message and IGC has not run, +// IGC is running, IGC has run and there are remaining steps (either IT or IGC), or all steps are done +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/pangea/constants/colors.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/l10n.dart'; + +enum AssistanceState { + noMessage, + notFetched, + fetching, + fetched, + complete, +} + +extension AssistanceStateExtension on AssistanceState { + Color stateColor(context) { + switch (this) { + case AssistanceState.noMessage: + case AssistanceState.notFetched: + case AssistanceState.fetching: + return Theme.of(context).colorScheme.primary; + case AssistanceState.fetched: + return PangeaColors.igcError; + case AssistanceState.complete: + return AppConfig.success; + } + } + + String tooltip(L10n l10n) { + switch (this) { + case AssistanceState.noMessage: + case AssistanceState.notFetched: + return l10n.runGrammarCorrection; + case AssistanceState.fetching: + return ""; + case AssistanceState.fetched: + return l10n.grammarCorrectionFailed; + case AssistanceState.complete: + return l10n.grammarCorrectionComplete; + } + } +} From 59e521b69e758cc31c339de93369ce4f4a0971be Mon Sep 17 00:00:00 2001 From: Kelrap Date: Wed, 26 Jun 2024 12:27:45 -0400 Subject: [PATCH 30/31] Hide some Chat permission options --- .../chat_permissions_settings_view.dart | 111 +++++++++++------- .../subscription/subscription_options.dart | 2 +- 2 files changed, 68 insertions(+), 45 deletions(-) diff --git a/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart b/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart index ea81c842c..2b1dff187 100644 --- a/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart +++ b/lib/pages/chat_permissions_settings/chat_permissions_settings_view.dart @@ -35,11 +35,26 @@ class ChatPermissionsSettingsView extends StatelessWidget { final powerLevels = Map.from(powerLevelsContent) // #Pangea // ..removeWhere((k, v) => v is! int); - ..removeWhere((k, v) => v is! int || k.equals("m.call.invite")); + ..removeWhere( + (k, v) => + v is! int || + k.equals("m.call.invite") || + k.equals("historical") || + k.equals("state_default"), + ); // Pangea# final eventsPowerLevels = Map.from( powerLevelsContent.tryGetMap('events') ?? {}, - )..removeWhere((k, v) => v is! int); + // #Pangea + )..removeWhere( + (k, v) => + v is! int || + k.equals("m.space.child") || + k.equals("pangea.usranalytics") || + k.equals(EventTypes.RoomPowerLevels), + ); + // )..removeWhere((k, v) => v is! int); + // Pangea# return Column( children: [ Column( @@ -57,51 +72,59 @@ class ChatPermissionsSettingsView extends StatelessWidget { ), canEdit: room.canChangePowerLevel, ), - Divider(color: Theme.of(context).dividerColor), - ListTile( - title: Text( - L10n.of(context)!.notifications, - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - fontWeight: FontWeight.bold, - ), - ), - ), - Builder( - builder: (context) { - const key = 'rooms'; - final value = powerLevelsContent - .containsKey('notifications') - ? powerLevelsContent - .tryGetMap('notifications') - ?.tryGet('rooms') ?? - 0 - : 0; - return PermissionsListTile( - permissionKey: key, - permission: value, - category: 'notifications', - canEdit: room.canChangePowerLevel, - onChanged: (level) => controller.editPowerLevel( - context, - key, - value, - newLevel: level, - category: 'notifications', + // #Pangea + // Why would teacher need to stop students from seeing notifications? + // Divider(color: Theme.of(context).dividerColor), + // ListTile( + // title: Text( + // L10n.of(context)!.notifications, + // style: TextStyle( + // color: Theme.of(context).colorScheme.primary, + // fontWeight: FontWeight.bold, + // ), + // ), + // ), + // Builder( + // builder: (context) { + // const key = 'rooms'; + // final value = powerLevelsContent + // .containsKey('notifications') + // ? powerLevelsContent + // .tryGetMap('notifications') + // ?.tryGet('rooms') ?? + // 0 + // : 0; + // return PermissionsListTile( + // permissionKey: key, + // permission: value, + // category: 'notifications', + // canEdit: room.canChangePowerLevel, + // onChanged: (level) => controller.editPowerLevel( + // context, + // key, + // value, + // newLevel: level, + // category: 'notifications', + // ), + // ); + // }, + // ), + // Only show if there are actually items in this category + if (eventsPowerLevels.isNotEmpty) + // Pangea# + Divider(color: Theme.of(context).dividerColor), + // #Pangea + if (eventsPowerLevels.isNotEmpty) + // Pangea# + ListTile( + title: Text( + L10n.of(context)!.configureChat, + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold, ), - ); - }, - ), - Divider(color: Theme.of(context).dividerColor), - ListTile( - title: Text( - L10n.of(context)!.configureChat, - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - fontWeight: FontWeight.bold, ), ), - ), for (final entry in eventsPowerLevels.entries) PermissionsListTile( permissionKey: entry.key, diff --git a/lib/pangea/widgets/subscription/subscription_options.dart b/lib/pangea/widgets/subscription/subscription_options.dart index 41c8cbe4f..d40f68023 100644 --- a/lib/pangea/widgets/subscription/subscription_options.dart +++ b/lib/pangea/widgets/subscription/subscription_options.dart @@ -109,7 +109,7 @@ class SubscriptionCard extends StatelessWidget { title ?? subscription?.displayName(context) ?? '', textAlign: TextAlign.center, style: TextStyle( - fontSize: 24, + fontSize: 20, color: enabled ? null : const Color.fromARGB(255, 174, 174, 174), ), From 941508222731b9053a0df471769a65b4d4ca01f9 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 27 Jun 2024 10:21:33 -0400 Subject: [PATCH 31/31] base last updated on target lang --- .../controllers/my_analytics_controller.dart | 48 ++++++++++++------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/lib/pangea/controllers/my_analytics_controller.dart b/lib/pangea/controllers/my_analytics_controller.dart index ea0d06c56..614fcf9db 100644 --- a/lib/pangea/controllers/my_analytics_controller.dart +++ b/lib/pangea/controllers/my_analytics_controller.dart @@ -25,20 +25,23 @@ class MyAnalyticsController extends BaseController { final int _maxMessagesCached = 10; final int _minutesBeforeUpdate = 5; + /// the time since the last update that will trigger an automatic update + final Duration _timeSinceUpdate = const Duration(days: 1); + MyAnalyticsController(PangeaController pangeaController) { _pangeaController = pangeaController; } - // adds the listener that handles when to run automatic updates - // to analytics - either after a certain number of messages sent/ - // received or after a certain amount of time without an update + /// adds the listener that handles when to run automatic updates + /// to analytics - either after a certain number of messages sent/ + /// received or after a certain amount of time [_timeSinceUpdate] without an update Future addEventsListener() async { final Client client = _pangeaController.matrixState.client; // if analytics haven't been updated in the last day, update them DateTime? lastUpdated = await _pangeaController.analytics .myAnalyticsLastUpdated(PangeaEventTypes.summaryAnalytics); - final DateTime yesterday = DateTime.now().subtract(const Duration(days: 1)); + final DateTime yesterday = DateTime.now().subtract(_timeSinceUpdate); if (lastUpdated?.isBefore(yesterday) ?? true) { debugPrint("analytics out-of-date, updating"); await updateAnalytics(); @@ -53,9 +56,9 @@ class MyAnalyticsController extends BaseController { }); } - // given an update from sync stream, check if the update contains - // messages for which analytics will be saved. If so, reset the timer - // and add the event ID to the cache of un-added event IDs + /// given an update from sync stream, check if the update contains + /// messages for which analytics will be saved. If so, reset the timer + /// and add the event ID to the cache of un-added event IDs void updateAnalyticsTimer(SyncUpdate update, DateTime? lastUpdated) { for (final entry in update.rooms!.join!.entries) { final Room room = @@ -160,6 +163,7 @@ class MyAnalyticsController extends BaseController { _updateCompleter = Completer(); try { await _updateAnalytics(); + clearMessagesSinceUpdate(); } catch (err, s) { ErrorHandler.logError( e: err, @@ -172,6 +176,9 @@ class MyAnalyticsController extends BaseController { } } + // top level analytics sending function. Send analytics + // for each type of analytics event + // to each of the applicable analytics rooms Future _updateAnalytics() async { // if the user's l2 is not sent, don't send analytics final String? userL2 = _pangeaController.languageController.activeL2Code(); @@ -179,11 +186,6 @@ class MyAnalyticsController extends BaseController { return; } - // top level analytics sending function. Send analytics - // for each type of analytics event - // to each of the applicable analytics rooms - clearMessagesSinceUpdate(); - // fetch a list of all the chats that the user is studying // and a list of all the spaces in which the user is studying await setStudentChats(); @@ -199,9 +201,21 @@ class MyAnalyticsController extends BaseController { .where((lastUpdate) => lastUpdate != null) .cast() .toList(); - lastUpdates.sort((a, b) => a.compareTo(b)); - final DateTime? leastRecentUpdate = - lastUpdates.isNotEmpty ? lastUpdates.first : null; + + /// Get the last time that analytics to for current target language + /// were updated. This my present a problem is the user has analytics + /// rooms for multiple languages, and a non-target language was updated + /// less recently than the target language. In this case, some data may + /// be missing, but a case like that seems relatively rare, and could + /// result in unnecessaily going too far back in the chat history + DateTime? l2AnalyticsLastUpdated = lastUpdatedMap[userL2]; + if (l2AnalyticsLastUpdated == null) { + /// if the target language has never been updated, use the least + /// recent update time + lastUpdates.sort((a, b) => a.compareTo(b)); + l2AnalyticsLastUpdated = + lastUpdates.isNotEmpty ? lastUpdates.first : null; + } // for each chat the user is studying in, get all the messages // since the least recent update analytics update, and sort them @@ -209,7 +223,7 @@ class MyAnalyticsController extends BaseController { final Map> langCodeToMsgs = await getLangCodesToMsgs( userL2, - leastRecentUpdate, + l2AnalyticsLastUpdated, ); final List langCodes = langCodeToMsgs.keys.toList(); @@ -223,7 +237,7 @@ class MyAnalyticsController extends BaseController { // message in this language at the time of the last analytics update // so fallback to the least recent update time final DateTime? lastUpdated = - lastUpdatedMap[analyticsRoom.id] ?? leastRecentUpdate; + lastUpdatedMap[analyticsRoom.id] ?? l2AnalyticsLastUpdated; // get the corresponding list of recent messages for this langCode final List recentMsgs =