diff --git a/packages/neon/neon/lib/src/pages/home.dart b/packages/neon/neon/lib/src/pages/home.dart index be4a7b39..a3cc9f7b 100644 --- a/packages/neon/neon/lib/src/pages/home.dart +++ b/packages/neon/neon/lib/src/pages/home.dart @@ -183,397 +183,391 @@ class _HomePageState extends State { ) => OptionBuilder( option: _globalOptions.navigationMode, - builder: (final context, final navigationMode) => WillPopScope( - onWillPop: () async { - if (_scaffoldKey.currentState!.isDrawerOpen) { - Navigator.pop(context); - return true; - } + builder: (final context, final navigationMode) { + final accounts = accountsSnapshot.data; + final account = accounts?.find(_account.id); + if (accounts == null || account == null) { + return const Scaffold(); + } - _scaffoldKey.currentState!.openDrawer(); - return false; - }, - child: Builder( - builder: (final context) { - if (accountsSnapshot.hasData) { - final accounts = accountsSnapshot.data!; - final account = accounts.find(_account.id); - if (account != null) { - final isQuickBar = navigationMode == NavigationMode.quickBar; - final drawer = Builder( - builder: (final context) => Drawer( - width: isQuickBar ? kQuickBarWidth : null, - child: Container( - padding: isQuickBar ? const EdgeInsets.all(5) : null, - child: Column( + final isQuickBar = navigationMode == NavigationMode.quickBar; + final drawer = Builder( + builder: (final context) => Drawer( + width: isQuickBar ? kQuickBarWidth : null, + child: Container( + padding: isQuickBar ? const EdgeInsets.all(5) : null, + child: Column( + children: [ + Expanded( + child: Scrollbar( + controller: drawerScrollController, + interactive: true, + child: ListView( + controller: drawerScrollController, + // Needed for the drawer header to also render in the statusbar + padding: EdgeInsets.zero, children: [ - Expanded( - child: Scrollbar( - controller: drawerScrollController, - interactive: true, - child: ListView( - controller: drawerScrollController, - // Needed for the drawer header to also render in the statusbar - padding: EdgeInsets.zero, - children: [ - Builder( - builder: (final context) { - if (accountsSnapshot.hasData) { - if (isQuickBar) { - return Column( - children: [ - if (accounts.length != 1) ...[ - for (final account in accounts) ...[ - Container( - margin: const EdgeInsets.symmetric( - vertical: 5, - ), - child: IconButton( - onPressed: () { - _accountsBloc.setActiveAccount(account); - }, - tooltip: account.client.humanReadableID, - icon: IntrinsicHeight( - child: NeonAccountAvatar( - account: account, - ), - ), - ), - ), - ], - Container( - margin: const EdgeInsets.only( - top: 10, - ), - child: Divider( - height: 5, - color: Theme.of(context).appBarTheme.foregroundColor, - ), + Builder( + builder: (final context) { + if (accountsSnapshot.hasData) { + if (isQuickBar) { + return Column( + children: [ + if (accounts.length != 1) ...[ + for (final account in accounts) ...[ + Container( + margin: const EdgeInsets.symmetric( + vertical: 5, + ), + child: IconButton( + onPressed: () { + _accountsBloc.setActiveAccount(account); + }, + tooltip: account.client.humanReadableID, + icon: IntrinsicHeight( + child: NeonAccountAvatar( + account: account, ), - ], - ], - ); - } - return DrawerHeader( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primary, + ), + ), ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - if (capabilities.data != null) ...[ - if (capabilities.data!.capabilities.theming?.name != - null) ...[ - Text( - capabilities.data!.capabilities.theming!.name!, - style: DefaultTextStyle.of(context).style.copyWith( - color: - Theme.of(context).appBarTheme.foregroundColor, - ), - ), - ], - if (capabilities.data!.capabilities.theming?.logo != - null) ...[ - Flexible( - child: NeonCachedUrlImage( - url: capabilities.data!.capabilities.theming!.logo!, - ), - ), - ], - ] else ...[ - NeonException( - capabilities.error, - onRetry: _capabilitiesBloc.refresh, - ), - NeonLinearProgressIndicator( - visible: capabilities.loading, - ), - ], - if (accounts.length != 1) ...[ - DropdownButtonHideUnderline( - child: DropdownButton( - isExpanded: true, - dropdownColor: Theme.of(context).colorScheme.primary, - iconEnabledColor: - Theme.of(context).colorScheme.onBackground, - value: _account.id, - items: accounts - .map>( - (final account) => DropdownMenuItem( - value: account.id, - child: NeonAccountTile( - account: account, - dense: true, - textColor: Theme.of(context) - .appBarTheme - .foregroundColor, - ), - ), - ) - .toList(), - onChanged: (final id) { - if (id != null) { - _accountsBloc.setActiveAccount(accounts.find(id)); - } - }, - ), - ), - ], - ], + ], + Container( + margin: const EdgeInsets.only( + top: 10, ), - ); - } - return Container(); - }, - ), - NeonException( - appImplementations.error, - onlyIcon: isQuickBar, - onRetry: _appsBloc.refresh, - ), - NeonLinearProgressIndicator( - visible: appImplementations.loading, + child: Divider( + height: 5, + color: Theme.of(context).appBarTheme.foregroundColor, + ), + ), + ], + ], + ); + } + return DrawerHeader( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.primary, ), - if (appImplementations.data != null) ...[ - for (final appImplementation in appImplementations.data!) ...[ - StreamBuilder( - stream: appImplementation.getUnreadCounter(_appsBloc) ?? - BehaviorSubject.seeded(0), - builder: (final context, final unreadCounterSnapshot) { - final unreadCount = unreadCounterSnapshot.data ?? 0; - if (isQuickBar) { - return IconButton( - onPressed: () async { - await _appsBloc.setActiveApp(appImplementation.id); - }, - tooltip: appImplementation.name(context), - icon: NeonAppImplementationIcon( - appImplementation: appImplementation, - unreadCount: unreadCount, - color: Theme.of(context).colorScheme.primary, - ), - ); - } - return ListTile( - key: Key('app-${appImplementation.id}'), - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(appImplementation.name(context)), - if (unreadCount > 0) ...[ - Text( - unreadCount.toString(), - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - fontWeight: FontWeight.bold, - fontSize: 14, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (capabilities.data != null) ...[ + if (capabilities.data!.capabilities.theming?.name != null) ...[ + Text( + capabilities.data!.capabilities.theming!.name!, + style: DefaultTextStyle.of(context).style.copyWith( + color: Theme.of(context).appBarTheme.foregroundColor, + ), + ), + ], + if (capabilities.data!.capabilities.theming?.logo != null) ...[ + Flexible( + child: NeonCachedUrlImage( + url: capabilities.data!.capabilities.theming!.logo!, + ), + ), + ], + ] else ...[ + NeonException( + capabilities.error, + onRetry: _capabilitiesBloc.refresh, + ), + NeonLinearProgressIndicator( + visible: capabilities.loading, + ), + ], + if (accounts.length != 1) ...[ + DropdownButtonHideUnderline( + child: DropdownButton( + isExpanded: true, + dropdownColor: Theme.of(context).colorScheme.primary, + iconEnabledColor: Theme.of(context).colorScheme.onBackground, + value: _account.id, + items: accounts + .map>( + (final account) => DropdownMenuItem( + value: account.id, + child: NeonAccountTile( + account: account, + dense: true, + textColor: + Theme.of(context).appBarTheme.foregroundColor, ), ), - ], - ], - ), - leading: appImplementation.buildIcon(context), - minLeadingWidth: 0, - onTap: () async { - await _appsBloc.setActiveApp(appImplementation.id); - - if (!mounted) { - return; + ) + .toList(), + onChanged: (final id) { + if (id != null) { + _accountsBloc.setActiveAccount(accounts.find(id)); } - Scaffold.maybeOf(context)?.closeDrawer(); }, - ); - }, - ), + ), + ), + ], ], - ], - ], - ), - ), + ), + ); + } + return Container(); + }, ), - if (isQuickBar) ...[ - IconButton( - onPressed: () => const SettingsRoute().go(context), - tooltip: AppLocalizations.of(context).settings, - icon: Icon( - Icons.settings, - color: Theme.of(context).appBarTheme.foregroundColor, - ), - ), - ] else ...[ - ListTile( - key: const Key('settings'), - title: Text(AppLocalizations.of(context).settings), - leading: const Icon(Icons.settings), - minLeadingWidth: 0, - onTap: () async { - Scaffold.maybeOf(context)?.closeDrawer(); - const SettingsRoute().go(context); - }, - ), - ], - ], - ), - ), - ), - ); - - return Row( - children: [ - if (navigationMode == NavigationMode.drawerAlwaysVisible) ...[ - drawer, - ], - Expanded( - child: Scaffold( - key: _scaffoldKey, - resizeToAvoidBottomInset: false, - drawer: navigationMode == NavigationMode.drawer ? drawer : null, - appBar: AppBar( - scrolledUnderElevation: navigationMode != NavigationMode.drawer ? 0 : null, - automaticallyImplyLeading: navigationMode == NavigationMode.drawer, - leadingWidth: isQuickBar ? kQuickBarWidth : null, - leading: isQuickBar - ? Container( - padding: const EdgeInsets.all(5), - child: capabilities.data?.capabilities.theming?.logo != null - ? NeonCachedUrlImage( - url: capabilities.data!.capabilities.theming!.logo!, - svgColor: Theme.of(context).iconTheme.color, - ) - : null, - ) - : null, - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - if (appImplementations.data != null && activeAppIDSnapshot.hasData) ...[ - Flexible( - child: Text( - appImplementations.data! - .find(activeAppIDSnapshot.data!)! - .name(context), - ), - ), - ], - if (appImplementations.error != null) ...[ - const SizedBox( - width: 8, - ), - NeonException( - appImplementations.error, - onRetry: _appsBloc.refresh, - onlyIcon: true, - ), - ], - if (appImplementations.loading) ...[ - const SizedBox( - width: 8, - ), - Expanded( - child: NeonLinearProgressIndicator( - color: Theme.of(context).appBarTheme.foregroundColor, - ), - ), - ], - ], - ), - if (accounts.length > 1) ...[ - Text( - account.client.humanReadableID, - style: Theme.of(context).textTheme.bodySmall, - ), - ], - ], + NeonException( + appImplementations.error, + onlyIcon: isQuickBar, + onRetry: _appsBloc.refresh, ), - actions: [ - if (notificationsAppImplementation.data != null) ...[ + NeonLinearProgressIndicator( + visible: appImplementations.loading, + ), + if (appImplementations.data != null) ...[ + for (final appImplementation in appImplementations.data!) ...[ StreamBuilder( - stream: notificationsAppImplementation.data!.getUnreadCounter(_appsBloc), + stream: appImplementation.getUnreadCounter(_appsBloc) ?? + BehaviorSubject.seeded(0), builder: (final context, final unreadCounterSnapshot) { final unreadCount = unreadCounterSnapshot.data ?? 0; - return IconButton( - key: Key('app-${notificationsAppImplementation.data!.id}'), - onPressed: () async { - await _openNotifications( - notificationsAppImplementation.data!, - accounts, - account, - ); - }, - tooltip: AppLocalizations.of(context) - .appImplementationName(notificationsAppImplementation.data!.id), - icon: NeonAppImplementationIcon( - appImplementation: notificationsAppImplementation.data!, - unreadCount: unreadCount, - color: unreadCount > 0 - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onBackground, - size: const Size.square(kAvatarSize * 2 / 3), + if (isQuickBar) { + return IconButton( + onPressed: () async { + await _appsBloc.setActiveApp(appImplementation.id); + }, + tooltip: appImplementation.name(context), + icon: NeonAppImplementationIcon( + appImplementation: appImplementation, + unreadCount: unreadCount, + color: Theme.of(context).colorScheme.primary, + ), + ); + } + return ListTile( + key: Key('app-${appImplementation.id}'), + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(appImplementation.name(context)), + if (unreadCount > 0) ...[ + Text( + unreadCount.toString(), + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold, + fontSize: 14, + ), + ), + ], + ], ), + leading: appImplementation.buildIcon(context), + minLeadingWidth: 0, + onTap: () async { + await _appsBloc.setActiveApp(appImplementation.id); + + if (!mounted) { + return; + } + Scaffold.maybeOf(context)?.closeDrawer(); + }, ); }, ), ], - IconButton( - onPressed: () { - AccountSettingsRoute(accountid: account.id).go(context); - }, - tooltip: AppLocalizations.of(context).settingsAccount, - icon: IntrinsicWidth( - child: NeonAccountAvatar( - account: account, - ), - ), - ), ], - ), - body: Row( - children: [ - if (navigationMode == NavigationMode.quickBar) ...[ - drawer, - ], - Expanded( - child: Column( - children: [ - if (appImplementations.data != null) ...[ - if (appImplementations.data!.isEmpty) ...[ - Expanded( - child: Center( - child: Text( - AppLocalizations.of(context) - .errorNoCompatibleNextcloudAppsFound, - textAlign: TextAlign.center, - ), - ), - ), - ] else ...[ - if (activeAppIDSnapshot.hasData) ...[ - Expanded( - child: appImplementations.data! - .find(activeAppIDSnapshot.data!)! - .buildPage(context, _appsBloc), - ), - ], - ], - ], - ], - ), - ), - ], - ), + ], + ), + ), + ), + if (isQuickBar) ...[ + IconButton( + onPressed: () => const SettingsRoute().go(context), + tooltip: AppLocalizations.of(context).settings, + icon: Icon( + Icons.settings, + color: Theme.of(context).appBarTheme.foregroundColor, + ), + ), + ] else ...[ + ListTile( + key: const Key('settings'), + title: Text(AppLocalizations.of(context).settings), + leading: const Icon(Icons.settings), + minLeadingWidth: 0, + onTap: () async { + Scaffold.maybeOf(context)?.closeDrawer(); + const SettingsRoute().go(context); + }, + ), + ], + ], + ), + ), + ), + ); + final appBar = AppBar( + scrolledUnderElevation: navigationMode != NavigationMode.drawer ? 0 : null, + automaticallyImplyLeading: navigationMode == NavigationMode.drawer, + leadingWidth: isQuickBar ? kQuickBarWidth : null, + leading: isQuickBar + ? Container( + padding: const EdgeInsets.all(5), + child: capabilities.data?.capabilities.theming?.logo != null + ? NeonCachedUrlImage( + url: capabilities.data!.capabilities.theming!.logo!, + svgColor: Theme.of(context).iconTheme.color, + ) + : null, + ) + : null, + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + if (appImplementations.data != null && activeAppIDSnapshot.hasData) ...[ + Flexible( + child: Text( + appImplementations.data!.find(activeAppIDSnapshot.data!)!.name(context), + ), + ), + ], + if (appImplementations.error != null) ...[ + const SizedBox( + width: 8, + ), + NeonException( + appImplementations.error, + onRetry: _appsBloc.refresh, + onlyIcon: true, + ), + ], + if (appImplementations.loading) ...[ + const SizedBox( + width: 8, + ), + Expanded( + child: NeonLinearProgressIndicator( + color: Theme.of(context).appBarTheme.foregroundColor, ), ), ], - ); - } + ], + ), + if (accounts.length > 1) ...[ + Text( + account.client.humanReadableID, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ], + ), + actions: [ + if (notificationsAppImplementation.data != null) ...[ + StreamBuilder( + stream: notificationsAppImplementation.data!.getUnreadCounter(_appsBloc), + builder: (final context, final unreadCounterSnapshot) { + final unreadCount = unreadCounterSnapshot.data ?? 0; + return IconButton( + key: Key('app-${notificationsAppImplementation.data!.id}'), + onPressed: () async { + await _openNotifications( + notificationsAppImplementation.data!, + accounts, + account, + ); + }, + tooltip: AppLocalizations.of(context) + .appImplementationName(notificationsAppImplementation.data!.id), + icon: NeonAppImplementationIcon( + appImplementation: notificationsAppImplementation.data!, + unreadCount: unreadCount, + color: unreadCount > 0 + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.onBackground, + size: const Size.square(kAvatarSize * 2 / 3), + ), + ); + }, + ), + ], + IconButton( + onPressed: () { + AccountSettingsRoute(accountid: account.id).go(context); + }, + tooltip: AppLocalizations.of(context).settingsAccount, + icon: IntrinsicWidth( + child: NeonAccountAvatar( + account: account, + ), + ), + ), + ], + ); + + final body = Builder( + builder: (final context) => Row( + children: [ + if (navigationMode == NavigationMode.quickBar) ...[ + drawer, + ], + Expanded( + child: Column( + children: [ + if (appImplementations.data != null) ...[ + if (appImplementations.data!.isEmpty) ...[ + Expanded( + child: Center( + child: Text( + AppLocalizations.of(context).errorNoCompatibleNextcloudAppsFound, + textAlign: TextAlign.center, + ), + ), + ), + ] else ...[ + if (activeAppIDSnapshot.hasData) ...[ + Expanded( + child: appImplementations.data! + .find(activeAppIDSnapshot.data!)! + .buildPage(context, _appsBloc), + ), + ], + ], + ], + ], + ), + ), + ], + ), + ); + + return WillPopScope( + onWillPop: () async { + if (_scaffoldKey.currentState!.isDrawerOpen) { + Navigator.pop(context); + return true; } - return const Scaffold(); + + _scaffoldKey.currentState!.openDrawer(); + return false; }, - ), - ), + child: Row( + children: [ + if (navigationMode == NavigationMode.drawerAlwaysVisible) ...[ + drawer, + ], + Expanded( + child: Scaffold( + key: _scaffoldKey, + resizeToAvoidBottomInset: false, + drawer: navigationMode == NavigationMode.drawer ? drawer : null, + appBar: appBar, + body: body, + ), + ), + ], + ), + ); + }, ), ), ),