Browse Source

Merge pull request #742 from nextcloud/refactor/types

Refactor/types
pull/743/head
Nikolas Rimikis 1 year ago committed by GitHub
parent
commit
abbb3d764a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      packages/app/integration_test/screenshot_test.dart
  2. 2
      packages/app/lib/main.dart
  3. 2
      packages/dynamite/dynamite/lib/src/builder/client.dart
  4. 5
      packages/neon/neon/lib/blocs.dart
  5. 2
      packages/neon/neon/lib/neon.dart
  6. 14
      packages/neon/neon/lib/src/app.dart
  7. 6
      packages/neon/neon/lib/src/bloc/bloc.dart
  8. 3
      packages/neon/neon/lib/src/blocs/accounts.dart
  9. 16
      packages/neon/neon/lib/src/blocs/apps.dart
  10. 8
      packages/neon/neon/lib/src/blocs/capabilities.dart
  11. 10
      packages/neon/neon/lib/src/blocs/first_launch.dart
  12. 6
      packages/neon/neon/lib/src/blocs/login_check_account.dart
  13. 6
      packages/neon/neon/lib/src/blocs/login_check_server_status.dart
  14. 10
      packages/neon/neon/lib/src/blocs/login_flow.dart
  15. 10
      packages/neon/neon/lib/src/blocs/next_push.dart
  16. 14
      packages/neon/neon/lib/src/blocs/push_notifications.dart
  17. 6
      packages/neon/neon/lib/src/blocs/timer.dart
  18. 10
      packages/neon/neon/lib/src/blocs/unified_search.dart
  19. 8
      packages/neon/neon/lib/src/blocs/user_details.dart
  20. 10
      packages/neon/neon/lib/src/blocs/user_statuses.dart
  21. 2
      packages/neon/neon/lib/src/models/app_implementation.dart
  22. 2
      packages/neon/neon/lib/src/pages/account_settings.dart
  23. 8
      packages/neon/neon/lib/src/pages/home.dart
  24. 2
      packages/neon/neon/lib/src/pages/login.dart
  25. 2
      packages/neon/neon/lib/src/pages/login_check_account.dart
  26. 2
      packages/neon/neon/lib/src/pages/login_check_server_status.dart
  27. 2
      packages/neon/neon/lib/src/pages/login_flow.dart
  28. 2
      packages/neon/neon/lib/src/pages/login_qrcode.dart
  29. 2
      packages/neon/neon/lib/src/pages/nextcloud_app_settings.dart
  30. 6
      packages/neon/neon/lib/src/pages/route_not_found.dart
  31. 3
      packages/neon/neon/lib/src/pages/settings.dart
  32. 4
      packages/neon/neon/lib/src/platform/platform.dart
  33. 2
      packages/neon/neon/lib/src/router.dart
  34. 4
      packages/neon/neon/lib/src/settings/models/options_collection.dart
  35. 18
      packages/neon/neon/lib/src/settings/models/storage.dart
  36. 2
      packages/neon/neon/lib/src/settings/utils/settings_export_helper.dart
  37. 2
      packages/neon/neon/lib/src/settings/widgets/account_settings_tile.dart
  38. 2
      packages/neon/neon/lib/src/settings/widgets/custom_settings_tile.dart
  39. 2
      packages/neon/neon/lib/src/settings/widgets/settings_tile.dart
  40. 3
      packages/neon/neon/lib/src/utils/account_options.dart
  41. 9
      packages/neon/neon/lib/src/utils/global_options.dart
  42. 2
      packages/neon/neon/lib/src/utils/global_popups.dart
  43. 2
      packages/neon/neon/lib/src/utils/push_utils.dart
  44. 39
      packages/neon/neon/lib/src/utils/request_manager.dart
  45. 6
      packages/neon/neon/lib/src/utils/stream_listenable.dart
  46. 1
      packages/neon/neon/lib/src/utils/user_agent.dart
  47. 2
      packages/neon/neon/lib/src/widgets/account_tile.dart
  48. 4
      packages/neon/neon/lib/src/widgets/app_bar.dart
  49. 42
      packages/neon/neon/lib/src/widgets/cached_image.dart
  50. 2
      packages/neon/neon/lib/src/widgets/exception.dart
  51. 2
      packages/neon/neon/lib/src/widgets/list_view.dart
  52. 4
      packages/neon/neon/test/option_test.dart
  53. 4
      packages/neon/neon/test/options_collection_test.dart
  54. 2
      packages/neon/neon/test/settings_export_test.dart
  55. 6
      packages/neon/neon_files/lib/blocs/browser.dart
  56. 8
      packages/neon/neon_files/lib/blocs/files.dart
  57. 4
      packages/neon/neon_files/lib/dialogs/choose_create.dart
  58. 2
      packages/neon/neon_files/lib/neon_files.dart
  59. 2
      packages/neon/neon_files/lib/options.dart
  60. 4
      packages/neon/neon_files/lib/utils/task.dart
  61. 13
      packages/neon/neon_files/lib/widgets/file_preview.dart
  62. 8
      packages/neon/neon_news/lib/blocs/article.dart
  63. 8
      packages/neon/neon_news/lib/blocs/articles.dart
  64. 8
      packages/neon/neon_news/lib/blocs/news.dart
  65. 3
      packages/neon/neon_news/lib/neon_news.dart
  66. 2
      packages/neon/neon_news/lib/pages/article.dart
  67. 2
      packages/neon/neon_news/lib/widgets/folder_select.dart
  68. 6
      packages/neon/neon_notes/lib/blocs/note.dart
  69. 8
      packages/neon/neon_notes/lib/blocs/notes.dart
  70. 2
      packages/neon/neon_notes/lib/neon_notes.dart
  71. 2
      packages/neon/neon_notes/lib/widgets/category_select.dart
  72. 2
      packages/neon/neon_notifications/lib/blocs/notifications.dart
  73. 2
      packages/neon/neon_notifications/lib/neon_notifications.dart
  74. 1
      packages/neon/neon_notifications/lib/pages/main.dart
  75. 1
      packages/neon_lints/lib/src/base.yaml
  76. 1
      packages/neon_lints/lint_maker.yaml
  77. 22
      packages/nextcloud/lib/src/api/news.openapi.dart
  78. 2
      packages/nextcloud/lib/src/webdav/client.dart
  79. 2
      packages/nextcloud/test/helper.dart
  80. 2
      packages/nextcloud/test/notifications_test.dart
  81. 4
      packages/nextcloud/test/settings_test.dart
  82. 2
      packages/sort_box/lib/sort_box.dart

10
packages/app/integration_test/screenshot_test.dart

