From 64aba148269d581ebd06c1c915bd8a268dac4387 Mon Sep 17 00:00:00 2001 From: Nikolas Rimikis Date: Thu, 22 Jun 2023 13:04:57 +0200 Subject: [PATCH 1/3] neon: make find extension methods null aware --- packages/neon/neon/lib/src/app.dart | 10 +++++----- packages/neon/neon/lib/src/blocs/accounts.dart | 4 ++-- packages/neon/neon/lib/src/blocs/apps.dart | 4 ++-- .../neon/neon/lib/src/blocs/push_notifications.dart | 2 +- packages/neon/neon/lib/src/models/account.dart | 3 ++- packages/neon/neon/lib/src/pages/home.dart | 2 +- packages/neon/neon/lib/src/pages/login.dart | 2 +- packages/neon/neon/lib/src/router.dart | 4 ++-- .../neon/neon/lib/src/utils/app_implementation.dart | 3 ++- packages/neon/neon/lib/src/utils/push_utils.dart | 2 +- .../neon/lib/src/utils/settings_export_helper.dart | 4 ++-- packages/neon/neon/lib/src/widgets/app_bar.dart | 2 +- packages/neon/neon_notifications/lib/pages/main.dart | 2 +- 13 files changed, 23 insertions(+), 21 deletions(-) diff --git a/packages/neon/neon/lib/src/app.dart b/packages/neon/neon/lib/src/app.dart index 7e2692e6..a54428bb 100644 --- a/packages/neon/neon/lib/src/app.dart +++ b/packages/neon/neon/lib/src/app.dart @@ -109,13 +109,13 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra if (_platform.canUsePushNotifications) { final localNotificationsPlugin = await PushUtils.initLocalNotifications(); Global.onPushNotificationReceived = (final accountID) async { - final account = _accountsBloc.accounts.value.find(accountID); + final account = _accountsBloc.accounts.value.tryFind(accountID); if (account == null) { return; } final allAppImplementations = Provider.of>(context, listen: false); - final app = allAppImplementations.find('notifications') as NotificationsAppInterface?; + final app = allAppImplementations.tryFind('notifications') as NotificationsAppInterface?; if (app == null) { return; @@ -124,7 +124,7 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra await _accountsBloc.getAppsBlocFor(account).getAppBloc(app).refresh(); }; Global.onPushNotificationClicked = (final pushNotificationWithAccountID) async { - final account = _accountsBloc.accounts.value.find(pushNotificationWithAccountID.accountID); + final account = _accountsBloc.accounts.value.tryFind(pushNotificationWithAccountID.accountID); if (account == null) { return; } @@ -132,7 +132,7 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra final allAppImplementations = Provider.of>(context, listen: false); - final notificationsApp = allAppImplementations.find('notifications') as NotificationsAppInterface?; + final notificationsApp = allAppImplementations.tryFind('notifications') as NotificationsAppInterface?; if (notificationsApp != null) { _accountsBloc .getAppsBlocFor(account) @@ -140,7 +140,7 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra .deleteNotification(pushNotificationWithAccountID.subject.nid!); } - final app = allAppImplementations.find(pushNotificationWithAccountID.subject.app!) ?? notificationsApp; + final app = allAppImplementations.tryFind(pushNotificationWithAccountID.subject.app) ?? notificationsApp; if (app == null) { return; } diff --git a/packages/neon/neon/lib/src/blocs/accounts.dart b/packages/neon/neon/lib/src/blocs/accounts.dart index 614136f2..c130ee00 100644 --- a/packages/neon/neon/lib/src/blocs/accounts.dart +++ b/packages/neon/neon/lib/src/blocs/accounts.dart @@ -65,7 +65,7 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState if (_globalOptions.rememberLastUsedAccount.value && _storage.containsKey(_keyLastUsedAccount)) { final lastUsedAccountID = _storage.getString(_keyLastUsedAccount); if (lastUsedAccountID != null) { - final aa = as.find(lastUsedAccountID); + final aa = as.tryFind(lastUsedAccountID); if (aa != null) { setActiveAccount(aa); } @@ -74,7 +74,7 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState unawaited( _globalOptions.initialAccount.stream.first.then((final lastAccount) { - final account = lastAccount != null ? as.find(lastAccount) : null; + final account = as.tryFind(lastAccount); if (activeAccount.valueOrNull == null) { if (account != null) { setActiveAccount(account); diff --git a/packages/neon/neon/lib/src/blocs/apps.dart b/packages/neon/neon/lib/src/blocs/apps.dart index 4c77a54a..95312818 100644 --- a/packages/neon/neon/lib/src/blocs/apps.dart +++ b/packages/neon/neon/lib/src/blocs/apps.dart @@ -39,7 +39,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates unawaited( options.initialApp.stream.first.then((var initialApp) async { if (initialApp == null) { - if (result.requireData.find('files') != null) { + if (result.requireData.tryFind('files') != null) { initialApp = 'files'; } else if (result.requireData.isNotEmpty) { // This should never happen, because the files app is always installed and can not be removed, but just in @@ -177,7 +177,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates @override Future setActiveApp(final String? appID) async { if (appID != null && - (await appImplementations.firstWhere((final a) => a.hasData)).requireData.find(appID) != null) { + (await appImplementations.firstWhere((final a) => a.hasData)).requireData.tryFind(appID) != null) { if (activeAppID.valueOrNull != appID) { activeAppID.add(appID); } diff --git a/packages/neon/neon/lib/src/blocs/push_notifications.dart b/packages/neon/neon/lib/src/blocs/push_notifications.dart index 9e37d771..8a2423b5 100644 --- a/packages/neon/neon/lib/src/blocs/push_notifications.dart +++ b/packages/neon/neon/lib/src/blocs/push_notifications.dart @@ -52,7 +52,7 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents, Future _setupUnifiedPush() async { await UnifiedPush.initialize( onNewEndpoint: (final endpoint, final instance) async { - final account = _accountsBloc.accounts.value.find(instance); + final account = _accountsBloc.accounts.value.tryFind(instance); if (account == null) { debugPrint('Account for $instance not found, can not process endpoint'); return; diff --git a/packages/neon/neon/lib/src/models/account.dart b/packages/neon/neon/lib/src/models/account.dart index 78bdf0d7..98741baa 100644 --- a/packages/neon/neon/lib/src/models/account.dart +++ b/packages/neon/neon/lib/src/models/account.dart @@ -105,5 +105,6 @@ extension NextcloudClientHelpers on NextcloudClient { } extension AccountFind on Iterable { - Account? find(final String accountID) => firstWhereOrNull((final account) => account.id == accountID); + Account? tryFind(final String? accountID) => firstWhereOrNull((final account) => account.id == accountID); + Account find(final String accountID) => firstWhere((final account) => account.id == accountID); } diff --git a/packages/neon/neon/lib/src/pages/home.dart b/packages/neon/neon/lib/src/pages/home.dart index 6e48c608..74e648cc 100644 --- a/packages/neon/neon/lib/src/pages/home.dart +++ b/packages/neon/neon/lib/src/pages/home.dart @@ -126,7 +126,7 @@ class _HomePageState extends State { ] else ...[ if (activeAppIDSnapshot.hasData) ...[ Expanded( - child: appImplementations.data!.find(activeAppIDSnapshot.data!)!.page, + child: appImplementations.data!.find(activeAppIDSnapshot.data!).page, ), ], ], diff --git a/packages/neon/neon/lib/src/pages/login.dart b/packages/neon/neon/lib/src/pages/login.dart index dc7976d6..0b0fd7ed 100644 --- a/packages/neon/neon/lib/src/pages/login.dart +++ b/packages/neon/neon/lib/src/pages/login.dart @@ -74,7 +74,7 @@ class _LoginPageState extends State { if (widget.serverURL != null) { _accountsBloc.updateAccount(account); } else { - final existingAccount = _accountsBloc.accounts.value.find(account.id); + final existingAccount = _accountsBloc.accounts.value.tryFind(account.id); if (existingAccount != null) { NeonException.showSnackbar(context, AppLocalizations.of(context).errorAccountAlreadyExists); await _loginBloc.refresh(); diff --git a/packages/neon/neon/lib/src/router.dart b/packages/neon/neon/lib/src/router.dart index 699f4cad..b37ebb8f 100644 --- a/packages/neon/neon/lib/src/router.dart +++ b/packages/neon/neon/lib/src/router.dart @@ -41,7 +41,7 @@ class AccountSettingsRoute extends GoRouteData { @override Widget build(final BuildContext context, final GoRouterState state) { final bloc = Provider.of(context, listen: false); - final account = bloc.accounts.value.find(accountid)!; + final account = bloc.accounts.value.find(accountid); return AccountSettingsPage( bloc: bloc, @@ -120,7 +120,7 @@ class NextcloudAppSettingsRoute extends GoRouteData { @override Widget build(final BuildContext context, final GoRouterState state) { final appImplementations = Provider.of>(context, listen: false); - final appImplementation = appImplementations.find(appid)!; + final appImplementation = appImplementations.tryFind(appid)!; return NextcloudAppSettingsPage(appImplementation: appImplementation); } diff --git a/packages/neon/neon/lib/src/utils/app_implementation.dart b/packages/neon/neon/lib/src/utils/app_implementation.dart index 9baced14..8cc14e47 100644 --- a/packages/neon/neon/lib/src/utils/app_implementation.dart +++ b/packages/neon/neon/lib/src/utils/app_implementation.dart @@ -74,5 +74,6 @@ abstract class AppImplementation { - AppImplementation? find(final String appID) => firstWhereOrNull((final app) => app.id == appID); + AppImplementation? tryFind(final String? appID) => firstWhereOrNull((final app) => app.id == appID); + AppImplementation find(final String appID) => firstWhere((final app) => app.id == appID); } diff --git a/packages/neon/neon/lib/src/utils/push_utils.dart b/packages/neon/neon/lib/src/utils/push_utils.dart index d9b9c4b5..04bb9e62 100644 --- a/packages/neon/neon/lib/src/utils/push_utils.dart +++ b/packages/neon/neon/lib/src/utils/push_utils.dart @@ -82,7 +82,7 @@ class PushUtils { AndroidBitmap? largeIconBitmap; try { accounts = loadAccounts(AppStorage('accounts', sharedPreferences)); - account = accounts.find(instance); + account = accounts.tryFind(instance); if (account != null) { notification = (await account.client.notifications.getNotification(id: pushNotification.subject.nid!)).ocs.data; diff --git a/packages/neon/neon/lib/src/utils/settings_export_helper.dart b/packages/neon/neon/lib/src/utils/settings_export_helper.dart index 91059e73..7152dc9c 100644 --- a/packages/neon/neon/lib/src/utils/settings_export_helper.dart +++ b/packages/neon/neon/lib/src/utils/settings_export_helper.dart @@ -20,7 +20,7 @@ class SettingsExportHelper { final appImplementationsData = data['apps'] as Map; for (final appId in appImplementationsData.keys) { - final app = appImplementations.find(appId); + final app = appImplementations.tryFind(appId); if (app == null) { return; } @@ -33,7 +33,7 @@ class SettingsExportHelper { final accountsData = data['accounts'] as Map; for (final accountId in accountsData.keys) { - final account = accountSpecificOptions.keys.find(accountId); + final account = accountSpecificOptions.keys.tryFind(accountId); if (account == null) { return; } diff --git a/packages/neon/neon/lib/src/widgets/app_bar.dart b/packages/neon/neon/lib/src/widgets/app_bar.dart index dddb6d25..2114335e 100644 --- a/packages/neon/neon/lib/src/widgets/app_bar.dart +++ b/packages/neon/neon/lib/src/widgets/app_bar.dart @@ -34,7 +34,7 @@ class NeonAppBar extends StatelessWidget implements PreferredSizeWidget { if (appImplementations.hasData && activeAppIDSnapshot.hasData) ...[ Flexible( child: Text( - appImplementations.requireData.find(activeAppIDSnapshot.data!)!.name(context), + appImplementations.requireData.find(activeAppIDSnapshot.data!).name(context), ), ), ], diff --git a/packages/neon/neon_notifications/lib/pages/main.dart b/packages/neon/neon_notifications/lib/pages/main.dart index bc1a0e00..976acacf 100644 --- a/packages/neon/neon_notifications/lib/pages/main.dart +++ b/packages/neon/neon_notifications/lib/pages/main.dart @@ -51,7 +51,7 @@ class _NotificationsMainPageState extends State { final BuildContext context, final NextcloudNotificationsNotification notification, ) { - final app = Provider.of>(context, listen: false).find(notification.app); + final app = Provider.of>(context, listen: false).tryFind(notification.app); return ListTile( title: Text(notification.subject), From 05210c8d23703018e1d1fd1322c1e75217880cef Mon Sep 17 00:00:00 2001 From: Nikolas Rimikis Date: Thu, 22 Jun 2023 13:28:54 +0200 Subject: [PATCH 2/3] neon: better nullability of AppsBloc.activeAppID --- packages/neon/neon/lib/src/blocs/apps.dart | 17 +++++++++-------- packages/neon/neon/lib/src/pages/home.dart | 4 ++-- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/packages/neon/neon/lib/src/blocs/apps.dart b/packages/neon/neon/lib/src/blocs/apps.dart index 95312818..90acfe60 100644 --- a/packages/neon/neon/lib/src/blocs/apps.dart +++ b/packages/neon/neon/lib/src/blocs/apps.dart @@ -3,7 +3,7 @@ part of 'blocs.dart'; typedef NextcloudApp = NextcloudCoreNavigationApps_Ocs_Data; abstract class AppsBlocEvents { - void setActiveApp(final String? appID); + void setActiveApp(final String appID); } abstract class AppsBlocStates { @@ -13,7 +13,7 @@ abstract class AppsBlocStates { BehaviorSubject> get notificationsAppImplementation; - BehaviorSubject get activeAppID; + BehaviorSubject get activeAppID; BehaviorSubject get openNotifications; @@ -48,7 +48,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates } } if (!activeAppID.hasValue) { - await setActiveApp(initialApp); + await setActiveApp(initialApp!); } }), ); @@ -144,7 +144,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates } @override - BehaviorSubject activeAppID = BehaviorSubject(); + BehaviorSubject activeAppID = BehaviorSubject(); @override BehaviorSubject>>> appImplementations = @@ -175,15 +175,16 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates } @override - Future setActiveApp(final String? appID) async { - if (appID != null && - (await appImplementations.firstWhere((final a) => a.hasData)).requireData.tryFind(appID) != null) { + Future setActiveApp(final String appID) async { + final apps = await appImplementations.firstWhere((final a) => a.hasData); + if (apps.requireData.tryFind(appID) != null) { + // TODO: make activeAppID distinct if (activeAppID.valueOrNull != appID) { activeAppID.add(appID); } } else if (appID == 'notifications') { openNotifications.add(null); - } else if (appID != null) { + } else { throw Exception('App $appID not found'); } } diff --git a/packages/neon/neon/lib/src/pages/home.dart b/packages/neon/neon/lib/src/pages/home.dart index 74e648cc..42c16476 100644 --- a/packages/neon/neon/lib/src/pages/home.dart +++ b/packages/neon/neon/lib/src/pages/home.dart @@ -100,7 +100,7 @@ class _HomePageState extends State { @override Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( stream: _appsBloc.appImplementations, - builder: (final context, final appImplementations) => StreamBuilder( + builder: (final context, final appImplementations) => StreamBuilder( stream: _appsBloc.activeAppID, builder: (final context, final activeAppIDSnapshot) => OptionBuilder( option: _globalOptions.navigationMode, @@ -126,7 +126,7 @@ class _HomePageState extends State { ] else ...[ if (activeAppIDSnapshot.hasData) ...[ Expanded( - child: appImplementations.data!.find(activeAppIDSnapshot.data!).page, + child: appImplementations.requireData.find(activeAppIDSnapshot.requireData).page, ), ], ], From de01354e78322ae5de36146e66966f3a9d80a53d Mon Sep 17 00:00:00 2001 From: Nikolas Rimikis Date: Thu, 22 Jun 2023 14:52:36 +0200 Subject: [PATCH 3/3] neon: add make AppsBlocStates supply the active AppImplementation --- packages/neon/neon/lib/src/blocs/apps.dart | 25 +++++++++++-------- packages/neon/neon/lib/src/pages/home.dart | 10 ++++---- .../neon/neon/lib/src/widgets/app_bar.dart | 10 ++++---- .../neon/neon/lib/src/widgets/drawer.dart | 2 +- 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/packages/neon/neon/lib/src/blocs/apps.dart b/packages/neon/neon/lib/src/blocs/apps.dart index 90acfe60..730a9945 100644 --- a/packages/neon/neon/lib/src/blocs/apps.dart +++ b/packages/neon/neon/lib/src/blocs/apps.dart @@ -13,7 +13,7 @@ abstract class AppsBlocStates { BehaviorSubject> get notificationsAppImplementation; - BehaviorSubject get activeAppID; + BehaviorSubject get activeApp; BehaviorSubject get openNotifications; @@ -47,8 +47,8 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates initialApp = result.requireData.first.id; } } - if (!activeAppID.hasValue) { - await setActiveApp(initialApp!); + if (!activeApp.hasValue && initialApp != null) { + await setActiveApp(initialApp); } }), ); @@ -132,7 +132,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates unawaited(apps.close()); unawaited(appImplementations.close()); unawaited(notificationsAppImplementation.close()); - unawaited(activeAppID.close()); + unawaited(activeApp.close()); unawaited(openNotifications.close()); unawaited(appVersions.close()); @@ -144,7 +144,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates } @override - BehaviorSubject activeAppID = BehaviorSubject(); + BehaviorSubject activeApp = BehaviorSubject(); @override BehaviorSubject>>> appImplementations = @@ -176,14 +176,17 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates @override Future setActiveApp(final String appID) async { + if (appID == 'notifications') { + openNotifications.add(null); + return; + } + final apps = await appImplementations.firstWhere((final a) => a.hasData); - if (apps.requireData.tryFind(appID) != null) { - // TODO: make activeAppID distinct - if (activeAppID.valueOrNull != appID) { - activeAppID.add(appID); + final app = apps.requireData.tryFind(appID); + if (app != null) { + if (activeApp.valueOrNull?.id != appID) { + activeApp.add(app); } - } else if (appID == 'notifications') { - openNotifications.add(null); } else { throw Exception('App $appID not found'); } diff --git a/packages/neon/neon/lib/src/pages/home.dart b/packages/neon/neon/lib/src/pages/home.dart index 42c16476..47f518bb 100644 --- a/packages/neon/neon/lib/src/pages/home.dart +++ b/packages/neon/neon/lib/src/pages/home.dart @@ -100,9 +100,9 @@ class _HomePageState extends State { @override Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( stream: _appsBloc.appImplementations, - builder: (final context, final appImplementations) => StreamBuilder( - stream: _appsBloc.activeAppID, - builder: (final context, final activeAppIDSnapshot) => OptionBuilder( + builder: (final context, final appImplementations) => StreamBuilder( + stream: _appsBloc.activeApp, + builder: (final context, final activeAppSnapshot) => OptionBuilder( option: _globalOptions.navigationMode, builder: (final context, final navigationMode) { final drawerAlwaysVisible = navigationMode == NavigationMode.drawerAlwaysVisible; @@ -124,9 +124,9 @@ class _HomePageState extends State { ), ), ] else ...[ - if (activeAppIDSnapshot.hasData) ...[ + if (activeAppSnapshot.hasData) ...[ Expanded( - child: appImplementations.requireData.find(activeAppIDSnapshot.requireData).page, + child: activeAppSnapshot.requireData.page, ), ], ], diff --git a/packages/neon/neon/lib/src/widgets/app_bar.dart b/packages/neon/neon/lib/src/widgets/app_bar.dart index 2114335e..eeb8bfe3 100644 --- a/packages/neon/neon/lib/src/widgets/app_bar.dart +++ b/packages/neon/neon/lib/src/widgets/app_bar.dart @@ -23,18 +23,18 @@ class NeonAppBar extends StatelessWidget implements PreferredSizeWidget { return ResultBuilder>.behaviorSubject( stream: appsBloc.appImplementations, - builder: (final context, final appImplementations) => StreamBuilder( - stream: appsBloc.activeAppID, - builder: (final context, final activeAppIDSnapshot) => AppBar( + builder: (final context, final appImplementations) => StreamBuilder( + stream: appsBloc.activeApp, + builder: (final context, final activeAppSnapshot) => AppBar( title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - if (appImplementations.hasData && activeAppIDSnapshot.hasData) ...[ + if (activeAppSnapshot.hasData) ...[ Flexible( child: Text( - appImplementations.requireData.find(activeAppIDSnapshot.data!).name(context), + activeAppSnapshot.requireData.name(context), ), ), ], diff --git a/packages/neon/neon/lib/src/widgets/drawer.dart b/packages/neon/neon/lib/src/widgets/drawer.dart index eff0fcdd..2cf034b4 100644 --- a/packages/neon/neon/lib/src/widgets/drawer.dart +++ b/packages/neon/neon/lib/src/widgets/drawer.dart @@ -61,7 +61,7 @@ class __NeonDrawerState extends State<_NeonDrawer> with SingleTickerProviderStat _appsBloc = _accountsBloc.activeAppsBloc; _apps = widget.apps.toList(); - _activeApp = _apps.indexWhere((final app) => app.id == _appsBloc.activeAppID.valueOrNull); + _activeApp = _apps.indexWhere((final app) => app.id == _appsBloc.activeApp.valueOrNull?.id); _tabController = TabController( vsync: this,