From f6175b9c0981a3c30747e3172a5c24cca69c0b8b Mon Sep 17 00:00:00 2001 From: ggurdin <46800240+ggurdin@users.noreply.github.com> Date: Mon, 2 Dec 2024 16:31:00 -0500 Subject: [PATCH] Testing updates (#1131) * extend time between activities * if user clicks on punctuation token, select the closest non-punctuation token * rebuild chat list after prevBatch is set --- lib/pages/chat/events/message_content.dart | 22 +++++++--- lib/pages/chat_list/chat_list_view.dart | 7 ++- .../pangea_representation_event.dart | 43 +++++++++++++++++++ .../chat/chat_list_view_body_wrapper.dart | 43 +++++++++++++++++++ .../practice_activity_card.dart | 2 +- 5 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 lib/pangea/widgets/chat/chat_list_view_body_wrapper.dart diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index e1e4718be..05f906d47 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -320,12 +320,22 @@ class MessageContent extends StatelessWidget { tokens: pangeaMessageEvent!.messageDisplayRepresentation?.tokens, style: messageTextStyle, - onClick: overlayController?.onClickOverlayMessageToken ?? - (token) => controller.showToolbar( - event, - pangeaMessageEvent: pangeaMessageEvent, - selectedToken: token, - ), + onClick: (token) { + token = pangeaMessageEvent?.messageDisplayRepresentation + ?.getClosestNonPunctToken(token) ?? + token; + + if (overlayController != null) { + overlayController?.onClickOverlayMessageToken(token); + return; + } + + controller.showToolbar( + event, + pangeaMessageEvent: pangeaMessageEvent, + selectedToken: token, + ); + }, isSelected: overlayController?.isTokenSelected, ); } diff --git a/lib/pages/chat_list/chat_list_view.dart b/lib/pages/chat_list/chat_list_view.dart index 2cc33da06..66d323c1f 100644 --- a/lib/pages/chat_list/chat_list_view.dart +++ b/lib/pages/chat_list/chat_list_view.dart @@ -2,6 +2,7 @@ import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart'; import 'package:fluffychat/pages/chat_list/navi_rail_item.dart'; +import 'package:fluffychat/pangea/widgets/chat/chat_list_view_body_wrapper.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/stream_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; @@ -11,7 +12,6 @@ import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; import '../../widgets/matrix.dart'; -import 'chat_list_body.dart'; class ChatListView extends StatelessWidget { final ChatListController controller; @@ -134,7 +134,10 @@ class ChatListView extends StatelessWidget { excludeFromSemantics: true, behavior: HitTestBehavior.translucent, child: Scaffold( - body: ChatListViewBody(controller), + // #Pangea + // body: ChatListViewBody(controller), + body: ChatListViewBodyWrapper(controller: controller), + // Pangea# floatingActionButton: // #Pangea // KeyBoardShortcuts( diff --git a/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart b/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart index da6c798cb..3c0d51603 100644 --- a/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart +++ b/lib/pangea/matrix_event_wrappers/pangea_representation_event.dart @@ -1,5 +1,6 @@ import 'dart:developer'; +import 'package:collection/collection.dart'; import 'package:fluffychat/pangea/extensions/pangea_event_extension.dart'; import 'package:fluffychat/pangea/matrix_event_wrappers/pangea_choreo_event.dart'; import 'package:fluffychat/pangea/models/pangea_token_model.dart'; @@ -190,4 +191,46 @@ class RepresentationEvent { String? formatBody() { return markdown(content.text); } + + /// Finds the closest non-punctuation token to the given token. + /// + /// This method checks if the provided token is a punctuation token. If it is not, + /// it returns the token itself. If the token is a punctuation token, it searches + /// through the list of tokens to find the closest non-punctuation token either to + /// the left or right of the given token. + /// + /// If both left and right non-punctuation tokens are found, it returns the one + /// that is closest to the given token. If only one of them is found, it returns + /// that token. If no non-punctuation tokens are found, it returns null. + /// + /// - Parameters: + /// - token: The token for which to find the closest non-punctuation token. + /// + /// - Returns: The closest non-punctuation token, or null if no such token exists. + PangeaToken? getClosestNonPunctToken(PangeaToken token) { + if (token.pos != "PUNCT") return token; + if (tokens == null) return null; + final index = tokens!.indexOf(token); + if (index > -1) { + final leftTokens = tokens!.sublist(0, index); + final rightTokens = tokens!.sublist(index + 1); + final leftMostToken = leftTokens.lastWhereOrNull( + (element) => element.pos != "PUNCT", + ); + final rightMostToken = rightTokens.firstWhereOrNull( + (element) => element.pos != "PUNCT", + ); + + if (leftMostToken != null && rightMostToken != null) { + final leftDistance = token.start - leftMostToken.end; + final rightDistance = rightMostToken.start - token.end; + return leftDistance < rightDistance ? leftMostToken : rightMostToken; + } else if (leftMostToken != null) { + return leftMostToken; + } else if (rightMostToken != null) { + return rightMostToken; + } + } + return null; + } } diff --git a/lib/pangea/widgets/chat/chat_list_view_body_wrapper.dart b/lib/pangea/widgets/chat/chat_list_view_body_wrapper.dart new file mode 100644 index 000000000..647aa1c5f --- /dev/null +++ b/lib/pangea/widgets/chat/chat_list_view_body_wrapper.dart @@ -0,0 +1,43 @@ +import 'package:fluffychat/pages/chat_list/chat_list.dart'; +import 'package:fluffychat/pages/chat_list/chat_list_body.dart'; +import 'package:fluffychat/widgets/matrix.dart'; +import 'package:flutter/material.dart'; +import 'package:matrix/matrix.dart'; + +/// The ChatListBody often appears to load forever if prevBatch is null when it first loads. +/// This wrapper triggers a rebuild when the client has finished its first sync. +class ChatListViewBodyWrapper extends StatefulWidget { + final ChatListController controller; + + const ChatListViewBodyWrapper({ + required this.controller, + super.key, + }); + + @override + State createState() => + ChatListViewBodyWrapperState(); +} + +class ChatListViewBodyWrapperState extends State { + @override + void initState() { + final client = Matrix.of(context).client; + if (client.prevBatch == null) { + // SyncStatus.cleaningUp is added to the stream right after prevBatch is set + // so wait for that to confirm that prevBatch is set + client.onSyncStatus.stream + .firstWhere((update) => update.status == SyncStatus.cleaningUp) + .then((update) => setState(() {})); + } + super.initState(); + } + + @override + void dispose() { + super.dispose(); + } + + @override + Widget build(BuildContext context) => ChatListViewBody(widget.controller); +} diff --git a/lib/pangea/widgets/practice_activity/practice_activity_card.dart b/lib/pangea/widgets/practice_activity/practice_activity_card.dart index 0898b8b74..2fbc7cd10 100644 --- a/lib/pangea/widgets/practice_activity/practice_activity_card.dart +++ b/lib/pangea/widgets/practice_activity/practice_activity_card.dart @@ -54,7 +54,7 @@ class PracticeActivityCardState extends State { // Used to show an animation when the user completes an activity // while simultaneously fetching a new activity and not showing the loading spinner // until the appropriate time has passed to 'savor the joy' - Duration appropriateTimeForJoy = const Duration(milliseconds: 1500); + Duration appropriateTimeForJoy = const Duration(milliseconds: 2500); bool savoringTheJoy = false; TtsController get tts =>