diff --git a/lib/config/app_config.dart b/lib/config/app_config.dart index 975f985a6..f566ed862 100644 --- a/lib/config/app_config.dart +++ b/lib/config/app_config.dart @@ -138,7 +138,6 @@ abstract class AppConfig { static bool hideRedactedEvents = true; // Pangea# static bool hideUnknownEvents = true; - static bool hideUnimportantStateEvents = true; static bool separateChatTypes = false; static bool autoplayImages = true; static bool sendTypingNotifications = true; @@ -153,7 +152,6 @@ abstract class AppConfig { // Pangea# static bool experimentalVoip = false; static const bool hideTypingUsernames = false; - static const bool hideAllStateEvents = false; static const String inviteLinkPrefix = 'https://matrix.to/#/'; static const String deepLinkPrefix = 'im.fluffychat://chat/'; static const String schemePrefix = 'matrix:'; diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index f61e52653..ce9694b2d 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -3241,6 +3241,7 @@ "commandHint_logoutall": "Logout all active devices", "displayNavigationRail": "Show navigation rail on mobile", "customReaction": "Custom reaction", + "moreEvents": "More events", "ignore": "Block", "ignoredUsers": "Blocked users", "writeAMessageLangCodes": "Type in {l1} or {l2}...", diff --git a/lib/l10n/intl_es.arb b/lib/l10n/intl_es.arb index ff0445450..0a31bc058 100644 --- a/lib/l10n/intl_es.arb +++ b/lib/l10n/intl_es.arb @@ -3429,15 +3429,6 @@ "makeSureTheIdentifierIsValid": "Asegúrese de que el identificador es válido", "messageWillBeRemovedWarning": "El mensaje será eliminado para todos los participantes", "monday": "Lunes", - "moreEvents": "{count,plural, =1{1 evento más} other{{count} más eventos}}", - "@moreEvents": { - "type": "String", - "placeholders": { - "count": { - "type": "String" - } - } - }, "noCrossSignBootstrap": "Fluffychat actualmente no soporta la activación de Cross-Signing. Por favor, actívelo dentro de Riot.", "noMegolmBootstrap": "Fluffychat actualmente no soporta la activación de Online Key Backup. Por favor, actívelo dentro de Riot.", "noPublicRoomsFound": "Sin chats públicos…", diff --git a/lib/pages/chat/chat.dart b/lib/pages/chat/chat.dart index 86d333ca1..db67eb068 100644 --- a/lib/pages/chat/chat.dart +++ b/lib/pages/chat/chat.dart @@ -561,6 +561,24 @@ class ChatController extends State } // Pangea# + final Set expandedEventIds = {}; + + void expandEventsFrom(Event event, bool expand) { + final events = timeline!.events.filterByVisibleInGui(); + final start = events.indexOf(event); + setState(() { + for (var i = start; i < events.length; i++) { + final event = events[i]; + if (!event.isCollapsedState) return; + if (expand) { + expandedEventIds.add(event.eventId); + } else { + expandedEventIds.remove(event.eventId); + } + } + }); + } + void _tryLoadTimeline() async { final initialEventId = widget.eventId; loadTimelineFuture = _getTimeline(); diff --git a/lib/pages/chat/chat_event_list.dart b/lib/pages/chat/chat_event_list.dart index 990469e8f..aa70c6d0c 100644 --- a/lib/pages/chat/chat_event_list.dart +++ b/lib/pages/chat/chat_event_list.dart @@ -150,6 +150,17 @@ class ChatEventList extends StatelessWidget { timeline.events.length > animateInEventIndex && event == timeline.events[animateInEventIndex]; + final nextEvent = i + 1 < events.length ? events[i + 1] : null; + final previousEvent = i > 0 ? events[i - 1] : null; + + // Collapsed state event + final canExpand = event.isCollapsedState && + nextEvent?.isCollapsedState == true && + previousEvent?.isCollapsedState != true; + final isCollapsed = event.isCollapsedState && + previousEvent?.isCollapsedState == true && + !controller.expandedEventIds.contains(event.eventId); + return AutoScrollTag( key: ValueKey(event.eventId), index: i, @@ -190,11 +201,19 @@ class ChatEventList extends StatelessWidget { timeline: timeline, displayReadMarker: i > 0 && controller.readMarkerEventId == event.eventId, - nextEvent: i + 1 < events.length ? events[i + 1] : null, - previousEvent: i > 0 ? events[i - 1] : null, + nextEvent: nextEvent, + previousEvent: previousEvent, wallpaperMode: hasWallpaper, scrollController: controller.scrollController, colors: colors, + isCollapsed: isCollapsed, + onExpand: canExpand + ? () => controller.expandEventsFrom( + event, + !controller.expandedEventIds + .contains(event.eventId), + ) + : null, ), ); }, diff --git a/lib/pages/chat/events/message.dart b/lib/pages/chat/events/message.dart index d4ee1d5c1..59f33c777 100644 --- a/lib/pages/chat/events/message.dart +++ b/lib/pages/chat/events/message.dart @@ -51,6 +51,8 @@ class Message extends StatelessWidget { final bool wallpaperMode; final ScrollController scrollController; final List colors; + final void Function()? onExpand; + final bool isCollapsed; // #Pangea final ChatController controller; final bool isButton; @@ -78,6 +80,8 @@ class Message extends StatelessWidget { required this.onMention, required this.scrollController, required this.colors, + this.onExpand, + this.isCollapsed = false, // #Pangea required this.controller, this.isButton = false, @@ -169,7 +173,7 @@ class Message extends StatelessWidget { } // Pangea# - return StateMessage(event); + return StateMessage(event, onExpand: onExpand, isCollapsed: isCollapsed); } if (event.type == EventTypes.Message && diff --git a/lib/pages/chat/events/state_message.dart b/lib/pages/chat/events/state_message.dart index 2975c8a06..b1d0dec8a 100644 --- a/lib/pages/chat/events/state_message.dart +++ b/lib/pages/chat/events/state_message.dart @@ -1,56 +1,95 @@ +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/l10n/l10n.dart'; import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import '../../../config/app_config.dart'; class StateMessage extends StatelessWidget { final Event event; - const StateMessage(this.event, {super.key}); + final void Function()? onExpand; + final bool isCollapsed; + const StateMessage( + this.event, { + this.onExpand, + this.isCollapsed = false, + super.key, + }); @override Widget build(BuildContext context) { final theme = Theme.of(context); - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 8.0), - child: Center( - child: Padding( - padding: const EdgeInsets.all(4), - child: Material( - color: theme.colorScheme.surface.withAlpha(128), - borderRadius: BorderRadius.circular(AppConfig.borderRadius / 3), - child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 8.0, vertical: 4.0), - child: Text( - // #Pangea - // event.calcLocalizedBodyFallback( - // MatrixLocals(L10n.of(context)), - // ), - (event.type == EventTypes.RoomMember) && - (event.roomMemberChangeType == - RoomMemberChangeType.leave) && - (event.stateKey == event.room.client.userID) - ? L10n.of(context).youLeftTheChat - : event.calcLocalizedBodyFallback( - MatrixLocals(L10n.of(context)), + return AnimatedSize( + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + child: isCollapsed + ? const SizedBox.shrink() + : Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Center( + child: Padding( + padding: const EdgeInsets.all(4), + child: Material( + color: theme.colorScheme.surface.withAlpha(128), + borderRadius: + BorderRadius.circular(AppConfig.borderRadius / 3), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 4.0, ), - // Pangea# - textAlign: TextAlign.center, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 12 * AppConfig.fontSizeFactor, - decoration: - event.redacted ? TextDecoration.lineThrough : null, + child: Text.rich( + TextSpan( + children: [ + TextSpan( + // #Pangea + // text: event.calcLocalizedBodyFallback( + // MatrixLocals(L10n.of(context)), + // ), + text: (event.type == EventTypes.RoomMember) && + (event.roomMemberChangeType == + RoomMemberChangeType.leave) && + (event.stateKey == + event.room.client.userID) + ? L10n.of(context).youLeftTheChat + : event.calcLocalizedBodyFallback( + MatrixLocals(L10n.of(context)), + ), + // Pangea# + ), + if (onExpand != null) ...[ + const TextSpan( + text: ' + ', + style: TextStyle(fontWeight: FontWeight.bold), + ), + TextSpan( + style: TextStyle( + color: theme.colorScheme.primary, + decoration: TextDecoration.underline, + ), + recognizer: TapGestureRecognizer() + ..onTap = onExpand, + text: L10n.of(context).moreEvents, + ), + ], + ], + ), + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 12 * AppConfig.fontSizeFactor, + decoration: event.redacted + ? TextDecoration.lineThrough + : null, + ), + ), + ), + ), ), ), ), - ), - ), - ), ); } } diff --git a/lib/pages/settings_chat/settings_chat_view.dart b/lib/pages/settings_chat/settings_chat_view.dart index 64341abba..9412cd1b0 100644 --- a/lib/pages/settings_chat/settings_chat_view.dart +++ b/lib/pages/settings_chat/settings_chat_view.dart @@ -36,13 +36,6 @@ class SettingsChatView extends StatelessWidget { // storeKey: SettingKeys.renderHtml, // defaultValue: AppConfig.renderHtml, // ), - // SettingsSwitchListTile.adaptive( - // title: L10n.of(context).hideMemberChangesInPublicChats, - // subtitle: L10n.of(context).hideMemberChangesInPublicChatsBody, - // onChanged: (b) => AppConfig.hideUnimportantStateEvents = b, - // storeKey: SettingKeys.hideUnimportantStateEvents, - // defaultValue: AppConfig.hideUnimportantStateEvents, - // ), // Pangea# SettingsSwitchListTile.adaptive( title: L10n.of(context).hideRedactedMessages, diff --git a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart index f15e1f119..e5f07543b 100644 --- a/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart +++ b/lib/utils/matrix_sdk_extensions/filtered_timeline_extension.dart @@ -3,6 +3,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/pangea/activity_sessions/activity_room_extension.dart'; import 'package:fluffychat/pangea/common/constants/model_keys.dart'; import 'package:fluffychat/pangea/events/constants/pangea_event_types.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/filtered_timeline_extension.dart'; import '../../config/app_config.dart'; extension VisibleInGuiExtension on List { @@ -30,29 +31,16 @@ extension IsStateExtension on Event { (!AppConfig.hideRedactedEvents || !redacted) && // if we enabled to hide all unknown events, don't show those // #Pangea - // (!AppConfig.hideUnknownEvents || isEventTypeKnown) && + // (!AppConfig.hideUnknownEvents || isEventTypeKnown); (!AppConfig.hideUnknownEvents || pangeaIsEventTypeKnown) && - // Pangea# - // remove state events that we don't want to render - (isState || !AppConfig.hideAllStateEvents) && - // #Pangea + (!isState || importantStateEvents.contains(type)) && content.tryGet(ModelKey.transcription) == null && // if sending of transcription fails, // don't show it as a errored audio event in timeline. ((unsigned?['extra_content'] as Map?)?[ModelKey.transcription] == - null) && - // hide unimportant state events - (!AppConfig.hideUnimportantStateEvents || - !isState || - importantStateEvents.contains(type)) && - // Pangea# - // hide simple join/leave member events in public rooms - (!AppConfig.hideUnimportantStateEvents || - type != EventTypes.RoomMember || - room.joinRules != JoinRules.public || - content.tryGet('membership') == 'ban' || - stateKey != senderId); + null); + // Pangea# bool get isState => !{ EventTypes.Message, @@ -60,6 +48,14 @@ extension IsStateExtension on Event { EventTypes.Encrypted, }.contains(type); + bool get isCollapsedState => !{ + EventTypes.Message, + EventTypes.Sticker, + EventTypes.Encrypted, + EventTypes.RoomCreate, + EventTypes.RoomTombstone, + }.contains(type); + // #Pangea bool get isVisibleInPangeaGui { if (!room.showActivityChatUI) { @@ -78,7 +74,6 @@ extension IsStateExtension on Event { PangeaEventTypes.activityRole, ].contains(type); - // we're filtering out some state events that we don't want to render static const Set importantStateEvents = { EventTypes.Encryption, EventTypes.RoomCreate, diff --git a/lib/widgets/matrix.dart b/lib/widgets/matrix.dart index 808678ffb..cb2a3959b 100644 --- a/lib/widgets/matrix.dart +++ b/lib/widgets/matrix.dart @@ -588,10 +588,6 @@ class MatrixState extends State with WidgetsBindingObserver { store.getBool(SettingKeys.hideUnknownEvents) ?? AppConfig.hideUnknownEvents; - AppConfig.hideUnimportantStateEvents = - store.getBool(SettingKeys.hideUnimportantStateEvents) ?? - AppConfig.hideUnimportantStateEvents; - AppConfig.separateChatTypes = store.getBool(SettingKeys.separateChatTypes) ?? AppConfig.separateChatTypes;