Browse Source

Merge pull request #495 from nextcloud/refactor/bloc-arguments-propagation

Refactor/bloc arguments propagation
pull/610/head
Kate 1 year ago committed by GitHub
parent
commit
c573648c31
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      packages/app/lib/apps.dart
  2. 20
      packages/neon/neon/lib/neon.dart
  3. 22
      packages/neon/neon/lib/src/app.dart
  4. 2
      packages/neon/neon/lib/src/bloc/bloc.dart
  5. 76
      packages/neon/neon/lib/src/blocs/accounts.dart
  6. 4
      packages/neon/neon/lib/src/blocs/apps.dart
  7. 4
      packages/neon/neon/lib/src/blocs/capabilities.dart
  8. 4
      packages/neon/neon/lib/src/blocs/push_notifications.dart
  9. 4
      packages/neon/neon/lib/src/blocs/user_details.dart
  10. 12
      packages/neon/neon/lib/src/blocs/user_statuses.dart
  11. 6
      packages/neon/neon/lib/src/models/app_implementation.dart
  12. 6
      packages/neon/neon/lib/src/models/notifications_interface.dart
  13. 4
      packages/neon/neon/lib/src/pages/login.dart
  14. 392
      packages/neon/neon/lib/src/pages/settings.dart
  15. 4
      packages/neon/neon/lib/src/platform/android.dart
  16. 4
      packages/neon/neon/lib/src/platform/linux.dart
  17. 50
      packages/neon/neon/lib/src/platform/platform.dart
  18. 3
      packages/neon/neon/lib/src/utils/global_popups.dart
  19. 6
      packages/neon/neon/lib/src/utils/push_utils.dart
  20. 36
      packages/neon/neon/lib/src/utils/request_manager.dart
  21. 2
      packages/neon/neon/lib/utils.dart
  22. 4
      packages/neon/neon_files/lib/blocs/browser.dart
  23. 10
      packages/neon/neon_files/lib/blocs/files.dart
  24. 2
      packages/neon/neon_files/lib/dialogs/choose_create.dart
  25. 4
      packages/neon/neon_files/lib/neon_files.dart
  26. 5
      packages/neon/neon_news/lib/blocs/articles.dart
  27. 7
      packages/neon/neon_news/lib/blocs/news.dart
  28. 5
      packages/neon/neon_news/lib/neon_news.dart
  29. 6
      packages/neon/neon_news/lib/options.dart
  30. 1
      packages/neon/neon_news/lib/pages/feed.dart
  31. 2
      packages/neon/neon_news/lib/widgets/articles_view.dart
  32. 1
      packages/neon/neon_news/lib/widgets/folder_view.dart
  33. 4
      packages/neon/neon_notes/lib/blocs/notes.dart
  34. 3
      packages/neon/neon_notes/lib/neon_notes.dart
  35. 4
      packages/neon/neon_notifications/lib/blocs/notifications.dart
  36. 3
      packages/neon/neon_notifications/lib/neon_notifications.dart

12
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<AppImplementation> 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),
];

20
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<AppImplementation> Function(SharedPreferences, RequestManager, NeonPlatform)
getAppImplementations,
required final Iterable<AppImplementation> 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<SharedPreferences>(
create: (final _) => sharedPreferences,
),
Provider<NeonPlatform>(
create: (final _) => platform,
),
Provider<GlobalOptions>(
create: (final _) => globalOptions,
),
Provider<RequestManager>(
create: (final _) => requestManager,
),
Provider<AccountsBloc>(
create: (final _) => accountsBloc,
),

22
packages/neon/neon/lib/src/app.dart

@ -44,7 +44,6 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
final _appRegex = RegExp(r'^app_([a-z]+)$', multiLine: true);
final _navigatorKey = GlobalKey<NavigatorState>();
late final Iterable<AppImplementation> _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<NeonApp> with WidgetsBindingObserver, tray.Tra
super.initState();
_appImplementations = Provider.of<Iterable<AppImplementation>>(context, listen: false);
_platform = Provider.of<NeonPlatform>(context, listen: false);
_globalOptions = Provider.of<GlobalOptions>(context, listen: false);
_accountsBloc = Provider.of<AccountsBloc>(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<NeonApp> 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<NeonApp> 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<NeonApp> 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<NeonApp> 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<NeonApp> 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<NeonApp> with WidgetsBindingObserver, tray.Tra
}
Future _showAndRestoreWindow() async {
if (!_platform.canUseWindowManager) {
if (!NeonPlatform.instance.canUseWindowManager) {
return;
}
@ -264,10 +262,10 @@ class _NeonAppState extends State<NeonApp> 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);
}

2
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)();

76
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].

