Browse Source

neon: Implement app unread counter

pull/50/head
jld3103 2 years ago
parent
commit
570bd0b725
No known key found for this signature in database
GPG Key ID: 9062417B9E8EB7B3
  1. 8
      packages/neon/lib/src/apps/files/app.dart
  2. 8
      packages/neon/lib/src/apps/news/app.dart
  3. 15
      packages/neon/lib/src/apps/news/blocs/news.dart
  4. 8
      packages/neon/lib/src/apps/news/blocs/news.rxb.g.dart
  5. 8
      packages/neon/lib/src/apps/notes/app.dart
  6. 10
      packages/neon/lib/src/apps/notifications/app.dart
  7. 14
      packages/neon/lib/src/apps/notifications/blocs/notifications.dart
  8. 8
      packages/neon/lib/src/apps/notifications/blocs/notifications.rxb.g.dart
  9. 23
      packages/neon/lib/src/pages/home/home.dart
  10. 6
      packages/neon/lib/src/utils/app_implementation.dart

8
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/browser.dart';
import 'package:neon/src/apps/files/blocs/files.dart'; import 'package:neon/src/apps/files/blocs/files.dart';
import 'package:neon/src/blocs/accounts.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/models/account.dart';
import 'package:neon/src/neon.dart'; import 'package:neon/src/neon.dart';
import 'package:nextcloud/nextcloud.dart'; import 'package:nextcloud/nextcloud.dart';
@ -59,7 +60,10 @@ class FilesApp extends AppImplementation<FilesBloc, FilesAppSpecificOptions> {
); );
@override @override
Widget buildPage(BuildContext context, FilesBloc bloc) => FilesMainPage( Widget buildPage(BuildContext context, AppsBloc appsBloc) => FilesMainPage(
bloc: bloc, bloc: appsBloc.getAppBloc(this),
); );
@override
BehaviorSubject<int>? getUnreadCounter(AppsBloc appsBloc) => null;
} }

8
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/articles.dart';
import 'package:neon/src/apps/news/blocs/news.dart'; import 'package:neon/src/apps/news/blocs/news.dart';
import 'package:neon/src/blocs/accounts.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/models/account.dart';
import 'package:neon/src/neon.dart'; import 'package:neon/src/neon.dart';
import 'package:nextcloud/nextcloud.dart'; import 'package:nextcloud/nextcloud.dart';
@ -66,7 +67,10 @@ class NewsApp extends AppImplementation<NewsBloc, NewsAppSpecificOptions> {
); );
@override @override
Widget buildPage(BuildContext context, NewsBloc bloc) => NewsMainPage( Widget buildPage(BuildContext context, AppsBloc appsBloc) => NewsMainPage(
bloc: bloc, bloc: appsBloc.getAppBloc(this),
); );
@override
BehaviorSubject<int>? getUnreadCounter(AppsBloc appsBloc) => appsBloc.getAppBloc<NewsBloc>(this).unreadCounter;
} }

15
packages/neon/lib/src/apps/news/blocs/news.dart

@ -38,6 +38,8 @@ abstract class NewsBlocStates {
BehaviorSubject<Result<List<NewsFeed>>> get feeds; BehaviorSubject<Result<List<NewsFeed>>> get feeds;
Stream<Exception> get errors; Stream<Exception> get errors;
BehaviorSubject<int> get unreadCounter;
} }
@RxBloc() @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); _loadAll(false);
} }
@ -223,12 +233,14 @@ class NewsBloc extends $NewsBloc {
final _foldersSubject = BehaviorSubject<Result<List<NewsFolder>>>(); final _foldersSubject = BehaviorSubject<Result<List<NewsFolder>>>();
final _feedsSubject = BehaviorSubject<Result<List<NewsFeed>>>(); final _feedsSubject = BehaviorSubject<Result<List<NewsFeed>>>();
final _errorsStreamController = StreamController<Exception>(); final _errorsStreamController = StreamController<Exception>();
final _unreadCounterSubject = BehaviorSubject<int>();
@override @override
void dispose() { void dispose() {
_foldersSubject.close(); _foldersSubject.close();
_feedsSubject.close(); _feedsSubject.close();
_errorsStreamController.close(); _errorsStreamController.close();
_unreadCounterSubject.close();
super.dispose(); super.dispose();
} }
@ -240,4 +252,7 @@ class NewsBloc extends $NewsBloc {
@override @override
Stream<Exception> _mapToErrorsState() => _errorsStreamController.stream.asBroadcastStream(); Stream<Exception> _mapToErrorsState() => _errorsStreamController.stream.asBroadcastStream();
@override
BehaviorSubject<int> _mapToUnreadCounterState() => _unreadCounterSubject;
} }

