From 77661f1eadb05e740487688144da53d3c783adb7 Mon Sep 17 00:00:00 2001 From: TheOneWithTheBraid Date: Tue, 12 Jul 2022 17:44:23 +0200 Subject: [PATCH] feat: space navigation enhancements - add ravigation rail on large screens - refactor space hierarchy dummy code - add material 3 like radius to spaces drawer - rename column router to catch the application-specific code inside Signed-off-by: TheOneWithTheBraid --- lib/config/routes.dart | 8 +- .../chat_list/chat_list_body_container.dart | 37 +++++++++ lib/pages/chat_list/chat_list_drawer.dart | 4 + .../chat_list/chat_list_navigation_rail.dart | 76 +++++++++++++++++++ lib/pages/chat_list/chat_list_view.dart | 6 +- lib/pages/chat_list/spaces_drawer.dart | 14 +++- lib/widgets/layouts/two_column_layout.dart | 54 ++++++++----- 7 files changed, 169 insertions(+), 30 deletions(-) create mode 100644 lib/pages/chat_list/chat_list_body_container.dart create mode 100644 lib/pages/chat_list/chat_list_navigation_rail.dart diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 6eb061a6..7adce503 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -113,7 +113,7 @@ class AppRoutes { List get _tabletRoutes => [ VNester( path: '/rooms', - widgetBuilder: (child) => TwoColumnLayout( + widgetBuilder: (child) => FluffyChatTwoColumnLayout( mainView: const ChatList(), sideView: child, ), @@ -198,7 +198,7 @@ class AppRoutes { ), VWidget( path: '/rooms', - widget: const TwoColumnLayout( + widget: const FluffyChatTwoColumnLayout( mainView: ChatList(), sideView: EmptyPage(), ), @@ -206,7 +206,7 @@ class AppRoutes { stackedRoutes: [ VNester( path: '/settings', - widgetBuilder: (child) => TwoColumnLayout( + widgetBuilder: (child) => FluffyChatTwoColumnLayout( mainView: const Settings(), sideView: child, ), @@ -222,7 +222,7 @@ class AppRoutes { ), VWidget( path: '/archive', - widget: const TwoColumnLayout( + widget: const FluffyChatTwoColumnLayout( mainView: Archive(), sideView: EmptyPage(), ), diff --git a/lib/pages/chat_list/chat_list_body_container.dart b/lib/pages/chat_list/chat_list_body_container.dart new file mode 100644 index 00000000..8d07f049 --- /dev/null +++ b/lib/pages/chat_list/chat_list_body_container.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +import 'package:fluffychat/pages/chat_list/chat_list.dart'; +import 'chat_list_body.dart'; +import 'chat_list_navigation_rail.dart'; + +class ChatListBodyContainer extends StatelessWidget { + final ChatListController controller; + + const ChatListBodyContainer(this.controller, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return LayoutBuilder(builder: (context, constraints) { + if (MediaQuery.of(context).size.width > 1024) { + return Row( + mainAxisSize: MainAxisSize.max, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 72.0, + height: constraints.maxHeight, + child: ChatListNavigationRail(controller), + ), + Container( + width: 1.0, + color: Theme.of(context).dividerColor, + ), + Expanded(child: ChatListViewBody(controller)), + ], + ); + } + return ChatListViewBody(controller); + }); + } +} diff --git a/lib/pages/chat_list/chat_list_drawer.dart b/lib/pages/chat_list/chat_list_drawer.dart index 73242d4b..75b7f564 100644 --- a/lib/pages/chat_list/chat_list_drawer.dart +++ b/lib/pages/chat_list/chat_list_drawer.dart @@ -12,10 +12,14 @@ import '../../config/app_config.dart'; class ChatListDrawer extends StatelessWidget { final ChatListController controller; + const ChatListDrawer(this.controller, {Key? key}) : super(key: key); @override Widget build(BuildContext context) => Drawer( + // TODO(TheOneWithTheBraid): remove once migrated to MD3 + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.horizontal(right: Radius.circular(32))), child: SafeArea( child: Column( children: [ diff --git a/lib/pages/chat_list/chat_list_navigation_rail.dart b/lib/pages/chat_list/chat_list_navigation_rail.dart new file mode 100644 index 00000000..caeecaba --- /dev/null +++ b/lib/pages/chat_list/chat_list_navigation_rail.dart @@ -0,0 +1,76 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:vrouter/vrouter.dart'; + +import 'package:fluffychat/pages/chat_list/chat_list.dart'; +import 'package:fluffychat/pages/chat_list/spaces_drawer.dart'; +import 'package:fluffychat/widgets/avatar.dart'; + +class ChatListNavigationRail extends StatelessWidget { + final ChatListController controller; + + const ChatListNavigationRail(this.controller, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + final spaceHierarchy = SpacesDrawer.getSpaceHierarchy(controller); + + final currentIndex = controller.spacesEntries.indexWhere((space) => + controller.activeSpacesEntry.runtimeType == space.runtimeType && + (controller.activeSpaceId == space.getSpace(context)?.id)); + + return SingleChildScrollView( + child: SizedBox( + // wasted 1.5 h for finding out the proper sizing mechanism... + height: spaceHierarchy.length * 44 + 64 + 8, + child: NavigationRail( + destinations: List.generate(spaceHierarchy.length + 1, (i) { + if (i == spaceHierarchy.length) { + return NavigationRailDestination( + icon: Tooltip( + message: L10n.of(context)!.archive, + child: const Icon( + Icons.archive_outlined, + ), + ), + label: Text(L10n.of(context)!.archive), + ); + } + final space = spaceHierarchy.keys.toList()[i]; + final room = space.getSpace(context); + final active = currentIndex == i; + return NavigationRailDestination( + icon: Tooltip( + message: space.getName(context), + child: room == null + ? space.getIcon(active) + : Avatar( + size: Avatar.defaultSize / 2, + mxContent: room.avatar, + name: space.getName(context), + )), + label: Text( + space.getName(context), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ); + }), + selectedIndex: currentIndex, + onDestinationSelected: (index) { + if (index == spaceHierarchy.length) { + VRouter.of(context).to('/archive'); + } else { + controller.setActiveSpacesEntry( + context, + spaceHierarchy.keys.toList()[index], + ); + } + }, + labelType: NavigationRailLabelType.selected, + ), + ), + ); + } +} diff --git a/lib/pages/chat_list/chat_list_view.dart b/lib/pages/chat_list/chat_list_view.dart index d7881af1..afdc81c2 100644 --- a/lib/pages/chat_list/chat_list_view.dart +++ b/lib/pages/chat_list/chat_list_view.dart @@ -7,9 +7,9 @@ import 'package:keyboard_shortcuts/keyboard_shortcuts.dart'; import 'package:vrouter/vrouter.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart'; +import 'package:fluffychat/pages/chat_list/chat_list_body_container.dart'; import 'package:fluffychat/pages/chat_list/chat_list_drawer.dart'; -import '../../widgets/matrix.dart'; -import 'chat_list_body.dart'; +import 'package:fluffychat/widgets/matrix.dart'; import 'chat_list_header.dart'; class ChatListView extends StatelessWidget { @@ -31,7 +31,7 @@ class ChatListView extends StatelessWidget { }, child: Scaffold( appBar: ChatListHeader(controller: controller), - body: ChatListViewBody(controller), + body: ChatListBodyContainer(controller), drawer: ChatListDrawer(controller), floatingActionButton: selectMode == SelectMode.normal ? KeyBoardShortcuts( diff --git a/lib/pages/chat_list/spaces_drawer.dart b/lib/pages/chat_list/spaces_drawer.dart index c9e5d6be..3a10eb17 100644 --- a/lib/pages/chat_list/spaces_drawer.dart +++ b/lib/pages/chat_list/spaces_drawer.dart @@ -12,16 +12,24 @@ class SpacesDrawer extends StatelessWidget { const SpacesDrawer({Key? key, required this.controller}) : super(key: key); + /// PLACEHOLDER for later computation of space hierarchy mapping + /// + /// TODO(TheOeWithTheBraid): implement space hierarchy + static Map getSpaceHierarchy( + ChatListController controller) { + return Map.fromEntries( + controller.spacesEntries.map((e) => MapEntry(e, null))); + } + @override Widget build(BuildContext context) { + /// keep this implementation in sync with [ChatListBodyContainer] final currentIndex = controller.spacesEntries.indexWhere((space) => controller.activeSpacesEntry.runtimeType == space.runtimeType && (controller.activeSpaceId == space.getSpace(context)?.id)); final Map spaceHierarchy = - Map.fromEntries(controller.spacesEntries.map((e) => MapEntry(e, null))); - - // TODO(TheOeWithTheBraid): wait for space hierarchy https://gitlab.com/famedly/company/frontend/libraries/matrix_api_lite/-/merge_requests/58 + SpacesDrawer.getSpaceHierarchy(controller); return ListView.builder( itemCount: spaceHierarchy.length + 1, diff --git a/lib/widgets/layouts/two_column_layout.dart b/lib/widgets/layouts/two_column_layout.dart index 9371be3d..c325ecaa 100644 --- a/lib/widgets/layouts/two_column_layout.dart +++ b/lib/widgets/layouts/two_column_layout.dart @@ -1,37 +1,51 @@ import 'package:flutter/material.dart'; -class TwoColumnLayout extends StatelessWidget { +import 'package:vrouter/vrouter.dart'; + +/// displays a two-column layout with some FluffyChat specific patches +/// +/// On huge screens width > 1024, the navigation rail for quick navigation is +/// rendered surround +class FluffyChatTwoColumnLayout extends StatelessWidget { final Widget mainView; final Widget sideView; - const TwoColumnLayout({ + const FluffyChatTwoColumnLayout({ Key? key, required this.mainView, required this.sideView, }) : super(key: key); + @override Widget build(BuildContext context) { return ScaffoldMessenger( child: Scaffold( - body: Row( - children: [ - Container( - clipBehavior: Clip.antiAlias, - decoration: const BoxDecoration(), - width: 360.0, - child: mainView, - ), - Container( - width: 1.0, - color: Theme.of(context).dividerColor, - ), - Expanded( - child: ClipRRect( - child: sideView, + body: LayoutBuilder(builder: (context, constraints) { + final columnWidth = context.vRouter.path.startsWith('/rooms') && + constraints.maxWidth > 1024 + ? 360.0 + 64 + : 360.0; + + return Row( + children: [ + Container( + clipBehavior: Clip.antiAlias, + decoration: const BoxDecoration(), + width: columnWidth, + child: mainView, ), - ), - ], - ), + Container( + width: 1.0, + color: Theme.of(context).dividerColor, + ), + Expanded( + child: ClipRRect( + child: sideView, + ), + ), + ], + ); + }), ), ); }