From 4222b37bb41a0344d708ba2ccb570ba050ec211f Mon Sep 17 00:00:00 2001 From: Kelrap Date: Thu, 18 Jul 2024 15:13:27 -0400 Subject: [PATCH 01/32] Attempt 3. Needs work. --- lib/pages/chat/chat.dart | 4 ++ lib/pages/chat/chat_view.dart | 26 +++---------- lib/pangea/widgets/chat/chat_footer.dart | 48 ++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 20 deletions(-) create mode 100644 lib/pangea/widgets/chat/chat_footer.dart diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index e1af6a2c0..4cb8b962e 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -24,6 +24,7 @@ import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/firebase_analytics.dart'; import 'package:fluffychat/pangea/utils/report_message.dart'; +import 'package:fluffychat/pangea/widgets/chat/chat_footer.dart'; import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; import 'package:fluffychat/pangea/widgets/igc/pangea_text_controller.dart'; import 'package:fluffychat/utils/error_reporter.dart'; @@ -112,6 +113,9 @@ class ChatController extends State with WidgetsBindingObserver { // #Pangea final PangeaController pangeaController = MatrixState.pangeaController; + + final GlobalKey chatFooterKey = GlobalKey(); + late Choreographer choreographer = Choreographer(pangeaController, this); // Pangea# Room get room => sendingClient.getRoomById(roomId) ?? widget.room; diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 36e6d69dc..077613559 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -5,14 +5,12 @@ import 'package:fluffychat/pages/chat/chat_app_bar_list_tile.dart'; import 'package:fluffychat/pages/chat/chat_app_bar_title.dart'; import 'package:fluffychat/pages/chat/chat_event_list.dart'; import 'package:fluffychat/pages/chat/pinned_events.dart'; -import 'package:fluffychat/pages/chat/reactions_picker.dart'; -import 'package:fluffychat/pages/chat/reply_display.dart'; import 'package:fluffychat/pangea/choreographer/widgets/start_igc_button.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/widgets/chat/chat_floating_action_button.dart'; +import 'package:fluffychat/pangea/widgets/chat/chat_footer.dart'; import 'package:fluffychat/utils/account_config.dart'; import 'package:fluffychat/widgets/chat_settings_popup_menu.dart'; -import 'package:fluffychat/widgets/connection_status_header.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/mxc_image.dart'; import 'package:fluffychat/widgets/unread_rooms_badge.dart'; @@ -22,10 +20,7 @@ import 'package:future_loading_dialog/future_loading_dialog.dart'; import 'package:go_router/go_router.dart'; import 'package:matrix/matrix.dart'; -import '../../pangea/choreographer/widgets/it_bar.dart'; import '../../utils/stream_extension.dart'; -import 'chat_emoji_picker.dart'; -import 'chat_input_row.dart'; enum _EventContextAction { info, report } @@ -404,19 +399,9 @@ class ChatView extends StatelessWidget { ), ], ) - : Column( - mainAxisSize: MainAxisSize.min, - children: [ - const ConnectionStatusHeader(), - ITBar( - choreographer: - controller.choreographer, - ), - ReactionsPicker(controller), - ReplyDisplay(controller), - ChatInputRow(controller), - ChatEmojiPicker(controller), - ], + : ChatFooter( + controller, + key: controller.chatFooterKey, ), ), ), @@ -437,7 +422,8 @@ class ChatView extends StatelessWidget { // ), Positioned( left: 20, - bottom: 75, + bottom: + controller.chatFooterKey.currentState?.height ?? 100, child: StartIGCButton(controller: controller), ), // Pangea# diff --git a/lib/pangea/widgets/chat/chat_footer.dart b/lib/pangea/widgets/chat/chat_footer.dart new file mode 100644 index 000000000..3235916e1 --- /dev/null +++ b/lib/pangea/widgets/chat/chat_footer.dart @@ -0,0 +1,48 @@ +import 'package:fluffychat/pages/chat/chat.dart'; +import 'package:fluffychat/pages/chat/chat_emoji_picker.dart'; +import 'package:fluffychat/pages/chat/chat_input_row.dart'; +import 'package:fluffychat/pages/chat/reactions_picker.dart'; +import 'package:fluffychat/pages/chat/reply_display.dart'; +import 'package:fluffychat/pangea/choreographer/widgets/it_bar.dart'; +import 'package:fluffychat/widgets/connection_status_header.dart'; +import 'package:flutter/material.dart'; + +class ChatFooter extends StatefulWidget { + final ChatController controller; + const ChatFooter( + this.controller, { + super.key, + }); + + @override + ChatFooterState createState() => ChatFooterState(); +} + +class ChatFooterState extends State { + final GlobalKey _widgetKey = GlobalKey(); + + double? get height => _widgetKey.currentContext?.size?.height; + + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Column( + key: _widgetKey, + mainAxisSize: MainAxisSize.min, + children: [ + const ConnectionStatusHeader(), + ITBar( + choreographer: widget.controller.choreographer, + ), + ReactionsPicker(widget.controller), + ReplyDisplay(widget.controller), + ChatInputRow(widget.controller), + ChatEmojiPicker(widget.controller), + ], + ); + } +} From 08da254bae57872471fa3a221832e14b2bf5f7d9 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Thu, 18 Jul 2024 16:33:54 -0400 Subject: [PATCH 02/32] Input text turns red when hit limit --- lib/pages/chat/input_bar.dart | 3 +++ lib/pangea/widgets/chat/input_bar_wrapper.dart | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/pages/chat/input_bar.dart b/lib/pages/chat/input_bar.dart index 9337b488a..0d2251203 100644 --- a/lib/pages/chat/input_bar.dart +++ b/lib/pages/chat/input_bar.dart @@ -504,6 +504,9 @@ class InputBar extends StatelessWidget { onSubmitted!(text); }, // #Pangea + style: controller?.text.length == 1000 + ? const TextStyle(color: Colors.red) + : null, onTap: () { controller!.onInputTap( context, diff --git a/lib/pangea/widgets/chat/input_bar_wrapper.dart b/lib/pangea/widgets/chat/input_bar_wrapper.dart index 374f60a80..5f6c1b49b 100644 --- a/lib/pangea/widgets/chat/input_bar_wrapper.dart +++ b/lib/pangea/widgets/chat/input_bar_wrapper.dart @@ -61,6 +61,11 @@ class InputBarWrapperState extends State { super.dispose(); } + void refreshOnChange(String text) { + widget.onChanged!(text); + setState(() {}); + } + @override Widget build(BuildContext context) { return InputBar( @@ -73,7 +78,7 @@ class InputBarWrapperState extends State { focusNode: widget.focusNode, controller: widget.controller, decoration: widget.decoration, - onChanged: widget.onChanged, + onChanged: widget.onChanged != null ? refreshOnChange : null, autofocus: widget.autofocus, textInputAction: widget.textInputAction, readOnly: widget.readOnly, From 276076b265f6fb0e1eac12daf2e0e98eb3abe3c2 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Fri, 19 Jul 2024 13:56:19 -0400 Subject: [PATCH 03/32] Buttons move when input bar height changes --- lib/pages/chat/chat_view.dart | 32 +++++++++++--- lib/pages/chat/reply_display.dart | 28 ++++++------ .../language_permissions_warning_buttons.dart | 11 ++--- .../chat/chat_floating_action_button.dart | 13 +++--- lib/pangea/widgets/chat/chat_footer.dart | 43 ++++++++++++------- 5 files changed, 77 insertions(+), 50 deletions(-) diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 077613559..f2d9f68fe 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -269,9 +269,6 @@ class ChatView extends StatelessWidget { // ), // ) // : null, - floatingActionButton: ChatFloatingActionButton( - controller: controller, - ), // Pangea# body: // #Pangea @@ -399,10 +396,25 @@ class ChatView extends StatelessWidget { ), ], ) + // #Pangea + // : Column( + // mainAxisSize: MainAxisSize.min, + // children: [ + // const ConnectionStatusHeader(), + // ITBar( + // choreographer: + // controller.choreographer, + // ), + // ReactionsPicker(controller), + // ReplyDisplay(controller), + // ChatInputRow(controller), + // ChatEmojiPicker(controller), + // ], : ChatFooter( controller, key: controller.chatFooterKey, ), + // Pangea# ), ), ], @@ -421,11 +433,19 @@ class ChatView extends StatelessWidget { // ), // ), Positioned( - left: 20, - bottom: - controller.chatFooterKey.currentState?.height ?? 100, + left: 7, + bottom: 50 + + (controller.chatFooterKey.currentState?.height ?? 50), child: StartIGCButton(controller: controller), ), + Positioned( + right: 7, + bottom: 50 + + (controller.chatFooterKey.currentState?.height ?? 50), + child: ChatFloatingActionButton( + controller: controller, + ), + ), // Pangea# ], ), diff --git a/lib/pages/chat/reply_display.dart b/lib/pages/chat/reply_display.dart index 03acd269e..e5b343906 100644 --- a/lib/pages/chat/reply_display.dart +++ b/lib/pages/chat/reply_display.dart @@ -1,9 +1,8 @@ +import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:flutter/material.dart'; - import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import '../../config/themes.dart'; import 'chat.dart'; import 'events/reply_content.dart'; @@ -65,19 +64,22 @@ class _EditContent extends StatelessWidget { Icons.edit, color: Theme.of(context).colorScheme.primary, ), - Container(width: 15.0), - Text( - event.calcLocalizedBodyFallback( - MatrixLocals(L10n.of(context)!), - withSenderNamePrefix: false, - hideReply: true, - ), - overflow: TextOverflow.ellipsis, - maxLines: 1, - style: TextStyle( - color: Theme.of(context).textTheme.bodyMedium!.color, + Container(width: 8.0), + Flexible( + child: Text( + event.calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)!), + withSenderNamePrefix: false, + hideReply: true, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: TextStyle( + color: Theme.of(context).textTheme.bodyMedium!.color, + ), ), ), + Container(width: 10.0), ], ); } diff --git a/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart b/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart index 0abe90925..fcf3b57be 100644 --- a/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart +++ b/lib/pangea/choreographer/widgets/language_permissions_warning_buttons.dart @@ -58,13 +58,10 @@ class LanguagePermissionsButtons extends StatelessWidget { ), ); - return Padding( - padding: const EdgeInsets.only(bottom: 56.0), - child: FloatingActionButton( - mini: true, - child: const Icon(Icons.history_edu_outlined), - onPressed: () => showMessage(context, text), - ), + return FloatingActionButton( + mini: true, + child: const Icon(Icons.history_edu_outlined), + onPressed: () => showMessage(context, text), ); } diff --git a/lib/pangea/widgets/chat/chat_floating_action_button.dart b/lib/pangea/widgets/chat/chat_floating_action_button.dart index 735b51b36..35ea1c3eb 100644 --- a/lib/pangea/widgets/chat/chat_floating_action_button.dart +++ b/lib/pangea/widgets/chat/chat_floating_action_button.dart @@ -67,14 +67,11 @@ class ChatFloatingActionButtonState extends State { return const SizedBox.shrink(); } if (widget.controller.showScrollDownButton) { - return Padding( - padding: const EdgeInsets.only(bottom: 56.0), - child: FloatingActionButton( - onPressed: widget.controller.scrollDown, - heroTag: null, - mini: true, - child: const Icon(Icons.arrow_downward_outlined), - ), + return FloatingActionButton( + onPressed: widget.controller.scrollDown, + heroTag: null, + mini: true, + child: const Icon(Icons.arrow_downward_outlined), ); } if (widget.controller.choreographer.errorService.error != null) { diff --git a/lib/pangea/widgets/chat/chat_footer.dart b/lib/pangea/widgets/chat/chat_footer.dart index 3235916e1..f47ebf193 100644 --- a/lib/pangea/widgets/chat/chat_footer.dart +++ b/lib/pangea/widgets/chat/chat_footer.dart @@ -19,30 +19,41 @@ class ChatFooter extends StatefulWidget { } class ChatFooterState extends State { - final GlobalKey _widgetKey = GlobalKey(); - - double? get height => _widgetKey.currentContext?.size?.height; + double? height; @override void initState() { super.initState(); + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() { + height = context.size!.height; + }); + }); } @override Widget build(BuildContext context) { - return Column( - key: _widgetKey, - mainAxisSize: MainAxisSize.min, - children: [ - const ConnectionStatusHeader(), - ITBar( - choreographer: widget.controller.choreographer, - ), - ReactionsPicker(widget.controller), - ReplyDisplay(widget.controller), - ChatInputRow(widget.controller), - ChatEmojiPicker(widget.controller), - ], + return NotificationListener( + onNotification: (_) { + if (height != context.size!.height) { + height = context.size!.height; + widget.controller.updateView(); + } + return true; + }, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const ConnectionStatusHeader(), + ITBar( + choreographer: widget.controller.choreographer, + ), + ReactionsPicker(widget.controller), + ReplyDisplay(widget.controller), + ChatInputRow(widget.controller), + ChatEmojiPicker(widget.controller), + ], + ), ); } } From d4febba12e4776aa0ea254c1d7eb6244d7ce8635 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Fri, 19 Jul 2024 14:06:45 -0400 Subject: [PATCH 04/32] Add #Pangea comments --- lib/pages/chat/reply_display.dart | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/pages/chat/reply_display.dart b/lib/pages/chat/reply_display.dart index e5b343906..b2b7ca0bb 100644 --- a/lib/pages/chat/reply_display.dart +++ b/lib/pages/chat/reply_display.dart @@ -64,9 +64,13 @@ class _EditContent extends StatelessWidget { Icons.edit, color: Theme.of(context).colorScheme.primary, ), + // #Pangea + // Container(width: 15.0), Container(width: 8.0), Flexible( - child: Text( + child: + // Pangea# + Text( event.calcLocalizedBodyFallback( MatrixLocals(L10n.of(context)!), withSenderNamePrefix: false, @@ -79,7 +83,9 @@ class _EditContent extends StatelessWidget { ), ), ), + // #Pangea Container(width: 10.0), + // Pangea# ], ); } From 828b406286926f622cac999e70d345687d24c95c Mon Sep 17 00:00:00 2001 From: Kelrap Date: Mon, 22 Jul 2024 10:04:18 -0400 Subject: [PATCH 05/32] Only rebuild when maxLength changes --- lib/pages/chat/input_bar.dart | 14 +++++++++++--- lib/pangea/widgets/chat/input_bar_wrapper.dart | 6 +++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/lib/pages/chat/input_bar.dart b/lib/pages/chat/input_bar.dart index 0d2251203..4ffde1f42 100644 --- a/lib/pages/chat/input_bar.dart +++ b/lib/pages/chat/input_bar.dart @@ -21,6 +21,7 @@ class InputBar extends StatelessWidget { final ValueChanged? onSubmitImage; final FocusNode? focusNode; // #Pangea + final Function? updateBar; // final TextEditingController? controller; final PangeaTextController? controller; // Pangea# @@ -38,6 +39,7 @@ class InputBar extends StatelessWidget { this.onSubmitImage, this.focusNode, this.controller, + this.updateBar, this.decoration, this.onChanged, this.autofocus, @@ -401,6 +403,9 @@ class InputBar extends StatelessWidget { @override Widget build(BuildContext context) { final useShortCuts = (AppConfig.sendOnEnter ?? !PlatformInfos.isMobile); + // #Pangea + final bool maxLength = controller?.text.length == 1000; + // Pangea# return Shortcuts( shortcuts: !useShortCuts ? {} @@ -504,9 +509,7 @@ class InputBar extends StatelessWidget { onSubmitted!(text); }, // #Pangea - style: controller?.text.length == 1000 - ? const TextStyle(color: Colors.red) - : null, + style: maxLength ? const TextStyle(color: Colors.red) : null, onTap: () { controller!.onInputTap( context, @@ -519,6 +522,11 @@ class InputBar extends StatelessWidget { // fix for the library for now // it sets the types for the callback incorrectly onChanged!(text); + // #Pangea + if (maxLength != (controller?.text.length == 1000)) { + updateBar!(); + } + // Pangea# }, textCapitalization: TextCapitalization.sentences, ), diff --git a/lib/pangea/widgets/chat/input_bar_wrapper.dart b/lib/pangea/widgets/chat/input_bar_wrapper.dart index 5f6c1b49b..5aa813bbe 100644 --- a/lib/pangea/widgets/chat/input_bar_wrapper.dart +++ b/lib/pangea/widgets/chat/input_bar_wrapper.dart @@ -61,8 +61,7 @@ class InputBarWrapperState extends State { super.dispose(); } - void refreshOnChange(String text) { - widget.onChanged!(text); + void refreshOnChange() { setState(() {}); } @@ -78,7 +77,8 @@ class InputBarWrapperState extends State { focusNode: widget.focusNode, controller: widget.controller, decoration: widget.decoration, - onChanged: widget.onChanged != null ? refreshOnChange : null, + updateBar: refreshOnChange, + onChanged: widget.onChanged, autofocus: widget.autofocus, textInputAction: widget.textInputAction, readOnly: widget.readOnly, From e8a4005a128de7ec2719849ff17e447968d0ae1c Mon Sep 17 00:00:00 2001 From: Kelrap Date: Mon, 22 Jul 2024 13:39:51 -0400 Subject: [PATCH 06/32] Use stack, minimize footer shifting messages --- lib/pages/chat/chat.dart | 10 +- lib/pages/chat/chat_view.dart | 117 +++++++++++++++++------ lib/pangea/widgets/chat/chat_footer.dart | 59 ------------ 3 files changed, 95 insertions(+), 91 deletions(-) delete mode 100644 lib/pangea/widgets/chat/chat_footer.dart diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 4cb8b962e..bd0462401 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -24,7 +24,6 @@ import 'package:fluffychat/pangea/models/tokens_event_content_model.dart'; import 'package:fluffychat/pangea/utils/error_handler.dart'; import 'package:fluffychat/pangea/utils/firebase_analytics.dart'; import 'package:fluffychat/pangea/utils/report_message.dart'; -import 'package:fluffychat/pangea/widgets/chat/chat_footer.dart'; import 'package:fluffychat/pangea/widgets/chat/message_toolbar.dart'; import 'package:fluffychat/pangea/widgets/igc/pangea_text_controller.dart'; import 'package:fluffychat/utils/error_reporter.dart'; @@ -114,7 +113,7 @@ class ChatController extends State // #Pangea final PangeaController pangeaController = MatrixState.pangeaController; - final GlobalKey chatFooterKey = GlobalKey(); + // final GlobalKey chatFooterKey = GlobalKey(); late Choreographer choreographer = Choreographer(pangeaController, this); // Pangea# @@ -381,6 +380,13 @@ class ChatController extends State setState(() {}); } + // #Pangea + void closeEmojiPicker() { + showEmojiPicker = false; + updateView(); + } + // Pangea# + Future? loadTimelineFuture; int? animateInEventIndex; diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index f2d9f68fe..783ccbb6a 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -3,14 +3,19 @@ import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/chat_app_bar_list_tile.dart'; import 'package:fluffychat/pages/chat/chat_app_bar_title.dart'; +import 'package:fluffychat/pages/chat/chat_emoji_picker.dart'; import 'package:fluffychat/pages/chat/chat_event_list.dart'; +import 'package:fluffychat/pages/chat/chat_input_row.dart'; import 'package:fluffychat/pages/chat/pinned_events.dart'; +import 'package:fluffychat/pages/chat/reactions_picker.dart'; +import 'package:fluffychat/pages/chat/reply_display.dart'; +import 'package:fluffychat/pangea/choreographer/widgets/it_bar.dart'; import 'package:fluffychat/pangea/choreographer/widgets/start_igc_button.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/widgets/chat/chat_floating_action_button.dart'; -import 'package:fluffychat/pangea/widgets/chat/chat_footer.dart'; import 'package:fluffychat/utils/account_config.dart'; import 'package:fluffychat/widgets/chat_settings_popup_menu.dart'; +import 'package:fluffychat/widgets/connection_status_header.dart'; import 'package:fluffychat/widgets/matrix.dart'; import 'package:fluffychat/widgets/mxc_image.dart'; import 'package:fluffychat/widgets/unread_rooms_badge.dart'; @@ -296,22 +301,57 @@ class ChatView extends StatelessWidget { child: Column( children: [ Expanded( - child: GestureDetector( - onTap: controller.clearSingleSelectedEvent, - child: Builder( - builder: (context) { - if (controller.timeline == null) { - return const Center( - child: CircularProgressIndicator.adaptive( - strokeWidth: 2, + child: Stack( + children: [ + GestureDetector( + onTap: controller.clearSingleSelectedEvent, + child: Builder( + builder: (context) { + if (controller.timeline == null) { + return const Center( + child: + CircularProgressIndicator.adaptive( + strokeWidth: 2, + ), + ); + } + return ChatEventList( + controller: controller, + ); + }, + ), + ), + Positioned( + left: 0, + right: 0, + bottom: 7, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (!controller.selectMode) + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + StartIGCButton( + controller: controller, + ), + ChatFloatingActionButton( + controller: controller, + ), + ], + ), + const ConnectionStatusHeader(), + ITBar( + choreographer: controller.choreographer, ), - ); - } - return ChatEventList( - controller: controller, - ); - }, - ), + // if (!controller.selectMode) + ReplyDisplay(controller), + ], + ), + ), + ], ), ), if (controller.room.canSendDefaultMessages && @@ -410,9 +450,11 @@ class ChatView extends StatelessWidget { // ChatInputRow(controller), // ChatEmojiPicker(controller), // ], - : ChatFooter( - controller, - key: controller.chatFooterKey, + : Column( + children: [ + ReactionsPicker(controller), + ChatInputRow(controller), + ], ), // Pangea# ), @@ -433,19 +475,34 @@ class ChatView extends StatelessWidget { // ), // ), Positioned( - left: 7, - bottom: 50 + - (controller.chatFooterKey.currentState?.height ?? 50), - child: StartIGCButton(controller: controller), + bottom: 0, + left: 0, + right: 0, + child: ChatEmojiPicker(controller), ), - Positioned( - right: 7, - bottom: 50 + - (controller.chatFooterKey.currentState?.height ?? 50), - child: ChatFloatingActionButton( - controller: controller, + // Close button placed at bottom of emoji picker + if (controller.showEmojiPicker) + Positioned( + left: 0, + right: 0, + bottom: 5, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FloatingActionButton( + onPressed: controller.closeEmojiPicker, + backgroundColor: Colors.black, + shape: const CircleBorder(), + heroTag: null, + mini: true, + child: const Icon( + Icons.close, + size: 20, + ), + ), + ], + ), ), - ), // Pangea# ], ), diff --git a/lib/pangea/widgets/chat/chat_footer.dart b/lib/pangea/widgets/chat/chat_footer.dart deleted file mode 100644 index f47ebf193..000000000 --- a/lib/pangea/widgets/chat/chat_footer.dart +++ /dev/null @@ -1,59 +0,0 @@ -import 'package:fluffychat/pages/chat/chat.dart'; -import 'package:fluffychat/pages/chat/chat_emoji_picker.dart'; -import 'package:fluffychat/pages/chat/chat_input_row.dart'; -import 'package:fluffychat/pages/chat/reactions_picker.dart'; -import 'package:fluffychat/pages/chat/reply_display.dart'; -import 'package:fluffychat/pangea/choreographer/widgets/it_bar.dart'; -import 'package:fluffychat/widgets/connection_status_header.dart'; -import 'package:flutter/material.dart'; - -class ChatFooter extends StatefulWidget { - final ChatController controller; - const ChatFooter( - this.controller, { - super.key, - }); - - @override - ChatFooterState createState() => ChatFooterState(); -} - -class ChatFooterState extends State { - double? height; - - @override - void initState() { - super.initState(); - WidgetsBinding.instance.addPostFrameCallback((_) { - setState(() { - height = context.size!.height; - }); - }); - } - - @override - Widget build(BuildContext context) { - return NotificationListener( - onNotification: (_) { - if (height != context.size!.height) { - height = context.size!.height; - widget.controller.updateView(); - } - return true; - }, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - const ConnectionStatusHeader(), - ITBar( - choreographer: widget.controller.choreographer, - ), - ReactionsPicker(widget.controller), - ReplyDisplay(widget.controller), - ChatInputRow(widget.controller), - ChatEmojiPicker(widget.controller), - ], - ), - ); - } -} From c20176d02953f143f010857e7aa827deabc3f628 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Mon, 22 Jul 2024 13:45:45 -0400 Subject: [PATCH 07/32] Add #pangea comments --- lib/pages/chat/chat.dart | 2 -- lib/pages/chat/chat_view.dart | 33 +++++++++++++++++---------------- 2 files changed, 17 insertions(+), 18 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index bd0462401..ea14cc600 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -113,8 +113,6 @@ class ChatController extends State // #Pangea final PangeaController pangeaController = MatrixState.pangeaController; - // final GlobalKey chatFooterKey = GlobalKey(); - late Choreographer choreographer = Choreographer(pangeaController, this); // Pangea# Room get room => sendingClient.getRoomById(roomId) ?? widget.room; diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 783ccbb6a..0b03441d1 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -301,8 +301,11 @@ class ChatView extends StatelessWidget { child: Column( children: [ Expanded( - child: Stack( + child: + // #Pangea + Stack( children: [ + // Pangea# GestureDetector( onTap: controller.clearSingleSelectedEvent, child: Builder( @@ -321,6 +324,7 @@ class ChatView extends StatelessWidget { }, ), ), + // #Pangea Positioned( left: 0, right: 0, @@ -346,12 +350,12 @@ class ChatView extends StatelessWidget { ITBar( choreographer: controller.choreographer, ), - // if (!controller.selectMode) ReplyDisplay(controller), ], ), ), ], + // Pangea# ), ), if (controller.room.canSendDefaultMessages && @@ -436,22 +440,19 @@ class ChatView extends StatelessWidget { ), ], ) - // #Pangea - // : Column( - // mainAxisSize: MainAxisSize.min, - // children: [ - // const ConnectionStatusHeader(), - // ITBar( - // choreographer: - // controller.choreographer, - // ), - // ReactionsPicker(controller), - // ReplyDisplay(controller), - // ChatInputRow(controller), - // ChatEmojiPicker(controller), - // ], : Column( + mainAxisSize: MainAxisSize.min, children: [ + // #Pangea + // const ConnectionStatusHeader(), + // ITBar( + // choreographer: + // controller.choreographer, + // ), + // ReactionsPicker(controller), + // ReplyDisplay(controller), + // ChatInputRow(controller), + // ChatEmojiPicker(controller), ReactionsPicker(controller), ChatInputRow(controller), ], From 5b8c2dc7a848ff71db285183ef1d5207b1494b66 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Mon, 22 Jul 2024 16:36:44 -0400 Subject: [PATCH 08/32] Minor emoji picker edits --- lib/pages/chat/chat.dart | 7 -- lib/pages/chat/chat_emoji_picker.dart | 152 ++++++++++++++--------- lib/pages/chat/chat_view.dart | 34 ++--- lib/pages/chat/events/reply_content.dart | 3 +- lib/pages/chat/reply_display.dart | 63 +++++----- 5 files changed, 140 insertions(+), 119 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index ea14cc600..f978330c1 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -378,13 +378,6 @@ class ChatController extends State setState(() {}); } - // #Pangea - void closeEmojiPicker() { - showEmojiPicker = false; - updateView(); - } - // Pangea# - Future? loadTimelineFuture; int? animateInEventIndex; diff --git a/lib/pages/chat/chat_emoji_picker.dart b/lib/pages/chat/chat_emoji_picker.dart index 3f424ab37..a59bba609 100644 --- a/lib/pages/chat/chat_emoji_picker.dart +++ b/lib/pages/chat/chat_emoji_picker.dart @@ -14,6 +14,9 @@ class ChatEmojiPicker extends StatelessWidget { @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); + // #Pangea + final bool lightMode = Theme.of(context).brightness == Brightness.light; + // Pangea# return AnimatedContainer( duration: FluffyThemes.animationDuration, curve: FluffyThemes.animationCurve, @@ -23,68 +26,103 @@ class ChatEmojiPicker extends StatelessWidget { ? MediaQuery.of(context).size.height / 2 : 0, child: controller.showEmojiPicker - ? DefaultTabController( - length: 2, - child: Column( - children: [ - TabBar( - tabs: [ - Tab(text: L10n.of(context)!.emojis), - Tab(text: L10n.of(context)!.stickers), - ], - ), - Expanded( - child: TabBarView( - children: [ - EmojiPicker( - onEmojiSelected: controller.onEmojiSelected, - onBackspacePressed: controller.emojiPickerBackspace, - config: Config( - emojiViewConfig: EmojiViewConfig( - noRecents: const NoRecent(), - backgroundColor: Theme.of(context) - .colorScheme - .onInverseSurface, - ), - bottomActionBarConfig: const BottomActionBarConfig( - enabled: false, + ? + // #Pangea + Stack( + children: [ + // Pangea# + DefaultTabController( + length: 2, + child: Column( + children: [ + TabBar( + tabs: [ + Tab(text: L10n.of(context)!.emojis), + Tab(text: L10n.of(context)!.stickers), + ], + ), + Expanded( + child: TabBarView( + children: [ + EmojiPicker( + onEmojiSelected: controller.onEmojiSelected, + onBackspacePressed: + controller.emojiPickerBackspace, + config: Config( + emojiViewConfig: EmojiViewConfig( + noRecents: const NoRecent(), + backgroundColor: Theme.of(context) + .colorScheme + .onInverseSurface, + ), + bottomActionBarConfig: + const BottomActionBarConfig( + enabled: false, + ), + categoryViewConfig: CategoryViewConfig( + backspaceColor: theme.colorScheme.primary, + iconColor: theme.colorScheme.primary + .withOpacity(0.5), + iconColorSelected: theme.colorScheme.primary, + indicatorColor: theme.colorScheme.primary, + ), + skinToneConfig: SkinToneConfig( + dialogBackgroundColor: Color.lerp( + theme.colorScheme.surface, + theme.colorScheme.primaryContainer, + 0.75, + )!, + indicatorColor: theme.colorScheme.onSurface, + ), + ), ), - categoryViewConfig: CategoryViewConfig( - backspaceColor: theme.colorScheme.primary, - iconColor: - theme.colorScheme.primary.withOpacity(0.5), - iconColorSelected: theme.colorScheme.primary, - indicatorColor: theme.colorScheme.primary, - ), - skinToneConfig: SkinToneConfig( - dialogBackgroundColor: Color.lerp( - theme.colorScheme.surface, - theme.colorScheme.primaryContainer, - 0.75, - )!, - indicatorColor: theme.colorScheme.onSurface, + StickerPickerDialog( + room: controller.room, + onSelected: (sticker) { + controller.room.sendEvent( + { + 'body': sticker.body, + 'info': sticker.info ?? {}, + 'url': sticker.url.toString(), + }, + type: EventTypes.Sticker, + ); + controller.hideEmojiPicker(); + }, ), - ), + ], ), - StickerPickerDialog( - room: controller.room, - onSelected: (sticker) { - controller.room.sendEvent( - { - 'body': sticker.body, - 'info': sticker.info ?? {}, - 'url': sticker.url.toString(), - }, - type: EventTypes.Sticker, - ); - controller.hideEmojiPicker(); - }, + ), + ], + ), + ), + // #Pangea + // Close button placed at bottom of emoji picker + Positioned( + left: 0, + right: 0, + bottom: 5, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FloatingActionButton( + onPressed: controller.hideEmojiPicker, + backgroundColor: lightMode + ? const Color.fromARGB(255, 211, 211, 211) + : Colors.black, + shape: const CircleBorder(), + heroTag: null, + mini: true, + child: const Icon( + Icons.close, + size: 20, ), - ], - ), + ), + ], ), - ], - ), + ), + // Pangea# + ], ) : null, ); diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 0b03441d1..64d70b60e 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -350,7 +350,16 @@ class ChatView extends StatelessWidget { ITBar( choreographer: controller.choreographer, ), - ReplyDisplay(controller), + Row( + // crossAxisAlignment: CrossAxisAlignment.end, + crossAxisAlignment: + CrossAxisAlignment.center, + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + ReplyDisplay(controller), + ], + ), ], ), ), @@ -481,29 +490,6 @@ class ChatView extends StatelessWidget { right: 0, child: ChatEmojiPicker(controller), ), - // Close button placed at bottom of emoji picker - if (controller.showEmojiPicker) - Positioned( - left: 0, - right: 0, - bottom: 5, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FloatingActionButton( - onPressed: controller.closeEmojiPicker, - backgroundColor: Colors.black, - shape: const CircleBorder(), - heroTag: null, - mini: true, - child: const Icon( - Icons.close, - size: 20, - ), - ), - ], - ), - ), // Pangea# ], ), diff --git a/lib/pages/chat/events/reply_content.dart b/lib/pages/chat/events/reply_content.dart index 945ae22ac..b48f16cd9 100644 --- a/lib/pages/chat/events/reply_content.dart +++ b/lib/pages/chat/events/reply_content.dart @@ -1,9 +1,8 @@ +import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:flutter/material.dart'; - import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; -import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import '../../../config/app_config.dart'; class ReplyContent extends StatelessWidget { diff --git a/lib/pages/chat/reply_display.dart b/lib/pages/chat/reply_display.dart index b2b7ca0bb..a6bec6bbb 100644 --- a/lib/pages/chat/reply_display.dart +++ b/lib/pages/chat/reply_display.dart @@ -13,35 +13,40 @@ class ReplyDisplay extends StatelessWidget { @override Widget build(BuildContext context) { - return AnimatedContainer( - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - height: controller.editEvent != null || controller.replyEvent != null - ? 56 - : 0, - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.onInverseSurface, - ), - child: Row( - children: [ - IconButton( - tooltip: L10n.of(context)!.close, - icon: const Icon(Icons.close), - onPressed: controller.cancelReplyEventAction, - ), - Expanded( - child: controller.replyEvent != null - ? ReplyContent( - controller.replyEvent!, - timeline: controller.timeline!, - backgroundColor: Colors.transparent, - ) - : _EditContent( - controller.editEvent?.getDisplayEvent(controller.timeline!), - ), - ), - ], + return Padding( + padding: const EdgeInsets.all(8.0), + child: AnimatedContainer( + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + height: controller.editEvent != null || controller.replyEvent != null + ? 56 + : 0, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + borderRadius: const BorderRadius.all(Radius.circular(28.0)), + color: Theme.of(context).colorScheme.onInverseSurface, + ), + child: Row( + children: [ + IconButton( + tooltip: L10n.of(context)!.close, + icon: const Icon(Icons.close), + onPressed: controller.cancelReplyEventAction, + ), + Expanded( + child: controller.replyEvent != null + ? ReplyContent( + controller.replyEvent!, + timeline: controller.timeline!, + backgroundColor: Colors.transparent, + ) + : _EditContent( + controller.editEvent + ?.getDisplayEvent(controller.timeline!), + ), + ), + ], + ), ), ); } From 842dd23c4bad4d14229a16bf88d5fefdd2a42532 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Mon, 22 Jul 2024 16:44:05 -0400 Subject: [PATCH 09/32] Remove useless code --- lib/pages/chat/chat_view.dart | 11 +----- lib/pages/chat/reply_display.dart | 66 +++++++++++++++---------------- 2 files changed, 33 insertions(+), 44 deletions(-) diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 64d70b60e..e99e6a389 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -350,16 +350,7 @@ class ChatView extends StatelessWidget { ITBar( choreographer: controller.choreographer, ), - Row( - // crossAxisAlignment: CrossAxisAlignment.end, - crossAxisAlignment: - CrossAxisAlignment.center, - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - ReplyDisplay(controller), - ], - ), + ReplyDisplay(controller), ], ), ), diff --git a/lib/pages/chat/reply_display.dart b/lib/pages/chat/reply_display.dart index a6bec6bbb..f05c749a2 100644 --- a/lib/pages/chat/reply_display.dart +++ b/lib/pages/chat/reply_display.dart @@ -13,40 +13,38 @@ class ReplyDisplay extends StatelessWidget { @override Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: AnimatedContainer( - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - height: controller.editEvent != null || controller.replyEvent != null - ? 56 - : 0, - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(28.0)), - color: Theme.of(context).colorScheme.onInverseSurface, - ), - child: Row( - children: [ - IconButton( - tooltip: L10n.of(context)!.close, - icon: const Icon(Icons.close), - onPressed: controller.cancelReplyEventAction, - ), - Expanded( - child: controller.replyEvent != null - ? ReplyContent( - controller.replyEvent!, - timeline: controller.timeline!, - backgroundColor: Colors.transparent, - ) - : _EditContent( - controller.editEvent - ?.getDisplayEvent(controller.timeline!), - ), - ), - ], - ), + return AnimatedContainer( + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + height: controller.editEvent != null || controller.replyEvent != null + ? 56 + : 0, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + // #Pangea + borderRadius: const BorderRadius.all(Radius.circular(28.0)), + // Pangea# + color: Theme.of(context).colorScheme.onInverseSurface, + ), + child: Row( + children: [ + IconButton( + tooltip: L10n.of(context)!.close, + icon: const Icon(Icons.close), + onPressed: controller.cancelReplyEventAction, + ), + Expanded( + child: controller.replyEvent != null + ? ReplyContent( + controller.replyEvent!, + timeline: controller.timeline!, + backgroundColor: Colors.transparent, + ) + : _EditContent( + controller.editEvent?.getDisplayEvent(controller.timeline!), + ), + ), + ], ), ); } From d640bce7a91412bbe68f3b78cf97a5ec281e097c Mon Sep 17 00:00:00 2001 From: Kelrap Date: Tue, 23 Jul 2024 09:11:28 -0400 Subject: [PATCH 10/32] Keep reply bar same length/style as input --- lib/pages/chat/chat_view.dart | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index e99e6a389..b032caaf7 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -346,11 +346,37 @@ class ChatView extends StatelessWidget { ), ], ), - const ConnectionStatusHeader(), - ITBar( - choreographer: controller.choreographer, + Container( + margin: EdgeInsets.only( + bottom: bottomSheetPadding, + left: bottomSheetPadding, + right: bottomSheetPadding, + ), + constraints: const BoxConstraints( + maxWidth: + FluffyThemes.columnWidth * 2.5, + ), + alignment: Alignment.center, + child: Material( + clipBehavior: Clip.hardEdge, + color: Theme.of(context) + .colorScheme + .surfaceContainerHighest, + borderRadius: const BorderRadius.all( + Radius.circular(24), + ), + child: Column( + children: [ + const ConnectionStatusHeader(), + ITBar( + choreographer: + controller.choreographer, + ), + ReplyDisplay(controller), + ], + ), + ), ), - ReplyDisplay(controller), ], ), ), From f2e112789d33f479c7226d23173e9ee8e290f71f Mon Sep 17 00:00:00 2001 From: Kelrap Date: Tue, 23 Jul 2024 10:17:40 -0400 Subject: [PATCH 11/32] Permissions default to yes/student choice --- .../controllers/permissions_controller.dart | 105 ++++++++++-------- 1 file changed, 59 insertions(+), 46 deletions(-) diff --git a/lib/pangea/controllers/permissions_controller.dart b/lib/pangea/controllers/permissions_controller.dart index ed84e13c3..175e048f3 100644 --- a/lib/pangea/controllers/permissions_controller.dart +++ b/lib/pangea/controllers/permissions_controller.dart @@ -1,5 +1,4 @@ import 'package:fluffychat/pangea/constants/age_limits.dart'; -import 'package:fluffychat/pangea/constants/pangea_event_types.dart'; import 'package:fluffychat/pangea/controllers/base_controller.dart'; import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; @@ -36,63 +35,73 @@ class PermissionsController extends BaseController { return dob?.isAtLeastYearsOld(AgeLimits.toAccessFeatures) ?? false; } - /// A user can private chat if - /// 1) they are 18 and outside a class context or - /// 2) they are in a class context and the class rules permit it - /// If no class is passed, uses classController.activeClass + /// A user can private chat if they are 18+ bool canUserPrivateChat({String? roomID}) { - final Room? classContext = - firstRoomWithState(roomID: roomID, type: PangeaEventTypes.rules); - return classContext?.pangeaRoomRules == null - ? isUser18() - : classContext!.pangeaRoomRules!.oneToOneChatClass || - classContext.isRoomAdmin; + return isUser18(); + // Rules can't be edited; default to true + // final Room? classContext = + // firstRoomWithState(roomID: roomID, type: PangeaEventTypes.rules); + // return classContext?.pangeaRoomRules == null + // ? isUser18() + // : classContext!.pangeaRoomRules!.oneToOneChatClass || + // classContext.isRoomAdmin; } bool canUserGroupChat({String? roomID}) { - final Room? classContext = - firstRoomWithState(roomID: roomID, type: PangeaEventTypes.rules); - - return classContext?.pangeaRoomRules == null - ? isUser18() - : classContext!.pangeaRoomRules!.isCreateRooms || - classContext.isRoomAdmin; + return isUser18(); + // Rules can't be edited; default to true + // final Room? classContext = + // firstRoomWithState(roomID: roomID, type: PangeaEventTypes.rules); + + // return classContext?.pangeaRoomRules == null + // ? isUser18() + // : classContext!.pangeaRoomRules!.isCreateRooms || + // classContext.isRoomAdmin; } bool showChatInputAddButton(String roomId) { final PangeaRoomRules? perms = _getRoomRules(roomId); if (perms == null) return isUser18(); - return perms.isShareFiles || - perms.isShareLocation || - perms.isSharePhoto || - perms.isShareVideo; + // Rules can't be edited; default to true + // return perms.isShareFiles || + // perms.isShareLocation || + // perms.isSharePhoto || + // perms.isShareVideo; + return true; } /// works for both roomID of chat and class - bool canShareVideo(String? roomID) => - _getRoomRules(roomID)?.isShareVideo ?? isUser18(); + bool canShareVideo(String? roomID) => isUser18(); + // Rules can't be edited; default to true + // _getRoomRules(roomID)?.isShareVideo ?? isUser18(); /// works for both roomID of chat and class - bool canSharePhoto(String? roomID) => - _getRoomRules(roomID)?.isSharePhoto ?? isUser18(); + bool canSharePhoto(String? roomID) => isUser18(); + // Rules can't be edited; default to true + // _getRoomRules(roomID)?.isSharePhoto ?? isUser18(); /// works for both roomID of chat and class - bool canShareFile(String? roomID) => - _getRoomRules(roomID)?.isShareFiles ?? isUser18(); + bool canShareFile(String? roomID) => isUser18(); + // Rules can't be edited; default to true + // _getRoomRules(roomID)?.isShareFiles ?? isUser18(); /// works for both roomID of chat and class - bool canShareLocation(String? roomID) => - _getRoomRules(roomID)?.isShareLocation ?? isUser18(); + bool canShareLocation(String? roomID) => isUser18(); + // Rules can't be edited; default to true + // _getRoomRules(roomID)?.isShareLocation ?? isUser18(); - int? classLanguageToolPermission(Room room, ToolSetting setting) => - room.firstRules?.getToolSettings(setting); + int? classLanguageToolPermission(Room room, ToolSetting setting) => 1; + // Rules can't be edited; default to student choice + // room.firstRules?.getToolSettings(setting); - //what happens if a room isn't in a class? + // what happens if a room isn't in a class? bool isToolDisabledByClass(ToolSetting setting, Room? room) { - if (room?.isSpaceAdmin ?? false) return false; - final int? classPermission = - room != null ? classLanguageToolPermission(room, setting) : 1; - return classPermission == 0; + return false; + // Rules can't be edited; default to false + // if (room?.isSpaceAdmin ?? false) return false; + // final int? classPermission = + // room != null ? classLanguageToolPermission(room, setting) : 1; + // return classPermission == 0; } bool userToolSetting(ToolSetting setting) { @@ -117,18 +126,22 @@ class PermissionsController extends BaseController { } bool isToolEnabled(ToolSetting setting, Room? room) { - if (room?.isSpaceAdmin ?? false) { - return userToolSetting(setting); - } - final int? classPermission = - room != null ? classLanguageToolPermission(room, setting) : 1; - if (classPermission == 0) return false; - if (classPermission == 2) return true; + // Rules can't be edited; default to true return userToolSetting(setting); + // if (room?.isSpaceAdmin ?? false) { + // return userToolSetting(setting); + // } + // final int? classPermission = + // room != null ? classLanguageToolPermission(room, setting) : 1; + // if (classPermission == 0) return false; + // if (classPermission == 2) return true; + // return userToolSetting(setting); } bool isWritingAssistanceEnabled(Room? room) { - return isToolEnabled(ToolSetting.interactiveTranslator, room) && - isToolEnabled(ToolSetting.interactiveGrammar, room); + // Rules can't be edited; default to true + return true; + // return isToolEnabled(ToolSetting.interactiveTranslator, room) && + // isToolEnabled(ToolSetting.interactiveGrammar, room); } } From 45c290d0d83ed5042db43cd6b26f14bea112cc05 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Tue, 23 Jul 2024 12:09:13 -0400 Subject: [PATCH 12/32] Keep input bar and reply bar together --- lib/pages/chat/chat_view.dart | 199 +++++++++++++++++----------------- 1 file changed, 102 insertions(+), 97 deletions(-) diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index b032caaf7..02a955591 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -14,6 +14,7 @@ import 'package:fluffychat/pangea/choreographer/widgets/start_igc_button.dart'; import 'package:fluffychat/pangea/extensions/pangea_room_extension/pangea_room_extension.dart'; import 'package:fluffychat/pangea/widgets/chat/chat_floating_action_button.dart'; import 'package:fluffychat/utils/account_config.dart'; +import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/chat_settings_popup_menu.dart'; import 'package:fluffychat/widgets/connection_status_header.dart'; import 'package:fluffychat/widgets/matrix.dart'; @@ -301,87 +302,22 @@ class ChatView extends StatelessWidget { child: Column( children: [ Expanded( - child: - // #Pangea - Stack( - children: [ - // Pangea# - GestureDetector( - onTap: controller.clearSingleSelectedEvent, - child: Builder( - builder: (context) { - if (controller.timeline == null) { - return const Center( - child: - CircularProgressIndicator.adaptive( - strokeWidth: 2, - ), - ); - } - return ChatEventList( - controller: controller, - ); - }, - ), - ), - // #Pangea - Positioned( - left: 0, - right: 0, - bottom: 7, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - if (!controller.selectMode) - Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - StartIGCButton( - controller: controller, - ), - ChatFloatingActionButton( - controller: controller, - ), - ], - ), - Container( - margin: EdgeInsets.only( - bottom: bottomSheetPadding, - left: bottomSheetPadding, - right: bottomSheetPadding, - ), - constraints: const BoxConstraints( - maxWidth: - FluffyThemes.columnWidth * 2.5, - ), - alignment: Alignment.center, - child: Material( - clipBehavior: Clip.hardEdge, - color: Theme.of(context) - .colorScheme - .surfaceContainerHighest, - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), - child: Column( - children: [ - const ConnectionStatusHeader(), - ITBar( - choreographer: - controller.choreographer, - ), - ReplyDisplay(controller), - ], - ), - ), + child: GestureDetector( + onTap: controller.clearSingleSelectedEvent, + child: Builder( + builder: (context) { + if (controller.timeline == null) { + return const Center( + child: CircularProgressIndicator.adaptive( + strokeWidth: 2, ), - ], - ), - ), - ], - // Pangea# + ); + } + return ChatEventList( + controller: controller, + ); + }, + ), ), ), if (controller.room.canSendDefaultMessages && @@ -466,26 +402,31 @@ class ChatView extends StatelessWidget { ), ], ) - : Column( - mainAxisSize: MainAxisSize.min, - children: [ - // #Pangea - // const ConnectionStatusHeader(), - // ITBar( - // choreographer: - // controller.choreographer, - // ), - // ReactionsPicker(controller), - // ReplyDisplay(controller), - // ChatInputRow(controller), - // ChatEmojiPicker(controller), - ReactionsPicker(controller), - ChatInputRow(controller), - ], - ), + : + // #Pangea + null, + // Column( + // mainAxisSize: MainAxisSize.min, + // children: [ + // const ConnectionStatusHeader(), + // ITBar( + // choreographer: + // controller.choreographer, + // ), + // ReactionsPicker(controller), + // ReplyDisplay(controller), + // ChatInputRow(controller), + // ChatEmojiPicker(controller), + // ], + // ), // Pangea# ), ), + // #Pangea + SizedBox( + height: (PlatformInfos.isMobile ? 15 : 50), + ), + // Pangea# ], ), ), @@ -501,6 +442,70 @@ class ChatView extends StatelessWidget { // size: 100, // ), // ), + Positioned( + left: 0, + right: 0, + bottom: 7, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (!controller.selectMode) + Container( + margin: const EdgeInsets.only( + bottom: 7, + left: 11, + right: 11, + ), + constraints: const BoxConstraints( + maxWidth: FluffyThemes.columnWidth * 2.4, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + StartIGCButton( + controller: controller, + ), + ChatFloatingActionButton( + controller: controller, + ), + ], + ), + ), + Container( + margin: EdgeInsets.only( + bottom: bottomSheetPadding, + left: bottomSheetPadding, + right: bottomSheetPadding, + ), + constraints: const BoxConstraints( + maxWidth: FluffyThemes.columnWidth * 2.5, + ), + alignment: Alignment.center, + child: Material( + clipBehavior: Clip.hardEdge, + color: Theme.of(context) + .colorScheme + .surfaceContainerHighest, + borderRadius: const BorderRadius.all( + Radius.circular(24), + ), + child: Column( + children: [ + const ConnectionStatusHeader(), + ITBar( + choreographer: controller.choreographer, + ), + ReactionsPicker(controller), + ReplyDisplay(controller), + ChatInputRow(controller), + ], + ), + ), + ), + ], + ), + ), Positioned( bottom: 0, left: 0, From 8c1df0eb88c85c663e0e0f4f28c015b297ff4533 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Tue, 23 Jul 2024 12:35:53 -0400 Subject: [PATCH 13/32] Move calculations to state --- lib/pages/chat/input_bar.dart | 13 ++++--------- lib/pangea/widgets/chat/input_bar_wrapper.dart | 13 +++++++++---- lib/pangea/widgets/igc/pangea_text_controller.dart | 5 +++++ 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/pages/chat/input_bar.dart b/lib/pages/chat/input_bar.dart index 4ffde1f42..54dba8674 100644 --- a/lib/pages/chat/input_bar.dart +++ b/lib/pages/chat/input_bar.dart @@ -21,7 +21,6 @@ class InputBar extends StatelessWidget { final ValueChanged? onSubmitImage; final FocusNode? focusNode; // #Pangea - final Function? updateBar; // final TextEditingController? controller; final PangeaTextController? controller; // Pangea# @@ -39,7 +38,6 @@ class InputBar extends StatelessWidget { this.onSubmitImage, this.focusNode, this.controller, - this.updateBar, this.decoration, this.onChanged, this.autofocus, @@ -404,7 +402,7 @@ class InputBar extends StatelessWidget { Widget build(BuildContext context) { final useShortCuts = (AppConfig.sendOnEnter ?? !PlatformInfos.isMobile); // #Pangea - final bool maxLength = controller?.text.length == 1000; + controller?.currentlyMaxLength = controller?.isMaxLength ?? false; // Pangea# return Shortcuts( shortcuts: !useShortCuts @@ -509,7 +507,9 @@ class InputBar extends StatelessWidget { onSubmitted!(text); }, // #Pangea - style: maxLength ? const TextStyle(color: Colors.red) : null, + style: controller?.isMaxLength ?? false + ? const TextStyle(color: Colors.red) + : null, onTap: () { controller!.onInputTap( context, @@ -522,11 +522,6 @@ class InputBar extends StatelessWidget { // fix for the library for now // it sets the types for the callback incorrectly onChanged!(text); - // #Pangea - if (maxLength != (controller?.text.length == 1000)) { - updateBar!(); - } - // Pangea# }, textCapitalization: TextCapitalization.sentences, ), diff --git a/lib/pangea/widgets/chat/input_bar_wrapper.dart b/lib/pangea/widgets/chat/input_bar_wrapper.dart index 5aa813bbe..1e8cd4727 100644 --- a/lib/pangea/widgets/chat/input_bar_wrapper.dart +++ b/lib/pangea/widgets/chat/input_bar_wrapper.dart @@ -61,8 +61,14 @@ class InputBarWrapperState extends State { super.dispose(); } - void refreshOnChange() { - setState(() {}); + void refreshOnChange(String text) { + if (widget.onChanged != null) { + widget.onChanged!(text); + } + if (widget.controller?.currentlyMaxLength != + widget.controller?.isMaxLength) { + setState(() {}); + } } @override @@ -77,8 +83,7 @@ class InputBarWrapperState extends State { focusNode: widget.focusNode, controller: widget.controller, decoration: widget.decoration, - updateBar: refreshOnChange, - onChanged: widget.onChanged, + onChanged: refreshOnChange, autofocus: widget.autofocus, textInputAction: widget.textInputAction, readOnly: widget.readOnly, diff --git a/lib/pangea/widgets/igc/pangea_text_controller.dart b/lib/pangea/widgets/igc/pangea_text_controller.dart index b91186c22..d7f24a7e2 100644 --- a/lib/pangea/widgets/igc/pangea_text_controller.dart +++ b/lib/pangea/widgets/igc/pangea_text_controller.dart @@ -25,6 +25,11 @@ class PangeaTextController extends TextEditingController { text ??= ''; this.text = text; } + + bool get isMaxLength => text.length == 1000; + + bool currentlyMaxLength = false; + bool forceKeepOpen = false; setSystemText(String text, EditType type) { From a87c296c1366298e75f2c96ba2893c2244620891 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Tue, 23 Jul 2024 12:41:39 -0400 Subject: [PATCH 14/32] Fix difference in function --- lib/pangea/controllers/permissions_controller.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pangea/controllers/permissions_controller.dart b/lib/pangea/controllers/permissions_controller.dart index 175e048f3..3a8eca775 100644 --- a/lib/pangea/controllers/permissions_controller.dart +++ b/lib/pangea/controllers/permissions_controller.dart @@ -60,14 +60,14 @@ class PermissionsController extends BaseController { } bool showChatInputAddButton(String roomId) { - final PangeaRoomRules? perms = _getRoomRules(roomId); - if (perms == null) return isUser18(); // Rules can't be edited; default to true + // final PangeaRoomRules? perms = _getRoomRules(roomId); + // if (perms == null) return isUser18(); // return perms.isShareFiles || // perms.isShareLocation || // perms.isSharePhoto || // perms.isShareVideo; - return true; + return isUser18(); } /// works for both roomID of chat and class From a7e6e7a4c9ffbbb876d954883260898f7044bce9 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Tue, 23 Jul 2024 15:16:34 -0400 Subject: [PATCH 15/32] Shorten overlay instead of toolbar --- lib/pages/chat/events/message_content.dart | 13 +- lib/pangea/widgets/chat/message_toolbar.dart | 151 ++++++++++--------- lib/pangea/widgets/chat/overlay_message.dart | 140 ++++++++--------- 3 files changed, 159 insertions(+), 145 deletions(-) diff --git a/lib/pages/chat/events/message_content.dart b/lib/pages/chat/events/message_content.dart index 01ae471f8..5468b59e8 100644 --- a/lib/pages/chat/events/message_content.dart +++ b/lib/pages/chat/events/message_content.dart @@ -289,17 +289,20 @@ class MessageContent extends StatelessWidget { // #Pangea // return Linkify( final messageTextStyle = TextStyle( + overflow: TextOverflow.ellipsis, color: textColor, fontSize: bigEmotes ? fontSize * 3 : fontSize, decoration: event.redacted ? TextDecoration.lineThrough : null, height: 1.3, ); if (immersionMode && pangeaMessageEvent != null) { - return PangeaRichText( - style: messageTextStyle, - pangeaMessageEvent: pangeaMessageEvent!, - immersionMode: immersionMode, - toolbarController: toolbarController, + return Flexible( + child: PangeaRichText( + style: messageTextStyle, + pangeaMessageEvent: pangeaMessageEvent!, + immersionMode: immersionMode, + toolbarController: toolbarController, + ), ); } else if (pangeaMessageEvent != null) { toolbarController?.toolbar?.textSelection.setMessageText( diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 5698b45c1..d4270fe5a 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -63,6 +63,7 @@ class ToolbarDisplayController { MessageMode? mode, }) { bool toolbarUp = true; + bool messageTooLong = false; if (highlighted) return; if (controller.selectMode) { controller.clearSelectedEvents(); @@ -82,6 +83,10 @@ class ToolbarDisplayController { messageWidth = transformTargetSize.width; final Offset targetOffset = (targetRenderBox).localToGlobal(Offset.zero); + // If the message is too long, will need to anchor by toolbar + messageTooLong = + transformTargetSize.height > MediaQuery.of(context).size.height - 320; + // If there is enough space above, procede as normal // Else if there is enough space below, show toolbar underneath if (targetOffset.dy < 320) { @@ -419,85 +424,87 @@ class MessageToolbarState extends State { @override Widget build(BuildContext context) { - return Flexible( - child: Material( - type: MaterialType.transparency, - child: Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: Theme.of(context).cardColor, - border: Border.all( - width: 2, - color: Theme.of(context).colorScheme.primary, - ), - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), + return + // Flexible( + // child: + Material( + type: MaterialType.transparency, + child: Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + border: Border.all( + width: 2, + color: Theme.of(context).colorScheme.primary, ), - constraints: const BoxConstraints( - maxWidth: 300, - minWidth: 300, - maxHeight: 300, + borderRadius: const BorderRadius.all( + Radius.circular(25), ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Flexible( - child: SingleChildScrollView( - child: AnimatedSize( - duration: FluffyThemes.animationDuration, - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: toolbarContent ?? const SizedBox(), - ), - SizedBox(height: toolbarContent == null ? 0 : 20), - ], - ), + ), + constraints: const BoxConstraints( + maxWidth: 300, + minWidth: 300, + maxHeight: 300, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: SingleChildScrollView( + child: AnimatedSize( + duration: FluffyThemes.animationDuration, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: toolbarContent ?? const SizedBox(), + ), + SizedBox(height: toolbarContent == null ? 0 : 20), + ], ), ), ), - Row( - mainAxisSize: MainAxisSize.min, - children: MessageMode.values.map((mode) { - if ([ - MessageMode.definition, - MessageMode.textToSpeech, - MessageMode.translation, - ].contains(mode) && - widget.pangeaMessageEvent.isAudioMessage) { - return const SizedBox.shrink(); - } - if (mode == MessageMode.speechToText && - !widget.pangeaMessageEvent.isAudioMessage) { - return const SizedBox.shrink(); - } - return Tooltip( - message: mode.tooltip(context), - child: IconButton( - icon: Icon(mode.icon), - color: mode.iconColor( - widget.pangeaMessageEvent, - currentMode, - context, - ), - onPressed: () => updateMode(mode), - ), - ); - }).toList() + - [ - Tooltip( - message: L10n.of(context)!.more, - child: IconButton( - icon: const Icon(Icons.add_reaction_outlined), - onPressed: showMore, + ), + Row( + mainAxisSize: MainAxisSize.min, + children: MessageMode.values.map((mode) { + if ([ + MessageMode.definition, + MessageMode.textToSpeech, + MessageMode.translation, + ].contains(mode) && + widget.pangeaMessageEvent.isAudioMessage) { + return const SizedBox.shrink(); + } + if (mode == MessageMode.speechToText && + !widget.pangeaMessageEvent.isAudioMessage) { + return const SizedBox.shrink(); + } + return Tooltip( + message: mode.tooltip(context), + child: IconButton( + icon: Icon(mode.icon), + color: mode.iconColor( + widget.pangeaMessageEvent, + currentMode, + context, ), + onPressed: () => updateMode(mode), ), - ], - ), - ], - ), + ); + }).toList() + + [ + Tooltip( + message: L10n.of(context)!.more, + child: IconButton( + icon: const Icon(Icons.add_reaction_outlined), + onPressed: showMore, + ), + ), + ], + ), + ], + // ), ), ), ); diff --git a/lib/pangea/widgets/chat/overlay_message.dart b/lib/pangea/widgets/chat/overlay_message.dart index 5f3d46c7e..31e2b10b1 100644 --- a/lib/pangea/widgets/chat/overlay_message.dart +++ b/lib/pangea/widgets/chat/overlay_message.dart @@ -118,81 +118,85 @@ class OverlayMessage extends StatelessWidget { ownMessage: ownMessage, ); - return Material( - color: noBubble ? Colors.transparent : color, - clipBehavior: Clip.antiAlias, - shape: RoundedRectangleBorder( - borderRadius: borderRadius, - ), - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), - ), - padding: noBubble || noPadding - ? EdgeInsets.zero - : const EdgeInsets.symmetric( - horizontal: 16, - vertical: 8, - ), - constraints: BoxConstraints( - maxWidth: width ?? FluffyThemes.columnWidth * 1.25, + return Flexible( + child: Material( + color: noBubble ? Colors.transparent : color, + clipBehavior: Clip.antiAlias, + shape: RoundedRectangleBorder( + borderRadius: borderRadius, ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - MessageContent( - event.getDisplayEvent(timeline), - textColor: textColor, - borderRadius: borderRadius, - selected: selected, - pangeaMessageEvent: pangeaMessageEvent, - immersionMode: immersionMode, - toolbarController: toolbarController, - isOverlay: true, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, ), - if (event.hasAggregatedEvents( - timeline, - RelationshipTypes.edit, - ) || - (pangeaMessageEvent.showUseType)) - Padding( - padding: const EdgeInsets.only( - top: 4.0, + ), + padding: noBubble || noPadding + ? EdgeInsets.zero + : const EdgeInsets.symmetric( + horizontal: 16, + vertical: 8, ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - if (pangeaMessageEvent.showUseType) ...[ - pangeaMessageEvent.msgUseType.iconView( - context, - textColor.withAlpha(164), - ), - const SizedBox(width: 4), - ], - if (event.hasAggregatedEvents( - timeline, - RelationshipTypes.edit, - )) ...[ - Icon( - Icons.edit_outlined, - color: textColor.withAlpha(164), - size: 14, - ), - Text( - ' - ${event.getDisplayEvent(timeline).originServerTs.localizedTimeShort(context)}', - style: TextStyle( + constraints: BoxConstraints( + maxWidth: width ?? FluffyThemes.columnWidth * 1.25, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Flexible( + child: MessageContent( + event.getDisplayEvent(timeline), + textColor: textColor, + borderRadius: borderRadius, + selected: selected, + pangeaMessageEvent: pangeaMessageEvent, + immersionMode: immersionMode, + toolbarController: toolbarController, + isOverlay: true, + ), + ), + if (event.hasAggregatedEvents( + timeline, + RelationshipTypes.edit, + ) || + (pangeaMessageEvent.showUseType)) + Padding( + padding: const EdgeInsets.only( + top: 4.0, + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + if (pangeaMessageEvent.showUseType) ...[ + pangeaMessageEvent.msgUseType.iconView( + context, + textColor.withAlpha(164), + ), + const SizedBox(width: 4), + ], + if (event.hasAggregatedEvents( + timeline, + RelationshipTypes.edit, + )) ...[ + Icon( + Icons.edit_outlined, color: textColor.withAlpha(164), - fontSize: 12, + size: 14, ), - ), + Text( + ' - ${event.getDisplayEvent(timeline).originServerTs.localizedTimeShort(context)}', + style: TextStyle( + color: textColor.withAlpha(164), + fontSize: 12, + ), + ), + ], ], - ], + ), ), - ), - ], + ], + ), ), ), ); From 51bf91d8e5f7301e31659a7dec1a2b77194195e0 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Tue, 23 Jul 2024 15:22:39 -0400 Subject: [PATCH 16/32] Edits I forgot to save earlier --- lib/pangea/widgets/chat/message_toolbar.dart | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index d4270fe5a..74fd4b7a6 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -63,7 +63,6 @@ class ToolbarDisplayController { MessageMode? mode, }) { bool toolbarUp = true; - bool messageTooLong = false; if (highlighted) return; if (controller.selectMode) { controller.clearSelectedEvents(); @@ -83,10 +82,6 @@ class ToolbarDisplayController { messageWidth = transformTargetSize.width; final Offset targetOffset = (targetRenderBox).localToGlobal(Offset.zero); - // If the message is too long, will need to anchor by toolbar - messageTooLong = - transformTargetSize.height > MediaQuery.of(context).size.height - 320; - // If there is enough space above, procede as normal // Else if there is enough space below, show toolbar underneath if (targetOffset.dy < 320) { @@ -424,10 +419,7 @@ class MessageToolbarState extends State { @override Widget build(BuildContext context) { - return - // Flexible( - // child: - Material( + return Material( type: MaterialType.transparency, child: Container( padding: const EdgeInsets.all(10), @@ -504,7 +496,6 @@ class MessageToolbarState extends State { ], ), ], - // ), ), ), ); From cc58771de0a0eca5998fcccda707ad6a8da1a9fa Mon Sep 17 00:00:00 2001 From: Kelrap Date: Wed, 24 Jul 2024 08:50:47 -0400 Subject: [PATCH 17/32] Silences web focus error --- lib/pages/chat/chat.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index e1af6a2c0..3ebe2dd98 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -475,10 +475,10 @@ class ChatController extends State if (kIsWeb && !Matrix.of(context).webHasFocus) return; // #Pangea } catch (err, s) { - ErrorHandler.logError( - e: PangeaWarningError("Web focus error: $err"), - s: s, - ); + // ErrorHandler.logError( + // e: PangeaWarningError("Web focus error: $err"), + // s: s, + // ); return; } // Pangea# From cfdeb40732e7398604937b03d1fbb1acf838a945 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Wed, 24 Jul 2024 08:57:19 -0400 Subject: [PATCH 18/32] Add #Pangea comments --- lib/pages/chat/chat.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 3ebe2dd98..1f32bcc64 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -475,10 +475,12 @@ class ChatController extends State if (kIsWeb && !Matrix.of(context).webHasFocus) return; // #Pangea } catch (err, s) { + // #Pangea // ErrorHandler.logError( // e: PangeaWarningError("Web focus error: $err"), // s: s, // ); + // Pangea# return; } // Pangea# From 859510be99a267b9bf7fe9ed18fcdcb5c2420ab8 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Wed, 24 Jul 2024 12:05:49 -0400 Subject: [PATCH 19/32] Prevents invite error --- .../room_analytics_extension.dart | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart b/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart index a0d6c21c9..b053fc409 100644 --- a/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart @@ -103,17 +103,19 @@ extension AnalyticsRoomExtension on Room { .where((teacher) => !participants.contains(teacher)) .toList(); - Future.wait( - uninvitedTeachers.map( - (teacher) => analyticsRoom.invite(teacher.id).catchError((err, s) { - ErrorHandler.logError( - e: err, - m: "Failed to invite teacher ${teacher.id} to analytics room ${analyticsRoom.id}", - s: s, - ); - }), - ), - ); + if (!canSendEvent(EventTypes.SpaceChild)) { + Future.wait( + uninvitedTeachers.map( + (teacher) => analyticsRoom.invite(teacher.id).catchError((err, s) { + ErrorHandler.logError( + e: err, + m: "Failed to invite teacher ${teacher.id} to analytics room ${analyticsRoom.id}", + s: s, + ); + }), + ), + ); + } } /// Invite all the user's teachers to 1 analytics room. From bed27abe510ab6add55ae029ab42f747df577bce Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 24 Jul 2024 12:59:52 -0400 Subject: [PATCH 20/32] added sentry breadcrumbs to help track down ios logout issue --- lib/pangea/utils/p_store.dart | 4 ++++ .../flutter_matrix_dart_sdk_database/builder.dart | 10 ++++++++++ .../flutter_matrix_dart_sdk_database/cipher.dart | 7 +++++++ 3 files changed, 21 insertions(+) diff --git a/lib/pangea/utils/p_store.dart b/lib/pangea/utils/p_store.dart index 0dfe0d5cd..233975fc5 100644 --- a/lib/pangea/utils/p_store.dart +++ b/lib/pangea/utils/p_store.dart @@ -1,5 +1,6 @@ import 'package:fluffychat/pangea/controllers/pangea_controller.dart'; import 'package:get_storage/get_storage.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; /// Utility to save and read data both in the matrix profile (this is the default /// behavior) and in the local storage (local needs to be specificied). An @@ -66,6 +67,9 @@ class PStore { /// Clears the storage by erasing all data in the box. void clearStorage() { + // this could potenitally be interfering with openning database + // at the start of the session, which is causing auto log outs on iOS + Sentry.addBreadcrumb(Breadcrumb(message: 'Clearing local storage')); _box.erase(); } } diff --git a/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart b/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart index 655fea198..b5665c635 100644 --- a/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart +++ b/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/builder.dart @@ -7,6 +7,7 @@ import 'package:flutter/foundation.dart'; import 'package:matrix/matrix.dart'; import 'package:path/path.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'package:universal_html/html.dart' as html; @@ -80,6 +81,9 @@ Future _constructDatabase(Client client) async { } final cipher = await getDatabaseCipher(); + // #Pangea + Sentry.addBreadcrumb(Breadcrumb(message: 'Database cipher: $cipher')); + // Pangea# Directory? fileStorageLocation; try { @@ -97,6 +101,9 @@ Future _constructDatabase(Client client) async { // import the SQLite / SQLCipher shared objects / dynamic libraries final factory = createDatabaseFactoryFfi(ffiInit: SQfLiteEncryptionHelper.ffiInit); + // #Pangea + Sentry.addBreadcrumb(Breadcrumb(message: 'Database path: $path')); + // Pangea# // migrate from potential previous SQLite database path to current one await _migrateLegacyLocation(path, client.clientName); @@ -113,6 +120,9 @@ Future _constructDatabase(Client client) async { path: path, cipher: cipher, ); + // #Pangea + Sentry.addBreadcrumb(Breadcrumb(message: 'Database cipher helper: $helper')); + // Pangea# // check whether the DB is already encrypted and otherwise do so await helper?.ensureDatabaseFileEncrypted(); diff --git a/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/cipher.dart b/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/cipher.dart index 612f74395..bfe1251dc 100644 --- a/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/cipher.dart +++ b/lib/utils/matrix_sdk_extensions/flutter_matrix_dart_sdk_database/cipher.dart @@ -5,6 +5,7 @@ import 'package:fluffychat/config/setting_keys.dart'; import 'package:flutter/services.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:matrix/matrix.dart'; +import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:shared_preferences/shared_preferences.dart'; const _passwordStorageKey = 'database_password'; @@ -58,6 +59,12 @@ void _sendNoEncryptionWarning(Object exception) async { // l10n.noDatabaseEncryption, // exception.toString(), // ); + Sentry.addBreadcrumb( + Breadcrumb( + message: 'No database encryption', + data: {'exception': exception}, + ), + ); // Pangea# await store.setBool(SettingKeys.noEncryptionWarningShown, true); From 9f6cd047aa4d75cc2313c8b1396ac4ebef00a85d Mon Sep 17 00:00:00 2001 From: ggurdin Date: Wed, 24 Jul 2024 13:25:16 -0400 Subject: [PATCH 21/32] auto reload space hierarchy view after it's invalidated in the matrix SDK --- lib/pages/chat_list/space_view.dart | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/pages/chat_list/space_view.dart b/lib/pages/chat_list/space_view.dart index d0ff8cdf5..ccc9aa9c4 100644 --- a/lib/pages/chat_list/space_view.dart +++ b/lib/pages/chat_list/space_view.dart @@ -53,6 +53,25 @@ class _SpaceViewState extends State { widget.controller.pangeaController.pStoreService.read(_chatCountsKey) ?? {}, ); + + /// Used to filter out sync updates with hierarchy updates for the active + /// space so that the view can be auto-reloaded in the room subscription + bool hasHierarchyUpdate(SyncUpdate update) { + final joinTimeline = + update.rooms?.join?[widget.controller.activeSpaceId]?.timeline; + final leaveTimeline = + update.rooms?.leave?[widget.controller.activeSpaceId]?.timeline; + if (joinTimeline == null && leaveTimeline == null) return false; + final bool hasJoinUpdate = joinTimeline?.events?.any( + (event) => event.type == EventTypes.SpaceChild, + ) ?? + false; + final bool hasLeaveUpdate = leaveTimeline?.events?.any( + (event) => event.type == EventTypes.SpaceChild, + ) ?? + false; + return hasJoinUpdate || hasLeaveUpdate; + } // Pangea# @override @@ -78,12 +97,9 @@ class _SpaceViewState extends State { // Listen for changes to the activeSpace's hierarchy, // and reload the hierarchy when they come through final client = Matrix.of(context).client; - _roomSubscription ??= client.onRoomState.stream.where((u) { - return u.state.type == EventTypes.SpaceChild && - u.roomId == widget.controller.activeSpaceId; - }).listen((update) { - loadHierarchy(hasUpdate: true); - }); + _roomSubscription ??= client.onSync.stream + .where(hasHierarchyUpdate) + .listen((update) => loadHierarchy(hasUpdate: true)); // Pangea# super.initState(); } From b510a09c9053e58e305c100928896a82757d5da3 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Wed, 24 Jul 2024 13:45:16 -0400 Subject: [PATCH 22/32] Fix logic error --- .../pangea_room_extension/room_analytics_extension.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart b/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart index b053fc409..3a2e62c86 100644 --- a/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart @@ -54,6 +54,7 @@ extension AnalyticsRoomExtension on Room { return Future.value(); } + // Checks that user has permission to add child to space if (!canSendEvent(EventTypes.SpaceChild)) return; if (spaceChildren.any((sc) => sc.roomId == analyticsRoom.id)) return; @@ -103,7 +104,7 @@ extension AnalyticsRoomExtension on Room { .where((teacher) => !participants.contains(teacher)) .toList(); - if (!canSendEvent(EventTypes.SpaceChild)) { + if (analyticsRoom.canSendEvent(EventTypes.RoomMember)) { Future.wait( uninvitedTeachers.map( (teacher) => analyticsRoom.invite(teacher.id).catchError((err, s) { From d37aae9cbf43978b6537d5fb6dcf387ecac082fc Mon Sep 17 00:00:00 2001 From: Kelrap Date: Wed, 24 Jul 2024 13:47:32 -0400 Subject: [PATCH 23/32] Delete commented code --- lib/pages/chat/chat.dart | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 1f32bcc64..c61beafd2 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -475,12 +475,6 @@ class ChatController extends State if (kIsWeb && !Matrix.of(context).webHasFocus) return; // #Pangea } catch (err, s) { - // #Pangea - // ErrorHandler.logError( - // e: PangeaWarningError("Web focus error: $err"), - // s: s, - // ); - // Pangea# return; } // Pangea# From 1886e30dd74882c3733733414f2edadc631a8c6c Mon Sep 17 00:00:00 2001 From: Kelrap Date: Wed, 24 Jul 2024 14:33:17 -0400 Subject: [PATCH 24/32] Make analytics buttons flexible --- .../analytics/analytics_view_button.dart | 58 ++++++++++--------- .../analytics/time_span_menu_button.dart | 54 ++++++++--------- 2 files changed, 59 insertions(+), 53 deletions(-) diff --git a/lib/pangea/pages/analytics/analytics_view_button.dart b/lib/pangea/pages/analytics/analytics_view_button.dart index c98bf47fc..5b4f46d03 100644 --- a/lib/pangea/pages/analytics/analytics_view_button.dart +++ b/lib/pangea/pages/analytics/analytics_view_button.dart @@ -13,36 +13,40 @@ class AnalyticsViewButton extends StatelessWidget { @override Widget build(BuildContext context) { - return PopupMenuButton( - tooltip: L10n.of(context)!.changeAnalyticsView, - initialValue: value, - onSelected: (BarChartViewSelection? view) { - if (view == null) { - debugPrint("when is view null?"); - return; - } - onChange(view); - }, - itemBuilder: (BuildContext context) => BarChartViewSelection.values - .map>( - (BarChartViewSelection view) { - return PopupMenuItem( - value: view, - child: Text(view.string(context)), - ); - }).toList(), - child: TextButton.icon( - label: Text( - value.string(context), - style: TextStyle( + return Flexible( + child: PopupMenuButton( + tooltip: L10n.of(context)!.changeAnalyticsView, + initialValue: value, + onSelected: (BarChartViewSelection? view) { + if (view == null) { + debugPrint("when is view null?"); + return; + } + onChange(view); + }, + itemBuilder: (BuildContext context) => BarChartViewSelection.values + .map>( + (BarChartViewSelection view) { + return PopupMenuItem( + value: view, + child: Text( + view.string(context), + ), + ); + }).toList(), + child: TextButton.icon( + label: Text( + value.string(context), + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + ), + ), + icon: Icon( + value.icon, color: Theme.of(context).colorScheme.onSurface, ), + onPressed: null, ), - icon: Icon( - value.icon, - color: Theme.of(context).colorScheme.onSurface, - ), - onPressed: null, ), ); } diff --git a/lib/pangea/pages/analytics/time_span_menu_button.dart b/lib/pangea/pages/analytics/time_span_menu_button.dart index 32f6668bc..eae5416b0 100644 --- a/lib/pangea/pages/analytics/time_span_menu_button.dart +++ b/lib/pangea/pages/analytics/time_span_menu_button.dart @@ -14,35 +14,37 @@ class TimeSpanMenuButton extends StatelessWidget { @override Widget build(BuildContext context) { - return PopupMenuButton( - tooltip: L10n.of(context)!.changeDateRange, - initialValue: value, - onSelected: (TimeSpan? timeSpan) { - if (timeSpan == null) { - debugPrint("when is timeSpan null?"); - return; - } - onChange(timeSpan); - }, - itemBuilder: (BuildContext context) => - TimeSpan.values.map>((TimeSpan timeSpan) { - return PopupMenuItem( - value: timeSpan, - child: Text(timeSpan.string(context)), - ); - }).toList(), - child: TextButton.icon( - label: Text( - value.string(context), - style: TextStyle( + return Flexible( + child: PopupMenuButton( + tooltip: L10n.of(context)!.changeDateRange, + initialValue: value, + onSelected: (TimeSpan? timeSpan) { + if (timeSpan == null) { + debugPrint("when is timeSpan null?"); + return; + } + onChange(timeSpan); + }, + itemBuilder: (BuildContext context) => + TimeSpan.values.map>((TimeSpan timeSpan) { + return PopupMenuItem( + value: timeSpan, + child: Text(timeSpan.string(context)), + ); + }).toList(), + child: TextButton.icon( + label: Text( + value.string(context), + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + ), + ), + icon: Icon( + Icons.calendar_month_outlined, color: Theme.of(context).colorScheme.onSurface, ), + onPressed: null, ), - icon: Icon( - Icons.calendar_month_outlined, - color: Theme.of(context).colorScheme.onSurface, - ), - onPressed: null, ), ); } From 233486449ae59b2207ee3d9d68659cbb0c002445 Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Wed, 24 Jul 2024 15:24:46 -0400 Subject: [PATCH 25/32] point values for construct uses --- lib/pangea/enum/construct_use_type_enum.dart | 48 ++++++++++++++++++-- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/lib/pangea/enum/construct_use_type_enum.dart b/lib/pangea/enum/construct_use_type_enum.dart index 0e3c52bbb..c42f7f4a9 100644 --- a/lib/pangea/enum/construct_use_type_enum.dart +++ b/lib/pangea/enum/construct_use_type_enum.dart @@ -32,6 +32,9 @@ enum ConstructUseTypeEnum { /// selected correctly in practice activity flow corPA, + /// encountered as distractor in practice activity flow and correctly ignored it + ignPA, + /// was target construct in practice activity but user did not select correctly incPA, } @@ -61,6 +64,8 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum { return 'corPA'; case ConstructUseTypeEnum.incPA: return 'incPA'; + case ConstructUseTypeEnum.ignPA: + return 'ignPA'; } } @@ -71,11 +76,11 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum { case ConstructUseTypeEnum.wa: return Icons.thumb_up_sharp; case ConstructUseTypeEnum.corIt: - return Icons.check; + return Icons.translate; case ConstructUseTypeEnum.incIt: - return Icons.close; + return Icons.translate; case ConstructUseTypeEnum.ignIt: - return Icons.close; + return Icons.translate; case ConstructUseTypeEnum.ignIGC: return Icons.close; case ConstructUseTypeEnum.corIGC: @@ -86,8 +91,45 @@ extension ConstructUseTypeExtension on ConstructUseTypeEnum { return Icons.check; case ConstructUseTypeEnum.incPA: return Icons.close; + case ConstructUseTypeEnum.ignPA: + return Icons.close; case ConstructUseTypeEnum.unk: return Icons.help; } } + + /// Returns the point value for the construct use type + /// This is used to calculate the both the total points for a user and per construct + /// Users get slightly negative points for incorrect uses to encourage them to be more careful + /// They get the most points for direct uses without help. + /// They get a small amount of points for correct uses in interactions. + /// Practice activities get a moderate amount of points. + int get pointValue { + switch (this) { + case ConstructUseTypeEnum.ga: + return 2; + case ConstructUseTypeEnum.wa: + return 3; + case ConstructUseTypeEnum.corIt: + return 1; + case ConstructUseTypeEnum.incIt: + return -1; + case ConstructUseTypeEnum.ignIt: + return 1; + case ConstructUseTypeEnum.ignIGC: + return 1; + case ConstructUseTypeEnum.corIGC: + return 2; + case ConstructUseTypeEnum.incIGC: + return -1; + case ConstructUseTypeEnum.unk: + return 0; + case ConstructUseTypeEnum.corPA: + return 2; + case ConstructUseTypeEnum.incPA: + return -1; + case ConstructUseTypeEnum.ignPA: + return 1; + } + } } From 343dcf5374cf640baa0b1f1d466ea230289faf7f Mon Sep 17 00:00:00 2001 From: William Jordan-Cooley Date: Wed, 24 Jul 2024 15:33:58 -0400 Subject: [PATCH 26/32] added missing case --- lib/pangea/models/headwords.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pangea/models/headwords.dart b/lib/pangea/models/headwords.dart index a55eeb188..2960d7b1d 100644 --- a/lib/pangea/models/headwords.dart +++ b/lib/pangea/models/headwords.dart @@ -187,6 +187,8 @@ class VocabTotals { break; case ConstructUseTypeEnum.unk: break; + case ConstructUseTypeEnum.ignPA: + break; } } } From 7fdf9a5c929399877cb3b1f66d8b9045afa7d461 Mon Sep 17 00:00:00 2001 From: Kelrap Date: Wed, 24 Jul 2024 16:48:32 -0400 Subject: [PATCH 27/32] Close keyboard when open toolbar --- lib/pages/chat/chat_view.dart | 5 +- lib/pangea/widgets/chat/message_toolbar.dart | 166 +++++++++---------- 2 files changed, 82 insertions(+), 89 deletions(-) diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 02a955591..25e990c32 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -423,8 +423,9 @@ class ChatView extends StatelessWidget { ), ), // #Pangea + // Keep messages above minimum input bar height SizedBox( - height: (PlatformInfos.isMobile ? 15 : 50), + height: (PlatformInfos.isMobile ? 24 : 50), ), // Pangea# ], @@ -445,7 +446,7 @@ class ChatView extends StatelessWidget { Positioned( left: 0, right: 0, - bottom: 7, + bottom: 13, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 5698b45c1..a638640e7 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -58,10 +58,9 @@ class ToolbarDisplayController { ); } - void showToolbar( - BuildContext context, { - MessageMode? mode, - }) { + void showToolbar(BuildContext context, {MessageMode? mode}) { + // Close keyboard, if open + FocusManager.instance.primaryFocus?.unfocus(); bool toolbarUp = true; if (highlighted) return; if (controller.selectMode) { @@ -87,12 +86,13 @@ class ToolbarDisplayController { if (targetOffset.dy < 320) { final spaceBeneath = MediaQuery.of(context).size.height - (targetOffset.dy + transformTargetSize.height); - if (spaceBeneath >= 320) { - toolbarUp = false; - } + // If toolbar is open, opening toolbar beneath without scrolling can cause issues + // if (spaceBeneath >= 320) { + // toolbarUp = false; + // } // See if it's possible to scroll up to make space - else if (controller.scrollController.offset - targetOffset.dy + 320 >= + if (controller.scrollController.offset - targetOffset.dy + 320 >= controller.scrollController.position.minScrollExtent && controller.scrollController.offset - targetOffset.dy + 320 <= controller.scrollController.position.maxScrollExtent) { @@ -152,13 +152,7 @@ class ToolbarDisplayController { ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ - toolbarUp - // Column is limited to screen height - // If message portion is too tall, decrease toolbar height - // as necessary to prevent toolbar from acting strange - // Problems may still occur if toolbar height is decreased too much - ? toolbar! - : overlayMessage, + toolbarUp ? toolbar! : overlayMessage, const SizedBox(height: 6), toolbarUp ? overlayMessage : toolbar!, ], @@ -419,85 +413,83 @@ class MessageToolbarState extends State { @override Widget build(BuildContext context) { - return Flexible( - child: Material( - type: MaterialType.transparency, - child: Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: Theme.of(context).cardColor, - border: Border.all( - width: 2, - color: Theme.of(context).colorScheme.primary, - ), - borderRadius: const BorderRadius.all( - Radius.circular(25), - ), + return Material( + type: MaterialType.transparency, + child: Container( + padding: const EdgeInsets.all(10), + decoration: BoxDecoration( + color: Theme.of(context).cardColor, + border: Border.all( + width: 2, + color: Theme.of(context).colorScheme.primary, ), - constraints: const BoxConstraints( - maxWidth: 300, - minWidth: 300, - maxHeight: 300, + borderRadius: const BorderRadius.all( + Radius.circular(25), ), - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Flexible( - child: SingleChildScrollView( - child: AnimatedSize( - duration: FluffyThemes.animationDuration, - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: toolbarContent ?? const SizedBox(), - ), - SizedBox(height: toolbarContent == null ? 0 : 20), - ], - ), + ), + constraints: const BoxConstraints( + maxWidth: 300, + minWidth: 300, + maxHeight: 300, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Flexible( + child: SingleChildScrollView( + child: AnimatedSize( + duration: FluffyThemes.animationDuration, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: toolbarContent ?? const SizedBox(), + ), + SizedBox(height: toolbarContent == null ? 0 : 20), + ], ), ), ), - Row( - mainAxisSize: MainAxisSize.min, - children: MessageMode.values.map((mode) { - if ([ - MessageMode.definition, - MessageMode.textToSpeech, - MessageMode.translation, - ].contains(mode) && - widget.pangeaMessageEvent.isAudioMessage) { - return const SizedBox.shrink(); - } - if (mode == MessageMode.speechToText && - !widget.pangeaMessageEvent.isAudioMessage) { - return const SizedBox.shrink(); - } - return Tooltip( - message: mode.tooltip(context), - child: IconButton( - icon: Icon(mode.icon), - color: mode.iconColor( - widget.pangeaMessageEvent, - currentMode, - context, - ), - onPressed: () => updateMode(mode), - ), - ); - }).toList() + - [ - Tooltip( - message: L10n.of(context)!.more, - child: IconButton( - icon: const Icon(Icons.add_reaction_outlined), - onPressed: showMore, + ), + Row( + mainAxisSize: MainAxisSize.min, + children: MessageMode.values.map((mode) { + if ([ + MessageMode.definition, + MessageMode.textToSpeech, + MessageMode.translation, + ].contains(mode) && + widget.pangeaMessageEvent.isAudioMessage) { + return const SizedBox.shrink(); + } + if (mode == MessageMode.speechToText && + !widget.pangeaMessageEvent.isAudioMessage) { + return const SizedBox.shrink(); + } + return Tooltip( + message: mode.tooltip(context), + child: IconButton( + icon: Icon(mode.icon), + color: mode.iconColor( + widget.pangeaMessageEvent, + currentMode, + context, ), + onPressed: () => updateMode(mode), ), - ], - ), - ], - ), + ); + }).toList() + + [ + Tooltip( + message: L10n.of(context)!.more, + child: IconButton( + icon: const Icon(Icons.add_reaction_outlined), + onPressed: showMore, + ), + ), + ], + ), + ], ), ), ); From 6311df08756d919cf50fd9707e83133b7db5492b Mon Sep 17 00:00:00 2001 From: ggurdin Date: Thu, 25 Jul 2024 11:25:59 -0400 Subject: [PATCH 28/32] moved stateful variable from text controller to input bar wrapper widget --- lib/pages/chat/input_bar.dart | 3 --- lib/pangea/widgets/chat/input_bar_wrapper.dart | 13 +++++++++++-- lib/pangea/widgets/igc/pangea_text_controller.dart | 3 +-- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/pages/chat/input_bar.dart b/lib/pages/chat/input_bar.dart index 54dba8674..84a802e54 100644 --- a/lib/pages/chat/input_bar.dart +++ b/lib/pages/chat/input_bar.dart @@ -401,9 +401,6 @@ class InputBar extends StatelessWidget { @override Widget build(BuildContext context) { final useShortCuts = (AppConfig.sendOnEnter ?? !PlatformInfos.isMobile); - // #Pangea - controller?.currentlyMaxLength = controller?.isMaxLength ?? false; - // Pangea# return Shortcuts( shortcuts: !useShortCuts ? {} diff --git a/lib/pangea/widgets/chat/input_bar_wrapper.dart b/lib/pangea/widgets/chat/input_bar_wrapper.dart index 1e8cd4727..9441312bd 100644 --- a/lib/pangea/widgets/chat/input_bar_wrapper.dart +++ b/lib/pangea/widgets/chat/input_bar_wrapper.dart @@ -44,6 +44,7 @@ class InputBarWrapper extends StatefulWidget { class InputBarWrapperState extends State { StreamSubscription? _choreoSub; + String _currentText = ''; @override void initState() { @@ -65,10 +66,18 @@ class InputBarWrapperState extends State { if (widget.onChanged != null) { widget.onChanged!(text); } - if (widget.controller?.currentlyMaxLength != - widget.controller?.isMaxLength) { + + final bool decreasedFromMaxLength = + _currentText.length >= PangeaTextController.maxLength && + text.length < PangeaTextController.maxLength; + final bool reachedMaxLength = + _currentText.length < PangeaTextController.maxLength && + text.length < PangeaTextController.maxLength; + + if (decreasedFromMaxLength || reachedMaxLength) { setState(() {}); } + _currentText = text; } @override diff --git a/lib/pangea/widgets/igc/pangea_text_controller.dart b/lib/pangea/widgets/igc/pangea_text_controller.dart index d7f24a7e2..8fc136edd 100644 --- a/lib/pangea/widgets/igc/pangea_text_controller.dart +++ b/lib/pangea/widgets/igc/pangea_text_controller.dart @@ -26,10 +26,9 @@ class PangeaTextController extends TextEditingController { this.text = text; } + static const int maxLength = 1000; bool get isMaxLength => text.length == 1000; - bool currentlyMaxLength = false; - bool forceKeepOpen = false; setSystemText(String text, EditType type) { From 8f39d0ad37f566eed3c48b19d78f63fbcc2f9718 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Fri, 26 Jul 2024 11:13:30 -0400 Subject: [PATCH 29/32] update refrence to commit for matrix SDK in pubspec.lock file --- pubspec.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.lock b/pubspec.lock index ae95fb892..a0affbe48 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1434,7 +1434,7 @@ packages: description: path: "." ref: main - resolved-ref: "0a95cd8f3cfac8c9b0b59d6ee7fdbdb159949ca3" + resolved-ref: "5f22d0e9aed99f3044a208bb69f446d470eaa464" url: "https://github.com/pangeachat/matrix-dart-sdk.git" source: git version: "0.30.0" From 42ced399aa10c58e80b89a078daead3fa2b3db51 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Fri, 26 Jul 2024 11:36:36 -0400 Subject: [PATCH 30/32] some simplifications of input bar stack --- lib/pages/chat/chat_emoji_picker.dart | 163 +++++++++++--------------- lib/pages/chat/chat_view.dart | 17 +-- lib/pages/chat/reply_display.dart | 34 ++---- 3 files changed, 85 insertions(+), 129 deletions(-) diff --git a/lib/pages/chat/chat_emoji_picker.dart b/lib/pages/chat/chat_emoji_picker.dart index a59bba609..f38243b84 100644 --- a/lib/pages/chat/chat_emoji_picker.dart +++ b/lib/pages/chat/chat_emoji_picker.dart @@ -14,9 +14,6 @@ class ChatEmojiPicker extends StatelessWidget { @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); - // #Pangea - final bool lightMode = Theme.of(context).brightness == Brightness.light; - // Pangea# return AnimatedContainer( duration: FluffyThemes.animationDuration, curve: FluffyThemes.animationCurve, @@ -26,103 +23,79 @@ class ChatEmojiPicker extends StatelessWidget { ? MediaQuery.of(context).size.height / 2 : 0, child: controller.showEmojiPicker - ? - // #Pangea - Stack( - children: [ - // Pangea# - DefaultTabController( - length: 2, - child: Column( - children: [ - TabBar( - tabs: [ - Tab(text: L10n.of(context)!.emojis), - Tab(text: L10n.of(context)!.stickers), - ], - ), - Expanded( - child: TabBarView( - children: [ - EmojiPicker( - onEmojiSelected: controller.onEmojiSelected, - onBackspacePressed: - controller.emojiPickerBackspace, - config: Config( - emojiViewConfig: EmojiViewConfig( - noRecents: const NoRecent(), - backgroundColor: Theme.of(context) - .colorScheme - .onInverseSurface, - ), - bottomActionBarConfig: - const BottomActionBarConfig( - enabled: false, - ), - categoryViewConfig: CategoryViewConfig( - backspaceColor: theme.colorScheme.primary, - iconColor: theme.colorScheme.primary - .withOpacity(0.5), - iconColorSelected: theme.colorScheme.primary, - indicatorColor: theme.colorScheme.primary, - ), - skinToneConfig: SkinToneConfig( - dialogBackgroundColor: Color.lerp( - theme.colorScheme.surface, - theme.colorScheme.primaryContainer, - 0.75, - )!, - indicatorColor: theme.colorScheme.onSurface, - ), - ), + ? DefaultTabController( + length: 2, + child: Column( + children: [ + TabBar( + tabs: [ + Tab(text: L10n.of(context)!.emojis), + Tab(text: L10n.of(context)!.stickers), + ], + ), + Expanded( + child: TabBarView( + children: [ + EmojiPicker( + onEmojiSelected: controller.onEmojiSelected, + onBackspacePressed: controller.emojiPickerBackspace, + config: Config( + emojiViewConfig: EmojiViewConfig( + noRecents: const NoRecent(), + backgroundColor: Theme.of(context) + .colorScheme + .onInverseSurface, ), - StickerPickerDialog( - room: controller.room, - onSelected: (sticker) { - controller.room.sendEvent( - { - 'body': sticker.body, - 'info': sticker.info ?? {}, - 'url': sticker.url.toString(), - }, - type: EventTypes.Sticker, - ); - controller.hideEmojiPicker(); - }, + bottomActionBarConfig: const BottomActionBarConfig( + enabled: false, + ), + categoryViewConfig: CategoryViewConfig( + backspaceColor: theme.colorScheme.primary, + iconColor: + theme.colorScheme.primary.withOpacity(0.5), + iconColorSelected: theme.colorScheme.primary, + indicatorColor: theme.colorScheme.primary, + ), + skinToneConfig: SkinToneConfig( + dialogBackgroundColor: Color.lerp( + theme.colorScheme.surface, + theme.colorScheme.primaryContainer, + 0.75, + )!, + indicatorColor: theme.colorScheme.onSurface, ), - ], + ), ), - ), - ], - ), - ), - // #Pangea - // Close button placed at bottom of emoji picker - Positioned( - left: 0, - right: 0, - bottom: 5, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FloatingActionButton( - onPressed: controller.hideEmojiPicker, - backgroundColor: lightMode - ? const Color.fromARGB(255, 211, 211, 211) - : Colors.black, - shape: const CircleBorder(), - heroTag: null, - mini: true, - child: const Icon( - Icons.close, - size: 20, + StickerPickerDialog( + room: controller.room, + onSelected: (sticker) { + controller.room.sendEvent( + { + 'body': sticker.body, + 'info': sticker.info ?? {}, + 'url': sticker.url.toString(), + }, + type: EventTypes.Sticker, + ); + controller.hideEmojiPicker(); + }, ), - ), - ], + ], + ), + ), + // #Pangea + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: FloatingActionButton( + onPressed: controller.hideEmojiPicker, + shape: const CircleBorder(), + mini: true, + child: const Icon(Icons.close), + ), ), - ), - // Pangea# - ], + // Pangea# + ], + ), ) : null, ); diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 25e990c32..ff4ba7015 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -446,17 +446,17 @@ class ChatView extends StatelessWidget { Positioned( left: 0, right: 0, - bottom: 13, + bottom: 16, child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.center, children: [ if (!controller.selectMode) Container( - margin: const EdgeInsets.only( - bottom: 7, - left: 11, - right: 11, + margin: EdgeInsets.only( + bottom: 10, + left: bottomSheetPadding, + right: bottomSheetPadding, ), constraints: const BoxConstraints( maxWidth: FluffyThemes.columnWidth * 2.4, @@ -500,6 +500,7 @@ class ChatView extends StatelessWidget { ReactionsPicker(controller), ReplyDisplay(controller), ChatInputRow(controller), + ChatEmojiPicker(controller), ], ), ), @@ -507,12 +508,6 @@ class ChatView extends StatelessWidget { ], ), ), - Positioned( - bottom: 0, - left: 0, - right: 0, - child: ChatEmojiPicker(controller), - ), // Pangea# ], ), diff --git a/lib/pages/chat/reply_display.dart b/lib/pages/chat/reply_display.dart index f05c749a2..e2bacac83 100644 --- a/lib/pages/chat/reply_display.dart +++ b/lib/pages/chat/reply_display.dart @@ -21,9 +21,6 @@ class ReplyDisplay extends StatelessWidget { : 0, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - // #Pangea - borderRadius: const BorderRadius.all(Radius.circular(28.0)), - // Pangea# color: Theme.of(context).colorScheme.onInverseSurface, ), child: Row( @@ -67,28 +64,19 @@ class _EditContent extends StatelessWidget { Icons.edit, color: Theme.of(context).colorScheme.primary, ), - // #Pangea - // Container(width: 15.0), - Container(width: 8.0), - Flexible( - child: - // Pangea# - Text( - event.calcLocalizedBodyFallback( - MatrixLocals(L10n.of(context)!), - withSenderNamePrefix: false, - hideReply: true, - ), - overflow: TextOverflow.ellipsis, - maxLines: 1, - style: TextStyle( - color: Theme.of(context).textTheme.bodyMedium!.color, - ), + Container(width: 15.0), + Text( + event.calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)!), + withSenderNamePrefix: false, + hideReply: true, + ), + overflow: TextOverflow.ellipsis, + maxLines: 1, + style: TextStyle( + color: Theme.of(context).textTheme.bodyMedium!.color, ), ), - // #Pangea - Container(width: 10.0), - // Pangea# ], ); } From 7d000ef4724aa3d04ff3b89ed9b66804e491a95b Mon Sep 17 00:00:00 2001 From: ggurdin Date: Fri, 26 Jul 2024 12:00:27 -0400 Subject: [PATCH 31/32] updated spacing under event list --- lib/pages/chat/chat_view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index ff4ba7015..9b12f9904 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -425,7 +425,7 @@ class ChatView extends StatelessWidget { // #Pangea // Keep messages above minimum input bar height SizedBox( - height: (PlatformInfos.isMobile ? 24 : 50), + height: (PlatformInfos.isMobile ? 30 : 60), ), // Pangea# ], From 70d4e4a5139f0b0ee04bdab1b65f443c78bb71c0 Mon Sep 17 00:00:00 2001 From: ggurdin Date: Fri, 26 Jul 2024 15:03:36 -0400 Subject: [PATCH 32/32] when sending construct analytics, chunk uses so that they don't exceed the max size for sending a matrix event --- .../pangea_room_extension.dart | 1 + .../room_analytics_extension.dart | 60 +++++++++++++++---- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart b/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart index f76ebff42..c22ba9a23 100644 --- a/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/pangea_room_extension.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:convert'; import 'dart:developer'; import 'package:adaptive_dialog/adaptive_dialog.dart'; diff --git a/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart b/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart index 3a2e62c86..7bdd13743 100644 --- a/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart +++ b/lib/pangea/extensions/pangea_room_extension/room_analytics_extension.dart @@ -203,30 +203,66 @@ extension AnalyticsRoomExtension on Room { creationContent?.tryGet(ModelKey.oldLangCode) == langCode; } - Future sendSummaryAnalyticsEvent( + Future sendSummaryAnalyticsEvent( List records, ) async { final SummaryAnalyticsModel analyticsModel = SummaryAnalyticsModel( messages: records, ); - final String? eventId = await sendEvent( + await sendEvent( analyticsModel.toJson(), type: PangeaEventTypes.summaryAnalytics, ); - return eventId; } - Future sendConstructsEvent( + /// Sends construct events to the server. + /// + /// The [uses] parameter is a list of [OneConstructUse] objects representing the + /// constructs to be sent. To prevent hitting the maximum event size, the events + /// are chunked into smaller lists. Each chunk is sent as a separate event. + Future sendConstructsEvent( List uses, ) async { - final ConstructAnalyticsModel constructsModel = ConstructAnalyticsModel( - uses: uses, - ); + // these events can get big, so we chunk them to prevent hitting the max event size. + // go through each of the uses being sent and add them to the current chunk until + // the size (in bytes) of the current chunk is greater than the max event size, then + // start a new chunk until all uses have been added. + final List> useChunks = []; + List currentChunk = []; + int currentChunkSize = 0; - final String? eventId = await sendEvent( - constructsModel.toJson(), - type: PangeaEventTypes.construct, - ); - return eventId; + for (final use in uses) { + // get the size, in bytes, of the json representation of the use + final json = use.toJson(); + final jsonString = jsonEncode(json); + final jsonSizeInBytes = utf8.encode(jsonString).length; + + // If this use would tip this chunk over the size limit, + // add it to the list of all chunks and start a new chunk. + // + // I tested with using the maxPDUSize constant, but the events + // were still too large. 50000 seems to be a safe number of bytes. + if (currentChunkSize + jsonSizeInBytes > (maxPDUSize - 10000)) { + useChunks.add(currentChunk); + currentChunk = []; + currentChunkSize = 0; + } + + // add this use to the current chunk + currentChunk.add(use); + currentChunkSize += jsonSizeInBytes; + } + + if (currentChunk.isNotEmpty) { + useChunks.add(currentChunk); + } + + for (final chunk in useChunks) { + final constructsModel = ConstructAnalyticsModel(uses: chunk); + await sendEvent( + constructsModel.toJson(), + type: PangeaEventTypes.construct, + ); + } } }