From b665bbe3cce9402e0e395c165af346576d84c08f Mon Sep 17 00:00:00 2001 From: Bruno D'Luka Date: Fri, 8 Dec 2023 12:32:50 -0300 Subject: [PATCH] feat: ui review --- lib/l10n/app_en.arb | 3 +- lib/l10n/app_fr.arb | 1 + lib/l10n/app_pl.arb | 1 + lib/l10n/app_pt.arb | 3 +- .../device_grid/desktop/desktop_sidebar.dart | 324 ++++++++++-------- lib/widgets/device_grid/device_grid.dart | 1 + lib/widgets/servers/add_server.dart | 11 +- 7 files changed, 191 insertions(+), 153 deletions(-) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index b2b872cc..c6d51bda 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -112,7 +112,8 @@ "directCamera": "Direct Camera", "addServer": "Add Server", "settings": "Settings", - "noServersAdded": "No servers added", + "noServersAdded": "You haven't added any servers yet :/", + "howToAddServer": "Go to the \"Add Server\" screen to add a server.", "editServerInfo": "Edit server info", "editServer": "Edit server {serverName}", "@editServer": { diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index b6de2b3f..ad6c5482 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -109,6 +109,7 @@ "addServer": "Ajouter serveur", "settings": "Paramètres", "noServersAdded": "Aucun serveur ajouté", + "howToAddServer": "Go to the \"Add Server\" screen to add a server.", "editServerInfo": "Modifier les info serveur", "editServer": "Modifier le serveur {serverName}", "@editServer": { diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index e677494f..0ab558d1 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -113,6 +113,7 @@ "addServer": "Dodaj serwer", "settings": "Ustawienia", "noServersAdded": "Nie dodano serwerów", + "howToAddServer": "Go to the \"Add Server\" screen to add a server.", "editServerInfo": "Modyfikuj informację serwera", "editServer": "Modyfikuj serwer {serverName}", "@editServer": { diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb index 8cc10c84..423c6554 100644 --- a/lib/l10n/app_pt.arb +++ b/lib/l10n/app_pt.arb @@ -112,7 +112,8 @@ "directCamera": "Câmera específica", "addServer": "Adicionar servidor", "settings": "Configurações", - "noServersAdded": "Nenhum servidor adicionado", + "noServersAdded": "Você ainda não adicionou nenhum servidor :/", + "howToAddServer": "Vá à \"Adicionar Servidor\" para adicionar um servidor.", "editServerInfo": "Editar informações do servidor", "editServer": "Editar servidor {serverName}", "@editServer": { diff --git a/lib/widgets/device_grid/desktop/desktop_sidebar.dart b/lib/widgets/device_grid/desktop/desktop_sidebar.dart index 117cfa20..f0783dee 100644 --- a/lib/widgets/device_grid/desktop/desktop_sidebar.dart +++ b/lib/widgets/device_grid/desktop/desktop_sidebar.dart @@ -43,154 +43,192 @@ class _DesktopSidebarState extends State { final servers = context.watch(); final view = context.watch(); - return Material( - color: theme.canvasColor, - child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ - LayoutManager(collapseButton: widget.collapseButton), - Expanded( - child: MouseRegion( - onEnter: (e) => setState(() => isSidebarHovering = true), - onExit: (e) => setState(() => isSidebarHovering = false), - // Add another material here because its descendants must be clipped. - child: Material( - type: MaterialType.transparency, - child: CustomScrollView(slivers: [ - for (final server in servers.servers) - () { - final devices = server.devices.sorted(); - final isLoading = servers.isServerLoading(server); - - /// Whether all the online devices are in the current view. - final isAllInView = devices - .where((d) => d.status) - .every((d) => view.currentLayout.devices.contains(d)); - - return MultiSliver(pushPinnedChildren: true, children: [ - SliverPinnedHeader( - child: SubHeader( - server.name, - materialType: MaterialType.canvas, - subtext: server.online - ? loc.nDevices(devices.length) - : loc.offline, - subtextStyle: TextStyle( - color: - !server.online ? theme.colorScheme.error : null, + return SafeArea( + top: false, + right: false, + child: Material( + color: theme.canvasColor, + child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [ + LayoutManager(collapseButton: widget.collapseButton), + if (servers.servers.isEmpty) + const Expanded(child: NoServers()) + else + Expanded( + child: MouseRegion( + onEnter: (e) => setState(() => isSidebarHovering = true), + onExit: (e) => setState(() => isSidebarHovering = false), + // Add another material here because its descendants must be clipped. + child: Material( + type: MaterialType.transparency, + child: CustomScrollView(slivers: [ + for (final server in servers.servers) + () { + final devices = server.devices.sorted(); + final isLoading = servers.isServerLoading(server); + + /// Whether all the online devices are in the current view. + final isAllInView = devices + .where((d) => d.status) + .every( + (d) => view.currentLayout.devices.contains(d)); + + return MultiSliver(pushPinnedChildren: true, children: [ + SliverPinnedHeader( + child: SubHeader( + server.name, + materialType: MaterialType.canvas, + subtext: server.online + ? loc.nDevices(devices.length) + : loc.offline, + subtextStyle: TextStyle( + color: !server.online + ? theme.colorScheme.error + : null, + ), + trailing: Builder(builder: (context) { + if (isLoading) { + // wrap in an icon button to ensure ui consistency + return const SquaredIconButton( + onPressed: null, + icon: SizedBox( + height: 16.0, + width: 16.0, + child: CircularProgressIndicator.adaptive( + strokeWidth: 1.5, + ), + ), + ); + } else if (!server.online && + isSidebarHovering) { + return SquaredIconButton( + icon: const Icon(Icons.refresh), + tooltip: loc.refreshServer, + onPressed: () => servers + .refreshDevices(ids: [server.id]), + ); + } else if (isSidebarHovering && + devices.isNotEmpty) { + return SquaredIconButton( + icon: Icon( + isAllInView + ? Icons.playlist_remove + : Icons.playlist_add, + ), + tooltip: isAllInView + ? loc.removeAllFromView + : loc.addAllToView, + onPressed: () { + if (isAllInView) { + view.removeDevicesFromCurrentLayout( + devices); + } else { + for (final device in devices) { + if (device.status && + !view.currentLayout.devices + .contains(device)) { + view.add(device); + } + } + } + }, + ); + } else { + return const SizedBox.shrink(); + } + }), + ), ), - trailing: Builder(builder: (context) { - if (isLoading) { - // wrap in an icon button to ensure ui consistency - return const SquaredIconButton( - onPressed: null, - icon: SizedBox( - height: 16.0, - width: 16.0, - child: CircularProgressIndicator.adaptive( - strokeWidth: 1.5, + if (server.online && !isLoading) + SliverList.builder( + itemCount: devices.length, + itemBuilder: (context, index) { + final device = devices[index]; + final selected = + view.currentLayout.devices.contains(device); + + final tile = DesktopDeviceSelectorTile( + device: device, + selected: selected, + ); + + if (selected || !device.status) return tile; + + final isBlocked = view.currentLayout.type == + DesktopLayoutType.singleView && + view.currentLayout.devices.isNotEmpty; + + return Draggable( + data: device, + feedback: Card( + child: SizedBox( + height: kDeviceSelectorTileHeight, + width: kSidebarConstraints.maxWidth, + child: Row(children: [ + Expanded(child: tile), + if (isBlocked) + Icon( + Icons.block, + color: theme.colorScheme.error, + size: 18.0, + ), + const SizedBox(width: 16.0), + ]), + ), ), - ), - ); - } else if (!server.online && isSidebarHovering) { - return SquaredIconButton( - icon: const Icon(Icons.refresh), - tooltip: loc.refreshServer, - onPressed: () => - servers.refreshDevices(ids: [server.id]), - ); - } else if (isSidebarHovering && - devices.isNotEmpty) { - return SquaredIconButton( - icon: Icon( - isAllInView - ? Icons.playlist_remove - : Icons.playlist_add, - ), - tooltip: isAllInView - ? loc.removeAllFromView - : loc.addAllToView, - onPressed: () { - if (isAllInView) { - view.removeDevicesFromCurrentLayout( - devices); - } else { - for (final device in devices) { - if (device.status && - !view.currentLayout.devices - .contains(device)) { - view.add(device); - } - } - } - }, - ); - } else { - return const SizedBox.shrink(); - } - }), - ), - ), - if (server.online && !isLoading) - SliverList.builder( - itemCount: devices.length, - itemBuilder: (context, index) { - final device = devices[index]; - final selected = - view.currentLayout.devices.contains(device); - - final tile = DesktopDeviceSelectorTile( - device: device, - selected: selected, - ); - - if (selected || !device.status) return tile; - - final isBlocked = view.currentLayout.type == - DesktopLayoutType.singleView && - view.currentLayout.devices.isNotEmpty; - - return Draggable( - data: device, - feedback: Card( - child: SizedBox( - height: kDeviceSelectorTileHeight, - width: kSidebarConstraints.maxWidth, - child: Row(children: [ - Expanded(child: tile), - if (isBlocked) - Icon( - Icons.block, - color: theme.colorScheme.error, - size: 18.0, - ), - const SizedBox(width: 16.0), - ]), - ), - ), - child: tile, - ); - }, - ), - ]); - }(), - const SliverToBoxAdapter(child: Divider()), - SliverToBoxAdapter( - child: ListTile( - dense: true, - trailing: const Icon(Icons.camera_outdoor, size: 20.0), - title: Text(loc.addExternalStream), - onTap: () => AddExternalStreamDialog.show(context), - ), - ), - SliverPadding( - padding: EdgeInsetsDirectional.only( - bottom: MediaQuery.viewPaddingOf(context).bottom, - ), + child: tile, + ); + }, + ), + ]); + }(), + ]), ), - ]), + ), ), + const Divider(), + ListTile( + dense: true, + trailing: const Icon(Icons.camera_outdoor, size: 20.0), + title: Text(loc.addExternalStream), + onTap: () => AddExternalStreamDialog.show(context), ), - ), - ]), + ]), + ), + ); + } +} + +class NoServers extends StatelessWidget { + const NoServers({super.key}); + + @override + Widget build(BuildContext context) { + final loc = AppLocalizations.of(context); + final home = context.watch(); + return Padding( + padding: const EdgeInsetsDirectional.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon( + Icons.dns, + size: 48.0, + ), + const SizedBox(height: 6.0), + Text(loc.noServersAdded, textAlign: TextAlign.center), + Text.rich( + TextSpan( + text: loc.howToAddServer, + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () => home.setTab(UnityTab.addServer, context), + ), + textAlign: TextAlign.center, + ), + ], + ), ); } } diff --git a/lib/widgets/device_grid/device_grid.dart b/lib/widgets/device_grid/device_grid.dart index 38a1882b..6d4435f0 100644 --- a/lib/widgets/device_grid/device_grid.dart +++ b/lib/widgets/device_grid/device_grid.dart @@ -25,6 +25,7 @@ import 'package:auto_size_text/auto_size_text.dart'; import 'package:bluecherry_client/models/device.dart'; import 'package:bluecherry_client/models/layout.dart'; import 'package:bluecherry_client/providers/desktop_view_provider.dart'; +import 'package:bluecherry_client/providers/home_provider.dart'; import 'package:bluecherry_client/providers/mobile_view_provider.dart'; import 'package:bluecherry_client/providers/server_provider.dart'; import 'package:bluecherry_client/providers/settings_provider.dart'; diff --git a/lib/widgets/servers/add_server.dart b/lib/widgets/servers/add_server.dart index 966b5964..8e552f46 100644 --- a/lib/widgets/servers/add_server.dart +++ b/lib/widgets/servers/add_server.dart @@ -61,17 +61,13 @@ Widget _buildCardAppBar({ ), Text( title, - style: const TextStyle( - fontWeight: FontWeight.w500, - color: Colors.white, - ), + style: theme.textTheme.displayMedium, ), ]), const SizedBox(height: 12.0), Text( description, - style: theme.textTheme.headlineMedium - ?.copyWith(color: Colors.white.withOpacity(0.87)), + style: theme.textTheme.headlineMedium, ), const SizedBox(height: 20.0), ], @@ -1035,8 +1031,7 @@ class LetsGoScreen extends StatelessWidget { children: [ Text( loc.letsGoDescription, - style: theme.textTheme.headlineMedium - ?.copyWith(color: Colors.white.withOpacity(0.87)), + style: theme.textTheme.headlineMedium, ), ...[loc.tip0, loc.tip1, loc.tip2, loc.tip3].map((tip) { return Padding(