@ -14,7 +14,7 @@ import 'package:neon/settings.dart';
import 'package:neon_files/widgets/actions.dart';
import 'package:shared_preferences/shared_preferences.dart';
Future runTestApp(
Future<void> runTestApp(
final WidgetTester tester,
final IntegrationTestWidgetsFlutterBinding binding, {
final Account? account,
@ -30,18 +30,18 @@ Future runTestApp(
await tester.pumpAndSettle();
}
Future openDrawer(final WidgetTester tester) async {
Future<void> openDrawer(final WidgetTester tester) async {
await tester.tap(find.byTooltip('Open navigation menu'));
await tester.pumpAndSettle();
}
Future switchPage(final WidgetTester tester, final String name) async {
Future<void> switchPage(final WidgetTester tester, final String name) async {
await openDrawer(tester);
await tester.tap(find.text(name).last);
await tester.pumpAndSettle();
}
Future prepareScreenshot(final WidgetTester tester, final IntegrationTestWidgetsFlutterBinding binding) async {
Future<void> prepareScreenshot(final WidgetTester tester, final IntegrationTestWidgetsFlutterBinding binding) async {
await binding.convertFlutterSurfaceToImage();
await tester.pumpAndSettle();
}
@ -63,7 +63,7 @@ Future<Account> getAccount(final String username) async {
);
}
Future main() async {
Future<void> main() async {
// The screenshots are pretty annoying on Android. See https://github.com/flutter/flutter/issues/92381
assert(Platform.isAndroid, 'Screenshots need to be taken on Android');

2
packages/app/lib/main.dart

@ -2,7 +2,7 @@ import 'package:app/apps.dart';
import 'package:app/branding.dart';
import 'package:neon/neon.dart';
Future main() async {
Future<void> main() async {
await runNeon(
appImplementations: appImplementations,
theme: neonTheme,

2
packages/dynamite/dynamite/lib/src/builder/client.dart

@ -604,7 +604,7 @@ final _response = await ${isRootClient ? 'this' : '_rootClient'}.doRequest(
b.returns = refer('Future<$dataType>');
code.write('return $dataValue;');
} else {
b.returns = refer('Future');
b.returns = refer('Future<void>');
code.write('return;');
}

5
packages/neon/neon/lib/blocs.dart

@ -1,5 +1,6 @@
export 'package:neon/src/bloc/bloc.dart';
export 'package:neon/src/bloc/result.dart';
export 'package:neon/src/bloc/result_builder.dart';
export 'package:neon/src/blocs/accounts.dart'; // TODO: Remove access to the AccountsBloc. Apps should not need to access this
export 'package:neon/src/blocs/timer.dart';
// TODO: Remove access to the AccountsBloc. Apps should not need to access this
export 'package:neon/src/blocs/accounts.dart' show AccountsBloc;
export 'package:neon/src/blocs/timer.dart' hide TimerBlocEvents, TimerBlocStates;

2
packages/neon/neon/lib/neon.dart

@ -18,7 +18,7 @@ import 'package:neon/src/utils/user_agent.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:provider/provider.dart';
Future runNeon({
Future<void> runNeon({
required final Iterable<AppImplementation> appImplementations,
required final NeonTheme theme,
@visibleForTesting final WidgetsBinding? bindingOverride,

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

@ -5,6 +5,7 @@ import 'dart:io';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter_native_splash/flutter_native_splash.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result_builder.dart';
import 'package:neon/src/blocs/accounts.dart';
@ -27,6 +28,7 @@ import 'package:quick_actions/quick_actions.dart';
import 'package:tray_manager/tray_manager.dart' as tray;
import 'package:window_manager/window_manager.dart';
@internal
class NeonApp extends StatefulWidget {
const NeonApp({
required this.neonTheme,
@ -196,7 +198,7 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
}
@override
Future onWindowClose() async {
Future<void> onWindowClose() async {
if (_globalOptions.startupMinimizeInsteadOfExit.value) {
await _saveAndMinimizeWindow();
} else {
@ -205,11 +207,11 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
}
@override
Future onWindowMinimize() async {
Future<void> onWindowMinimize() async {
await _saveAndMinimizeWindow();
}
Future _handleShortcut(final String shortcutType) async {
Future<void> _handleShortcut(final String shortcutType) async {
if (shortcutType == 'show_hide') {
if (NeonPlatform.instance.canUseWindowManager) {
if (await windowManager.isVisible()) {
@ -231,13 +233,13 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
}
}
Future _openAppFromExternal(final Account account, final String id) async {
Future<void> _openAppFromExternal(final Account account, final String id) async {
await _accountsBloc.getAppsBlocFor(account).setActiveApp(id);
_navigatorKey.currentState!.popUntil((final route) => route.settings.name == 'home');
await _showAndRestoreWindow();
}
Future _saveAndMinimizeWindow() async {
Future<void> _saveAndMinimizeWindow() async {
_lastBounds = await windowManager.getBounds();
if (_globalOptions.systemTrayEnabled.value && _globalOptions.systemTrayHideToTrayWhenMinimized.value) {
await windowManager.hide();
@ -246,7 +248,7 @@ class _NeonAppState extends State<NeonApp> with WidgetsBindingObserver, tray.Tra
}
}
Future _showAndRestoreWindow() async {
Future<void> _showAndRestoreWindow() async {
if (!NeonPlatform.instance.canUseWindowManager) {
return;
}

6
packages/neon/neon/lib/src/bloc/bloc.dart

@ -17,7 +17,7 @@ abstract class InteractiveBloc extends Bloc {
final _errorsStreamController = StreamController<Object>();
late Stream<Object> errors = _errorsStreamController.stream.asBroadcastStream();
Future refresh();
Future<void> refresh();
void addError(final Object error) {
_errorsStreamController.add(error);
@ -25,9 +25,9 @@ abstract class InteractiveBloc extends Bloc {
// ignore: avoid_void_async
void wrapAction(
final Future Function() call, {
final AsyncCallback call, {
final bool disableTimeout = false,
final Future Function()? refresh,
final AsyncCallback? refresh,
}) async {
try {
if (disableTimeout) {

3
packages/neon/neon/lib/src/blocs/accounts.dart

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:meta/meta.dart';
import 'package:neon/src/bloc/bloc.dart';
import 'package:neon/src/blocs/apps.dart';
import 'package:neon/src/blocs/capabilities.dart';
@ -17,6 +18,7 @@ import 'package:rxdart/rxdart.dart';
const _keyAccounts = 'accounts';
@internal
abstract interface class AccountsBlocEvents {
/// Logs in the given [account].
///
@ -40,6 +42,7 @@ abstract interface class AccountsBlocEvents {
void setActiveAccount(final Account account);
}
@internal
abstract interface class AccountsBlocStates {
/// All registered accounts.
///

16
packages/neon/neon/lib/src/blocs/apps.dart

@ -16,7 +16,8 @@ import 'package:nextcloud/nextcloud.dart';
import 'package:provider/provider.dart';
import 'package:rxdart/rxdart.dart';
abstract class AppsBlocEvents {
@internal
abstract interface class AppsBlocEvents {
/// Sets the active app using the [appID].
///
/// If the app is already the active app nothing will happen.
@ -24,7 +25,8 @@ abstract class AppsBlocEvents {
void setActiveApp(final String appID, {final bool skipAlreadySet = false});
}
abstract class AppsBlocStates {
@internal
abstract interface class AppsBlocStates {
BehaviorSubject<Result<List<CoreNavigationEntry>>> get apps;
BehaviorSubject<Result<Iterable<AppImplementation>>> get appImplementations;
@ -33,7 +35,7 @@ abstract class AppsBlocStates {
BehaviorSubject<AppImplementation> get activeApp;
BehaviorSubject get openNotifications;
BehaviorSubject<void> get openNotifications;
BehaviorSubject<Iterable<(String, Object?)>?> get appVersions;
}
@ -182,13 +184,13 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates
BehaviorSubject<Result<NotificationsAppInterface?>>();
@override
BehaviorSubject openNotifications = BehaviorSubject();
BehaviorSubject<void> openNotifications = BehaviorSubject();
@override
BehaviorSubject<List<(String, Object?)>?> appVersions = BehaviorSubject();
@override
Future refresh() async {
Future<void> refresh() async {
await RequestManager.instance
.wrapNextcloud<List<CoreNavigationEntry>, CoreNavigationGetAppsNavigationResponseApplicationJson>(
_account.id,
@ -200,7 +202,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates
}
@override
Future setActiveApp(final String appID, {final bool skipAlreadySet = false}) async {
Future<void> setActiveApp(final String appID, {final bool skipAlreadySet = false}) async {
if (appID == AppIDs.notifications) {
openNotifications.add(null);
return;
@ -220,6 +222,6 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates
T getAppBloc<T extends Bloc>(final AppImplementation<T, dynamic> appImplementation) =>
appImplementation.getBloc(_account);
List<Provider> get appBlocProviders =>
List<Provider<Bloc>> get appBlocProviders =>
_allAppImplementations.map((final appImplementation) => appImplementation.blocProvider).toList();
}

8
packages/neon/neon/lib/src/blocs/capabilities.dart

@ -8,9 +8,11 @@ import 'package:neon/src/utils/request_manager.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:rxdart/rxdart.dart';
abstract class CapabilitiesBlocEvents {}
@internal
abstract interface class CapabilitiesBlocEvents {}
abstract class CapabilitiesBlocStates {
@internal
abstract interface class CapabilitiesBlocStates {
BehaviorSubject<Result<CoreOcsGetCapabilitiesResponseApplicationJson_Ocs_Data>> get capabilities;
}
@ -35,7 +37,7 @@ class CapabilitiesBloc extends InteractiveBloc implements CapabilitiesBlocEvents
BehaviorSubject<Result<CoreOcsGetCapabilitiesResponseApplicationJson_Ocs_Data>>();
@override
Future refresh() async {
Future<void> refresh() async {
await RequestManager.instance.wrapNextcloud<CoreOcsGetCapabilitiesResponseApplicationJson_Ocs_Data,
CoreOcsGetCapabilitiesResponseApplicationJson>(
_account.id,

10
packages/neon/neon/lib/src/blocs/first_launch.dart

@ -5,10 +5,12 @@ import 'package:neon/src/bloc/bloc.dart';
import 'package:neon/src/settings/models/storage.dart';
import 'package:rxdart/rxdart.dart';
abstract class FirstLaunchBlocEvents {}
@internal
abstract interface class FirstLaunchBlocEvents {}
abstract class FirstLaunchBlocStates {
BehaviorSubject get onFirstLaunch;
@internal
abstract interface class FirstLaunchBlocStates {
BehaviorSubject<void> get onFirstLaunch;
}
@immutable
@ -31,5 +33,5 @@ class FirstLaunchBloc extends Bloc implements FirstLaunchBlocEvents, FirstLaunch
}
@override
final BehaviorSubject onFirstLaunch = BehaviorSubject();
final BehaviorSubject<void> onFirstLaunch = BehaviorSubject();
}

6
packages/neon/neon/lib/src/blocs/login_check_account.dart

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:meta/meta.dart';
import 'package:neon/src/bloc/bloc.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/models/account.dart';
@ -8,13 +9,16 @@ import 'package:neon/src/utils/user_agent.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:rxdart/rxdart.dart';
@internal
abstract interface class LoginCheckAccountBlocEvents {}
@internal
abstract interface class LoginCheckAccountBlocStates {
/// Contains the account for the user
BehaviorSubject<Result<Account>> get state;
}
@internal
class LoginCheckAccountBloc extends InteractiveBloc
implements LoginCheckAccountBlocEvents, LoginCheckAccountBlocStates {
LoginCheckAccountBloc(
@ -40,7 +44,7 @@ class LoginCheckAccountBloc extends InteractiveBloc
BehaviorSubject<Result<Account>> state = BehaviorSubject();
@override
Future refresh() async {
Future<void> refresh() async {
state.add(Result.loading());
try {

6
packages/neon/neon/lib/src/blocs/login_check_server_status.dart

@ -1,19 +1,23 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:meta/meta.dart';
import 'package:neon/src/bloc/bloc.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/utils/user_agent.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:rxdart/rxdart.dart';
@internal
abstract interface class LoginCheckServerStatusBlocEvents {}
@internal
abstract interface class LoginCheckServerStatusBlocStates {
/// Contains the current server connection state
BehaviorSubject<Result<CoreStatus>> get state;
}
@internal
class LoginCheckServerStatusBloc extends InteractiveBloc
implements LoginCheckServerStatusBlocEvents, LoginCheckServerStatusBlocStates {
LoginCheckServerStatusBloc(this.serverURL) {
@ -33,7 +37,7 @@ class LoginCheckServerStatusBloc extends InteractiveBloc
BehaviorSubject<Result<CoreStatus>> state = BehaviorSubject();
@override
Future refresh() async {
Future<void> refresh() async {
state.add(Result.loading());
try {

10
packages/neon/neon/lib/src/blocs/login_flow.dart

@ -1,20 +1,24 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:meta/meta.dart';
import 'package:neon/src/bloc/bloc.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/utils/user_agent.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:rxdart/rxdart.dart';
abstract class LoginFlowBlocEvents {}
@internal
abstract interface class LoginFlowBlocEvents {}
abstract class LoginFlowBlocStates {
@internal
abstract interface class LoginFlowBlocStates {
BehaviorSubject<Result<CoreLoginFlowV2>> get init;
Stream<CoreLoginFlowV2Credentials> get result;
}
@internal
class LoginFlowBloc extends InteractiveBloc implements LoginFlowBlocEvents, LoginFlowBlocStates {
LoginFlowBloc(this.serverURL) {
unawaited(refresh());
@ -45,7 +49,7 @@ class LoginFlowBloc extends InteractiveBloc implements LoginFlowBlocEvents, Logi
late Stream<CoreLoginFlowV2Credentials> result = _resultController.stream.asBroadcastStream();
@override
Future refresh() async {
Future<void> refresh() async {
try {
init.add(Result.loading());

10
packages/neon/neon/lib/src/blocs/next_push.dart

@ -8,10 +8,12 @@ import 'package:neon/src/models/account.dart';
import 'package:neon/src/utils/global_options.dart';
import 'package:rxdart/rxdart.dart';
abstract class NextPushBlocEvents {}
@internal
abstract interface class NextPushBlocEvents {}
abstract class NextPushBlocStates {
BehaviorSubject get onNextPushSupported;
@internal
abstract interface class NextPushBlocStates {
BehaviorSubject<void> get onNextPushSupported;
}
@internal
@ -78,5 +80,5 @@ class NextPushBloc extends Bloc implements NextPushBlocEvents, NextPushBlocState
}
@override
BehaviorSubject onNextPushSupported = BehaviorSubject();
BehaviorSubject<void> onNextPushSupported = BehaviorSubject();
}

14
packages/neon/neon/lib/src/blocs/push_notifications.dart

@ -14,9 +14,11 @@ import 'package:neon/src/utils/push_utils.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:unifiedpush/unifiedpush.dart';
abstract class PushNotificationsBlocEvents {}
@internal
abstract interface class PushNotificationsBlocEvents {}
abstract class PushNotificationsBlocStates {
@internal
abstract interface class PushNotificationsBlocStates {
Stream<PushNotification> get notifications;
}
@ -40,7 +42,7 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents,
final GlobalOptions _globalOptions;
final _notificationsController = StreamController<PushNotification>();
StreamSubscription? _accountsListener;
StreamSubscription<List<Account>>? _accountsListener;
@override
void dispose() {
@ -66,7 +68,7 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents,
}
}
Future _setupUnifiedPush() async {
Future<void> _setupUnifiedPush() async {
// We just use a single RSA keypair for all accounts
final keypair = await PushUtils.loadRSAKeypair();
@ -118,7 +120,7 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents,
}
}
Future _unregisterUnifiedPushInstances(final List<Account> accounts) async {
Future<void> _unregisterUnifiedPushInstances(final List<Account> accounts) async {
for (final account in accounts) {
try {
await account.client.notifications.push.removeDevice();
@ -130,7 +132,7 @@ class PushNotificationsBloc extends Bloc implements PushNotificationsBlocEvents,
}
}
Future _registerUnifiedPushInstances(final List<Account> accounts) async {
Future<void> _registerUnifiedPushInstances(final List<Account> accounts) async {
// Notifications will only work on accounts with app password
for (final account in accounts.where((final a) => a.password != null)) {
await UnifiedPush.registerApp(account.id);

6
packages/neon/neon/lib/src/blocs/timer.dart

@ -4,7 +4,8 @@ import 'dart:ui';
import 'package:meta/meta.dart';
import 'package:neon/src/bloc/bloc.dart';
abstract class TimerBlocEvents {
@internal
abstract interface class TimerBlocEvents {
/// Register a [callback] that will be called periodically.
/// The time between the executions is defined by the [duration].
NeonTimer registerTimer(final Duration duration, final VoidCallback callback);
@ -14,7 +15,8 @@ abstract class TimerBlocEvents {
void unregisterTimer(final NeonTimer timer);
}
abstract class TimerBlocStates {}
@internal
abstract interface class TimerBlocStates {}
/// Execute callbacks at defined periodic intervals.
/// Components can register their callbacks and everything with the same periodicity will be executed at the same time.

10
packages/neon/neon/lib/src/blocs/unified_search.dart

@ -10,6 +10,7 @@ import 'package:neon/src/blocs/apps.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:rxdart/rxdart.dart';
@internal
abstract interface class UnifiedSearchBlocEvents {
void search(final String term);
@ -18,6 +19,7 @@ abstract interface class UnifiedSearchBlocEvents {
void disable();
}
@internal
abstract interface class UnifiedSearchBlocStates {
BehaviorSubject<bool> get enabled;
@ -56,12 +58,12 @@ class UnifiedSearchBloc extends InteractiveBloc implements UnifiedSearchBlocEven
}
@override
Future refresh() async {
Future<void> refresh() async {
await _search();
}
@override
Future search(final String term) async {
Future<void> search(final String term) async {
_term = term.trim();
await _search();
}
@ -78,7 +80,7 @@ class UnifiedSearchBloc extends InteractiveBloc implements UnifiedSearchBlocEven
_term = '';
}
Future _search() async {
Future<void> _search() async {
if (_term.isEmpty) {
results.add(Result.success(null));
return;
@ -108,7 +110,7 @@ class UnifiedSearchBloc extends InteractiveBloc implements UnifiedSearchBlocEven
}
}
Future _searchProvider(final CoreUnifiedSearchProvider provider) async {
Future<void> _searchProvider(final CoreUnifiedSearchProvider provider) async {
_updateResults(provider, Result.loading());
try {
final response = await _account.client.core.unifiedSearch.search(

8
packages/neon/neon/lib/src/blocs/user_details.dart

@ -8,9 +8,11 @@ import 'package:neon/src/utils/request_manager.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:rxdart/rxdart.dart';
abstract class UserDetailsBlocEvents {}
@internal
abstract interface class UserDetailsBlocEvents {}
abstract class UserDetailsBlocStates {
@internal
abstract interface class UserDetailsBlocStates {
BehaviorSubject<Result<ProvisioningApiUserDetails>> get userDetails;
}
@ -35,7 +37,7 @@ class UserDetailsBloc extends InteractiveBloc implements UserDetailsBlocEvents,
BehaviorSubject<Result<ProvisioningApiUserDetails>>();
@override
Future refresh() async {
Future<void> refresh() async {
await RequestManager.instance
.wrapNextcloud<ProvisioningApiUserDetails, ProvisioningApiUsersGetCurrentUserResponseApplicationJson>(
_account.id,

10
packages/neon/neon/lib/src/blocs/user_statuses.dart

@ -11,11 +11,13 @@ import 'package:nextcloud/nextcloud.dart';
import 'package:rxdart/rxdart.dart';
import 'package:window_manager/window_manager.dart';
abstract class UserStatusesBlocEvents {
@internal
abstract interface class UserStatusesBlocEvents {
void load(final String username, {final bool force = false});
}
abstract class UserStatusesBlocStates {
@internal
abstract interface class UserStatusesBlocStates {
BehaviorSubject<Map<String, Result<UserStatusPublicInterface?>>> get statuses;
}
@ -43,14 +45,14 @@ class UserStatusesBloc extends InteractiveBloc implements UserStatusesBlocEvents
BehaviorSubject<Map<String, Result<UserStatusPublicInterface?>>>();
@override
Future refresh() async {
Future<void> refresh() async {
for (final username in _statuses.keys) {
await load(username, force: true);
}
}
@override
Future load(final String username, {final bool force = false}) async {
Future<void> load(final String username, {final bool force = false}) async {
if (!force && _statuses.containsKey(username)) {
return;
}

2
packages/neon/neon/lib/src/models/app_implementation.dart

@ -18,7 +18,7 @@ abstract class AppImplementation<T extends Bloc, R extends NextcloudAppOptions>
AppImplementation();
String get id;
LocalizationsDelegate get localizationsDelegate;
LocalizationsDelegate<Object> get localizationsDelegate;
List<Locale> get supportedLocales;
String nameFromLocalization(final AppLocalizations localizations) => localizations.appImplementationName(id);

2
packages/neon/neon/lib/src/pages/account_settings.dart

@ -1,6 +1,7 @@
import 'package:filesize/filesize.dart';
import 'package:flutter/material.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result_builder.dart';
import 'package:neon/src/blocs/accounts.dart';
@ -16,6 +17,7 @@ import 'package:neon/src/widgets/exception.dart';
import 'package:neon/src/widgets/linear_progress_indicator.dart';
import 'package:nextcloud/nextcloud.dart';
@internal
class AccountSettingsPage extends StatelessWidget {
const AccountSettingsPage({
required this.bloc,

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

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result_builder.dart';
import 'package:neon/src/blocs/accounts.dart';
@ -16,6 +17,7 @@ import 'package:neon/src/widgets/exception.dart';
import 'package:neon/src/widgets/unified_search_results.dart';
import 'package:provider/provider.dart';
@internal
class HomePage extends StatefulWidget {
const HomePage({
super.key,
@ -32,7 +34,7 @@ class _HomePageState extends State<HomePage> {
late GlobalOptions _globalOptions;
late AccountsBloc _accountsBloc;
late AppsBloc _appsBloc;
late StreamSubscription _versionCheckSubscription;
late StreamSubscription<List<(String, Object?)>?> _versionCheckSubscription;
@override
void initState() {
@ -76,7 +78,7 @@ class _HomePageState extends State<HomePage> {
super.dispose();
}
Future _checkMaintenanceMode() async {
Future<void> _checkMaintenanceMode() async {
try {
final status = await _account.client.core.getStatus();
if (status.maintenance && mounted) {
@ -93,7 +95,7 @@ class _HomePageState extends State<HomePage> {
}
}
Future _showProblem(final String title) async {
Future<void> _showProblem(final String title) async {
final colorScheme = Theme.of(context).colorScheme;
await showDialog(

2
packages/neon/neon/lib/src/pages/login.dart

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/platform/platform.dart';
import 'package:neon/src/router.dart';
@ -7,6 +8,7 @@ import 'package:neon/src/theme/dialog.dart';
import 'package:neon/src/utils/validators.dart';
import 'package:neon/src/widgets/nextcloud_logo.dart';
@internal
class LoginPage extends StatefulWidget {
const LoginPage({
super.key,

2
packages/neon/neon/lib/src/pages/login_check_account.dart

@ -1,6 +1,7 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/bloc/result_builder.dart';
@ -14,6 +15,7 @@ import 'package:neon/src/widgets/exception.dart';
import 'package:neon/src/widgets/validation_tile.dart';
import 'package:provider/provider.dart';
@internal
class LoginCheckAccountPage extends StatefulWidget {
const LoginCheckAccountPage({
required this.serverURL,

2
packages/neon/neon/lib/src/pages/login_check_server_status.dart

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result.dart';
import 'package:neon/src/bloc/result_builder.dart';
@ -9,6 +10,7 @@ import 'package:neon/src/widgets/exception.dart';
import 'package:neon/src/widgets/validation_tile.dart';
import 'package:nextcloud/nextcloud.dart';
@internal
class LoginCheckServerStatusPage extends StatefulWidget {
const LoginCheckServerStatusPage({
required this.serverURL,

2
packages/neon/neon/lib/src/pages/login_flow.dart

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/bloc/result_builder.dart';
import 'package:neon/src/blocs/login_flow.dart';
@ -7,6 +8,7 @@ import 'package:neon/src/widgets/exception.dart';
import 'package:neon/src/widgets/linear_progress_indicator.dart';
import 'package:url_launcher/url_launcher_string.dart';
@internal
class LoginFlowPage extends StatefulWidget {
const LoginFlowPage({
required this.serverURL,

2
packages/neon/neon/lib/src/pages/login_qrcode.dart

@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_zxing/flutter_zxing.dart';
import 'package:meta/meta.dart';
import 'package:neon/src/models/account.dart';
import 'package:neon/src/router.dart';
import 'package:neon/src/utils/exceptions.dart';
import 'package:neon/src/widgets/exception.dart';
@internal
class LoginQrcodePage extends StatefulWidget {
const LoginQrcodePage({
super.key,

2
packages/neon/neon/lib/src/pages/nextcloud_app_settings.dart

@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/models/app_implementation.dart';
import 'package:neon/src/settings/models/select_option.dart';
@ -11,6 +12,7 @@ import 'package:neon/src/settings/widgets/settings_list.dart';
import 'package:neon/src/theme/dialog.dart';
import 'package:neon/src/utils/confirmation_dialog.dart';
@internal
class NextcloudAppSettingsPage extends StatelessWidget {
const NextcloudAppSettingsPage({
required this.appImplementation,

6
packages/neon/neon/lib/src/pages/route_not_found.dart

@ -1,12 +1,14 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:neon/blocs.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/router.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
@internal
class RouteNotFoundPage extends StatefulWidget {
const RouteNotFoundPage({
required this.uri,
@ -27,7 +29,7 @@ class _RouteNotFoundPageState extends State<RouteNotFoundPage> {
unawaited(_checkLaunchable());
}
Future _checkLaunchable() async {
Future<void> _checkLaunchable() async {
final accountsBloc = Provider.of<AccountsBloc>(context, listen: false);
if (!accountsBloc.hasAccounts) {
return;

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

@ -1,6 +1,7 @@
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:flutter_material_design_icons/flutter_material_design_icons.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/models/account.dart';
@ -26,6 +27,7 @@ import 'package:package_info_plus/package_info_plus.dart';
import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher_string.dart';
@internal
enum SettingsCategories {
apps,
theme,
@ -37,6 +39,7 @@ enum SettingsCategories {
other,
}
@internal
class SettingsPage extends StatefulWidget {
const SettingsPage({
this.initialCategory,

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

@ -16,7 +16,7 @@ abstract interface class NeonPlatform {
/// Infers and configures the platform automatically.
///
/// Required to be called before accessing [NeonPlatform.instance].
static Future setup() async {
static Future<void> setup() async {
if (Platform.isAndroid) {
_platform = const AndroidNeonPlatform();
} else if (Platform.isLinux) {
@ -55,5 +55,5 @@ abstract interface class NeonPlatform {
FutureOr<String> get userAccessibleAppDataPath;
FutureOr init();
FutureOr<void> init();
}

2
packages/neon/neon/lib/src/router.dart

@ -62,7 +62,7 @@ class AppRouter extends GoRouter {
routes: $appRoutes,
);
static Page _buildErrorPage(final BuildContext context, final GoRouterState state) => MaterialPage(
static Page<void> _buildErrorPage(final BuildContext context, final GoRouterState state) => MaterialPage(
child: RouteNotFoundPage(
uri: state.uri,
),

4
packages/neon/neon/lib/src/settings/models/options_collection.dart

@ -14,7 +14,7 @@ abstract class OptionsCollection implements Exportable {
/// Collection of options.
@protected
Iterable<Option> get options;
Iterable<Option<dynamic>> get options;
/// Resets all [options].
///
@ -61,5 +61,5 @@ abstract class NextcloudAppOptions extends OptionsCollection {
late final Iterable<OptionsCategory> categories;
@override
late final Iterable<Option> options;
late final Iterable<Option<dynamic>> options;
}

18
packages/neon/neon/lib/src/settings/models/storage.dart

@ -6,12 +6,12 @@ import 'package:shared_preferences/shared_preferences.dart';
abstract interface class SettingsStorage {
String? getString(final String key);
Future setString(final String key, final String value);
Future<bool> setString(final String key, final String value);
bool? getBool(final String key);
// ignore: avoid_positional_boolean_parameters
Future setBool(final String key, final bool value);
Future<bool> setBool(final String key, final bool value);
Future<bool> remove(final String key);
}
@ -53,7 +53,7 @@ final class NeonStorage {
/// Sets up the [SharedPreferences] instance.
///
/// Required to be called before accessing [database].
static Future init() async {
static Future<void> init() async {
if (_sharedPreferences != null) {
return;
}
@ -86,16 +86,16 @@ final class SingleValueStorage {
String? getString() => NeonStorage.database.getString(key.value);
Future setString(final String value) => NeonStorage.database.setString(key.value, value);
Future<bool> setString(final String value) => NeonStorage.database.setString(key.value, value);
bool? getBool() => NeonStorage.database.getBool(key.value);
// ignore: avoid_positional_boolean_parameters
Future setBool(final bool value) => NeonStorage.database.setBool(key.value, value);
Future<bool> setBool(final bool value) => NeonStorage.database.setBool(key.value, value);
List<String>? getStringList() => NeonStorage.database.getStringList(key.value);
Future setStringList(final List<String> value) => NeonStorage.database.setStringList(key.value, value);
Future<bool> setStringList(final List<String> value) => NeonStorage.database.setStringList(key.value, value);
}
@immutable
@ -130,16 +130,16 @@ final class AppStorage implements SettingsStorage {
String? getString(final String key) => NeonStorage.database.getString(formatKey(key));
@override
Future setString(final String key, final String value) => NeonStorage.database.setString(formatKey(key), value);
Future<bool> setString(final String key, final String value) => NeonStorage.database.setString(formatKey(key), value);
@override
bool? getBool(final String key) => NeonStorage.database.getBool(formatKey(key));
@override
Future setBool(final String key, final bool value) => NeonStorage.database.setBool(formatKey(key), value);
Future<bool> setBool(final String key, final bool value) => NeonStorage.database.setBool(formatKey(key), value);
List<String>? getStringList(final String key) => NeonStorage.database.getStringList(formatKey(key));
Future setStringList(final String key, final List<String> value) =>
Future<bool> setStringList(final String key, final List<String> value) =>
NeonStorage.database.setStringList(formatKey(key), value);
}

2
packages/neon/neon/lib/src/settings/utils/settings_export_helper.dart

@ -74,6 +74,7 @@ class SettingsExportHelper {
}
/// Helper class to export [AppImplementation]s implementing the [Exportable] interface.
@internal
@immutable
class AppImplementationsExporter implements Exportable {
const AppImplementationsExporter(this.appImplementations);
@ -109,6 +110,7 @@ class AppImplementationsExporter implements Exportable {
}
/// Helper class to export [Account]s implementing the [Exportable] interface.
@internal
@immutable
class AccountsBlocExporter implements Exportable {
const AccountsBlocExporter(this.accountsBloc);

2
packages/neon/neon/lib/src/settings/widgets/account_settings_tile.dart

@ -17,7 +17,7 @@ class AccountSettingsTile extends SettingsTile {
final Account account;
final Color? color;
final Widget? trailing;
final VoidCallback? onTap;
final GestureTapCallback? onTap;
@override
Widget build(final BuildContext context) => NeonAccountTile(

2
packages/neon/neon/lib/src/settings/widgets/custom_settings_tile.dart

@ -17,7 +17,7 @@ class CustomSettingsTile extends SettingsTile {
final Widget? subtitle;
final Widget? leading;
final Widget? trailing;
final Function()? onTap;
final GestureTapCallback? onTap;
@override
Widget build(final BuildContext context) => ListTile(

2
packages/neon/neon/lib/src/settings/widgets/settings_tile.dart

@ -10,7 +10,7 @@ abstract class SettingsTile extends StatelessWidget {
}
@internal
abstract class InputSettingsTile<T extends Option> extends SettingsTile {
abstract class InputSettingsTile<T extends Option<dynamic>> extends SettingsTile {
const InputSettingsTile({
required this.option,
super.key,

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

@ -30,7 +30,7 @@ class AccountSpecificOptions extends OptionsCollection {
final AppsBloc _appsBloc;
@override
late final List<Option> options = [
late final List<Option<dynamic>> options = [
initialApp,
];
@ -43,6 +43,7 @@ class AccountSpecificOptions extends OptionsCollection {
);
}
@internal
enum AccountOptionKeys implements Storable {
initialApp._('initial-app');

9
packages/neon/neon/lib/src/utils/global_options.dart

@ -4,6 +4,7 @@ import 'package:flutter/material.dart';
import 'package:meta/meta.dart';
import 'package:neon/l10n/localizations.dart';
import 'package:neon/src/models/account.dart';
import 'package:neon/src/models/label_builder.dart';
import 'package:neon/src/settings/models/option.dart';
import 'package:neon/src/settings/models/options_collection.dart';
import 'package:neon/src/settings/models/select_option.dart';
@ -51,7 +52,7 @@ class GlobalOptions extends OptionsCollection {
final PackageInfo _packageInfo;
late final _distributorsMap = <String, String Function(BuildContext)>{
late final _distributorsMap = <String, LabelBuilder>{
_packageInfo.packageName: (final context) =>
AppLocalizations.of(context).globalOptionsPushNotificationsDistributorFirebaseEmbedded,
'com.github.gotify.up': (final context) =>
@ -68,7 +69,7 @@ class GlobalOptions extends OptionsCollection {
};
@override
late final List<Option> options = [
late final List<Option<dynamic>> options = [
themeMode,
themeOLEDAsDark,
themeKeepOriginalAccentColor,
@ -101,7 +102,7 @@ class GlobalOptions extends OptionsCollection {
}
}
Future updateDistributors(final List<String> distributors) async {
Future<void> updateDistributors(final List<String> distributors) async {
pushNotificationsDistributor.values = {
for (final distributor in distributors) ...{
distributor: _distributorsMap[distributor] ?? (final _) => distributor,
@ -220,6 +221,7 @@ class GlobalOptions extends OptionsCollection {
);
}
@internal
enum GlobalOptionKeys implements Storable {
themeMode._('theme-mode'),
themeOledAsDark._('theme-oled-as-dark'),
@ -240,6 +242,7 @@ enum GlobalOptionKeys implements Storable {
final String value;
}
@internal
enum NavigationMode {
drawer,
drawerAlwaysVisible,

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

@ -26,7 +26,7 @@ class GlobalPopups {
bool _registered = false;
late BuildContext _context;
final _subscriptions = <StreamSubscription>[];
final _subscriptions = <StreamSubscription<dynamic>>[];
void dispose() {
for (final subscription in _subscriptions) {

2
packages/neon/neon/lib/src/utils/push_utils.dart

@ -58,7 +58,7 @@ class PushUtils {
return localNotificationsPlugin;
}
static Future onMessage(final Uint8List messages, final String instance) async {
static Future<void> onMessage(final Uint8List messages, final String instance) async {
WidgetsFlutterBinding.ensureInitialized();
final localNotificationsPlugin = await initLocalNotifications(

39
packages/neon/neon/lib/src/utils/request_manager.dart

@ -10,6 +10,11 @@ import 'package:rxdart/rxdart.dart';
import 'package:sqflite/sqflite.dart';
import 'package:xml/xml.dart' as xml;
typedef UnwrapCallback<T, R> = T Function(R);
typedef SerializeCallback<T> = String Function(T);
typedef DeserializeCallback<T> = T Function(String);
typedef NextcloudApiCallback<T> = Future<T> Function();
class RequestManager {
RequestManager();
@ -22,19 +27,19 @@ class RequestManager {
// ignore: prefer_constructors_over_static_methods
static RequestManager get instance => _requestManager ??= RequestManager();
Future initCache() async {
Future<void> initCache() async {
_cache = Cache();
await _cache!.init();
}
Cache? _cache;
Future wrapNextcloud<T, R>(
Future<void> wrapNextcloud<T, R>(
final String clientID,
final String k,
final BehaviorSubject<Result<T>> subject,
final Future<R> Function() call,
final T Function(R) unwrap, {
final NextcloudApiCallback<R> call,
final UnwrapCallback<T, R> unwrap, {
final bool disableTimeout = false,
final bool emitEmptyCache = false,
}) async =>
@ -51,12 +56,12 @@ class RequestManager {
0,
);
Future wrapWebDav<T>(
Future<void> wrapWebDav<T>(
final String clientID,
final String k,
final BehaviorSubject<Result<T>> subject,
final Future<WebDavMultistatus> Function() call,
final T Function(WebDavMultistatus) unwrap, {
final NextcloudApiCallback<WebDavMultistatus> call,
final UnwrapCallback<T, WebDavMultistatus> unwrap, {
final bool disableTimeout = false,
final bool emitEmptyCache = false,
}) async =>
@ -73,14 +78,14 @@ class RequestManager {
0,
);
Future _wrap<T, R>(
Future<void> _wrap<T, R>(
final String clientID,
final String k,
final BehaviorSubject<Result<T>> subject,
final Future<R> Function() call,
final T Function(R) unwrap,
final String Function(R) serialize,
final R Function(String) deserialize,
final NextcloudApiCallback<R> call,
final UnwrapCallback<T, R> unwrap,
final SerializeCallback<R> serialize,
final DeserializeCallback<R> deserialize,
final bool disableTimeout,
final bool emitEmptyCache,
final int retries,
@ -150,8 +155,8 @@ class RequestManager {
Future<bool> _emitCached<T, R>(
final String key,
final BehaviorSubject<Result<T>> subject,
final T Function(R) unwrap,
final R Function(String) deserialize,
final UnwrapCallback<T, R> unwrap,
final DeserializeCallback<R> deserialize,
final bool emitEmptyCache,
final bool loading,
final Object? error,
@ -180,7 +185,7 @@ class RequestManager {
}
Future<T> timeout<T>(
final Future<T> Function() call,
final NextcloudApiCallback<T> call,
) =>
call().timeout(const Duration(seconds: 30));
}
@ -189,7 +194,7 @@ class RequestManager {
class Cache {
Database? _database;
Future init() async {
Future<void> init() async {
if (_database != null) {
return;
}
@ -210,7 +215,7 @@ class Cache {
Future<String?> get(final String key) async =>
(await _database!.rawQuery('SELECT value FROM cache WHERE key = ?', [key]))[0]['value'] as String?;
Future set(final String key, final String value) async => _database!.rawQuery(
Future<void> set(final String key, final String value) async => _database!.rawQuery(
'INSERT INTO cache (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value',
[key, value],
);

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

@ -14,17 +14,17 @@ class StreamListenable extends ChangeNotifier {
notifyListeners();
}
addSubscription(stream);
_addSubscription(stream);
}
/// Listenable for multiple Streams.
///
/// Notifies it's listeners on every event emitted by any of the streams.
StreamListenable.multiListenable(final Iterable<Stream<dynamic>> streams) {
streams.forEach(addSubscription);
streams.forEach(_addSubscription);
}
void addSubscription(final Stream<dynamic> stream) {
void _addSubscription(final Stream<dynamic> stream) {
_subscriptions.add(
stream.asBroadcastStream().listen((final _) {
notifyListeners();

1
packages/neon/neon/lib/src/utils/user_agent.dart

@ -3,6 +3,7 @@ import 'package:package_info_plus/package_info_plus.dart';
late String? _userAgent;
@internal
void buildUserAgent(final PackageInfo packageInfo) {
var buildNumber = packageInfo.buildNumber;
if (buildNumber.isEmpty) {

2
packages/neon/neon/lib/src/widgets/account_tile.dart

@ -25,7 +25,7 @@ class NeonAccountTile extends StatelessWidget {
final Account account;
final Color? color;
final Widget? trailing;
final VoidCallback? onTap;
final GestureTapCallback? onTap;
final Color? textColor;
final bool dense;
final bool showStatus;

4
packages/neon/neon/lib/src/widgets/app_bar.dart

@ -36,7 +36,7 @@ class _NeonAppBarState extends State<NeonAppBar> {
final _searchBarFocusNode = FocusNode();
final _searchTermController = StreamController<String>();
late final StreamSubscription _searchTermSubscription;
late final StreamSubscription<String> _searchTermSubscription;
@override
void initState() {
@ -181,7 +181,7 @@ class _NotificationIconButtonState extends State<NotificationIconButton> {
late AppsBloc _appsBloc;
late List<Account> _accounts;
late Account _account;
late StreamSubscription notificationSubscription;
late StreamSubscription<void> notificationSubscription;
@override
void initState() {

42
packages/neon/neon/lib/src/widgets/cached_image.dart

@ -5,15 +5,20 @@ import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_cache_manager/flutter_cache_manager.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:neon/nextcloud.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/models/account.dart';
import 'package:neon/src/widgets/exception.dart';
import 'package:neon/src/widgets/linear_progress_indicator.dart';
import 'package:provider/provider.dart';
typedef CacheReviver = FutureOr<Uint8List?> Function(CacheManager cacheManager);
typedef ImageDownloader = FutureOr<Uint8List> Function();
typedef CacheWriter = Future<void> Function(CacheManager cacheManager, Uint8List image);
typedef ErrorWidgetBuilder = Widget? Function(BuildContext, dynamic);
typedef ApiImageDownloader = FutureOr<Uint8List> Function(NextcloudClient client);
class NeonCachedImage extends StatefulWidget {
const NeonCachedImage({
required this.image,
@ -186,3 +191,40 @@ class _NeonCachedImageState extends State<NeonCachedImage> {
iconSize: widget.size?.shortestSide,
);
}
class NeonApiImage extends StatelessWidget {
const NeonApiImage({
required this.getImage,
required this.cacheKey,
this.reviver,
this.writeCache,
this.isSvgHint = false,
this.size,
this.fit,
this.svgColor,
this.iconColor,
this.errorBuilder,
super.key,
});
final ApiImageDownloader getImage;
final String cacheKey;
final CacheReviver? reviver;
final CacheWriter? writeCache;
final bool isSvgHint;
final Size? size;
final BoxFit? fit;
final Color? svgColor;
final Color? iconColor;
final ErrorWidgetBuilder? errorBuilder;
@override
Widget build(final BuildContext context) {
final account = Provider.of<AccountsBloc>(context, listen: false).activeAccount.value!;
return NeonCachedImage.custom(
getImage: () async => getImage(account.client),
cacheKey: '${account.id}-$cacheKey',
);
}
}

2
packages/neon/neon/lib/src/widgets/exception.dart

@ -22,7 +22,7 @@ class NeonException extends StatelessWidget {
});
final dynamic exception;
final Function() onRetry;
final VoidCallback onRetry;
final bool onlyIcon;
final double? iconSize;
final Color? color;

2
packages/neon/neon/lib/src/widgets/list_view.dart

@ -19,7 +19,7 @@ class NeonListView<T> extends StatelessWidget {
final Iterable<T>? items;
final bool isLoading;
final dynamic error;
final Future Function() onRefresh;
final RefreshCallback onRefresh;
final Widget Function(BuildContext, T data) builder;
final String? scrollKey;
final bool withFloatingActionButton;

4
packages/neon/neon/test/option_test.dart

@ -45,7 +45,7 @@ void main() {
late SelectOption<SelectValues> option;
setUp(() {
when(() => storage.setString(key.value, any())).thenAnswer((final _) async {});
when(() => storage.setString(key.value, any())).thenAnswer((final _) async => true);
when(() => storage.remove(key.value)).thenAnswer((final _) async => true);
option = SelectOption<SelectValues>(
@ -209,7 +209,7 @@ void main() {
late ToggleOption option;
setUp(() {
when(() => storage.setBool(key.value, any())).thenAnswer((final _) async {});
when(() => storage.setBool(key.value, any())).thenAnswer((final _) async => true);
when(() => storage.remove(key.value)).thenAnswer((final _) async => true);
option = ToggleOption(

4
packages/neon/neon/test/options_collection_test.dart

@ -5,10 +5,10 @@ import 'package:neon/src/settings/models/storage.dart';
import 'package:test/test.dart';
// ignore: missing_override_of_must_be_overridden
class OptionMock extends Mock implements Option {}
class OptionMock extends Mock implements Option<Object> {}
class Collection extends NextcloudAppOptions {
Collection(final List<Option> options) : super(const AppStorage(StorageKeys.apps)) {
Collection(final List<Option<Object>> options) : super(const AppStorage(StorageKeys.apps)) {
super.options = options;
}
}

2
packages/neon/neon/test/settings_export_test.dart

@ -4,7 +4,7 @@ import 'dart:convert';
import 'dart:typed_data';
import 'package:mocktail/mocktail.dart';
import 'package:neon/blocs.dart';
import 'package:neon/src/blocs/accounts.dart';
import 'package:neon/src/models/account.dart';
import 'package:neon/src/models/app_implementation.dart';
import 'package:neon/src/settings/models/exportable.dart';

6
packages/neon/neon_files/lib/blocs/browser.dart

@ -1,12 +1,12 @@
part of '../neon_files.dart';
abstract class FilesBrowserBlocEvents {
abstract interface class FilesBrowserBlocEvents {
void setPath(final List<String> path);
void createFolder(final List<String> path);
}
abstract class FilesBrowserBlocStates {
abstract interface class FilesBrowserBlocStates {
BehaviorSubject<Result<List<WebDavFile>>> get files;
BehaviorSubject<List<String>> get path;
@ -37,7 +37,7 @@ class FilesBrowserBloc extends InteractiveBloc implements FilesBrowserBlocEvents
BehaviorSubject<List<String>> path = BehaviorSubject<List<String>>.seeded([]);
@override
Future refresh() async {
Future<void> refresh() async {
await RequestManager.instance.wrapWebDav<List<WebDavFile>>(
account.id,
'files-${path.value.join('/')}',

8
packages/neon/neon_files/lib/blocs/files.dart

@ -1,6 +1,6 @@
part of '../neon_files.dart';
abstract class FilesBlocEvents {
abstract interface class FilesBlocEvents {
void uploadFile(final List<String> path, final String localPath);
void syncFile(final List<String> path);
@ -20,7 +20,7 @@ abstract class FilesBlocEvents {
void removeFavorite(final List<String> path);
}
abstract class FilesBlocStates {
abstract interface class FilesBlocStates {
BehaviorSubject<List<FilesTask>> get tasks;
}
@ -103,7 +103,7 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta
}
@override
Future refresh() async {
Future<void> refresh() async {
await browser.refresh();
}
@ -164,7 +164,7 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta
);
}
Future _downloadFile(
Future<void> _downloadFile(
final List<String> path,
final File file,
) async {

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

@ -15,7 +15,7 @@ class FilesChooseCreateDialog extends StatefulWidget {
}
class _FilesChooseCreateDialogState extends State<FilesChooseCreateDialog> {
Future uploadFromPick(final FileType type) async {
Future<void> uploadFromPick(final FileType type) async {
final result = await FilePicker.platform.pickFiles(
allowMultiple: true,
type: type,
@ -27,7 +27,7 @@ class _FilesChooseCreateDialogState extends State<FilesChooseCreateDialog> {
}
}
Future upload(final File file) async {
Future<void> upload(final File file) async {
final sizeWarning = widget.bloc.options.uploadSizeWarning.value;
if (sizeWarning != null) {
final stat = file.statSync();

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

@ -52,7 +52,7 @@ class FilesApp extends AppImplementation<FilesBloc, FilesAppSpecificOptions> {
final String id = AppIDs.files;
@override
final LocalizationsDelegate localizationsDelegate = AppLocalizations.delegate;
final LocalizationsDelegate<AppLocalizations> localizationsDelegate = AppLocalizations.delegate;
@override
final List<Locale> supportedLocales = AppLocalizations.supportedLocales;

2
packages/neon/neon_files/lib/options.dart

@ -86,7 +86,7 @@ class FilesAppSpecificOptions extends NextcloudAppOptions {
},
);
late final _sizeWarningValues = <int?, String Function(BuildContext)>{
late final _sizeWarningValues = <int?, LabelBuilder>{
null: (final context) => AppLocalizations.of(context).optionsSizeWarningDisabled,
for (final i in [
1,

4
packages/neon/neon_files/lib/utils/task.dart

@ -23,7 +23,7 @@ class FilesDownloadTask extends FilesTask {
required super.file,
});
Future execute(final NextcloudClient client) async {
Future<void> execute(final NextcloudClient client) async {
await client.webdav.getFile(
Uri(pathSegments: path),
file,
@ -43,7 +43,7 @@ class FilesUploadTask extends FilesTask {
FileStat? _stat;
FileStat get stat => _stat ??= file.statSync();
Future execute(final NextcloudClient client) async {
Future<void> execute(final NextcloudClient client) async {
await client.webdav.putFile(
file,
stat,

13
packages/neon/neon_files/lib/widgets/file_preview.dart

@ -41,9 +41,7 @@ class FilePreview extends StatelessWidget {
valueListenable: bloc.options.showPreviewsOption,
builder: (final context, final showPreviews, final child) {
if (showPreviews && (details.hasPreview ?? false)) {
final account = Provider.of<AccountsBloc>(context, listen: false).activeAccount.value!;
final preview = FilePreviewImage(
account: account,
file: details,
size: size,
);
@ -72,19 +70,17 @@ class FilePreview extends StatelessWidget {
}
}
class FilePreviewImage extends NeonCachedImage {
class FilePreviewImage extends NeonApiImage {
factory FilePreviewImage({
required final Account account,
required final FileDetails file,
required final Size size,
}) {
final width = size.width.toInt();
final height = size.height.toInt();
final path = file.path.join('/');
final cacheKey = '${account.id}-preview-$path-$width-$height';
final cacheKey = 'preview-$path-$width-$height';
return FilePreviewImage._(
account: account,
file: file,
size: size,
cacheKey: cacheKey,
@ -95,15 +91,14 @@ class FilePreviewImage extends NeonCachedImage {
}
FilePreviewImage._({
required final Account account,
required final FileDetails file,
required Size super.size,
required super.cacheKey,
required final String path,
required final int width,
required final int height,
}) : super.custom(
getImage: () async => account.client.core.preview.getPreview(
}) : super(
getImage: (final client) async => client.core.preview.getPreview(
file: path,
x: width,
y: height,

8
packages/neon/neon_news/lib/blocs/article.dart

@ -1,6 +1,6 @@
part of '../neon_news.dart';
abstract class NewsArticleBlocEvents {
abstract interface class NewsArticleBlocEvents {
void markArticleAsRead();
void markArticleAsUnread();
@ -10,7 +10,7 @@ abstract class NewsArticleBlocEvents {
void unstarArticle();
}
abstract class NewsArticleBlocStates {
abstract interface class NewsArticleBlocStates {
BehaviorSubject<bool> get unread;
BehaviorSubject<bool> get starred;
@ -44,7 +44,7 @@ class NewsArticleBloc extends InteractiveBloc implements NewsArticleBlocEvents,
BehaviorSubject<bool> unread = BehaviorSubject<bool>();
@override
Future refresh() async {}
Future<void> refresh() async {}
@override
void markArticleAsRead() {
@ -78,7 +78,7 @@ class NewsArticleBloc extends InteractiveBloc implements NewsArticleBlocEvents,
});
}
void _wrapArticleAction(final Future Function() call) => wrapAction(
void _wrapArticleAction(final AsyncCallback call) => wrapAction(
call,
refresh: () async {
await _newsArticlesBloc.refresh();

8
packages/neon/neon_news/lib/blocs/articles.dart

@ -11,7 +11,7 @@ enum ListType {
folder,
}
abstract class NewsArticlesBlocEvents {
abstract interface class NewsArticlesBlocEvents {
void setFilterType(final FilterType type);
void markArticleAsRead(final NewsArticle article);
@ -23,7 +23,7 @@ abstract class NewsArticlesBlocEvents {
void unstarArticle(final NewsArticle article);
}
abstract class NewsArticlesBlocStates {
abstract interface class NewsArticlesBlocStates {
BehaviorSubject<Result<List<NewsArticle>>> get articles;
BehaviorSubject<FilterType> get filterType;
@ -75,14 +75,14 @@ class NewsArticlesBloc extends InteractiveBloc implements NewsArticlesBlocEvents
BehaviorSubject<FilterType> filterType = BehaviorSubject<FilterType>();
@override
Future refresh() async {
Future<void> refresh() async {
if (this is! NewsMainArticlesBloc) {
await reload();
}
await _newsBloc.refresh();
}
Future reload() async {
Future<void> reload() async {
// The API for pagination is pretty useless in this case sadly. So no pagination for us :(
// https://github.com/nextcloud/news/blob/master/docs/api/api-v1-2.md#get-items

8
packages/neon/neon_news/lib/blocs/news.dart

@ -1,6 +1,6 @@
part of '../neon_news.dart';
abstract class NewsBlocEvents {
abstract interface class NewsBlocEvents {
void addFeed(final String url, final int? folderId);
void removeFeed(final int feedId);
@ -20,7 +20,7 @@ abstract class NewsBlocEvents {
void markFolderAsRead(final int folderId);
}
abstract class NewsBlocStates {
abstract interface class NewsBlocStates {
BehaviorSubject<Result<List<NewsFolder>>> get folders;
BehaviorSubject<Result<List<NewsFeed>>> get feeds;
@ -90,7 +90,7 @@ class NewsBloc extends InteractiveBloc implements NewsBlocEvents, NewsBlocStates
late BehaviorSubject<FilterType> filterType = mainArticlesBloc.filterType;
@override
Future refresh() async {
Future<void> refresh() async {
await Future.wait([
RequestManager.instance.wrapNextcloud<List<NewsFolder>, NewsListFolders>(
account.id,
@ -187,7 +187,7 @@ class NewsBloc extends InteractiveBloc implements NewsBlocEvents, NewsBlocStates
}
@override
Future reload() async {
Future<void> reload() async {
await mainArticlesBloc.reload();
}
}

3
packages/neon/neon_news/lib/neon_news.dart

@ -2,6 +2,7 @@ library neon_news;
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_html/flutter_html.dart';
@ -59,7 +60,7 @@ class NewsApp extends AppImplementation<NewsBloc, NewsAppSpecificOptions> {
final String id = AppIDs.news;
@override
final LocalizationsDelegate localizationsDelegate = AppLocalizations.delegate;
final LocalizationsDelegate<AppLocalizations> localizationsDelegate = AppLocalizations.delegate;
@override
final List<Locale> supportedLocales = AppLocalizations.supportedLocales;

2
packages/neon/neon_news/lib/pages/article.dart

@ -63,7 +63,7 @@ class _NewsArticlePageState extends State<NewsArticlePage> {
super.dispose();
}
Future _startMarkAsReadTimer() async {
Future<void> _startMarkAsReadTimer() async {
if (await widget.bloc.unread.first) {
if (widget.articlesBloc.options.articleDisableMarkAsReadTimeoutOption.value) {
widget.bloc.markArticleAsRead();

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

@ -9,7 +9,7 @@ class NewsFolderSelect extends StatelessWidget {
});
final List<NewsFolder> folders;
final void Function(NewsFolder?) onChanged;
final ValueChanged<NewsFolder?> onChanged;
final NewsFolder? value;
@override

6
packages/neon/neon_notes/lib/blocs/note.dart

@ -1,6 +1,6 @@
part of '../neon_notes.dart';
abstract class NotesNoteBlocEvents {
abstract interface class NotesNoteBlocEvents {
void updateContent(final String content);
void updateTitle(final String title);
@ -8,7 +8,7 @@ abstract class NotesNoteBlocEvents {
void updateCategory(final String category);
}
abstract class NotesNoteBlocStates {
abstract interface class NotesNoteBlocStates {
BehaviorSubject<String> get category;
}
@ -62,7 +62,7 @@ class NotesNoteBloc extends InteractiveBloc implements NotesNoteBlocEvents, Note
BehaviorSubject<String> category = BehaviorSubject<String>();
@override
Future refresh() async {}
Future<void> refresh() async {}
@override
void updateCategory(final String category) {

8
packages/neon/neon_notes/lib/blocs/notes.dart

@ -1,6 +1,6 @@
part of '../neon_notes.dart';
abstract class NotesBlocEvents {
abstract interface class NotesBlocEvents {
void createNote({
final String title = '',
final String category = '',
@ -18,7 +18,7 @@ abstract class NotesBlocEvents {
void deleteNote(final int id);
}
abstract class NotesBlocStates {
abstract interface class NotesBlocStates {
BehaviorSubject<Result<List<NotesNote>>> get notes;
}
@ -43,8 +43,8 @@ class NotesBloc extends InteractiveBloc implements NotesBlocEvents, NotesBlocSta
BehaviorSubject<Result<List<NotesNote>>> notes = BehaviorSubject<Result<List<NotesNote>>>();
@override
Future refresh() async {
await RequestManager.instance.wrapNextcloud<List<NotesNote>, BuiltList>(
Future<void> refresh() async {
await RequestManager.instance.wrapNextcloud<List<NotesNote>, BuiltList<NotesNote>>(
account.id,
'notes-notes',
notes,

2
packages/neon/neon_notes/lib/neon_notes.dart

@ -52,7 +52,7 @@ class NotesApp extends AppImplementation<NotesBloc, NotesAppSpecificOptions> {
final List<Locale> supportedLocales = AppLocalizations.supportedLocales;
@override
final LocalizationsDelegate localizationsDelegate = AppLocalizations.delegate;
final LocalizationsDelegate<AppLocalizations> localizationsDelegate = AppLocalizations.delegate;
@override
late final NotesAppSpecificOptions options = NotesAppSpecificOptions(storage);

2
packages/neon/neon_notes/lib/widgets/category_select.dart

@ -22,7 +22,7 @@ class NotesCategorySelect extends StatelessWidget {
final List<String> categories;
final String? initialValue;
final Function(String category) onChanged;
final Function() onSubmitted;
final VoidCallback onSubmitted;
@override
Widget build(final BuildContext context) => Autocomplete<String>(

2
packages/neon/neon_notifications/lib/blocs/notifications.dart

@ -49,7 +49,7 @@ class NotificationsBloc extends InteractiveBloc
BehaviorSubject<int> unreadCounter = BehaviorSubject<int>();
@override
Future refresh() async {
Future<void> refresh() async {
await RequestManager.instance
.wrapNextcloud<List<NotificationsNotification>, NotificationsEndpointListNotificationsResponseApplicationJson>(
_account.id,

2
packages/neon/neon_notifications/lib/neon_notifications.dart

@ -29,7 +29,7 @@ class NotificationsApp extends AppImplementation<NotificationsBloc, Notification
final String id = AppIDs.notifications;
@override
final LocalizationsDelegate localizationsDelegate = AppLocalizations.delegate;
final LocalizationsDelegate<AppLocalizations> localizationsDelegate = AppLocalizations.delegate;
@override
final List<Locale> supportedLocales = AppLocalizations.supportedLocales;

1
packages/neon/neon_notifications/lib/pages/main.dart

@ -89,6 +89,7 @@ class _NotificationsMainPageState extends State<NotificationsMainPage> {
return;
}
if (app != null) {
// TODO: use go_router once implemented
final accountsBloc = Provider.of<AccountsBloc>(context, listen: false);
await accountsBloc.activeAppsBloc.setActiveApp(app.id);
} else {

1
packages/neon_lints/lib/src/base.yaml

@ -1,6 +1,7 @@
analyzer:
language:
strict-casts: true
strict-raw-types: true
errors:
flutter_style_todos: ignore
todo: ignore

1
packages/neon_lints/lint_maker.yaml

@ -19,6 +19,7 @@ dart:
analyzer:
language:
strict-casts: true
strict-raw-types: true
errors:
flutter_style_todos: ignore
todo: ignore

22
packages/nextcloud/lib/src/api/news.openapi.dart

@ -154,7 +154,7 @@ class NewsClient extends DynamiteClient {
throw await NewsApiException.fromResponse(response); // coverage:ignore-line
}
Future renameFolder({
Future<void> renameFolder({
required final int folderId,
required final String name,
}) async {
@ -183,7 +183,7 @@ class NewsClient extends DynamiteClient {
throw await NewsApiException.fromResponse(response); // coverage:ignore-line
}
Future deleteFolder({required final int folderId}) async {
Future<void> deleteFolder({required final int folderId}) async {
var path = '/index.php/apps/news/api/v1-3/folders/{folderId}';
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
@ -208,7 +208,7 @@ class NewsClient extends DynamiteClient {
throw await NewsApiException.fromResponse(response); // coverage:ignore-line
}
Future markFolderAsRead({
Future<void> markFolderAsRead({
required final int folderId,
required final int newestItemId,
}) async {
@ -298,7 +298,7 @@ class NewsClient extends DynamiteClient {
throw await NewsApiException.fromResponse(response); // coverage:ignore-line
}
Future deleteFeed({required final int feedId}) async {
Future<void> deleteFeed({required final int feedId}) async {
var path = '/index.php/apps/news/api/v1-3/feeds/{feedId}';
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
@ -323,7 +323,7 @@ class NewsClient extends DynamiteClient {
throw await NewsApiException.fromResponse(response); // coverage:ignore-line
}
Future moveFeed({
Future<void> moveFeed({
required final int feedId,
final int? folderId,
}) async {
@ -354,7 +354,7 @@ class NewsClient extends DynamiteClient {
throw await NewsApiException.fromResponse(response); // coverage:ignore-line
}
Future renameFeed({
Future<void> renameFeed({
required final int feedId,
required final String feedTitle,
}) async {
@ -383,7 +383,7 @@ class NewsClient extends DynamiteClient {
throw await NewsApiException.fromResponse(response); // coverage:ignore-line
}
Future markFeedAsRead({
Future<void> markFeedAsRead({
required final int feedId,
required final int newestItemId,
}) async {
@ -504,7 +504,7 @@ class NewsClient extends DynamiteClient {
throw await NewsApiException.fromResponse(response); // coverage:ignore-line
}
Future markArticleAsRead({required final int itemId}) async {
Future<void> markArticleAsRead({required final int itemId}) async {
var path = '/index.php/apps/news/api/v1-3/items/{itemId}/read';
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
@ -529,7 +529,7 @@ class NewsClient extends DynamiteClient {
throw await NewsApiException.fromResponse(response); // coverage:ignore-line
}
Future markArticleAsUnread({required final int itemId}) async {
Future<void> markArticleAsUnread({required final int itemId}) async {
var path = '/index.php/apps/news/api/v1-3/items/{itemId}/unread';
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
@ -554,7 +554,7 @@ class NewsClient extends DynamiteClient {
throw await NewsApiException.fromResponse(response); // coverage:ignore-line
}
Future starArticle({required final int itemId}) async {
Future<void> starArticle({required final int itemId}) async {
var path = '/index.php/apps/news/api/v1-3/items/{itemId}/star';
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
@ -579,7 +579,7 @@ class NewsClient extends DynamiteClient {
throw await NewsApiException.fromResponse(response); // coverage:ignore-line
}
Future unstarArticle({required final int itemId}) async {
Future<void> unstarArticle({required final int itemId}) async {
var path = '/index.php/apps/news/api/v1-3/items/{itemId}/unstar';
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};

2
packages/nextcloud/lib/src/webdav/client.dart

@ -214,7 +214,7 @@ class WebDavClient {
);
/// Gets the content of the file at [path].
Future getFile(
Future<void> getFile(
final Uri path,
final File file, {
final Function(double progress)? onProgress,

2
packages/nextcloud/test/helper.dart

@ -20,7 +20,7 @@ class DockerContainer {
final int port;
Future runOccCommand(final List<String> args) async {
Future<void> runOccCommand(final List<String> args) async {
final result = await runExecutableArguments(
'docker',
[

2
packages/nextcloud/test/notifications_test.dart

@ -21,7 +21,7 @@ void main() {
});
tearDown(() => container.destroy());
Future sendTestNotification() async {
Future<void> sendTestNotification() async {
await client.notifications.api.generateNotification(
userId: 'admin',
shortMessage: '123',

4
packages/nextcloud/test/settings_test.dart

@ -4,11 +4,11 @@ import 'package:test/test.dart';
import 'helper.dart';
Future main() async {
Future<void> main() async {
await run(await getDockerImage());
}
Future run(final DockerImage image) async {
Future<void> run(final DockerImage image) async {
group('settings', () {
late DockerContainer container;
late TestNextcloudClient client;

2
packages/sort_box/lib/sort_box.dart

@ -1,5 +1,5 @@
/// Signature of a function returning a [Comparable].
typedef ComparableGetter<T> = Comparable Function(T);
typedef ComparableGetter<T> = Comparable<Object> Function(T);
/// Sorting Box to sort [List]s on multiple properties.
class SortBox<T extends Enum, R> {

Loading…
Cancel
Save