diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index d978346..bbb73e5 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -317,6 +317,8 @@ class ChatController extends State if (kIsWeb) { onFocusSub = html.window.onFocus.listen((_) => setReadMarker()); } + + _getThreads(); } void _tryLoadTimeline() async { @@ -376,6 +378,7 @@ class ChatController extends State } Future? loadTimelineFuture; + Map? threads; int? animateInEventIndex; @@ -418,6 +421,15 @@ class ChatController extends State return; } + Future _getThreads() async { + try { + threads = await room.getThreads(); + Logs().w('Thread amount: ${threads?.length}'); + } catch (e, s) { + Logs().w('Unable to load threads in $roomId', e, s); + } + } + String? scrollToEventIdMarker; @override @@ -754,7 +766,7 @@ class ChatController extends State return; } - Navigator.of(context).push(new MaterialPageRoute( + Navigator.of(context).push(MaterialPageRoute( builder: (BuildContext ctx) { return RecoveredEventDialog( event: recoveredEvent, @@ -774,7 +786,7 @@ class ChatController extends State } final event = selectedEvents.single; var text = event.isRichMessage ? event.formattedText : event.text; - var content = {...event.content}; + final content = {...event.content}; try { text = await Translator.translate( text, PlatformDispatcher.instance.locale.languageCode); @@ -873,18 +885,23 @@ class ChatController extends State void endPollAction() async { final event = selectedEvents.first; - if (event == null) return; final client = currentRoomBundle.firstWhere( (cl) => selectedEvents.first.senderId == cl!.userID, orElse: () => null, ); if (client == null) return; - if (event.senderId != client!.userID) return; - await room.sendEvent({ - 'org.matrix.msc1767.text': 'Ended poll', - 'm.relates_to': {'rel_type': 'm.reference', 'event_id': event.eventId}, - 'body': 'Ended poll' - }, type: 'org.matrix.msc3381.poll.end'); + if (event.senderId != client.userID) return; + await room.sendEvent( + { + 'org.matrix.msc1767.text': 'Ended poll', + 'm.relates_to': { + 'rel_type': 'm.reference', + 'event_id': event.eventId, + }, + 'body': 'Ended poll', + }, + type: 'org.matrix.msc3381.poll.end', + ); } void redactEventsAction() async { diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index 3fc2937..1db76b2 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -37,7 +37,7 @@ class ChatEventList extends StatelessWidget { final horizontalPadding = FluffyThemes.isColumnMode(context) ? 8.0 : 0.0; - final events = timeline.events.filterByVisibleInGui(); + final events = timeline.events.filterByVisibleInGui().filterByThreaded(false); final animateInEventIndex = controller.animateInEventIndex; // create a map of eventId --> index to greatly improve performance of @@ -120,6 +120,10 @@ class ChatEventList extends StatelessWidget { final animateIn = animateInEventIndex != null && timeline.events.length > animateInEventIndex && event == timeline.events[animateInEventIndex]; + + final thread = (controller.threads?.containsKey(event.eventId) ?? false) + ? controller.threads![event.eventId] + : null; return AutoScrollTag( key: ValueKey(event.eventId), @@ -128,6 +132,7 @@ class ChatEventList extends StatelessWidget { child: Message( event, animateIn: animateIn, + thread: thread, resetAnimateIn: () { controller.animateInEventIndex = null; }, diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 0f4d26b..cb55680 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -45,6 +45,7 @@ class Message extends StatelessWidget { final List colors; final bool gradient; final bool singleSelected; + final Thread? thread; const Message( this.event, { @@ -54,6 +55,7 @@ class Message extends StatelessWidget { this.longPressSelect = false, this.gradient = false, this.singleSelected = false, + this.thread, required this.onSelect, required this.onInfoTab, required this.scrollToEventId, @@ -688,6 +690,11 @@ class Message extends StatelessWidget { : const SizedBox.shrink(), ), ), + Text( + thread == null + ? 'No thread' + : 'Has thread, last event: ${thread!.lastEvent != null ? thread!.lastEvent!.eventId : 'None'}', + ), ], ), ), diff --git a/lib/pages/chat_list/chat_list_item.dart b/lib/pages/chat_list/chat_list_item.dart index 9ccac97..ffa49a4 100644 --- a/lib/pages/chat_list/chat_list_item.dart +++ b/lib/pages/chat_list/chat_list_item.dart @@ -253,7 +253,7 @@ class ChatListItem extends StatelessWidget { children: [ if (typingText.isEmpty && ownMessage && - room.lastEvent!.status.isSending) ...[ + (room.lastEvent?.status.isSending ?? false)) ...[ const SizedBox( width: 16, height: 16, diff --git a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart index 756d23f..4732c56 100644 --- a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart +++ b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart @@ -4,6 +4,10 @@ import 'package:matrix/matrix.dart'; import '../../config/app_config.dart'; extension VisibleInGuiExtension on List { + List filterByThreaded(bool threaded) { + return where((e) => e.isThreaded == threaded).toList(); + } + List filterByVisibleInGui({String? exceptionEventId}) { final visibleEvents = where((e) => e.isVisibleInGui || e.eventId == exceptionEventId) @@ -46,7 +50,9 @@ extension IsStateExtension on Event { // if we enabled to hide all redacted events, don't show those (!AppConfig.hideRedactedEvents || !redacted) && // if we enabled to hide all unknown events, don't show those - (!AppConfig.hideUnknownEvents || isEventTypeKnown || type == PollEvents.PollStart) && + (!AppConfig.hideUnknownEvents || + isEventTypeKnown || + type == PollEvents.PollStart) && // remove state events that we don't want to render (isState || !AppConfig.hideAllStateEvents) && // hide simple join/leave member events in public rooms @@ -56,5 +62,9 @@ extension IsStateExtension on Event { content.tryGet('membership') == 'ban' || stateKey != senderId); + bool get isThreaded => + relationshipEventId != null && + relationshipType == RelationshipTypes.thread; + bool get isState => this.stateKey != null; }