diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 66d77ea12..1f3521273 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -1664,22 +1664,15 @@ class ChatController extends State editEvent = null; }); -// #Pangea + // #Pangea void showToolbar( - PangeaMessageEvent pangeaMessageEvent, { + Event event, { + PangeaMessageEvent? pangeaMessageEvent, PangeaToken? selectedToken, MessageMode? mode, Event? nextEvent, Event? prevEvent, }) { - if (![ - MessageTypes.Text, - MessageTypes.Audio, - ].contains(pangeaMessageEvent.event.messageType) || - pangeaMessageEvent.event.redacted) { - return; - } - // Close keyboard, if open if (inputFocus.hasFocus && PlatformInfos.isMobile) { inputFocus.unfocus(); @@ -1698,7 +1691,7 @@ class ChatController extends State try { overlayEntry = MessageSelectionOverlay( chatController: this, - event: pangeaMessageEvent.event, + event: event, pangeaMessageEvent: pangeaMessageEvent, initialSelectedToken: selectedToken, nextEvent: nextEvent, @@ -1723,7 +1716,7 @@ class ChatController extends State ); // select the message - onSelectMessage(pangeaMessageEvent.event); + onSelectMessage(event); if (!kIsWeb) { HapticFeedback.mediumImpact(); } diff --git a/lib/pages/chat/events/html_message.dart b/lib/pages/chat/events/html_message.dart index d06fa96c0..7f505f1d2 100644 --- a/lib/pages/chat/events/html_message.dart +++ b/lib/pages/chat/events/html_message.dart @@ -24,6 +24,7 @@ class HtmlMessage extends StatelessWidget { final bool isOverlay; final PangeaMessageEvent? pangeaMessageEvent; final ChatController controller; + final Event event; final Event? nextEvent; final Event? prevEvent; // Pangea# @@ -35,6 +36,7 @@ class HtmlMessage extends StatelessWidget { this.textColor = Colors.black, // #Pangea required this.isOverlay, + required this.event, this.pangeaMessageEvent, required this.controller, this.nextEvent, @@ -95,9 +97,10 @@ class HtmlMessage extends StatelessWidget { return SelectionArea( child: GestureDetector( onTap: () { - if (pangeaMessageEvent != null && !isOverlay) { + if (!isOverlay) { controller.showToolbar( - pangeaMessageEvent!, + event, + pangeaMessageEvent: pangeaMessageEvent, nextEvent: nextEvent, prevEvent: prevEvent, ); diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index ba8e35ae2..0839c1b9b 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -74,7 +74,8 @@ class Message extends StatelessWidget { // if overlayController is not null, the message is already in overlay mode if (pangeaMessageEvent != null && overlayController == null) { controller.showToolbar( - pangeaMessageEvent, + event, + pangeaMessageEvent: pangeaMessageEvent, nextEvent: nextEvent, prevEvent: previousEvent, ); @@ -611,6 +612,7 @@ class Message extends StatelessWidget { children: [ if (pangeaMessageEvent?.showMessageButtons ?? false) MessageButtons( + event: event, controller: controller, pangeaMessageEvent: pangeaMessageEvent!, nextEvent: nextEvent, diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index f76a58f78..e1e4718be 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -210,6 +210,7 @@ class MessageContent extends StatelessWidget { textColor: textColor, room: event.room, // #Pangea + event: event, isOverlay: overlayController != null, controller: controller, pangeaMessageEvent: pangeaMessageEvent, @@ -321,7 +322,8 @@ class MessageContent extends StatelessWidget { style: messageTextStyle, onClick: overlayController?.onClickOverlayMessageToken ?? (token) => controller.showToolbar( - pangeaMessageEvent!, + event, + pangeaMessageEvent: pangeaMessageEvent, selectedToken: token, ), isSelected: overlayController?.isTokenSelected, @@ -333,6 +335,7 @@ class MessageContent extends StatelessWidget { return // #Pangea ToolbarSelectionArea( + event: event, controller: controller, pangeaMessageEvent: pangeaMessageEvent, isOverlay: overlayController != null, diff --git a/lib/pangea/widgets/chat/message_buttons.dart b/lib/pangea/widgets/chat/message_buttons.dart index 528b11fb2..1fa8883df 100644 --- a/lib/pangea/widgets/chat/message_buttons.dart +++ b/lib/pangea/widgets/chat/message_buttons.dart @@ -6,6 +6,7 @@ import 'package:matrix/matrix.dart'; class MessageButtons extends StatelessWidget { final ChatController controller; + final Event event; final PangeaMessageEvent pangeaMessageEvent; final Event? nextEvent; final Event? prevEvent; @@ -13,6 +14,7 @@ class MessageButtons extends StatelessWidget { const MessageButtons({ super.key, required this.controller, + required this.event, required this.pangeaMessageEvent, this.nextEvent, this.prevEvent, @@ -20,7 +22,8 @@ class MessageButtons extends StatelessWidget { void showActivity(BuildContext context) { controller.showToolbar( - pangeaMessageEvent, + event, + pangeaMessageEvent: pangeaMessageEvent, mode: MessageMode.practiceActivity, nextEvent: nextEvent, prevEvent: prevEvent, diff --git a/lib/pangea/widgets/chat/message_selection_overlay.dart b/lib/pangea/widgets/chat/message_selection_overlay.dart index 1492682b5..80363e7d0 100644 --- a/lib/pangea/widgets/chat/message_selection_overlay.dart +++ b/lib/pangea/widgets/chat/message_selection_overlay.dart @@ -32,13 +32,13 @@ class MessageSelectionOverlay extends StatefulWidget { final Event _event; final Event? _nextEvent; final Event? _prevEvent; - final PangeaMessageEvent _pangeaMessageEvent; + final PangeaMessageEvent? _pangeaMessageEvent; final PangeaToken? _initialSelectedToken; const MessageSelectionOverlay({ required this.chatController, required Event event, - required PangeaMessageEvent pangeaMessageEvent, + required PangeaMessageEvent? pangeaMessageEvent, required PangeaToken? initialSelectedToken, required Event? nextEvent, required Event? prevEvent, @@ -65,12 +65,14 @@ class MessageOverlayController extends State List? tokens; bool initialized = false; - PangeaMessageEvent get pangeaMessageEvent => widget._pangeaMessageEvent; + PangeaMessageEvent? get pangeaMessageEvent => widget._pangeaMessageEvent; final TtsController tts = TtsController(); bool _isPlayingAudio = false; - bool get showToolbarButtons => !widget._pangeaMessageEvent.isAudioMessage; + bool get showToolbarButtons => + pangeaMessageEvent != null && + pangeaMessageEvent!.event.messageType == MessageTypes.Text; int get activitiesLeftToComplete => messageAnalyticsEntry?.numActivities ?? 0; @@ -127,7 +129,7 @@ class MessageOverlayController extends State final timelineEvents = update.rooms?.join?[room.id]?.timeline?.events; if (timelineEvents == null) return false; - final eventID = widget._pangeaMessageEvent.event.eventId; + final eventID = widget._event.eventId; return timelineEvents.any( (e) => e.type == EventTypes.Redaction || @@ -141,20 +143,21 @@ class MessageOverlayController extends State tts.setupTTS(); } - MessageAnalyticsEntry? get messageAnalyticsEntry => tokens != null - ? MatrixState.pangeaController.getAnalytics.perMessage.get( - tokens!, - pangeaMessageEvent, - ) - : null; + MessageAnalyticsEntry? get messageAnalyticsEntry => + pangeaMessageEvent != null && tokens != null + ? MatrixState.pangeaController.getAnalytics.perMessage.get( + tokens!, + pangeaMessageEvent!, + ) + : null; Future _initializeTokensAndMode() async { try { - final repEvent = pangeaMessageEvent.messageDisplayRepresentation; + final repEvent = pangeaMessageEvent?.messageDisplayRepresentation; if (repEvent != null) { tokens = await repEvent.tokensGlobal( - pangeaMessageEvent.senderId, - pangeaMessageEvent.originServerTs, + pangeaMessageEvent!.senderId, + pangeaMessageEvent!.originServerTs, ); } } catch (e, s) { @@ -172,7 +175,7 @@ class MessageOverlayController extends State } Future _setInitialToolbarMode() async { - if (widget._pangeaMessageEvent.isAudioMessage) { + if (widget._pangeaMessageEvent?.isAudioMessage ?? false) { toolbarMode = MessageMode.speechToText; return setState(() {}); } @@ -259,11 +262,12 @@ class MessageOverlayController extends State /// If there is no selectedSpan, then the whole message is the target /// If there is a selectedSpan, then the target is the selected text String get targetText { - if (_selectedSpan == null) { - return widget._pangeaMessageEvent.messageDisplayText; + if (_selectedSpan == null || pangeaMessageEvent == null) { + return widget._pangeaMessageEvent?.messageDisplayText ?? + widget._event.body; } - return widget._pangeaMessageEvent.messageDisplayText.substring( + return widget._pangeaMessageEvent!.messageDisplayText.substring( _selectedSpan!.offset, _selectedSpan!.offset + _selectedSpan!.length, ); @@ -297,6 +301,8 @@ class MessageOverlayController extends State } void setSelectedSpan(PracticeActivityModel activity) { + if (pangeaMessageEvent == null) return; + final RelevantSpanDisplayDetails? span = activity.content.spanDisplayDetails; @@ -309,7 +315,7 @@ class MessageOverlayController extends State _selectedSpan = PangeaTokenText( offset: span.offset, length: span.length, - content: widget._pangeaMessageEvent.messageDisplayText + content: widget._pangeaMessageEvent!.messageDisplayText .substring(span.offset, span.offset + span.length), ); } else { @@ -331,7 +337,7 @@ class MessageOverlayController extends State PangeaTokenText? get selectedSpan => _selectedSpan; bool get _hasReactions { - final reactionsEvents = widget._pangeaMessageEvent.event.aggregatedEvents( + final reactionsEvents = widget._event.aggregatedEvents( widget.chatController.timeline!, RelationshipTypes.reaction, ); @@ -547,20 +553,23 @@ class MessageOverlayController extends State type: MaterialType.transparency, child: Column( mainAxisSize: MainAxisSize.min, - crossAxisAlignment: widget._pangeaMessageEvent.ownMessage - ? CrossAxisAlignment.end - : CrossAxisAlignment.start, + crossAxisAlignment: + widget._event.senderId == widget._event.room.client.userID + ? CrossAxisAlignment.end + : CrossAxisAlignment.start, children: [ - MessageToolbar( - pangeaMessageEvent: widget._pangeaMessageEvent, - overLayController: this, - ttsController: tts, - ), + if (pangeaMessageEvent != null) + MessageToolbar( + pangeaMessageEvent: pangeaMessageEvent!, + overLayController: this, + ttsController: tts, + ), const SizedBox(height: 8), SizedBox( height: _adjustedMessageHeight, child: OverlayMessage( - pangeaMessageEvent, + widget._event, + pangeaMessageEvent: pangeaMessageEvent, immersionMode: widget.chatController.choreographer.immersionMode, controller: widget.chatController, @@ -578,12 +587,13 @@ class MessageOverlayController extends State child: SizedBox( height: _reactionsHeight - 8, child: MessageReactions( - widget._pangeaMessageEvent.event, + widget._event, widget.chatController.timeline!, ), ), ), ToolbarButtons( + event: widget._event, overlayController: this, width: 250, ), @@ -597,19 +607,21 @@ class MessageOverlayController extends State : 0; final double? leftPadding = - (widget._pangeaMessageEvent.ownMessage || _messageOffset == null) + (widget._event.senderId == widget._event.room.client.userID || + _messageOffset == null) ? null : _messageOffset!.dx - horizontalPadding - columnOffset; - final double? rightPadding = (widget._pangeaMessageEvent.ownMessage && - _screenWidth != null && - _messageOffset != null && - _messageSize != null) - ? _screenWidth! - - _messageOffset!.dx - - _messageSize!.width - - horizontalPadding - : null; + final double? rightPadding = + (widget._event.senderId == widget._event.room.client.userID && + _screenWidth != null && + _messageOffset != null && + _messageSize != null) + ? _screenWidth! - + _messageOffset!.dx - + _messageSize!.width - + horizontalPadding + : null; final positionedOverlayMessage = (_overlayPositionAnimation == null) ? (_screenHeight == null || diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 8c01880c4..cb88bbf5e 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -20,6 +20,7 @@ import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:matrix/matrix_api_lite/model/message_types.dart'; const double minCardHeight = 70; @@ -149,6 +150,12 @@ class MessageToolbar extends StatelessWidget { @override Widget build(BuildContext context) { + if (![MessageTypes.Text, MessageTypes.Audio].contains( + pangeaMessageEvent.event.messageType, + )) { + return const SizedBox(); + } + return Container( decoration: BoxDecoration( color: Theme.of(context).cardColor, diff --git a/lib/pangea/widgets/chat/message_toolbar_buttons.dart b/lib/pangea/widgets/chat/message_toolbar_buttons.dart index c1961b72b..25fef4c7d 100644 --- a/lib/pangea/widgets/chat/message_toolbar_buttons.dart +++ b/lib/pangea/widgets/chat/message_toolbar_buttons.dart @@ -11,26 +11,29 @@ import 'package:fluffychat/widgets/matrix.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:matrix/matrix.dart'; class ToolbarButtons extends StatelessWidget { + final Event event; final MessageOverlayController overlayController; final double width; const ToolbarButtons({ + required this.event, required this.overlayController, required this.width, super.key, }); - PangeaMessageEvent get pangeaMessageEvent => + PangeaMessageEvent? get pangeaMessageEvent => overlayController.pangeaMessageEvent; List get modes => MessageMode.values - .where((mode) => mode.shouldShowAsToolbarButton(pangeaMessageEvent.event)) + .where((mode) => mode.shouldShowAsToolbarButton(event)) .toList(); bool get messageInUserL2 => - pangeaMessageEvent.messageDisplayLangCode == + pangeaMessageEvent?.messageDisplayLangCode == MatrixState.pangeaController.languageController.userL2?.langCode; static const double iconWidth = 36.0; @@ -42,7 +45,7 @@ class ToolbarButtons extends StatelessWidget { overlayController.isPracticeComplete || !messageInUserL2; final double barWidth = width - iconWidth; - if (!overlayController.showToolbarButtons) { + if (!overlayController.showToolbarButtons || pangeaMessageEvent == null) { return const SizedBox(); } @@ -70,7 +73,7 @@ class ToolbarButtons extends StatelessWidget { : min( barWidth, (barWidth / 3) * - pangeaMessageEvent.numberOfActivitiesCompleted, + pangeaMessageEvent!.numberOfActivitiesCompleted, ), color: AppConfig.success, margin: const EdgeInsets.symmetric(horizontal: iconWidth / 2), @@ -83,14 +86,14 @@ class ToolbarButtons extends StatelessWidget { children: modes.mapIndexed((index, mode) { final enabled = mode.isUnlocked( index, - pangeaMessageEvent.numberOfActivitiesCompleted, + pangeaMessageEvent!.numberOfActivitiesCompleted, totallyDone, ); final color = mode.iconButtonColor( context, index, overlayController.toolbarMode, - pangeaMessageEvent.numberOfActivitiesCompleted, + pangeaMessageEvent!.numberOfActivitiesCompleted, totallyDone, ); return Tooltip( diff --git a/lib/pangea/widgets/chat/message_toolbar_selection_area.dart b/lib/pangea/widgets/chat/message_toolbar_selection_area.dart index 6ddc1026b..03b5db0fd 100644 --- a/lib/pangea/widgets/chat/message_toolbar_selection_area.dart +++ b/lib/pangea/widgets/chat/message_toolbar_selection_area.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; class ToolbarSelectionArea extends StatelessWidget { + final Event event; final ChatController controller; final PangeaMessageEvent? pangeaMessageEvent; final bool isOverlay; @@ -12,6 +13,7 @@ class ToolbarSelectionArea extends StatelessWidget { final Event? prevEvent; const ToolbarSelectionArea({ + required this.event, required this.controller, this.pangeaMessageEvent, this.isOverlay = false, @@ -27,7 +29,8 @@ class ToolbarSelectionArea extends StatelessWidget { onTap: () { if (pangeaMessageEvent != null && !isOverlay) { controller.showToolbar( - pangeaMessageEvent!, + event, + pangeaMessageEvent: pangeaMessageEvent, nextEvent: nextEvent, prevEvent: prevEvent, ); @@ -36,7 +39,8 @@ class ToolbarSelectionArea extends StatelessWidget { onLongPress: () { if (pangeaMessageEvent != null && !isOverlay) { controller.showToolbar( - pangeaMessageEvent!, + event, + pangeaMessageEvent: pangeaMessageEvent, nextEvent: nextEvent, prevEvent: prevEvent, ); diff --git a/lib/pangea/widgets/chat/overlay_message.dart b/lib/pangea/widgets/chat/overlay_message.dart index c6cab6b3d..b28246ed8 100644 --- a/lib/pangea/widgets/chat/overlay_message.dart +++ b/lib/pangea/widgets/chat/overlay_message.dart @@ -10,7 +10,8 @@ import 'package:matrix/matrix.dart'; // @ggurdin be great to explain the need/function of a widget like this class OverlayMessage extends StatelessWidget { - final PangeaMessageEvent pangeaMessageEvent; + final Event event; + final PangeaMessageEvent? pangeaMessageEvent; final MessageOverlayController overlayController; final ChatController controller; final Event? nextEvent; @@ -21,13 +22,14 @@ class OverlayMessage extends StatelessWidget { final double messageHeight; const OverlayMessage( - this.pangeaMessageEvent, { + this.event, { this.immersionMode = false, required this.overlayController, required this.controller, required this.timeline, required this.messageWidth, required this.messageHeight, + this.pangeaMessageEvent, this.nextEvent, this.prevEvent, super.key, @@ -36,14 +38,11 @@ class OverlayMessage extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); - final bool ownMessage = - pangeaMessageEvent.event.senderId == Matrix.of(context).client.userID; + final bool ownMessage = event.senderId == Matrix.of(context).client.userID; - final displayTime = - pangeaMessageEvent.event.type == EventTypes.RoomCreate || - nextEvent == null || - !pangeaMessageEvent.event.originServerTs - .sameEnvironment(nextEvent!.originServerTs); + final displayTime = event.type == EventTypes.RoomCreate || + nextEvent == null || + event.originServerTs.sameEnvironment(nextEvent!.originServerTs); final nextEventSameSender = nextEvent != null && { @@ -51,7 +50,7 @@ class OverlayMessage extends StatelessWidget { EventTypes.Sticker, EventTypes.Encrypted, }.contains(nextEvent!.type) && - nextEvent!.senderId == pangeaMessageEvent.event.senderId && + nextEvent!.senderId == event.senderId && !displayTime; final previousEventSameSender = prevEvent != null && @@ -60,9 +59,8 @@ class OverlayMessage extends StatelessWidget { EventTypes.Sticker, EventTypes.Encrypted, }.contains(prevEvent!.type) && - prevEvent!.senderId == pangeaMessageEvent.event.senderId && - prevEvent!.originServerTs - .sameEnvironment(pangeaMessageEvent.event.originServerTs); + prevEvent!.senderId == event.senderId && + prevEvent!.originServerTs.sameEnvironment(event.originServerTs); const hardCorner = Radius.circular(4); const roundedCorner = Radius.circular(AppConfig.borderRadius); @@ -75,7 +73,7 @@ class OverlayMessage extends StatelessWidget { ownMessage && previousEventSameSender ? hardCorner : roundedCorner, ); - final displayEvent = pangeaMessageEvent.event.getDisplayEvent(timeline); + final displayEvent = event.getDisplayEvent(timeline); // ignore: deprecated_member_use var color = theme.colorScheme.surfaceVariant; if (ownMessage) { @@ -88,12 +86,12 @@ class OverlayMessage extends StatelessWidget { MessageTypes.Video, MessageTypes.Image, MessageTypes.Sticker, - }.contains(pangeaMessageEvent.event.messageType) && - !pangeaMessageEvent.event.redacted; + }.contains(event.messageType) && + !event.redacted; final noPadding = { MessageTypes.File, MessageTypes.Audio, - }.contains(pangeaMessageEvent.event.messageType); + }.contains(event.messageType); return Material( color: color, @@ -116,7 +114,7 @@ class OverlayMessage extends StatelessWidget { ), width: messageWidth, child: MessageContent( - pangeaMessageEvent.event, + event, textColor: ownMessage ? theme.colorScheme.onPrimary : theme.colorScheme.onSurface, diff --git a/lib/pangea/widgets/igc/pangea_rich_text.dart b/lib/pangea/widgets/igc/pangea_rich_text.dart index 8eecdd2f8..278d1ebbb 100644 --- a/lib/pangea/widgets/igc/pangea_rich_text.dart +++ b/lib/pangea/widgets/igc/pangea_rich_text.dart @@ -133,6 +133,7 @@ class PangeaRichTextState extends State { //TODO - take out of build function of every message final Widget richText = ToolbarSelectionArea( + event: widget.pangeaMessageEvent.event, isOverlay: widget.isOverlay, pangeaMessageEvent: widget.pangeaMessageEvent, controller: widget.controller,