From 8478f480d35ca83883e1d0c702ee3fcaaede5935 Mon Sep 17 00:00:00 2001 From: wcjord <32568597+wcjord@users.noreply.github.com> Date: Mon, 27 Jan 2025 14:13:50 -0500 Subject: [PATCH] 1594-activity-planner-feedback-new (#1599) * feat(activity_planner): updating for feedback * fix(main): point to appropriate env --------- Co-authored-by: ggurdin --- assets/l10n/intl_en.arb | 9 +- .../activity_planner/activity_list_view.dart | 10 +- .../activity_planner/activity_plan_card.dart | 99 +++++++++++-------- .../activity_plan_request.dart | 6 +- .../activity_planner_page.dart | 58 +++++++++-- .../suggestion_form_field.dart | 3 + .../toolbar/widgets/overlay_header.dart | 5 +- 7 files changed, 138 insertions(+), 52 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index ba58fd2ab..e496fc55b 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -4757,7 +4757,7 @@ "modePlaceholder": "Choose a mode...", "learningObjectiveLabel": "Learning Objective", "learningObjectivePlaceholder": "Choose a learning objective...", - "mediaLabel": "Media students should share", + "mediaLabel": "Media learners should share", "languageOfInstructionsLabel": "Language of activity instructions", "targetLanguageLabel": "Target language", "cefrLevelLabel": "CEFR Level", @@ -4770,6 +4770,13 @@ "activityPlannerOverviewInstructionsBody": "Choose a topic, mode, learning objective and generate an activity for the chat!", "completeActivitiesToUnlock": "Complete the highlighted word activities to unlock", "myBookmarkedActivities": "My Bookmarked Activities", + "noBookmarkedActivities": "When you bookmark activities, they will appear here. Bookmarked activities can be re-used across spaces and chats.", + "activityTitle": "Activity Title", + "addVocabulary": "Add Vocabulary", + "instructions": "Instructions", + "bookmark": "Bookmark this activity", + "numberOfLearners": "Number of learners", + "mustBeInteger": "Must be an integer e.g. 1, 2, 3, ...", "noBookmarkedActivities": "No bookmarked activities", "noLemmasFound": "No lemmas found" } \ No newline at end of file diff --git a/lib/pangea/activity_planner/activity_list_view.dart b/lib/pangea/activity_planner/activity_list_view.dart index 91ca48486..a30b3cc7c 100644 --- a/lib/pangea/activity_planner/activity_list_view.dart +++ b/lib/pangea/activity_planner/activity_list_view.dart @@ -124,7 +124,15 @@ class ActivityListViewState extends State { ); } else if (!snapshot.hasData || snapshot.data!.isEmpty) { if (showBookmarkedActivities) { - return Center(child: Text(l10n.noBookmarkedActivities)); + return Center( + child: Container( + constraints: const BoxConstraints(maxWidth: 200), + child: Text( + l10n.noBookmarkedActivities, + textAlign: TextAlign.center, + ), + ), + ); } return Center(child: Text(l10n.noDataFound)); } else { diff --git a/lib/pangea/activity_planner/activity_plan_card.dart b/lib/pangea/activity_planner/activity_plan_card.dart index ecb9cd6a9..d7c2f8c78 100644 --- a/lib/pangea/activity_planner/activity_plan_card.dart +++ b/lib/pangea/activity_planner/activity_plan_card.dart @@ -35,6 +35,7 @@ class ActivityPlanCardState extends State { late TextEditingController _learningObjectiveController; late TextEditingController _instructionsController; final TextEditingController _newVocabController = TextEditingController(); + final FocusNode _vocabFocusNode = FocusNode(); @override void initState() { @@ -47,7 +48,7 @@ class ActivityPlanCardState extends State { TextEditingController(text: _tempActivity.instructions); } - static const double itemPadding = 8; + static const double itemPadding = 12; @override void dispose() { @@ -55,6 +56,7 @@ class ActivityPlanCardState extends State { _learningObjectiveController.dispose(); _instructionsController.dispose(); _newVocabController.dispose(); + _vocabFocusNode.dispose(); super.dispose(); } @@ -99,6 +101,7 @@ class ActivityPlanCardState extends State { setState(() { _tempActivity.vocab.add(Vocab(lemma: _newVocabController.text, pos: '')); _newVocabController.clear(); + _vocabFocusNode.requestFocus(); }); } @@ -134,7 +137,7 @@ class ActivityPlanCardState extends State { ? TextField( controller: _titleController, decoration: InputDecoration( - labelText: L10n.of(context).title, + labelText: L10n.of(context).activityTitle, ), maxLines: null, ) @@ -166,8 +169,8 @@ class ActivityPlanCardState extends State { child: _isEditing ? TextField( controller: _learningObjectiveController, - decoration: const InputDecoration( - labelText: 'Learning Objective', + decoration: InputDecoration( + labelText: l10n.learningObjectiveLabel, ), maxLines: null, ) @@ -190,8 +193,8 @@ class ActivityPlanCardState extends State { child: _isEditing ? TextField( controller: _instructionsController, - decoration: const InputDecoration( - labelText: 'Instructions', + decoration: InputDecoration( + labelText: l10n.instructions, ), maxLines: null, ) @@ -244,50 +247,62 @@ class ActivityPlanCardState extends State { ), ], ), - if (_isEditing) - Padding( - padding: const EdgeInsets.only(top: itemPadding), - child: Row( - children: [ - Expanded( - child: TextField( - controller: _newVocabController, - decoration: const InputDecoration( - labelText: 'Add Vocabulary', - ), - ), - ), - IconButton( - icon: const Icon(Icons.add), - onPressed: _addVocab, - ), - ], - ), - ), ], - const SizedBox(height: 16), + if (_isEditing) ...[ + const SizedBox(height: itemPadding), + Padding( + padding: const EdgeInsets.only(top: itemPadding), + child: Row( + children: [ + Expanded( + child: TextField( + controller: _newVocabController, + focusNode: _vocabFocusNode, + decoration: InputDecoration( + labelText: l10n.addVocabulary, + ), + onSubmitted: (value) { + _addVocab(); + }, + ), + ), + IconButton( + icon: const Icon(Icons.add), + onPressed: _addVocab, + ), + ], + ), + ), + ], + const SizedBox(height: itemPadding), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Row( children: [ - IconButton( - icon: Icon(!_isEditing ? Icons.edit : Icons.save), - onPressed: () => !_isEditing - ? setState(() { - _isEditing = true; - }) - : _saveEdits(), - isSelected: _isEditing, + Tooltip( + message: !_isEditing ? l10n.edit : l10n.saveChanges, + child: IconButton( + icon: Icon(!_isEditing ? Icons.edit : Icons.save), + onPressed: () => !_isEditing + ? setState(() { + _isEditing = true; + }) + : _saveEdits(), + isSelected: _isEditing, + ), ), if (_isEditing) - IconButton( - icon: const Icon(Icons.cancel), - onPressed: () { - setState(() { - _isEditing = false; - }); - }, + Tooltip( + message: l10n.cancel, + child: IconButton( + icon: const Icon(Icons.cancel), + onPressed: () { + setState(() { + _isEditing = false; + }); + }, + ), ), ], ), diff --git a/lib/pangea/activity_planner/activity_plan_request.dart b/lib/pangea/activity_planner/activity_plan_request.dart index a648f73e9..36fa91fc7 100644 --- a/lib/pangea/activity_planner/activity_plan_request.dart +++ b/lib/pangea/activity_planner/activity_plan_request.dart @@ -9,6 +9,7 @@ class ActivityPlanRequest { final String languageOfInstructions; final String targetLanguage; final int count; + final int numberOfParticipants; ActivityPlanRequest({ required this.topic, @@ -19,6 +20,7 @@ class ActivityPlanRequest { required this.languageOfInstructions, required this.targetLanguage, this.count = 3, + required this.numberOfParticipants, }); Map toJson() { @@ -31,6 +33,7 @@ class ActivityPlanRequest { 'language_of_instructions': languageOfInstructions, 'target_language': targetLanguage, 'count': count, + 'number_of_participants': numberOfParticipants, }; } @@ -68,11 +71,12 @@ class ActivityPlanRequest { languageOfInstructions: json['language_of_instructions'], targetLanguage: json['target_language'], count: json['count'], + numberOfParticipants: json['number_of_participants'], ); } String get storageKey => - '$topic-$mode-$objective-${media.string}-$cefrLevel-$languageOfInstructions-$targetLanguage'; + '$topic-$mode-$objective-${media.string}-$cefrLevel-$languageOfInstructions-$targetLanguage-$numberOfParticipants'; String get cefrLanguageLevel { switch (cefrLevel) { diff --git a/lib/pangea/activity_planner/activity_planner_page.dart b/lib/pangea/activity_planner/activity_planner_page.dart index 123de95b5..5003df285 100644 --- a/lib/pangea/activity_planner/activity_planner_page.dart +++ b/lib/pangea/activity_planner/activity_planner_page.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:dropdown_button2/dropdown_button2.dart'; @@ -47,6 +49,7 @@ class ActivityPlannerPageState extends State { String? _selectedLanguageOfInstructions; String? _selectedTargetLanguage; int? _selectedCefrLevel; + int? _selectedNumberOfParticipants; List activities = []; @@ -65,6 +68,7 @@ class ActivityPlannerPageState extends State { _selectedTargetLanguage = MatrixState.pangeaController.languageController.userL2?.langCode; _selectedCefrLevel = 0; + _selectedNumberOfParticipants = max(room?.getParticipants().length ?? 1, 1); } final _topicController = TextEditingController(); @@ -149,13 +153,34 @@ class ActivityPlannerPageState extends State { icon: const Icon(Icons.arrow_back), ), title: _pageMode == _PageMode.savedActivities - ? Text(l10n.myBookmarkedActivities) - : Text(l10n.activityPlannerTitle), + ? Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.bookmarks), + const SizedBox(width: 8), + Text(l10n.myBookmarkedActivities), + ], + ), + ) + : Center( + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.event_note_outlined), + const SizedBox(width: 8), + Text(l10n.activityPlannerTitle), + ], + ), + ), actions: [ - IconButton( - onPressed: () => - setState(() => _pageMode = _PageMode.savedActivities), - icon: const Icon(Icons.bookmarks), + Tooltip( + message: l10n.myBookmarkedActivities, + child: IconButton( + onPressed: () => + setState(() => _pageMode = _PageMode.savedActivities), + icon: const Icon(Icons.bookmarks), + ), ), ], ), @@ -172,6 +197,7 @@ class ActivityPlannerPageState extends State { languageOfInstructions: _selectedLanguageOfInstructions!, targetLanguage: _selectedTargetLanguage!, cefrLevel: _selectedCefrLevel!, + numberOfParticipants: _selectedNumberOfParticipants!, ), ) : Center( @@ -279,6 +305,26 @@ class ActivityPlannerPageState extends State { isL2List: true, ), const SizedBox(height: 24), + TextFormField( + decoration: InputDecoration( + labelText: l10n.numberOfLearners, + ), + keyboardType: TextInputType.number, + validator: (value) { + if (value == null || value.isEmpty) { + return l10n.mustBeInteger; + } + final n = int.tryParse(value); + if (n == null || n <= 0) { + return l10n.mustBeInteger; + } + return null; + }, + onChanged: (val) => + _selectedNumberOfParticipants = int.tryParse(val), + initialValue: _selectedNumberOfParticipants?.toString(), + ), + const SizedBox(height: 24), ElevatedButton( onPressed: () { if (_formKey.currentState?.validate() ?? false) { diff --git a/lib/pangea/activity_planner/suggestion_form_field.dart b/lib/pangea/activity_planner/suggestion_form_field.dart index 5d8538c01..96513d94f 100644 --- a/lib/pangea/activity_planner/suggestion_form_field.dart +++ b/lib/pangea/activity_planner/suggestion_form_field.dart @@ -43,6 +43,9 @@ class SuggestionFormField extends StatelessWidget { VoidCallback onFieldSubmitted, ) { textEditingController.value = controller.value; + textEditingController.addListener(() { + onSelected(textEditingController.text); + }); return TextFormField( controller: textEditingController, focusNode: focusNode, diff --git a/lib/pangea/toolbar/widgets/overlay_header.dart b/lib/pangea/toolbar/widgets/overlay_header.dart index 7e15885aa..bfee832b7 100644 --- a/lib/pangea/toolbar/widgets/overlay_header.dart +++ b/lib/pangea/toolbar/widgets/overlay_header.dart @@ -6,6 +6,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/chat/chat.dart'; +import 'package:fluffychat/pangea/common/constants/model_keys.dart'; class OverlayHeader extends StatelessWidget { final ChatController controller; @@ -53,7 +54,9 @@ class OverlayHeader extends StatelessWidget { tooltip: L10n.of(context).pinMessage, color: Theme.of(context).colorScheme.primary, ), - if (controller.canEditSelectedEvents) + if (controller.canEditSelectedEvents && + controller.selectedEvents.first.content[ModelKey.messageTags] != + ModelKey.messageTagActivityPlan) IconButton( icon: const Icon(Icons.edit_outlined), tooltip: L10n.of(context).edit,