From 425779e8687e422220ced3efe97403af9524af4b Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 5 Nov 2024 09:43:17 -0500 Subject: [PATCH 01/11] redact the current activity event when submitting feedback --- ...actice_activity_generation_controller.dart | 47 +++++++++++++++---- .../practice_activity_card.dart | 31 +++++++++--- pubspec.yaml | 2 +- 3 files changed, 64 insertions(+), 16 deletions(-) diff --git a/lib/pangea/controllers/practice_activity_generation_controller.dart b/lib/pangea/controllers/practice_activity_generation_controller.dart index 410f8eeaa..c7238c9cc 100644 --- a/lib/pangea/controllers/practice_activity_generation_controller.dart +++ b/lib/pangea/controllers/practice_activity_generation_controller.dart @@ -19,7 +19,7 @@ import 'package:matrix/matrix.dart'; /// Represents an item in the completion cache. class _RequestCacheItem { MessageActivityRequest req; - PracticeActivityModel? practiceActivity; + PracticeActivityModelResponse? practiceActivity; _RequestCacheItem({ required this.req, @@ -99,7 +99,7 @@ class PracticeGenerationController { //TODO - allow return of activity content before sending the event // this requires some downstream changes to the way the event is handled - Future getPracticeActivity( + Future getPracticeActivity( MessageActivityRequest req, PangeaMessageEvent event, ) async { @@ -119,6 +119,8 @@ class PracticeGenerationController { return null; } + final eventCompleter = Completer(); + // if the server points to an existing event, return that event if (res.existingActivityEventId != null) { final Event? existingEvent = @@ -127,11 +129,19 @@ class PracticeGenerationController { debugPrint( 'Existing activity event found: ${existingEvent?.content}', ); - if (existingEvent != null) { - return PracticeActivityEvent( + debugPrint( + "eventID: ${existingEvent?.eventId}, event is redacted: ${existingEvent?.redacted}", + ); + if (existingEvent != null && !existingEvent.redacted) { + final activityEvent = PracticeActivityEvent( event: existingEvent, timeline: event.timeline, - ).practiceActivity; + ); + eventCompleter.complete(activityEvent); + return PracticeActivityModelResponse( + activity: activityEvent.practiceActivity, + eventCompleter: eventCompleter, + ); } } @@ -141,11 +151,30 @@ class PracticeGenerationController { } debugPrint('Activity generated: ${res.activity!.toJson()}'); + _sendAndPackageEvent(res.activity!, event).then((event) { + eventCompleter.complete(event); + }); - _sendAndPackageEvent(res.activity!, event); - _cache[cacheKey] = - _RequestCacheItem(req: req, practiceActivity: res.activity!); + final responseModel = PracticeActivityModelResponse( + activity: res.activity!, + eventCompleter: eventCompleter, + ); - return _cache[cacheKey]!.practiceActivity; + _cache[cacheKey] = _RequestCacheItem( + req: req, + practiceActivity: responseModel, + ); + + return responseModel; } } + +class PracticeActivityModelResponse { + final PracticeActivityModel? activity; + final Completer eventCompleter; + + PracticeActivityModelResponse({ + required this.activity, + required this.eventCompleter, + }); +} diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index 1e97f2fe2..00b11f658 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -3,6 +3,7 @@ import 'dart:developer'; import 'package:fluffychat/pangea/controllers/my_analytics_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; +import 'package:fluffychat/pangea/controllers/practice_activity_generation_controller.dart'; import 'package:fluffychat/pangea/enum/activity_type_enum.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_message_event.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/practice_activity_event.dart'; @@ -45,6 +46,8 @@ class PracticeActivityCard extends StatefulWidget { class PracticeActivityCardState extends State { PracticeActivityModel? currentActivity; + Completer? currentActivityCompleter; + PracticeActivityRecordModel? currentCompletionRecord; bool fetchingActivity = false; @@ -133,9 +136,9 @@ class PracticeActivityCardState extends State { return null; } - final PracticeActivityModel? ourNewActivity = await pangeaController - .practiceGenerationController - .getPracticeActivity( + final PracticeActivityModelResponse? activityResponse = + await pangeaController.practiceGenerationController + .getPracticeActivity( MessageActivityRequest( userL1: pangeaController.languageController.userL1!.langCode, userL2: pangeaController.languageController.userL2!.langCode, @@ -157,9 +160,10 @@ class PracticeActivityCardState extends State { widget.pangeaMessageEvent, ); + currentActivityCompleter = activityResponse?.eventCompleter; _updateFetchingActivity(false); - return ourNewActivity; + return activityResponse?.activity; } catch (e, s) { debugger(when: kDebugMode); ErrorHandler.logError( @@ -255,12 +259,27 @@ class PracticeActivityCardState extends State { /// clear the current activity, record, and selection /// fetch a new activity, including the offending activity in the request - void submitFeedback(String feedback) { - if (currentActivity == null) { + Future submitFeedback(String feedback) async { + if (currentActivity == null || currentCompletionRecord == null) { debugger(when: kDebugMode); return; } + if (currentActivityCompleter != null) { + final activityEvent = await currentActivityCompleter!.future; + await activityEvent?.event.redactEvent(reason: feedback); + } else { + debugger(when: kDebugMode); + ErrorHandler.logError( + e: Exception('No completer found for current activity'), + data: { + 'activity': currentActivity, + 'record': currentCompletionRecord, + 'feedback': feedback, + }, + ); + } + _fetchNewActivity( ActivityQualityFeedback( feedbackText: feedback, diff --git a/pubspec.yaml b/pubspec.yaml index 25c564cef..ca105194e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -6,7 +6,7 @@ description: Learn a language while texting your friends. # Pangea# publish_to: none # On version bump also increase the build number for F-Droid -version: 1.23.3+3562 +version: 1.23.4+3563 environment: sdk: ">=3.0.0 <4.0.0" From c315d5b97078adf4c21e23dc94fcb79d58bfde77 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 5 Nov 2024 11:31:44 -0500 Subject: [PATCH 02/11] added press animations to toolbar buttons --- lib/config/app_config.dart | 1 + lib/pangea/enum/message_mode_enum.dart | 6 +- .../chat/message_selection_overlay.dart | 4 +- .../widgets/chat/message_toolbar_buttons.dart | 75 ++++++++----- lib/pangea/widgets/pressable_button.dart | 106 ++++++++++++++++++ 5 files changed, 155 insertions(+), 37 deletions(-) create mode 100644 lib/pangea/widgets/pressable_button.dart diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index ab05ffd99..bec1df519 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -25,6 +25,7 @@ abstract class AppConfig { static const double toolbarMaxHeight = 300.0; static const double toolbarMinHeight = 70.0; static const double toolbarMinWidth = 270.0; + static const double toolbarButtonsHeight = 50.0; // #Pangea // static const Color primaryColor = Color(0xFF5625BA); // static const Color primaryColorLight = Color(0xFFCCBDEA); diff --git a/lib/pangea/enum/message_mode_enum.dart b/lib/pangea/enum/message_mode_enum.dart index cfc42f63b..f8ad41a5b 100644 --- a/lib/pangea/enum/message_mode_enum.dart +++ b/lib/pangea/enum/message_mode_enum.dart @@ -101,11 +101,7 @@ extension MessageModeExtension on MessageMode { } //unlocked and active - if (this == currentMode) { - return Theme.of(context).brightness == Brightness.dark - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.primary; - } + if (this == currentMode) return Theme.of(context).colorScheme.primary; //unlocked and inactive return Theme.of(context).colorScheme.primaryContainer; diff --git a/lib/pangea/widgets/chat/message_selection_overlay.dart b/lib/pangea/widgets/chat/message_selection_overlay.dart index 4c5cf86da..ea42c9854 100644 --- a/lib/pangea/widgets/chat/message_selection_overlay.dart +++ b/lib/pangea/widgets/chat/message_selection_overlay.dart @@ -281,9 +281,9 @@ class MessageOverlayController extends State return reactionsEvents.where((e) => !e.redacted).isNotEmpty; } - final double toolbarButtonsHeight = 50; double get reactionsHeight => hasReactions ? 28 : 0; - double get belowMessageHeight => toolbarButtonsHeight + reactionsHeight; + double get belowMessageHeight => + AppConfig.toolbarButtonsHeight + reactionsHeight; void setIsPlayingAudio(bool isPlaying) { if (mounted) { diff --git a/lib/pangea/widgets/chat/message_toolbar_buttons.dart b/lib/pangea/widgets/chat/message_toolbar_buttons.dart index 7cead7b5a..75cd4c5c3 100644 --- a/lib/pangea/widgets/chat/message_toolbar_buttons.dart +++ b/lib/pangea/widgets/chat/message_toolbar_buttons.dart @@ -6,6 +6,7 @@ import 'package:fluffychat/config/themes.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/widgets/chat/message_selection_overlay.dart'; +import 'package:fluffychat/pangea/widgets/pressable_button.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; @@ -31,6 +32,7 @@ class ToolbarButtons extends StatelessWidget { MatrixState.pangeaController.languageController.userL2?.langCode; static const double iconWidth = 36.0; + static const buttonSize = 40.0; @override Widget build(BuildContext context) { @@ -44,7 +46,7 @@ class ToolbarButtons extends StatelessWidget { return SizedBox( width: width, - height: 50, + height: AppConfig.toolbarButtonsHeight, child: Stack( alignment: Alignment.center, children: [ @@ -75,37 +77,50 @@ class ToolbarButtons extends StatelessWidget { ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: modes - .mapIndexed( - (index, mode) => IconButton( - iconSize: 20, - icon: Icon(mode.icon), - tooltip: mode.tooltip(context), - color: mode == overlayController.toolbarMode - ? Colors.white - : null, - isSelected: mode == overlayController.toolbarMode, - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all( - mode.iconButtonColor( - context, - index, - overlayController.toolbarMode, - pangeaMessageEvent.numberOfActivitiesCompleted, - totallyDone, - ), - ), + children: modes.mapIndexed((index, mode) { + final enabled = mode.isUnlocked( + index, + pangeaMessageEvent.numberOfActivitiesCompleted, + totallyDone, + ); + final color = mode.iconButtonColor( + context, + index, + overlayController.toolbarMode, + pangeaMessageEvent.numberOfActivitiesCompleted, + totallyDone, + ); + return Tooltip( + message: mode.tooltip(context), + child: PressableButton( + width: buttonSize, + height: buttonSize, + borderRadius: BorderRadius.circular(20), + enabled: enabled, + depressed: !enabled || mode == overlayController.toolbarMode, + color: color, + onPressed: enabled + ? () => overlayController.updateToolbarMode(mode) + : null, + child: AnimatedContainer( + duration: FluffyThemes.animationDuration, + height: buttonSize, + width: buttonSize, + decoration: BoxDecoration( + color: color, + shape: BoxShape.circle, + ), + child: Icon( + mode.icon, + size: 20, + color: mode == overlayController.toolbarMode + ? Colors.white + : null, ), - onPressed: mode.isUnlocked( - index, - pangeaMessageEvent.numberOfActivitiesCompleted, - totallyDone, - ) - ? () => overlayController.updateToolbarMode(mode) - : null, ), - ) - .toList(), + ), + ); + }).toList(), ), ], ), diff --git a/lib/pangea/widgets/pressable_button.dart b/lib/pangea/widgets/pressable_button.dart new file mode 100644 index 000000000..b112dff0d --- /dev/null +++ b/lib/pangea/widgets/pressable_button.dart @@ -0,0 +1,106 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class PressableButton extends StatefulWidget { + final double width; + final double height; + final BorderRadius borderRadius; + + final bool enabled; + final bool depressed; + + final Color color; + final Widget child; + final void Function()? onPressed; + + const PressableButton({ + required this.width, + required this.height, + required this.borderRadius, + required this.child, + required this.onPressed, + required this.color, + this.enabled = true, + this.depressed = false, + super.key, + }); + + @override + PressableButtonState createState() => PressableButtonState(); +} + +class PressableButtonState extends State + with SingleTickerProviderStateMixin { + late AnimationController _controller; + late Animation _tweenAnimation; + + @override + void initState() { + super.initState(); + _controller = AnimationController( + duration: const Duration(milliseconds: 100), + vsync: this, + ); + _tweenAnimation = Tween(begin: 5, end: 0).animate(_controller); + } + + void _onTapDown(TapDownDetails details) { + if (!widget.enabled) return; + _controller.forward(); + } + + void _onTapUp(TapUpDetails details) { + if (!widget.enabled) return; + _controller.reverse(); + widget.onPressed?.call(); + HapticFeedback.mediumImpact(); + } + + void _onTapCancel() { + if (!widget.enabled) return; + _controller.reverse(); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTapDown: _onTapDown, + onTapUp: _onTapUp, + onTapCancel: _onTapCancel, + child: SizedBox( + height: 45, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + Container( + width: widget.width, + height: widget.height, + decoration: BoxDecoration( + color: Color.alphaBlend( + Colors.black.withOpacity(0.25), + widget.color, + ), + borderRadius: widget.borderRadius, + ), + ), + AnimatedBuilder( + animation: _tweenAnimation, + builder: (context, _) { + return Positioned( + bottom: widget.depressed ? 0 : _tweenAnimation.value, + child: widget.child, + ); + }, + ), + ], + ), + ), + ); + } +} From 7beb14dfafe1b65d0178e3669cb30bb48e0b1490 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 5 Nov 2024 11:55:05 -0500 Subject: [PATCH 03/11] reorder options in chat header --- .../chat/message_selection_overlay.dart | 64 +++++++++---------- lib/pangea/widgets/chat/overlay_header.dart | 48 ++++++-------- 2 files changed, 50 insertions(+), 62 deletions(-) diff --git a/lib/pangea/widgets/chat/message_selection_overlay.dart b/lib/pangea/widgets/chat/message_selection_overlay.dart index ea42c9854..f614bb4e2 100644 --- a/lib/pangea/widgets/chat/message_selection_overlay.dart +++ b/lib/pangea/widgets/chat/message_selection_overlay.dart @@ -550,7 +550,13 @@ class MessageOverlayController extends State messageOffset!.dy - messageSize!.height - belowMessageHeight, - child: overlayMessage, + child: Padding( + padding: EdgeInsets.only( + left: horizontalPadding, + right: horizontalPadding, + ), + child: overlayMessage, + ), ) : AnimatedBuilder( animation: _overlayPositionAnimation!, @@ -564,39 +570,33 @@ class MessageOverlayController extends State }, ); - return Padding( - padding: EdgeInsets.only( - left: horizontalPadding, - right: horizontalPadding, - ), - child: Stack( - children: [ - positionedOverlayMessage, - Align( - alignment: Alignment.bottomCenter, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - OverlayFooter(controller: widget.chatController), - ], - ), + return Stack( + children: [ + positionedOverlayMessage, + Align( + alignment: Alignment.bottomCenter, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + OverlayFooter(controller: widget.chatController), + ], ), - if (showDetails) - const SizedBox( - width: FluffyThemes.columnWidth, - ), - ], - ), + ), + if (showDetails) + const SizedBox( + width: FluffyThemes.columnWidth, + ), + ], ), - Material( - child: OverlayHeader(controller: widget.chatController), - ), - ], - ), + ), + Material( + child: OverlayHeader(controller: widget.chatController), + ), + ], ); } } diff --git a/lib/pangea/widgets/chat/overlay_header.dart b/lib/pangea/widgets/chat/overlay_header.dart index cce47adcc..7588cea8a 100644 --- a/lib/pangea/widgets/chat/overlay_header.dart +++ b/lib/pangea/widgets/chat/overlay_header.dart @@ -1,7 +1,7 @@ import 'package:fluffychat/pages/chat/chat.dart'; -import 'package:fluffychat/pages/chat/chat_app_bar_title.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:material_symbols_icons/symbols.dart'; import 'package:matrix/matrix.dart'; class OverlayHeader extends StatelessWidget { @@ -21,21 +21,12 @@ class OverlayHeader extends StatelessWidget { actionsIconTheme: IconThemeData( color: Theme.of(context).colorScheme.primary, ), - leading: IconButton( - icon: const Icon(Icons.close), - onPressed: controller.clearSelectedEvents, - tooltip: L10n.of(context)!.close, - color: Theme.of(context).colorScheme.primary, - ), - titleSpacing: 0, - title: ChatAppBarTitle(controller), actions: [ - if (controller.canEditSelectedEvents) - IconButton( - icon: const Icon(Icons.edit_outlined), - tooltip: L10n.of(context)!.edit, - onPressed: controller.editSelectedEventAction, - ), + IconButton( + icon: const Icon(Symbols.forward), + tooltip: L10n.of(context)!.forward, + onPressed: controller.forwardEventsAction, + ), if (controller.selectedEvents.length == 1 && controller.selectedEvents.single.messageType == MessageTypes.Text) @@ -44,27 +35,30 @@ class OverlayHeader extends StatelessWidget { tooltip: L10n.of(context)!.copy, onPressed: controller.copyEventsAction, ), - if (controller.canSaveSelectedEvent) - // Use builder context to correctly position the share dialog on iPad - Builder( - builder: (context) => IconButton( - icon: Icon(Icons.adaptive.share), - tooltip: L10n.of(context)!.share, - onPressed: () => controller.saveSelectedEvent(context), - ), - ), if (controller.canPinSelectedEvents) IconButton( icon: const Icon(Icons.push_pin_outlined), onPressed: controller.pinEvent, tooltip: L10n.of(context)!.pinMessage, ), + if (controller.canEditSelectedEvents) + IconButton( + icon: const Icon(Icons.edit_outlined), + tooltip: L10n.of(context)!.edit, + onPressed: controller.editSelectedEventAction, + ), if (controller.canRedactSelectedEvents) IconButton( icon: const Icon(Icons.delete_outlined), tooltip: L10n.of(context)!.redactMessage, onPressed: controller.redactEventsAction, ), + if (controller.selectedEvents.length == 1) + IconButton( + icon: const Icon(Icons.shield_outlined), + tooltip: L10n.of(context)!.reportMessage, + onPressed: controller.reportEventAction, + ), if (controller.selectedEvents.length == 1) IconButton( icon: const Icon(Icons.info_outlined), @@ -74,12 +68,6 @@ class OverlayHeader extends StatelessWidget { controller.clearSelectedEvents(); }, ), - if (controller.selectedEvents.length == 1) - IconButton( - icon: const Icon(Icons.shield_outlined), - tooltip: L10n.of(context)!.reportMessage, - onPressed: controller.reportEventAction, - ), ], ), ], From 8330f2c505f30e931a13b7b6378e27d0cf522d24 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 5 Nov 2024 12:58:29 -0500 Subject: [PATCH 04/11] mmove reaction picker down into row with reply button --- lib/pages/chat/chat_input_row.dart | 97 +++++++++++-------- lib/pangea/widgets/chat/overlay_footer.dart | 10 +- .../widgets/chat/pangea_reaction_picker.dart | 86 ++++++++++++++++ 3 files changed, 143 insertions(+), 50 deletions(-) create mode 100644 lib/pangea/widgets/chat/pangea_reaction_picker.dart diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index 340784455..90b9c0793 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -1,11 +1,13 @@ import 'package:animations/animations.dart'; import 'package:fluffychat/pangea/choreographer/widgets/send_button.dart'; import 'package:fluffychat/pangea/constants/language_constants.dart'; +import 'package:fluffychat/pangea/widgets/chat/pangea_reaction_picker.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:material_symbols_icons/symbols.dart'; import 'package:matrix/matrix.dart'; import '../../config/themes.dart'; @@ -55,9 +57,9 @@ class ChatInputRow extends StatelessWidget { children: [ Row( // crossAxisAlignment: CrossAxisAlignment.end, + // mainAxisAlignment: MainAxisAlignment.spaceBetween, crossAxisAlignment: CrossAxisAlignment.center, // Pangea# - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: controller.selectMode ? [ if (controller.selectedEvents @@ -77,50 +79,61 @@ class ChatInputRow extends StatelessWidget { ), ), ) + // #Pangea + // else + // SizedBox( + // height: height, + // child: TextButton( + // onPressed: controller.forwardEventsAction, + // child: Row( + // children: [ + // const Icon(Icons.keyboard_arrow_left_outlined), + // Text(L10n.of(context)!.forward), + // ], + // ), + // ), + // ), else - SizedBox( - height: height, - child: TextButton( - onPressed: controller.forwardEventsAction, - child: Row( - children: [ - const Icon(Icons.keyboard_arrow_left_outlined), - Text(L10n.of(context)!.forward), - ], - ), - ), - ), - controller.selectedEvents.length == 1 - ? controller.selectedEvents.first - .getDisplayEvent(controller.timeline!) - .status - .isSent - ? SizedBox( - height: height, - child: TextButton( - onPressed: controller.replyAction, - child: Row( - children: [ - Text(L10n.of(context)!.reply), - const Icon(Icons.keyboard_arrow_right), - ], + // Pangea# + controller.selectedEvents.length == 1 + ? controller.selectedEvents.first + .getDisplayEvent(controller.timeline!) + .status + .isSent + ? SizedBox( + height: height, + child: TextButton( + onPressed: controller.replyAction, + child: Row( + children: [ + // #Pangea + // Text(L10n.of(context)!.reply), + // const Icon(Icons.keyboard_arrow_right), + const Icon(Symbols.reply), + const SizedBox(width: 6), + Text(L10n.of(context)!.reply), + // Pangea# + ], + ), ), - ), - ) - : SizedBox( - height: height, - child: TextButton( - onPressed: controller.sendAgainAction, - child: Row( - children: [ - Text(L10n.of(context)!.tryToSendAgain), - const SizedBox(width: 4), - const Icon(Icons.send_outlined, size: 16), - ], + ) + : SizedBox( + height: height, + child: TextButton( + onPressed: controller.sendAgainAction, + child: Row( + children: [ + Text(L10n.of(context)!.tryToSendAgain), + const SizedBox(width: 4), + const Icon(Icons.send_outlined, size: 16), + ], + ), ), - ), - ) - : const SizedBox.shrink(), + ) + : const SizedBox.shrink(), + // #Pangea + PangeaReactionsPicker(controller), + // Pangea# ] : [ // #Pangea diff --git a/lib/pangea/widgets/chat/overlay_footer.dart b/lib/pangea/widgets/chat/overlay_footer.dart index b4c51d07c..43c6acbb2 100644 --- a/lib/pangea/widgets/chat/overlay_footer.dart +++ b/lib/pangea/widgets/chat/overlay_footer.dart @@ -1,7 +1,6 @@ import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/chat_input_row.dart'; -import 'package:fluffychat/pages/chat/reactions_picker.dart'; import 'package:flutter/material.dart'; class OverlayFooter extends StatelessWidget { @@ -18,7 +17,7 @@ class OverlayFooter extends StatelessWidget { return Container( margin: EdgeInsets.only( - bottom: bottomSheetPadding, + bottom: bottomSheetPadding + 16, left: bottomSheetPadding, right: bottomSheetPadding, ), @@ -34,12 +33,7 @@ class OverlayFooter extends StatelessWidget { borderRadius: const BorderRadius.all( Radius.circular(24), ), - child: Column( - children: [ - ReactionsPicker(controller), - ChatInputRow(controller), - ], - ), + child: ChatInputRow(controller), ), ], ), diff --git a/lib/pangea/widgets/chat/pangea_reaction_picker.dart b/lib/pangea/widgets/chat/pangea_reaction_picker.dart new file mode 100644 index 000000000..33f1a490f --- /dev/null +++ b/lib/pangea/widgets/chat/pangea_reaction_picker.dart @@ -0,0 +1,86 @@ +import 'package:fluffychat/config/app_emojis.dart'; +import 'package:fluffychat/pages/chat/chat.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:matrix/matrix.dart'; + +class PangeaReactionsPicker extends StatelessWidget { + final ChatController controller; + + const PangeaReactionsPicker(this.controller, {super.key}); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + if (controller.showEmojiPicker) return const SizedBox.shrink(); + final display = controller.editEvent == null && + controller.replyEvent == null && + controller.room.canSendDefaultMessages && + controller.selectedEvents.isNotEmpty; + + if (!display) { + return const SizedBox.shrink(); + } + final emojis = List.from(AppEmojis.emojis); + final allReactionEvents = controller.selectedEvents.first + .aggregatedEvents( + controller.timeline!, + RelationshipTypes.reaction, + ) + .where( + (event) => + event.senderId == event.room.client.userID && + event.type == 'm.reaction', + ); + + for (final event in allReactionEvents) { + try { + emojis.remove(event.content.tryGetMap('m.relates_to')!['key']); + } catch (_) {} + } + return Flexible( + child: Row( + children: [ + Flexible( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + children: emojis + .map( + (emoji) => InkWell( + borderRadius: BorderRadius.circular(8), + onTap: () => controller.sendEmojiAction(emoji), + child: Container( + width: kIsWeb ? 56 : 48, + alignment: Alignment.center, + child: Text( + emoji, + style: const TextStyle(fontSize: 24), + ), + ), + ), + ) + .toList(), + ), + ), + ), + InkWell( + borderRadius: BorderRadius.circular(8), + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 8), + width: 36, + height: 56, + decoration: BoxDecoration( + color: theme.colorScheme.onInverseSurface, + shape: BoxShape.circle, + ), + child: const Icon(Icons.add_outlined), + ), + onTap: () => controller.pickEmojiReactionAction(allReactionEvents), + ), + ], + ), + ); + } +} From 7d80d8f6e2bc6e015ad82fc8ad4bf5c589f5de36 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 5 Nov 2024 13:03:25 -0500 Subject: [PATCH 05/11] added back record button --- lib/pages/chat/chat_input_row.dart | 35 ++++++++++++++++-------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index 90b9c0793..0167d5d21 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -349,22 +349,25 @@ class ChatInputRow extends StatelessWidget { height: height, width: height, alignment: Alignment.center, - child: + child: PlatformInfos.platformCanRecord && + controller.sendController.text.isEmpty + // #Pangea + && + !controller.choreographer.itController.willOpen + // Pangea# + ? FloatingActionButton.small( + tooltip: L10n.of(context)!.voiceMessage, + onPressed: controller.voiceMessageAction, + elevation: 0, + heroTag: null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(height), + ), + backgroundColor: theme.colorScheme.primary, + foregroundColor: theme.colorScheme.onPrimary, + child: const Icon(Icons.mic_none_outlined), + ) // #Pangea - // PlatformInfos.platformCanRecord && - // controller.sendController.text.isEmpty - // ? FloatingActionButton.small( - // tooltip: L10n.of(context)!.voiceMessage, - // onPressed: controller.voiceMessageAction, - // elevation: 0, - // heroTag: null, - // shape: RoundedRectangleBorder( - // borderRadius: BorderRadius.circular(height), - // ), - // backgroundColor: theme.colorScheme.primary, - // foregroundColor: theme.colorScheme.onPrimary, - // child: const Icon(Icons.mic_none_outlined), - // ) // : FloatingActionButton.small( // tooltip: L10n.of(context)!.send, // onPressed: controller.send, @@ -378,7 +381,7 @@ class ChatInputRow extends StatelessWidget { // foregroundColor: theme.colorScheme.onPrimary, // child: const Icon(Icons.send_outlined), // ), - ChoreographerSendButton(controller: controller), + : ChoreographerSendButton(controller: controller), // Pangea# ), ], From 43b663d66bf6b753bb4dc8e1422335f5428e5e08 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 5 Nov 2024 13:19:03 -0500 Subject: [PATCH 06/11] revent change to padding in message overlay --- .../chat/message_selection_overlay.dart | 64 +++++++++---------- 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/lib/pangea/widgets/chat/message_selection_overlay.dart b/lib/pangea/widgets/chat/message_selection_overlay.dart index f614bb4e2..ea42c9854 100644 --- a/lib/pangea/widgets/chat/message_selection_overlay.dart +++ b/lib/pangea/widgets/chat/message_selection_overlay.dart @@ -550,13 +550,7 @@ class MessageOverlayController extends State messageOffset!.dy - messageSize!.height - belowMessageHeight, - child: Padding( - padding: EdgeInsets.only( - left: horizontalPadding, - right: horizontalPadding, - ), - child: overlayMessage, - ), + child: overlayMessage, ) : AnimatedBuilder( animation: _overlayPositionAnimation!, @@ -570,33 +564,39 @@ class MessageOverlayController extends State }, ); - return Stack( - children: [ - positionedOverlayMessage, - Align( - alignment: Alignment.bottomCenter, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - OverlayFooter(controller: widget.chatController), - ], + return Padding( + padding: EdgeInsets.only( + left: horizontalPadding, + right: horizontalPadding, + ), + child: Stack( + children: [ + positionedOverlayMessage, + Align( + alignment: Alignment.bottomCenter, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + OverlayFooter(controller: widget.chatController), + ], + ), ), - ), - if (showDetails) - const SizedBox( - width: FluffyThemes.columnWidth, - ), - ], + if (showDetails) + const SizedBox( + width: FluffyThemes.columnWidth, + ), + ], + ), ), - ), - Material( - child: OverlayHeader(controller: widget.chatController), - ), - ], + Material( + child: OverlayHeader(controller: widget.chatController), + ), + ], + ), ); } } From 4d805d82066f55b932c976e5f57b43a4aea9773c Mon Sep 17 00:00:00 2001 From: ggurdin Date: Tue, 5 Nov 2024 14:24:28 -0500 Subject: [PATCH 07/11] fixes audio message overlay warping --- .../chat/message_selection_overlay.dart | 7 +++++-- .../widgets/chat/message_toolbar_buttons.dart | 2 +- lib/pangea/widgets/chat/overlay_message.dart | 21 +++++++++++++++---- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/pangea/widgets/chat/message_selection_overlay.dart b/lib/pangea/widgets/chat/message_selection_overlay.dart index ea42c9854..baa84ccc7 100644 --- a/lib/pangea/widgets/chat/message_selection_overlay.dart +++ b/lib/pangea/widgets/chat/message_selection_overlay.dart @@ -73,6 +73,8 @@ class MessageOverlayController extends State final TtsController tts = TtsController(); bool isPlayingAudio = false; + bool get showToolbarButtons => !widget._pangeaMessageEvent.isAudioMessage; + @override void initState() { super.initState(); @@ -281,9 +283,10 @@ class MessageOverlayController extends State return reactionsEvents.where((e) => !e.redacted).isNotEmpty; } + double get toolbarButtonsHeight => + showToolbarButtons ? AppConfig.toolbarButtonsHeight : 0; double get reactionsHeight => hasReactions ? 28 : 0; - double get belowMessageHeight => - AppConfig.toolbarButtonsHeight + reactionsHeight; + double get belowMessageHeight => toolbarButtonsHeight + reactionsHeight; void setIsPlayingAudio(bool isPlaying) { if (mounted) { diff --git a/lib/pangea/widgets/chat/message_toolbar_buttons.dart b/lib/pangea/widgets/chat/message_toolbar_buttons.dart index 75cd4c5c3..3976c7505 100644 --- a/lib/pangea/widgets/chat/message_toolbar_buttons.dart +++ b/lib/pangea/widgets/chat/message_toolbar_buttons.dart @@ -40,7 +40,7 @@ class ToolbarButtons extends StatelessWidget { overlayController.isPracticeComplete || !messageInUserL2; final double barWidth = width - iconWidth; - if (overlayController.pangeaMessageEvent.isAudioMessage) { + if (!overlayController.showToolbarButtons) { return const SizedBox(); } diff --git a/lib/pangea/widgets/chat/overlay_message.dart b/lib/pangea/widgets/chat/overlay_message.dart index 23c5cf267..c0a362fc3 100644 --- a/lib/pangea/widgets/chat/overlay_message.dart +++ b/lib/pangea/widgets/chat/overlay_message.dart @@ -82,6 +82,17 @@ class OverlayMessage extends StatelessWidget { : theme.colorScheme.primary; } + final noBubble = { + MessageTypes.Video, + MessageTypes.Image, + MessageTypes.Sticker, + }.contains(pangeaMessageEvent.event.messageType) && + !pangeaMessageEvent.event.redacted; + final noPadding = { + MessageTypes.File, + MessageTypes.Audio, + }.contains(pangeaMessageEvent.event.messageType); + return Material( color: color, clipBehavior: Clip.antiAlias, @@ -95,10 +106,12 @@ class OverlayMessage extends StatelessWidget { AppConfig.borderRadius, ), ), - padding: const EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, - ), + padding: noBubble || noPadding + ? EdgeInsets.zero + : const EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, + ), width: messageWidth, height: messageHeight, child: MessageContent( From e48d8d57c9d7c33997dd74a8fa33b5f428790bc0 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 6 Nov 2024 09:30:11 -0500 Subject: [PATCH 08/11] filter redacted events from _practiceActivityEvents to prevent them from being sent to the choreographer --- lib/pangea/matrix_event_wrappers/pangea_message_event.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart index 74d4483dc..a06d4df7e 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_message_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_message_event.dart @@ -596,6 +596,7 @@ class PangeaMessageEvent { timeline, PangeaEventTypes.pangeaActivity, ) + .where((event) => !event.redacted) .toList(); final List practiceEvents = []; From d5f8be57b5d8bee6215adabc7eb7973018807e94 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 6 Nov 2024 11:58:08 -0500 Subject: [PATCH 09/11] added buttonHeight as parameter of pressable button and added completer to prevent up animation from starting before the down animation finishes --- lib/pangea/widgets/pressable_button.dart | 25 ++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/pangea/widgets/pressable_button.dart b/lib/pangea/widgets/pressable_button.dart index b112dff0d..40d9be423 100644 --- a/lib/pangea/widgets/pressable_button.dart +++ b/lib/pangea/widgets/pressable_button.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -5,6 +7,7 @@ class PressableButton extends StatefulWidget { final double width; final double height; final BorderRadius borderRadius; + final double buttonHeight; final bool enabled; final bool depressed; @@ -20,6 +23,7 @@ class PressableButton extends StatefulWidget { required this.child, required this.onPressed, required this.color, + this.buttonHeight = 5, this.enabled = true, this.depressed = false, super.key, @@ -33,6 +37,7 @@ class PressableButtonState extends State with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation _tweenAnimation; + Completer? _animationCompleter; @override void initState() { @@ -41,16 +46,24 @@ class PressableButtonState extends State duration: const Duration(milliseconds: 100), vsync: this, ); - _tweenAnimation = Tween(begin: 5, end: 0).animate(_controller); + _tweenAnimation = + Tween(begin: widget.buttonHeight, end: 0).animate(_controller); } void _onTapDown(TapDownDetails details) { if (!widget.enabled) return; - _controller.forward(); + _animationCompleter = Completer(); + _controller.forward().then((_) { + _animationCompleter?.complete(); + _animationCompleter = null; + }); } - void _onTapUp(TapUpDetails details) { + Future _onTapUp(TapUpDetails details) async { if (!widget.enabled) return; + if (_animationCompleter != null) { + await _animationCompleter!.future; + } _controller.reverse(); widget.onPressed?.call(); HapticFeedback.mediumImpact(); @@ -73,14 +86,14 @@ class PressableButtonState extends State onTapDown: _onTapDown, onTapUp: _onTapUp, onTapCancel: _onTapCancel, - child: SizedBox( - height: 45, + child: Container( + decoration: BoxDecoration(border: Border.all(color: Colors.green)), child: Stack( alignment: Alignment.bottomCenter, children: [ Container( width: widget.width, - height: widget.height, + // height: widget.height, decoration: BoxDecoration( color: Color.alphaBlend( Colors.black.withOpacity(0.25), From a2513c7bd48d04c3db0c78c2c6101718040ef264 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 6 Nov 2024 11:59:19 -0500 Subject: [PATCH 10/11] stretch toolbar button rows to hold buttons --- lib/pangea/widgets/chat/message_toolbar_buttons.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pangea/widgets/chat/message_toolbar_buttons.dart b/lib/pangea/widgets/chat/message_toolbar_buttons.dart index 3976c7505..12c71e736 100644 --- a/lib/pangea/widgets/chat/message_toolbar_buttons.dart +++ b/lib/pangea/widgets/chat/message_toolbar_buttons.dart @@ -77,6 +77,7 @@ class ToolbarButtons extends StatelessWidget { ), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.stretch, children: modes.mapIndexed((index, mode) { final enabled = mode.isUnlocked( index, From 25251ae613fae81cff6eee8fb7ad68354d75cacf Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 6 Nov 2024 12:00:10 -0500 Subject: [PATCH 11/11] uncomment button height --- lib/pangea/widgets/pressable_button.dart | 47 +++++++++++------------- 1 file changed, 22 insertions(+), 25 deletions(-) diff --git a/lib/pangea/widgets/pressable_button.dart b/lib/pangea/widgets/pressable_button.dart index 40d9be423..596511adf 100644 --- a/lib/pangea/widgets/pressable_button.dart +++ b/lib/pangea/widgets/pressable_button.dart @@ -86,33 +86,30 @@ class PressableButtonState extends State onTapDown: _onTapDown, onTapUp: _onTapUp, onTapCancel: _onTapCancel, - child: Container( - decoration: BoxDecoration(border: Border.all(color: Colors.green)), - child: Stack( - alignment: Alignment.bottomCenter, - children: [ - Container( - width: widget.width, - // height: widget.height, - decoration: BoxDecoration( - color: Color.alphaBlend( - Colors.black.withOpacity(0.25), - widget.color, - ), - borderRadius: widget.borderRadius, + child: Stack( + alignment: Alignment.bottomCenter, + children: [ + Container( + width: widget.width, + height: widget.height, + decoration: BoxDecoration( + color: Color.alphaBlend( + Colors.black.withOpacity(0.25), + widget.color, ), + borderRadius: widget.borderRadius, ), - AnimatedBuilder( - animation: _tweenAnimation, - builder: (context, _) { - return Positioned( - bottom: widget.depressed ? 0 : _tweenAnimation.value, - child: widget.child, - ); - }, - ), - ], - ), + ), + AnimatedBuilder( + animation: _tweenAnimation, + builder: (context, _) { + return Positioned( + bottom: widget.depressed ? 0 : _tweenAnimation.value, + child: widget.child, + ); + }, + ), + ], ), ); }