diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 3fdeee8ea2..5a78c74d3c 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -10,6 +10,7 @@ import 'package:fluffychat/utils/string_color.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; import '../../../config/app_config.dart'; +import '../../read_receipt_overview/read_receipt_overview.dart'; import 'message_content.dart'; import 'message_reactions.dart'; import 'reply_content.dart'; @@ -31,22 +32,23 @@ class Message extends StatelessWidget { final bool selected; final Timeline timeline; final String? searchTerm; + final ReadReceiptOverviewController? readReceiptOverviewController; - const Message( - this.event, { - this.nextEvent, - this.longPressSelect = false, - this.onSelect, - this.onInfoTab, - this.onAvatarTab, - this.scrollToEventId, - required this.onSwipe, - this.onReadReceipt, - this.selected = false, - required this.timeline, - this.searchTerm, - Key? key, - }) : super(key: key); + const Message(this.event, + {this.nextEvent, + this.longPressSelect = false, + this.onSelect, + this.onInfoTab, + this.onAvatarTab, + this.scrollToEventId, + required this.onSwipe, + this.onReadReceipt, + this.selected = false, + required this.timeline, + this.searchTerm, + this.readReceiptOverviewController, + Key? key}) + : super(key: key); /// Indicates wheither the user may use a mouse instead /// of touchscreen. @@ -129,40 +131,47 @@ class Message extends StatelessWidget { final readReceiptGiven = event .aggregatedEvents(timeline, RelationshipTypes.readReceipt) - .where( - (e) => - e.content - .tryGetMap('m.relates_to') - ?.tryGet('user_id') == - client.userID, - ) + .where((e) => + e.content + .tryGetMap('m.relates_to') + ?.tryGet('user_id') == + client.userID) .toList() .isNotEmpty; final rowChildren = [ if (requiresReadReceipt && !ownMessage) Padding( - padding: EdgeInsets.all( - 8.0 * AppConfig.bubbleSizeFactor, - ), - child: readReceiptGiven - ? Tooltip( - message: L10n.of(context)!.readReceiptGiven, - child: const Icon( - Icons.mark_chat_read, - color: AppConfig.primaryColor, - ), - ) - : IconButton( - tooltip: L10n.of(context)!.readReceiptGive, - padding: const EdgeInsets.all(0), - icon: const Icon( - Icons.mark_chat_read_outlined, - color: AppConfig.primaryColor, - ), - onPressed: () => onReadReceipt?.call(displayEvent), - ), - ), + padding: EdgeInsets.all( + 8.0 * AppConfig.bubbleSizeFactor, + ), + child: readReceiptGiven + ? Tooltip( + message: L10n.of(context)!.readReceiptGiven, + child: const Icon( + Icons.mark_chat_read, + color: AppConfig.primaryColor, + ), + ) + : (readReceiptOverviewController != null && + readReceiptOverviewController! + .readReceiptInProgress(event)) + ? const SizedBox( + width: 22, + height: 22, + child: CircularProgressIndicator.adaptive( + strokeWidth: 2, + ), + ) + : IconButton( + tooltip: L10n.of(context)!.readReceiptGive, + padding: const EdgeInsets.all(0), + icon: const Icon( + Icons.mark_chat_read_outlined, + color: AppConfig.primaryColor, + ), + onPressed: () => onReadReceipt?.call(displayEvent), + )), sameSender || ownMessage ? SizedBox( width: Avatar.defaultSize, @@ -286,9 +295,8 @@ class Message extends StatelessWidget { child: AbsorbPointer( child: Container( margin: EdgeInsets.symmetric( - vertical: - 4.0 * AppConfig.bubbleSizeFactor, - ), + vertical: 4.0 * + AppConfig.bubbleSizeFactor), child: ReplyContent( replyEvent, ownMessage: ownMessage, diff --git a/lib/pages/read_receipt_overview/read_receipt_overview.dart b/lib/pages/read_receipt_overview/read_receipt_overview.dart index 93ba14ffb0..64dcebcba1 100644 --- a/lib/pages/read_receipt_overview/read_receipt_overview.dart +++ b/lib/pages/read_receipt_overview/read_receipt_overview.dart @@ -18,7 +18,7 @@ class ReadReceiptOverviewPage extends StatefulWidget { class ExpansionPanelItem { List readReceiptRequests = []; - List messages = []; + List messageItems = []; bool messagesLoaded = false; Timeline? timeline; Room? room; @@ -29,9 +29,16 @@ class ExpansionPanelItem { ExpansionPanelItem(Room this.room); } +class MessageItem { + final Event message; + bool readReceiptInProgress = false; + + MessageItem(this.message); +} + class ReadReceiptOverviewController extends State { Map panelItems = {}; - Map> _localStorageEvents = {}; + final Map> _localStorageEvents = {}; bool roomsLoaded = false; Client? _client; @@ -177,7 +184,9 @@ class ReadReceiptOverviewController extends State { } Future _addParentToMessages( - MatrixEvent mEvent, ExpansionPanelItem panelItem) async { + MatrixEvent mEvent, + ExpansionPanelItem panelItem, + ) async { final String? parentId = mEvent.content .tryGetMap('m.relates_to') ?.tryGet("event_id"); @@ -190,7 +199,11 @@ class ReadReceiptOverviewController extends State { if (parentEvent != null) { // add related events as aggregated events to timeline await _addAggregatedEventsToTimeline( - parentEvent, mEvent, panelItem.timeline!, room); + parentEvent, + mEvent, + panelItem.timeline!, + room, + ); // events from sync are sorted chronologically up // but we need latest event first -> therefore insert(0, ... @@ -202,8 +215,12 @@ class ReadReceiptOverviewController extends State { return false; } - Future _addAggregatedEventsToTimeline(Event parentEvent, - MatrixEvent aggregatedEvent, Timeline timeline, Room room) async { + Future _addAggregatedEventsToTimeline( + Event parentEvent, + MatrixEvent aggregatedEvent, + Timeline timeline, + Room room, + ) async { final relations = await parentEvent.getRelations(); for (final relation in relations) { @@ -263,28 +280,53 @@ class ReadReceiptOverviewController extends State { return parentEvent; } - void onReadReceiptClick(Event event, ExpansionPanelItem panelItem) async { - final String? readReceiptEventId = - await event.onReadReceiptIconClick(event, panelItem.timeline!, context); + void onReadReceiptClick(Event event, ExpansionPanelItem panelItem, + MessageItem messageItem) async { + if (!messageItem.readReceiptInProgress) { + setState(() { + messageItem.readReceiptInProgress = true; + }); + + final String? readReceiptEventId = await event.onReadReceiptIconClick( + event, panelItem.timeline!, context); - // if readReceiptEventId is !null, then the user has given a new read receipt - if (readReceiptEventId != null) { - final MatrixEvent readReceiptEvent = await panelItem.room!.client - .getOneRoomEvent(panelItem.room!.id, readReceiptEventId); + // if readReceiptEventId is !null, then the user has given a new read receipt + if (readReceiptEventId != null) { + final MatrixEvent readReceiptEvent = await panelItem.room!.client + .getOneRoomEvent(panelItem.room!.id, readReceiptEventId); - _addAggregatedEventsToTimeline( - event, readReceiptEvent, panelItem.timeline!, panelItem.room!); + _addAggregatedEventsToTimeline( + event, + readReceiptEvent, + panelItem.timeline!, + panelItem.room!, + ); - await _client!.updateOpenReadReceipts(panelItem.room!.id); - _updateOpenReadReceipt(panelItem); - _sortPanelItems(); + await _client!.updateOpenReadReceipts(panelItem.room!.id); + _updateOpenReadReceipt(panelItem); + _sortPanelItems(); - setState(() { - panelItems; - }); + messageItem.readReceiptInProgress = false; + setState(() { + messageItem; + }); + } } } + // returns true if process of giving read receipt for current user is in progress + bool readReceiptInProgress(Event event) { + for (final panelItem in panelItems.values) { + for (final messageItem in panelItem.messageItems) { + if (messageItem.message.eventId == event.eventId) { + return messageItem.readReceiptInProgress; + } + } + } + + return false; + } + void expansionCallback(panelIndex, isExpanded) { if (panelItems.length > panelIndex) { final panelItem = panelItems.values.elementAt(panelIndex); diff --git a/lib/pages/read_receipt_overview/read_receipt_overview_view.dart b/lib/pages/read_receipt_overview/read_receipt_overview_view.dart index 95d8027645..dc11e5d625 100644 --- a/lib/pages/read_receipt_overview/read_receipt_overview_view.dart +++ b/lib/pages/read_receipt_overview/read_receipt_overview_view.dart @@ -98,28 +98,31 @@ class ReadReceiptOverviewView extends StatelessWidget { child: item.messagesLoaded == true ? Column( children: [ - if (item.messages.isEmpty) + if (item.messageItems.isEmpty) Text( L10n.of(context)! .noReadReceiptRequestsFound, ) else - for (var message in item.messages) + for (var messageItem + in item.messageItems) Padding( padding: const EdgeInsets.only( right: 25, ), child: Message( - message, + messageItem.message, onSwipe: (swipeDirection) {}, onReadReceipt: (event) => controller .onReadReceiptClick( - event, - item, - ), + event, + item, + messageItem), onSelect: (event) {}, timeline: item.timeline!, + readReceiptOverviewController: + controller, ), ), ],