diff --git a/lib/widgets/connection_indicator.dart b/lib/widgets/connection_indicator.dart index a45867a..8ce20d6 100644 --- a/lib/widgets/connection_indicator.dart +++ b/lib/widgets/connection_indicator.dart @@ -4,6 +4,7 @@ import 'package:gap/gap.dart'; import 'package:material_symbols_icons/symbols.dart'; import 'package:provider/provider.dart'; import 'package:styled_widget/styled_widget.dart'; +import 'package:surface/providers/config.dart'; import 'package:surface/providers/userinfo.dart'; import 'package:surface/providers/websocket.dart'; @@ -13,6 +14,9 @@ class ConnectionIndicator extends StatelessWidget { @override Widget build(BuildContext context) { final ws = context.watch(); + final cfg = context.watch(); + + final marginLeft = cfg.drawerIsCollapsed ? 0.0 : cfg.drawerIsExpanded ? 304.0 : 80.0; return ListenableBuilder( listenable: ws, @@ -22,45 +26,50 @@ class ConnectionIndicator extends StatelessWidget { return IgnorePointer( ignoring: !show, - child: GestureDetector( - child: Material( - elevation: 2, - shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), - color: Theme.of(context).colorScheme.secondaryContainer, - child: ua.isAuthorized - ? Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - if (ws.isBusy) - Text('serverConnecting').tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer) - else if (!ws.isConnected) - Text('serverDisconnected').tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer) - else - Text('serverConnected').tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer), - const Gap(8), - if (ws.isBusy) - const CircularProgressIndicator(strokeWidth: 2.5) - .width(12) - .height(12) - .padding(horizontal: 4, right: 4) - else if (!ws.isConnected) - const Icon(Symbols.power_off, size: 18) - else - const Icon(Symbols.power, size: 18), - ], - ).padding(horizontal: 8, vertical: 4) - : const SizedBox.shrink(), - ).opacity(show ? 1 : 0, animate: true).animate( - const Duration(milliseconds: 300), - Curves.easeInOut, - ), - onTap: () { - if (!ws.isConnected && !ws.isBusy) { - ws.connect(); - } - }, - ), + child: Center( + child: GestureDetector( + child: Material( + elevation: 2, + shape: const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(16))), + color: Theme.of(context).colorScheme.secondaryContainer, + child: ua.isAuthorized + ? Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if (ws.isBusy) + Text('serverConnecting').tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer) + else if (!ws.isConnected) + Text('serverDisconnected') + .tr() + .textColor(Theme.of(context).colorScheme.onSecondaryContainer) + else + Text('serverConnected').tr().textColor(Theme.of(context).colorScheme.onSecondaryContainer), + const Gap(8), + if (ws.isBusy) + const CircularProgressIndicator(strokeWidth: 2.5) + .width(12) + .height(12) + .padding(horizontal: 4, right: 4) + else if (!ws.isConnected) + const Icon(Symbols.power_off, size: 18) + else + const Icon(Symbols.power, size: 18), + ], + ).padding(horizontal: 8, vertical: 4) + : const SizedBox.shrink(), + ).opacity(show ? 1 : 0, animate: true).animate( + const Duration(milliseconds: 300), + Curves.easeInOut, + ), + onTap: () { + if (!ws.isConnected && !ws.isBusy) { + ws.connect(); + } + }, + ), + ).padding(left: marginLeft), ); }, ); diff --git a/lib/widgets/context_menu.dart b/lib/widgets/context_menu.dart index 0206b1f..940f6da 100644 --- a/lib/widgets/context_menu.dart +++ b/lib/widgets/context_menu.dart @@ -28,7 +28,7 @@ class ContextMenuArea extends StatelessWidget { // Leave padding for side navigation mousePosition = cfg.drawerIsExpanded ? mousePosition.copyWith(dx: mousePosition.dx - 304 * 2) - : mousePosition.copyWith(dx: mousePosition.dx - 72 * 2); + : mousePosition.copyWith(dx: mousePosition.dx - 80 * 2); } }, child: GestureDetector( diff --git a/lib/widgets/navigation/app_rail_navigation.dart b/lib/widgets/navigation/app_rail_navigation.dart index 17df855..c3c7a55 100644 --- a/lib/widgets/navigation/app_rail_navigation.dart +++ b/lib/widgets/navigation/app_rail_navigation.dart @@ -31,34 +31,37 @@ class _AppRailNavigationState extends State { builder: (context, _) { final destinations = nav.destinations.where((ele) => ele.isPinned).toList(); - return NavigationRail( - selectedIndex: - nav.currentIndex != null && nav.currentIndex! < nav.pinnedDestinationCount ? nav.currentIndex : null, - destinations: [ - ...destinations.where((ele) => ele.isPinned).map((ele) { - return NavigationRailDestination( - icon: ele.icon, - label: Text(ele.label).tr(), - ); - }), - ], - trailing: Expanded( - child: Align( - alignment: Alignment.bottomCenter, - child: StyledWidget( - IconButton( - icon: const Icon(Symbols.menu), - onPressed: () { - Scaffold.of(context).openDrawer(); - }, - ), - ).padding(bottom: 16), + return SizedBox( + width: 80, + child: NavigationRail( + selectedIndex: + nav.currentIndex != null && nav.currentIndex! < nav.pinnedDestinationCount ? nav.currentIndex : null, + destinations: [ + ...destinations.where((ele) => ele.isPinned).map((ele) { + return NavigationRailDestination( + icon: ele.icon, + label: Text(ele.label).tr(), + ); + }), + ], + trailing: Expanded( + child: Align( + alignment: Alignment.bottomCenter, + child: StyledWidget( + IconButton( + icon: const Icon(Symbols.menu), + onPressed: () { + Scaffold.of(context).openDrawer(); + }, + ), + ).padding(bottom: 16), + ), ), + onDestinationSelected: (idx) { + nav.setIndex(idx); + GoRouter.of(context).goNamed(destinations[idx].screen); + }, ), - onDestinationSelected: (idx) { - nav.setIndex(idx); - GoRouter.of(context).goNamed(destinations[idx].screen); - }, ); }, ); diff --git a/lib/widgets/navigation/app_scaffold.dart b/lib/widgets/navigation/app_scaffold.dart index 350a71a..4bf00ec 100644 --- a/lib/widgets/navigation/app_scaffold.dart +++ b/lib/widgets/navigation/app_scaffold.dart @@ -140,6 +140,7 @@ class AppRootScaffold extends StatelessWidget { ); final safeTop = MediaQuery.of(context).padding.top; + final safeBottom = MediaQuery.of(context).padding.bottom; return Scaffold( key: globalRootScaffoldKey, @@ -191,7 +192,10 @@ class AppRootScaffold extends StatelessWidget { ], ), Positioned(top: safeTop > 0 ? safeTop : 16, right: 8, child: NotifyIndicator()), - Positioned(top: safeTop > 0 ? safeTop : 16, left: 8, child: ConnectionIndicator()), + if (ResponsiveBreakpoints.of(context).smallerOrEqualTo(MOBILE)) + Positioned(bottom: safeBottom > 0 ? safeBottom : 16, left: 0, right: 0, child: ConnectionIndicator()) + else + Positioned(top: safeTop > 0 ? safeTop : 16, left: 0, right: 0, child: ConnectionIndicator()), ], ), drawer: !isExpandedDrawer ? AppNavigationDrawer() : null, diff --git a/lib/widgets/notify_indicator.dart b/lib/widgets/notify_indicator.dart index 96179bb..9299580 100644 --- a/lib/widgets/notify_indicator.dart +++ b/lib/widgets/notify_indicator.dart @@ -84,6 +84,7 @@ class _NotifyIndicatorState extends State with SingleTickerProv ignoring: !show, child: GestureDetector( child: Animate( + autoPlay: false, controller: _animationController, effects: [ SlideEffect(