diff --git a/packages/app/lib/apps.dart b/packages/app/lib/apps.dart index bf516df9..b93b4baa 100644 --- a/packages/app/lib/apps.dart +++ b/packages/app/lib/apps.dart @@ -1,6 +1,4 @@ import 'package:neon/models.dart'; -import 'package:neon/platform.dart'; -import 'package:neon/utils.dart'; import 'package:neon_files/neon_files.dart'; import 'package:neon_news/neon_news.dart'; import 'package:neon_notes/neon_notes.dart'; @@ -9,12 +7,10 @@ import 'package:shared_preferences/shared_preferences.dart'; List getAppImplementations( final SharedPreferences sharedPreferences, - final RequestManager requestManager, - final NeonPlatform platform, ) => [ - FilesApp(sharedPreferences, requestManager, platform), - NewsApp(sharedPreferences, requestManager, platform), - NotesApp(sharedPreferences, requestManager, platform), - NotificationsApp(sharedPreferences, requestManager, platform), + FilesApp(sharedPreferences), + NewsApp(sharedPreferences), + NotesApp(sharedPreferences), + NotificationsApp(sharedPreferences), ]; diff --git a/packages/neon/neon/lib/neon.dart b/packages/neon/neon/lib/neon.dart index 9d01e322..5973bfea 100644 --- a/packages/neon/neon/lib/neon.dart +++ b/packages/neon/neon/lib/neon.dart @@ -9,7 +9,6 @@ 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/platform/platform.dart'; import 'package:neon/src/theme/neon.dart'; import 'package:neon/src/utils/global_options.dart'; import 'package:neon/src/utils/request_manager.dart'; @@ -19,8 +18,7 @@ import 'package:provider/provider.dart'; import 'package:shared_preferences/shared_preferences.dart'; Future runNeon({ - required final Iterable Function(SharedPreferences, RequestManager, NeonPlatform) - getAppImplementations, + required final Iterable Function(SharedPreferences) getAppImplementations, required final NeonTheme theme, @visibleForTesting final WidgetsBinding? bindingOverride, @visibleForTesting final Account? account, @@ -32,11 +30,8 @@ Future runNeon({ final sharedPreferences = await SharedPreferences.getInstance(); - final platform = await getNeonPlatform(); - final cache = Cache(platform); - await cache.init(); - final requestManager = RequestManager(cache); - final allAppImplementations = getAppImplementations(sharedPreferences, requestManager, platform); + await RequestManager.instance.initCache(); + final allAppImplementations = getAppImplementations(sharedPreferences); final packageInfo = await PackageInfo.fromPlatform(); buildUserAgent(packageInfo); @@ -47,8 +42,6 @@ Future runNeon({ ); final accountsBloc = AccountsBloc( - requestManager, - platform, sharedPreferences, globalOptions, allAppImplementations, @@ -62,7 +55,6 @@ Future runNeon({ accountsBloc, sharedPreferences, globalOptions, - platform, ); final firstLaunchBloc = FirstLaunchBloc( sharedPreferences, @@ -80,15 +72,9 @@ Future runNeon({ Provider( create: (final _) => sharedPreferences, ), - Provider( - create: (final _) => platform, - ), Provider( create: (final _) => globalOptions, ), - Provider( - create: (final _) => requestManager, - ), Provider( create: (final _) => accountsBloc, ), diff --git a/packages/neon/neon/lib/src/app.dart b/packages/neon/neon/lib/src/app.dart index 5673d950..4018c94d 100644 --- a/packages/neon/neon/lib/src/app.dart +++ b/packages/neon/neon/lib/src/app.dart @@ -44,7 +44,6 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra final _appRegex = RegExp(r'^app_([a-z]+)$', multiLine: true); final _navigatorKey = GlobalKey(); late final Iterable _appImplementations; - late final NeonPlatform _platform; late final GlobalOptions _globalOptions; late final AccountsBloc _accountsBloc; late final _routerDelegate = AppRouter( @@ -59,15 +58,14 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra super.initState(); _appImplementations = Provider.of>(context, listen: false); - _platform = Provider.of(context, listen: false); _globalOptions = Provider.of(context, listen: false); _accountsBloc = Provider.of(context, listen: false); WidgetsBinding.instance.addObserver(this); - if (_platform.canUseSystemTray) { + if (NeonPlatform.instance.canUseSystemTray) { tray.trayManager.addListener(this); } - if (_platform.canUseWindowManager) { + if (NeonPlatform.instance.canUseWindowManager) { windowManager.addListener(this); } @@ -77,7 +75,7 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra if (!mounted) { return; } - if (_platform.canUseQuickActions) { + if (NeonPlatform.instance.canUseQuickActions) { const quickActions = QuickActions(); await quickActions.setShortcutItems( _appImplementations @@ -93,7 +91,7 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra await quickActions.initialize(_handleShortcut); } - if (_platform.canUseWindowManager) { + if (NeonPlatform.instance.canUseWindowManager) { await windowManager.setPreventClose(true); if (_globalOptions.startupMinimized.value) { @@ -101,7 +99,7 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra } } - if (_platform.canUseSystemTray) { + if (NeonPlatform.instance.canUseSystemTray) { _globalOptions.systemTrayEnabled.addListener(() async { if (_globalOptions.systemTrayEnabled.value) { // TODO: This works on Linux, but maybe not on macOS or Windows @@ -136,7 +134,7 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra }); } - if (_platform.canUsePushNotifications) { + if (NeonPlatform.instance.canUsePushNotifications) { final localNotificationsPlugin = await PushUtils.initLocalNotifications(); Global.onPushNotificationReceived = (final accountID) async { final account = _accountsBloc.accounts.value.tryFind(accountID); @@ -213,7 +211,7 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra Future _handleShortcut(final String shortcutType) async { if (shortcutType == 'show_hide') { - if (_platform.canUseWindowManager) { + if (NeonPlatform.instance.canUseWindowManager) { if (await windowManager.isVisible()) { await _saveAndMinimizeWindow(); } else { @@ -249,7 +247,7 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra } Future _showAndRestoreWindow() async { - if (!_platform.canUseWindowManager) { + if (!NeonPlatform.instance.canUseWindowManager) { return; } @@ -264,10 +262,10 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra @override void dispose() { WidgetsBinding.instance.removeObserver(this); - if (_platform.canUseSystemTray) { + if (NeonPlatform.instance.canUseSystemTray) { tray.trayManager.removeListener(this); } - if (_platform.canUseWindowManager) { + if (NeonPlatform.instance.canUseWindowManager) { windowManager.removeListener(this); } diff --git a/packages/neon/neon/lib/src/bloc/bloc.dart b/packages/neon/neon/lib/src/bloc/bloc.dart index f4ba59b8..d1e9b2bf 100644 --- a/packages/neon/neon/lib/src/bloc/bloc.dart +++ b/packages/neon/neon/lib/src/bloc/bloc.dart @@ -32,7 +32,7 @@ abstract class InteractiveBloc extends Bloc { if (disableTimeout) { await call(); } else { - await RequestManager.timeout(call); + await RequestManager.instance.timeout(call); } await (refresh ?? this.refresh)(); diff --git a/packages/neon/neon/lib/src/blocs/accounts.dart b/packages/neon/neon/lib/src/blocs/accounts.dart index f760beeb..8068d10a 100644 --- a/packages/neon/neon/lib/src/blocs/accounts.dart +++ b/packages/neon/neon/lib/src/blocs/accounts.dart @@ -10,11 +10,9 @@ 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/app_implementation.dart'; -import 'package:neon/src/platform/platform.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'; -import 'package:neon/src/utils/request_manager.dart'; import 'package:rxdart/rxdart.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -58,8 +56,6 @@ abstract interface class AccountsBlocStates { class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocStates { AccountsBloc( - this._requestManager, - this._platform, this._sharedPreferences, this._globalOptions, this._allAppImplementations, @@ -100,8 +96,6 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState } } - final RequestManager _requestManager; - final NeonPlatform _platform; late final AppStorage _storage = AppStorage('accounts', _sharedPreferences); final SharedPreferences _sharedPreferences; final GlobalOptions _globalOptions; @@ -231,19 +225,12 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState /// The appsBloc for the specified [account]. /// /// Use [activeAppsBloc] to get them for the [activeAccount]. - AppsBloc getAppsBlocFor(final Account account) { - if (_appsBlocs[account.id] != null) { - return _appsBlocs[account.id]!; - } - - return _appsBlocs[account.id] = AppsBloc( - _requestManager, - getCapabilitiesBlocFor(account), - this, - account, - _allAppImplementations, - ); - } + AppsBloc getAppsBlocFor(final Account account) => _appsBlocs[account.id] ??= AppsBloc( + getCapabilitiesBlocFor(account), + this, + account, + _allAppImplementations, + ); /// The capabilitiesBloc for the [activeAccount]. /// @@ -253,16 +240,8 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState /// The capabilitiesBloc for the specified [account]. /// /// Use [activeCapabilitiesBloc] to get them for the [activeAccount]. - CapabilitiesBloc getCapabilitiesBlocFor(final Account account) { - if (_capabilitiesBlocs[account.id] != null) { - return _capabilitiesBlocs[account.id]!; - } - - return _capabilitiesBlocs[account.id] = CapabilitiesBloc( - _requestManager, - account, - ); - } + CapabilitiesBloc getCapabilitiesBlocFor(final Account account) => + _capabilitiesBlocs[account.id] ??= CapabilitiesBloc(account); /// The userDetailsBloc for the [activeAccount]. /// @@ -272,16 +251,8 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState /// The userDetailsBloc for the specified [account]. /// /// Use [activeUerDetailsBloc] to get them for the [activeAccount]. - UserDetailsBloc getUserDetailsBlocFor(final Account account) { - if (_userDetailsBlocs[account.id] != null) { - return _userDetailsBlocs[account.id]!; - } - - return _userDetailsBlocs[account.id] = UserDetailsBloc( - _requestManager, - account, - ); - } + UserDetailsBloc getUserDetailsBlocFor(final Account account) => + _userDetailsBlocs[account.id] ??= UserDetailsBloc(account); /// The userStatusBloc for the [activeAccount]. /// @@ -291,16 +262,8 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState /// The userStatusBloc for the specified [account]. /// /// Use [activeUserStatusesBloc] to get them for the [activeAccount]. - UserStatusesBloc getUserStatusesBlocFor(final Account account) { - if (_userStatusesBlocs[account.id] != null) { - return _userStatusesBlocs[account.id]!; - } - - return _userStatusesBlocs[account.id] = UserStatusesBloc( - _platform, - account, - ); - } + UserStatusesBloc getUserStatusesBlocFor(final Account account) => + _userStatusesBlocs[account.id] ??= UserStatusesBloc(account); /// The UnifiedSearchBloc for the [activeAccount]. /// @@ -310,16 +273,11 @@ class AccountsBloc extends Bloc implements AccountsBlocEvents, AccountsBlocState /// The UnifiedSearchBloc for the specified [account]. /// /// Use [activeUnifiedSearchBloc] to get them for the [activeAccount]. - UnifiedSearchBloc getUnifiedSearchBlocFor(final Account account) { - if (_unifiedSearchBlocs[account.id] != null) { - return _unifiedSearchBlocs[account.id]!; - } - - return _unifiedSearchBlocs[account.id] = UnifiedSearchBloc( - getAppsBlocFor(account), - account, - ); - } + UnifiedSearchBloc getUnifiedSearchBlocFor(final Account account) => + _unifiedSearchBlocs[account.id] ??= UnifiedSearchBloc( + getAppsBlocFor(account), + account, + ); } /// Get a list of logged in accounts from [storage]. diff --git a/packages/neon/neon/lib/src/blocs/apps.dart b/packages/neon/neon/lib/src/blocs/apps.dart index a04ae662..d3727017 100644 --- a/packages/neon/neon/lib/src/blocs/apps.dart +++ b/packages/neon/neon/lib/src/blocs/apps.dart @@ -41,7 +41,6 @@ abstract class AppsBlocStates { @internal class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates { AppsBloc( - this._requestManager, this._capabilitiesBloc, this._accountsBloc, this._account, @@ -151,7 +150,6 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates Iterable _filteredAppImplementations(final Iterable appIds) => _allAppImplementations.where((final a) => appIds.contains(a.id)); - final RequestManager _requestManager; final CapabilitiesBloc _capabilitiesBloc; final AccountsBloc _accountsBloc; final Account _account; @@ -195,7 +193,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates @override Future refresh() async { - await _requestManager + await RequestManager.instance .wrapNextcloud, CoreNavigationGetAppsNavigationResponse200ApplicationJson>( _account.id, 'apps-apps', diff --git a/packages/neon/neon/lib/src/blocs/capabilities.dart b/packages/neon/neon/lib/src/blocs/capabilities.dart index ce1a1a35..9bd04931 100644 --- a/packages/neon/neon/lib/src/blocs/capabilities.dart +++ b/packages/neon/neon/lib/src/blocs/capabilities.dart @@ -17,13 +17,11 @@ abstract class CapabilitiesBlocStates { @internal class CapabilitiesBloc extends InteractiveBloc implements CapabilitiesBlocEvents, CapabilitiesBlocStates { CapabilitiesBloc( - this._requestManager, this._account, ) { unawaited(refresh()); } - final RequestManager _requestManager; final Account _account; @override @@ -38,7 +36,7 @@ class CapabilitiesBloc extends InteractiveBloc implements CapabilitiesBlocEvents @override Future refresh() async { - await _requestManager.wrapNextcloud( _account.id, 'capabilities', diff --git a/packages/neon/neon/lib/src/blocs/push_notifications.dart b/packages/neon/neon/lib/src/blocs/push_notifications.dart index 7b5e0134..d280b379 100644 --- a/packages/neon/neon/lib/src/blocs/push_notifications.dart +++ b/packages/neon/neon/lib/src/blocs/push_notifications.dart @@ -28,9 +28,8 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents, this._accountsBloc, this._sharedPreferences, this._globalOptions, - this._platform, ) { - if (_platform.canUsePushNotifications) { + if (NeonPlatform.instance.canUsePushNotifications) { unawaited(UnifiedPush.getDistributors().then(_globalOptions.updateDistributors)); _globalOptions.pushNotificationsEnabled.addListener(_pushNotificationsEnabledListener); @@ -40,7 +39,6 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents, } final AccountsBloc _accountsBloc; - final NeonPlatform _platform; final SharedPreferences _sharedPreferences; late final _storage = AppStorage(AppIDs.notifications, _sharedPreferences); final GlobalOptions _globalOptions; diff --git a/packages/neon/neon/lib/src/blocs/user_details.dart b/packages/neon/neon/lib/src/blocs/user_details.dart index f1c7282d..1111bfeb 100644 --- a/packages/neon/neon/lib/src/blocs/user_details.dart +++ b/packages/neon/neon/lib/src/blocs/user_details.dart @@ -17,13 +17,11 @@ abstract class UserDetailsBlocStates { @internal class UserDetailsBloc extends InteractiveBloc implements UserDetailsBlocEvents, UserDetailsBlocStates { UserDetailsBloc( - this._requestManager, this._account, ) { unawaited(refresh()); } - final RequestManager _requestManager; final Account _account; @override @@ -38,7 +36,7 @@ class UserDetailsBloc extends InteractiveBloc implements UserDetailsBlocEvents, @override Future refresh() async { - await _requestManager + await RequestManager.instance .wrapNextcloud( _account.id, 'user-details', diff --git a/packages/neon/neon/lib/src/blocs/user_statuses.dart b/packages/neon/neon/lib/src/blocs/user_statuses.dart index aa2989e3..d5867aad 100644 --- a/packages/neon/neon/lib/src/blocs/user_statuses.dart +++ b/packages/neon/neon/lib/src/blocs/user_statuses.dart @@ -2,11 +2,11 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:meta/meta.dart'; +import 'package:neon/platform.dart'; import 'package:neon/src/bloc/bloc.dart'; import 'package:neon/src/bloc/result.dart'; import 'package:neon/src/blocs/timer.dart'; import 'package:neon/src/models/account.dart'; -import 'package:neon/src/platform/platform.dart'; import 'package:nextcloud/nextcloud.dart'; import 'package:rxdart/rxdart.dart'; import 'package:window_manager/window_manager.dart'; @@ -22,14 +22,12 @@ abstract class UserStatusesBlocStates { @internal class UserStatusesBloc extends InteractiveBloc implements UserStatusesBlocEvents, UserStatusesBlocStates { UserStatusesBloc( - this._platform, this._account, ) { unawaited(refresh()); _timer = TimerBloc().registerTimer(const Duration(minutes: 5), refresh); } - final NeonPlatform _platform; final Account _account; late final NeonTimer _timer; @@ -63,8 +61,12 @@ class UserStatusesBloc extends InteractiveBloc implements UserStatusesBlocEvents UserStatusPublic? data; if (_account.username == username) { - final isAway = - _platform.canUseWindowManager && (!(await windowManager.isFocused()) || !(await windowManager.isVisible())); + var isAway = false; + if (NeonPlatform.instance.canUseWindowManager) { + final focused = await windowManager.isFocused(); + final visible = await windowManager.isFocused(); + isAway = !focused || !visible; + } try { final response = await _account.client.userStatus.heartbeat.heartbeat( status: isAway ? 'away' : 'online', diff --git a/packages/neon/neon/lib/src/models/app_implementation.dart b/packages/neon/neon/lib/src/models/app_implementation.dart index 5c387cff..fd59adc4 100644 --- a/packages/neon/neon/lib/src/models/app_implementation.dart +++ b/packages/neon/neon/lib/src/models/app_implementation.dart @@ -6,10 +6,8 @@ import 'package:neon/l10n/localizations.dart'; import 'package:neon/src/bloc/bloc.dart'; import 'package:neon/src/blocs/accounts.dart'; import 'package:neon/src/models/account.dart'; -import 'package:neon/src/platform/platform.dart'; import 'package:neon/src/settings/models/nextcloud_app_options.dart'; import 'package:neon/src/settings/models/storage.dart'; -import 'package:neon/src/utils/request_manager.dart'; import 'package:neon/src/widgets/drawer_destination.dart'; import 'package:provider/provider.dart'; import 'package:rxdart/rxdart.dart'; @@ -18,8 +16,6 @@ import 'package:shared_preferences/shared_preferences.dart'; abstract class AppImplementation { AppImplementation( final SharedPreferences sharedPreferences, - this.requestManager, - this.platform, ) { final storage = AppStorage('app-$id', sharedPreferences); options = buildOptions(storage); @@ -28,8 +24,6 @@ abstract class AppImplementation String get id; LocalizationsDelegate get localizationsDelegate; List get supportedLocales; - final RequestManager requestManager; - final NeonPlatform platform; String nameFromLocalization(final AppLocalizations localizations) => localizations.appImplementationName(id); String name(final BuildContext context) => nameFromLocalization(AppLocalizations.of(context)); diff --git a/packages/neon/neon/lib/src/models/notifications_interface.dart b/packages/neon/neon/lib/src/models/notifications_interface.dart index 742be3a6..bf2fcdb1 100644 --- a/packages/neon/neon/lib/src/models/notifications_interface.dart +++ b/packages/neon/neon/lib/src/models/notifications_interface.dart @@ -4,11 +4,7 @@ import 'package:neon/src/settings/models/nextcloud_app_options.dart'; abstract interface class NotificationsAppInterface extends AppImplementation { - NotificationsAppInterface( - super.sharedPreferences, - super.requestManager, - super.platform, - ); + NotificationsAppInterface(super.sharedPreferences); } abstract interface class NotificationsBlocInterface extends InteractiveBloc { diff --git a/packages/neon/neon/lib/src/pages/login.dart b/packages/neon/neon/lib/src/pages/login.dart index faa4726f..d0c9b9a7 100644 --- a/packages/neon/neon/lib/src/pages/login.dart +++ b/packages/neon/neon/lib/src/pages/login.dart @@ -6,7 +6,6 @@ import 'package:neon/src/theme/branding.dart'; import 'package:neon/src/theme/dialog.dart'; import 'package:neon/src/utils/validators.dart'; import 'package:neon/src/widgets/nextcloud_logo.dart'; -import 'package:provider/provider.dart'; class LoginPage extends StatefulWidget { const LoginPage({ @@ -40,7 +39,6 @@ class _LoginPageState extends State { @override Widget build(final BuildContext context) { final branding = Branding.of(context); - final platform = Provider.of(context, listen: false); return Scaffold( resizeToAvoidBottomInset: true, @@ -101,7 +99,7 @@ class _LoginPageState extends State { onFieldSubmitted: login, ), ), - if (platform.canUseCamera) ...[ + if (NeonPlatform.instance.canUseCamera) ...[ const SizedBox( height: 50, ), diff --git a/packages/neon/neon/lib/src/pages/settings.dart b/packages/neon/neon/lib/src/pages/settings.dart index 35aa37af..92a2fa58 100644 --- a/packages/neon/neon/lib/src/pages/settings.dart +++ b/packages/neon/neon/lib/src/pages/settings.dart @@ -86,236 +86,234 @@ class _SettingsPageState extends State { builder: ( final context, final accountsSnapshot, - ) { - final platform = Provider.of(context, listen: false); - return ValueListenableBuilder( - valueListenable: globalOptions.pushNotificationsEnabled, - builder: ( - final context, - final _, - final __, - ) => - SettingsList( - initialCategory: widget.initialCategory?.name, - categories: [ + ) => + ValueListenableBuilder( + valueListenable: globalOptions.pushNotificationsEnabled, + builder: ( + final context, + final _, + final __, + ) => + SettingsList( + initialCategory: widget.initialCategory?.name, + categories: [ + SettingsCategory( + title: Text(AppLocalizations.of(context).settingsApps), + key: ValueKey(SettingsCageories.apps.name), + tiles: [ + for (final appImplementation in appImplementations) ...[ + if (appImplementation.options.options.isNotEmpty) ...[ + CustomSettingsTile( + leading: appImplementation.buildIcon(), + title: Text(appImplementation.name(context)), + onTap: () { + NextcloudAppSettingsRoute(appid: appImplementation.id).go(context); + }, + ), + ], + ], + ], + ), + SettingsCategory( + title: Text(AppLocalizations.of(context).optionsCategoryTheme), + key: ValueKey(SettingsCageories.theme.name), + tiles: [ + DropdownButtonSettingsTile( + option: globalOptions.themeMode, + ), + CheckBoxSettingsTile( + option: globalOptions.themeOLEDAsDark, + ), + CheckBoxSettingsTile( + option: globalOptions.themeKeepOriginalAccentColor, + ), + ], + ), + SettingsCategory( + title: Text(AppLocalizations.of(context).optionsCategoryNavigation), + key: ValueKey(SettingsCageories.navigation.name), + tiles: [ + DropdownButtonSettingsTile( + option: globalOptions.navigationMode, + ), + ], + ), + if (NeonPlatform.instance.canUsePushNotifications) ...[ SettingsCategory( - title: Text(AppLocalizations.of(context).settingsApps), - key: ValueKey(SettingsCageories.apps.name), - tiles: [ - for (final appImplementation in appImplementations) ...[ - if (appImplementation.options.options.isNotEmpty) ...[ - CustomSettingsTile( - leading: appImplementation.buildIcon(), - title: Text(appImplementation.name(context)), - onTap: () { - NextcloudAppSettingsRoute(appid: appImplementation.id).go(context); - }, + title: Text(AppLocalizations.of(context).optionsCategoryPushNotifications), + key: ValueKey(SettingsCageories.pushNotifications.name), + tiles: [ + if (!globalOptions.pushNotificationsEnabled.enabled) ...[ + TextSettingsTile( + text: AppLocalizations.of(context).globalOptionsPushNotificationsEnabledDisabledNotice, + style: TextStyle( + fontWeight: FontWeight.w600, + fontStyle: FontStyle.italic, + color: Theme.of(context).colorScheme.error, ), - ], + ), ], + CheckBoxSettingsTile( + option: globalOptions.pushNotificationsEnabled, + ), + DropdownButtonSettingsTile( + option: globalOptions.pushNotificationsDistributor, + ), ], ), + ], + if (NeonPlatform.instance.canUseWindowManager) ...[ SettingsCategory( - title: Text(AppLocalizations.of(context).optionsCategoryTheme), - key: ValueKey(SettingsCageories.theme.name), + title: Text(AppLocalizations.of(context).optionsCategoryStartup), + key: ValueKey(SettingsCageories.startup.name), tiles: [ - DropdownButtonSettingsTile( - option: globalOptions.themeMode, - ), CheckBoxSettingsTile( - option: globalOptions.themeOLEDAsDark, + option: globalOptions.startupMinimized, ), CheckBoxSettingsTile( - option: globalOptions.themeKeepOriginalAccentColor, + option: globalOptions.startupMinimizeInsteadOfExit, ), ], ), + ], + if (NeonPlatform.instance.canUseWindowManager && NeonPlatform.instance.canUseSystemTray) ...[ SettingsCategory( - title: Text(AppLocalizations.of(context).optionsCategoryNavigation), - key: ValueKey(SettingsCageories.navigation.name), + title: Text(AppLocalizations.of(context).optionsCategorySystemTray), + key: ValueKey(SettingsCageories.systemTray.name), tiles: [ - DropdownButtonSettingsTile( - option: globalOptions.navigationMode, + CheckBoxSettingsTile( + option: globalOptions.systemTrayEnabled, + ), + CheckBoxSettingsTile( + option: globalOptions.systemTrayHideToTrayWhenMinimized, ), ], ), - if (platform.canUsePushNotifications) ...[ - SettingsCategory( - title: Text(AppLocalizations.of(context).optionsCategoryPushNotifications), - key: ValueKey(SettingsCageories.pushNotifications.name), - tiles: [ - if (!globalOptions.pushNotificationsEnabled.enabled) ...[ - TextSettingsTile( - text: AppLocalizations.of(context).globalOptionsPushNotificationsEnabledDisabledNotice, - style: TextStyle( - fontWeight: FontWeight.w600, - fontStyle: FontStyle.italic, - color: Theme.of(context).colorScheme.error, - ), - ), - ], - CheckBoxSettingsTile( - option: globalOptions.pushNotificationsEnabled, - ), - DropdownButtonSettingsTile( - option: globalOptions.pushNotificationsDistributor, - ), - ], - ), - ], - if (platform.canUseWindowManager) ...[ - SettingsCategory( - title: Text(AppLocalizations.of(context).optionsCategoryStartup), - key: ValueKey(SettingsCageories.startup.name), - tiles: [ - CheckBoxSettingsTile( - option: globalOptions.startupMinimized, - ), - CheckBoxSettingsTile( - option: globalOptions.startupMinimizeInsteadOfExit, - ), - ], - ), - ], - if (platform.canUseWindowManager && platform.canUseSystemTray) ...[ - SettingsCategory( - title: Text(AppLocalizations.of(context).optionsCategorySystemTray), - key: ValueKey(SettingsCageories.systemTray.name), - tiles: [ - CheckBoxSettingsTile( - option: globalOptions.systemTrayEnabled, - ), - CheckBoxSettingsTile( - option: globalOptions.systemTrayHideToTrayWhenMinimized, - ), - ], - ), - ], - SettingsCategory( - title: Text(AppLocalizations.of(context).optionsCategoryAccounts), - key: ValueKey(SettingsCageories.accounts.name), - tiles: [ - if (accountsSnapshot.requireData.length > 1) ...[ - CheckBoxSettingsTile( - option: globalOptions.rememberLastUsedAccount, - ), - DropdownButtonSettingsTile( - option: globalOptions.initialAccount, - ), - ], - for (final account in accountsSnapshot.requireData) ...[ - AccountSettingsTile( - account: account, - onTap: () { - AccountSettingsRoute(accountid: account.id).go(context); - }, - ), - ], - CustomSettingsTile( - title: ElevatedButton.icon( - onPressed: () async => const LoginRoute().push(context), - icon: Icon(MdiIcons.accountPlus), - label: Text(AppLocalizations.of(context).globalOptionsAccountsAdd), - ), + ], + SettingsCategory( + title: Text(AppLocalizations.of(context).optionsCategoryAccounts), + key: ValueKey(SettingsCageories.accounts.name), + tiles: [ + if (accountsSnapshot.requireData.length > 1) ...[ + CheckBoxSettingsTile( + option: globalOptions.rememberLastUsedAccount, + ), + DropdownButtonSettingsTile( + option: globalOptions.initialAccount, ), ], - ), - SettingsCategory( - title: Text(AppLocalizations.of(context).optionsCategoryOther), - key: ValueKey(SettingsCageories.other.name), - tiles: [ - CustomSettingsTile( - leading: Icon( - MdiIcons.scriptText, - color: Theme.of(context).colorScheme.primary, - ), - title: Text(AppLocalizations.of(context).licenses), - onTap: () async { - final branding = Branding.of(context); - showLicensePage( - context: context, - applicationName: branding.name, - applicationIcon: branding.logo, - applicationLegalese: branding.legalese, - applicationVersion: Provider.of(context, listen: false).version, - ); + for (final account in accountsSnapshot.requireData) ...[ + AccountSettingsTile( + account: account, + onTap: () { + AccountSettingsRoute(accountid: account.id).go(context); }, ), - CustomSettingsTile( - leading: Icon( - MdiIcons.export, - color: Theme.of(context).colorScheme.primary, - ), - title: Text(AppLocalizations.of(context).settingsExport), - onTap: () async { - final settingsExportHelper = _buildSettingsExportHelper(context); + ], + CustomSettingsTile( + title: ElevatedButton.icon( + onPressed: () async => const LoginRoute().push(context), + icon: Icon(MdiIcons.accountPlus), + label: Text(AppLocalizations.of(context).globalOptionsAccountsAdd), + ), + ), + ], + ), + SettingsCategory( + title: Text(AppLocalizations.of(context).optionsCategoryOther), + key: ValueKey(SettingsCageories.other.name), + tiles: [ + CustomSettingsTile( + leading: Icon( + MdiIcons.scriptText, + color: Theme.of(context).colorScheme.primary, + ), + title: Text(AppLocalizations.of(context).licenses), + onTap: () async { + final branding = Branding.of(context); + showLicensePage( + context: context, + applicationName: branding.name, + applicationIcon: branding.logo, + applicationLegalese: branding.legalese, + applicationVersion: Provider.of(context, listen: false).version, + ); + }, + ), + CustomSettingsTile( + leading: Icon( + MdiIcons.export, + color: Theme.of(context).colorScheme.primary, + ), + title: Text(AppLocalizations.of(context).settingsExport), + onTap: () async { + final settingsExportHelper = _buildSettingsExportHelper(context); - try { - final fileName = - 'nextcloud-neon-settings-${DateTime.now().millisecondsSinceEpoch ~/ 1000}.json.base64'; - final data = base64.encode( - utf8.encode( - json.encode( - settingsExportHelper.toJsonExport(), - ), + try { + final fileName = + 'nextcloud-neon-settings-${DateTime.now().millisecondsSinceEpoch ~/ 1000}.json.base64'; + final data = base64.encode( + utf8.encode( + json.encode( + settingsExportHelper.toJsonExport(), ), - ); - await saveFileWithPickDialog(fileName, utf8.encode(data) as Uint8List); - } catch (e, s) { - debugPrint(e.toString()); - debugPrint(s.toString()); - if (mounted) { - NeonException.showSnackbar(context, e); - } + ), + ); + await saveFileWithPickDialog(fileName, utf8.encode(data) as Uint8List); + } catch (e, s) { + debugPrint(e.toString()); + debugPrint(s.toString()); + if (mounted) { + NeonException.showSnackbar(context, e); } - }, + } + }, + ), + CustomSettingsTile( + leading: Icon( + MdiIcons.import, + color: Theme.of(context).colorScheme.primary, ), - CustomSettingsTile( - leading: Icon( - MdiIcons.import, - color: Theme.of(context).colorScheme.primary, - ), - title: Text(AppLocalizations.of(context).settingsImport), - onTap: () async { - final settingsExportHelper = _buildSettingsExportHelper(context); + title: Text(AppLocalizations.of(context).settingsImport), + onTap: () async { + final settingsExportHelper = _buildSettingsExportHelper(context); - try { - final result = await FilePicker.platform.pickFiles( - withData: true, - ); + try { + final result = await FilePicker.platform.pickFiles( + withData: true, + ); - if (result == null) { - return; - } + if (result == null) { + return; + } - if (!result.files.single.path!.endsWith('.json.base64')) { - if (mounted) { - NeonException.showSnackbar( - context, - AppLocalizations.of(context).settingsImportWrongFileExtension, - ); - } - return; + if (!result.files.single.path!.endsWith('.json.base64')) { + if (mounted) { + NeonException.showSnackbar( + context, + AppLocalizations.of(context).settingsImportWrongFileExtension, + ); } + return; + } - final data = json.decode(utf8.decode(base64.decode(utf8.decode(result.files.single.bytes!)))); + final data = json.decode(utf8.decode(base64.decode(utf8.decode(result.files.single.bytes!)))); - await settingsExportHelper.applyFromJson(data as Map); - } catch (e, s) { - debugPrint(e.toString()); - debugPrint(s.toString()); - if (mounted) { - NeonException.showSnackbar(context, e); - } + await settingsExportHelper.applyFromJson(data as Map); + } catch (e, s) { + debugPrint(e.toString()); + debugPrint(s.toString()); + if (mounted) { + NeonException.showSnackbar(context, e); } - }, - ), - ], - ), - ], - ), - ); - }, + } + }, + ), + ], + ), + ], + ), + ), ); return Scaffold( diff --git a/packages/neon/neon/lib/src/platform/android.dart b/packages/neon/neon/lib/src/platform/android.dart index 20450859..2ee8d3ea 100644 --- a/packages/neon/neon/lib/src/platform/android.dart +++ b/packages/neon/neon/lib/src/platform/android.dart @@ -29,13 +29,13 @@ class AndroidNeonPlatform implements NeonPlatform { bool get canUseWindowManager => false; @override - Future getApplicationCachePath() async { + Future get applicationCachePath async { final tempDir = await getTemporaryDirectory(); return tempDir.absolute.path; } @override - Future getUserAccessibleAppDataPath() async { + Future get userAccessibleAppDataPath async { if (!await Permission.storage.request().isGranted) { throw MissingPermissionException(Permission.storage); } diff --git a/packages/neon/neon/lib/src/platform/linux.dart b/packages/neon/neon/lib/src/platform/linux.dart index 0ec74e83..17c270a4 100644 --- a/packages/neon/neon/lib/src/platform/linux.dart +++ b/packages/neon/neon/lib/src/platform/linux.dart @@ -30,13 +30,13 @@ class LinuxNeonPlatform implements NeonPlatform { bool get canUsePushNotifications => false; @override - String getApplicationCachePath() => p.join( + String get applicationCachePath => p.join( xdg.cacheHome.absolute.path, 'de.provokateurin.neon', ); @override - String getUserAccessibleAppDataPath() => p.join(Platform.environment['HOME']!, 'Neon'); + String get userAccessibleAppDataPath => p.join(Platform.environment['HOME']!, 'Neon'); @override void init() { diff --git a/packages/neon/neon/lib/src/platform/platform.dart b/packages/neon/neon/lib/src/platform/platform.dart index af6a06fb..85eb196a 100644 --- a/packages/neon/neon/lib/src/platform/platform.dart +++ b/packages/neon/neon/lib/src/platform/platform.dart @@ -5,22 +5,42 @@ import 'package:meta/meta.dart'; import 'package:neon/src/platform/android.dart'; import 'package:neon/src/platform/linux.dart'; -Future getNeonPlatform() async { - final NeonPlatform platform; - if (Platform.isAndroid) { - platform = const AndroidNeonPlatform(); - } else if (Platform.isLinux) { - platform = const LinuxNeonPlatform(); - } else { - throw UnimplementedError('No implementation for platform ${Platform.operatingSystem} found'); +/// Implements platform specific functionality and exposes the availability of certain features. +@immutable +abstract interface class NeonPlatform { + @visibleForTesting + factory NeonPlatform.mocked(final NeonPlatform platform) => _platform = platform; + + static NeonPlatform? _platform; + + /// Infers and configures the platform automatically. + /// + /// Required to be called before accessing [NeonPlatform.instance]. + static Future setup() async { + if (Platform.isAndroid) { + _platform = const AndroidNeonPlatform(); + } else if (Platform.isLinux) { + _platform = const LinuxNeonPlatform(); + } else { + throw UnimplementedError('No implementation for platform ${Platform.operatingSystem} found'); + } + + await _platform!.init(); } - await platform.init.call(); - return platform; -} + /// Gets the current instance of [NeonPlatform]. + /// + /// Make sure [NeonPlatform.setup] has been called before accessing the instance. + static NeonPlatform get instance { + if (_platform == null) { + throw StateError( + 'NeonPlatform has not been set up yet. Please make sure NeonPlatform.setup() has been called before and completed.', + ); + } + + return _platform!; + } -@immutable -abstract interface class NeonPlatform { abstract final bool canUseWebView; abstract final bool canUseQuickActions; @@ -33,9 +53,9 @@ abstract interface class NeonPlatform { abstract final bool canUsePushNotifications; - FutureOr getApplicationCachePath(); + FutureOr get applicationCachePath; - FutureOr getUserAccessibleAppDataPath(); + FutureOr get userAccessibleAppDataPath; FutureOr init(); } diff --git a/packages/neon/neon/lib/src/utils/global_popups.dart b/packages/neon/neon/lib/src/utils/global_popups.dart index 10748f2b..b0d83022 100644 --- a/packages/neon/neon/lib/src/utils/global_popups.dart +++ b/packages/neon/neon/lib/src/utils/global_popups.dart @@ -45,10 +45,9 @@ class GlobalPopups { final globalOptions = Provider.of(context, listen: false); final firstLaunchBloc = Provider.of(context, listen: false); final nextPushBloc = Provider.of(context, listen: false); - final platform = Provider.of(context, listen: false); _subscriptions.addAll([ - if (platform.canUsePushNotifications) ...[ + if (NeonPlatform.instance.canUsePushNotifications) ...[ firstLaunchBloc.onFirstLaunch.listen((final _) { assert(context.mounted, 'Context should be mounted'); if (!globalOptions.pushNotificationsEnabled.enabled) { diff --git a/packages/neon/neon/lib/src/utils/push_utils.dart b/packages/neon/neon/lib/src/utils/push_utils.dart index af42b13b..353bfc01 100644 --- a/packages/neon/neon/lib/src/utils/push_utils.dart +++ b/packages/neon/neon/lib/src/utils/push_utils.dart @@ -13,12 +13,10 @@ import 'package:neon/src/blocs/accounts.dart'; import 'package:neon/src/models/account.dart'; import 'package:neon/src/models/app_ids.dart'; import 'package:neon/src/models/push_notification.dart'; -import 'package:neon/src/platform/platform.dart'; import 'package:neon/src/settings/models/storage.dart'; import 'package:neon/src/theme/colors.dart'; import 'package:neon/src/utils/global.dart'; import 'package:neon/src/utils/localizations.dart'; -import 'package:neon/src/utils/request_manager.dart'; import 'package:nextcloud/nextcloud.dart'; import 'package:shared_preferences/shared_preferences.dart'; @@ -96,10 +94,6 @@ class PushUtils { } else { final localizations = await appLocalizationsFromSystem(); - final platform = await getNeonPlatform(); - final cache = Cache(platform); - await cache.init(); - var accounts = []; Account? account; NotificationsNotification? notification; diff --git a/packages/neon/neon/lib/src/utils/request_manager.dart b/packages/neon/neon/lib/src/utils/request_manager.dart index 59084143..e29954e3 100644 --- a/packages/neon/neon/lib/src/utils/request_manager.dart +++ b/packages/neon/neon/lib/src/utils/request_manager.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; +import 'package:meta/meta.dart'; import 'package:neon/src/bloc/result.dart'; import 'package:neon/src/platform/platform.dart'; import 'package:nextcloud/nextcloud.dart'; @@ -9,13 +10,24 @@ import 'package:rxdart/rxdart.dart'; import 'package:sqflite/sqflite.dart'; import 'package:xml/xml.dart' as xml; -@immutable class RequestManager { - const RequestManager([ - this.cache, - ]); + RequestManager(); - final Cache? cache; + @visibleForTesting + factory RequestManager.mocked(final RequestManager requestManager) => _requestManager = requestManager; + + static RequestManager? _requestManager; + + /// Gets the current instance of [RequestManager]. + // ignore: prefer_constructors_over_static_methods + static RequestManager get instance => _requestManager ??= RequestManager(); + + Future initCache() async { + _cache = Cache(); + await _cache!.init(); + } + + Cache? _cache; Future wrapNextcloud( final String clientID, @@ -100,7 +112,7 @@ class RequestManager { try { final response = await (disableTimeout ? call() : timeout(call)); - await cache?.set(key, await compute(serialize, response)); + await _cache?.set(key, await compute(serialize, response)); subject.add(Result.success(unwrap(response))); } catch (e, s) { debugPrint(e.toString()); @@ -146,8 +158,8 @@ class RequestManager { ) async { T? cached; try { - if (cache != null && await cache!.has(key)) { - cached = unwrap(await compute(deserialize, (await cache!.get(key))!)); + if (_cache != null && await _cache!.has(key)) { + cached = unwrap(await compute(deserialize, (await _cache!.get(key))!)); } } catch (e, s) { debugPrint(e.toString()); @@ -167,16 +179,14 @@ class RequestManager { return false; } - static Future timeout( + Future timeout( final Future Function() call, ) => call().timeout(const Duration(seconds: 30)); } +@internal class Cache { - Cache(this._platform); - - final NeonPlatform _platform; Database? _database; Future init() async { @@ -186,7 +196,7 @@ class Cache { _database = await openDatabase( p.join( - await _platform.getApplicationCachePath(), + await NeonPlatform.instance.applicationCachePath, 'cache.db', ), version: 1, diff --git a/packages/neon/neon/lib/utils.dart b/packages/neon/neon/lib/utils.dart index 6f1522a1..a0ecb48e 100644 --- a/packages/neon/neon/lib/utils.dart +++ b/packages/neon/neon/lib/utils.dart @@ -3,5 +3,5 @@ export 'package:neon/src/utils/confirmation_dialog.dart'; export 'package:neon/src/utils/exceptions.dart'; export 'package:neon/src/utils/hex_color.dart'; export 'package:neon/src/utils/rename_dialog.dart'; -export 'package:neon/src/utils/request_manager.dart'; +export 'package:neon/src/utils/request_manager.dart' hide Cache; export 'package:neon/src/utils/validators.dart'; diff --git a/packages/neon/neon_files/lib/blocs/browser.dart b/packages/neon/neon_files/lib/blocs/browser.dart index 2b1092c0..d322e095 100644 --- a/packages/neon/neon_files/lib/blocs/browser.dart +++ b/packages/neon/neon_files/lib/blocs/browser.dart @@ -14,14 +14,12 @@ abstract class FilesBrowserBlocStates { class FilesBrowserBloc extends InteractiveBloc implements FilesBrowserBlocEvents, FilesBrowserBlocStates { FilesBrowserBloc( - this._requestManager, this.options, this.account, ) { unawaited(refresh()); } - final RequestManager _requestManager; final FilesAppSpecificOptions options; final Account account; @@ -40,7 +38,7 @@ class FilesBrowserBloc extends InteractiveBloc implements FilesBrowserBlocEvents @override Future refresh() async { - await _requestManager.wrapWebDav>( + await RequestManager.instance.wrapWebDav>( account.id, 'files-${path.value.join('/')}', files, diff --git a/packages/neon/neon_files/lib/blocs/files.dart b/packages/neon/neon_files/lib/blocs/files.dart index b6da7f78..9946c131 100644 --- a/packages/neon/neon_files/lib/blocs/files.dart +++ b/packages/neon/neon_files/lib/blocs/files.dart @@ -28,8 +28,6 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta FilesBloc( this.options, this.account, - this._requestManager, - this._platform, ) { options.uploadQueueParallelism.addListener(_uploadParalelismListener); options.downloadQueueParallelism.addListener(_downloadParalelismListener); @@ -37,8 +35,6 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta final FilesAppSpecificOptions options; final Account account; - final RequestManager _requestManager; - final NeonPlatform _platform; late final browser = getNewFilesBrowserBloc(); final _uploadQueue = Queue(); @@ -88,7 +84,7 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta () async { final file = File( p.join( - await _platform.getApplicationCachePath(), + await NeonPlatform.instance.applicationCachePath, 'files', etag.replaceAll('"', ''), path.last, @@ -141,7 +137,7 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta () async { final file = File( p.join( - await _platform.getUserAccessibleAppDataPath(), + await NeonPlatform.instance.userAccessibleAppDataPath, account.humanReadableID, 'files', path.join(Platform.pathSeparator), @@ -185,7 +181,7 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta tasks.add(tasks.value..remove(task)); } - FilesBrowserBloc getNewFilesBrowserBloc() => FilesBrowserBloc(_requestManager, options, account); + FilesBrowserBloc getNewFilesBrowserBloc() => FilesBrowserBloc(options, account); void _downloadParalelismListener() { _downloadQueue.parallel = options.downloadQueueParallelism.value; diff --git a/packages/neon/neon_files/lib/dialogs/choose_create.dart b/packages/neon/neon_files/lib/dialogs/choose_create.dart index 92332c00..c86b3f3e 100644 --- a/packages/neon/neon_files/lib/dialogs/choose_create.dart +++ b/packages/neon/neon_files/lib/dialogs/choose_create.dart @@ -77,7 +77,7 @@ class _FilesChooseCreateDialogState extends State { } }, ), - if (Provider.of(context, listen: false).canUseCamera) ...[ + if (NeonPlatform.instance.canUseCamera) ...[ ListTile( leading: Icon( MdiIcons.cameraPlus, diff --git a/packages/neon/neon_files/lib/neon_files.dart b/packages/neon/neon_files/lib/neon_files.dart index 0c0abcd4..ea3f00e0 100644 --- a/packages/neon/neon_files/lib/neon_files.dart +++ b/packages/neon/neon_files/lib/neon_files.dart @@ -44,7 +44,7 @@ part 'widgets/browser_view.dart'; part 'widgets/file_preview.dart'; class FilesApp extends AppImplementation { - FilesApp(super.sharedPreferences, super.requestManager, super.platform); + FilesApp(super.sharedPreferences); @override String id = AppIDs.files; @@ -62,8 +62,6 @@ class FilesApp extends AppImplementation { FilesBloc buildBloc(final Account account) => FilesBloc( options, account, - requestManager, - platform, ); @override diff --git a/packages/neon/neon_news/lib/blocs/articles.dart b/packages/neon/neon_news/lib/blocs/articles.dart index 210a2492..f8ae1f55 100644 --- a/packages/neon/neon_news/lib/blocs/articles.dart +++ b/packages/neon/neon_news/lib/blocs/articles.dart @@ -33,7 +33,6 @@ class NewsMainArticlesBloc extends NewsArticlesBloc { NewsMainArticlesBloc( super._newsBloc, super.options, - super.requestManager, super.account, ); } @@ -42,7 +41,6 @@ class NewsArticlesBloc extends InteractiveBloc implements NewsArticlesBlocEvents NewsArticlesBloc( this._newsBloc, this.options, - this.requestManager, this.account, { this.id, this.listType, @@ -59,7 +57,6 @@ class NewsArticlesBloc extends InteractiveBloc implements NewsArticlesBlocEvents final NewsBloc _newsBloc; final NewsAppSpecificOptions options; - final RequestManager requestManager; final Account account; final int? id; final ListType? listType; @@ -118,7 +115,7 @@ class NewsArticlesBloc extends InteractiveBloc implements NewsArticlesBlocEvents } } - await requestManager.wrapNextcloud, NewsListArticles>( + await RequestManager.instance.wrapNextcloud, NewsListArticles>( account.id, 'news-articles-${type.index}-$id-$getRead', articles, diff --git a/packages/neon/neon_news/lib/blocs/news.dart b/packages/neon/neon_news/lib/blocs/news.dart index e505f274..d1022df4 100644 --- a/packages/neon/neon_news/lib/blocs/news.dart +++ b/packages/neon/neon_news/lib/blocs/news.dart @@ -31,7 +31,6 @@ abstract class NewsBlocStates { class NewsBloc extends InteractiveBloc implements NewsBlocEvents, NewsBlocStates, NewsMainArticlesBloc { NewsBloc( this.options, - this.requestManager, this.account, ) { mainArticlesBloc.articles.listen((final result) { @@ -50,13 +49,11 @@ class NewsBloc extends InteractiveBloc implements NewsBlocEvents, NewsBlocStates @override final NewsAppSpecificOptions options; @override - final RequestManager requestManager; @override final Account account; late final mainArticlesBloc = NewsMainArticlesBloc( this, options, - requestManager, account, ); @@ -95,14 +92,14 @@ class NewsBloc extends InteractiveBloc implements NewsBlocEvents, NewsBlocStates @override Future refresh() async { await Future.wait([ - requestManager.wrapNextcloud, NewsListFolders>( + RequestManager.instance.wrapNextcloud, NewsListFolders>( account.id, 'news-folders', folders, () async => account.client.news.listFolders(), (final response) => response.folders.toList(), ), - requestManager.wrapNextcloud, NewsListFeeds>( + RequestManager.instance.wrapNextcloud, NewsListFeeds>( account.id, 'news-feeds', feeds, diff --git a/packages/neon/neon_news/lib/neon_news.dart b/packages/neon/neon_news/lib/neon_news.dart index 412ab5d1..47b3bf13 100644 --- a/packages/neon/neon_news/lib/neon_news.dart +++ b/packages/neon/neon_news/lib/neon_news.dart @@ -52,7 +52,7 @@ part 'widgets/folder_view.dart'; part 'widgets/folders_view.dart'; class NewsApp extends AppImplementation { - NewsApp(super.sharedPreferences, super.requestManager, super.platform); + NewsApp(super.sharedPreferences); @override String id = AppIDs.news; @@ -64,12 +64,11 @@ class NewsApp extends AppImplementation { List supportedLocales = AppLocalizations.supportedLocales; @override - NewsAppSpecificOptions buildOptions(final AppStorage storage) => NewsAppSpecificOptions(storage, platform); + NewsAppSpecificOptions buildOptions(final AppStorage storage) => NewsAppSpecificOptions(storage); @override NewsBloc buildBloc(final Account account) => NewsBloc( options, - requestManager, account, ); diff --git a/packages/neon/neon_news/lib/options.dart b/packages/neon/neon_news/lib/options.dart index 03cb1fb0..6e5baf18 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, this._platform) { + NewsAppSpecificOptions(super.storage) { super.categories = [ generalCategory, articlesCategory, @@ -23,8 +23,6 @@ class NewsAppSpecificOptions extends NextcloudAppOptions { ]; } - final NeonPlatform _platform; - final generalCategory = OptionsCategory( name: (final context) => AppLocalizations.of(context).general, ); @@ -62,7 +60,7 @@ class NewsAppSpecificOptions extends NextcloudAppOptions { defaultValue: ArticleViewType.direct, values: { ArticleViewType.direct: (final context) => AppLocalizations.of(context).optionsArticleViewTypeDirect, - if (_platform.canUseWebView) + if (NeonPlatform.instance.canUseWebView) ArticleViewType.internalBrowser: (final context) => AppLocalizations.of(context).optionsArticleViewTypeInternalBrowser, ArticleViewType.externalBrowser: (final context) => diff --git a/packages/neon/neon_news/lib/pages/feed.dart b/packages/neon/neon_news/lib/pages/feed.dart index 09fa438f..108c5ba0 100644 --- a/packages/neon/neon_news/lib/pages/feed.dart +++ b/packages/neon/neon_news/lib/pages/feed.dart @@ -20,7 +20,6 @@ class NewsFeedPage extends StatelessWidget { bloc: NewsArticlesBloc( bloc, bloc.options, - bloc.requestManager, bloc.account, id: feed.id, listType: ListType.feed, diff --git a/packages/neon/neon_news/lib/widgets/articles_view.dart b/packages/neon/neon_news/lib/widgets/articles_view.dart index 1c63cf79..e9d9abb9 100644 --- a/packages/neon/neon_news/lib/widgets/articles_view.dart +++ b/packages/neon/neon_news/lib/widgets/articles_view.dart @@ -203,7 +203,7 @@ class _NewsArticlesViewState extends State { ); } else if (viewType == ArticleViewType.internalBrowser && article.url != null && - Provider.of(context, listen: false).canUseWebView) { + NeonPlatform.instance.canUseWebView) { await Navigator.of(context).push( MaterialPageRoute( builder: (final context) => NewsArticlePage( diff --git a/packages/neon/neon_news/lib/widgets/folder_view.dart b/packages/neon/neon_news/lib/widgets/folder_view.dart index c58743a3..aa6f438b 100644 --- a/packages/neon/neon_news/lib/widgets/folder_view.dart +++ b/packages/neon/neon_news/lib/widgets/folder_view.dart @@ -47,7 +47,6 @@ class _NewsFolderViewState extends State { bloc: NewsArticlesBloc( widget.bloc, widget.bloc.options, - widget.bloc.requestManager, widget.bloc.account, id: widget.folder.id, listType: ListType.folder, diff --git a/packages/neon/neon_notes/lib/blocs/notes.dart b/packages/neon/neon_notes/lib/blocs/notes.dart index f25386c6..95642765 100644 --- a/packages/neon/neon_notes/lib/blocs/notes.dart +++ b/packages/neon/neon_notes/lib/blocs/notes.dart @@ -25,14 +25,12 @@ abstract class NotesBlocStates { class NotesBloc extends InteractiveBloc implements NotesBlocEvents, NotesBlocStates { NotesBloc( this.options, - this.requestManager, this.account, ) { unawaited(refresh()); } final NotesAppSpecificOptions options; - final RequestManager requestManager; final Account account; @override @@ -46,7 +44,7 @@ class NotesBloc extends InteractiveBloc implements NotesBlocEvents, NotesBlocSta @override Future refresh() async { - await requestManager.wrapNextcloud, BuiltList>( + await RequestManager.instance.wrapNextcloud, BuiltList>( account.id, 'notes-notes', notes, diff --git a/packages/neon/neon_notes/lib/neon_notes.dart b/packages/neon/neon_notes/lib/neon_notes.dart index 5cd6dec2..7f511280 100644 --- a/packages/neon/neon_notes/lib/neon_notes.dart +++ b/packages/neon/neon_notes/lib/neon_notes.dart @@ -42,7 +42,7 @@ part 'widgets/notes_floating_action_button.dart'; part 'widgets/notes_view.dart'; class NotesApp extends AppImplementation { - NotesApp(super.sharedPreferences, super.requestManager, super.platform); + NotesApp(super.sharedPreferences); @override String id = AppIDs.notes; @@ -59,7 +59,6 @@ class NotesApp extends AppImplementation { @override NotesBloc buildBloc(final Account account) => NotesBloc( options, - requestManager, account, ); diff --git a/packages/neon/neon_notifications/lib/blocs/notifications.dart b/packages/neon/neon_notifications/lib/blocs/notifications.dart index 82d1aec7..93dde480 100644 --- a/packages/neon/neon_notifications/lib/blocs/notifications.dart +++ b/packages/neon/neon_notifications/lib/blocs/notifications.dart @@ -16,7 +16,6 @@ class NotificationsBloc extends InteractiveBloc implements NotificationsBlocInterface, NotificationsBlocEvents, NotificationsBlocStates { NotificationsBloc( this.options, - this._requestManager, this._account, ) { notifications.listen((final result) { @@ -31,7 +30,6 @@ class NotificationsBloc extends InteractiveBloc @override final NotificationsAppSpecificOptions options; - final RequestManager _requestManager; final Account _account; late final NeonTimer _timer; @@ -52,7 +50,7 @@ class NotificationsBloc extends InteractiveBloc @override Future refresh() async { - await _requestManager.wrapNextcloud, NotificationsListNotifications>( + await RequestManager.instance.wrapNextcloud, NotificationsListNotifications>( _account.id, 'notifications-notifications', notifications, diff --git a/packages/neon/neon_notifications/lib/neon_notifications.dart b/packages/neon/neon_notifications/lib/neon_notifications.dart index 7760a412..20e9aee6 100644 --- a/packages/neon/neon_notifications/lib/neon_notifications.dart +++ b/packages/neon/neon_notifications/lib/neon_notifications.dart @@ -22,7 +22,7 @@ part 'pages/main.dart'; class NotificationsApp extends AppImplementation implements NotificationsAppInterface { - NotificationsApp(super.sharedPreferences, super.requestManager, super.platform); + NotificationsApp(super.sharedPreferences); @override String id = AppIDs.notifications; @@ -39,7 +39,6 @@ class NotificationsApp extends AppImplementation NotificationsBloc( options, - requestManager, account, );