From 74a2e6e14410b870c3b92c6da9cf525269b13f41 Mon Sep 17 00:00:00 2001 From: jld3103 Date: Sat, 13 Aug 2022 10:09:46 +0200 Subject: [PATCH 1/2] neon: Refactor app implementations to only override methods --- packages/neon/lib/src/apps/files/app.dart | 43 ++++++++++--------- packages/neon/lib/src/apps/news/app.dart | 41 ++++++++++-------- packages/neon/lib/src/apps/notes/app.dart | 40 +++++++++-------- .../neon/lib/src/apps/notifications/app.dart | 40 +++++++++-------- packages/neon/lib/src/pages/home/home.dart | 2 +- .../lib/src/utils/app_implementation.dart | 29 +++++++------ 6 files changed, 105 insertions(+), 90 deletions(-) diff --git a/packages/neon/lib/src/apps/files/app.dart b/packages/neon/lib/src/apps/files/app.dart index 7a80f83e..ae159865 100644 --- a/packages/neon/lib/src/apps/files/app.dart +++ b/packages/neon/lib/src/apps/files/app.dart @@ -25,7 +25,6 @@ import 'package:path/path.dart' as p; import 'package:provider/provider.dart'; import 'package:rxdart/rxdart.dart'; import 'package:settings/settings.dart'; -import 'package:shared_preferences/shared_preferences.dart'; part 'dialogs/choose_create.dart'; part 'dialogs/choose_folder.dart'; @@ -40,23 +39,27 @@ part 'widgets/browser_view.dart'; part 'widgets/file_preview.dart'; class FilesApp extends AppImplementation { - FilesApp( - final SharedPreferences sharedPreferences, - final RequestManager requestManager, - final NeonPlatform platform, - ) : super( - 'files', - (final localizations) => localizations.filesName, - sharedPreferences, - FilesAppSpecificOptions.new, - (final options, final client) => FilesBloc( - options, - requestManager, - client, - platform, - ), - (final context, final bloc) => FilesMainPage( - bloc: bloc, - ), - ); + FilesApp(super.sharedPreferences, super.requestManager, super.platform); + + @override + String id = 'files'; + + @override + String nameFromLocalization(AppLocalizations localizations) => localizations.filesName; + + @override + FilesAppSpecificOptions buildOptions(Storage storage) => FilesAppSpecificOptions(storage); + + @override + FilesBloc buildBloc(NextcloudClient client) => FilesBloc( + options, + requestManager, + client, + platform, + ); + + @override + Widget buildPage(BuildContext context, FilesBloc bloc) => FilesMainPage( + bloc: bloc, + ); } diff --git a/packages/neon/lib/src/apps/news/app.dart b/packages/neon/lib/src/apps/news/app.dart index fdbba317..a73582b5 100644 --- a/packages/neon/lib/src/apps/news/app.dart +++ b/packages/neon/lib/src/apps/news/app.dart @@ -21,7 +21,6 @@ import 'package:provider/provider.dart'; import 'package:rxdart/rxdart.dart'; import 'package:settings/settings.dart'; import 'package:share_plus/share_plus.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:sort_box/sort_box.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:wakelock/wakelock.dart'; @@ -48,22 +47,26 @@ part 'widgets/folder_view.dart'; part 'widgets/folders_view.dart'; class NewsApp extends AppImplementation { - NewsApp( - final SharedPreferences sharedPreferences, - final RequestManager requestManager, - final NeonPlatform platform, - ) : super( - 'news', - (final localizations) => localizations.newsName, - sharedPreferences, - (final storage) => NewsAppSpecificOptions(storage, platform), - (final options, final client) => NewsBloc( - options, - requestManager, - client, - ), - (final context, final bloc) => NewsMainPage( - bloc: bloc, - ), - ); + NewsApp(super.sharedPreferences, super.requestManager, super.platform); + + @override + String id = 'news'; + + @override + String nameFromLocalization(AppLocalizations localizations) => localizations.newsName; + + @override + NewsAppSpecificOptions buildOptions(Storage storage) => NewsAppSpecificOptions(storage, platform); + + @override + NewsBloc buildBloc(NextcloudClient client) => NewsBloc( + options, + requestManager, + client, + ); + + @override + Widget buildPage(BuildContext context, NewsBloc bloc) => NewsMainPage( + bloc: bloc, + ); } diff --git a/packages/neon/lib/src/apps/notes/app.dart b/packages/neon/lib/src/apps/notes/app.dart index 121402c8..d3b3a71b 100644 --- a/packages/neon/lib/src/apps/notes/app.dart +++ b/packages/neon/lib/src/apps/notes/app.dart @@ -15,7 +15,6 @@ import 'package:nextcloud/nextcloud.dart'; import 'package:provider/provider.dart'; import 'package:rxdart/rxdart.dart'; import 'package:settings/settings.dart'; -import 'package:shared_preferences/shared_preferences.dart'; import 'package:sort_box/sort_box.dart'; import 'package:url_launcher/url_launcher_string.dart'; import 'package:wakelock/wakelock.dart'; @@ -35,21 +34,26 @@ part 'widgets/category_select.dart'; part 'widgets/notes_view.dart'; class NotesApp extends AppImplementation { - NotesApp( - final SharedPreferences sharedPreferences, - final RequestManager requestManager, - ) : super( - 'notes', - (final localizations) => localizations.notesName, - sharedPreferences, - NotesAppSpecificOptions.new, - (final options, final client) => NotesBloc( - options, - requestManager, - client, - ), - (final context, final bloc) => NotesMainPage( - bloc: bloc, - ), - ); + NotesApp(super.sharedPreferences, super.requestManager, super.platform); + + @override + String id = 'notes'; + + @override + String nameFromLocalization(AppLocalizations localizations) => localizations.notesName; + + @override + NotesAppSpecificOptions buildOptions(Storage storage) => NotesAppSpecificOptions(storage); + + @override + NotesBloc buildBloc(NextcloudClient client) => NotesBloc( + options, + requestManager, + client, + ); + + @override + Widget buildPage(BuildContext context, NotesBloc bloc) => NotesMainPage( + bloc: bloc, + ); } diff --git a/packages/neon/lib/src/apps/notifications/app.dart b/packages/neon/lib/src/apps/notifications/app.dart index 1b1c4da9..42043a6c 100644 --- a/packages/neon/lib/src/apps/notifications/app.dart +++ b/packages/neon/lib/src/apps/notifications/app.dart @@ -8,27 +8,31 @@ import 'package:neon/src/apps/notifications/blocs/notifications.dart'; import 'package:neon/src/neon.dart'; import 'package:nextcloud/nextcloud.dart'; import 'package:provider/provider.dart'; -import 'package:shared_preferences/shared_preferences.dart'; part 'options.dart'; part 'pages/main.dart'; class NotificationsApp extends AppImplementation { - NotificationsApp( - final SharedPreferences sharedPreferences, - final RequestManager requestManager, - ) : super( - 'notifications', - (final localizations) => localizations.notificationsName, - sharedPreferences, - NotificationsAppSpecificOptions.new, - (final options, final client) => NotificationsBloc( - options, - requestManager, - client, - ), - (final context, final bloc) => NotificationsMainPage( - bloc: bloc, - ), - ); + NotificationsApp(super.sharedPreferences, super.requestManager, super.platform); + + @override + String id = 'notifications'; + + @override + String nameFromLocalization(AppLocalizations localizations) => localizations.notificationsName; + + @override + NotificationsAppSpecificOptions buildOptions(Storage storage) => NotificationsAppSpecificOptions(storage); + + @override + NotificationsBloc buildBloc(NextcloudClient client) => NotificationsBloc( + options, + requestManager, + client, + ); + + @override + Widget buildPage(BuildContext context, NotificationsBloc bloc) => NotificationsMainPage( + bloc: bloc, + ); } diff --git a/packages/neon/lib/src/pages/home/home.dart b/packages/neon/lib/src/pages/home/home.dart index 0e5d7b60..f4c9d5cc 100644 --- a/packages/neon/lib/src/pages/home/home.dart +++ b/packages/neon/lib/src/pages/home/home.dart @@ -565,7 +565,7 @@ class _HomePageState extends State with tray.TrayListener, WindowListe Expanded( child: appsData .singleWhere((final a) => a.id == activeAppIDSnapshot.data!) - .buildPage(context, _appsBloc), + .buildPageFromAppsBloc(context, _appsBloc), ), ], ], diff --git a/packages/neon/lib/src/utils/app_implementation.dart b/packages/neon/lib/src/utils/app_implementation.dart index 0540d0f6..e2edba62 100644 --- a/packages/neon/lib/src/utils/app_implementation.dart +++ b/packages/neon/lib/src/utils/app_implementation.dart @@ -8,34 +8,35 @@ List getAppImplementations( [ FilesApp(sharedPreferences, requestManager, platform), NewsApp(sharedPreferences, requestManager, platform), - NotesApp(sharedPreferences, requestManager), - NotificationsApp(sharedPreferences, requestManager), + NotesApp(sharedPreferences, requestManager, platform), + NotificationsApp(sharedPreferences, requestManager, platform), ]; abstract class AppImplementation { AppImplementation( - this.id, - this.nameFromLocalization, final SharedPreferences sharedPreferences, - final R Function(Storage) buildOptions, - this._buildBloc, - this._buildPage, + final this.requestManager, + final this.platform, ) { final storage = Storage('app-$id', sharedPreferences); options = buildOptions(storage); } - final String Function(AppLocalizations) nameFromLocalization; + String get id; + final RequestManager requestManager; + final NeonPlatform platform; + + String nameFromLocalization(AppLocalizations localizations); String name(BuildContext context) => nameFromLocalization(AppLocalizations.of(context)); - final String id; + late final R options; - final T Function(R options, NextcloudClient client) _buildBloc; - final Widget Function(BuildContext context, T bloc) _buildPage; + R buildOptions(Storage storage); - T buildBloc(final NextcloudClient client) => _buildBloc(options, client); + T buildBloc(final NextcloudClient client); - Widget buildPage(final BuildContext context, final AppsBloc appsBloc) => - _buildPage(context, appsBloc.getAppBloc(this)); + Widget buildPage(BuildContext context, T bloc); + Widget buildPageFromAppsBloc(final BuildContext context, final AppsBloc appsBloc) => + buildPage(context, appsBloc.getAppBloc(this)); Widget buildIcon( final BuildContext context, { From 570bd0b725b1f34dadda8cc92a95131a402f6825 Mon Sep 17 00:00:00 2001 From: jld3103 Date: Sat, 13 Aug 2022 10:49:00 +0200 Subject: [PATCH 2/2] 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, {