diff --git a/lib/pangea/activity_generator/activity_generator.dart b/lib/pangea/activity_generator/activity_generator.dart index 40e4fdd82..3c8813854 100644 --- a/lib/pangea/activity_generator/activity_generator.dart +++ b/lib/pangea/activity_generator/activity_generator.dart @@ -49,7 +49,6 @@ class ActivityGeneratorState extends State { LanguageLevelTypeEnum? selectedCefrLevel; int? selectedNumberOfParticipants; - String? avatarURL; String? filename; @override @@ -186,11 +185,15 @@ class ActivityGeneratorState extends State { final modeName = mode.defaultName.toLowerCase().replaceAll(RegExp(r'\s+'), ''); - if (!mounted) return; + if (!mounted || activities == null) return; + final imageUrl = + "${AppConfig.assetsBaseURL}/${ActivitySuggestionsConstants.modeImageFileStart}$modeName.jpg"; setState(() { filename = "${ActivitySuggestionsConstants.modeImageFileStart}$modeName.jpg"; - avatarURL = "${AppConfig.assetsBaseURL}/$filename"; + for (final activity in activities!) { + activity.imageURL = imageUrl; + } }); } @@ -217,12 +220,9 @@ class ActivityGeneratorState extends State { }); try { - await _setModeImageURL(); final resp = await ActivityPlanGenerationRepo.get(planRequest); - for (final activity in resp.activityPlans) { - activity.imageURL = avatarURL; - } activities = resp.activityPlans; + await _setModeImageURL(); } catch (e, s) { error = e.toString(); ErrorHandler.logError( diff --git a/lib/pangea/activity_generator/activity_generator_view.dart b/lib/pangea/activity_generator/activity_generator_view.dart index 3464f1e10..f7840b43d 100644 --- a/lib/pangea/activity_generator/activity_generator_view.dart +++ b/lib/pangea/activity_generator/activity_generator_view.dart @@ -59,7 +59,6 @@ class ActivityGeneratorView extends StatelessWidget { onEdit: (updatedActivity) => controller.onEdit(index, updatedActivity), onChange: controller.update, - avatarURL: controller.avatarURL, initialFilename: controller.filename, ); }, diff --git a/lib/pangea/activity_planner/activity_plan_card.dart b/lib/pangea/activity_planner/activity_plan_card.dart index 3152a8b8f..b5ed335d4 100644 --- a/lib/pangea/activity_planner/activity_plan_card.dart +++ b/lib/pangea/activity_planner/activity_plan_card.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:go_router/go_router.dart'; +import 'package:http/http.dart' as http; import 'package:material_symbols_icons/symbols.dart'; import 'package:matrix/matrix.dart' as sdk; import 'package:matrix/matrix.dart'; @@ -26,7 +27,6 @@ class ActivityPlanCard extends StatefulWidget { final VoidCallback onChange; final ValueChanged onEdit; final double maxWidth; - final String? avatarURL; final String? initialFilename; const ActivityPlanCard({ @@ -36,7 +36,6 @@ class ActivityPlanCard extends StatefulWidget { required this.onChange, required this.onEdit, this.maxWidth = 400, - this.avatarURL, this.initialFilename, }); @@ -53,7 +52,6 @@ class ActivityPlanCardState extends State { final TextEditingController _newVocabController = TextEditingController(); final FocusNode _vocabFocusNode = FocusNode(); - String? _avatarURL; Uint8List? _avatar; String? _filename; @@ -67,7 +65,6 @@ class ActivityPlanCardState extends State { _instructionsController = TextEditingController(text: _tempActivity.instructions); _filename = widget.initialFilename; - _avatarURL = widget.avatarURL ?? widget.activity.imageURL; } static const double itemPadding = 12; @@ -89,7 +86,7 @@ class ActivityPlanCardState extends State { learningObjective: _learningObjectiveController.text, instructions: _instructionsController.text, vocab: _tempActivity.vocab, - imageURL: _avatarURL, + imageURL: widget.activity.imageURL, ); widget.onEdit(updatedActivity); @@ -162,11 +159,27 @@ class ActivityPlanCardState extends State { await showFutureLoadingDialog( context: context, future: () async { + if (_avatar == null && widget.activity.imageURL != null) { + final resp = await http + .get(Uri.parse(widget.activity.imageURL!)) + .timeout(const Duration(seconds: 5)); + _avatar = resp.bodyBytes; + } + + String? avatarUrl; + if (_avatar != null) { + final client = Matrix.of(context).client; + final url = await client.uploadContent( + _avatar!, + filename: _filename, + ); + avatarUrl = url.toString(); + } + if (widget.room != null) { await widget.room?.sendActivityPlan( widget.activity, avatar: _avatar, - avatarURL: _avatarURL, filename: _filename, ); @@ -181,6 +194,15 @@ class ActivityPlanCardState extends State { groupName: widget.activity.title.isNotEmpty ? widget.activity.title : null, initialState: [ + if (_avatar != null) ...[ + StateEvent( + type: EventTypes.RoomAvatar, + stateKey: '', + content: { + "url": avatarUrl, + }, + ), + ], StateEvent( type: EventTypes.RoomPowerLevels, stateKey: '', @@ -200,7 +222,6 @@ class ActivityPlanCardState extends State { await room.sendActivityPlan( widget.activity, avatar: _avatar, - avatarURL: _avatarURL, filename: _filename, ); @@ -232,12 +253,12 @@ class ActivityPlanCardState extends State { ), clipBehavior: Clip.hardEdge, alignment: Alignment.center, - child: _avatarURL != null || _avatar != null + child: widget.activity.imageURL != null || _avatar != null ? ClipRRect( child: _avatar == null ? CachedNetworkImage( fit: BoxFit.cover, - imageUrl: _avatarURL!, + imageUrl: widget.activity.imageURL!, placeholder: (context, url) { return const Center( child: CircularProgressIndicator(), diff --git a/lib/pangea/activity_suggestions/activity_suggestion_dialog.dart b/lib/pangea/activity_suggestions/activity_suggestion_dialog.dart index 455df5c34..1a904956b 100644 --- a/lib/pangea/activity_suggestions/activity_suggestion_dialog.dart +++ b/lib/pangea/activity_suggestions/activity_suggestion_dialog.dart @@ -19,6 +19,7 @@ import 'package:fluffychat/pangea/activity_suggestions/activity_suggestion_card_ import 'package:fluffychat/pangea/chat/constants/default_power_level.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension.dart'; +import 'package:fluffychat/utils/client_download_content_extension.dart'; import 'package:fluffychat/utils/file_selector.dart'; import 'package:fluffychat/widgets/future_loading_dialog.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -119,12 +120,22 @@ class ActivitySuggestionDialogState extends State { if (widget.activity.imageURL == null) return; try { if (_avatar == null) { - final Response response = - await http.get(Uri.parse(widget.activity.imageURL!)); - _avatar = response.bodyBytes; - _filename = Uri.encodeComponent( - Uri.parse(widget.activity.imageURL!).pathSegments.last, - ); + if (widget.activity.imageURL!.startsWith("mxc")) { + final client = Matrix.of(context).client; + final mxcUri = Uri.parse(widget.activity.imageURL!); + final data = await client.downloadMxcCached(mxcUri); + _avatar = data; + _filename = Uri.encodeComponent( + mxcUri.pathSegments.last, + ); + } else { + final Response response = + await http.get(Uri.parse(widget.activity.imageURL!)); + _avatar = response.bodyBytes; + _filename = Uri.encodeComponent( + Uri.parse(widget.activity.imageURL!).pathSegments.last, + ); + } } } catch (err, s) { ErrorHandler.logError( @@ -183,12 +194,29 @@ class ActivitySuggestionDialogState extends State { return; } + String? avatarUrl; + if (_avatar != null) { + final url = await Matrix.of(context).client.uploadContent( + _avatar!, + filename: _filename, + ); + avatarUrl = url.toString(); + } + final client = Matrix.of(context).client; final roomId = await client.createGroupChat( preset: CreateRoomPreset.publicChat, visibility: sdk.Visibility.private, groupName: widget.activity.title, initialState: [ + if (avatarUrl != null) + StateEvent( + type: EventTypes.RoomAvatar, + stateKey: '', + content: { + "url": avatarUrl, + }, + ), StateEvent( type: EventTypes.RoomPowerLevels, stateKey: '', @@ -246,34 +274,55 @@ class ActivitySuggestionDialogState extends State { mainAxisSize: MainAxisSize.min, mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - DecoratedBox( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(24.0), - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(24.0), - child: _avatar != null - ? Image.memory(_avatar!, fit: BoxFit.cover) - : widget.activity.imageURL != null - ? widget.activity.imageURL!.startsWith("mxc") - ? MxcImage( - uri: Uri.parse(widget.activity.imageURL!), - width: width, - height: 200, - cacheKey: widget.activity.bookmarkId, - fit: BoxFit.cover, - ) - : CachedNetworkImage( - imageUrl: widget.activity.imageURL!, - fit: BoxFit.cover, - placeholder: (context, url) => const Center( - child: CircularProgressIndicator(), - ), - errorWidget: (context, url, error) => - const SizedBox(), - ) - : null, - ), + Stack( + alignment: Alignment.center, + children: [ + DecoratedBox( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(24.0), + ), + child: ClipRRect( + borderRadius: BorderRadius.circular(24.0), + child: _avatar != null + ? Image.memory(_avatar!, fit: BoxFit.cover) + : widget.activity.imageURL != null + ? widget.activity.imageURL!.startsWith("mxc") + ? MxcImage( + uri: Uri.parse(widget.activity.imageURL!), + width: width, + height: 200, + cacheKey: widget.activity.bookmarkId, + fit: BoxFit.cover, + ) + : CachedNetworkImage( + imageUrl: widget.activity.imageURL!, + fit: BoxFit.cover, + placeholder: (context, url) => + const Center( + child: CircularProgressIndicator(), + ), + errorWidget: (context, url, error) => + const SizedBox(), + ) + : null, + ), + ), + if (_isEditing) + Positioned( + bottom: 8.0, + child: InkWell( + borderRadius: BorderRadius.circular(90), + onTap: _setAvatar, + child: const CircleAvatar( + radius: 24.0, + child: Icon( + Icons.add_a_photo_outlined, + size: 24.0, + ), + ), + ), + ), + ], ), Flexible( child: SingleChildScrollView( @@ -601,21 +650,6 @@ class ActivitySuggestionDialogState extends State { tooltip: L10n.of(context).close, ), ), - if (_isEditing) - Positioned( - top: 160.0, - child: InkWell( - borderRadius: BorderRadius.circular(90), - onTap: _setAvatar, - child: const CircleAvatar( - radius: 24.0, - child: Icon( - Icons.add_a_photo_outlined, - size: 24.0, - ), - ), - ), - ), ], ); diff --git a/lib/pangea/practice_activities/practice_match.dart b/lib/pangea/practice_activities/practice_match.dart index 58fa684a1..d614dabf9 100644 --- a/lib/pangea/practice_activities/practice_match.dart +++ b/lib/pangea/practice_activities/practice_match.dart @@ -1,8 +1,10 @@ +import 'package:flutter/material.dart'; + import 'package:collection/collection.dart'; + import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/constructs/construct_form.dart'; import 'package:fluffychat/pangea/practice_activities/practice_choice.dart'; -import 'package:flutter/material.dart'; class PracticeMatchActivity { /// The constructIdenfifiers involved in the activity diff --git a/lib/pangea/practice_activities/practice_selection.dart b/lib/pangea/practice_activities/practice_selection.dart index e3521ff43..03117a0d0 100644 --- a/lib/pangea/practice_activities/practice_selection.dart +++ b/lib/pangea/practice_activities/practice_selection.dart @@ -1,6 +1,9 @@ import 'dart:developer'; +import 'package:flutter/foundation.dart'; + import 'package:collection/collection.dart'; + import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/learning_settings/constants/language_constants.dart'; import 'package:fluffychat/pangea/lemmas/lemma_info_response.dart'; @@ -9,7 +12,6 @@ import 'package:fluffychat/pangea/practice_activities/activity_type_enum.dart'; import 'package:fluffychat/pangea/practice_activities/practice_selection_repo.dart'; import 'package:fluffychat/pangea/practice_activities/practice_target.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import 'package:flutter/foundation.dart'; class PracticeSelection { late String _userL2; @@ -166,11 +168,15 @@ class PracticeSelection { ).sorted( (a, b) { final bScore = b.tokens.first.activityPriorityScore( - ActivityTypeEnum.morphId, b.morphFeature!) * + ActivityTypeEnum.morphId, + b.morphFeature!, + ) * (tokenIsIncludedInActivityOfAnyType(b.tokens.first) ? 1.1 : 1); final aScore = a.tokens.first.activityPriorityScore( - ActivityTypeEnum.morphId, a.morphFeature!) * + ActivityTypeEnum.morphId, + a.morphFeature!, + ) * (tokenIsIncludedInActivityOfAnyType(a.tokens.first) ? 1.1 : 1); return bScore.compareTo(aScore); diff --git a/lib/pangea/practice_activities/practice_selection_repo.dart b/lib/pangea/practice_activities/practice_selection_repo.dart index 5198b0c4e..1cd80c891 100644 --- a/lib/pangea/practice_activities/practice_selection_repo.dart +++ b/lib/pangea/practice_activities/practice_selection_repo.dart @@ -1,8 +1,10 @@ +import 'package:flutter/material.dart'; + +import 'package:get_storage/get_storage.dart'; + import 'package:fluffychat/pangea/common/utils/error_handler.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; import 'package:fluffychat/pangea/practice_activities/practice_selection.dart'; -import 'package:flutter/material.dart'; -import 'package:get_storage/get_storage.dart'; class PracticeSelectionRepo { static final GetStorage _storage = GetStorage('practice_selection_cache'); @@ -21,7 +23,8 @@ class PracticeSelectionRepo { } static MapEntry? _parsePracticeSelection( - String key) { + String key, + ) { if (!_storage.hasData(key)) { return null; } diff --git a/lib/pangea/toolbar/reading_assistance_input_row/reading_assistance_input_bar.dart b/lib/pangea/toolbar/reading_assistance_input_row/reading_assistance_input_bar.dart index a5e26831f..e008f66cd 100644 --- a/lib/pangea/toolbar/reading_assistance_input_row/reading_assistance_input_bar.dart +++ b/lib/pangea/toolbar/reading_assistance_input_row/reading_assistance_input_bar.dart @@ -1,3 +1,7 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_gen/gen_l10n/l10n.dart'; + import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart'; @@ -7,8 +11,6 @@ import 'package:fluffychat/pangea/toolbar/widgets/message_speech_to_text_card.da import 'package:fluffychat/pangea/toolbar/widgets/message_translation_card.dart'; import 'package:fluffychat/pangea/toolbar/widgets/practice_activity/practice_activity_card.dart'; import 'package:fluffychat/pangea/toolbar/widgets/word_zoom/morph_focus_widget.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/l10n.dart'; const double minContentHeight = 120; diff --git a/lib/pangea/toolbar/widgets/message_selection_overlay.dart b/lib/pangea/toolbar/widgets/message_selection_overlay.dart index a9989ed55..216330e11 100644 --- a/lib/pangea/toolbar/widgets/message_selection_overlay.dart +++ b/lib/pangea/toolbar/widgets/message_selection_overlay.dart @@ -1,7 +1,13 @@ import 'dart:async'; import 'dart:developer'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + import 'package:collection/collection.dart'; +import 'package:matrix/matrix.dart'; + import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pangea/common/utils/error_handler.dart'; @@ -25,10 +31,6 @@ import 'package:fluffychat/pangea/toolbar/reading_assistance_input_row/morph_sel import 'package:fluffychat/pangea/toolbar/widgets/message_selection_positioner.dart'; import 'package:fluffychat/pangea/toolbar/widgets/reading_assistance_content.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; -import 'package:matrix/matrix.dart'; /// Controls data at the top level of the toolbar (mainly token / toolbar mode selection) class MessageSelectionOverlay extends StatefulWidget { diff --git a/lib/pangea/toolbar/widgets/word_zoom/lemma_meaning_widget.dart b/lib/pangea/toolbar/widgets/word_zoom/lemma_meaning_widget.dart index bddbebaa7..ccc9d776b 100644 --- a/lib/pangea/toolbar/widgets/word_zoom/lemma_meaning_widget.dart +++ b/lib/pangea/toolbar/widgets/word_zoom/lemma_meaning_widget.dart @@ -1,5 +1,11 @@ import 'dart:developer'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:material_symbols_icons/symbols.dart'; + import 'package:fluffychat/pangea/analytics_misc/construct_use_model.dart'; import 'package:fluffychat/pangea/analytics_misc/text_loading_shimmer.dart'; import 'package:fluffychat/pangea/events/models/pangea_token_model.dart'; @@ -12,10 +18,6 @@ import 'package:fluffychat/pangea/toolbar/enums/message_mode_enum.dart'; import 'package:fluffychat/pangea/toolbar/widgets/message_selection_overlay.dart'; import 'package:fluffychat/pangea/toolbar/widgets/practice_activity/word_zoom_activity_button.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:material_symbols_icons/symbols.dart'; class LemmaMeaningWidget extends StatefulWidget { final ConstructUses constructUse;