From 7fd221ad9bda18e128307fa960d530c1a7214612 Mon Sep 17 00:00:00 2001 From: Nikolas Rimikis Date: Sun, 18 Jun 2023 18:09:56 +0200 Subject: [PATCH] neon: make move appBar to it's own widget --- packages/neon/neon/lib/neon.dart | 1 + packages/neon/neon/lib/src/pages/home.dart | 131 +----------- .../neon/neon/lib/src/widgets/app_bar.dart | 194 ++++++++++++++++++ 3 files changed, 196 insertions(+), 130 deletions(-) create mode 100644 packages/neon/neon/lib/src/widgets/app_bar.dart diff --git a/packages/neon/neon/lib/neon.dart b/packages/neon/neon/lib/neon.dart index 8f4ea9ff..048c8f32 100644 --- a/packages/neon/neon/lib/neon.dart +++ b/packages/neon/neon/lib/neon.dart @@ -26,6 +26,7 @@ import 'package:neon/src/blocs/blocs.dart'; import 'package:neon/src/models/account.dart'; import 'package:neon/src/models/push_notification.dart'; import 'package:neon/src/router.dart'; +import 'package:neon/src/widgets/app_bar.dart'; import 'package:neon/src/widgets/drawer.dart'; import 'package:neon/src/widgets/drawer_destination.dart'; import 'package:nextcloud/nextcloud.dart'; diff --git a/packages/neon/neon/lib/src/pages/home.dart b/packages/neon/neon/lib/src/pages/home.dart index 6758aeb5..52bbe074 100644 --- a/packages/neon/neon/lib/src/pages/home.dart +++ b/packages/neon/neon/lib/src/pages/home.dart @@ -31,17 +31,6 @@ class _HomePageState extends State { _appsBloc = _accountsBloc.activeAppsBloc; _capabilitiesBloc = _accountsBloc.activeCapabilitiesBloc; - _appsBloc.openNotifications.listen((final _) async { - final notificationsAppImplementation = _appsBloc.notificationsAppImplementation.valueOrNull; - if (notificationsAppImplementation != null) { - await _openNotifications( - notificationsAppImplementation.data!, - _accountsBloc.accounts.value, - _accountsBloc.activeAccount.value!, - ); - } - }); - _appsBloc.appVersions.listen((final values) { if (values == null || !mounted) { return; @@ -108,40 +97,6 @@ class _HomePageState extends State { ); } - Future _openNotifications( - final NotificationsAppInterface app, - final List accounts, - final Account account, - ) async { - final page = Scaffold( - resizeToAvoidBottomInset: false, - appBar: AppBar( - title: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(app.name(context)), - if (accounts.length > 1) ...[ - Text( - account.client.humanReadableID, - style: Theme.of(context).textTheme.bodySmall, - ), - ], - ], - ), - ), - body: app.page, - ); - - await Navigator.of(context).push( - MaterialPageRoute( - builder: (final context) => Provider( - create: (final context) => app.getBloc(account), - child: page, - ), - ), - ); - } - @override void dispose() { drawerScrollController.dispose(); @@ -172,91 +127,7 @@ class _HomePageState extends State { final drawerAlwaysVisible = navigationMode == NavigationMode.drawerAlwaysVisible; const drawer = NeonDrawer(); - final appBar = AppBar( - automaticallyImplyLeading: !drawerAlwaysVisible, - 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.hasError) ...[ - const SizedBox( - width: 8, - ), - NeonException( - appImplementations.error, - onRetry: _appsBloc.refresh, - onlyIcon: true, - ), - ], - if (appImplementations.isLoading) ...[ - 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(notificationsAppImplementation.data!.getBloc(account)), - 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: NeonUserAvatar( - account: account, - ), - ), - ], - ); + const appBar = NeonAppBar(); Widget body = Builder( builder: (final context) => Column( diff --git a/packages/neon/neon/lib/src/widgets/app_bar.dart b/packages/neon/neon/lib/src/widgets/app_bar.dart new file mode 100644 index 00000000..24e5a785 --- /dev/null +++ b/packages/neon/neon/lib/src/widgets/app_bar.dart @@ -0,0 +1,194 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:meta/meta.dart'; +import 'package:neon/l10n/localizations.dart'; +import 'package:neon/neon.dart'; +import 'package:neon/src/router.dart'; +import 'package:provider/provider.dart'; + +@internal +class NeonAppBar extends StatelessWidget implements PreferredSizeWidget { + const NeonAppBar({super.key}); + + @override + Size get preferredSize => const Size.fromHeight(kToolbarHeight); + + @override + Widget build(final BuildContext context) { + final accountsBloc = Provider.of(context, listen: false); + final accounts = accountsBloc.accounts.value; + final account = accountsBloc.activeAccount.value!; + final appsBloc = accountsBloc.activeAppsBloc; + + return ResultBuilder>.behaviorSubject( + stream: appsBloc.appImplementations, + builder: (final context, final appImplementations) => StreamBuilder( + stream: appsBloc.activeAppID, + builder: (final context, final activeAppIDSnapshot) => AppBar( + 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.isLoading) ...[ + 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: [ + const NotificationIconButton(), + IconButton( + onPressed: () { + AccountSettingsRoute(accountid: account.id).go(context); + }, + tooltip: AppLocalizations.of(context).settingsAccount, + icon: NeonUserAvatar( + account: account, + ), + ), + ], + ), + ), + ); + } +} + +@internal +class NotificationIconButton extends StatefulWidget { + const NotificationIconButton({ + super.key, + }); + + @override + State createState() => _NotificationIconButtonState(); +} + +class _NotificationIconButtonState extends State { + late AccountsBloc _accountsBloc; + late AppsBloc _appsBloc; + late List _accounts; + late Account _account; + late StreamSubscription notificationSubscription; + + @override + void initState() { + super.initState(); + _accountsBloc = Provider.of(context, listen: false); + _appsBloc = _accountsBloc.activeAppsBloc; + _accounts = _accountsBloc.accounts.value; + _account = _accountsBloc.activeAccount.value!; + + notificationSubscription = _appsBloc.openNotifications.listen((final _) async { + final notificationsAppImplementation = _appsBloc.notificationsAppImplementation.valueOrNull; + if (notificationsAppImplementation != null) { + await _openNotifications(notificationsAppImplementation.data!); + } + }); + } + + @override + void dispose() { + unawaited(notificationSubscription.cancel()); + + super.dispose(); + } + + // TODO: migrate to go_router with a separate page + Future _openNotifications( + final NotificationsAppInterface app, + ) async { + final page = Scaffold( + resizeToAvoidBottomInset: false, + appBar: AppBar( + title: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(app.name(context)), + if (_accounts.length > 1) ...[ + Text( + _account.client.humanReadableID, + style: Theme.of(context).textTheme.bodySmall, + ), + ], + ], + ), + ), + body: app.page, + ); + + await Navigator.of(context).push( + MaterialPageRoute( + builder: (final context) => Provider( + create: (final context) => app.getBloc(_account), + child: page, + ), + ), + ); + } + + @override + Widget build(final BuildContext context) => ResultBuilder.behaviorSubject( + stream: _appsBloc.notificationsAppImplementation, + builder: (final context, final notificationsAppImplementation) { + if (notificationsAppImplementation.data == null) { + return const SizedBox.shrink(); + } + + return StreamBuilder( + stream: notificationsAppImplementation.data! + .getUnreadCounter(notificationsAppImplementation.data!.getBloc(_account)), + builder: (final context, final unreadCounterSnapshot) { + final unreadCount = unreadCounterSnapshot.data ?? 0; + return IconButton( + key: Key('app-${notificationsAppImplementation.data!.id}'), + onPressed: () async { + await _openNotifications(notificationsAppImplementation.data!); + }, + 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), + ), + ); + }, + ); + }, + ); +}