8
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] /// The state of [errors] implemented in [_mapToErrorsState]
late final Stream<Exception> _errorsState = _mapToErrorsState(); late final Stream<Exception> _errorsState = _mapToErrorsState();
/// The state of [unreadCounter] implemented in [_mapToUnreadCounterState]
late final BehaviorSubject<int> _unreadCounterState = _mapToUnreadCounterState();
@override @override
void refresh({required bool mainArticlesToo}) => _$refreshEvent.add(mainArticlesToo); void refresh({required bool mainArticlesToo}) => _$refreshEvent.add(mainArticlesToo);
@ -96,12 +99,17 @@ abstract class $NewsBloc extends RxBlocBase implements NewsBlocEvents, NewsBlocS
@override @override
Stream<Exception> get errors => _errorsState; Stream<Exception> get errors => _errorsState;
@override
BehaviorSubject<int> get unreadCounter => _unreadCounterState;
BehaviorSubject<Result<List<NewsFolder>>> _mapToFoldersState(); BehaviorSubject<Result<List<NewsFolder>>> _mapToFoldersState();
BehaviorSubject<Result<List<NewsFeed>>> _mapToFeedsState(); BehaviorSubject<Result<List<NewsFeed>>> _mapToFeedsState();
Stream<Exception> _mapToErrorsState(); Stream<Exception> _mapToErrorsState();
BehaviorSubject<int> _mapToUnreadCounterState();
@override @override
NewsBlocEvents get events => this; NewsBlocEvents get events => this;

8
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:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:neon/l10n/localizations.dart'; import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/apps/notes/blocs/notes.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/neon.dart';
import 'package:neon/src/widgets/custom_auto_complete.dart'; import 'package:neon/src/widgets/custom_auto_complete.dart';
import 'package:nextcloud/nextcloud.dart'; import 'package:nextcloud/nextcloud.dart';
@ -53,7 +54,10 @@ class NotesApp extends AppImplementation<NotesBloc, NotesAppSpecificOptions> {
); );
@override @override
Widget buildPage(BuildContext context, NotesBloc bloc) => NotesMainPage( Widget buildPage(BuildContext context, AppsBloc appsBloc) => NotesMainPage(
bloc: bloc, bloc: appsBloc.getAppBloc(this),
); );
@override
BehaviorSubject<int>? getUnreadCounter(AppsBloc appsBloc) => null;
} }

10
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:material_design_icons_flutter/material_design_icons_flutter.dart';
import 'package:neon/l10n/localizations.dart'; import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/apps/notifications/blocs/notifications.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:neon/src/neon.dart';
import 'package:nextcloud/nextcloud.dart'; import 'package:nextcloud/nextcloud.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:rxdart/rxdart.dart';
part 'options.dart'; part 'options.dart';
part 'pages/main.dart'; part 'pages/main.dart';
@ -32,7 +34,11 @@ class NotificationsApp extends AppImplementation<NotificationsBloc, Notification
); );
@override @override
Widget buildPage(BuildContext context, NotificationsBloc bloc) => NotificationsMainPage( Widget buildPage(BuildContext context, AppsBloc appsBloc) => NotificationsMainPage(
bloc: bloc, bloc: appsBloc.getAppBloc(this),
); );
@override
BehaviorSubject<int>? getUnreadCounter(AppsBloc appsBloc) =>
appsBloc.getAppBloc<NotificationsBloc>(this).unreadCounter;
} }

14
packages/neon/lib/src/apps/notifications/blocs/notifications.dart

