diff --git a/assets/l10n/intl_de.arb b/assets/l10n/intl_de.arb index 07dea5ae8..cb0c713d9 100644 --- a/assets/l10n/intl_de.arb +++ b/assets/l10n/intl_de.arb @@ -2,8 +2,8 @@ "@@locale": "de", "@@last_modified": "2021-08-14 12:41:10.119255", "alwaysUse24HourFormat": "true", - "@alwaysUse24HourFormat": { - "description": "Set to true to always display time of day in 24 hour format." + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." }, "about": "Über", "@about": { @@ -2710,5 +2710,22 @@ } }, "searchMore": "Weiter suchen ...", - "@searchMore": {} + "@searchMore": {}, + "unread": "Ungelesen", + "@unread": {}, + "noMoreChatsFound": "Keine weiteren Chats gefunden ...", + "@noMoreChatsFound": {}, + "joinedChats": "Beigetretene Chats", + "@joinedChats": {}, + "space": "Space", + "@space": {}, + "spaces": "Spaces", + "@spaces": {}, + "goToSpace": "Geh zum Space: {space}", + "@goToSpace": { + "type": "text", + "space": {} + }, + "markAsUnread": "Als ungelesen markieren", + "@markAsUnread": {} } diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index 61824dad2..f787f2b4f 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -2,8 +2,8 @@ "@@locale": "en", "@@last_modified": "2021-08-14 12:38:37.885451", "alwaysUse24HourFormat": "false", - "@alwaysUse24HourFormat": { - "description": "Set to true to always display time of day in 24 hour format." + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." }, "repeatPassword": "Repeat password", "@repeatPassword": {}, @@ -4146,5 +4146,11 @@ "error520Title": "Please try again.", "error520Desc": "Sorry, we could not understand your message...", "translationChoicesBody": "Click and hold an option for a hint.", - "sendCanceled": "Sending canceled" + "sendCanceled": "Sending canceled", + "goToSpace": "Go to space: {space}", + "@goToSpace": { + "type": "text", + "space": {} + }, + "markAsUnread": "Mark as unread" } \ No newline at end of file diff --git a/assets/l10n/intl_gl.arb b/assets/l10n/intl_gl.arb index 2fe2464f5..8a7ef7922 100644 --- a/assets/l10n/intl_gl.arb +++ b/assets/l10n/intl_gl.arb @@ -2708,5 +2708,9 @@ "restricted": "Non accesible", "@restricted": {}, "swipeRightToLeftToReply": "Despraza hacia a esquerda para responder", - "@swipeRightToLeftToReply": {} + "@swipeRightToLeftToReply": {}, + "alwaysUse24HourFormat": "falso", + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." + } } diff --git a/assets/l10n/intl_hr.arb b/assets/l10n/intl_hr.arb index a3f981b2d..3d6c84730 100644 --- a/assets/l10n/intl_hr.arb +++ b/assets/l10n/intl_hr.arb @@ -2708,5 +2708,9 @@ "count": {} }, "swipeRightToLeftToReply": "Za odgovaranje povuci prstom zdesna ulijevo", - "@swipeRightToLeftToReply": {} + "@swipeRightToLeftToReply": {}, + "alwaysUse24HourFormat": "true", + "@alwaysUse24HourFormat": { + "description": "Set to true to always display time of day in 24 hour format." + } } diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 75e01fcf0..d9c7a70f7 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -136,8 +136,12 @@ abstract class AppRoutes { FluffyThemes.isColumnMode(context) && state.fullPath?.startsWith('/rooms/settings') == false ? TwoColumnLayout( + displayNavigationRail: + state.path?.startsWith('/rooms/settings') != true, mainView: ChatList( activeChat: state.pathParameters['roomid'], + displayNavigationRail: + state.path?.startsWith('/rooms/settings') != true, ), sideView: child, ) @@ -280,6 +284,7 @@ abstract class AppRoutes { ? TwoColumnLayout( mainView: const Settings(), sideView: child, + displayNavigationRail: false, ) : child, ), diff --git a/lib/pages/chat/chat_view.dart b/lib/pages/chat/chat_view.dart index 83ca6ca27..4447599f1 100644 --- a/lib/pages/chat/chat_view.dart +++ b/lib/pages/chat/chat_view.dart @@ -202,15 +202,17 @@ class ChatView extends StatelessWidget { tooltip: L10n.of(context)!.close, color: Theme.of(context).colorScheme.primary, ) - : UnreadRoomsBadge( - filter: (r) => - r.id != controller.roomId - // #Pangea - && - !r.isAnalyticsRoom, - // Pangea# - badgePosition: BadgePosition.topEnd(end: 8, top: 4), - child: const Center(child: BackButton()), + : StreamBuilder( + stream: Matrix.of(context) + .client + .onSync + .stream + .where((syncUpdate) => syncUpdate.hasRoomUpdate), + builder: (context, _) => UnreadRoomsBadge( + filter: (r) => r.id != controller.roomId, + badgePosition: BadgePosition.topEnd(end: 8, top: 4), + child: const Center(child: BackButton()), + ), ), titleSpacing: 0, title: ChatAppBarTitle(controller), diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index b5868b1ef..52e4cff4e 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -17,6 +17,7 @@ import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/tor_stub.dart' if (dart.library.html) 'package:tor_detector_web/tor_detector_web.dart'; +import 'package:fluffychat/widgets/avatar.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -76,10 +77,12 @@ extension LocalizedActiveFilter on ActiveFilter { class ChatList extends StatefulWidget { static BuildContext? contextForVoip; final String? activeChat; + final bool displayNavigationRail; const ChatList({ super.key, required this.activeChat, + this.displayNavigationRail = false, }); @override @@ -207,11 +210,7 @@ class ChatListController extends State if (result.error != null) return; } - void onChatTap(Room room, BuildContext context) async { - if (room.isSpace) { - setActiveSpace(room.id); - return; - } + void onChatTap(Room room) async { if (room.membership == Membership.invite) { final inviterId = room.getState(EventTypes.RoomMember, room.client.userID!)?.senderId; @@ -282,6 +281,10 @@ class ChatListController extends State return; } + if (room.isSpace) { + setActiveSpace(room.id); + return; + } // Share content into this room final shareContent = Matrix.of(context).shareContent; if (shareContent != null) { @@ -725,41 +728,138 @@ class ChatListController extends State super.dispose(); } - void chatContextAction(Room room) async { - final action = await showModalActionSheet( - context: context, - title: room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)), - actions: [ - SheetAction( - key: ChatContextAction.markUnread, - icon: room.markedUnread - ? Icons.mark_as_unread - : Icons.mark_as_unread_outlined, - label: room.markedUnread - ? L10n.of(context)!.markAsRead - : L10n.of(context)!.unread, + void chatContextAction( + Room room, + BuildContext posContext, [ + Room? space, + ]) async { + final overlay = + Overlay.of(posContext).context.findRenderObject() as RenderBox; + + final button = posContext.findRenderObject() as RenderBox; + + final position = RelativeRect.fromRect( + Rect.fromPoints( + button.localToGlobal(const Offset(0, -65), ancestor: overlay), + button.localToGlobal( + button.size.bottomRight(Offset.zero) + const Offset(-50, 0), + ancestor: overlay, ), - SheetAction( - key: ChatContextAction.favorite, - icon: room.isFavourite ? Icons.pin : Icons.pin_outlined, - label: room.isFavourite - ? L10n.of(context)!.unpin - : L10n.of(context)!.pin, + ), + Offset.zero & overlay.size, + ); + + final displayname = + room.getLocalizedDisplayname(MatrixLocals(L10n.of(context)!)); + + final action = await showMenu( + context: posContext, + position: position, + items: [ + PopupMenuItem( + enabled: false, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Avatar( + mxContent: room.avatar, + size: Avatar.defaultSize / 2, + name: displayname, + ), + const SizedBox(width: 12), + Text( + displayname, + style: + TextStyle(color: Theme.of(context).colorScheme.onSurface), + ), + ], + ), ), - SheetAction( - key: ChatContextAction.mute, - icon: room.pushRuleState == PushRuleState.notify - ? Icons.notifications_off_outlined - : Icons.notifications, - label: room.pushRuleState == PushRuleState.notify - ? L10n.of(context)!.muteChat - : L10n.of(context)!.unmuteChat, + const PopupMenuDivider(), + if (space != null) + PopupMenuItem( + value: ChatContextAction.goToSpace, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Avatar( + mxContent: space.avatar, + size: Avatar.defaultSize / 2, + name: space.getLocalizedDisplayname(), + ), + const SizedBox(width: 12), + Expanded( + child: Text( + L10n.of(context)! + .goToSpace(space.getLocalizedDisplayname()), + ), + ), + ], + ), + ), + PopupMenuItem( + value: ChatContextAction.mute, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + room.pushRuleState == PushRuleState.notify + ? Icons.notifications_off_outlined + : Icons.notifications_off, + ), + const SizedBox(width: 12), + Text( + room.pushRuleState == PushRuleState.notify + ? L10n.of(context)!.muteChat + : L10n.of(context)!.unmuteChat, + ), + ], + ), ), - SheetAction( - isDestructiveAction: true, - key: ChatContextAction.leave, - icon: Icons.delete_outlined, - label: L10n.of(context)!.leave, + PopupMenuItem( + value: ChatContextAction.markUnread, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon( + room.markedUnread + ? Icons.mark_as_unread + : Icons.mark_as_unread_outlined, + ), + const SizedBox(width: 12), + Text( + room.markedUnread + ? L10n.of(context)!.markAsRead + : L10n.of(context)!.markAsUnread, + ), + ], + ), + ), + PopupMenuItem( + value: ChatContextAction.favorite, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(room.isFavourite ? Icons.push_pin : Icons.push_pin_outlined), + const SizedBox(width: 12), + Text( + room.isFavourite + ? L10n.of(context)!.unpin + : L10n.of(context)!.pin, + ), + ], + ), + ), + PopupMenuItem( + value: ChatContextAction.leave, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.delete_outlined), + const SizedBox(width: 12), + Text(L10n.of(context)!.leave), + ], + ), ), ], ); @@ -767,10 +867,31 @@ class ChatListController extends State if (action == null) return; if (!mounted) return; + if (action == ChatContextAction.goToSpace) { + setActiveSpace(space!.id); + return; + } + + if (action == ChatContextAction.leave) { + final confirmed = await showOkCancelAlertDialog( + useRootNavigator: false, + context: context, + title: L10n.of(context)!.areYouSure, + okLabel: L10n.of(context)!.leave, + cancelLabel: L10n.of(context)!.no, + message: L10n.of(context)!.archiveRoomDescription, + isDestructiveAction: true, + ); + if (confirmed == OkCancelResult.cancel) return; + } + if (!mounted) return; + await showFutureLoadingDialog( context: context, - future: () { + future: () async { switch (action) { + case ChatContextAction.goToSpace: + return; case ChatContextAction.favorite: return room.setFavourite(!room.isFavourite); case ChatContextAction.markUnread: @@ -1015,6 +1136,7 @@ enum InviteActions { enum AddRoomType { chat, subspace } enum ChatContextAction { + goToSpace, favorite, markUnread, mute, diff --git a/lib/pages/chat_list/chat_list_body.dart b/lib/pages/chat_list/chat_list_body.dart index fda313141..47dcd7eb0 100644 --- a/lib/pages/chat_list/chat_list_body.dart +++ b/lib/pages/chat_list/chat_list_body.dart @@ -9,6 +9,7 @@ import 'package:fluffychat/pangea/widgets/chat_list/chat_list_body_text.dart'; import 'package:fluffychat/utils/adaptive_bottom_sheet.dart'; import 'package:fluffychat/utils/stream_extension.dart'; import 'package:fluffychat/widgets/avatar.dart'; +import 'package:fluffychat/widgets/hover_builder.dart'; import 'package:fluffychat/widgets/public_room_bottom_sheet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; @@ -25,17 +26,29 @@ class ChatListViewBody extends StatelessWidget { @override Widget build(BuildContext context) { + final client = Matrix.of(context).client; final activeSpace = controller.activeSpaceId; if (activeSpace != null) { return SpaceView( spaceId: activeSpace, onBack: controller.clearActiveSpace, - onChatTab: (room) => controller.onChatTap(room, context), - onChatContext: (room) => controller.chatContextAction(room), + onChatTab: (room) => controller.onChatTap(room), + onChatContext: (room, context) => + controller.chatContextAction(room, context), activeChat: controller.activeChat, toParentSpace: controller.setActiveSpace, ); } + final spaces = client.rooms.where((r) => r.isSpace); + final spaceDelegateCandidates = {}; + for (final space in spaces) { + for (final spaceChild in space.spaceChildren) { + final roomId = spaceChild.roomId; + if (roomId == null) continue; + spaceDelegateCandidates[roomId] = space; + } + } + final publicRooms = controller.roomSearchResult?.chunk .where((room) => room.roomType != 'm.space') .toList(); @@ -43,7 +56,6 @@ class ChatListViewBody extends StatelessWidget { .where((room) => room.roomType == 'm.space') .toList(); final userSearchResult = controller.userSearchResult; - final client = Matrix.of(context).client; const dummyChatCount = 4; final titleColor = Theme.of(context).textTheme.bodyLarge!.color!.withAlpha(100); @@ -60,18 +72,6 @@ class ChatListViewBody extends StatelessWidget { builder: (context, _) { final rooms = controller.filteredRooms; - final spaces = rooms.where((r) => r.isSpace); - final spaceDelegateCandidates = {}; - for (final space in spaces) { - spaceDelegateCandidates[space.id] = space; - for (final spaceChild in space.spaceChildren) { - final roomId = spaceChild.roomId; - if (roomId == null) continue; - spaceDelegateCandidates[roomId] = space; - } - } - final spaceDelegates = {}; - return SafeArea( child: CustomScrollView( controller: controller.scrollController, @@ -164,50 +164,66 @@ class ChatListViewBody extends StatelessWidget { ), shrinkWrap: true, scrollDirection: Axis.horizontal, - children: ActiveFilter.values + children: [ + ActiveFilter.allChats, + ActiveFilter.unread, + ActiveFilter.groups, + if (spaceDelegateCandidates.isNotEmpty && + !controller.widget.displayNavigationRail) + ActiveFilter.spaces, + ] .map( (filter) => Padding( padding: const EdgeInsets.symmetric(horizontal: 4), - child: InkWell( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, - ), - onTap: () => - controller.setActiveFilter(filter), - child: Container( - padding: const EdgeInsets.symmetric( - horizontal: 12, - vertical: 6, - ), - decoration: BoxDecoration( - color: filter == controller.activeFilter - ? Theme.of(context) - .colorScheme - .primary - : Theme.of(context) - .colorScheme - .secondaryContainer, + child: HoverBuilder( + builder: (context, hovered) => + AnimatedScale( + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + scale: hovered ? 1.1 : 1.0, + child: InkWell( borderRadius: BorderRadius.circular( AppConfig.borderRadius, ), - ), - alignment: Alignment.center, - child: Text( - filter.toLocalizedString(context), - style: TextStyle( - fontWeight: - filter == controller.activeFilter + onTap: () => + controller.setActiveFilter(filter), + child: Container( + padding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 6, + ), + decoration: BoxDecoration( + color: filter == + controller.activeFilter + ? Theme.of(context) + .colorScheme + .primary + : Theme.of(context) + .colorScheme + .secondaryContainer, + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), + ), + alignment: Alignment.center, + child: Text( + filter.toLocalizedString(context), + style: TextStyle( + fontWeight: filter == + controller.activeFilter ? FontWeight.bold : FontWeight.normal, - color: - filter == controller.activeFilter + color: filter == + controller.activeFilter ? Theme.of(context) .colorScheme .onPrimary : Theme.of(context) .colorScheme .onSecondaryContainer, + ), + ), ), ), ), @@ -305,27 +321,16 @@ class ChatListViewBody extends StatelessWidget { SliverList.builder( itemCount: rooms.length, itemBuilder: (BuildContext context, int i) { - var room = rooms[i]; - if (controller.activeFilter != ActiveFilter.groups) { - final parent = room.isSpace - ? room - : spaceDelegateCandidates[room.id]; - if (parent != null) { - if (spaceDelegates.contains(parent.id)) { - return const SizedBox.shrink(); - } - spaceDelegates.add(parent.id); - room = parent; - } - } - + final room = rooms[i]; + final space = spaceDelegateCandidates[room.id]; return ChatListItem( room, - lastEventRoom: rooms[i], + space: space, key: Key('chat_list_item_${room.id}'), filter: filter, - onTap: () => controller.onChatTap(room, context), - onLongPress: () => controller.chatContextAction(room), + onTap: () => controller.onChatTap(room), + onLongPress: (context) => + controller.chatContextAction(room, context, space), activeChat: controller.activeChat == room.id, ); }, diff --git a/lib/pages/chat_list/chat_list_item.dart b/lib/pages/chat_list/chat_list_item.dart index cf92ba9cd..acc8270ca 100644 --- a/lib/pages/chat_list/chat_list_item.dart +++ b/lib/pages/chat_list/chat_list_item.dart @@ -19,9 +19,9 @@ enum ArchivedRoomAction { delete, rejoin } class ChatListItem extends StatelessWidget { final Room room; - final Room? lastEventRoom; + final Room? space; final bool activeChat; - final void Function()? onLongPress; + final void Function(BuildContext context)? onLongPress; final void Function()? onForget; final void Function() onTap; final String? filter; @@ -33,7 +33,7 @@ class ChatListItem extends StatelessWidget { this.onLongPress, this.onForget, this.filter, - this.lastEventRoom, + this.space, super.key, }); @@ -66,21 +66,19 @@ class ChatListItem extends StatelessWidget { @override Widget build(BuildContext context) { final isMuted = room.pushRuleState != PushRuleState.notify; - final lastEventRoom = this.lastEventRoom ?? room; - final typingText = lastEventRoom.getLocalizedTypingText(context); - final lastEvent = lastEventRoom.lastEvent; + final typingText = room.getLocalizedTypingText(context); + final lastEvent = room.lastEvent; final ownMessage = lastEvent?.senderId == room.client.userID; - final unread = - lastEventRoom.isUnread || lastEventRoom.membership == Membership.invite; + final unread = room.isUnread || room.membership == Membership.invite; final theme = Theme.of(context); final directChatMatrixId = room.directChatMatrixID; final isDirectChat = directChatMatrixId != null; - final unreadBubbleSize = unread || lastEventRoom.hasNewMessages - ? lastEventRoom.notificationCount > 0 + final unreadBubbleSize = unread || room.hasNewMessages + ? room.notificationCount > 0 ? 20.0 : 14.0 : 0.0; - final hasNotifications = lastEventRoom.notificationCount > 0; + final hasNotifications = room.notificationCount > 0; final backgroundColor = activeChat ? theme.colorScheme.secondaryContainer : null; final displayname = room.getLocalizedDisplayname( @@ -94,6 +92,7 @@ class ChatListItem extends StatelessWidget { final needLastEventSender = lastEvent == null ? false : room.getState(EventTypes.RoomMember, lastEvent.senderId) == null; + final space = this.space; return Padding( padding: const EdgeInsets.symmetric( @@ -107,48 +106,93 @@ class ChatListItem extends StatelessWidget { child: FutureBuilder( future: room.loadHeroUsers(), builder: (context, snapshot) => HoverBuilder( - builder: (context, hovered) => ListTile( + builder: (context, listTileHovered) => ListTile( visualDensity: const VisualDensity(vertical: -0.5), contentPadding: const EdgeInsets.symmetric(horizontal: 8), - onLongPress: onLongPress, - leading: Stack( - clipBehavior: Clip.none, - children: [ - HoverBuilder( - builder: (context, hovered) => AnimatedScale( - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - scale: hovered ? 1.1 : 1.0, - child: Avatar( - borderRadius: room.isSpace - ? BorderRadius.circular(AppConfig.borderRadius / 3) - : null, - mxContent: room.avatar, - name: displayname, - presenceUserId: directChatMatrixId, - presenceBackgroundColor: backgroundColor, - onTap: onLongPress, - ), - ), - ), - Positioned( - bottom: -2, - right: -2, - child: AnimatedScale( - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - scale: (hovered) ? 1.0 : 0.0, - child: Material( - color: backgroundColor, - borderRadius: BorderRadius.circular(16), - child: const Icon( - Icons.check_circle_outlined, - size: 18, + onLongPress: () => onLongPress?.call(context), + leading: HoverBuilder( + builder: (context, hovered) => AnimatedScale( + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + scale: hovered ? 1.1 : 1.0, + child: SizedBox( + width: Avatar.defaultSize, + height: Avatar.defaultSize, + child: Stack( + children: [ + if (space != null) + Positioned( + top: 0, + left: 0, + child: Avatar( + border: BorderSide( + width: 2, + color: backgroundColor ?? + Theme.of(context).colorScheme.surface, + ), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius / 4, + ), + mxContent: space.avatar, + size: Avatar.defaultSize * 0.75, + name: space.getLocalizedDisplayname(), + onTap: () => onLongPress?.call(context), + ), + ), + Positioned( + bottom: 0, + right: 0, + child: Avatar( + border: space == null + ? room.isSpace + ? BorderSide( + width: 0, + color: Theme.of(context) + .colorScheme + .outline, + ) + : null + : BorderSide( + width: 2, + color: backgroundColor ?? + Theme.of(context).colorScheme.surface, + ), + borderRadius: room.isSpace + ? BorderRadius.circular( + AppConfig.borderRadius / 4, + ) + : null, + mxContent: room.avatar, + size: space != null + ? Avatar.defaultSize * 0.75 + : Avatar.defaultSize, + name: displayname, + presenceUserId: directChatMatrixId, + presenceBackgroundColor: backgroundColor, + onTap: () => onLongPress?.call(context), + ), ), - ), + Positioned( + top: 0, + right: 0, + child: AnimatedScale( + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + scale: listTileHovered ? 1.0 : 0.0, + child: Material( + color: backgroundColor, + borderRadius: BorderRadius.circular(16), + child: const Icon( + Icons.arrow_drop_down_circle_outlined, + size: 18, + ), + ), + ), + ), + ], ), ), - ], + ), ), title: Row( children: [ @@ -207,20 +251,6 @@ class ChatListItem extends StatelessWidget { subtitle: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - if (room.isSpace) ...[ - room.id != lastEventRoom.id && - lastEventRoom.isUnreadOrInvited - ? Avatar( - mxContent: lastEventRoom.avatar, - name: lastEventRoom.name, - size: 18, - ) - : const Icon( - Icons.workspaces_outlined, - size: 18, - ), - const SizedBox(width: 4), - ], if (typingText.isEmpty && ownMessage && room.lastEvent!.status.isSending) ...[ @@ -245,7 +275,7 @@ class ChatListItem extends StatelessWidget { ), ), Expanded( - child: room.isSpace && !lastEventRoom.isUnreadOrInvited + child: room.isSpace && room.membership == Membership.join ? Text( L10n.of(context)!.countChatsAndCountParticipants( room.spaceChildren.length.toString(), @@ -308,10 +338,9 @@ class ChatListItem extends StatelessWidget { maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle( - fontWeight: - unread || lastEventRoom.hasNewMessages - ? FontWeight.bold - : null, + fontWeight: unread || room.hasNewMessages + ? FontWeight.bold + : null, color: theme.colorScheme.onSurfaceVariant, decoration: room.lastEvent?.redacted == true ? TextDecoration.lineThrough @@ -339,9 +368,7 @@ class ChatListItem extends StatelessWidget { width: !hasNotifications && !unread && !room.hasNewMessages ? 0 : (unreadBubbleSize - 9) * - lastEventRoom.notificationCount - .toString() - .length + + room.notificationCount.toString().length + 9, decoration: BoxDecoration( color: room.highlightCount > 0 || @@ -356,7 +383,7 @@ class ChatListItem extends StatelessWidget { child: Center( child: hasNotifications ? Text( - lastEventRoom.notificationCount.toString(), + room.notificationCount.toString(), style: TextStyle( color: room.highlightCount > 0 ? Colors.white diff --git a/lib/pages/chat_list/nav_rail_item.dart b/lib/pages/chat_list/nav_rail_item.dart new file mode 100644 index 000000000..d09659f88 --- /dev/null +++ b/lib/pages/chat_list/nav_rail_item.dart @@ -0,0 +1,95 @@ +import 'package:flutter/material.dart'; + +import 'package:fluffychat/config/app_config.dart'; +import '../../config/themes.dart'; + +class NaviRailItem extends StatefulWidget { + final String toolTip; + final bool isSelected; + final void Function() onTap; + final Widget icon; + final Widget? selectedIcon; + + const NaviRailItem({ + required this.toolTip, + required this.isSelected, + required this.onTap, + required this.icon, + this.selectedIcon, + super.key, + }); + + @override + State createState() => _NaviRailItemState(); +} + +class _NaviRailItemState extends State { + bool _hovered = false; + + void _onHover(bool hover) { + if (hover == _hovered) return; + setState(() { + _hovered = hover; + }); + } + + @override + Widget build(BuildContext context) { + final borderRadius = BorderRadius.circular(AppConfig.borderRadius); + return SizedBox( + height: 64, + width: 64, + child: Stack( + children: [ + Positioned( + top: 16, + bottom: 16, + left: 0, + child: AnimatedContainer( + width: widget.isSelected ? 4 : 0, + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary, + borderRadius: const BorderRadius.only( + topRight: Radius.circular(90), + bottomRight: Radius.circular(90), + ), + ), + ), + ), + Center( + child: AnimatedScale( + scale: _hovered ? 1.2 : 1.0, + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + child: Material( + borderRadius: borderRadius, + color: widget.isSelected + ? Theme.of(context).colorScheme.primaryContainer + : Theme.of(context).colorScheme.surface, + child: Tooltip( + message: widget.toolTip, + child: InkWell( + borderRadius: borderRadius, + onTap: widget.onTap, + onHover: _onHover, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 8.0, + ), + child: widget.isSelected + ? widget.selectedIcon ?? widget.icon + : widget.icon, + ), + ), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/chat_list/navi_rail_item.dart b/lib/pages/chat_list/navi_rail_item.dart index d09659f88..66ad7c041 100644 --- a/lib/pages/chat_list/navi_rail_item.dart +++ b/lib/pages/chat_list/navi_rail_item.dart @@ -1,14 +1,20 @@ import 'package:flutter/material.dart'; +import 'package:badges/badges.dart'; +import 'package:matrix/matrix.dart'; + import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/widgets/hover_builder.dart'; +import 'package:fluffychat/widgets/unread_rooms_badge.dart'; import '../../config/themes.dart'; -class NaviRailItem extends StatefulWidget { +class NaviRailItem extends StatelessWidget { final String toolTip; final bool isSelected; final void Function() onTap; final Widget icon; final Widget? selectedIcon; + final bool Function(Room)? unreadBadgeFilter; const NaviRailItem({ required this.toolTip, @@ -16,80 +22,78 @@ class NaviRailItem extends StatefulWidget { required this.onTap, required this.icon, this.selectedIcon, + this.unreadBadgeFilter, super.key, }); - - @override - State createState() => _NaviRailItemState(); -} - -class _NaviRailItemState extends State { - bool _hovered = false; - - void _onHover(bool hover) { - if (hover == _hovered) return; - setState(() { - _hovered = hover; - }); - } - @override Widget build(BuildContext context) { final borderRadius = BorderRadius.circular(AppConfig.borderRadius); - return SizedBox( - height: 64, - width: 64, - child: Stack( - children: [ - Positioned( - top: 16, - bottom: 16, - left: 0, - child: AnimatedContainer( - width: widget.isSelected ? 4 : 0, - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primary, - borderRadius: const BorderRadius.only( - topRight: Radius.circular(90), - bottomRight: Radius.circular(90), - ), - ), - ), - ), - Center( - child: AnimatedScale( - scale: _hovered ? 1.2 : 1.0, - duration: FluffyThemes.animationDuration, - curve: FluffyThemes.animationCurve, - child: Material( - borderRadius: borderRadius, - color: widget.isSelected - ? Theme.of(context).colorScheme.primaryContainer - : Theme.of(context).colorScheme.surface, - child: Tooltip( - message: widget.toolTip, - child: InkWell( - borderRadius: borderRadius, - onTap: widget.onTap, - onHover: _onHover, - child: Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8.0, - vertical: 8.0, - ), - child: widget.isSelected - ? widget.selectedIcon ?? widget.icon - : widget.icon, + final icon = isSelected ? selectedIcon ?? this.icon : this.icon; + final unreadBadgeFilter = this.unreadBadgeFilter; + return HoverBuilder( + builder: (context, hovered) { + return SizedBox( + height: 64, + width: 64, + child: Stack( + children: [ + Positioned( + top: 16, + bottom: 16, + left: 0, + child: AnimatedContainer( + width: isSelected ? 4 : 0, + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary, + borderRadius: const BorderRadius.only( + topRight: Radius.circular(90), + bottomRight: Radius.circular(90), ), ), ), ), - ), + Center( + child: AnimatedScale( + scale: hovered ? 1.2 : 1.0, + duration: FluffyThemes.animationDuration, + curve: FluffyThemes.animationCurve, + child: Material( + borderRadius: borderRadius, + color: isSelected + ? Theme.of(context).colorScheme.primaryContainer + : Theme.of(context).colorScheme.surface, + child: Tooltip( + message: toolTip, + child: InkWell( + borderRadius: borderRadius, + onTap: onTap, + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8.0, + vertical: 8.0, + ), + child: unreadBadgeFilter == null + ? icon + : UnreadRoomsBadge( + filter: unreadBadgeFilter, + badgePosition: BadgePosition.topEnd( + top: -12, + end: -8, + ), + child: icon, + ), + ), + ), + ), + ), + ), + ), + ], ), - ], - ), + ); + }, ); } } diff --git a/lib/pages/chat_list/space_view.dart b/lib/pages/chat_list/space_view.dart index 7191556c5..6b11eae26 100644 --- a/lib/pages/chat_list/space_view.dart +++ b/lib/pages/chat_list/space_view.dart @@ -18,7 +18,7 @@ class SpaceView extends StatefulWidget { final void Function() onBack; final void Function(String spaceId) toParentSpace; final void Function(Room room) onChatTab; - final void Function(Room room) onChatContext; + final void Function(Room room, BuildContext context) onChatContext; final String? activeChat; const SpaceView({ @@ -163,316 +163,315 @@ class _SpaceViewState extends State { final room = Matrix.of(context).client.getRoomById(widget.spaceId); final displayname = room?.getLocalizedDisplayname() ?? L10n.of(context)!.nothingFound; - return Scaffold( - appBar: AppBar( - leading: Center( - child: CloseButton( - onPressed: widget.onBack, + return PopScope( + canPop: false, + onPopInvoked: (_) => widget.onBack(), + child: Scaffold( + appBar: AppBar( + leading: Center( + child: CloseButton( + onPressed: widget.onBack, + ), ), - ), - titleSpacing: 0, - title: ListTile( - contentPadding: EdgeInsets.zero, - leading: Avatar( - mxContent: room?.avatar, - name: displayname, - borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), - ), - title: Text( - displayname, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - subtitle: room == null - ? null - : Text( - L10n.of(context)!.countChatsAndCountParticipants( - room.spaceChildren.length, - room.summary.mJoinedMemberCount ?? 1, + titleSpacing: 0, + title: ListTile( + contentPadding: EdgeInsets.zero, + leading: Avatar( + mxContent: room?.avatar, + name: displayname, + borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2), + ), + title: Text( + displayname, + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + subtitle: room == null + ? null + : Text( + L10n.of(context)!.countChatsAndCountParticipants( + room.spaceChildren.length, + room.summary.mJoinedMemberCount ?? 1, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - actions: [ - PopupMenuButton( - onSelected: _onSpaceAction, - itemBuilder: (context) => [ - PopupMenuItem( - value: SpaceActions.settings, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.settings_outlined), - const SizedBox(width: 12), - Text(L10n.of(context)!.settings), - ], - ), - ), - PopupMenuItem( - value: SpaceActions.invite, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.person_add_outlined), - const SizedBox(width: 12), - Text(L10n.of(context)!.invite), - ], - ), - ), - PopupMenuItem( - value: SpaceActions.leave, - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon(Icons.delete_outlined), - const SizedBox(width: 12), - Text(L10n.of(context)!.leave), - ], - ), - ), - ], ), - ], - ), - body: room == null - ? const Center( - child: Icon( - Icons.search_outlined, - size: 80, - ), - ) - : StreamBuilder( - stream: room.client.onSync.stream - .where((s) => s.hasRoomUpdate) - .rateLimit(const Duration(seconds: 1)), - builder: (context, snapshot) { - final joinedRooms = room.spaceChildren - .map((child) { - final roomId = child.roomId; - if (roomId == null) return null; - return room.client.getRoomById(roomId); - }) - .whereType() - .where((room) => room.membership != Membership.leave) - .toList(); - - // Sort rooms by last activity - joinedRooms.sort( - (b, a) => (a.lastEvent?.originServerTs ?? - DateTime.fromMillisecondsSinceEpoch(0)) - .compareTo( - b.lastEvent?.originServerTs ?? - DateTime.fromMillisecondsSinceEpoch(0), + actions: [ + PopupMenuButton( + onSelected: _onSpaceAction, + itemBuilder: (context) => [ + PopupMenuItem( + value: SpaceActions.settings, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.settings_outlined), + const SizedBox(width: 12), + Text(L10n.of(context)!.settings), + ], ), - ); + ), + PopupMenuItem( + value: SpaceActions.invite, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.person_add_outlined), + const SizedBox(width: 12), + Text(L10n.of(context)!.invite), + ], + ), + ), + PopupMenuItem( + value: SpaceActions.leave, + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + const Icon(Icons.delete_outlined), + const SizedBox(width: 12), + Text(L10n.of(context)!.leave), + ], + ), + ), + ], + ), + ], + ), + body: room == null + ? const Center( + child: Icon( + Icons.search_outlined, + size: 80, + ), + ) + : StreamBuilder( + stream: room.client.onSync.stream + .where((s) => s.hasRoomUpdate) + .rateLimit(const Duration(seconds: 1)), + builder: (context, snapshot) { + final childrenIds = room.spaceChildren + .map((c) => c.roomId) + .whereType() + .toSet(); - final joinedParents = room.spaceParents - .map((parent) { - final roomId = parent.roomId; - if (roomId == null) return null; - return room.client.getRoomById(roomId); - }) - .whereType() - .toList(); - final filter = _filterController.text.trim().toLowerCase(); - return CustomScrollView( - slivers: [ - SliverAppBar( - floating: true, - toolbarHeight: 72, - scrolledUnderElevation: 0, - backgroundColor: Colors.transparent, - automaticallyImplyLeading: false, - title: TextField( - controller: _filterController, - onChanged: (_) => setState(() {}), - textInputAction: TextInputAction.search, - decoration: InputDecoration( - fillColor: - Theme.of(context).colorScheme.secondaryContainer, - border: OutlineInputBorder( - borderSide: BorderSide.none, - borderRadius: BorderRadius.circular(99), - ), - contentPadding: EdgeInsets.zero, - hintText: L10n.of(context)!.search, - hintStyle: TextStyle( - color: Theme.of(context) + final joinedRooms = room.client.rooms + .where((room) => childrenIds.remove(room.id)) + .toList(); + + final joinedParents = room.spaceParents + .map((parent) { + final roomId = parent.roomId; + if (roomId == null) return null; + return room.client.getRoomById(roomId); + }) + .whereType() + .toList(); + final filter = _filterController.text.trim().toLowerCase(); + return CustomScrollView( + slivers: [ + SliverAppBar( + floating: true, + toolbarHeight: 72, + scrolledUnderElevation: 0, + backgroundColor: Colors.transparent, + automaticallyImplyLeading: false, + title: TextField( + controller: _filterController, + onChanged: (_) => setState(() {}), + textInputAction: TextInputAction.search, + decoration: InputDecoration( + fillColor: Theme.of(context) .colorScheme - .onPrimaryContainer, - fontWeight: FontWeight.normal, - ), - floatingLabelBehavior: FloatingLabelBehavior.never, - prefixIcon: IconButton( - onPressed: () {}, - icon: Icon( - Icons.search_outlined, + .secondaryContainer, + border: OutlineInputBorder( + borderSide: BorderSide.none, + borderRadius: BorderRadius.circular(99), + ), + contentPadding: EdgeInsets.zero, + hintText: L10n.of(context)!.search, + hintStyle: TextStyle( color: Theme.of(context) .colorScheme .onPrimaryContainer, + fontWeight: FontWeight.normal, + ), + floatingLabelBehavior: FloatingLabelBehavior.never, + prefixIcon: IconButton( + onPressed: () {}, + icon: Icon( + Icons.search_outlined, + color: Theme.of(context) + .colorScheme + .onPrimaryContainer, + ), ), ), ), ), - ), - SliverList.builder( - itemCount: joinedParents.length, - itemBuilder: (context, i) { - final displayname = - joinedParents[i].getLocalizedDisplayname(); - return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 1, - ), - child: Material( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), - clipBehavior: Clip.hardEdge, - child: ListTile( - minVerticalPadding: 0, - leading: Icon( - Icons.adaptive.arrow_back_outlined, - size: 16, - ), - title: Row( - children: [ - Avatar( - mxContent: joinedParents[i].avatar, - name: displayname, - size: Avatar.defaultSize / 2, - borderRadius: BorderRadius.circular( - AppConfig.borderRadius / 4, - ), - ), - const SizedBox(width: 8), - Expanded(child: Text(displayname)), - ], - ), - onTap: () => - widget.toParentSpace(joinedParents[i].id), + SliverList.builder( + itemCount: joinedParents.length, + itemBuilder: (context, i) { + final displayname = + joinedParents[i].getLocalizedDisplayname(); + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 1, ), - ), - ); - }, - ), - SliverList.builder( - itemCount: joinedRooms.length + 1, - itemBuilder: (context, i) { - if (i == 0) { - return SearchTitle( - title: L10n.of(context)!.joinedChats, - icon: const Icon(Icons.chat_outlined), - ); - } - i--; - final room = joinedRooms[i]; - return ChatListItem( - room, - filter: filter, - onTap: () => widget.onChatTab(room), - onLongPress: () => widget.onChatContext(room), - activeChat: widget.activeChat == room.id, - ); - }, - ), - SliverList.builder( - itemCount: _discoveredChildren.length + 2, - itemBuilder: (context, i) { - if (i == 0) { - return SearchTitle( - title: L10n.of(context)!.discover, - icon: const Icon(Icons.explore_outlined), - ); - } - i--; - if (i == _discoveredChildren.length) { - if (_noMoreRooms) { - return Padding( - padding: const EdgeInsets.all(12.0), - child: Center( - child: Text( - L10n.of(context)!.noMoreChatsFound, - style: const TextStyle(fontSize: 13), + child: Material( + borderRadius: + BorderRadius.circular(AppConfig.borderRadius), + clipBehavior: Clip.hardEdge, + child: ListTile( + minVerticalPadding: 0, + leading: Icon( + Icons.adaptive.arrow_back_outlined, + size: 16, ), + title: Row( + children: [ + Avatar( + mxContent: joinedParents[i].avatar, + name: displayname, + size: Avatar.defaultSize / 2, + borderRadius: BorderRadius.circular( + AppConfig.borderRadius / 4, + ), + ), + const SizedBox(width: 8), + Expanded(child: Text(displayname)), + ], + ), + onTap: () => + widget.toParentSpace(joinedParents[i].id), + ), + ), + ); + }, + ), + SliverList.builder( + itemCount: joinedRooms.length + 1, + itemBuilder: (context, i) { + if (i == 0) { + return SearchTitle( + title: L10n.of(context)!.joinedChats, + icon: const Icon(Icons.chat_outlined), + ); + } + i--; + final room = joinedRooms[i]; + return ChatListItem( + room, + filter: filter, + onTap: () => widget.onChatTab(room), + onLongPress: (context) => widget.onChatContext( + room, + context, + ), + activeChat: widget.activeChat == room.id, + ); + }, + ), + SliverList.builder( + itemCount: _discoveredChildren.length + 2, + itemBuilder: (context, i) { + if (i == 0) { + return SearchTitle( + title: L10n.of(context)!.discover, + icon: const Icon(Icons.explore_outlined), + ); + } + i--; + if (i == _discoveredChildren.length) { + if (_noMoreRooms) { + return Padding( + padding: const EdgeInsets.all(12.0), + child: Center( + child: Text( + L10n.of(context)!.noMoreChatsFound, + style: const TextStyle(fontSize: 13), + ), + ), + ); + } + return Padding( + padding: const EdgeInsets.symmetric( + horizontal: 12.0, + vertical: 2.0, + ), + child: TextButton( + onPressed: _isLoading ? null : _loadHierarchy, + child: _isLoading + ? LinearProgressIndicator( + borderRadius: BorderRadius.circular( + AppConfig.borderRadius, + ), + ) + : Text(L10n.of(context)!.loadMore), ), ); } + final item = _discoveredChildren[i]; + final displayname = item.name ?? + item.canonicalAlias ?? + L10n.of(context)!.emptyChat; + if (!displayname.toLowerCase().contains(filter)) { + return const SizedBox.shrink(); + } return Padding( padding: const EdgeInsets.symmetric( - horizontal: 12.0, - vertical: 2.0, + horizontal: 8, + vertical: 1, ), - child: TextButton( - onPressed: _isLoading ? null : _loadHierarchy, - child: _isLoading - ? LinearProgressIndicator( - borderRadius: BorderRadius.circular( - AppConfig.borderRadius, + child: Material( + borderRadius: + BorderRadius.circular(AppConfig.borderRadius), + clipBehavior: Clip.hardEdge, + child: ListTile( + onTap: () => _joinChildRoom(item), + leading: Avatar( + mxContent: item.avatarUrl, + name: displayname, + borderRadius: item.roomType == 'm.space' + ? BorderRadius.circular( + AppConfig.borderRadius / 2, + ) + : null, + ), + title: Row( + children: [ + Expanded( + child: Text( + displayname, + maxLines: 1, + overflow: TextOverflow.ellipsis, ), - ) - : Text(L10n.of(context)!.loadMore), + ), + const SizedBox(width: 8), + const Icon( + Icons.add_circle_outline_outlined, + ), + ], + ), + subtitle: Text( + item.topic ?? + L10n.of(context)!.countParticipants( + item.numJoinedMembers, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), ), ); - } - final item = _discoveredChildren[i]; - final displayname = item.name ?? - item.canonicalAlias ?? - L10n.of(context)!.emptyChat; - if (!displayname.toLowerCase().contains(filter)) { - return const SizedBox.shrink(); - } - return Padding( - padding: const EdgeInsets.symmetric( - horizontal: 8, - vertical: 1, - ), - child: Material( - borderRadius: - BorderRadius.circular(AppConfig.borderRadius), - clipBehavior: Clip.hardEdge, - child: ListTile( - onTap: () => _joinChildRoom(item), - leading: Avatar( - mxContent: item.avatarUrl, - name: displayname, - borderRadius: item.roomType == 'm.space' - ? BorderRadius.circular( - AppConfig.borderRadius / 2, - ) - : null, - ), - title: Row( - children: [ - Expanded( - child: Text( - displayname, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - const SizedBox(width: 8), - const Icon(Icons.add_circle_outline_outlined), - ], - ), - subtitle: Text( - item.topic ?? - L10n.of(context)!.countParticipants( - item.numJoinedMembers, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ), - ); - }, - ), - ], - ); - }, - ), + }, + ), + ], + ); + }, + ), + ), ); } } diff --git a/lib/widgets/avatar.dart b/lib/widgets/avatar.dart index edf53cc9a..2db15e96f 100644 --- a/lib/widgets/avatar.dart +++ b/lib/widgets/avatar.dart @@ -15,6 +15,7 @@ class Avatar extends StatelessWidget { final Color? presenceBackgroundColor; final BorderRadius? borderRadius; final IconData? icon; + final BorderSide? border; const Avatar({ this.mxContent, @@ -25,6 +26,7 @@ class Avatar extends StatelessWidget { this.presenceUserId, this.presenceBackgroundColor, this.borderRadius, + this.border, this.icon, super.key, }); @@ -65,10 +67,7 @@ class Avatar extends StatelessWidget { color: color, shape: RoundedRectangleBorder( borderRadius: borderRadius, - side: BorderSide( - width: 0, - color: Theme.of(context).dividerColor, - ), + side: border ?? BorderSide.none, ), clipBehavior: Clip.hardEdge, child: noPic diff --git a/lib/widgets/layouts/two_column_layout.dart b/lib/widgets/layouts/two_column_layout.dart index c270f120a..a6f4c8bdf 100644 --- a/lib/widgets/layouts/two_column_layout.dart +++ b/lib/widgets/layouts/two_column_layout.dart @@ -3,11 +3,13 @@ import 'package:flutter/material.dart'; class TwoColumnLayout extends StatelessWidget { final Widget mainView; final Widget sideView; + final bool displayNavigationRail; const TwoColumnLayout({ super.key, required this.mainView, required this.sideView, + required this.displayNavigationRail, }); @override Widget build(BuildContext context) { @@ -18,7 +20,7 @@ class TwoColumnLayout extends StatelessWidget { Container( clipBehavior: Clip.antiAlias, decoration: const BoxDecoration(), - width: 384.0, + width: 360.0 + (displayNavigationRail ? 64 : 0), child: mainView, ), Container( diff --git a/lib/widgets/unread_rooms_badge.dart b/lib/widgets/unread_rooms_badge.dart index 9104b0f07..4704e6c26 100644 --- a/lib/widgets/unread_rooms_badge.dart +++ b/lib/widgets/unread_rooms_badge.dart @@ -30,12 +30,6 @@ class UnreadRoomsBadge extends StatelessWidget { // Pangea# builder: (context, _) { // #Pangea - // final unreadCount = Matrix.of(context) - // .client - // .rooms - // .where(filter) - // .where((r) => (r.isUnread || r.membership == Membership.invite)) - // .length; final unreadCounts = Matrix.of(context) .client .rooms @@ -44,6 +38,12 @@ class UnreadRoomsBadge extends StatelessWidget { .map((r) => r.notificationCount); final unreadCount = unreadCounts.isEmpty ? 0 : unreadCounts.reduce((a, b) => a + b); + // final unreadCount = Matrix.of(context) + // .client + // .rooms + // .where(filter) + // .where((r) => (r.isUnread || r.membership == Membership.invite)) + // .length; // Pangea# return b.Badge( badgeStyle: b.BadgeStyle( diff --git a/linux/my_application.cc b/linux/my_application.cc index 0abe77c60..c185bcd78 100644 --- a/linux/my_application.cc +++ b/linux/my_application.cc @@ -60,7 +60,7 @@ static void my_application_activate(GApplication* application) { gtk_window_set_title(window, "FluffyChat"); } - gtk_window_set_default_size(window, 800, 600); + gtk_window_set_default_size(window, 864, 680); gtk_widget_show(GTK_WIDGET(window)); g_autoptr(FlDartProject) project = fl_dart_project_new();