mirror of
https://gitlab.com/famedly/fluffychat.git
synced 2025-04-10 18:27:57 +02:00
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 <the-one@with-the-braid.cf>
This commit is contained in:
parent
3e80e3f67e
commit
77661f1ead
@ -113,7 +113,7 @@ class AppRoutes {
|
||||
List<VRouteElement> 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(),
|
||||
),
|
||||
|
37
lib/pages/chat_list/chat_list_body_container.dart
Normal file
37
lib/pages/chat_list/chat_list_body_container.dart
Normal file
@ -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);
|
||||
});
|
||||
}
|
||||
}
|
@ -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: [
|
||||
|
76
lib/pages/chat_list/chat_list_navigation_rail.dart
Normal file
76
lib/pages/chat_list/chat_list_navigation_rail.dart
Normal file
@ -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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -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(
|
||||
|
@ -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<SpacesEntry, dynamic> 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<SpacesEntry, dynamic> 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,
|
||||
|
@ -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,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user