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/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/home.dart b/packages/neon/neon/lib/src/pages/home.dart index eb4daba0..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.isCached) { - 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.isCached) { - 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); diff --git a/packages/neon/neon/lib/src/utils/result.dart b/packages/neon/neon/lib/src/utils/result.dart index 60dbd146..c7955ae9 100644 --- a/packages/neon/neon/lib/src/utils/result.dart +++ b/packages/neon/neon/lib/src/utils/result.dart @@ -51,6 +51,17 @@ class Result { 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;