@ -21,6 +21,8 @@ abstract class NotificationsBlocStates {
BehaviorSubject<Result<List<NotificationsNotification>>> get notifications; BehaviorSubject<Result<List<NotificationsNotification>>> get notifications;
Stream<Exception> get errors; Stream<Exception> get errors;
BehaviorSubject<int> get unreadCounter;
} }
@RxBloc() @RxBloc()
@ -35,10 +37,17 @@ class NotificationsBloc extends $NotificationsBloc {
_$deleteNotificationEvent.listen((final notification) { _$deleteNotificationEvent.listen((final notification) {
_wrapAction(() async => client.notifications.deleteNotification(notification.notificationId!)); _wrapAction(() async => client.notifications.deleteNotification(notification.notificationId!));
}); });
_$deleteAllNotificationsEvent.listen((final notification) { _$deleteAllNotificationsEvent.listen((final notification) {
_wrapAction(() async => client.notifications.deleteAllNotifications()); _wrapAction(() async => client.notifications.deleteAllNotifications());
}); });
_notificationsSubject.listen((final result) {
if (result.data != null) {
_unreadCounterSubject.add(result.data!.length);
}
});
_loadNotifications(); _loadNotifications();
} }
@ -70,11 +79,13 @@ class NotificationsBloc extends $NotificationsBloc {
final _notificationsSubject = BehaviorSubject<Result<List<NotificationsNotification>>>(); final _notificationsSubject = BehaviorSubject<Result<List<NotificationsNotification>>>();
final _errorsStreamController = StreamController<Exception>(); final _errorsStreamController = StreamController<Exception>();
final _unreadCounterSubject = BehaviorSubject<int>();
@override @override
void dispose() { void dispose() {
_notificationsSubject.close(); _notificationsSubject.close();
_errorsStreamController.close(); _errorsStreamController.close();
_unreadCounterSubject.close();
super.dispose(); super.dispose();
} }
@ -83,4 +94,7 @@ class NotificationsBloc extends $NotificationsBloc {
@override @override
Stream<Exception> _mapToErrorsState() => _errorsStreamController.stream.asBroadcastStream(); Stream<Exception> _mapToErrorsState() => _errorsStreamController.stream.asBroadcastStream();
@override
BehaviorSubject<int> _mapToUnreadCounterState() => _unreadCounterSubject;
} }

8
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] /// The state of [errors] implemented in [_mapToErrorsState]
late final Stream<Exception> _errorsState = _mapToErrorsState(); late final Stream<Exception> _errorsState = _mapToErrorsState();
/// The state of [unreadCounter] implemented in [_mapToUnreadCounterState]
late final BehaviorSubject<int> _unreadCounterState = _mapToUnreadCounterState();
@override @override
void refresh() => _$refreshEvent.add(null); void refresh() => _$refreshEvent.add(null);
@ -49,10 +52,15 @@ abstract class $NotificationsBloc extends RxBlocBase
@override @override
Stream<Exception> get errors => _errorsState; Stream<Exception> get errors => _errorsState;
@override
BehaviorSubject<int> get unreadCounter => _unreadCounterState;
BehaviorSubject<Result<List<NotificationsNotification>>> _mapToNotificationsState(); BehaviorSubject<Result<List<NotificationsNotification>>> _mapToNotificationsState();
Stream<Exception> _mapToErrorsState(); Stream<Exception> _mapToErrorsState();
BehaviorSubject<int> _mapToUnreadCounterState();
@override @override
NotificationsBlocEvents get events => this; NotificationsBlocEvents get events => this;

23
packages/neon/lib/src/pages/home/home.dart

@ -508,7 +508,26 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
if (appsData.map((final a) => a.id).contains(appImplementation.id)) ...[ if (appsData.map((final a) => a.id).contains(appImplementation.id)) ...[
ListTile( ListTile(
key: Key('app-${appImplementation.id}'), key: Key('app-${appImplementation.id}'),
title: Text(appImplementation.name(context)), title: StreamBuilder<int>(
stream: appImplementation.getUnreadCounter(_appsBloc) ??
BehaviorSubject<int>.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), leading: appImplementation.buildIcon(context),
minLeadingWidth: 0, minLeadingWidth: 0,
onTap: () { onTap: () {
@ -565,7 +584,7 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
Expanded( Expanded(
child: appsData child: appsData
.singleWhere((final a) => a.id == activeAppIDSnapshot.data!) .singleWhere((final a) => a.id == activeAppIDSnapshot.data!)
.buildPageFromAppsBloc(context, _appsBloc), .buildPage(context, _appsBloc),
), ),
], ],
], ],

6
packages/neon/lib/src/utils/app_implementation.dart

@ -34,9 +34,9 @@ abstract class AppImplementation<T extends RxBlocBase, R extends NextcloudAppSpe
T buildBloc(final NextcloudClient client); T buildBloc(final NextcloudClient client);
Widget buildPage(BuildContext context, T bloc); BehaviorSubject<int>? getUnreadCounter(AppsBloc appsBloc);
Widget buildPageFromAppsBloc(final BuildContext context, final AppsBloc appsBloc) =>
buildPage(context, appsBloc.getAppBloc(this)); Widget buildPage(BuildContext context, AppsBloc appsBloc);
Widget buildIcon( Widget buildIcon(
final BuildContext context, { final BuildContext context, {

Loading…
Cancel
Save