diff --git a/lib/config/routes.dart b/lib/config/routes.dart index 491865f7c..97e684a6a 100644 --- a/lib/config/routes.dart +++ b/lib/config/routes.dart @@ -93,8 +93,6 @@ 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: @@ -174,9 +172,8 @@ abstract class AppRoutes { state, FluffyThemes.isColumnMode(context) ? TwoColumnLayout( - mainView: const Settings(), + mainView: Settings(key: state.pageKey), sideView: child, - displayNavigationRail: false, ) : child, ), diff --git a/lib/pages/chat_list/chat_list_view.dart b/lib/pages/chat_list/chat_list_view.dart index 81150cb20..31f39b835 100644 --- a/lib/pages/chat_list/chat_list_view.dart +++ b/lib/pages/chat_list/chat_list_view.dart @@ -2,16 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:go_router/go_router.dart'; -import 'package:matrix/matrix.dart'; -import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/chat_list/chat_list.dart'; -import 'package:fluffychat/pages/chat_list/navi_rail_item.dart'; -import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; -import 'package:fluffychat/utils/stream_extension.dart'; -import 'package:fluffychat/widgets/avatar.dart'; -import '../../widgets/matrix.dart'; +import 'package:fluffychat/widgets/navigation_rail.dart'; import 'chat_list_body.dart'; class ChatListView extends StatelessWidget { @@ -21,7 +15,6 @@ class ChatListView extends StatelessWidget { @override Widget build(BuildContext context) { - final client = Matrix.of(context).client; return PopScope( canPop: !controller.isSearchMode && controller.activeSpaceId == null, onPopInvokedWithResult: (pop, _) { @@ -39,111 +32,10 @@ class ChatListView extends StatelessWidget { children: [ if (FluffyThemes.isColumnMode(context) && controller.widget.displayNavigationRail) ...[ - StreamBuilder( - key: ValueKey( - client.userID.toString(), - ), - stream: client.onSync.stream - .where((s) => s.hasRoomUpdate) - .rateLimit(const Duration(seconds: 1)), - builder: (context, _) { - final allSpaces = Matrix.of(context) - .client - .rooms - .where((room) => room.isSpace); - final rootSpaces = allSpaces - .where( - (space) => !allSpaces.any( - (parentSpace) => parentSpace.spaceChildren - .any((child) => child.roomId == space.id), - ), - ) - .toList(); - - return SizedBox( - width: FluffyThemes.navRailWidth, - child: Column( - children: [ - Expanded( - child: ListView.builder( - scrollDirection: Axis.vertical, - itemCount: rootSpaces.length + 2, - itemBuilder: (context, i) { - if (i == 0) { - return NaviRailItem( - isSelected: controller.activeSpaceId == null, - onTap: controller.clearActiveSpace, - icon: const Padding( - padding: EdgeInsets.all(10.0), - child: Icon(Icons.forum_outlined), - ), - selectedIcon: const Padding( - padding: EdgeInsets.all(10.0), - child: Icon(Icons.forum), - ), - toolTip: L10n.of(context).chats, - unreadBadgeFilter: (room) => true, - ); - } - i--; - if (i == rootSpaces.length) { - return NaviRailItem( - isSelected: false, - onTap: () => context.go('/rooms/newspace'), - icon: const Padding( - padding: EdgeInsets.all(8.0), - child: Icon(Icons.add), - ), - toolTip: L10n.of(context).createNewSpace, - ); - } - final space = rootSpaces[i]; - final displayname = - rootSpaces[i].getLocalizedDisplayname( - MatrixLocals(L10n.of(context)), - ); - final spaceChildrenIds = space.spaceChildren - .map((c) => c.roomId) - .toSet(); - return NaviRailItem( - toolTip: displayname, - isSelected: controller.activeSpaceId == space.id, - onTap: () => - controller.setActiveSpace(rootSpaces[i].id), - unreadBadgeFilter: (room) => - spaceChildrenIds.contains(room.id), - icon: Avatar( - mxContent: rootSpaces[i].avatar, - name: displayname, - border: BorderSide( - width: 1, - color: Theme.of(context).dividerColor, - ), - borderRadius: BorderRadius.circular( - AppConfig.borderRadius / 2, - ), - ), - ); - }, - ), - ), - NaviRailItem( - isSelected: false, - onTap: () => context.go('/rooms/settings'), - icon: const Padding( - padding: EdgeInsets.all(10.0), - child: Icon(Icons.settings_outlined), - ), - selectedIcon: const Padding( - padding: EdgeInsets.all(10.0), - child: Icon(Icons.settings), - ), - toolTip: L10n.of(context).settings, - ), - ], - ), - ); - }, + SpacesNavigationRail( + activeSpaceId: controller.activeSpaceId, + onGoToChats: controller.clearActiveSpace, + onGoToSpaceId: controller.setActiveSpace, ), Container( color: Theme.of(context).dividerColor, diff --git a/lib/pages/device_settings/device_settings_view.dart b/lib/pages/device_settings/device_settings_view.dart index 57fb87cad..4e9dab79c 100644 --- a/lib/pages/device_settings/device_settings_view.dart +++ b/lib/pages/device_settings/device_settings_view.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/device_settings/device_settings.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import 'user_device_list_item.dart'; @@ -15,7 +16,8 @@ class DevicesSettingsView extends StatelessWidget { Widget build(BuildContext context) { return Scaffold( appBar: AppBar( - leading: const Center(child: BackButton()), + automaticallyImplyLeading: !FluffyThemes.isColumnMode(context), + centerTitle: FluffyThemes.isColumnMode(context), title: Text(L10n.of(context).devices), ), body: MaxWidthBody( diff --git a/lib/pages/settings/settings_view.dart b/lib/pages/settings/settings_view.dart index 3b5b02902..eace397bc 100644 --- a/lib/pages/settings/settings_view.dart +++ b/lib/pages/settings/settings_view.dart @@ -6,10 +6,12 @@ import 'package:matrix/matrix.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/utils/fluffy_share.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/avatar.dart'; import 'package:fluffychat/widgets/matrix.dart'; +import 'package:fluffychat/widgets/navigation_rail.dart'; import 'settings.dart'; class SettingsView extends StatelessWidget { @@ -21,191 +23,242 @@ class SettingsView extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final showChatBackupBanner = controller.showChatBackupBanner; - return Scaffold( - appBar: AppBar( - leading: Center( - child: CloseButton( - onPressed: () => context.go('/rooms'), + final activeRoute = + GoRouter.of(context).routeInformationProvider.value.uri.path; + return Row( + children: [ + if (FluffyThemes.isColumnMode(context)) ...[ + SpacesNavigationRail( + activeSpaceId: null, + onGoToChats: () => context.go('/rooms'), + onGoToSpaceId: (spaceId) => context.go('/rooms?spaceId=$spaceId'), + ), + Container( + color: Theme.of(context).dividerColor, + width: 1, + ), + ], + Expanded( + child: Scaffold( + appBar: FluffyThemes.isColumnMode(context) + ? null + : AppBar( + title: Text(L10n.of(context).settings), + leading: Center( + child: BackButton( + onPressed: () => context.go('/rooms'), + ), + ), + ), + body: ListTileTheme( + iconColor: theme.colorScheme.onSurface, + child: FutureBuilder( + future: controller.getOidcAccountManageUrl(), + builder: (context, snapshot) { + final accountManageUrl = snapshot.data; + return ListView( + key: const Key('SettingsListViewContent'), + children: [ + FutureBuilder( + future: controller.profileFuture, + builder: (context, snapshot) { + final profile = snapshot.data; + final mxid = Matrix.of(context).client.userID ?? + L10n.of(context).user; + final displayname = + profile?.displayName ?? mxid.localpart ?? mxid; + return Row( + children: [ + Padding( + padding: const EdgeInsets.all(32.0), + child: Stack( + children: [ + Avatar( + mxContent: profile?.avatarUrl, + name: displayname, + size: Avatar.defaultSize * 2.5, + ), + if (profile != null) + Positioned( + bottom: 0, + right: 0, + child: FloatingActionButton.small( + elevation: 2, + onPressed: controller.setAvatarAction, + heroTag: null, + child: const Icon( + Icons.camera_alt_outlined, + ), + ), + ), + ], + ), + ), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + TextButton.icon( + onPressed: + controller.setDisplaynameAction, + icon: const Icon( + Icons.edit_outlined, + size: 16, + ), + style: TextButton.styleFrom( + foregroundColor: + theme.colorScheme.onSurface, + iconColor: theme.colorScheme.onSurface, + ), + label: Text( + displayname, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 18, + ), + ), + ), + TextButton.icon( + onPressed: () => + FluffyShare.share(mxid, context), + icon: const Icon( + Icons.copy_outlined, + size: 14, + ), + style: TextButton.styleFrom( + foregroundColor: + theme.colorScheme.secondary, + iconColor: theme.colorScheme.secondary, + ), + label: Text( + mxid, + maxLines: 1, + overflow: TextOverflow.ellipsis, + // style: const TextStyle(fontSize: 12), + ), + ), + ], + ), + ), + ], + ); + }, + ), + if (accountManageUrl != null) + ListTile( + leading: const Icon(Icons.account_circle_outlined), + title: Text(L10n.of(context).manageAccount), + trailing: const Icon(Icons.open_in_new_outlined), + onTap: () => launchUrlString( + accountManageUrl, + mode: LaunchMode.inAppBrowserView, + ), + ), + Divider(color: theme.dividerColor), + if (showChatBackupBanner == null) + ListTile( + leading: const Icon(Icons.backup_outlined), + title: Text(L10n.of(context).chatBackup), + trailing: const CircularProgressIndicator.adaptive(), + ) + else + SwitchListTile.adaptive( + controlAffinity: ListTileControlAffinity.trailing, + value: controller.showChatBackupBanner == false, + secondary: const Icon(Icons.backup_outlined), + title: Text(L10n.of(context).chatBackup), + onChanged: controller.firstRunBootstrapAction, + ), + Divider( + color: theme.dividerColor, + ), + ListTile( + leading: const Icon(Icons.format_paint_outlined), + title: Text(L10n.of(context).changeTheme), + tileColor: + activeRoute.startsWith('/rooms/settings/style') + ? theme.colorScheme.surfaceContainerHigh + : null, + onTap: () => context.go('/rooms/settings/style'), + ), + ListTile( + leading: const Icon(Icons.notifications_outlined), + title: Text(L10n.of(context).notifications), + tileColor: activeRoute + .startsWith('/rooms/settings/notifications') + ? theme.colorScheme.surfaceContainerHigh + : null, + onTap: () => + context.go('/rooms/settings/notifications'), + ), + ListTile( + leading: const Icon(Icons.devices_outlined), + title: Text(L10n.of(context).devices), + onTap: () => context.go('/rooms/settings/devices'), + tileColor: + activeRoute.startsWith('/rooms/settings/devices') + ? theme.colorScheme.surfaceContainerHigh + : null, + ), + ListTile( + leading: const Icon(Icons.forum_outlined), + title: Text(L10n.of(context).chat), + onTap: () => context.go('/rooms/settings/chat'), + tileColor: + activeRoute.startsWith('/rooms/settings/chat') + ? theme.colorScheme.surfaceContainerHigh + : null, + ), + ListTile( + leading: const Icon(Icons.shield_outlined), + title: Text(L10n.of(context).security), + onTap: () => context.go('/rooms/settings/security'), + tileColor: + activeRoute.startsWith('/rooms/settings/security') + ? theme.colorScheme.surfaceContainerHigh + : null, + ), + Divider(color: theme.dividerColor), + ListTile( + leading: const Icon(Icons.dns_outlined), + title: Text( + L10n.of(context).aboutHomeserver( + Matrix.of(context).client.userID?.domain ?? + 'homeserver', + ), + ), + onTap: () => context.go('/rooms/settings/homeserver'), + tileColor: + activeRoute.startsWith('/rooms/settings/homeserver') + ? theme.colorScheme.surfaceContainerHigh + : null, + ), + ListTile( + leading: const Icon(Icons.privacy_tip_outlined), + title: Text(L10n.of(context).privacy), + onTap: () => launchUrlString(AppConfig.privacyUrl), + ), + ListTile( + leading: const Icon(Icons.info_outline_rounded), + title: Text(L10n.of(context).about), + onTap: () => PlatformInfos.showDialog(context), + ), + Divider(color: theme.dividerColor), + ListTile( + leading: const Icon(Icons.logout_outlined), + title: Text(L10n.of(context).logout), + onTap: controller.logoutAction, + ), + ], + ); + }, + ), + ), ), ), - title: Text(L10n.of(context).settings), - ), - body: ListTileTheme( - iconColor: theme.colorScheme.onSurface, - child: FutureBuilder( - future: controller.getOidcAccountManageUrl(), - builder: (context, snapshot) { - final accountManageUrl = snapshot.data; - return ListView( - key: const Key('SettingsListViewContent'), - children: [ - FutureBuilder( - future: controller.profileFuture, - builder: (context, snapshot) { - final profile = snapshot.data; - final mxid = Matrix.of(context).client.userID ?? - L10n.of(context).user; - final displayname = - profile?.displayName ?? mxid.localpart ?? mxid; - return Row( - children: [ - Padding( - padding: const EdgeInsets.all(32.0), - child: Stack( - children: [ - Avatar( - mxContent: profile?.avatarUrl, - name: displayname, - size: Avatar.defaultSize * 2.5, - ), - if (profile != null) - Positioned( - bottom: 0, - right: 0, - child: FloatingActionButton.small( - elevation: 2, - onPressed: controller.setAvatarAction, - heroTag: null, - child: - const Icon(Icons.camera_alt_outlined), - ), - ), - ], - ), - ), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TextButton.icon( - onPressed: controller.setDisplaynameAction, - icon: const Icon( - Icons.edit_outlined, - size: 16, - ), - style: TextButton.styleFrom( - foregroundColor: theme.colorScheme.onSurface, - iconColor: theme.colorScheme.onSurface, - ), - label: Text( - displayname, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontSize: 18, - ), - ), - ), - TextButton.icon( - onPressed: () => - FluffyShare.share(mxid, context), - icon: const Icon( - Icons.copy_outlined, - size: 14, - ), - style: TextButton.styleFrom( - foregroundColor: theme.colorScheme.secondary, - iconColor: theme.colorScheme.secondary, - ), - label: Text( - mxid, - maxLines: 1, - overflow: TextOverflow.ellipsis, - // style: const TextStyle(fontSize: 12), - ), - ), - ], - ), - ), - ], - ); - }, - ), - if (accountManageUrl != null) - ListTile( - leading: const Icon(Icons.account_circle_outlined), - title: Text(L10n.of(context).manageAccount), - trailing: const Icon(Icons.open_in_new_outlined), - onTap: () => launchUrlString( - accountManageUrl, - mode: LaunchMode.inAppBrowserView, - ), - ), - Divider(color: theme.dividerColor), - if (showChatBackupBanner == null) - ListTile( - leading: const Icon(Icons.backup_outlined), - title: Text(L10n.of(context).chatBackup), - trailing: const CircularProgressIndicator.adaptive(), - ) - else - SwitchListTile.adaptive( - controlAffinity: ListTileControlAffinity.trailing, - value: controller.showChatBackupBanner == false, - secondary: const Icon(Icons.backup_outlined), - title: Text(L10n.of(context).chatBackup), - onChanged: controller.firstRunBootstrapAction, - ), - Divider( - color: theme.dividerColor, - ), - ListTile( - leading: const Icon(Icons.format_paint_outlined), - title: Text(L10n.of(context).changeTheme), - onTap: () => context.go('/rooms/settings/style'), - ), - ListTile( - leading: const Icon(Icons.notifications_outlined), - title: Text(L10n.of(context).notifications), - onTap: () => context.go('/rooms/settings/notifications'), - ), - ListTile( - leading: const Icon(Icons.devices_outlined), - title: Text(L10n.of(context).devices), - onTap: () => context.go('/rooms/settings/devices'), - ), - ListTile( - leading: const Icon(Icons.forum_outlined), - title: Text(L10n.of(context).chat), - onTap: () => context.go('/rooms/settings/chat'), - ), - ListTile( - leading: const Icon(Icons.shield_outlined), - title: Text(L10n.of(context).security), - onTap: () => context.go('/rooms/settings/security'), - ), - Divider(color: theme.dividerColor), - ListTile( - leading: const Icon(Icons.dns_outlined), - title: Text( - L10n.of(context).aboutHomeserver( - Matrix.of(context).client.userID?.domain ?? 'homeserver', - ), - ), - onTap: () => context.go('/rooms/settings/homeserver'), - ), - ListTile( - leading: const Icon(Icons.privacy_tip_outlined), - title: Text(L10n.of(context).privacy), - onTap: () => launchUrlString(AppConfig.privacyUrl), - ), - ListTile( - leading: const Icon(Icons.info_outline_rounded), - title: Text(L10n.of(context).about), - onTap: () => PlatformInfos.showDialog(context), - ), - Divider(color: theme.dividerColor), - ListTile( - leading: const Icon(Icons.logout_outlined), - title: Text(L10n.of(context).logout), - onTap: controller.logoutAction, - ), - ], - ); - }, - ), - ), + ], ); } } diff --git a/lib/pages/settings_chat/settings_chat_view.dart b/lib/pages/settings_chat/settings_chat_view.dart index 7efa65a1e..7d9015429 100644 --- a/lib/pages/settings_chat/settings_chat_view.dart +++ b/lib/pages/settings_chat/settings_chat_view.dart @@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart'; +import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/voip/callkeep_manager.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; @@ -21,7 +22,11 @@ class SettingsChatView extends StatelessWidget { final theme = Theme.of(context); return Scaffold( - appBar: AppBar(title: Text(L10n.of(context).chat)), + appBar: AppBar( + title: Text(L10n.of(context).chat), + automaticallyImplyLeading: !FluffyThemes.isColumnMode(context), + centerTitle: FluffyThemes.isColumnMode(context), + ), body: ListTileTheme( iconColor: theme.textTheme.bodyLarge!.color, child: MaxWidthBody( diff --git a/lib/pages/settings_homeserver/settings_homeserver_view.dart b/lib/pages/settings_homeserver/settings_homeserver_view.dart index 59ee61c96..712bcd707 100644 --- a/lib/pages/settings_homeserver/settings_homeserver_view.dart +++ b/lib/pages/settings_homeserver/settings_homeserver_view.dart @@ -8,6 +8,7 @@ import 'package:matrix/matrix.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/utils/localized_exception_extension.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import '../../widgets/matrix.dart'; @@ -26,7 +27,8 @@ class SettingsHomeserverView extends StatelessWidget { return Scaffold( appBar: AppBar( - leading: const Center(child: BackButton()), + automaticallyImplyLeading: !FluffyThemes.isColumnMode(context), + centerTitle: FluffyThemes.isColumnMode(context), title: Text( L10n.of(context) .aboutHomeserver(client.userID?.domain ?? 'Homeserver'), diff --git a/lib/pages/settings_notifications/settings_notifications_view.dart b/lib/pages/settings_notifications/settings_notifications_view.dart index 280ef26fc..63bd3a166 100644 --- a/lib/pages/settings_notifications/settings_notifications_view.dart +++ b/lib/pages/settings_notifications/settings_notifications_view.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:matrix/matrix.dart'; +import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/pages/settings_notifications/push_rule_extensions.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; import '../../utils/localized_exception_extension.dart'; @@ -29,7 +30,8 @@ class SettingsNotificationsView extends StatelessWidget { ]; return Scaffold( appBar: AppBar( - leading: const Center(child: BackButton()), + automaticallyImplyLeading: !FluffyThemes.isColumnMode(context), + centerTitle: FluffyThemes.isColumnMode(context), title: Text(L10n.of(context).notifications), ), body: MaxWidthBody( diff --git a/lib/pages/settings_security/settings_security_view.dart b/lib/pages/settings_security/settings_security_view.dart index e21240323..0861f1351 100644 --- a/lib/pages/settings_security/settings_security_view.dart +++ b/lib/pages/settings_security/settings_security_view.dart @@ -6,6 +6,7 @@ import 'package:matrix/matrix.dart'; import 'package:fluffychat/config/app_config.dart'; import 'package:fluffychat/config/setting_keys.dart'; +import 'package:fluffychat/config/themes.dart'; import 'package:fluffychat/utils/beautify_string_extension.dart'; import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/widgets/layouts/max_width_body.dart'; @@ -22,7 +23,11 @@ class SettingsSecurityView extends StatelessWidget { final theme = Theme.of(context); return Scaffold( - appBar: AppBar(title: Text(L10n.of(context).security)), + appBar: AppBar( + title: Text(L10n.of(context).security), + automaticallyImplyLeading: !FluffyThemes.isColumnMode(context), + centerTitle: FluffyThemes.isColumnMode(context), + ), body: ListTileTheme( iconColor: theme.colorScheme.onSurface, child: MaxWidthBody( diff --git a/lib/pages/settings_style/settings_style_view.dart b/lib/pages/settings_style/settings_style_view.dart index 2309dbc3d..d234759df 100644 --- a/lib/pages/settings_style/settings_style_view.dart +++ b/lib/pages/settings_style/settings_style_view.dart @@ -32,7 +32,8 @@ class SettingsStyleView extends StatelessWidget { final client = Matrix.of(context).client; return Scaffold( appBar: AppBar( - leading: const Center(child: BackButton()), + automaticallyImplyLeading: !FluffyThemes.isColumnMode(context), + centerTitle: FluffyThemes.isColumnMode(context), title: Text(L10n.of(context).changeTheme), ), backgroundColor: theme.colorScheme.surface, diff --git a/lib/widgets/layouts/two_column_layout.dart b/lib/widgets/layouts/two_column_layout.dart index 36e815ebb..86a6f56c5 100644 --- a/lib/widgets/layouts/two_column_layout.dart +++ b/lib/widgets/layouts/two_column_layout.dart @@ -5,13 +5,11 @@ import 'package:fluffychat/config/themes.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) { @@ -24,8 +22,7 @@ class TwoColumnLayout extends StatelessWidget { Container( clipBehavior: Clip.antiAlias, decoration: const BoxDecoration(), - width: FluffyThemes.columnWidth + - (displayNavigationRail ? FluffyThemes.navRailWidth : 0), + width: FluffyThemes.columnWidth + FluffyThemes.navRailWidth, child: mainView, ), Container( diff --git a/lib/widgets/navigation_rail.dart b/lib/widgets/navigation_rail.dart new file mode 100644 index 000000000..04be771e2 --- /dev/null +++ b/lib/widgets/navigation_rail.dart @@ -0,0 +1,137 @@ +import 'package:flutter/material.dart'; + +import 'package:flutter_gen/gen_l10n/l10n.dart'; +import 'package:go_router/go_router.dart'; +import 'package:matrix/matrix.dart'; + +import 'package:fluffychat/config/app_config.dart'; +import 'package:fluffychat/config/themes.dart'; +import 'package:fluffychat/pages/chat_list/navi_rail_item.dart'; +import 'package:fluffychat/utils/matrix_sdk_extensions/matrix_locals.dart'; +import 'package:fluffychat/utils/stream_extension.dart'; +import 'package:fluffychat/widgets/avatar.dart'; +import 'package:fluffychat/widgets/matrix.dart'; + +class SpacesNavigationRail extends StatelessWidget { + final String? activeSpaceId; + final void Function() onGoToChats; + final void Function(String) onGoToSpaceId; + + const SpacesNavigationRail({ + required this.activeSpaceId, + required this.onGoToChats, + required this.onGoToSpaceId, + super.key, + }); + + @override + Widget build(BuildContext context) { + final client = Matrix.of(context).client; + final isSettings = GoRouter.of(context) + .routeInformationProvider + .value + .uri + .path + .startsWith('/rooms/settings'); + return StreamBuilder( + key: ValueKey( + client.userID.toString(), + ), + stream: client.onSync.stream + .where((s) => s.hasRoomUpdate) + .rateLimit(const Duration(seconds: 1)), + builder: (context, _) { + final allSpaces = client.rooms.where((room) => room.isSpace); + final rootSpaces = allSpaces + .where( + (space) => !allSpaces.any( + (parentSpace) => parentSpace.spaceChildren + .any((child) => child.roomId == space.id), + ), + ) + .toList(); + + return SizedBox( + width: FluffyThemes.navRailWidth, + child: Column( + children: [ + Expanded( + child: ListView.builder( + scrollDirection: Axis.vertical, + itemCount: rootSpaces.length + 2, + itemBuilder: (context, i) { + if (i == 0) { + return NaviRailItem( + isSelected: activeSpaceId == null && !isSettings, + onTap: onGoToChats, + icon: const Padding( + padding: EdgeInsets.all(10.0), + child: Icon(Icons.forum_outlined), + ), + selectedIcon: const Padding( + padding: EdgeInsets.all(10.0), + child: Icon(Icons.forum), + ), + toolTip: L10n.of(context).chats, + unreadBadgeFilter: (room) => true, + ); + } + i--; + if (i == rootSpaces.length) { + return NaviRailItem( + isSelected: false, + onTap: () => context.go('/rooms/newspace'), + icon: const Padding( + padding: EdgeInsets.all(8.0), + child: Icon(Icons.add), + ), + toolTip: L10n.of(context).createNewSpace, + ); + } + final space = rootSpaces[i]; + final displayname = rootSpaces[i].getLocalizedDisplayname( + MatrixLocals(L10n.of(context)), + ); + final spaceChildrenIds = + space.spaceChildren.map((c) => c.roomId).toSet(); + return NaviRailItem( + toolTip: displayname, + isSelected: activeSpaceId == space.id, + onTap: () => onGoToSpaceId(rootSpaces[i].id), + unreadBadgeFilter: (room) => + spaceChildrenIds.contains(room.id), + icon: Avatar( + mxContent: rootSpaces[i].avatar, + name: displayname, + border: BorderSide( + width: 1, + color: Theme.of(context).dividerColor, + ), + borderRadius: BorderRadius.circular( + AppConfig.borderRadius / 2, + ), + ), + ); + }, + ), + ), + NaviRailItem( + isSelected: isSettings, + onTap: () => context.go('/rooms/settings'), + icon: const Padding( + padding: EdgeInsets.all(10.0), + child: Icon(Icons.settings_outlined), + ), + selectedIcon: const Padding( + padding: EdgeInsets.all(10.0), + child: Icon(Icons.settings), + ), + toolTip: L10n.of(context).settings, + ), + ], + ), + ); + }, + ); + } +}