4
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<AppImplementation> _filteredAppImplementations(final Iterable<String> 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<List<CoreNavigationEntry>, CoreNavigationGetAppsNavigationResponse200ApplicationJson>(
_account.id,
'apps-apps',

4
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<CoreOcsGetCapabilitiesResponse200ApplicationJson_Ocs_Data,
await RequestManager.instance.wrapNextcloud<CoreOcsGetCapabilitiesResponse200ApplicationJson_Ocs_Data,
CoreOcsGetCapabilitiesResponse200ApplicationJson>(
_account.id,
'capabilities',

4
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;

4
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<ProvisioningApiUserDetails, ProvisioningApiUsersGetCurrentUserResponse200ApplicationJson>(
_account.id,
'user-details',

12
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',

6
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<T extends Bloc, R extends NextcloudAppOptions> {
AppImplementation(
final SharedPreferences sharedPreferences,
this.requestManager,
this.platform,
) {
final storage = AppStorage('app-$id', sharedPreferences);
options = buildOptions(storage);
@ -28,8 +24,6 @@ abstract class AppImplementation<T extends Bloc, R extends NextcloudAppOptions>
String get id;
LocalizationsDelegate get localizationsDelegate;
List<Locale> 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));

6
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<T extends NotificationsBlocInterface,
R extends NotificationsOptionsInterface> extends AppImplementation<T, R> {
NotificationsAppInterface(
super.sharedPreferences,
super.requestManager,
super.platform,
);
NotificationsAppInterface(super.sharedPreferences);
}
abstract interface class NotificationsBlocInterface extends InteractiveBloc {

4
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<LoginPage> {
@override
Widget build(final BuildContext context) {
final branding = Branding.of(context);
final platform = Provider.of<NeonPlatform>(context, listen: false);
return Scaffold(
resizeToAvoidBottomInset: true,
@ -101,7 +99,7 @@ class _LoginPageState extends State<LoginPage> {
onFieldSubmitted: login,
),
),
if (platform.canUseCamera) ...[
if (NeonPlatform.instance.canUseCamera) ...[
const SizedBox(
height: 50,
),

392
packages/neon/neon/lib/src/pages/settings.dart

@ -86,236 +86,234 @@ class _SettingsPageState extends State<SettingsPage> {
builder: (
final context,
final accountsSnapshot,
) {
final platform = Provider.of<NeonPlatform>(context, listen: false);
return ValueListenableBuilder<bool>(
valueListenable: globalOptions.pushNotificationsEnabled,
builder: (
final context,
final _,
final __,
) =>
SettingsList(
initialCategory: widget.initialCategory?.name,
categories: [
) =>
ValueListenableBuilder<bool>(
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: <SettingsTile>[
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: <SettingsTile>[
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: <SettingsTile>[
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<PackageInfo>(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: <SettingsTile>[
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<PackageInfo>(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<String, dynamic>);
} catch (e, s) {
debugPrint(e.toString());
debugPrint(s.toString());
if (mounted) {
NeonException.showSnackbar(context, e);
}
await settingsExportHelper.applyFromJson(data as Map<String, dynamic>);
} catch (e, s) {
debugPrint(e.toString());
debugPrint(s.toString());
if (mounted) {
NeonException.showSnackbar(context, e);
}
},
),
],
),
],
),
);
},
}
},
),
],
),
],
),
),
);
return Scaffold(

4
packages/neon/neon/lib/src/platform/android.dart

@ -29,13 +29,13 @@ class AndroidNeonPlatform implements NeonPlatform {
bool get canUseWindowManager => false;
@override
Future<String> getApplicationCachePath() async {
Future<String> get applicationCachePath async {
final tempDir = await getTemporaryDirectory();
return tempDir.absolute.path;
}
@override
Future<String> getUserAccessibleAppDataPath() async {
Future<String> get userAccessibleAppDataPath async {
if (!await Permission.storage.request().isGranted) {
throw MissingPermissionException(Permission.storage);
}

4
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() {

50
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<NeonPlatform> 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<String> getApplicationCachePath();
FutureOr<String> get applicationCachePath;
FutureOr<String> getUserAccessibleAppDataPath();
FutureOr<String> get userAccessibleAppDataPath;
FutureOr init();
}

3
packages/neon/neon/lib/src/utils/global_popups.dart

@ -45,10 +45,9 @@ class GlobalPopups {
final globalOptions = Provider.of<GlobalOptions>(context, listen: false);
final firstLaunchBloc = Provider.of<FirstLaunchBloc>(context, listen: false);
final nextPushBloc = Provider.of<NextPushBloc>(context, listen: false);
final platform = Provider.of<NeonPlatform>(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) {

6
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? account;
NotificationsNotification? notification;

36
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<T, R>(
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<T> timeout<T>(
Future<T> timeout<T>(
final Future<T> 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,

2
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';

4
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<List<WebDavFile>>(
await RequestManager.instance.wrapWebDav<List<WebDavFile>>(
account.id,
'files-${path.value.join('/')}',
files,

10
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;

2
packages/neon/neon_files/lib/dialogs/choose_create.dart

@ -77,7 +77,7 @@ class _FilesChooseCreateDialogState extends State<FilesChooseCreateDialog> {
}
},
),
if (Provider.of<NeonPlatform>(context, listen: false).canUseCamera) ...[
if (NeonPlatform.instance.canUseCamera) ...[
ListTile(
leading: Icon(
MdiIcons.cameraPlus,

4
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<FilesBloc, FilesAppSpecificOptions> {
FilesApp(super.sharedPreferences, super.requestManager, super.platform);
FilesApp(super.sharedPreferences);
@override
String id = AppIDs.files;
@ -62,8 +62,6 @@ class FilesApp extends AppImplementation<FilesBloc, FilesAppSpecificOptions> {
FilesBloc buildBloc(final Account account) => FilesBloc(
options,
account,
requestManager,
platform,
);
@override

5
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<List<NewsArticle>, NewsListArticles>(
await RequestManager.instance.wrapNextcloud<List<NewsArticle>, NewsListArticles>(
account.id,
'news-articles-${type.index}-$id-$getRead',
articles,

7
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<List<NewsFolder>, NewsListFolders>(
RequestManager.instance.wrapNextcloud<List<NewsFolder>, NewsListFolders>(
account.id,
'news-folders',
folders,
() async => account.client.news.listFolders(),
(final response) => response.folders.toList(),
),
requestManager.wrapNextcloud<List<NewsFeed>, NewsListFeeds>(
RequestManager.instance.wrapNextcloud<List<NewsFeed>, NewsListFeeds>(
account.id,
'news-feeds',
feeds,

5
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<NewsBloc, NewsAppSpecificOptions> {
NewsApp(super.sharedPreferences, super.requestManager, super.platform);
NewsApp(super.sharedPreferences);
@override
String id = AppIDs.news;
@ -64,12 +64,11 @@ class NewsApp extends AppImplementation<NewsBloc, NewsAppSpecificOptions> {
List<Locale> 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,
);

6
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) =>

1
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,

2
packages/neon/neon_news/lib/widgets/articles_view.dart

@ -203,7 +203,7 @@ class _NewsArticlesViewState extends State<NewsArticlesView> {
);
} else if (viewType == ArticleViewType.internalBrowser &&
article.url != null &&
Provider.of<NeonPlatform>(context, listen: false).canUseWebView) {
NeonPlatform.instance.canUseWebView) {
await Navigator.of(context).push(
MaterialPageRoute(
builder: (final context) => NewsArticlePage(

1
packages/neon/neon_news/lib/widgets/folder_view.dart

@ -47,7 +47,6 @@ class _NewsFolderViewState extends State<NewsFolderView> {
bloc: NewsArticlesBloc(
widget.bloc,
widget.bloc.options,
widget.bloc.requestManager,
widget.bloc.account,
id: widget.folder.id,
listType: ListType.folder,

4
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<List<NotesNote>, BuiltList>(
await RequestManager.instance.wrapNextcloud<List<NotesNote>, BuiltList>(
account.id,
'notes-notes',
notes,

3
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<NotesBloc, NotesAppSpecificOptions> {
NotesApp(super.sharedPreferences, super.requestManager, super.platform);
NotesApp(super.sharedPreferences);
@override
String id = AppIDs.notes;
@ -59,7 +59,6 @@ class NotesApp extends AppImplementation<NotesBloc, NotesAppSpecificOptions> {
@override
NotesBloc buildBloc(final Account account) => NotesBloc(
options,
requestManager,
account,
);

4
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<List<NotificationsNotification>, NotificationsListNotifications>(
await RequestManager.instance.wrapNextcloud<List<NotificationsNotification>, NotificationsListNotifications>(
_account.id,
'notifications-notifications',
notifications,

3
packages/neon/neon_notifications/lib/neon_notifications.dart

@ -22,7 +22,7 @@ part 'pages/main.dart';
class NotificationsApp extends AppImplementation<NotificationsBloc, NotificationsAppSpecificOptions>
implements NotificationsAppInterface<NotificationsBloc, NotificationsAppSpecificOptions> {
NotificationsApp(super.sharedPreferences, super.requestManager, super.platform);
NotificationsApp(super.sharedPreferences);
@override
String id = AppIDs.notifications;
@ -39,7 +39,6 @@ class NotificationsApp extends AppImplementation<NotificationsBloc, Notification
@override
NotificationsBloc buildBloc(final Account account) => NotificationsBloc(
options,
requestManager,
account,
);

Loading…
Cancel
Save