diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 7c146d6c6..a24121b4f 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -104,7 +104,7 @@ class ChatPageWithRoom extends StatefulWidget { class ChatController extends State with WidgetsBindingObserver { -// #Pangea + // #Pangea final PangeaController pangeaController = MatrixState.pangeaController; late Choreographer choreographer = Choreographer(pangeaController, this); // Pangea# @@ -135,26 +135,32 @@ class ChatController extends State // void onDragDone(DropDoneDetails details) async { // setState(() => dragging = false); - // final bytesList = await showFutureLoadingDialog( + // if (details.files.isEmpty) return; + // final result = await showFutureLoadingDialog( // context: context, - // future: () => Future.wait( - // details.files.map( - // (xfile) => xfile.readAsBytes(), - // ), - // ), + // future: () async { + // final clientConfig = await room.client.getConfig(); + // final maxUploadSize = clientConfig.mUploadSize ?? 100 * 1024 * 1024; + // final matrixFiles = await Future.wait( + // details.files.map( + // (xfile) async { + // final length = await xfile.length(); + // if (length > maxUploadSize) { + // throw FileTooBigMatrixException(length, maxUploadSize); + // } + // return MatrixFile( + // bytes: await xfile.readAsBytes(), + // name: xfile.name, + // mimeType: xfile.mimeType, + // ).detectFileType; + // }, + // ), + // ); + // return matrixFiles; + // }, // ); - // if (bytesList.error != null) return; - - // final matrixFiles = []; - // for (var i = 0; i < bytesList.result!.length; i++) { - // matrixFiles.add( - // MatrixFile( - // bytes: bytesList.result![i], - // name: details.files[i].name, - // ).detectFileType, - // ); - // } - // if (matrixFiles.isEmpty) return; + // final matrixFiles = result.result; + // if (matrixFiles == null || matrixFiles.isEmpty) return; // await showAdaptiveDialog( // context: context, @@ -165,7 +171,6 @@ class ChatController extends State // ); // } // Pangea# - bool get canSaveSelectedEvent => selectedEvents.length == 1 && { @@ -178,7 +183,7 @@ class ChatController extends State void saveSelectedEvent(context) => selectedEvents.single.saveFile(context); - final List selectedEvents = []; + List selectedEvents = []; final Set unfolded = {}; @@ -186,14 +191,14 @@ class ChatController extends State Event? editEvent; - bool scrolledUp = false; + bool _scrolledUp = false; bool get showScrollDownButton => - scrolledUp || timeline?.allowNewEvent == false; + _scrolledUp || timeline?.allowNewEvent == false; bool get selectMode => selectedEvents.isNotEmpty; - final int loadHistoryCount = 100; + final int _loadHistoryCount = 100; String pendingText = ''; @@ -225,13 +230,13 @@ class ChatController extends State EmojiPickerType emojiPickerType = EmojiPickerType.keyboard; // #Pangea - // void requestHistory() async { + // void requestHistory([_]) async { Future requestHistory() async { if (timeline == null) return; // Pangea# if (!timeline!.canRequestHistory) return; Logs().v('Requesting history...'); - await timeline!.requestHistory(historyCount: loadHistoryCount); + await timeline!.requestHistory(historyCount: _loadHistoryCount); } void requestFuture() async { @@ -240,20 +245,20 @@ class ChatController extends State if (!timeline.canRequestFuture) return; Logs().v('Requesting future...'); final mostRecentEventId = timeline.events.first.eventId; - await timeline.requestFuture(historyCount: loadHistoryCount); + await timeline.requestFuture(historyCount: _loadHistoryCount); setReadMarker(eventId: mostRecentEventId); } - void updateScrollController() { + void _updateScrollController() { if (!mounted) { return; } if (!scrollController.hasClients) return; if (timeline?.allowNewEvent == false || - scrollController.position.pixels > 0 && scrolledUp == false) { - setState(() => scrolledUp = true); - } else if (scrollController.position.pixels <= 0 && scrolledUp == true) { - setState(() => scrolledUp = false); + scrollController.position.pixels > 0 && _scrolledUp == false) { + setState(() => _scrolledUp = true); + } else if (scrollController.position.pixels <= 0 && _scrolledUp == true) { + setState(() => _scrolledUp = false); setReadMarker(); } @@ -263,7 +268,7 @@ class ChatController extends State } } - void loadDraft() async { + void _loadDraft() async { final prefs = await SharedPreferences.getInstance(); final draft = widget.shareText ?? prefs.getString('draft_$roomId'); if (draft != null && draft.isNotEmpty) { @@ -277,15 +282,16 @@ class ChatController extends State @override void initState() { - scrollController.addListener(updateScrollController); - inputFocus.addListener(inputFocusListener); - loadDraft(); + scrollController.addListener(_updateScrollController); + inputFocus.addListener(_inputFocusListener); + _loadDraft(); super.initState(); - displayChatDetailsColumn = ValueNotifier( + _displayChatDetailsColumn = ValueNotifier( Matrix.of(context).store.getBool(SettingKeys.displayChatDetailsColumn) ?? false, ); sendingClient = Matrix.of(context).client; + WidgetsBinding.instance.addObserver(this); // #Pangea if (!mounted) return; Future.delayed(const Duration(seconds: 1), () async { @@ -327,15 +333,14 @@ class ChatController extends State }, ); // Pangea# - WidgetsBinding.instance.addObserver(this); - tryLoadTimeline(); + _tryLoadTimeline(); if (kIsWeb) { onFocusSub = html.window.onFocus.listen((_) => setReadMarker()); } } - void tryLoadTimeline() async { - loadTimelineFuture = getTimeline(); + void _tryLoadTimeline() async { + loadTimelineFuture = _getTimeline(); try { await loadTimelineFuture; final fullyRead = room.fullyRead; @@ -349,7 +354,7 @@ class ChatController extends State return; } if (!mounted) return; - showScrollUpMaterialBanner(fullyRead); + _showScrollUpMaterialBanner(fullyRead); } catch (e, s) { ErrorReporter(context, 'Unable to load timeline').onErrorCallback(e, s); rethrow; @@ -362,7 +367,7 @@ class ChatController extends State scrollUpBannerEventId = null; }); - void showScrollUpMaterialBanner(String eventId) => setState(() { + void _showScrollUpMaterialBanner(String eventId) => setState(() { scrollUpBannerEventId = eventId; }); @@ -395,7 +400,7 @@ class ChatController extends State []; // Pangea# - Future getTimeline({ + Future _getTimeline({ String? eventContextId, }) async { await Matrix.of(context).client.roomsLoading; @@ -433,7 +438,7 @@ class ChatController extends State ); if (!mounted) return; if (e is TimeoutException || e is IOException) { - showScrollUpMaterialBanner(eventContextId!); + _showScrollUpMaterialBanner(eventContextId!); } } timeline!.requestKeys(onlineKeyBackupOnly: false); @@ -462,11 +467,11 @@ class ChatController extends State setReadMarker(); } - Future? setReadMarkerFuture; + Future? _setReadMarkerFuture; void setReadMarker({String? eventId}) { - if (setReadMarkerFuture != null) return; - if (scrolledUp) return; + if (_setReadMarkerFuture != null) return; + if (_scrolledUp) return; if (scrollUpBannerEventId != null) return; if (eventId == null && !room.hasNewMessages && @@ -486,13 +491,13 @@ class ChatController extends State Logs().d('Set read marker...', eventId); // ignore: unawaited_futures - setReadMarkerFuture = timeline + _setReadMarkerFuture = timeline .setReadMarker( eventId: eventId, public: AppConfig.sendPublicReadReceipts, ) .then((_) { - setReadMarkerFuture = null; + _setReadMarkerFuture = null; }); if (eventId == null || eventId == timeline.room.lastEvent?.eventId) { Matrix.of(context).backgroundPush?.cancelNotification(roomId); @@ -503,12 +508,12 @@ class ChatController extends State void dispose() { timeline?.cancelSubscriptions(); timeline = null; - inputFocus.removeListener(inputFocusListener); + inputFocus.removeListener(_inputFocusListener); + onFocusSub?.cancel(); //#Pangea choreographer.stateListener.close(); choreographer.dispose(); //Pangea# - onFocusSub?.cancel(); super.dispose(); } @@ -528,7 +533,7 @@ class ChatController extends State } // then cancel the old timeline // fixes bug with read reciepts and quick switching - loadTimelineFuture = getTimeline(eventContextId: room.fullyRead).onError( + loadTimelineFuture = _getTimeline(eventContextId: room.fullyRead).onError( ErrorReporter( context, 'Unable to load timeline after changing sending Client', @@ -558,7 +563,7 @@ class ChatController extends State }) async { // Pangea# if (sendController.text.trim().isEmpty) return; - storeInputTimeoutTimer?.cancel(); + _storeInputTimeoutTimer?.cancel(); final prefs = await SharedPreferences.getInstance(); prefs.remove('draft_$roomId'); var parseCommands = true; @@ -782,7 +787,6 @@ class ChatController extends State // #Pangea // if (await Record().hasPermission() == false) return; // Pangea# - final result = await showDialog( context: context, barrierDismissible: false, @@ -828,11 +832,6 @@ class ChatController extends State } void emojiPickerAction() { -// #Pangea - if (choreographer.itController.isOpen) { - return; - } - // Pangea# if (showEmojiPicker) { inputFocus.requestFocus(); } else { @@ -842,7 +841,7 @@ class ChatController extends State setState(() => showEmojiPicker = !showEmojiPicker); } - void inputFocusListener() { + void _inputFocusListener() { if (showEmojiPicker && inputFocus.hasFocus) { emojiPickerType = EmojiPickerType.keyboard; setState(() => showEmojiPicker = false); @@ -856,7 +855,7 @@ class ChatController extends State ); } - String getSelectedEventString() { + String _getSelectedEventString() { var copyString = ''; if (selectedEvents.length == 1) { return selectedEvents.first @@ -874,7 +873,7 @@ class ChatController extends State } void copyEventsAction() { - Clipboard.setData(ClipboardData(text: getSelectedEventString())); + Clipboard.setData(ClipboardData(text: _getSelectedEventString())); setState(() { showEmojiPicker = false; selectedEvents.clear(); @@ -1065,7 +1064,7 @@ class ChatController extends State } else { Matrix.of(context).shareContent = { 'msgtype': 'm.text', - 'body': getSelectedEventString(), + 'body': _getSelectedEventString(), }; } setState(() => selectedEvents.clear()); @@ -1099,8 +1098,8 @@ class ChatController extends State if (eventIndex == -1) { setState(() { timeline = null; - scrolledUp = false; - loadTimelineFuture = getTimeline(eventContextId: eventId).onError( + _scrolledUp = false; + loadTimelineFuture = _getTimeline(eventContextId: eventId).onError( ErrorReporter(context, 'Unable to load timeline after scroll to ID') .onErrorCallback, ); @@ -1118,15 +1117,15 @@ class ChatController extends State eventIndex, preferPosition: AutoScrollPosition.middle, ); - updateScrollController(); + _updateScrollController(); } void scrollDown() async { if (!timeline!.allowNewEvent) { setState(() { timeline = null; - scrolledUp = false; - loadTimelineFuture = getTimeline().onError( + _scrolledUp = false; + loadTimelineFuture = _getTimeline().onError( ErrorReporter(context, 'Unable to load timeline after scroll down') .onErrorCallback, ); @@ -1152,7 +1151,7 @@ class ChatController extends State setState(() => showEmojiPicker = false); if (emoji == null) return; // make sure we don't send the same emoji twice - if (allReactionEvents.any( + if (_allReactionEvents.any( (e) => e.content.tryGetMap('m.relates_to')?['key'] == emoji.emoji, )) { return; @@ -1176,7 +1175,7 @@ class ChatController extends State ); } - late Iterable allReactionEvents; + late Iterable _allReactionEvents; void emojiPickerBackspace() { switch (emojiPickerType) { @@ -1194,7 +1193,7 @@ class ChatController extends State } void pickEmojiReactionAction(Iterable allReactionEvents) async { - allReactionEvents = allReactionEvents; + _allReactionEvents = allReactionEvents; emojiPickerType = EmojiPickerType.reaction; setState(() => showEmojiPicker = true); } @@ -1213,7 +1212,7 @@ class ChatController extends State void clearSelectedEvents() => setState(() { selectedEvents.clear(); showEmojiPicker = false; -//#Pangea + //#Pangea choreographer.messageOptions.resetSelectedDisplayLang(); //Pangea# }); @@ -1284,7 +1283,6 @@ class ChatController extends State if (choreographer.itController.isOpen) { return; } - // Pangea# if (!event.redacted) { if (selectedEvents.contains(event)) { @@ -1387,8 +1385,8 @@ class ChatController extends State ); } - Timer? storeInputTimeoutTimer; - static const Duration storeInputTimeout = Duration(milliseconds: 500); + Timer? _storeInputTimeoutTimer; + static const Duration _storeInputTimeout = Duration(milliseconds: 500); void onInputBarChanged(String text) { if (_inputTextIsEmpty != text.isEmpty) { @@ -1397,8 +1395,8 @@ class ChatController extends State }); } - storeInputTimeoutTimer?.cancel(); - storeInputTimeoutTimer = Timer(storeInputTimeout, () async { + _storeInputTimeoutTimer?.cancel(); + _storeInputTimeoutTimer = Timer(_storeInputTimeout, () async { final prefs = await SharedPreferences.getInstance(); await prefs.setString('draft_$roomId', text); }); @@ -1518,10 +1516,12 @@ class ChatController extends State bool? lastState; bool get isRowScrollable { if (availableSpace == null || inputRowSize == null) { - lastState = false; - Future.delayed(Duration.zero, () { - setState(() {}); - }); + if (lastState == null) { + lastState = false; + Future.delayed(Duration.zero, () { + setState(() {}); + }); + } return false; } const double offSetValue = 10; @@ -1540,39 +1540,45 @@ class ChatController extends State return currentState; } - final Map pangeaMessageEvents = {}; - final Map toolbarDisplayControllers = {}; + final Map _pangeaMessageEvents = {}; + final Map _toolbarDisplayControllers = {}; void setPangeaMessageEvent(String eventId) { final Event? event = timeline!.events.firstWhereOrNull( (e) => e.eventId == eventId, ); if (event == null || timeline == null) return; - pangeaMessageEvents[eventId] = PangeaMessageEvent( + _pangeaMessageEvents[eventId] = PangeaMessageEvent( event: event, timeline: timeline!, ownMessage: event.senderId == room.client.userID, ); } - void setToolbarDisplayController(String eventId) { + void setToolbarDisplayController( + String eventId, { + Event? nextEvent, + Event? previousEvent, + }) { final Event? event = timeline!.events.firstWhereOrNull( (e) => e.eventId == eventId, ); if (event == null || timeline == null) return; - if (pangeaMessageEvents[eventId] == null) { + if (_pangeaMessageEvents[eventId] == null) { setPangeaMessageEvent(eventId); - if (pangeaMessageEvents[eventId] == null) return; + if (_pangeaMessageEvents[eventId] == null) return; } try { - toolbarDisplayControllers[eventId] = ToolbarDisplayController( + _toolbarDisplayControllers[eventId] = ToolbarDisplayController( targetId: event.eventId, - pangeaMessageEvent: pangeaMessageEvents[eventId]!, + pangeaMessageEvent: _pangeaMessageEvents[eventId]!, immersionMode: choreographer.immersionMode, controller: this, + nextEvent: nextEvent, + previousEvent: previousEvent, ); - toolbarDisplayControllers[eventId]!.setToolbar(); + _toolbarDisplayControllers[eventId]!.setToolbar(); } catch (e, s) { ErrorHandler.logError( e: e, @@ -1581,35 +1587,43 @@ class ChatController extends State data: { "eventId": eventId, "event": event.toJson(), - "pangeaMessageEvent": pangeaMessageEvents[eventId]?.toString(), + "pangeaMessageEvent": _pangeaMessageEvents[eventId]?.toString(), }, ); } } PangeaMessageEvent? getPangeaMessageEvent(String eventId) { - if (pangeaMessageEvents[eventId] == null) { + if (_pangeaMessageEvents[eventId] == null) { setPangeaMessageEvent(eventId); } - return pangeaMessageEvents[eventId]; + return _pangeaMessageEvents[eventId]; } - ToolbarDisplayController? getToolbarDisplayController(String eventId) { - if (toolbarDisplayControllers[eventId] == null) { - setToolbarDisplayController(eventId); + ToolbarDisplayController? getToolbarDisplayController( + String eventId, { + Event? nextEvent, + Event? previousEvent, + }) { + if (_toolbarDisplayControllers[eventId] == null) { + setToolbarDisplayController( + eventId, + nextEvent: nextEvent, + previousEvent: previousEvent, + ); } - return toolbarDisplayControllers[eventId]; + return _toolbarDisplayControllers[eventId]; } // Pangea# - late final ValueNotifier displayChatDetailsColumn; + late final ValueNotifier _displayChatDetailsColumn; void toggleDisplayChatDetailsColumn() async { await Matrix.of(context).store.setBool( SettingKeys.displayChatDetailsColumn, - !displayChatDetailsColumn.value, + !_displayChatDetailsColumn.value, ); - displayChatDetailsColumn.value = !displayChatDetailsColumn.value; + _displayChatDetailsColumn.value = !_displayChatDetailsColumn.value; } @override @@ -1622,7 +1636,7 @@ class ChatController extends State duration: FluffyThemes.animationDuration, curve: FluffyThemes.animationCurve, child: ValueListenableBuilder( - valueListenable: displayChatDetailsColumn, + valueListenable: _displayChatDetailsColumn, builder: (context, displayChatDetailsColumn, _) { if (!FluffyThemes.isThreeColumnMode(context) || room.membership != Membership.join || diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index e4c1653d6..67e9358f2 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -28,7 +28,6 @@ class ChatEventList extends StatelessWidget { final events = controller.timeline!.events .where((event) => event.isVisibleInGui) .toList(); - final animateInEventIndex = controller.animateInEventIndex; // create a map of eventId --> index to greatly improve performance of @@ -98,8 +97,12 @@ class ChatEventList extends StatelessWidget { if (controller.timeline!.canRequestHistory) { return Builder( builder: (context) { + // #Pangea WidgetsBinding.instance .addPostFrameCallback((_) => controller.requestHistory); + // WidgetsBinding.instance + // .addPostFrameCallback(controller.requestHistory); + // Pangea# return Center( child: IconButton( onPressed: controller.requestHistory, @@ -151,8 +154,8 @@ class ChatEventList extends StatelessWidget { onSelect: controller.onSelectMessage, scrollToEventId: (String eventId) => controller.scrollToEventId(eventId), - // #Pangea longPressSelect: controller.selectedEvents.isNotEmpty, + // #Pangea selectedDisplayLang: controller.choreographer.messageOptions.selectedDisplayLang, immersionMode: controller.choreographer.immersionMode, diff --git a/lib/pages/chat/chat_input_row.dart b/lib/pages/chat/chat_input_row.dart index f2eebb77f..c26c0ac6a 100644 --- a/lib/pages/chat/chat_input_row.dart +++ b/lib/pages/chat/chat_input_row.dart @@ -116,12 +116,13 @@ class ChatInputRow extends StatelessWidget { duration: FluffyThemes.animationDuration, curve: FluffyThemes.animationCurve, height: height, - //#Pangea - // width: controller.sendController.text.isEmpty ? 56 : 0, + // #Pangea + // width: + // controller.sendController.text.isEmpty ? height : 0, width: controller.sendController.text.isEmpty && controller.pangeaController.permissionsController .showChatInputAddButton(controller.roomId) - ? height + ? 56 : 0, //Pangea# alignment: Alignment.center, @@ -164,8 +165,7 @@ class ChatInputRow extends StatelessWidget { contentPadding: const EdgeInsets.all(0), ), ), - - //#Pangea + // #Pangea // if (PlatformInfos.isMobile) if (PlatformInfos.isMobile && controller.pangeaController.permissionsController @@ -191,15 +191,15 @@ class ChatInputRow extends StatelessWidget { //Pangea# PopupMenuItem( value: 'camera-video', - child: ListTile( - leading: const CircleAvatar( - backgroundColor: Colors.red, - foregroundColor: Colors.white, - child: Icon(Icons.videocam_outlined), - ), - title: Text(L10n.of(context)!.openVideoCamera), - contentPadding: const EdgeInsets.all(0), + child: ListTile( + leading: const CircleAvatar( + backgroundColor: Colors.red, + foregroundColor: Colors.white, + child: Icon(Icons.videocam_outlined), ), + title: Text(L10n.of(context)!.openVideoCamera), + contentPadding: const EdgeInsets.all(0), + ), ), //#Pangea // if (PlatformInfos.isMobile) @@ -219,22 +219,6 @@ class ChatInputRow extends StatelessWidget { contentPadding: const EdgeInsets.all(0), ), ), - - if (controller.room - .getImagePacks(ImagePackUsage.sticker) - .isNotEmpty) - PopupMenuItem( - value: 'sticker', - child: ListTile( - leading: const CircleAvatar( - backgroundColor: Colors.orange, - foregroundColor: Colors.white, - child: Icon(Icons.emoji_emotions_outlined), - ), - title: Text(L10n.of(context)!.sendSticker), - contentPadding: const EdgeInsets.all(0), - ), - ), ], ), ), @@ -269,7 +253,7 @@ class ChatInputRow extends StatelessWidget { child: Icon( controller.showEmojiPicker ? Icons.keyboard - : Icons.emoji_emotions_outlined, + : Icons.add_reaction_outlined, key: ValueKey(controller.showEmojiPicker), ), ), @@ -282,14 +266,15 @@ class ChatInputRow extends StatelessWidget { // Matrix.of(context).hasComplexBundles && // Matrix.of(context).currentBundle!.length > 1) // Container( - // height: 56, + // width: height, + // height: height, // alignment: Alignment.center, // child: _ChatAccountPicker(controller), // ), // Pangea# Expanded( child: Padding( - padding: const EdgeInsets.symmetric(vertical: 4.0), + padding: const EdgeInsets.symmetric(vertical: 0.0), child: InputBar( room: controller.room, minLines: 1, @@ -312,7 +297,7 @@ class ChatInputRow extends StatelessWidget { focusNode: controller.inputFocus, controller: controller.sendController, decoration: InputDecoration( - contentPadding: const EdgeInsets.only( + contentPadding: const EdgeInsets.only( left: 6.0, right: 6.0, bottom: 6.0, @@ -323,61 +308,51 @@ class ChatInputRow extends StatelessWidget { border: InputBorder.none, enabledBorder: InputBorder.none, filled: false, - ), + ), onChanged: controller.onInputBarChanged, ), ), ), - if (PlatformInfos.platformCanRecord && - controller.sendController.text.isEmpty) - Container( - height: height, - width: height, - alignment: Alignment.center, - child: PlatformInfos.platformCanRecord && - controller.sendController.text.isEmpty - ? FloatingActionButton.small( - tooltip: L10n.of(context)!.voiceMessage, - onPressed: controller.voiceMessageAction, - elevation: 0, - heroTag: null, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(height), - ), - backgroundColor: Theme.of(context).colorScheme.primary, - foregroundColor: - Theme.of(context).colorScheme.onPrimary, - child: const Icon(Icons.mic_none_outlined), - ) - : FloatingActionButton.small( - tooltip: L10n.of(context)!.send, - onPressed: controller.send, - elevation: 0, - heroTag: null, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(height), - ), - backgroundColor: - Theme.of(context).colorScheme.onPrimaryContainer, - foregroundColor: - Theme.of(context).colorScheme.onPrimary, - child: const Icon(Icons.send_outlined), + Container( + height: height, + width: height, + alignment: Alignment.center, + child: PlatformInfos.platformCanRecord && + controller.sendController.text.isEmpty + ? FloatingActionButton.small( + tooltip: L10n.of(context)!.voiceMessage, + onPressed: controller.voiceMessageAction, + elevation: 0, + heroTag: null, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(height), ), - ), - if (!PlatformInfos.isMobile || - controller.sendController.text.isNotEmpty) - // #Pangea - ChoreographerSendButton(controller: controller), - // Container( - // height: 56, - // alignment: Alignment.center, - // child: IconButton( - // icon: const Icon(Icons.send_outlined), - // onPressed: controller.send, - // tooltip: L10n.of(context)!.send, - // ), - // ), - // Pangea# + backgroundColor: + Theme.of(context).colorScheme.primary, + foregroundColor: + Theme.of(context).colorScheme.onPrimary, + child: const Icon(Icons.mic_none_outlined), + ) + : + // #Pangea + ChoreographerSendButton(controller: controller), + // FloatingActionButton.small( + // tooltip: L10n.of(context)!.send, + // onPressed: controller.send, + // elevation: 0, + // heroTag: null, + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(height), + // ), + // backgroundColor: Theme.of(context) + // .colorScheme + // .onPrimaryContainer, + // foregroundColor: + // Theme.of(context).colorScheme.onPrimary, + // child: const Icon(Icons.send_outlined), + // ), + // Pangea# + ), ], ), ], diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 8310c395d..57255ffc9 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -1,7 +1,4 @@ import 'package:badges/badges.dart'; -// #Pangea -// import 'package:desktop_drop/desktop_drop.dart'; -// Pangea# import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat/chat.dart'; import 'package:fluffychat/pages/chat/chat_app_bar_list_tile.dart'; @@ -122,7 +119,8 @@ class ChatView extends StatelessWidget { ChatSettingsPopupMenu(controller.room, !controller.room.isDirectChat), ]; } - // } else if (!controller.room.isArchived) { + + // else if (!controller.room.isArchived) { // return [ // if (Matrix.of(context).voipPlugin != null && // controller.room.isDirectChat) @@ -200,7 +198,7 @@ class ChatView extends StatelessWidget { // #Pangea && !r.isAnalyticsRoom, - // Pangea# + // Pangea#, badgePosition: BadgePosition.topEnd(end: 8, top: 4), child: const Center(child: BackButton()), ), @@ -252,6 +250,9 @@ class ChatView extends StatelessWidget { ), ), ), + // #Pangea + // floatingActionButton: controller.showScrollDownButton && + // controller.selectedEvents.isEmpty floatingActionButton: controller.selectedEvents.isEmpty ? (controller.showScrollDownButton // Pangea# @@ -427,7 +428,6 @@ class ChatView extends StatelessWidget { // Pangea# ], ), - // ), ); }, ), diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index 83f9211c8..a866e0358 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -159,7 +159,11 @@ class Message extends StatelessWidget { ToolbarDisplayController? toolbarController; if (event.messageType == MessageTypes.Text || event.messageType == MessageTypes.Notice) { - toolbarController = controller.getToolbarDisplayController(event.eventId); + toolbarController = controller.getToolbarDisplayController( + event.eventId, + nextEvent: nextEvent, + previousEvent: previousEvent, + ); } // Pangea# diff --git a/lib/pages/chat_list/client_chooser_button.dart b/lib/pages/chat_list/client_chooser_button.dart index 74c3f34b7..2edf5fd99 100644 --- a/lib/pages/chat_list/client_chooser_button.dart +++ b/lib/pages/chat_list/client_chooser_button.dart @@ -1,5 +1,3 @@ -import 'dart:developer'; - import 'package:adaptive_dialog/adaptive_dialog.dart'; import 'package:fluffychat/pangea/constants/class_default_values.dart'; import 'package:fluffychat/pangea/extensions/client_extension.dart'; @@ -7,7 +5,6 @@ import 'package:fluffychat/pangea/utils/class_code.dart'; import 'package:fluffychat/pangea/utils/find_conversation_partner_dialog.dart'; import 'package:fluffychat/pangea/utils/logout.dart'; import 'package:fluffychat/widgets/matrix.dart'; -import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -113,17 +110,6 @@ class ClientChooserButton extends StatelessWidget { ], ), ), - PopupMenuItem( - value: SettingsAction.findAClass, - enabled: false, - child: Row( - children: [ - const Icon(Icons.class_outlined), - const SizedBox(width: 18), - Expanded(child: Text(L10n.of(context)!.findAClass)), - ], - ), - ), if (controller.pangeaController.permissionsController.isUser18()) PopupMenuItem( value: SettingsAction.findAConversationPartner, @@ -421,9 +407,6 @@ class ClientChooserButton extends StatelessWidget { case SettingsAction.myAnalytics: context.go('/rooms/mylearning'); break; - case SettingsAction.findAClass: - debugger(when: kDebugMode, message: "left to implement"); - break; case SettingsAction.logout: pLogoutAction(context); break; @@ -514,7 +497,6 @@ enum SettingsAction { joinWithClassCode, classAnalytics, myAnalytics, - findAClass, findAConversationPartner, logout, newClass, diff --git a/lib/pangea/widgets/chat/message_toolbar.dart b/lib/pangea/widgets/chat/message_toolbar.dart index 2e9627c71..8e88d3a51 100644 --- a/lib/pangea/widgets/chat/message_toolbar.dart +++ b/lib/pangea/widgets/chat/message_toolbar.dart @@ -26,6 +26,8 @@ class ToolbarDisplayController { final bool immersionMode; final ChatController controller; final FocusNode focusNode = FocusNode(); + Event? nextEvent; + Event? previousEvent; MessageToolbar? toolbar; String? overlayId; @@ -38,6 +40,8 @@ class ToolbarDisplayController { required this.targetId, required this.immersionMode, required this.controller, + this.nextEvent, + this.previousEvent, }); void setToolbar() { @@ -86,6 +90,8 @@ class ToolbarDisplayController { ownMessage: pangeaMessageEvent.ownMessage, toolbarController: this, width: messageWidth, + nextEvent: nextEvent, + previousEvent: previousEvent, ), ], ); diff --git a/lib/pangea/widgets/chat/overlay_message.dart b/lib/pangea/widgets/chat/overlay_message.dart index e39ffe827..630addc17 100644 --- a/lib/pangea/widgets/chat/overlay_message.dart +++ b/lib/pangea/widgets/chat/overlay_message.dart @@ -11,6 +11,8 @@ import '../../../config/app_config.dart'; class OverlayMessage extends StatelessWidget { final Event event; + final Event? nextEvent; + final Event? previousEvent; final bool selected; final Timeline timeline; // #Pangea @@ -24,6 +26,8 @@ class OverlayMessage extends StatelessWidget { const OverlayMessage( this.event, { + this.nextEvent, + this.previousEvent, this.selected = false, required this.timeline, // #Pangea @@ -44,19 +48,43 @@ class OverlayMessage extends StatelessWidget { var color = Theme.of(context).colorScheme.surfaceVariant; final textColor = ownMessage - ? Theme.of(context).colorScheme.onPrimaryContainer + ? Theme.of(context).colorScheme.onPrimary : Theme.of(context).colorScheme.onBackground; + const hardCorner = Radius.circular(4); + + final displayTime = event.type == EventTypes.RoomCreate || + nextEvent == null || + !event.originServerTs.sameEnvironment(nextEvent!.originServerTs); + + final nextEventSameSender = nextEvent != null && + { + EventTypes.Message, + EventTypes.Sticker, + EventTypes.Encrypted, + }.contains(nextEvent!.type) && + nextEvent!.senderId == event.senderId && + !displayTime; + + final previousEventSameSender = previousEvent != null && + { + EventTypes.Message, + EventTypes.Sticker, + EventTypes.Encrypted, + }.contains(previousEvent!.type) && + previousEvent!.senderId == event.senderId && + previousEvent!.originServerTs.sameEnvironment(event.originServerTs); + + const roundedCorner = Radius.circular(AppConfig.borderRadius); final borderRadius = BorderRadius.only( - topLeft: !ownMessage - ? const Radius.circular(4) - : const Radius.circular(AppConfig.borderRadius), - topRight: const Radius.circular(AppConfig.borderRadius), - bottomLeft: const Radius.circular(AppConfig.borderRadius), - bottomRight: ownMessage - ? const Radius.circular(4) - : const Radius.circular(AppConfig.borderRadius), + topLeft: !ownMessage && nextEventSameSender ? hardCorner : roundedCorner, + topRight: ownMessage && nextEventSameSender ? hardCorner : roundedCorner, + bottomLeft: + !ownMessage && previousEventSameSender ? hardCorner : roundedCorner, + bottomRight: + ownMessage && previousEventSameSender ? hardCorner : roundedCorner, ); + final noBubble = { MessageTypes.Video, MessageTypes.Image, @@ -69,7 +97,7 @@ class OverlayMessage extends StatelessWidget { }.contains(event.messageType); if (ownMessage) { - color = Theme.of(context).colorScheme.primaryContainer; + color = Theme.of(context).colorScheme.primary; } // #Pangea