From 43382e221ceb27e9a590e63550caed7bc69025cb Mon Sep 17 00:00:00 2001 From: Nikolas Rimikis Date: Thu, 26 Oct 2023 17:55:21 +0200 Subject: [PATCH] refactor: rename app to client This alligns the code with our documentations. Signed-off-by: Nikolas Rimikis --- .cspell/neon.txt | 1 + .../app/integration_test/screenshot_test.dart | 2 +- packages/app/lib/apps.dart | 12 +- packages/app/lib/main.dart | 2 +- packages/neon/neon/lib/blocs.dart | 2 +- packages/neon/neon/lib/l10n/en.arb | 4 +- .../neon/neon/lib/l10n/localizations.dart | 4 +- .../neon/neon/lib/l10n/localizations_en.dart | 2 +- packages/neon/neon/lib/models.dart | 2 +- packages/neon/neon/lib/neon.dart | 12 +- packages/neon/neon/lib/src/app.dart | 58 +++-- .../neon/neon/lib/src/blocs/accounts.dart | 42 ++-- packages/neon/neon/lib/src/blocs/apps.dart | 232 ------------------ packages/neon/neon/lib/src/blocs/clients.dart | 232 ++++++++++++++++++ .../lib/src/blocs/push_notifications.dart | 2 +- .../neon/lib/src/blocs/unified_search.dart | 18 +- ...tation.dart => client_implementation.dart} | 26 +- .../src/models/notifications_interface.dart | 10 +- .../neon/lib/src/pages/account_settings.dart | 2 +- ...app_settings.dart => client_settings.dart} | 24 +- packages/neon/neon/lib/src/pages/home.dart | 42 ++-- .../neon/neon/lib/src/pages/settings.dart | 26 +- packages/neon/neon/lib/src/router.dart | 24 +- packages/neon/neon/lib/src/router.g.dart | 16 +- .../settings/models/options_collection.dart | 8 +- .../neon/lib/src/settings/models/storage.dart | 6 +- .../utils/settings_export_helper.dart | 22 +- packages/neon/neon/lib/src/theme/theme.dart | 6 +- .../neon/lib/src/utils/account_options.dart | 20 +- .../{app_route.dart => client_route.dart} | 14 +- .../neon/lib/src/utils/global_options.dart | 2 +- .../neon/neon/lib/src/utils/push_utils.dart | 20 +- .../neon/neon/lib/src/widgets/app_bar.dart | 66 ++--- ...n.dart => client_implementation_icon.dart} | 12 +- .../neon/neon/lib/src/widgets/drawer.dart | 48 ++-- packages/neon/neon/lib/utils.dart | 2 +- .../neon/test/app_implementation_test.dart | 30 --- .../neon/test/client_implementation_test.dart | 30 +++ .../neon/test/options_collection_test.dart | 4 +- .../neon/neon/test/settings_export_test.dart | 10 +- packages/neon/neon/test/storage_test.dart | 6 +- packages/neon/neon_dashboard/lib/src/app.dart | 10 +- .../neon/neon_dashboard/lib/src/options.dart | 6 +- .../neon/neon_dashboard/lib/src/routes.dart | 12 +- .../neon/neon_dashboard/lib/src/routes.g.dart | 10 +- .../neon/neon_files/lib/blocs/browser.dart | 2 +- packages/neon/neon_files/lib/blocs/files.dart | 2 +- packages/neon/neon_files/lib/neon_files.dart | 8 +- packages/neon/neon_files/lib/options.dart | 4 +- packages/neon/neon_files/lib/routes.dart | 8 +- packages/neon/neon_files/lib/routes.g.dart | 10 +- .../neon/neon_news/lib/blocs/articles.dart | 2 +- packages/neon/neon_news/lib/blocs/news.dart | 2 +- packages/neon/neon_news/lib/neon_news.dart | 8 +- packages/neon/neon_news/lib/options.dart | 4 +- packages/neon/neon_news/lib/routes.dart | 8 +- packages/neon/neon_news/lib/routes.g.dart | 10 +- packages/neon/neon_notes/lib/blocs/note.dart | 2 +- packages/neon/neon_notes/lib/blocs/notes.dart | 2 +- packages/neon/neon_notes/lib/neon_notes.dart | 8 +- packages/neon/neon_notes/lib/options.dart | 4 +- packages/neon/neon_notes/lib/routes.dart | 8 +- packages/neon/neon_notes/lib/routes.g.dart | 10 +- .../lib/blocs/notifications.dart | 2 +- .../lib/neon_notifications.dart | 10 +- .../neon/neon_notifications/lib/options.dart | 4 +- .../neon_notifications/lib/pages/main.dart | 10 +- .../neon/neon_notifications/lib/routes.dart | 8 +- .../neon/neon_notifications/lib/routes.g.dart | 10 +- 69 files changed, 646 insertions(+), 641 deletions(-) delete mode 100644 packages/neon/neon/lib/src/blocs/apps.dart create mode 100644 packages/neon/neon/lib/src/blocs/clients.dart rename packages/neon/neon/lib/src/models/{app_implementation.dart => client_implementation.dart} (80%) rename packages/neon/neon/lib/src/pages/{nextcloud_app_settings.dart => client_settings.dart} (68%) rename packages/neon/neon/lib/src/utils/{app_route.dart => client_route.dart} (50%) rename packages/neon/neon/lib/src/widgets/{app_implementation_icon.dart => client_implementation_icon.dart} (76%) delete mode 100644 packages/neon/neon/test/app_implementation_test.dart create mode 100644 packages/neon/neon/test/client_implementation_test.dart diff --git a/.cspell/neon.txt b/.cspell/neon.txt index 175b008f..53935250 100644 --- a/.cspell/neon.txt +++ b/.cspell/neon.txt @@ -1,3 +1,4 @@ +clientid crypton exportables fcmup diff --git a/packages/app/integration_test/screenshot_test.dart b/packages/app/integration_test/screenshot_test.dart index ca0078d4..252fcd4d 100644 --- a/packages/app/integration_test/screenshot_test.dart +++ b/packages/app/integration_test/screenshot_test.dart @@ -24,7 +24,7 @@ Future runTestApp( final Account? account, }) async { await runNeon( - appImplementations: appImplementations, + clientImplementations: clientImplementations, theme: neonTheme, bindingOverride: binding, account: account, diff --git a/packages/app/lib/apps.dart b/packages/app/lib/apps.dart index 20731e47..80e608ab 100644 --- a/packages/app/lib/apps.dart +++ b/packages/app/lib/apps.dart @@ -6,10 +6,10 @@ import 'package:neon_notes/neon_notes.dart'; import 'package:neon_notifications/neon_notifications.dart'; /// The collection of clients enabled for the Neon app. -final Set appImplementations = { - DashboardApp(), - FilesApp(), - NewsApp(), - NotesApp(), - NotificationsApp(), +final Set clientImplementations = { + DashboardClient(), + FilesClient(), + NewsClient(), + NotesClient(), + NotificationsClient(), }; diff --git a/packages/app/lib/main.dart b/packages/app/lib/main.dart index c732b8ba..9e4b4fb4 100644 --- a/packages/app/lib/main.dart +++ b/packages/app/lib/main.dart @@ -4,7 +4,7 @@ import 'package:neon/neon.dart'; Future main() async { await runNeon( - appImplementations: appImplementations, + clientImplementations: clientImplementations, theme: neonTheme, ); } diff --git a/packages/neon/neon/lib/blocs.dart b/packages/neon/neon/lib/blocs.dart index 820fbc22..995b949e 100644 --- a/packages/neon/neon/lib/blocs.dart +++ b/packages/neon/neon/lib/blocs.dart @@ -1,5 +1,5 @@ export 'package:neon/src/bloc/bloc.dart'; export 'package:neon/src/bloc/result.dart'; -// TODO: Remove access to the AccountsBloc. Apps should not need to access this +// TODO: Remove access to the AccountsBloc. Clients should not need to access this export 'package:neon/src/blocs/accounts.dart' show AccountsBloc; export 'package:neon/src/blocs/timer.dart' hide TimerBlocEvents, TimerBlocStates; diff --git a/packages/neon/neon/lib/l10n/en.arb b/packages/neon/neon/lib/l10n/en.arb index 53515aa9..634491f0 100644 --- a/packages/neon/neon/lib/l10n/en.arb +++ b/packages/neon/neon/lib/l10n/en.arb @@ -2,8 +2,8 @@ "@@locale": "en", "nextcloud": "Nextcloud", "nextcloudLogo": "Nextcloud logo", - "appImplementationName": "{app, select, nextcloud{Nextcloud} core{Server} dashboard{Dashboard} files{Files} news{News} notes{Notes} notifications{Notifications} other{}}", - "@appImplementationName": { + "clientImplementationName": "{app, select, nextcloud{Nextcloud} core{Server} dashboard{Dashboard} files{Files} news{News} notes{Notes} notifications{Notifications} other{}}", + "@clientImplementationName": { "placeholders": { "app": {} } diff --git a/packages/neon/neon/lib/l10n/localizations.dart b/packages/neon/neon/lib/l10n/localizations.dart index ce9f982a..9e8d75a7 100644 --- a/packages/neon/neon/lib/l10n/localizations.dart +++ b/packages/neon/neon/lib/l10n/localizations.dart @@ -101,11 +101,11 @@ abstract class NeonLocalizations { /// **'Nextcloud logo'** String get nextcloudLogo; - /// No description provided for @appImplementationName. + /// No description provided for @clientImplementationName. /// /// In en, this message translates to: /// **'{app, select, nextcloud{Nextcloud} core{Server} dashboard{Dashboard} files{Files} news{News} notes{Notes} notifications{Notifications} other{}}'** - String appImplementationName(String app); + String clientImplementationName(String app); /// No description provided for @loginAgain. /// diff --git a/packages/neon/neon/lib/l10n/localizations_en.dart b/packages/neon/neon/lib/l10n/localizations_en.dart index e1aba5d6..fe556e92 100644 --- a/packages/neon/neon/lib/l10n/localizations_en.dart +++ b/packages/neon/neon/lib/l10n/localizations_en.dart @@ -13,7 +13,7 @@ class NeonLocalizationsEn extends NeonLocalizations { String get nextcloudLogo => 'Nextcloud logo'; @override - String appImplementationName(String app) { + String clientImplementationName(String app) { String _temp0 = intl.Intl.selectLogic( app, { diff --git a/packages/neon/neon/lib/models.dart b/packages/neon/neon/lib/models.dart index f9ac5fc2..c176a5f3 100644 --- a/packages/neon/neon/lib/models.dart +++ b/packages/neon/neon/lib/models.dart @@ -1,3 +1,3 @@ export 'package:neon/src/models/account.dart' hide Credentials, LoginQRcode; -export 'package:neon/src/models/app_implementation.dart'; +export 'package:neon/src/models/client_implementation.dart'; export 'package:neon/src/models/notifications_interface.dart'; diff --git a/packages/neon/neon/lib/neon.dart b/packages/neon/neon/lib/neon.dart index 6bf60d46..a5e3bd44 100644 --- a/packages/neon/neon/lib/neon.dart +++ b/packages/neon/neon/lib/neon.dart @@ -8,7 +8,7 @@ import 'package:neon/src/blocs/first_launch.dart'; import 'package:neon/src/blocs/next_push.dart'; import 'package:neon/src/blocs/push_notifications.dart'; import 'package:neon/src/models/account.dart'; -import 'package:neon/src/models/app_implementation.dart'; +import 'package:neon/src/models/client_implementation.dart'; import 'package:neon/src/models/disposable.dart'; import 'package:neon/src/platform/platform.dart'; import 'package:neon/src/settings/models/storage.dart'; @@ -21,7 +21,7 @@ import 'package:package_info_plus/package_info_plus.dart'; import 'package:provider/provider.dart'; Future runNeon({ - required final Set appImplementations, + required final Set clientImplementations, required final NeonTheme theme, @visibleForTesting final WidgetsBinding? bindingOverride, @visibleForTesting final Account? account, @@ -44,7 +44,7 @@ Future runNeon({ final accountsBloc = AccountsBloc( globalOptions, - appImplementations, + clientImplementations, ); if (account != null) { accountsBloc @@ -71,9 +71,9 @@ Future runNeon({ NeonProvider.value(value: accountsBloc), NeonProvider.value(value: firstLaunchBloc), NeonProvider.value(value: nextPushBloc), - Provider>( - create: (final _) => appImplementations, - dispose: (final _, final appImplementations) => appImplementations.disposeAll(), + Provider>( + create: (final _) => clientImplementations, + dispose: (final _, final clientImplementations) => clientImplementations.disposeAll(), ), Provider.value(value: packageInfo), ], diff --git a/packages/neon/neon/lib/src/app.dart b/packages/neon/neon/lib/src/app.dart index 42a61086..7933208e 100644 --- a/packages/neon/neon/lib/src/app.dart +++ b/packages/neon/neon/lib/src/app.dart @@ -10,7 +10,7 @@ import 'package:neon/l10n/localizations.dart'; import 'package:neon/src/bloc/result.dart'; import 'package:neon/src/blocs/accounts.dart'; import 'package:neon/src/models/account.dart'; -import 'package:neon/src/models/app_implementation.dart'; +import 'package:neon/src/models/client_implementation.dart'; import 'package:neon/src/models/notifications_interface.dart'; import 'package:neon/src/models/push_notification.dart'; import 'package:neon/src/platform/platform.dart'; @@ -46,7 +46,7 @@ class NeonApp extends StatefulWidget { class _NeonAppState extends State with WidgetsBindingObserver, tray.TrayListener, WindowListener { final _appRegex = RegExp(r'^app_([a-z]+)$', multiLine: true); final _navigatorKey = GlobalKey(); - late final Iterable _appImplementations; + late final Iterable _clientImplementations; late final GlobalOptions _globalOptions; late final AccountsBloc _accountsBloc; late final _routerDelegate = buildAppRouter( @@ -60,7 +60,7 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra void initState() { super.initState(); - _appImplementations = NeonProvider.of>(context); + _clientImplementations = NeonProvider.of>(context); _globalOptions = NeonProvider.of(context); _accountsBloc = NeonProvider.of(context); @@ -81,12 +81,12 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra if (NeonPlatform.instance.canUseQuickActions) { const quickActions = QuickActions(); await quickActions.setShortcutItems( - _appImplementations + _clientImplementations .map( - (final app) => ShortcutItem( - type: 'app_${app.id}', - localizedTitle: app.nameFromLocalization(localizations), - icon: 'app_${app.id}', + (final client) => ShortcutItem( + type: 'app_${client.id}', + localizedTitle: client.nameFromLocalization(localizations), + icon: 'app_${client.id}', ), ) .toList(), @@ -111,10 +111,10 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra await tray.trayManager.setContextMenu( tray.Menu( items: [ - for (final app in _appImplementations) ...[ + for (final client in _clientImplementations) ...[ tray.MenuItem( - key: 'app_${app.id}', - label: app.nameFromLocalization(localizations), + key: 'app_${client.id}', + label: client.nameFromLocalization(localizations), // TODO: Add icons which should work on macOS and Windows ), ], @@ -145,14 +145,14 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra return; } - final allAppImplementations = NeonProvider.of>(context); - final app = allAppImplementations.tryFind(AppIDs.notifications) as NotificationsAppInterface?; + final allClientImplementations = NeonProvider.of>(context); + final client = allClientImplementations.tryFind(AppIDs.notifications) as NotificationsClientInterface?; - if (app == null) { + if (client == null) { return; } - await _accountsBloc.getAppsBlocFor(account).getAppBloc(app).refresh(); + await _accountsBloc.getClientsBlocFor(account).getClientBloc(client).refresh(); }; Global.onPushNotificationClicked = (final pushNotificationWithAccountID) async { final account = _accountsBloc.accounts.value.tryFind(pushNotificationWithAccountID.accountID); @@ -161,22 +161,24 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra } _accountsBloc.setActiveAccount(account); - final allAppImplementations = NeonProvider.of>(context); + final allClientImplementations = NeonProvider.of>(context); - final notificationsApp = allAppImplementations.tryFind(AppIDs.notifications) as NotificationsAppInterface?; - if (notificationsApp != null) { + final notificationsClient = + allClientImplementations.tryFind(AppIDs.notifications) as NotificationsClientInterface?; + if (notificationsClient != null) { _accountsBloc - .getAppsBlocFor(account) - .getAppBloc(notificationsApp) + .getClientsBlocFor(account) + .getClientBloc(notificationsClient) .deleteNotification(pushNotificationWithAccountID.subject.nid!); } - final app = allAppImplementations.tryFind(pushNotificationWithAccountID.subject.app) ?? notificationsApp; - if (app == null) { + final client = + allClientImplementations.tryFind(pushNotificationWithAccountID.subject.app) ?? notificationsClient; + if (client == null) { return; } - await _openAppFromExternal(account, app.id); + await _openAppFromExternal(account, client.id); }; final details = await localNotificationsPlugin.getNotificationAppLaunchDetails(); @@ -235,7 +237,7 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra } Future _openAppFromExternal(final Account account, final String id) async { - await _accountsBloc.getAppsBlocFor(account).setActiveApp(id); + await _accountsBloc.getClientsBlocFor(account).setActiveClient(id); _navigatorKey.currentState!.popUntil((final route) => route.settings.name == 'home'); await _showAndRestoreWindow(); } @@ -291,17 +293,19 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra capabilitiesSnapshot.data?.capabilities.themingPublicCapabilities?.theming, keepOriginalAccentColor: options.themeKeepOriginalAccentColor.value, oledAsDark: options.themeOLEDAsDark.value, - appThemes: _appImplementations.map((final a) => a.theme).whereNotNull(), + clientThemes: _clientImplementations.map((final c) => c.theme).whereNotNull(), neonTheme: widget.neonTheme, ); return MaterialApp.router( localizationsDelegates: [ - ..._appImplementations.map((final app) => app.localizationsDelegate), + ..._clientImplementations.map((final client) => client.localizationsDelegate), ...NeonLocalizations.localizationsDelegates, ], supportedLocales: { - ..._appImplementations.map((final app) => app.supportedLocales).expand((final element) => element), + ..._clientImplementations + .map((final client) => client.supportedLocales) + .expand((final element) => element), ...NeonLocalizations.supportedLocales, }, themeMode: options.themeMode.value, diff --git a/packages/neon/neon/lib/src/blocs/accounts.dart b/packages/neon/neon/lib/src/blocs/accounts.dart index 91e085e9..04aac7c7 100644 --- a/packages/neon/neon/lib/src/blocs/accounts.dart +++ b/packages/neon/neon/lib/src/blocs/accounts.dart @@ -5,14 +5,14 @@ import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:meta/meta.dart'; import 'package:neon/src/bloc/bloc.dart'; -import 'package:neon/src/blocs/apps.dart'; import 'package:neon/src/blocs/capabilities.dart'; +import 'package:neon/src/blocs/clients.dart'; import 'package:neon/src/blocs/unified_search.dart'; import 'package:neon/src/blocs/user_details.dart'; import 'package:neon/src/blocs/user_statuses.dart'; import 'package:neon/src/models/account.dart'; import 'package:neon/src/models/account_cache.dart'; -import 'package:neon/src/models/app_implementation.dart'; +import 'package:neon/src/models/client_implementation.dart'; import 'package:neon/src/settings/models/storage.dart'; import 'package:neon/src/utils/account_options.dart'; import 'package:neon/src/utils/global_options.dart'; @@ -62,7 +62,7 @@ abstract interface class AccountsBlocStates { class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocStates { AccountsBloc( this._globalOptions, - this._allAppImplementations, + this._allClientImplementations, ) { const lastUsedStorage = SingleValueStorage(StorageKeys.lastUsedAccount); @@ -103,22 +103,22 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState accounts.listen((final accounts) { _accountsOptions.pruneAgainst(accounts); - _appsBlocs.pruneAgainst(accounts); + _clientsBlocs.pruneAgainst(accounts); _capabilitiesBlocs.pruneAgainst(accounts); _userDetailsBlocs.pruneAgainst(accounts); _userStatusesBlocs.pruneAgainst(accounts); _unifiedSearchBlocs.pruneAgainst(accounts); - for (final app in _allAppImplementations) { - app.blocsCache.pruneAgainst(accounts); + for (final client in _allClientImplementations) { + client.blocsCache.pruneAgainst(accounts); } }); } final GlobalOptions _globalOptions; - final Iterable _allAppImplementations; + final Iterable _allClientImplementations; final _accountsOptions = AccountCache(); - final _appsBlocs = AccountCache(); + final _clientsBlocs = AccountCache(); final _capabilitiesBlocs = AccountCache(); final _userDetailsBlocs = AccountCache(); final _userStatusesBlocs = AccountCache(); @@ -128,7 +128,7 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState void dispose() { unawaited(activeAccount.close()); unawaited(accounts.close()); - _appsBlocs.dispose(); + _clientsBlocs.dispose(); _capabilitiesBlocs.dispose(); _userDetailsBlocs.dispose(); _userStatusesBlocs.dispose(); @@ -226,23 +226,23 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState /// /// Use [activeOptions] to get them for the [activeAccount]. AccountSpecificOptions getOptionsFor(final Account account) => _accountsOptions[account] ??= AccountSpecificOptions( - AppStorage(StorageKeys.accounts, account.id), - getAppsBlocFor(account), + ClientStorage(StorageKeys.accounts, account.id), + getClientsBlocFor(account), ); - /// The appsBloc for the [activeAccount]. + /// The clientsBloc for the [activeAccount]. /// - /// Convenience method for [getAppsBlocFor] with the currently active account. - AppsBloc get activeAppsBloc => getAppsBlocFor(aa); + /// Convenience method for [getClientsBlocFor] with the currently active account. + ClientsBloc get activeClientsBloc => getClientsBlocFor(aa); - /// The appsBloc for the specified [account]. + /// The clientsBloc for the specified [account]. /// - /// Use [activeAppsBloc] to get them for the [activeAccount]. - AppsBloc getAppsBlocFor(final Account account) => _appsBlocs[account] ??= AppsBloc( + /// Use [activeClientsBloc] to get them for the [activeAccount]. + ClientsBloc getClientsBlocFor(final Account account) => _clientsBlocs[account] ??= ClientsBloc( getCapabilitiesBlocFor(account), this, account, - _allAppImplementations, + _allClientImplementations, ); /// The capabilitiesBloc for the [activeAccount]. @@ -288,7 +288,7 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState /// Use [activeUnifiedSearchBloc] to get them for the [activeAccount]. UnifiedSearchBloc getUnifiedSearchBlocFor(final Account account) => _unifiedSearchBlocs[account] ??= UnifiedSearchBloc( - getAppsBlocFor(account), + getClientsBlocFor(account), account, ); } @@ -297,7 +297,7 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState /// /// It is not checked whether the stored information is still valid. List loadAccounts() { - const storage = AppStorage(StorageKeys.accounts); + const storage = ClientStorage(StorageKeys.accounts); if (storage.containsKey(_keyAccounts)) { return storage @@ -310,7 +310,7 @@ List loadAccounts() { /// Saves the given [accounts] to the storage. Future saveAccounts(final List accounts) async { - const storage = AppStorage(StorageKeys.accounts); + const storage = ClientStorage(StorageKeys.accounts); final values = accounts.map((final a) => json.encode(a.toJson())).toList(); await storage.setStringList(_keyAccounts, values); diff --git a/packages/neon/neon/lib/src/blocs/apps.dart b/packages/neon/neon/lib/src/blocs/apps.dart deleted file mode 100644 index 411e1a77..00000000 --- a/packages/neon/neon/lib/src/blocs/apps.dart +++ /dev/null @@ -1,232 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/foundation.dart'; -import 'package:meta/meta.dart'; -import 'package:neon/src/bloc/bloc.dart'; -import 'package:neon/src/bloc/result.dart'; -import 'package:neon/src/blocs/accounts.dart'; -import 'package:neon/src/blocs/capabilities.dart'; -import 'package:neon/src/models/account.dart'; -import 'package:neon/src/models/app_implementation.dart'; -import 'package:neon/src/models/notifications_interface.dart'; -import 'package:neon/src/settings/models/options_collection.dart'; -import 'package:neon/src/utils/request_manager.dart'; -import 'package:nextcloud/core.dart' as core; -import 'package:nextcloud/nextcloud.dart'; -import 'package:provider/provider.dart'; -import 'package:rxdart/rxdart.dart'; - -@internal -abstract interface class AppsBlocEvents { - /// Sets the active app using the [appID]. - /// - /// If the app is already the active app nothing will happen. - /// When using [skipAlreadySet] nothing will be done if there already is an active app. - void setActiveApp(final String appID, {final bool skipAlreadySet = false}); -} - -@internal -abstract interface class AppsBlocStates { - BehaviorSubject>> get appImplementations; - - BehaviorSubject> get notificationsAppImplementation; - - BehaviorSubject get activeApp; - - BehaviorSubject get openNotifications; - - BehaviorSubject> get appVersions; -} - -@internal -class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates { - AppsBloc( - this._capabilitiesBloc, - this._accountsBloc, - this._account, - this._allAppImplementations, - ) { - _apps.listen((final result) { - appImplementations - .add(result.transform((final data) => _filteredAppImplementations(data.map((final a) => a.id)))); - }); - - appImplementations.listen((final result) async { - if (!result.hasData) { - return; - } - - // dispose unsupported apps - for (final app in _allAppImplementations) { - if (result.requireData.tryFind(app.id) == null) { - app.blocsCache.remove(_account); - } - } - - final options = _accountsBloc.getOptionsFor(_account); - final initialApp = options.initialApp.value ?? _getInitialAppFallback(); - if (initialApp != null) { - await setActiveApp(initialApp, skipAlreadySet: true); - } - - unawaited(_checkCompatibility()); - }); - - _capabilitiesBloc.capabilities.listen((final result) { - notificationsAppImplementation.add( - result.transform( - (final data) => data.capabilities.notificationsCapabilities?.notifications != null - ? _findAppImplementation(AppIDs.notifications) - : null, - ), - ); - - unawaited(_checkCompatibility()); - }); - - unawaited(refresh()); - } - - /// Determines the appid of initial app. - /// - /// It requires [appImplementations] to have both a value and data. - /// - /// The files app is always installed and can not be removed so it will be used, but in the - /// case this changes at a later point the first supported app will be returned. - /// - /// Returns null when no app is supported by the server. - String? _getInitialAppFallback() { - final supportedApps = appImplementations.value.requireData; - - for (final fallback in {AppIDs.dashboard, AppIDs.files}) { - if (supportedApps.tryFind(fallback) != null) { - return fallback; - } - } - - if (supportedApps.isNotEmpty) { - return supportedApps.first.id; - } - - return null; - } - - Future _checkCompatibility() async { - final apps = appImplementations.valueOrNull; - final capabilities = _capabilitiesBloc.capabilities.valueOrNull; - - // ignore cached data - if (capabilities == null || apps == null || !capabilities.hasUncachedData || !apps.hasUncachedData) { - return; - } - - final notSupported = {}; - - try { - final (coreSupported, coreMinimumVersion) = _account.client.core.isSupported(capabilities.requireData); - if (!coreSupported) { - notSupported['core'] = coreMinimumVersion.toString(); - } - } catch (e, s) { - debugPrint(e.toString()); - debugPrint(s.toString()); - } - - for (final app in apps.requireData) { - try { - final (supported, minimumVersion) = await app.isSupported(_account, capabilities.requireData); - if (!(supported ?? true)) { - notSupported[app.id] = minimumVersion; - } - } catch (e, s) { - debugPrint(e.toString()); - debugPrint(s.toString()); - } - } - - if (notSupported.isNotEmpty) { - appVersions.add(notSupported); - } - } - - T? _findAppImplementation(final String id) { - final matches = _filteredAppImplementations([id]); - if (matches.isNotEmpty) { - return matches.single as T; - } - - return null; - } - - Iterable _filteredAppImplementations(final Iterable appIds) => - _allAppImplementations.where((final a) => appIds.contains(a.id)); - - final CapabilitiesBloc _capabilitiesBloc; - final AccountsBloc _accountsBloc; - final Account _account; - final Iterable _allAppImplementations; - final _apps = BehaviorSubject>>(); - - @override - void dispose() { - unawaited(_apps.close()); - unawaited(appImplementations.close()); - unawaited(notificationsAppImplementation.close()); - unawaited(activeApp.close()); - unawaited(openNotifications.close()); - unawaited(appVersions.close()); - - super.dispose(); - } - - @override - BehaviorSubject activeApp = BehaviorSubject(); - - @override - BehaviorSubject>>> appImplementations = - BehaviorSubject(); - - @override - BehaviorSubject> notificationsAppImplementation = BehaviorSubject(); - - @override - BehaviorSubject openNotifications = BehaviorSubject(); - - @override - BehaviorSubject> appVersions = BehaviorSubject(); - - @override - Future refresh() async { - await RequestManager.instance.wrapNextcloud( - _account.id, - 'apps-apps', - _apps, - _account.client.core.navigation.getAppsNavigationRaw(), - (final response) => response.body.ocs.data.toList(), - ); - } - - @override - Future setActiveApp(final String appID, {final bool skipAlreadySet = false}) async { - if (appID == AppIDs.notifications) { - openNotifications.add(null); - return; - } - - final apps = await appImplementations.firstWhere((final a) => a.hasData); - final app = apps.requireData.tryFind(appID); - if (app != null) { - if ((!activeApp.hasValue || !skipAlreadySet) && activeApp.valueOrNull?.id != appID) { - activeApp.add(app); - } - } else { - throw Exception('App $appID not found'); - } - } - - T getAppBloc(final AppImplementation appImplementation) => - appImplementation.getBloc(_account); - - List> get appBlocProviders => - _allAppImplementations.map((final appImplementation) => appImplementation.blocProvider).toList(); -} diff --git a/packages/neon/neon/lib/src/blocs/clients.dart b/packages/neon/neon/lib/src/blocs/clients.dart new file mode 100644 index 00000000..e1348821 --- /dev/null +++ b/packages/neon/neon/lib/src/blocs/clients.dart @@ -0,0 +1,232 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:meta/meta.dart'; +import 'package:neon/src/bloc/bloc.dart'; +import 'package:neon/src/bloc/result.dart'; +import 'package:neon/src/blocs/accounts.dart'; +import 'package:neon/src/blocs/capabilities.dart'; +import 'package:neon/src/models/account.dart'; +import 'package:neon/src/models/client_implementation.dart'; +import 'package:neon/src/models/notifications_interface.dart'; +import 'package:neon/src/settings/models/options_collection.dart'; +import 'package:neon/src/utils/request_manager.dart'; +import 'package:nextcloud/core.dart' as core; +import 'package:nextcloud/nextcloud.dart'; +import 'package:provider/provider.dart'; +import 'package:rxdart/rxdart.dart'; + +@internal +abstract interface class ClientsBlocEvents { + /// Sets the active client using the [clientID]. + /// + /// If the client is already the active client nothing will happen. + /// When using [skipAlreadySet] nothing will be done if there already is an active client. + void setActiveClient(final String clientID, {final bool skipAlreadySet = false}); +} + +@internal +abstract interface class ClientsBlocStates { + BehaviorSubject>> get clientImplementations; + + BehaviorSubject> get notificationsClientImplementation; + + BehaviorSubject get activeClient; + + BehaviorSubject get openNotifications; + + BehaviorSubject> get clientVersions; +} + +@internal +class ClientsBloc extends InteractiveBloc implements ClientsBlocEvents, ClientsBlocStates { + ClientsBloc( + this._capabilitiesBloc, + this._accountsBloc, + this._account, + this._allClientImplementations, + ) { + _clients.listen((final result) { + clientImplementations + .add(result.transform((final data) => _filteredClientImplementations(data.map((final c) => c.id)))); + }); + + clientImplementations.listen((final result) async { + if (!result.hasData) { + return; + } + + // dispose unsupported clients + for (final client in _allClientImplementations) { + if (result.requireData.tryFind(client.id) == null) { + client.blocsCache.remove(_account); + } + } + + final options = _accountsBloc.getOptionsFor(_account); + final initialClient = options.initialClient.value ?? _getInitialClientFallback(); + if (initialClient != null) { + await setActiveClient(initialClient, skipAlreadySet: true); + } + + unawaited(_checkCompatibility()); + }); + + _capabilitiesBloc.capabilities.listen((final result) { + notificationsClientImplementation.add( + result.transform( + (final data) => data.capabilities.notificationsCapabilities?.notifications != null + ? _findClientImplementation(AppIDs.notifications) + : null, + ), + ); + + unawaited(_checkCompatibility()); + }); + + unawaited(refresh()); + } + + /// Determines the clientid of initial client. + /// + /// It requires [clientImplementations] to have both a value and data. + /// + /// The files client is always installed and can not be removed so it will be used, but in the + /// case this changes at a later point the first supported client will be returned. + /// + /// Returns null when no client is supported by the server. + String? _getInitialClientFallback() { + final supportedClients = clientImplementations.value.requireData; + + for (final fallback in {AppIDs.dashboard, AppIDs.files}) { + if (supportedClients.tryFind(fallback) != null) { + return fallback; + } + } + + if (supportedClients.isNotEmpty) { + return supportedClients.first.id; + } + + return null; + } + + Future _checkCompatibility() async { + final clients = clientImplementations.valueOrNull; + final capabilities = _capabilitiesBloc.capabilities.valueOrNull; + + // ignore cached data + if (capabilities == null || clients == null || !capabilities.hasUncachedData || !clients.hasUncachedData) { + return; + } + + final notSupported = {}; + + try { + final (coreSupported, coreMinimumVersion) = _account.client.core.isSupported(capabilities.requireData); + if (!coreSupported) { + notSupported['core'] = coreMinimumVersion.toString(); + } + } catch (e, s) { + debugPrint(e.toString()); + debugPrint(s.toString()); + } + + for (final client in clients.requireData) { + try { + final (supported, minimumVersion) = await client.isSupported(_account, capabilities.requireData); + if (!(supported ?? true)) { + notSupported[client.id] = minimumVersion; + } + } catch (e, s) { + debugPrint(e.toString()); + debugPrint(s.toString()); + } + } + + if (notSupported.isNotEmpty) { + clientVersions.add(notSupported); + } + } + + T? _findClientImplementation(final String id) { + final matches = _filteredClientImplementations([id]); + if (matches.isNotEmpty) { + return matches.single as T; + } + + return null; + } + + Iterable _filteredClientImplementations(final Iterable clientIds) => + _allClientImplementations.where((final c) => clientIds.contains(c.id)); + + final CapabilitiesBloc _capabilitiesBloc; + final AccountsBloc _accountsBloc; + final Account _account; + final Iterable _allClientImplementations; + final _clients = BehaviorSubject>>(); + + @override + void dispose() { + unawaited(_clients.close()); + unawaited(clientImplementations.close()); + unawaited(notificationsClientImplementation.close()); + unawaited(activeClient.close()); + unawaited(openNotifications.close()); + unawaited(clientVersions.close()); + + super.dispose(); + } + + @override + BehaviorSubject activeClient = BehaviorSubject(); + + @override + BehaviorSubject>>> clientImplementations = + BehaviorSubject(); + + @override + BehaviorSubject> notificationsClientImplementation = BehaviorSubject(); + + @override + BehaviorSubject openNotifications = BehaviorSubject(); + + @override + BehaviorSubject> clientVersions = BehaviorSubject(); + + @override + Future refresh() async { + await RequestManager.instance.wrapNextcloud( + _account.id, + 'apps-apps', + _clients, + _account.client.core.navigation.getAppsNavigationRaw(), + (final response) => response.body.ocs.data.toList(), + ); + } + + @override + Future setActiveClient(final String clientID, {final bool skipAlreadySet = false}) async { + if (clientID == AppIDs.notifications) { + openNotifications.add(null); + return; + } + + final clients = await clientImplementations.firstWhere((final c) => c.hasData); + final client = clients.requireData.tryFind(clientID); + if (client != null) { + if ((!activeClient.hasValue || !skipAlreadySet) && activeClient.valueOrNull?.id != clientID) { + activeClient.add(client); + } + } else { + throw Exception('Client $clientID not found'); + } + } + + T getClientBloc(final ClientImplementation clientImplementation) => + clientImplementation.getBloc(_account); + + List> get clientBlocProviders => + _allClientImplementations.map((final clientImplementation) => clientImplementation.blocProvider).toList(); +} diff --git a/packages/neon/neon/lib/src/blocs/push_notifications.dart b/packages/neon/neon/lib/src/blocs/push_notifications.dart index 1563c331..6f6a83ca 100644 --- a/packages/neon/neon/lib/src/blocs/push_notifications.dart +++ b/packages/neon/neon/lib/src/blocs/push_notifications.dart @@ -35,7 +35,7 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents, } final AccountsBloc _accountsBloc; - late final _storage = const AppStorage(StorageKeys.lastEndpoint); + late final _storage = const ClientStorage(StorageKeys.lastEndpoint); final GlobalOptions _globalOptions; StreamSubscription>? _accountsListener; diff --git a/packages/neon/neon/lib/src/blocs/unified_search.dart b/packages/neon/neon/lib/src/blocs/unified_search.dart index 3be1cb50..4c015b2d 100644 --- a/packages/neon/neon/lib/src/blocs/unified_search.dart +++ b/packages/neon/neon/lib/src/blocs/unified_search.dart @@ -6,7 +6,7 @@ import 'package:meta/meta.dart'; import 'package:neon/models.dart'; import 'package:neon/src/bloc/bloc.dart'; import 'package:neon/src/bloc/result.dart'; -import 'package:neon/src/blocs/apps.dart'; +import 'package:neon/src/blocs/clients.dart'; import 'package:nextcloud/core.dart' as core; import 'package:rxdart/rxdart.dart'; @@ -29,17 +29,17 @@ abstract interface class UnifiedSearchBlocStates { @internal class UnifiedSearchBloc extends InteractiveBloc implements UnifiedSearchBlocEvents, UnifiedSearchBlocStates { UnifiedSearchBloc( - this._appsBloc, + this._clientsBloc, this._account, ) { - _appsBloc.activeApp.listen((final _) { + _clientsBloc.activeClient.listen((final _) { if (enabled.value) { disable(); } }); } - final AppsBloc _appsBloc; + final ClientsBloc _clientsBloc; final Account _account; String _term = ''; @@ -141,19 +141,19 @@ class UnifiedSearchBloc extends InteractiveBloc implements UnifiedSearchBlocEven Iterable>> _sortResults( final Map> results, ) sync* { - final activeApp = _appsBloc.activeApp.value; + final activeClient = _clientsBloc.activeClient.value; yield* results.entries - .where((final entry) => _providerMatchesApp(entry.key, activeApp)) + .where((final entry) => _providerMatchesClient(entry.key, activeClient)) .sorted((final a, final b) => _sortEntriesCount(a.value, b.value)); yield* results.entries - .whereNot((final entry) => _providerMatchesApp(entry.key, activeApp)) + .whereNot((final entry) => _providerMatchesClient(entry.key, activeClient)) .where((final entry) => _hasEntries(entry.value)) .sorted((final a, final b) => _sortEntriesCount(a.value, b.value)); } - bool _providerMatchesApp(final core.UnifiedSearchProvider provider, final AppImplementation app) => - provider.id == app.id || provider.id.startsWith('${app.id}_'); + bool _providerMatchesClient(final core.UnifiedSearchProvider provider, final ClientImplementation client) => + provider.id == client.id || provider.id.startsWith('${client.id}_'); bool _hasEntries(final Result result) => !result.hasData || result.requireData.entries.isNotEmpty; diff --git a/packages/neon/neon/lib/src/models/app_implementation.dart b/packages/neon/neon/lib/src/models/client_implementation.dart similarity index 80% rename from packages/neon/neon/lib/src/models/app_implementation.dart rename to packages/neon/neon/lib/src/models/client_implementation.dart index bca76377..9ccd0a09 100644 --- a/packages/neon/neon/lib/src/models/app_implementation.dart +++ b/packages/neon/neon/lib/src/models/client_implementation.dart @@ -20,24 +20,24 @@ import 'package:rxdart/rxdart.dart'; import 'package:vector_graphics/vector_graphics.dart'; @immutable -abstract class AppImplementation implements Disposable { +abstract class ClientImplementation implements Disposable { String get id; LocalizationsDelegate get localizationsDelegate; Iterable get supportedLocales; - String nameFromLocalization(final NeonLocalizations localizations) => localizations.appImplementationName(id); + String nameFromLocalization(final NeonLocalizations localizations) => localizations.clientImplementationName(id); String name(final BuildContext context) => nameFromLocalization(NeonLocalizations.of(context)); @protected - late final AppStorage storage = AppStorage(StorageKeys.apps, id); + late final ClientStorage storage = ClientStorage(StorageKeys.clients, id); @mustBeOverridden R get options; - /// Checks if the app is supported on the server of the [account]. + /// Checks if the client is supported on the server of the [account]. /// - /// A `supported` value of `null` means that it can not be known if the app is supported. - /// This is the case for apps that depend on the server version like files and we assume that the app is supported. + /// A `supported` value of `null` means that it can not be known if the client is supported. + /// This is the case for clients that depend on the server version like files and we assume that the client is supported. /// The server support is handled differently. /// /// The first value of the record is the supported status and the second value is the supported minimum version. @@ -87,13 +87,13 @@ abstract class AppImplementation ], ); - /// Route for the app. + /// Route for the client. /// - /// All pages of the app must be specified as subroutes. + /// All pages of the client must be specified as subroutes. /// If this is not [GoRoute] an initial route name must be specified by overriding [initialRouteName]. RouteBase get route; - /// Name of the initial route for this app. + /// Name of the initial route for this client. /// /// Subclasses that don't provide a [GoRoute] for [route] must override this. String get initialRouteName { @@ -139,13 +139,13 @@ abstract class AppImplementation final ThemeExtension? theme = null; @override - bool operator ==(final Object other) => other is AppImplementation && other.id == id; + bool operator ==(final Object other) => other is ClientImplementation && other.id == id; @override int get hashCode => id.hashCode; } -extension AppImplementationFind on Iterable { - AppImplementation? tryFind(final String? appID) => firstWhereOrNull((final app) => app.id == appID); - AppImplementation find(final String appID) => firstWhere((final app) => app.id == appID); +extension ClientImplementationFind on Iterable { + ClientImplementation? tryFind(final String? clientID) => firstWhereOrNull((final client) => client.id == clientID); + ClientImplementation find(final String clientID) => firstWhere((final client) => client.id == clientID); } diff --git a/packages/neon/neon/lib/src/models/notifications_interface.dart b/packages/neon/neon/lib/src/models/notifications_interface.dart index abcc1356..266a33ab 100644 --- a/packages/neon/neon/lib/src/models/notifications_interface.dart +++ b/packages/neon/neon/lib/src/models/notifications_interface.dart @@ -1,11 +1,11 @@ import 'package:meta/meta.dart'; import 'package:neon/src/bloc/bloc.dart'; -import 'package:neon/src/models/app_implementation.dart'; +import 'package:neon/src/models/client_implementation.dart'; import 'package:neon/src/settings/models/options_collection.dart'; -abstract interface class NotificationsAppInterface extends AppImplementation { - NotificationsAppInterface(); +abstract interface class NotificationsClientInterface extends ClientImplementation { + NotificationsClientInterface(); @override @mustBeOverridden @@ -19,6 +19,6 @@ abstract interface class NotificationsBlocInterface extends InteractiveBloc { void deleteNotification(final int id); } -abstract interface class NotificationsOptionsInterface extends NextcloudAppOptions { +abstract interface class NotificationsOptionsInterface extends NextcloudClientOptions { NotificationsOptionsInterface(super.storage); } diff --git a/packages/neon/neon/lib/src/pages/account_settings.dart b/packages/neon/neon/lib/src/pages/account_settings.dart index e41b507d..5f36a924 100644 --- a/packages/neon/neon/lib/src/pages/account_settings.dart +++ b/packages/neon/neon/lib/src/pages/account_settings.dart @@ -125,7 +125,7 @@ class AccountSettingsPage extends StatelessWidget { title: Text(NeonLocalizations.of(context).optionsCategoryGeneral), tiles: [ SelectSettingsTile( - option: options.initialApp, + option: options.initialClient, ), ], ), diff --git a/packages/neon/neon/lib/src/pages/nextcloud_app_settings.dart b/packages/neon/neon/lib/src/pages/client_settings.dart similarity index 68% rename from packages/neon/neon/lib/src/pages/nextcloud_app_settings.dart rename to packages/neon/neon/lib/src/pages/client_settings.dart index 09a1b459..37f64b64 100644 --- a/packages/neon/neon/lib/src/pages/nextcloud_app_settings.dart +++ b/packages/neon/neon/lib/src/pages/client_settings.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_material_design_icons/flutter_material_design_icons.dart'; import 'package:meta/meta.dart'; import 'package:neon/l10n/localizations.dart'; -import 'package:neon/src/models/app_implementation.dart'; +import 'package:neon/src/models/client_implementation.dart'; import 'package:neon/src/settings/widgets/option_settings_tile.dart'; import 'package:neon/src/settings/widgets/settings_category.dart'; import 'package:neon/src/settings/widgets/settings_list.dart'; @@ -10,29 +10,29 @@ import 'package:neon/src/theme/dialog.dart'; import 'package:neon/src/utils/confirmation_dialog.dart'; @internal -class NextcloudAppSettingsPage extends StatelessWidget { - const NextcloudAppSettingsPage({ - required this.appImplementation, +class NextcloudClientSettingsPage extends StatelessWidget { + const NextcloudClientSettingsPage({ + required this.clientImplementation, super.key, }); - final AppImplementation appImplementation; + final ClientImplementation clientImplementation; @override Widget build(final BuildContext context) { final appBar = AppBar( - title: Text(appImplementation.name(context)), + title: Text(clientImplementation.name(context)), actions: [ IconButton( onPressed: () async { if (await showConfirmationDialog( context, - NeonLocalizations.of(context).settingsResetForConfirmation(appImplementation.name(context)), + NeonLocalizations.of(context).settingsResetForConfirmation(clientImplementation.name(context)), )) { - appImplementation.options.reset(); + clientImplementation.options.reset(); } }, - tooltip: NeonLocalizations.of(context).settingsResetFor(appImplementation.name(context)), + tooltip: NeonLocalizations.of(context).settingsResetFor(clientImplementation.name(context)), icon: const Icon(MdiIcons.cogRefresh), ), ], @@ -40,15 +40,15 @@ class NextcloudAppSettingsPage extends StatelessWidget { final body = SettingsList( categories: [ - for (final category in [...appImplementation.options.categories, null]) ...[ - if (appImplementation.options.options.where((final option) => option.category == category).isNotEmpty) ...[ + for (final category in [...clientImplementation.options.categories, null]) ...[ + if (clientImplementation.options.options.where((final option) => option.category == category).isNotEmpty) ...[ SettingsCategory( title: Text( category != null ? category.name(context) : NeonLocalizations.of(context).optionsCategoryOther, ), tiles: [ for (final option - in appImplementation.options.options.where((final option) => option.category == category)) ...[ + in clientImplementation.options.options.where((final option) => option.category == category)) ...[ OptionSettingsTile(option: option), ], ], diff --git a/packages/neon/neon/lib/src/pages/home.dart b/packages/neon/neon/lib/src/pages/home.dart index 30d2e759..cf9f671d 100644 --- a/packages/neon/neon/lib/src/pages/home.dart +++ b/packages/neon/neon/lib/src/pages/home.dart @@ -5,9 +5,9 @@ import 'package:meta/meta.dart'; import 'package:neon/l10n/localizations.dart'; import 'package:neon/src/bloc/result.dart'; import 'package:neon/src/blocs/accounts.dart'; -import 'package:neon/src/blocs/apps.dart'; +import 'package:neon/src/blocs/clients.dart'; import 'package:neon/src/models/account.dart'; -import 'package:neon/src/models/app_implementation.dart'; +import 'package:neon/src/models/client_implementation.dart'; import 'package:neon/src/utils/global_options.dart'; import 'package:neon/src/utils/global_options.dart' as global_options; import 'package:neon/src/utils/global_popups.dart'; @@ -35,7 +35,7 @@ class _HomePageState extends State { late Account _account; late GlobalOptions _globalOptions; late AccountsBloc _accountsBloc; - late AppsBloc _appsBloc; + late ClientsBloc _clientsBloc; late StreamSubscription> _versionCheckSubscription; @override @@ -44,9 +44,9 @@ class _HomePageState extends State { _globalOptions = NeonProvider.of(context); _accountsBloc = NeonProvider.of(context); _account = _accountsBloc.activeAccount.value!; - _appsBloc = _accountsBloc.activeAppsBloc; + _clientsBloc = _accountsBloc.activeClientsBloc; - _versionCheckSubscription = _appsBloc.appVersions.listen((final values) { + _versionCheckSubscription = _clientsBloc.clientVersions.listen((final values) { if (!mounted) { return; } @@ -56,12 +56,12 @@ class _HomePageState extends State { final buffer = StringBuffer()..writeln(); for (final error in values.entries) { - final appId = error.key; + final clientId = error.key; final minVersion = error.value; - final appName = l10n.appImplementationName(appId); + final clientName = l10n.clientImplementationName(clientId); - if (appName.isNotEmpty && minVersion != null) { - buffer.writeln('- $appName $minVersion'); + if (clientName.isNotEmpty && minVersion != null) { + buffer.writeln('- $clientName $minVersion'); } } @@ -126,20 +126,20 @@ class _HomePageState extends State { const drawer = NeonDrawer(); const appBar = NeonAppBar(); - final appView = StreamBuilder( + final clientView = StreamBuilder( stream: _accountsBloc.activeUnifiedSearchBloc.enabled, builder: (final context, final unifiedSearchEnabledSnapshot) { if (unifiedSearchEnabledSnapshot.data ?? false) { return const NeonUnifiedSearchResults(); } - return ResultBuilder>.behaviorSubject( - subject: _appsBloc.appImplementations, - builder: (final context, final appImplementations) { - if (!appImplementations.hasData) { + return ResultBuilder>.behaviorSubject( + subject: _clientsBloc.clientImplementations, + builder: (final context, final clientImplementations) { + if (!clientImplementations.hasData) { return const SizedBox(); } - if (appImplementations.requireData.isEmpty) { + if (clientImplementations.requireData.isEmpty) { return Center( child: Text( NeonLocalizations.of(context).errorNoCompatibleNextcloudAppsFound, @@ -149,13 +149,13 @@ class _HomePageState extends State { } return StreamBuilder( - stream: _appsBloc.activeApp, - builder: (final context, final activeAppIDSnapshot) { - if (!activeAppIDSnapshot.hasData) { + stream: _clientsBloc.activeClient, + builder: (final context, final activeClientIDSnapshot) { + if (!activeClientIDSnapshot.hasData) { return const SizedBox(); } - return activeAppIDSnapshot.requireData.page; + return activeClientIDSnapshot.requireData.page; }, ); }, @@ -173,7 +173,7 @@ class _HomePageState extends State { resizeToAvoidBottomInset: false, drawer: !drawerAlwaysVisible ? drawer : null, appBar: appBar, - body: appView, + body: clientView, ); if (drawerAlwaysVisible) { @@ -205,7 +205,7 @@ class _HomePageState extends State { return false; }, child: MultiProvider( - providers: _appsBloc.appBlocProviders, + providers: _clientsBloc.clientBlocProviders, child: body, ), ); diff --git a/packages/neon/neon/lib/src/pages/settings.dart b/packages/neon/neon/lib/src/pages/settings.dart index 7dc71852..b160785b 100644 --- a/packages/neon/neon/lib/src/pages/settings.dart +++ b/packages/neon/neon/lib/src/pages/settings.dart @@ -4,7 +4,7 @@ import 'package:flutter_material_design_icons/flutter_material_design_icons.dart import 'package:meta/meta.dart'; import 'package:neon/l10n/localizations.dart'; import 'package:neon/src/blocs/accounts.dart'; -import 'package:neon/src/models/app_implementation.dart'; +import 'package:neon/src/models/client_implementation.dart'; import 'package:neon/src/platform/platform.dart'; import 'package:neon/src/router.dart'; import 'package:neon/src/settings/utils/settings_export_helper.dart'; @@ -28,7 +28,7 @@ import 'package:url_launcher/url_launcher_string.dart'; @internal enum SettingsCategories { - apps, + clients, theme, navigation, pushNotifications, @@ -56,7 +56,7 @@ class _SettingsPageState extends State { Widget build(final BuildContext context) { final globalOptions = NeonProvider.of(context); final accountsBloc = NeonProvider.of(context); - final appImplementations = NeonProvider.of>(context); + final clientImplementations = NeonProvider.of>(context); final branding = Branding.of(context); final appBar = AppBar( @@ -67,8 +67,8 @@ class _SettingsPageState extends State { if (await showConfirmationDialog(context, NeonLocalizations.of(context).settingsResetAllConfirmation)) { globalOptions.reset(); - for (final appImplementation in appImplementations) { - appImplementation.options.reset(); + for (final clientImplementation in clientImplementations) { + clientImplementation.options.reset(); } for (final account in accountsBloc.accounts.value) { @@ -100,15 +100,15 @@ class _SettingsPageState extends State { categories: [ SettingsCategory( title: Text(NeonLocalizations.of(context).settingsApps), - key: ValueKey(SettingsCategories.apps.name), + key: ValueKey(SettingsCategories.clients.name), tiles: [ - for (final appImplementation in appImplementations) ...[ - if (appImplementation.options.options.isNotEmpty) ...[ + for (final clientImplementation in clientImplementations) ...[ + if (clientImplementation.options.options.isNotEmpty) ...[ CustomSettingsTile( - leading: appImplementation.buildIcon(), - title: Text(appImplementation.name(context)), + leading: clientImplementation.buildIcon(), + title: Text(clientImplementation.name(context)), onTap: () { - NextcloudAppSettingsRoute(appid: appImplementation.id).go(context); + NextcloudClientSettingsRoute(clientid: clientImplementation.id).go(context); }, ), ], @@ -351,13 +351,13 @@ class _SettingsPageState extends State { SettingsExportHelper _buildSettingsExportHelper(final BuildContext context) { final globalOptions = NeonProvider.of(context); final accountsBloc = NeonProvider.of(context); - final appImplementations = NeonProvider.of>(context); + final clientImplementations = NeonProvider.of>(context); return SettingsExportHelper( exportables: { globalOptions, AccountsBlocExporter(accountsBloc), - AppImplementationsExporter(appImplementations), + ClientImplementationsExporter(clientImplementations), }, ); } diff --git a/packages/neon/neon/lib/src/router.dart b/packages/neon/neon/lib/src/router.dart index 83c20b18..e418dd11 100644 --- a/packages/neon/neon/lib/src/router.dart +++ b/packages/neon/neon/lib/src/router.dart @@ -8,15 +8,15 @@ import 'package:go_router/go_router.dart'; import 'package:meta/meta.dart'; import 'package:neon/src/blocs/accounts.dart'; import 'package:neon/src/models/account.dart'; -import 'package:neon/src/models/app_implementation.dart'; +import 'package:neon/src/models/client_implementation.dart'; import 'package:neon/src/pages/account_settings.dart'; +import 'package:neon/src/pages/client_settings.dart'; import 'package:neon/src/pages/home.dart'; import 'package:neon/src/pages/login.dart'; import 'package:neon/src/pages/login_check_account.dart'; import 'package:neon/src/pages/login_check_server_status.dart'; import 'package:neon/src/pages/login_flow.dart'; import 'package:neon/src/pages/login_qr_code.dart'; -import 'package:neon/src/pages/nextcloud_app_settings.dart'; import 'package:neon/src/pages/route_not_found.dart'; import 'package:neon/src/pages/settings.dart'; import 'package:neon/src/utils/provider.dart'; @@ -96,9 +96,9 @@ class AccountSettingsRoute extends GoRouteData { path: 'settings', name: 'Settings', routes: [ - TypedGoRoute( - path: 'apps/:appid', - name: 'NextcloudAppSettings', + TypedGoRoute( + path: 'apps/:clientid', + name: 'NextcloudClientSettings', ), TypedGoRoute<_AddAccountRoute>( path: 'account/add', @@ -365,19 +365,19 @@ class _AddAccountCheckAccountRoute extends LoginCheckAccountRoute { } @immutable -class NextcloudAppSettingsRoute extends GoRouteData { - const NextcloudAppSettingsRoute({ - required this.appid, +class NextcloudClientSettingsRoute extends GoRouteData { + const NextcloudClientSettingsRoute({ + required this.clientid, }); - final String appid; + final String clientid; @override Widget build(final BuildContext context, final GoRouterState state) { - final appImplementations = NeonProvider.of>(context); - final appImplementation = appImplementations.tryFind(appid)!; + final clientImplementations = NeonProvider.of>(context); + final clientImplementation = clientImplementations.tryFind(clientid)!; - return NextcloudAppSettingsPage(appImplementation: appImplementation); + return NextcloudClientSettingsPage(clientImplementation: clientImplementation); } } diff --git a/packages/neon/neon/lib/src/router.g.dart b/packages/neon/neon/lib/src/router.g.dart index 71b8c324..139e2c9c 100644 --- a/packages/neon/neon/lib/src/router.g.dart +++ b/packages/neon/neon/lib/src/router.g.dart @@ -22,9 +22,9 @@ RouteBase get $homeRoute => GoRouteData.$route( factory: $SettingsRouteExtension._fromState, routes: [ GoRouteData.$route( - path: 'apps/:appid', - name: 'NextcloudAppSettings', - factory: $NextcloudAppSettingsRouteExtension._fromState, + path: 'apps/:clientid', + name: 'NextcloudClientSettings', + factory: $NextcloudClientSettingsRouteExtension._fromState, ), GoRouteData.$route( path: 'account/add', @@ -98,7 +98,7 @@ extension $SettingsRouteExtension on SettingsRoute { } const _$SettingsCategoriesEnumMap = { - SettingsCategories.apps: 'apps', + SettingsCategories.clients: 'clients', SettingsCategories.theme: 'theme', SettingsCategories.navigation: 'navigation', SettingsCategories.pushNotifications: 'push-notifications', @@ -108,13 +108,13 @@ const _$SettingsCategoriesEnumMap = { SettingsCategories.other: 'other', }; -extension $NextcloudAppSettingsRouteExtension on NextcloudAppSettingsRoute { - static NextcloudAppSettingsRoute _fromState(GoRouterState state) => NextcloudAppSettingsRoute( - appid: state.pathParameters['appid']!, +extension $NextcloudClientSettingsRouteExtension on NextcloudClientSettingsRoute { + static NextcloudClientSettingsRoute _fromState(GoRouterState state) => NextcloudClientSettingsRoute( + clientid: state.pathParameters['clientid']!, ); String get location => GoRouteData.$location( - '/settings/apps/${Uri.encodeComponent(appid)}', + '/settings/apps/${Uri.encodeComponent(clientid)}', ); void go(BuildContext context) => context.go(location); diff --git a/packages/neon/neon/lib/src/settings/models/options_collection.dart b/packages/neon/neon/lib/src/settings/models/options_collection.dart index 37b99377..cfaae990 100644 --- a/packages/neon/neon/lib/src/settings/models/options_collection.dart +++ b/packages/neon/neon/lib/src/settings/models/options_collection.dart @@ -11,7 +11,7 @@ abstract class OptionsCollection implements Exportable, Disposable { /// Storage backend to use. @protected - final AppStorage storage; + final ClientStorage storage; /// Collection of options. @protected @@ -56,9 +56,9 @@ abstract class OptionsCollection implements Exportable, Disposable { } } -/// OptionsCollection for a neon app. -abstract class NextcloudAppOptions extends OptionsCollection { - NextcloudAppOptions(super.storage); +/// OptionsCollection for a neon client. +abstract class NextcloudClientOptions extends OptionsCollection { + NextcloudClientOptions(super.storage); /// Collection of categories to display the options in the settings. late final Iterable categories; diff --git a/packages/neon/neon/lib/src/settings/models/storage.dart b/packages/neon/neon/lib/src/settings/models/storage.dart index 9dcd4af1..f9ec138c 100644 --- a/packages/neon/neon/lib/src/settings/models/storage.dart +++ b/packages/neon/neon/lib/src/settings/models/storage.dart @@ -22,7 +22,7 @@ abstract interface class Storable { @internal enum StorageKeys implements Storable { - apps._('app'), + clients._('app'), accounts._('accounts'), global._('global'), lastUsedAccount._('last-used-account'), @@ -100,8 +100,8 @@ final class SingleValueStorage { @immutable @internal -final class AppStorage implements SettingsStorage { - const AppStorage( +final class ClientStorage implements SettingsStorage { + const ClientStorage( this.groupKey, [ this.suffix, ]); diff --git a/packages/neon/neon/lib/src/settings/utils/settings_export_helper.dart b/packages/neon/neon/lib/src/settings/utils/settings_export_helper.dart index acda7208..07636ac1 100644 --- a/packages/neon/neon/lib/src/settings/utils/settings_export_helper.dart +++ b/packages/neon/neon/lib/src/settings/utils/settings_export_helper.dart @@ -5,7 +5,7 @@ import 'dart:typed_data'; import 'package:meta/meta.dart'; import 'package:neon/src/blocs/accounts.dart'; import 'package:neon/src/models/account.dart' show Account, AccountFind; -import 'package:neon/src/models/app_implementation.dart'; +import 'package:neon/src/models/client_implementation.dart'; import 'package:neon/src/settings/models/exportable.dart'; import 'package:neon/src/settings/models/option.dart'; import 'package:neon/src/settings/models/storage.dart'; @@ -73,22 +73,22 @@ class SettingsExportHelper { Map exportToJson() => Map.fromEntries(exportables.map((final e) => e.export())); } -/// Helper class to export [AppImplementation]s implementing the [Exportable] interface. +/// Helper class to export [ClientImplementation]s implementing the [Exportable] interface. @internal @immutable -class AppImplementationsExporter implements Exportable { - const AppImplementationsExporter(this.appImplementations); +class ClientImplementationsExporter implements Exportable { + const ClientImplementationsExporter(this.clientImplementations); - /// List of apps to export. - final Iterable appImplementations; + /// List of clients to export. + final Iterable clientImplementations; /// Key the exported value will be stored at. - static final _key = StorageKeys.apps.value; + static final _key = StorageKeys.clients.value; @override MapEntry export() => MapEntry( _key, - Map.fromEntries(appImplementations.map((final app) => app.options.export())), + Map.fromEntries(clientImplementations.map((final client) => client.options.export())), ); @override @@ -100,10 +100,10 @@ class AppImplementationsExporter implements Exportable { } for (final element in values.entries) { - final app = appImplementations.tryFind(element.key); + final client = clientImplementations.tryFind(element.key); - if (app != null) { - app.options.import(values); + if (client != null) { + client.options.import(values); } } } diff --git a/packages/neon/neon/lib/src/theme/theme.dart b/packages/neon/neon/lib/src/theme/theme.dart index a2692d61..be295b26 100644 --- a/packages/neon/neon/lib/src/theme/theme.dart +++ b/packages/neon/neon/lib/src/theme/theme.dart @@ -13,13 +13,13 @@ class AppTheme { required this.neonTheme, final bool keepOriginalAccentColor = false, this.oledAsDark = false, - this.appThemes, + this.clientThemes, }) : keepOriginalAccentColor = nextcloudTheme == null || keepOriginalAccentColor; final core.ThemingPublicCapabilities_Theming? nextcloudTheme; final bool keepOriginalAccentColor; final bool oledAsDark; - final Iterable? appThemes; + final Iterable? clientThemes; final NeonTheme neonTheme; ColorScheme _buildColorScheme(final Brightness brightness) { @@ -51,7 +51,7 @@ class AppTheme { inputDecorationTheme: _inputDecorationTheme, extensions: [ neonTheme, - ...?appThemes, + ...?clientThemes, ], ); } diff --git a/packages/neon/neon/lib/src/utils/account_options.dart b/packages/neon/neon/lib/src/utils/account_options.dart index 225c2245..6eaaec59 100644 --- a/packages/neon/neon/lib/src/utils/account_options.dart +++ b/packages/neon/neon/lib/src/utils/account_options.dart @@ -1,6 +1,6 @@ import 'package:meta/meta.dart'; import 'package:neon/l10n/localizations.dart'; -import 'package:neon/src/blocs/apps.dart'; +import 'package:neon/src/blocs/clients.dart'; import 'package:neon/src/settings/models/option.dart'; import 'package:neon/src/settings/models/options_collection.dart'; import 'package:neon/src/settings/models/storage.dart'; @@ -10,29 +10,29 @@ import 'package:neon/src/settings/models/storage.dart'; class AccountSpecificOptions extends OptionsCollection { AccountSpecificOptions( super.storage, - this._appsBloc, + this._clientsBloc, ) { - _appsBloc.appImplementations.listen((final result) { + _clientsBloc.clientImplementations.listen((final result) { if (!result.hasData) { return; } - initialApp.values = { + initialClient.values = { null: (final context) => NeonLocalizations.of(context).accountOptionsAutomatic, - }..addEntries(result.requireData.map((final app) => MapEntry(app.id, app.name))); + }..addEntries(result.requireData.map((final client) => MapEntry(client.id, client.name))); }); } - final AppsBloc _appsBloc; + final ClientsBloc _clientsBloc; @override late final List> options = [ - initialApp, + initialClient, ]; - late final initialApp = SelectOption( + late final initialClient = SelectOption( storage: storage, - key: AccountOptionKeys.initialApp, + key: AccountOptionKeys.initialClient, label: (final context) => NeonLocalizations.of(context).accountOptionsInitialApp, defaultValue: null, values: {}, @@ -41,7 +41,7 @@ class AccountSpecificOptions extends OptionsCollection { @internal enum AccountOptionKeys implements Storable { - initialApp._('initial-app'); + initialClient._('initial-app'); const AccountOptionKeys._(this.value); diff --git a/packages/neon/neon/lib/src/utils/app_route.dart b/packages/neon/neon/lib/src/utils/client_route.dart similarity index 50% rename from packages/neon/neon/lib/src/utils/app_route.dart rename to packages/neon/neon/lib/src/utils/client_route.dart index 0985b9a7..57d44dc8 100644 --- a/packages/neon/neon/lib/src/utils/app_route.dart +++ b/packages/neon/neon/lib/src/utils/client_route.dart @@ -1,14 +1,14 @@ import 'package:flutter/widgets.dart'; import 'package:go_router/go_router.dart'; -/// [RouteData] for the initial page of an app. +/// [RouteData] for the initial page of a client. /// /// Subclasses must override one of [build] or [redirect]. -/// Routes should be prefixed with [appsBaseRoutePrefix]. +/// Routes should be prefixed with [clientsBaseRoutePrefix]. @immutable -abstract class NeonBaseAppRoute extends GoRouteData { - /// Creates a new app base route. - const NeonBaseAppRoute(); +abstract class NeonBaseClientRoute extends GoRouteData { + /// Creates a new client base route. + const NeonBaseClientRoute(); @override Page buildPage(final BuildContext context, final GoRouterState state) => NoTransitionPage( @@ -16,5 +16,5 @@ abstract class NeonBaseAppRoute extends GoRouteData { ); } -/// Prefix for [NeonBaseAppRoute]s. -const appsBaseRoutePrefix = '/apps/'; +/// Prefix for [NeonBaseClientRoute]s. +const clientsBaseRoutePrefix = '/apps/'; diff --git a/packages/neon/neon/lib/src/utils/global_options.dart b/packages/neon/neon/lib/src/utils/global_options.dart index c2d93699..68ef2592 100644 --- a/packages/neon/neon/lib/src/utils/global_options.dart +++ b/packages/neon/neon/lib/src/utils/global_options.dart @@ -18,7 +18,7 @@ const unifiedPushNextPushID = 'org.unifiedpush.distributor.nextpush'; class GlobalOptions extends OptionsCollection { GlobalOptions( this._packageInfo, - ) : super(const AppStorage(StorageKeys.global)) { + ) : super(const ClientStorage(StorageKeys.global)) { pushNotificationsEnabled.addListener(_pushNotificationsEnabledListener); rememberLastUsedAccount.addListener(_rememberLastUsedAccountListener); } diff --git a/packages/neon/neon/lib/src/utils/push_utils.dart b/packages/neon/neon/lib/src/utils/push_utils.dart index 13a8a808..762d1519 100644 --- a/packages/neon/neon/lib/src/utils/push_utils.dart +++ b/packages/neon/neon/lib/src/utils/push_utils.dart @@ -25,7 +25,7 @@ class PushUtils { const PushUtils._(); static notifications.RSAKeypair loadRSAKeypair() { - const storage = AppStorage(StorageKeys.notifications); + const storage = ClientStorage(StorageKeys.notifications); const keyDevicePrivateKey = 'device-private-key'; final notifications.RSAKeypair keypair; @@ -132,11 +132,11 @@ class PushUtils { } if (notification?.shouldNotify ?? true) { - final appID = notification?.app ?? pushNotification.subject.app ?? 'nextcloud'; - String? appName = localizations.appImplementationName(appID); - if (appName.isEmpty) { - debugPrint('Missing app name for $appID'); - appName = null; + final clientID = notification?.app ?? pushNotification.subject.app ?? 'nextcloud'; + String? clientName = localizations.clientImplementationName(clientID); + if (clientName.isEmpty) { + debugPrint('Missing client name for $clientID'); + clientName = null; } final title = (notification?.subject ?? pushNotification.subject.subject)!; final message = (notification?.message.isNotEmpty ?? false) ? notification!.message : null; @@ -144,14 +144,14 @@ class PushUtils { await localNotificationsPlugin.show( _getNotificationID(instance, pushNotification), - message != null && appName != null ? '$appName: $title' : title, + message != null && clientName != null ? '$clientName: $title' : title, message, NotificationDetails( android: AndroidNotificationDetails( - appID, - appName ?? appID, + clientID, + clientName ?? clientID, subText: accounts.length > 1 && account != null ? account.humanReadableID : null, - groupKey: 'app_$appID', + groupKey: 'app_$clientID', icon: '@mipmap/ic_launcher', largeIcon: largeIconBitmap, when: when?.millisecondsSinceEpoch, diff --git a/packages/neon/neon/lib/src/widgets/app_bar.dart b/packages/neon/neon/lib/src/widgets/app_bar.dart index 9482beff..c599a2bb 100644 --- a/packages/neon/neon/lib/src/widgets/app_bar.dart +++ b/packages/neon/neon/lib/src/widgets/app_bar.dart @@ -5,13 +5,13 @@ import 'package:meta/meta.dart'; import 'package:neon/l10n/localizations.dart'; import 'package:neon/src/bloc/result.dart'; import 'package:neon/src/blocs/accounts.dart'; -import 'package:neon/src/blocs/apps.dart'; +import 'package:neon/src/blocs/clients.dart'; import 'package:neon/src/models/account.dart'; -import 'package:neon/src/models/app_implementation.dart'; +import 'package:neon/src/models/client_implementation.dart'; import 'package:neon/src/models/notifications_interface.dart'; import 'package:neon/src/utils/provider.dart'; import 'package:neon/src/widgets/account_switcher_button.dart'; -import 'package:neon/src/widgets/app_implementation_icon.dart'; +import 'package:neon/src/widgets/client_implementation_icon.dart'; import 'package:neon/src/widgets/error.dart'; import 'package:neon/src/widgets/linear_progress_indicator.dart'; import 'package:provider/provider.dart'; @@ -32,7 +32,7 @@ class _NeonAppBarState extends State { late final AccountsBloc accountsBloc = NeonProvider.of(context); late final accounts = accountsBloc.accounts.value; late final account = accountsBloc.activeAccount.value!; - late final appsBloc = accountsBloc.activeAppsBloc; + late final clientsBloc = accountsBloc.activeClientsBloc; late final unifiedSearchBloc = accountsBloc.activeUnifiedSearchBloc; final _searchBarFocusNode = FocusNode(); @@ -62,11 +62,11 @@ class _NeonAppBarState extends State { } @override - Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( - subject: appsBloc.appImplementations, - builder: (final context, final appImplementations) => StreamBuilder( - stream: appsBloc.activeApp, - builder: (final context, final activeAppSnapshot) => StreamBuilder( + Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( + subject: clientsBloc.clientImplementations, + builder: (final context, final clientImplementations) => StreamBuilder( + stream: clientsBloc.activeClient, + builder: (final context, final activeClientSnapshot) => StreamBuilder( stream: unifiedSearchBloc.enabled, builder: (final context, final unifiedSearchEnabledSnapshot) { final unifiedSearchEnabled = unifiedSearchEnabledSnapshot.data ?? false; @@ -78,24 +78,24 @@ class _NeonAppBarState extends State { children: [ Row( children: [ - if (activeAppSnapshot.hasData) ...[ + if (activeClientSnapshot.hasData) ...[ Flexible( child: Text( - activeAppSnapshot.requireData.name(context), + activeClientSnapshot.requireData.name(context), ), ), ], - if (appImplementations.hasError) ...[ + if (clientImplementations.hasError) ...[ const SizedBox( width: 8, ), NeonError( - appImplementations.error, - onRetry: appsBloc.refresh, + clientImplementations.error, + onRetry: clientsBloc.refresh, onlyIcon: true, ), ], - if (appImplementations.isLoading) ...[ + if (clientImplementations.isLoading) ...[ const SizedBox( width: 8, ), @@ -179,7 +179,7 @@ class NotificationIconButton extends StatefulWidget { class _NotificationIconButtonState extends State { late AccountsBloc _accountsBloc; - late AppsBloc _appsBloc; + late ClientsBloc _clientsBloc; late List _accounts; late Account _account; late StreamSubscription notificationSubscription; @@ -188,14 +188,14 @@ class _NotificationIconButtonState extends State { void initState() { super.initState(); _accountsBloc = NeonProvider.of(context); - _appsBloc = _accountsBloc.activeAppsBloc; + _clientsBloc = _accountsBloc.activeClientsBloc; _accounts = _accountsBloc.accounts.value; _account = _accountsBloc.activeAccount.value!; - notificationSubscription = _appsBloc.openNotifications.listen((final _) async { - final notificationsAppImplementation = _appsBloc.notificationsAppImplementation.valueOrNull; - if (notificationsAppImplementation != null && notificationsAppImplementation.hasData) { - await _openNotifications(notificationsAppImplementation.data!); + notificationSubscription = _clientsBloc.openNotifications.listen((final _) async { + final notificationsClientImplementation = _clientsBloc.notificationsClientImplementation.valueOrNull; + if (notificationsClientImplementation != null && notificationsClientImplementation.hasData) { + await _openNotifications(notificationsClientImplementation.data!); } }); } @@ -209,7 +209,7 @@ class _NotificationIconButtonState extends State { // TODO: migrate to go_router with a separate page Future _openNotifications( - final NotificationsAppInterface app, + final NotificationsClientInterface client, ) async { final page = Scaffold( resizeToAvoidBottomInset: false, @@ -217,7 +217,7 @@ class _NotificationIconButtonState extends State { title: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text(app.name(context)), + Text(client.name(context)), if (_accounts.length > 1) ...[ Text( _account.humanReadableID, @@ -227,13 +227,13 @@ class _NotificationIconButtonState extends State { ], ), ), - body: app.page, + body: client.page, ); await Navigator.of(context).push( MaterialPageRoute( builder: (final context) => Provider( - create: (final context) => app.getBloc(_account), + create: (final context) => client.getBloc(_account), child: page, ), ), @@ -241,14 +241,14 @@ class _NotificationIconButtonState extends State { } @override - Widget build(final BuildContext context) => ResultBuilder.behaviorSubject( - subject: _appsBloc.notificationsAppImplementation, - builder: (final context, final notificationsAppImplementation) { - if (!notificationsAppImplementation.hasData) { + Widget build(final BuildContext context) => ResultBuilder.behaviorSubject( + subject: _clientsBloc.notificationsClientImplementation, + builder: (final context, final notificationsClientImplementation) { + if (!notificationsClientImplementation.hasData) { return const SizedBox.shrink(); } - final notificationsImplementationData = notificationsAppImplementation.data!; + final notificationsImplementationData = notificationsClientImplementation.data!; final notificationBloc = notificationsImplementationData.getBloc(_account); return IconButton( @@ -256,11 +256,11 @@ class _NotificationIconButtonState extends State { onPressed: () async { await _openNotifications(notificationsImplementationData); }, - tooltip: NeonLocalizations.of(context).appImplementationName(notificationsImplementationData.id), + tooltip: NeonLocalizations.of(context).clientImplementationName(notificationsImplementationData.id), icon: StreamBuilder( stream: notificationsImplementationData.getUnreadCounter(notificationBloc), - builder: (final context, final unreadCounterSnapshot) => NeonAppImplementationIcon( - appImplementation: notificationsImplementationData, + builder: (final context, final unreadCounterSnapshot) => NeonClientImplementationIcon( + clientImplementation: notificationsImplementationData, unreadCount: unreadCounterSnapshot.data, ), ), diff --git a/packages/neon/neon/lib/src/widgets/app_implementation_icon.dart b/packages/neon/neon/lib/src/widgets/client_implementation_icon.dart similarity index 76% rename from packages/neon/neon/lib/src/widgets/app_implementation_icon.dart rename to packages/neon/neon/lib/src/widgets/client_implementation_icon.dart index f8c83072..183af322 100644 --- a/packages/neon/neon/lib/src/widgets/app_implementation_icon.dart +++ b/packages/neon/neon/lib/src/widgets/client_implementation_icon.dart @@ -1,18 +1,18 @@ import 'package:flutter/material.dart'; import 'package:meta/meta.dart'; -import 'package:neon/src/models/app_implementation.dart'; +import 'package:neon/src/models/client_implementation.dart'; @internal -class NeonAppImplementationIcon extends StatelessWidget { - const NeonAppImplementationIcon({ - required this.appImplementation, +class NeonClientImplementationIcon extends StatelessWidget { + const NeonClientImplementationIcon({ + required this.clientImplementation, this.unreadCount, this.color, this.size, super.key, }); - final AppImplementation appImplementation; + final ClientImplementation clientImplementation; final int? unreadCount; @@ -29,7 +29,7 @@ class NeonAppImplementationIcon extends StatelessWidget { final icon = Container( margin: const EdgeInsets.all(5), - child: appImplementation.buildIcon( + child: clientImplementation.buildIcon( size: size, color: color, ), diff --git a/packages/neon/neon/lib/src/widgets/drawer.dart b/packages/neon/neon/lib/src/widgets/drawer.dart index a412a894..a859ddd6 100644 --- a/packages/neon/neon/lib/src/widgets/drawer.dart +++ b/packages/neon/neon/lib/src/widgets/drawer.dart @@ -5,8 +5,8 @@ import 'package:meta/meta.dart'; import 'package:neon/l10n/localizations.dart'; import 'package:neon/src/bloc/result.dart'; import 'package:neon/src/blocs/accounts.dart'; -import 'package:neon/src/blocs/apps.dart'; -import 'package:neon/src/models/app_implementation.dart'; +import 'package:neon/src/blocs/clients.dart'; +import 'package:neon/src/models/client_implementation.dart'; import 'package:neon/src/router.dart'; import 'package:neon/src/utils/provider.dart'; import 'package:neon/src/widgets/drawer_destination.dart'; @@ -24,17 +24,17 @@ class NeonDrawer extends StatelessWidget { @override Widget build(final BuildContext context) { final accountsBloc = NeonProvider.of(context); - final appsBloc = accountsBloc.activeAppsBloc; + final clientsBloc = accountsBloc.activeClientsBloc; return ResultBuilder.behaviorSubject( - subject: appsBloc.appImplementations, + subject: clientsBloc.clientImplementations, builder: (final context, final snapshot) { if (!snapshot.hasData) { return const SizedBox.shrink(); } return _NeonDrawer( - apps: snapshot.requireData, + clients: snapshot.requireData, ); }, ); @@ -43,10 +43,10 @@ class NeonDrawer extends StatelessWidget { class _NeonDrawer extends StatefulWidget { const _NeonDrawer({ - required this.apps, + required this.clients, }); - final Iterable apps; + final Iterable clients; @override State<_NeonDrawer> createState() => __NeonDrawerState(); @@ -54,53 +54,53 @@ class _NeonDrawer extends StatefulWidget { class __NeonDrawerState extends State<_NeonDrawer> { late AccountsBloc _accountsBloc; - late AppsBloc _appsBloc; - late List _apps; + late ClientsBloc _clientsBloc; + late List _clients; - late int _activeApp; + late int _activeClient; @override void initState() { super.initState(); _accountsBloc = NeonProvider.of(context); - _appsBloc = _accountsBloc.activeAppsBloc; + _clientsBloc = _accountsBloc.activeClientsBloc; - _apps = widget.apps.toList(); - _activeApp = _apps.indexWhere((final app) => app.id == _appsBloc.activeApp.valueOrNull?.id); + _clients = widget.clients.toList(); + _activeClient = _clients.indexWhere((final client) => client.id == _clientsBloc.activeClient.valueOrNull?.id); } - void onAppChange(final int index) { + void onDestinationSelected(final int index) { Scaffold.maybeOf(context)?.closeDrawer(); - // selected item is not a registered app like the SettingsPage - if (index >= _apps.length) { + // selected item is not a registered client like the SettingsPage + if (index >= _clients.length) { const SettingsRoute().go(context); return; } setState(() { - _activeApp = index; + _activeClient = index; }); - unawaited(_appsBloc.setActiveApp(_apps[index].id)); + unawaited(_clientsBloc.setActiveClient(_clients[index].id)); //context.goNamed(apps[index].routeName); } @override Widget build(final BuildContext context) { - final appDestinations = _apps.map( - (final app) => NavigationDrawerDestinationExtension.fromNeonDestination( - app.destination(context), + final clientDestinations = _clients.map( + (final client) => NavigationDrawerDestinationExtension.fromNeonDestination( + client.destination(context), ), ); final drawer = NavigationDrawer( - selectedIndex: _activeApp, - onDestinationSelected: onAppChange, + selectedIndex: _activeClient, + onDestinationSelected: onDestinationSelected, children: [ const NeonDrawerHeader(), - ...appDestinations, + ...clientDestinations, NavigationDrawerDestination( icon: const Icon(Icons.settings), label: Text(NeonLocalizations.of(context).settings), diff --git a/packages/neon/neon/lib/utils.dart b/packages/neon/neon/lib/utils.dart index f3d8012e..36a7337c 100644 --- a/packages/neon/neon/lib/utils.dart +++ b/packages/neon/neon/lib/utils.dart @@ -1,5 +1,5 @@ export 'package:neon/l10n/localizations.dart'; -export 'package:neon/src/utils/app_route.dart'; +export 'package:neon/src/utils/client_route.dart'; export 'package:neon/src/utils/confirmation_dialog.dart'; export 'package:neon/src/utils/exceptions.dart'; export 'package:neon/src/utils/hex_color.dart'; diff --git a/packages/neon/neon/test/app_implementation_test.dart b/packages/neon/neon/test/app_implementation_test.dart deleted file mode 100644 index 487f5229..00000000 --- a/packages/neon/neon/test/app_implementation_test.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:mocktail/mocktail.dart'; -import 'package:neon/src/models/app_implementation.dart'; -import 'package:test/test.dart'; - -// ignore: missing_override_of_must_be_overridden, avoid_implementing_value_types -class AppImplementationMock extends Mock implements AppImplementation {} - -void main() { - group('group name', () { - test('AccountFind', () { - final app1 = AppImplementationMock(); - final app2 = AppImplementationMock(); - - final apps = { - app1, - app2, - }; - - when(() => app1.id).thenReturn('app1'); - when(() => app2.id).thenReturn('app2'); - - expect(apps.tryFind(null), isNull); - expect(apps.tryFind('invalidID'), isNull); - expect(apps.tryFind(app2.id), equals(app2)); - - expect(() => apps.find('invalidID'), throwsA(isA())); - expect(apps.find(app2.id), equals(app2)); - }); - }); -} diff --git a/packages/neon/neon/test/client_implementation_test.dart b/packages/neon/neon/test/client_implementation_test.dart new file mode 100644 index 00000000..834ea7c2 --- /dev/null +++ b/packages/neon/neon/test/client_implementation_test.dart @@ -0,0 +1,30 @@ +import 'package:mocktail/mocktail.dart'; +import 'package:neon/src/models/client_implementation.dart'; +import 'package:test/test.dart'; + +// ignore: missing_override_of_must_be_overridden, avoid_implementing_value_types +class ClientImplementationMock extends Mock implements ClientImplementation {} + +void main() { + group('group name', () { + test('AccountFind', () { + final client1 = ClientImplementationMock(); + final client2 = ClientImplementationMock(); + + final apps = { + client1, + client2, + }; + + when(() => client1.id).thenReturn('app1'); + when(() => client2.id).thenReturn('app2'); + + expect(apps.tryFind(null), isNull); + expect(apps.tryFind('invalidID'), isNull); + expect(apps.tryFind(client2.id), equals(client2)); + + expect(() => apps.find('invalidID'), throwsA(isA())); + expect(apps.find(client2.id), equals(client2)); + }); + }); +} diff --git a/packages/neon/neon/test/options_collection_test.dart b/packages/neon/neon/test/options_collection_test.dart index 92e157c0..e9e0ce39 100644 --- a/packages/neon/neon/test/options_collection_test.dart +++ b/packages/neon/neon/test/options_collection_test.dart @@ -6,8 +6,8 @@ import 'package:test/test.dart'; // ignore: missing_override_of_must_be_overridden class OptionMock extends Mock implements ToggleOption {} -class Collection extends NextcloudAppOptions { - Collection(final List> options) : super(const AppStorage(StorageKeys.apps)) { +class Collection extends NextcloudClientOptions { + Collection(final List> options) : super(const ClientStorage(StorageKeys.clients)) { super.options = options; } } diff --git a/packages/neon/neon/test/settings_export_test.dart b/packages/neon/neon/test/settings_export_test.dart index 4afb95fb..8852e8ea 100644 --- a/packages/neon/neon/test/settings_export_test.dart +++ b/packages/neon/neon/test/settings_export_test.dart @@ -4,7 +4,7 @@ import 'dart:typed_data'; import 'package:mocktail/mocktail.dart'; import 'package:neon/src/blocs/accounts.dart'; import 'package:neon/src/models/account.dart'; -import 'package:neon/src/models/app_implementation.dart'; +import 'package:neon/src/models/client_implementation.dart'; import 'package:neon/src/settings/models/exportable.dart'; import 'package:neon/src/settings/models/options_collection.dart'; import 'package:neon/src/settings/utils/settings_export_helper.dart'; @@ -13,9 +13,9 @@ import 'package:rxdart/rxdart.dart'; import 'package:test/test.dart'; // ignore: missing_override_of_must_be_overridden, avoid_implementing_value_types -class FakeAppImplementation extends Mock implements AppImplementation {} +class FakeAppImplementation extends Mock implements ClientImplementation {} -class NextcloudAppOptionsMock extends Mock implements NextcloudAppOptions {} +class NextcloudAppOptionsMock extends Mock implements NextcloudClientOptions {} class AccountsBlocMock extends Mock implements AccountsBloc {} @@ -29,14 +29,14 @@ class ExporterMock extends Mock implements Exportable {} void main() { group('Exporter', () { test('AccountsBlocExporter', () { - var exporter = const AppImplementationsExporter([]); + var exporter = const ClientImplementationsExporter([]); var export = exporter.export(); expect(Map.fromEntries([export]), {'app': {}}); final fakeApp = FakeAppImplementation(); final fakeOptions = NextcloudAppOptionsMock(); - exporter = AppImplementationsExporter([fakeApp]); + exporter = ClientImplementationsExporter([fakeApp]); const appValue = MapEntry('appID', 'value'); const appExport = { diff --git a/packages/neon/neon/test/storage_test.dart b/packages/neon/neon/test/storage_test.dart index b50376d9..c2815ab8 100644 --- a/packages/neon/neon/test/storage_test.dart +++ b/packages/neon/neon/test/storage_test.dart @@ -17,12 +17,12 @@ void main() { group('AppStorage', () { test('formatKey', () async { - var appStorage = const AppStorage(StorageKeys.accounts); + var appStorage = const ClientStorage(StorageKeys.accounts); var key = appStorage.formatKey('test-key'); expect(key, 'accounts-test-key'); expect(appStorage.id, StorageKeys.accounts.value); - appStorage = const AppStorage(StorageKeys.accounts, 'test-suffix'); + appStorage = const ClientStorage(StorageKeys.accounts, 'test-suffix'); key = appStorage.formatKey('test-key'); expect(key, 'accounts-test-suffix-test-key'); expect(appStorage.id, 'test-suffix'); @@ -31,7 +31,7 @@ void main() { test('interface', () async { final sharedPreferences = SharedPreferencesMock(); NeonStorage.mock(sharedPreferences); - const appStorage = AppStorage(StorageKeys.accounts); + const appStorage = ClientStorage(StorageKeys.accounts); const key = 'key'; final formattedKey = appStorage.formatKey(key); diff --git a/packages/neon/neon_dashboard/lib/src/app.dart b/packages/neon/neon_dashboard/lib/src/app.dart index 69c4c1f6..6d4eff01 100644 --- a/packages/neon/neon_dashboard/lib/src/app.dart +++ b/packages/neon/neon_dashboard/lib/src/app.dart @@ -10,9 +10,9 @@ import 'package:nextcloud/core.dart' as core; import 'package:nextcloud/nextcloud.dart'; /// Implementation of the server `dashboard` app. -class DashboardApp extends AppImplementation { - /// Creates a new Dashboard app implementation instance. - DashboardApp(); +class DashboardClient extends ClientImplementation { + /// Creates a new Dashboard client implementation instance. + DashboardClient(); @override final String id = AppIDs.dashboard; @@ -24,7 +24,7 @@ class DashboardApp extends AppImplementation supportedLocales = DashboardLocalizations.supportedLocales; @override - late final DashboardAppSpecificOptions options = DashboardAppSpecificOptions(storage); + late final DashboardClientSpecificOptions options = DashboardClientSpecificOptions(storage); @override DashboardBloc buildBloc(final Account account) => DashboardBloc(account); @@ -33,7 +33,7 @@ class DashboardApp extends AppImplementation( - path: '$appsBaseRoutePrefix${AppIDs.dashboard}', +/// Route for the dashboard client. +@TypedGoRoute( + path: '$clientsBaseRoutePrefix${AppIDs.dashboard}', name: AppIDs.dashboard, ) @immutable -class DashboardAppRoute extends NeonBaseAppRoute { - /// Creates a new dashboard app route. - const DashboardAppRoute(); +class DashboardClientRoute extends NeonBaseClientRoute { + /// Creates a new dashboard client route. + const DashboardClientRoute(); @override Widget build(final BuildContext context, final GoRouterState state) => const DashboardMainPage(); diff --git a/packages/neon/neon_dashboard/lib/src/routes.g.dart b/packages/neon/neon_dashboard/lib/src/routes.g.dart index 8dae6752..74cded98 100644 --- a/packages/neon/neon_dashboard/lib/src/routes.g.dart +++ b/packages/neon/neon_dashboard/lib/src/routes.g.dart @@ -7,17 +7,17 @@ part of 'routes.dart'; // ************************************************************************** List get $appRoutes => [ - $dashboardAppRoute, + $dashboardClientRoute, ]; -RouteBase get $dashboardAppRoute => GoRouteData.$route( +RouteBase get $dashboardClientRoute => GoRouteData.$route( path: '/apps/dashboard', name: 'dashboard', - factory: $DashboardAppRouteExtension._fromState, + factory: $DashboardClientRouteExtension._fromState, ); -extension $DashboardAppRouteExtension on DashboardAppRoute { - static DashboardAppRoute _fromState(GoRouterState state) => const DashboardAppRoute(); +extension $DashboardClientRouteExtension on DashboardClientRoute { + static DashboardClientRoute _fromState(GoRouterState state) => const DashboardClientRoute(); String get location => GoRouteData.$location( '/apps/dashboard', diff --git a/packages/neon/neon_files/lib/blocs/browser.dart b/packages/neon/neon_files/lib/blocs/browser.dart index 80c97062..9d8237ab 100644 --- a/packages/neon/neon_files/lib/blocs/browser.dart +++ b/packages/neon/neon_files/lib/blocs/browser.dart @@ -25,7 +25,7 @@ class FilesBrowserBloc extends InteractiveBloc implements FilesBrowserBlocEvents unawaited(refresh()); } - final FilesAppSpecificOptions options; + final FilesClientSpecificOptions options; final Account account; @override diff --git a/packages/neon/neon_files/lib/blocs/files.dart b/packages/neon/neon_files/lib/blocs/files.dart index 2711c40b..9b174f91 100644 --- a/packages/neon/neon_files/lib/blocs/files.dart +++ b/packages/neon/neon_files/lib/blocs/files.dart @@ -35,7 +35,7 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta options.downloadQueueParallelism.addListener(_downloadParallelismListener); } - final FilesAppSpecificOptions options; + final FilesClientSpecificOptions options; final Account account; late final browser = getNewFilesBrowserBloc(); diff --git a/packages/neon/neon_files/lib/neon_files.dart b/packages/neon/neon_files/lib/neon_files.dart index 739242f2..f8d65e79 100644 --- a/packages/neon/neon_files/lib/neon_files.dart +++ b/packages/neon/neon_files/lib/neon_files.dart @@ -46,8 +46,8 @@ part 'widgets/browser_view.dart'; part 'widgets/file_preview.dart'; part 'widgets/navigator.dart'; -class FilesApp extends AppImplementation { - FilesApp(); +class FilesClient extends ClientImplementation { + FilesClient(); @override final String id = AppIDs.files; @@ -59,7 +59,7 @@ class FilesApp extends AppImplementation { final List supportedLocales = FilesLocalizations.supportedLocales; @override - late final FilesAppSpecificOptions options = FilesAppSpecificOptions(storage); + late final FilesClientSpecificOptions options = FilesClientSpecificOptions(storage); @override FilesBloc buildBloc(final Account account) => FilesBloc( @@ -71,7 +71,7 @@ class FilesApp extends AppImplementation { final Widget page = const FilesMainPage(); @override - final RouteBase route = $filesAppRoute; + final RouteBase route = $filesClientRoute; @override (bool? supported, String? minimumVersion) isSupported( diff --git a/packages/neon/neon_files/lib/options.dart b/packages/neon/neon_files/lib/options.dart index 2e8d5ab0..dabee7de 100644 --- a/packages/neon/neon_files/lib/options.dart +++ b/packages/neon/neon_files/lib/options.dart @@ -1,7 +1,7 @@ part of 'neon_files.dart'; -class FilesAppSpecificOptions extends NextcloudAppOptions { - FilesAppSpecificOptions(super.storage) { +class FilesClientSpecificOptions extends NextcloudClientOptions { + FilesClientSpecificOptions(super.storage) { super.categories = [ generalCategory, ]; diff --git a/packages/neon/neon_files/lib/routes.dart b/packages/neon/neon_files/lib/routes.dart index 5bec7a54..550666d5 100644 --- a/packages/neon/neon_files/lib/routes.dart +++ b/packages/neon/neon_files/lib/routes.dart @@ -6,13 +6,13 @@ import 'package:nextcloud/nextcloud.dart'; part 'routes.g.dart'; -@TypedGoRoute( - path: '$appsBaseRoutePrefix${AppIDs.files}', +@TypedGoRoute( + path: '$clientsBaseRoutePrefix${AppIDs.files}', name: AppIDs.files, ) @immutable -class FilesAppRoute extends NeonBaseAppRoute { - const FilesAppRoute(); +class FilesClientRoute extends NeonBaseClientRoute { + const FilesClientRoute(); @override Widget build(final BuildContext context, final GoRouterState state) => const FilesMainPage(); diff --git a/packages/neon/neon_files/lib/routes.g.dart b/packages/neon/neon_files/lib/routes.g.dart index d8943daa..f7529a5a 100644 --- a/packages/neon/neon_files/lib/routes.g.dart +++ b/packages/neon/neon_files/lib/routes.g.dart @@ -7,17 +7,17 @@ part of 'routes.dart'; // ************************************************************************** List get $appRoutes => [ - $filesAppRoute, + $filesClientRoute, ]; -RouteBase get $filesAppRoute => GoRouteData.$route( +RouteBase get $filesClientRoute => GoRouteData.$route( path: '/apps/files', name: 'files', - factory: $FilesAppRouteExtension._fromState, + factory: $FilesClientRouteExtension._fromState, ); -extension $FilesAppRouteExtension on FilesAppRoute { - static FilesAppRoute _fromState(GoRouterState state) => const FilesAppRoute(); +extension $FilesClientRouteExtension on FilesClientRoute { + static FilesClientRoute _fromState(GoRouterState state) => const FilesClientRoute(); String get location => GoRouteData.$location( '/apps/files', diff --git a/packages/neon/neon_news/lib/blocs/articles.dart b/packages/neon/neon_news/lib/blocs/articles.dart index 46a09711..9b7d9412 100644 --- a/packages/neon/neon_news/lib/blocs/articles.dart +++ b/packages/neon/neon_news/lib/blocs/articles.dart @@ -56,7 +56,7 @@ class NewsArticlesBloc extends InteractiveBloc implements NewsArticlesBlocEvents } final NewsBloc _newsBloc; - final NewsAppSpecificOptions options; + final NewsClientSpecificOptions options; final Account account; final int? id; final ListType? listType; diff --git a/packages/neon/neon_news/lib/blocs/news.dart b/packages/neon/neon_news/lib/blocs/news.dart index 8164977b..915fb2bc 100644 --- a/packages/neon/neon_news/lib/blocs/news.dart +++ b/packages/neon/neon_news/lib/blocs/news.dart @@ -47,7 +47,7 @@ class NewsBloc extends InteractiveBloc implements NewsBlocEvents, NewsBlocStates @override NewsBloc get _newsBloc => this; @override - final NewsAppSpecificOptions options; + final NewsClientSpecificOptions options; @override @override final Account account; diff --git a/packages/neon/neon_news/lib/neon_news.dart b/packages/neon/neon_news/lib/neon_news.dart index 53948723..ff3e1870 100644 --- a/packages/neon/neon_news/lib/neon_news.dart +++ b/packages/neon/neon_news/lib/neon_news.dart @@ -54,8 +54,8 @@ part 'widgets/folder_select.dart'; part 'widgets/folder_view.dart'; part 'widgets/folders_view.dart'; -class NewsApp extends AppImplementation { - NewsApp(); +class NewsClient extends ClientImplementation { + NewsClient(); @override final String id = AppIDs.news; @@ -67,7 +67,7 @@ class NewsApp extends AppImplementation { final List supportedLocales = NewsLocalizations.supportedLocales; @override - late final NewsAppSpecificOptions options = NewsAppSpecificOptions(storage); + late final NewsClientSpecificOptions options = NewsClientSpecificOptions(storage); @override NewsBloc buildBloc(final Account account) => NewsBloc( @@ -79,7 +79,7 @@ class NewsApp extends AppImplementation { final Widget page = const NewsMainPage(); @override - final RouteBase route = $newsAppRoute; + final RouteBase route = $newsClientRoute; @override BehaviorSubject getUnreadCounter(final NewsBloc bloc) => bloc.unreadCounter; diff --git a/packages/neon/neon_news/lib/options.dart b/packages/neon/neon_news/lib/options.dart index 08c5b70f..3435f671 100644 --- a/packages/neon/neon_news/lib/options.dart +++ b/packages/neon/neon_news/lib/options.dart @@ -1,7 +1,7 @@ part of 'neon_news.dart'; -class NewsAppSpecificOptions extends NextcloudAppOptions { - NewsAppSpecificOptions(super.storage) { +class NewsClientSpecificOptions extends NextcloudClientOptions { + NewsClientSpecificOptions(super.storage) { super.categories = [ generalCategory, articlesCategory, diff --git a/packages/neon/neon_news/lib/routes.dart b/packages/neon/neon_news/lib/routes.dart index 1d1e5528..5f869d0f 100644 --- a/packages/neon/neon_news/lib/routes.dart +++ b/packages/neon/neon_news/lib/routes.dart @@ -6,13 +6,13 @@ import 'package:nextcloud/nextcloud.dart'; part 'routes.g.dart'; -@TypedGoRoute( - path: '$appsBaseRoutePrefix${AppIDs.news}', +@TypedGoRoute( + path: '$clientsBaseRoutePrefix${AppIDs.news}', name: AppIDs.news, ) @immutable -class NewsAppRoute extends NeonBaseAppRoute { - const NewsAppRoute(); +class NewsClientRoute extends NeonBaseClientRoute { + const NewsClientRoute(); @override Widget build(final BuildContext context, final GoRouterState state) => const NewsMainPage(); diff --git a/packages/neon/neon_news/lib/routes.g.dart b/packages/neon/neon_news/lib/routes.g.dart index db34106d..b13e4aa8 100644 --- a/packages/neon/neon_news/lib/routes.g.dart +++ b/packages/neon/neon_news/lib/routes.g.dart @@ -7,17 +7,17 @@ part of 'routes.dart'; // ************************************************************************** List get $appRoutes => [ - $newsAppRoute, + $newsClientRoute, ]; -RouteBase get $newsAppRoute => GoRouteData.$route( +RouteBase get $newsClientRoute => GoRouteData.$route( path: '/apps/news', name: 'news', - factory: $NewsAppRouteExtension._fromState, + factory: $NewsClientRouteExtension._fromState, ); -extension $NewsAppRouteExtension on NewsAppRoute { - static NewsAppRoute _fromState(GoRouterState state) => const NewsAppRoute(); +extension $NewsClientRouteExtension on NewsClientRoute { + static NewsClientRoute _fromState(GoRouterState state) => const NewsClientRoute(); String get location => GoRouteData.$location( '/apps/news', diff --git a/packages/neon/neon_notes/lib/blocs/note.dart b/packages/neon/neon_notes/lib/blocs/note.dart index bd85bded..b92ea995 100644 --- a/packages/neon/neon_notes/lib/blocs/note.dart +++ b/packages/neon/neon_notes/lib/blocs/note.dart @@ -43,7 +43,7 @@ class NotesNoteBloc extends InteractiveBloc implements NotesNoteBlocEvents, Note }); } - late final NotesAppSpecificOptions options = _notesBloc.options; + late final NotesClientSpecificOptions options = _notesBloc.options; final NotesBloc _notesBloc; final _updateQueue = Queue(); diff --git a/packages/neon/neon_notes/lib/blocs/notes.dart b/packages/neon/neon_notes/lib/blocs/notes.dart index 929fd9f9..38cf082c 100644 --- a/packages/neon/neon_notes/lib/blocs/notes.dart +++ b/packages/neon/neon_notes/lib/blocs/notes.dart @@ -30,7 +30,7 @@ class NotesBloc extends InteractiveBloc implements NotesBlocEvents, NotesBlocSta unawaited(refresh()); } - final NotesAppSpecificOptions options; + final NotesClientSpecificOptions options; final Account account; @override diff --git a/packages/neon/neon_notes/lib/neon_notes.dart b/packages/neon/neon_notes/lib/neon_notes.dart index 620174a3..28b6d648 100644 --- a/packages/neon/neon_notes/lib/neon_notes.dart +++ b/packages/neon/neon_notes/lib/neon_notes.dart @@ -43,8 +43,8 @@ part 'widgets/category_select.dart'; part 'widgets/notes_floating_action_button.dart'; part 'widgets/notes_view.dart'; -class NotesApp extends AppImplementation { - NotesApp(); +class NotesClient extends ClientImplementation { + NotesClient(); @override final String id = AppIDs.notes; @@ -56,7 +56,7 @@ class NotesApp extends AppImplementation { final LocalizationsDelegate localizationsDelegate = NotesLocalizations.delegate; @override - late final NotesAppSpecificOptions options = NotesAppSpecificOptions(storage); + late final NotesClientSpecificOptions options = NotesClientSpecificOptions(storage); @override NotesBloc buildBloc(final Account account) => NotesBloc( @@ -68,7 +68,7 @@ class NotesApp extends AppImplementation { final Widget page = const NotesMainPage(); @override - final RouteBase route = $notesAppRoute; + final RouteBase route = $notesClientRoute; @override (bool? supported, String? minimumVersion) isSupported( diff --git a/packages/neon/neon_notes/lib/options.dart b/packages/neon/neon_notes/lib/options.dart index e8156ebb..f9f42202 100644 --- a/packages/neon/neon_notes/lib/options.dart +++ b/packages/neon/neon_notes/lib/options.dart @@ -1,7 +1,7 @@ part of 'neon_notes.dart'; -class NotesAppSpecificOptions extends NextcloudAppOptions { - NotesAppSpecificOptions(super.storage) { +class NotesClientSpecificOptions extends NextcloudClientOptions { + NotesClientSpecificOptions(super.storage) { super.categories = [ generalCategory, notesCategory, diff --git a/packages/neon/neon_notes/lib/routes.dart b/packages/neon/neon_notes/lib/routes.dart index e8a187ae..7f3844b9 100644 --- a/packages/neon/neon_notes/lib/routes.dart +++ b/packages/neon/neon_notes/lib/routes.dart @@ -6,13 +6,13 @@ import 'package:nextcloud/nextcloud.dart'; part 'routes.g.dart'; -@TypedGoRoute( - path: '$appsBaseRoutePrefix${AppIDs.notes}', +@TypedGoRoute( + path: '$clientsBaseRoutePrefix${AppIDs.notes}', name: AppIDs.notes, ) @immutable -class NotesAppRoute extends NeonBaseAppRoute { - const NotesAppRoute(); +class NotesClientRoute extends NeonBaseClientRoute { + const NotesClientRoute(); @override Widget build(final BuildContext context, final GoRouterState state) => const NotesMainPage(); diff --git a/packages/neon/neon_notes/lib/routes.g.dart b/packages/neon/neon_notes/lib/routes.g.dart index cb68c111..aec4722d 100644 --- a/packages/neon/neon_notes/lib/routes.g.dart +++ b/packages/neon/neon_notes/lib/routes.g.dart @@ -7,17 +7,17 @@ part of 'routes.dart'; // ************************************************************************** List get $appRoutes => [ - $notesAppRoute, + $notesClientRoute, ]; -RouteBase get $notesAppRoute => GoRouteData.$route( +RouteBase get $notesClientRoute => GoRouteData.$route( path: '/apps/notes', name: 'notes', - factory: $NotesAppRouteExtension._fromState, + factory: $NotesClientRouteExtension._fromState, ); -extension $NotesAppRouteExtension on NotesAppRoute { - static NotesAppRoute _fromState(GoRouterState state) => const NotesAppRoute(); +extension $NotesClientRouteExtension on NotesClientRoute { + static NotesClientRoute _fromState(GoRouterState state) => const NotesClientRoute(); String get location => GoRouteData.$location( '/apps/notes', diff --git a/packages/neon/neon_notifications/lib/blocs/notifications.dart b/packages/neon/neon_notifications/lib/blocs/notifications.dart index 45cd5b96..4f2f4be7 100644 --- a/packages/neon/neon_notifications/lib/blocs/notifications.dart +++ b/packages/neon/neon_notifications/lib/blocs/notifications.dart @@ -29,7 +29,7 @@ class NotificationsBloc extends InteractiveBloc } @override - final NotificationsAppSpecificOptions options; + final NotificationsClientSpecificOptions options; final Account _account; late final NeonTimer _timer; diff --git a/packages/neon/neon_notifications/lib/neon_notifications.dart b/packages/neon/neon_notifications/lib/neon_notifications.dart index 82c149d0..b75874b0 100644 --- a/packages/neon/neon_notifications/lib/neon_notifications.dart +++ b/packages/neon/neon_notifications/lib/neon_notifications.dart @@ -22,11 +22,11 @@ part 'blocs/notifications.dart'; part 'options.dart'; part 'pages/main.dart'; -class NotificationsApp extends AppImplementation +class NotificationsClient extends ClientImplementation implements // ignore: avoid_implementing_value_types - NotificationsAppInterface { - NotificationsApp(); + NotificationsClientInterface { + NotificationsClient(); @override final String id = AppIDs.notifications; @@ -38,7 +38,7 @@ class NotificationsApp extends AppImplementation supportedLocales = NotificationsLocalizations.supportedLocales; @override - late final NotificationsAppSpecificOptions options = NotificationsAppSpecificOptions(storage); + late final NotificationsClientSpecificOptions options = NotificationsClientSpecificOptions(storage); @override NotificationsBloc buildBloc(final Account account) => NotificationsBloc( @@ -50,7 +50,7 @@ class NotificationsApp extends AppImplementation getUnreadCounter(final NotificationsBloc bloc) => bloc.unreadCounter; diff --git a/packages/neon/neon_notifications/lib/options.dart b/packages/neon/neon_notifications/lib/options.dart index 101572d4..26e74db9 100644 --- a/packages/neon/neon_notifications/lib/options.dart +++ b/packages/neon/neon_notifications/lib/options.dart @@ -1,7 +1,7 @@ part of 'neon_notifications.dart'; -class NotificationsAppSpecificOptions extends NextcloudAppOptions implements NotificationsOptionsInterface { - NotificationsAppSpecificOptions(super.storage) { +class NotificationsClientSpecificOptions extends NextcloudClientOptions implements NotificationsOptionsInterface { + NotificationsClientSpecificOptions(super.storage) { super.categories = []; super.options = []; } diff --git a/packages/neon/neon_notifications/lib/pages/main.dart b/packages/neon/neon_notifications/lib/pages/main.dart index 7783f2d3..fc47c2a2 100644 --- a/packages/neon/neon_notifications/lib/pages/main.dart +++ b/packages/neon/neon_notifications/lib/pages/main.dart @@ -54,7 +54,7 @@ class _NotificationsMainPageState extends State { final BuildContext context, final notifications.Notification notification, ) { - final app = NeonProvider.of>(context).tryFind(notification.app); + final client = NeonProvider.of>(context).tryFind(notification.app); return ListTile( title: Text(notification.subject), @@ -75,8 +75,8 @@ class _NotificationsMainPageState extends State { ), ], ), - leading: app != null - ? app.buildIcon( + leading: client != null + ? client.buildIcon( size: largeIconSize, ) : SizedBox.fromSize( @@ -91,10 +91,10 @@ class _NotificationsMainPageState extends State { if (notification.app == AppIDs.notifications) { return; } - if (app != null) { + if (client != null) { // TODO: use go_router once implemented final accountsBloc = NeonProvider.of(context); - await accountsBloc.activeAppsBloc.setActiveApp(app.id); + await accountsBloc.activeClientsBloc.setActiveClient(client.id); } else { final colorScheme = Theme.of(context).colorScheme; diff --git a/packages/neon/neon_notifications/lib/routes.dart b/packages/neon/neon_notifications/lib/routes.dart index 4e80ccce..9fa55b4a 100644 --- a/packages/neon/neon_notifications/lib/routes.dart +++ b/packages/neon/neon_notifications/lib/routes.dart @@ -6,13 +6,13 @@ import 'package:nextcloud/nextcloud.dart'; part 'routes.g.dart'; -@TypedGoRoute( - path: '$appsBaseRoutePrefix${AppIDs.notifications}', +@TypedGoRoute( + path: '$clientsBaseRoutePrefix${AppIDs.notifications}', name: AppIDs.notifications, ) @immutable -class NotificationsAppRoute extends NeonBaseAppRoute { - const NotificationsAppRoute(); +class NotificationsClientRoute extends NeonBaseClientRoute { + const NotificationsClientRoute(); @override Widget build(final BuildContext context, final GoRouterState state) => const NotificationsMainPage(); diff --git a/packages/neon/neon_notifications/lib/routes.g.dart b/packages/neon/neon_notifications/lib/routes.g.dart index 46db16cb..66ab49bb 100644 --- a/packages/neon/neon_notifications/lib/routes.g.dart +++ b/packages/neon/neon_notifications/lib/routes.g.dart @@ -7,17 +7,17 @@ part of 'routes.dart'; // ************************************************************************** List get $appRoutes => [ - $notificationsAppRoute, + $notificationsClientRoute, ]; -RouteBase get $notificationsAppRoute => GoRouteData.$route( +RouteBase get $notificationsClientRoute => GoRouteData.$route( path: '/apps/notifications', name: 'notifications', - factory: $NotificationsAppRouteExtension._fromState, + factory: $NotificationsClientRouteExtension._fromState, ); -extension $NotificationsAppRouteExtension on NotificationsAppRoute { - static NotificationsAppRoute _fromState(GoRouterState state) => const NotificationsAppRoute(); +extension $NotificationsClientRouteExtension on NotificationsClientRoute { + static NotificationsClientRoute _fromState(GoRouterState state) => const NotificationsClientRoute(); String get location => GoRouteData.$location( '/apps/notifications',