From 570bd0b725b1f34dadda8cc92a95131a402f6825 Mon Sep 17 00:00:00 2001 From: jld3103 Date: Sat, 13 Aug 2022 10:49:00 +0200 Subject: [PATCH] neon: Implement app unread counter --- packages/neon/lib/src/apps/files/app.dart | 8 +++++-- packages/neon/lib/src/apps/news/app.dart | 8 +++++-- .../neon/lib/src/apps/news/blocs/news.dart | 15 ++++++++++++ .../lib/src/apps/news/blocs/news.rxb.g.dart | 8 +++++++ packages/neon/lib/src/apps/notes/app.dart | 8 +++++-- .../neon/lib/src/apps/notifications/app.dart | 10 ++++++-- .../notifications/blocs/notifications.dart | 14 +++++++++++ .../blocs/notifications.rxb.g.dart | 8 +++++++ packages/neon/lib/src/pages/home/home.dart | 23 +++++++++++++++++-- .../lib/src/utils/app_implementation.dart | 6 ++--- 10 files changed, 95 insertions(+), 13 deletions(-) diff --git a/packages/neon/lib/src/apps/files/app.dart b/packages/neon/lib/src/apps/files/app.dart index ae159865..85f4ea32 100644 --- a/packages/neon/lib/src/apps/files/app.dart +++ b/packages/neon/lib/src/apps/files/app.dart @@ -18,6 +18,7 @@ import 'package:neon/l10n/localizations.dart'; import 'package:neon/src/apps/files/blocs/browser.dart'; import 'package:neon/src/apps/files/blocs/files.dart'; import 'package:neon/src/blocs/accounts.dart'; +import 'package:neon/src/blocs/apps.dart'; import 'package:neon/src/models/account.dart'; import 'package:neon/src/neon.dart'; import 'package:nextcloud/nextcloud.dart'; @@ -59,7 +60,10 @@ class FilesApp extends AppImplementation { ); @override - Widget buildPage(BuildContext context, FilesBloc bloc) => FilesMainPage( - bloc: bloc, + Widget buildPage(BuildContext context, AppsBloc appsBloc) => FilesMainPage( + bloc: appsBloc.getAppBloc(this), ); + + @override + BehaviorSubject? getUnreadCounter(AppsBloc appsBloc) => null; } diff --git a/packages/neon/lib/src/apps/news/app.dart b/packages/neon/lib/src/apps/news/app.dart index a73582b5..9ad8e6bc 100644 --- a/packages/neon/lib/src/apps/news/app.dart +++ b/packages/neon/lib/src/apps/news/app.dart @@ -14,6 +14,7 @@ import 'package:neon/l10n/localizations.dart'; import 'package:neon/src/apps/news/blocs/articles.dart'; import 'package:neon/src/apps/news/blocs/news.dart'; import 'package:neon/src/blocs/accounts.dart'; +import 'package:neon/src/blocs/apps.dart'; import 'package:neon/src/models/account.dart'; import 'package:neon/src/neon.dart'; import 'package:nextcloud/nextcloud.dart'; @@ -66,7 +67,10 @@ class NewsApp extends AppImplementation { ); @override - Widget buildPage(BuildContext context, NewsBloc bloc) => NewsMainPage( - bloc: bloc, + Widget buildPage(BuildContext context, AppsBloc appsBloc) => NewsMainPage( + bloc: appsBloc.getAppBloc(this), ); + + @override + BehaviorSubject? getUnreadCounter(AppsBloc appsBloc) => appsBloc.getAppBloc(this).unreadCounter; } diff --git a/packages/neon/lib/src/apps/news/blocs/news.dart b/packages/neon/lib/src/apps/news/blocs/news.dart index 12503d91..ef89e558 100644 --- a/packages/neon/lib/src/apps/news/blocs/news.dart +++ b/packages/neon/lib/src/apps/news/blocs/news.dart @@ -38,6 +38,8 @@ abstract class NewsBlocStates { BehaviorSubject>> get feeds; Stream get errors; + + BehaviorSubject get unreadCounter; } @RxBloc() @@ -146,6 +148,14 @@ class NewsBloc extends $NewsBloc { }); }); + mainArticlesBloc.articles.listen((final result) { + if (result.data != null) { + final type = mainArticlesBloc.filterType.valueOrNull; + _unreadCounterSubject + .add(result.data!.where((final a) => type == FilterType.starred ? a.starred! : a.unread!).length); + } + }); + _loadAll(false); } @@ -223,12 +233,14 @@ class NewsBloc extends $NewsBloc { final _foldersSubject = BehaviorSubject>>(); final _feedsSubject = BehaviorSubject>>(); final _errorsStreamController = StreamController(); + final _unreadCounterSubject = BehaviorSubject(); @override void dispose() { _foldersSubject.close(); _feedsSubject.close(); _errorsStreamController.close(); + _unreadCounterSubject.close(); super.dispose(); } @@ -240,4 +252,7 @@ class NewsBloc extends $NewsBloc { @override Stream _mapToErrorsState() => _errorsStreamController.stream.asBroadcastStream(); + + @override + BehaviorSubject _mapToUnreadCounterState() => _unreadCounterSubject; } diff --git a/packages/neon/lib/src/apps/news/blocs/news.rxb.g.dart b/packages/neon/lib/src/apps/news/blocs/news.rxb.g.dart index ec39268d..e9b3694a 100644 --- a/packages/neon/lib/src/apps/news/blocs/news.rxb.g.dart +++ b/packages/neon/lib/src/apps/news/blocs/news.rxb.g.dart @@ -57,6 +57,9 @@ abstract class $NewsBloc extends RxBlocBase implements NewsBlocEvents, NewsBlocS /// The state of [errors] implemented in [_mapToErrorsState] late final Stream _errorsState = _mapToErrorsState(); + /// The state of [unreadCounter] implemented in [_mapToUnreadCounterState] + late final BehaviorSubject _unreadCounterState = _mapToUnreadCounterState(); + @override void refresh({required bool mainArticlesToo}) => _$refreshEvent.add(mainArticlesToo); @@ -96,12 +99,17 @@ abstract class $NewsBloc extends RxBlocBase implements NewsBlocEvents, NewsBlocS @override Stream get errors => _errorsState; + @override + BehaviorSubject get unreadCounter => _unreadCounterState; + BehaviorSubject>> _mapToFoldersState(); BehaviorSubject>> _mapToFeedsState(); Stream _mapToErrorsState(); + BehaviorSubject _mapToUnreadCounterState(); + @override NewsBlocEvents get events => this; diff --git a/packages/neon/lib/src/apps/notes/app.dart b/packages/neon/lib/src/apps/notes/app.dart index d3b3a71b..28c08679 100644 --- a/packages/neon/lib/src/apps/notes/app.dart +++ b/packages/neon/lib/src/apps/notes/app.dart @@ -9,6 +9,7 @@ import 'package:intersperse/intersperse.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:neon/l10n/localizations.dart'; import 'package:neon/src/apps/notes/blocs/notes.dart'; +import 'package:neon/src/blocs/apps.dart'; import 'package:neon/src/neon.dart'; import 'package:neon/src/widgets/custom_auto_complete.dart'; import 'package:nextcloud/nextcloud.dart'; @@ -53,7 +54,10 @@ class NotesApp extends AppImplementation { ); @override - Widget buildPage(BuildContext context, NotesBloc bloc) => NotesMainPage( - bloc: bloc, + Widget buildPage(BuildContext context, AppsBloc appsBloc) => NotesMainPage( + bloc: appsBloc.getAppBloc(this), ); + + @override + BehaviorSubject? getUnreadCounter(AppsBloc appsBloc) => null; } diff --git a/packages/neon/lib/src/apps/notifications/app.dart b/packages/neon/lib/src/apps/notifications/app.dart index 42043a6c..24c948af 100644 --- a/packages/neon/lib/src/apps/notifications/app.dart +++ b/packages/neon/lib/src/apps/notifications/app.dart @@ -5,9 +5,11 @@ import 'package:intersperse/intersperse.dart'; import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; import 'package:neon/l10n/localizations.dart'; import 'package:neon/src/apps/notifications/blocs/notifications.dart'; +import 'package:neon/src/blocs/apps.dart'; import 'package:neon/src/neon.dart'; import 'package:nextcloud/nextcloud.dart'; import 'package:provider/provider.dart'; +import 'package:rxdart/rxdart.dart'; part 'options.dart'; part 'pages/main.dart'; @@ -32,7 +34,11 @@ class NotificationsApp extends AppImplementation NotificationsMainPage( - bloc: bloc, + Widget buildPage(BuildContext context, AppsBloc appsBloc) => NotificationsMainPage( + bloc: appsBloc.getAppBloc(this), ); + + @override + BehaviorSubject? getUnreadCounter(AppsBloc appsBloc) => + appsBloc.getAppBloc(this).unreadCounter; } diff --git a/packages/neon/lib/src/apps/notifications/blocs/notifications.dart b/packages/neon/lib/src/apps/notifications/blocs/notifications.dart index 6a58d687..7f866dcf 100644 --- a/packages/neon/lib/src/apps/notifications/blocs/notifications.dart +++ b/packages/neon/lib/src/apps/notifications/blocs/notifications.dart @@ -21,6 +21,8 @@ abstract class NotificationsBlocStates { BehaviorSubject>> get notifications; Stream get errors; + + BehaviorSubject get unreadCounter; } @RxBloc() @@ -35,10 +37,17 @@ class NotificationsBloc extends $NotificationsBloc { _$deleteNotificationEvent.listen((final notification) { _wrapAction(() async => client.notifications.deleteNotification(notification.notificationId!)); }); + _$deleteAllNotificationsEvent.listen((final notification) { _wrapAction(() async => client.notifications.deleteAllNotifications()); }); + _notificationsSubject.listen((final result) { + if (result.data != null) { + _unreadCounterSubject.add(result.data!.length); + } + }); + _loadNotifications(); } @@ -70,11 +79,13 @@ class NotificationsBloc extends $NotificationsBloc { final _notificationsSubject = BehaviorSubject>>(); final _errorsStreamController = StreamController(); + final _unreadCounterSubject = BehaviorSubject(); @override void dispose() { _notificationsSubject.close(); _errorsStreamController.close(); + _unreadCounterSubject.close(); super.dispose(); } @@ -83,4 +94,7 @@ class NotificationsBloc extends $NotificationsBloc { @override Stream _mapToErrorsState() => _errorsStreamController.stream.asBroadcastStream(); + + @override + BehaviorSubject _mapToUnreadCounterState() => _unreadCounterSubject; } diff --git a/packages/neon/lib/src/apps/notifications/blocs/notifications.rxb.g.dart b/packages/neon/lib/src/apps/notifications/blocs/notifications.rxb.g.dart index 37644669..b8750028 100644 --- a/packages/neon/lib/src/apps/notifications/blocs/notifications.rxb.g.dart +++ b/packages/neon/lib/src/apps/notifications/blocs/notifications.rxb.g.dart @@ -34,6 +34,9 @@ abstract class $NotificationsBloc extends RxBlocBase /// The state of [errors] implemented in [_mapToErrorsState] late final Stream _errorsState = _mapToErrorsState(); + /// The state of [unreadCounter] implemented in [_mapToUnreadCounterState] + late final BehaviorSubject _unreadCounterState = _mapToUnreadCounterState(); + @override void refresh() => _$refreshEvent.add(null); @@ -49,10 +52,15 @@ abstract class $NotificationsBloc extends RxBlocBase @override Stream get errors => _errorsState; + @override + BehaviorSubject get unreadCounter => _unreadCounterState; + BehaviorSubject>> _mapToNotificationsState(); Stream _mapToErrorsState(); + BehaviorSubject _mapToUnreadCounterState(); + @override NotificationsBlocEvents get events => this; diff --git a/packages/neon/lib/src/pages/home/home.dart b/packages/neon/lib/src/pages/home/home.dart index f4c9d5cc..8fd9c260 100644 --- a/packages/neon/lib/src/pages/home/home.dart +++ b/packages/neon/lib/src/pages/home/home.dart @@ -508,7 +508,26 @@ class _HomePageState extends State with tray.TrayListener, WindowListe if (appsData.map((final a) => a.id).contains(appImplementation.id)) ...[ ListTile( key: Key('app-${appImplementation.id}'), - title: Text(appImplementation.name(context)), + title: StreamBuilder( + stream: appImplementation.getUnreadCounter(_appsBloc) ?? + BehaviorSubject.seeded(0), + builder: (final context, final unreadCounterSnapshot) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(appImplementation.name(context)), + if (unreadCounterSnapshot.hasData && unreadCounterSnapshot.data! > 0) ...[ + Text( + unreadCounterSnapshot.data!.toString(), + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + fontWeight: FontWeight.bold, + fontSize: 14, + ), + ), + ], + ], + ), + ), leading: appImplementation.buildIcon(context), minLeadingWidth: 0, onTap: () { @@ -565,7 +584,7 @@ class _HomePageState extends State with tray.TrayListener, WindowListe Expanded( child: appsData .singleWhere((final a) => a.id == activeAppIDSnapshot.data!) - .buildPageFromAppsBloc(context, _appsBloc), + .buildPage(context, _appsBloc), ), ], ], diff --git a/packages/neon/lib/src/utils/app_implementation.dart b/packages/neon/lib/src/utils/app_implementation.dart index e2edba62..bbaabfde 100644 --- a/packages/neon/lib/src/utils/app_implementation.dart +++ b/packages/neon/lib/src/utils/app_implementation.dart @@ -34,9 +34,9 @@ abstract class AppImplementation - buildPage(context, appsBloc.getAppBloc(this)); + BehaviorSubject? getUnreadCounter(AppsBloc appsBloc); + + Widget buildPage(BuildContext context, AppsBloc appsBloc); Widget buildIcon( final BuildContext context, {