diff --git a/.gitignore b/.gitignore index aaa75598..cb14981b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /.dart_tool/ /pubspec.lock +packages/**/coverage # Melos reccomends not adding them to vcs but we need them as we don't use melos in CI # **/pubspec_overrides.yaml diff --git a/packages/neon/neon/lib/l10n/en.arb b/packages/neon/neon/lib/l10n/en.arb index 7e0d1c06..36557956 100644 --- a/packages/neon/neon/lib/l10n/en.arb +++ b/packages/neon/neon/lib/l10n/en.arb @@ -36,10 +36,10 @@ } }, "errorUnableToOpenFile": "Unable to open the file", - "errorUnsupportedVersion": "Sorry, this Nextcloud {name} version is not supported.", + "errorUnsupportedVersion": "Sorry, the version of the following apps on your Nextcloud instance are not supported. \n {unsupported} \n Please contact your administrator to resolve the issues.", "@errorUnsupportedVersion" : { "placeholders": { - "name": { + "unsupported": { "type": "String" } } diff --git a/packages/neon/neon/lib/l10n/localizations.dart b/packages/neon/neon/lib/l10n/localizations.dart index a93ace95..06cfaebe 100644 --- a/packages/neon/neon/lib/l10n/localizations.dart +++ b/packages/neon/neon/lib/l10n/localizations.dart @@ -194,8 +194,8 @@ abstract class AppLocalizations { /// No description provided for @errorUnsupportedVersion. /// /// In en, this message translates to: - /// **'Sorry, this Nextcloud {name} version is not supported.'** - String errorUnsupportedVersion(String name); + /// **'Sorry, the version of the following apps on your Nextcloud instance are not supported. \n {unsupported} \n Please contact your administrator to resolve the issues.'** + String errorUnsupportedVersion(String unsupported); /// No description provided for @errorEmptyField. /// diff --git a/packages/neon/neon/lib/l10n/localizations_en.dart b/packages/neon/neon/lib/l10n/localizations_en.dart index 4e8f59fc..12626573 100644 --- a/packages/neon/neon/lib/l10n/localizations_en.dart +++ b/packages/neon/neon/lib/l10n/localizations_en.dart @@ -79,8 +79,8 @@ class AppLocalizationsEn extends AppLocalizations { String get errorUnableToOpenFile => 'Unable to open the file'; @override - String errorUnsupportedVersion(String name) { - return 'Sorry, this Nextcloud $name version is not supported.'; + String errorUnsupportedVersion(String unsupported) { + return 'Sorry, the version of the following apps on your Nextcloud instance are not supported. \n $unsupported \n Please contact your administrator to resolve the issues.'; } @override diff --git a/packages/neon/neon/lib/src/app.dart b/packages/neon/neon/lib/src/app.dart index ce8f91fa..115bc381 100644 --- a/packages/neon/neon/lib/src/app.dart +++ b/packages/neon/neon/lib/src/app.dart @@ -262,7 +262,7 @@ class _NeonAppState extends State with WidgetsBindingObserver, tray.Tra } FlutterNativeSplash.remove(); - return ResultBuilder( + return ResultBuilder.behaviorSubject( stream: activeAccountSnapshot.hasData ? _accountsBloc.getCapabilitiesBlocFor(activeAccountSnapshot.data!).capabilities : null, diff --git a/packages/neon/neon/lib/src/blocs/apps.dart b/packages/neon/neon/lib/src/blocs/apps.dart index ff0e5d7a..1f859b5f 100644 --- a/packages/neon/neon/lib/src/blocs/apps.dart +++ b/packages/neon/neon/lib/src/blocs/apps.dart @@ -16,6 +16,8 @@ abstract class AppsBlocStates { BehaviorSubject get activeAppID; BehaviorSubject get openNotifications; + + BehaviorSubject?> get appVersions; } class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates { @@ -50,6 +52,8 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates } }), ); + + unawaited(_checkCompatibility()); } }); @@ -59,11 +63,52 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates (final data) => data.capabilities.notifications != null ? _findAppImplementation('notifications') : null, ), ); + + unawaited(_checkCompatibility()); }); unawaited(refresh()); } + Future _checkCompatibility() async { + final apps = appImplementations.valueOrNull; + final capabilities = _capabilitiesBloc.capabilities.valueOrNull; + + // ignore cached data + if (capabilities == null || apps == null || !capabilities.hasUncachedData || !apps.hasUncachedData) { + return; + } + + final appIds = { + 'core', + ...apps.requireData.map((final a) => a.id), + }; + + final notSupported = <(String, Object?)>[]; + + for (final id in appIds) { + try { + final (supported, minVersion) = switch (id) { + 'core' => await _account.client.core.isSupported(capabilities.requireData), + 'news' => await _account.client.news.isSupported(), + 'notes' => await _account.client.notes.isSupported(capabilities.requireData), + _ => (true, null), + }; + + if (!supported) { + notSupported.add((id, minVersion)); + } + } catch (e, s) { + debugPrint(e.toString()); + debugPrint(s.toString()); + } + } + + if (notSupported.isNotEmpty) { + appVersions.add(notSupported); + } + } + T? _findAppImplementation(final String id) { final matches = _filteredAppImplementations([id]); if (matches.isNotEmpty) { @@ -89,6 +134,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates unawaited(notificationsAppImplementation.close()); unawaited(activeAppID.close()); unawaited(openNotifications.close()); + unawaited(appVersions.close()); for (final app in _allAppImplementations) { for (final bloc in app.blocs.values) { @@ -114,6 +160,9 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates @override BehaviorSubject openNotifications = BehaviorSubject(); + @override + BehaviorSubject?> appVersions = BehaviorSubject(); + @override Future refresh() async { await _requestManager.wrapNextcloud, NextcloudCoreNavigationApps>( diff --git a/packages/neon/neon/lib/src/pages/account_settings.dart b/packages/neon/neon/lib/src/pages/account_settings.dart index 59274770..b35deda0 100644 --- a/packages/neon/neon/lib/src/pages/account_settings.dart +++ b/packages/neon/neon/lib/src/pages/account_settings.dart @@ -60,7 +60,7 @@ class AccountSettingsPage extends StatelessWidget { ), ], ), - body: ResultBuilder( + body: ResultBuilder.behaviorSubject( stream: _userDetailsBloc.userDetails, builder: (final context, final userDetails) => SettingsList( categories: [ @@ -92,7 +92,7 @@ class AccountSettingsPage extends StatelessWidget { onRetry: _userDetailsBloc.refresh, ), NeonLinearProgressIndicator( - visible: userDetails.loading, + visible: userDetails.isLoading, ), ], ), diff --git a/packages/neon/neon/lib/src/pages/home.dart b/packages/neon/neon/lib/src/pages/home.dart index 9eef37df..6758aeb5 100644 --- a/packages/neon/neon/lib/src/pages/home.dart +++ b/packages/neon/neon/lib/src/pages/home.dart @@ -42,45 +42,26 @@ class _HomePageState extends State { } }); - _capabilitiesBloc.capabilities.listen((final result) async { - if (result.data != null) { - // ignore cached version and prevent duplicate dialogs - if (result.cached) { - return; + _appsBloc.appVersions.listen((final values) { + if (values == null || !mounted) { + return; + } + + final l10n = AppLocalizations.of(context); + + final buffer = StringBuffer()..writeln(); + + for (final error in values) { + final (appId, minVersion) = error; + final appName = l10n.appImplementationName(appId); + + if (appName.isNotEmpty && minVersion != null) { + buffer.writeln('- $appName $minVersion'); } - _appsBloc.appImplementations.listen((final appsResult) async { - // ignore cached version and prevent duplicate dialogs - if (appsResult.data == null || appsResult.cached) { - return; - } - for (final id in [ - 'core', - ...appsResult.data!.map((final a) => a.id), - ]) { - try { - final (supported, _) = switch (id) { - 'core' => await _account.client.core.isSupported(result.data), - 'news' => await _account.client.news.isSupported(), - 'notes' => await _account.client.notes.isSupported(result.data), - _ => (true, null), - }; - if (supported || !mounted) { - return; - } - var name = AppLocalizations.of(context).appImplementationName(id); - if (name == '') { - name = id; - } - await _showProblem( - AppLocalizations.of(context).errorUnsupportedVersion(name), - ); - } catch (e, s) { - debugPrint(e.toString()); - debugPrint(s.toString()); - } - } - }); } + + final message = l10n.errorUnsupportedVersion(buffer.toString()); + unawaited(_showProblem(message)); }); GlobalPopups().register(context); @@ -168,11 +149,12 @@ class _HomePageState extends State { } @override - Widget build(final BuildContext context) => ResultBuilder( + Widget build(final BuildContext context) => ResultBuilder.behaviorSubject( stream: _capabilitiesBloc.capabilities, - builder: (final context, final capabilities) => ResultBuilder>( + builder: (final context, final capabilities) => ResultBuilder>.behaviorSubject( stream: _appsBloc.appImplementations, - builder: (final context, final appImplementations) => ResultBuilder( + builder: (final context, final appImplementations) => + ResultBuilder.behaviorSubject( stream: _appsBloc.notificationsAppImplementation, builder: (final context, final notificationsAppImplementation) => StreamBuilder( stream: _appsBloc.activeAppID, @@ -204,7 +186,7 @@ class _HomePageState extends State { ), ), ], - if (appImplementations.error != null) ...[ + if (appImplementations.hasError) ...[ const SizedBox( width: 8, ), @@ -214,7 +196,7 @@ class _HomePageState extends State { onlyIcon: true, ), ], - if (appImplementations.loading) ...[ + if (appImplementations.isLoading) ...[ const SizedBox( width: 8, ), diff --git a/packages/neon/neon/lib/src/utils/request_manager.dart b/packages/neon/neon/lib/src/utils/request_manager.dart index 343bb70d..55f4c9e6 100644 --- a/packages/neon/neon/lib/src/utils/request_manager.dart +++ b/packages/neon/neon/lib/src/utils/request_manager.dart @@ -68,8 +68,8 @@ class RequestManager { Result( subject.value.data, null, - loading: true, - cached: true, + isLoading: true, + isCached: true, ), ); } else { @@ -148,8 +148,8 @@ class RequestManager { Result( cached, error, - loading: loading, - cached: true, + isLoading: loading, + isCached: true, ), ); return true; diff --git a/packages/neon/neon/lib/src/utils/result.dart b/packages/neon/neon/lib/src/utils/result.dart index 73ef6d61..c7955ae9 100644 --- a/packages/neon/neon/lib/src/utils/result.dart +++ b/packages/neon/neon/lib/src/utils/result.dart @@ -1,43 +1,71 @@ part of '../../neon.dart'; +@immutable class Result { - Result( + const Result( this.data, this.error, { - required this.loading, - required this.cached, + required this.isLoading, + required this.isCached, }); - factory Result.loading() => Result( + factory Result.loading() => const Result( null, null, - loading: true, - cached: false, + isLoading: true, + isCached: false, ); factory Result.success(final T data) => Result( data, null, - loading: false, - cached: false, + isLoading: false, + isCached: false, ); factory Result.error(final Object error) => Result( null, error, - loading: false, - cached: false, + isLoading: false, + isCached: false, ); final T? data; final Object? error; - final bool loading; - final bool cached; + final bool isLoading; + final bool isCached; Result transform(final R? Function(T data) call) => Result( data != null ? call(data as T) : null, error, - loading: loading, - cached: cached, + isLoading: isLoading, + isCached: isCached, ); + + Result asLoading() => Result( + data, + error, + isLoading: true, + isCached: isCached, + ); + + bool get hasError => error != null; + + bool get hasData => data != null; + bool get hasUncachedData => hasData && !isCached; + + T get requireData { + if (hasData) { + return data!; + } + + throw StateError('Result has no data'); + } + + @override + bool operator ==(final Object other) => + other is Result && other.isLoading == isLoading && other.data == data && other.error == error; + + @override + int get hashCode => Object.hash(data, error, isLoading, isCached); } diff --git a/packages/neon/neon/lib/src/widgets/account_tile.dart b/packages/neon/neon/lib/src/widgets/account_tile.dart index 1c8ff4b1..7fa8d211 100644 --- a/packages/neon/neon/lib/src/widgets/account_tile.dart +++ b/packages/neon/neon/lib/src/widgets/account_tile.dart @@ -36,7 +36,7 @@ class NeonAccountTile extends StatelessWidget { leading: NeonUserAvatar( account: account, ), - title: ResultBuilder( + title: ResultBuilder.behaviorSubject( stream: userDetailsBloc.userDetails, builder: (final context, final userDetails) => Row( children: [ @@ -51,7 +51,7 @@ class NeonAccountTile extends StatelessWidget { ), ), ], - if (userDetails.loading) ...[ + if (userDetails.isLoading) ...[ const SizedBox( width: 5, ), @@ -61,7 +61,7 @@ class NeonAccountTile extends StatelessWidget { ), ), ], - if (userDetails.error != null) ...[ + if (userDetails.hasError) ...[ const SizedBox( width: 5, ), diff --git a/packages/neon/neon/lib/src/widgets/drawer.dart b/packages/neon/neon/lib/src/widgets/drawer.dart index 8cb8cec0..a5e020ee 100644 --- a/packages/neon/neon/lib/src/widgets/drawer.dart +++ b/packages/neon/neon/lib/src/widgets/drawer.dart @@ -166,7 +166,7 @@ class NeonDrawerHeader extends StatelessWidget { }, ); - return ResultBuilder( + return ResultBuilder.behaviorSubject( stream: capabilitiesBloc.capabilities, builder: (final context, final capabilities) => DrawerHeader( decoration: BoxDecoration( @@ -198,7 +198,7 @@ class NeonDrawerHeader extends StatelessWidget { onRetry: capabilitiesBloc.refresh, ), NeonLinearProgressIndicator( - visible: capabilities.loading, + visible: capabilities.isLoading, ), ], accountSelecor, diff --git a/packages/neon/neon/lib/src/widgets/result_builder.dart b/packages/neon/neon/lib/src/widgets/result_builder.dart index d527287f..ae6954a3 100644 --- a/packages/neon/neon/lib/src/widgets/result_builder.dart +++ b/packages/neon/neon/lib/src/widgets/result_builder.dart @@ -1,28 +1,51 @@ part of '../../neon.dart'; -class ResultBuilder extends StatelessWidget { +typedef ResultWidgetBuilder = Widget Function(BuildContext context, Result snapshot); + +class ResultBuilder extends StreamBuilderBase, Result> { const ResultBuilder({ - required this.stream, required this.builder, + this.initialData, + super.stream, super.key, }); - final Stream?>? stream; + ResultBuilder.behaviorSubject({ + required this.builder, + BehaviorSubject>? super.stream, + super.key, + }) : initialData = stream?.valueOrNull; + + final ResultWidgetBuilder builder; + final Result? initialData; + + @override + Result initial() => initialData?.asLoading() ?? Result.loading(); + + @override + Result afterData(final Result current, final Result data) { + // prevent rebuild when only the cache state cahnges + if (current == data) { + return current; + } + + return data; + } + + @override + Result afterError(final Result current, final Object error, final StackTrace stackTrace) { + if (current.hasError) { + return current; + } - final Widget Function(BuildContext, Result) builder; + return Result( + current.data, + error, + isLoading: false, + isCached: false, + ); + } @override - Widget build(final BuildContext context) => StreamBuilder( - stream: stream, - builder: (final context, final snapshot) { - if (snapshot.hasError) { - return builder(context, Result.error(snapshot.error!)); - } - if (snapshot.hasData) { - return builder(context, snapshot.data!); - } - - return builder(context, Result.loading()); - }, - ); + Widget build(final BuildContext context, final Result currentSummary) => builder(context, currentSummary); } diff --git a/packages/neon/neon/lib/src/widgets/user_avatar.dart b/packages/neon/neon/lib/src/widgets/user_avatar.dart index 027d04af..e8d3a205 100644 --- a/packages/neon/neon/lib/src/widgets/user_avatar.dart +++ b/packages/neon/neon/lib/src/widgets/user_avatar.dart @@ -74,7 +74,7 @@ class _UserAvatarState extends State { ), if (widget.showStatus) ...[ ResultBuilder( - stream: _userStatusBloc.statuses.map((final statuses) => statuses[widget.username]), + stream: _userStatusBloc.statuses.mapNotNull((final statuses) => statuses[widget.username]), builder: _userStatusIconBuilder, ), ], @@ -89,12 +89,12 @@ class _UserAvatarState extends State { Widget? child; Decoration? decoration; - if (result.loading) { + if (result.isLoading) { child = CircularProgressIndicator( strokeWidth: 1.5, color: widget.foregroundColor ?? Theme.of(context).colorScheme.onPrimary, ); - } else if (result.error != null) { + } else if (result.hasError) { child = Icon( Icons.error_outline, size: scaledSize, diff --git a/packages/neon/neon/test/result_test.dart b/packages/neon/neon/test/result_test.dart new file mode 100644 index 00000000..e3a9d9de --- /dev/null +++ b/packages/neon/neon/test/result_test.dart @@ -0,0 +1,84 @@ +import 'package:neon/neon.dart'; +import 'package:test/test.dart'; + +void main() { + group('Result', () { + test('Equality', () { + const data = 'someData'; + + const a = Result( + data, + null, + isLoading: true, + isCached: false, + ); + const b = Result( + data, + null, + isLoading: true, + isCached: true, + ); + + expect(a, equals(a), reason: 'identical'); + expect(a, equals(b), reason: 'ignore cached state in equality'); + + expect(a.hashCode, equals(a.hashCode), reason: 'identical'); + expect(a.hashCode, isNot(equals(b.hashCode)), reason: 'hashcode should respect the cached state'); + }); + + test('Transform to loading', () { + const data = 'someData'; + + final a = Result.success(data); + const b = Result( + data, + null, + isLoading: true, + isCached: false, + ); + + expect(a, isNot(equals(b))); + expect(a.asLoading(), equals(b)); + }); + + test('data check', () { + const data = 'someData'; + + final a = Result.loading(); + final b = Result.success(data); + const c = Result( + data, + null, + isLoading: false, + isCached: true, + ); + + expect(a.hasData, false); + expect(b.hasData, true); + + expect(() => a.requireData, throwsStateError); + expect(b.requireData, equals(data)); + + expect(b.hasUncachedData, true); + expect(c.hasUncachedData, false); + }); + + test('error check', () { + const error = 'someError'; + + final a = Result.error(error); + + expect(a.hasError, true); + }); + + test('transform', () { + const data = 1; + + final a = Result.success(data); + + String transformer(final int data) => data.toString(); + + expect(a.transform(transformer), equals(Result.success(data.toString()))); + }); + }); +} diff --git a/packages/neon/neon_files/lib/widgets/browser_view.dart b/packages/neon/neon_files/lib/widgets/browser_view.dart index 28b972dc..49fbc2df 100644 --- a/packages/neon/neon_files/lib/widgets/browser_view.dart +++ b/packages/neon/neon_files/lib/widgets/browser_view.dart @@ -32,7 +32,7 @@ class _FilesBrowserViewState extends State { } @override - Widget build(final BuildContext context) => ResultBuilder>( + Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( stream: widget.bloc.files, builder: (final context, final files) => StreamBuilder>( stream: widget.bloc.path, @@ -136,7 +136,7 @@ class _FilesBrowserViewState extends State { ], ], ], - isLoading: files.loading, + isLoading: files.isLoading, error: files.error, onRefresh: widget.bloc.refresh, builder: (final context, final widget) => widget, diff --git a/packages/neon/neon_news/lib/dialogs/add_feed.dart b/packages/neon/neon_news/lib/dialogs/add_feed.dart index 294b2188..63c0cda7 100644 --- a/packages/neon/neon_news/lib/dialogs/add_feed.dart +++ b/packages/neon/neon_news/lib/dialogs/add_feed.dart @@ -43,7 +43,7 @@ class _NewsAddFeedDialogState extends State { } @override - Widget build(final BuildContext context) => ResultBuilder>( + Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( stream: widget.bloc.folders, builder: (final context, final folders) => NeonDialog( title: Text(AppLocalizations.of(context).feedAdd), @@ -74,7 +74,7 @@ class _NewsAddFeedDialogState extends State { ), Center( child: NeonLinearProgressIndicator( - visible: folders.loading, + visible: folders.isLoading, ), ), if (folders.data != null) ...[ diff --git a/packages/neon/neon_news/lib/widgets/articles_view.dart b/packages/neon/neon_news/lib/widgets/articles_view.dart index 2518caea..49047bfb 100644 --- a/packages/neon/neon_news/lib/widgets/articles_view.dart +++ b/packages/neon/neon_news/lib/widgets/articles_view.dart @@ -25,9 +25,9 @@ class _NewsArticlesViewState extends State { } @override - Widget build(final BuildContext context) => ResultBuilder>( + Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( stream: widget.newsBloc.feeds, - builder: (final context, final feeds) => ResultBuilder>( + builder: (final context, final feeds) => ResultBuilder>.behaviorSubject( stream: widget.bloc.articles, builder: (final context, final articles) => SortBoxBuilder( sortBox: articlesSortBox, @@ -37,7 +37,7 @@ class _NewsArticlesViewState extends State { builder: (final context, final sorted) => NeonListView( scrollKey: 'news-articles', items: feeds.data == null ? null : sorted, - isLoading: articles.loading || feeds.loading, + isLoading: articles.isLoading || feeds.isLoading, error: articles.error ?? feeds.error, onRefresh: () async { await Future.wait([ diff --git a/packages/neon/neon_news/lib/widgets/feeds_view.dart b/packages/neon/neon_news/lib/widgets/feeds_view.dart index 7a6d942d..bff83a61 100644 --- a/packages/neon/neon_news/lib/widgets/feeds_view.dart +++ b/packages/neon/neon_news/lib/widgets/feeds_view.dart @@ -11,9 +11,9 @@ class NewsFeedsView extends StatelessWidget { final int? folderID; @override - Widget build(final BuildContext context) => ResultBuilder>( + Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( stream: bloc.folders, - builder: (final context, final folders) => ResultBuilder>( + builder: (final context, final folders) => ResultBuilder>.behaviorSubject( stream: bloc.feeds, builder: (final context, final feeds) => SortBoxBuilder( sortBox: feedsSortBox, @@ -26,7 +26,7 @@ class NewsFeedsView extends StatelessWidget { scrollKey: 'news-feeds', withFloatingActionButton: true, items: sorted, - isLoading: feeds.loading || folders.loading, + isLoading: feeds.isLoading || folders.isLoading, error: feeds.error ?? folders.error, onRefresh: bloc.refresh, builder: (final context, final feed) => _buildFeed( diff --git a/packages/neon/neon_news/lib/widgets/folders_view.dart b/packages/neon/neon_news/lib/widgets/folders_view.dart index 6839df97..47418cdc 100644 --- a/packages/neon/neon_news/lib/widgets/folders_view.dart +++ b/packages/neon/neon_news/lib/widgets/folders_view.dart @@ -9,9 +9,9 @@ class NewsFoldersView extends StatelessWidget { final NewsBloc bloc; @override - Widget build(final BuildContext context) => ResultBuilder>( + Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( stream: bloc.folders, - builder: (final context, final folders) => ResultBuilder>( + builder: (final context, final folders) => ResultBuilder>.behaviorSubject( stream: bloc.feeds, builder: (final context, final feeds) => SortBoxBuilder( sortBox: foldersSortBox, @@ -30,7 +30,7 @@ class NewsFoldersView extends StatelessWidget { scrollKey: 'news-folders', withFloatingActionButton: true, items: sorted, - isLoading: feeds.loading || folders.loading, + isLoading: feeds.isLoading || folders.isLoading, error: feeds.error ?? folders.error, onRefresh: bloc.refresh, builder: _buildFolder, diff --git a/packages/neon/neon_notes/lib/dialogs/create_note.dart b/packages/neon/neon_notes/lib/dialogs/create_note.dart index 36b1b9ce..715d56e0 100644 --- a/packages/neon/neon_notes/lib/dialogs/create_note.dart +++ b/packages/neon/neon_notes/lib/dialogs/create_note.dart @@ -26,7 +26,7 @@ class _NotesCreateNoteDialogState extends State { } @override - Widget build(final BuildContext context) => ResultBuilder>( + Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( stream: widget.bloc.notes, builder: (final context, final notes) => NeonDialog( title: Text(AppLocalizations.of(context).noteCreate), @@ -56,7 +56,7 @@ class _NotesCreateNoteDialogState extends State { ), Center( child: NeonLinearProgressIndicator( - visible: notes.loading, + visible: notes.isLoading, ), ), if (notes.data != null) ...[ diff --git a/packages/neon/neon_notes/lib/dialogs/select_category.dart b/packages/neon/neon_notes/lib/dialogs/select_category.dart index 57055644..85623fda 100644 --- a/packages/neon/neon_notes/lib/dialogs/select_category.dart +++ b/packages/neon/neon_notes/lib/dialogs/select_category.dart @@ -26,7 +26,7 @@ class _NotesSelectCategoryDialogState extends State { } @override - Widget build(final BuildContext context) => ResultBuilder>( + Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( stream: widget.bloc.notes, builder: (final context, final notes) => NeonDialog( title: Text(AppLocalizations.of(context).category), @@ -44,7 +44,7 @@ class _NotesSelectCategoryDialogState extends State { ), Center( child: NeonLinearProgressIndicator( - visible: notes.loading, + visible: notes.isLoading, ), ), if (notes.data != null) ...[ diff --git a/packages/neon/neon_notes/lib/widgets/categories_view.dart b/packages/neon/neon_notes/lib/widgets/categories_view.dart index bef23e35..a8dfbfc1 100644 --- a/packages/neon/neon_notes/lib/widgets/categories_view.dart +++ b/packages/neon/neon_notes/lib/widgets/categories_view.dart @@ -9,7 +9,7 @@ class NotesCategoriesView extends StatelessWidget { final NotesBloc bloc; @override - Widget build(final BuildContext context) => ResultBuilder>( + Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( stream: bloc.notes, builder: (final context, final notes) => SortBoxBuilder( sortBox: categoriesSortBox, @@ -28,7 +28,7 @@ class NotesCategoriesView extends StatelessWidget { builder: (final context, final sorted) => NeonListView( scrollKey: 'notes-categories', items: sorted, - isLoading: notes.loading, + isLoading: notes.isLoading, error: notes.error, onRefresh: bloc.refresh, builder: _buildCategory, diff --git a/packages/neon/neon_notes/lib/widgets/notes_view.dart b/packages/neon/neon_notes/lib/widgets/notes_view.dart index a014e28b..eaffc784 100644 --- a/packages/neon/neon_notes/lib/widgets/notes_view.dart +++ b/packages/neon/neon_notes/lib/widgets/notes_view.dart @@ -11,7 +11,7 @@ class NotesView extends StatelessWidget { final String? category; @override - Widget build(final BuildContext context) => ResultBuilder>( + Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( stream: bloc.notes, builder: (final context, final notes) => SortBoxBuilder( sortBox: notesSortBox, @@ -34,7 +34,7 @@ class NotesView extends StatelessWidget { ...?sortedFavorites, ...?sortedNonFavorites, ], - isLoading: notes.loading, + isLoading: notes.isLoading, error: notes.error, onRefresh: bloc.refresh, builder: _buildNote, diff --git a/packages/neon/neon_notifications/lib/pages/main.dart b/packages/neon/neon_notifications/lib/pages/main.dart index b1048fcd..bc1a0e00 100644 --- a/packages/neon/neon_notifications/lib/pages/main.dart +++ b/packages/neon/neon_notifications/lib/pages/main.dart @@ -24,7 +24,7 @@ class _NotificationsMainPageState extends State { } @override - Widget build(final BuildContext context) => ResultBuilder>( + Widget build(final BuildContext context) => ResultBuilder>.behaviorSubject( stream: bloc.notifications, builder: (final context, final notifications) => Scaffold( resizeToAvoidBottomInset: false, @@ -39,7 +39,7 @@ class _NotificationsMainPageState extends State { scrollKey: 'notifications-notifications', withFloatingActionButton: true, items: notifications.data, - isLoading: notifications.loading, + isLoading: notifications.isLoading, error: notifications.error, onRefresh: bloc.refresh, builder: _buildNotification,