Skip to content

Commit

Permalink
Merge pull request #1157 from nextcloud/refactor/nextcloud/version-check
Browse files Browse the repository at this point in the history
  • Loading branch information
provokateurin authored Nov 20, 2023
2 parents b207ded + 3c74993 commit 940678c
Show file tree
Hide file tree
Showing 17 changed files with 228 additions and 82 deletions.
18 changes: 9 additions & 9 deletions packages/neon/neon/lib/src/blocs/apps.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ abstract interface class AppsBlocStates {
BehaviorSubject<void> get openNotifications;

/// A collection of unsupported apps and their minimum required version.
BehaviorSubject<Map<String, String?>> get appVersions;
BehaviorSubject<Map<String, VersionCheck>> get appVersionChecks;
}

/// The Bloc responsible for managing the [AppImplementation]s.
Expand Down Expand Up @@ -131,12 +131,12 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates
return;
}

final notSupported = <String, String?>{};
final notSupported = <String, VersionCheck>{};

try {
final coreCheck = _account.client.core.isSupported(capabilities.requireData);
final coreCheck = _account.client.core.getVersionCheck(capabilities.requireData);
if (!coreCheck.isSupported) {
notSupported['core'] = coreCheck.minimumVersion.toString();
notSupported['core'] = coreCheck;
}
} catch (e, s) {
debugPrint(e.toString());
Expand All @@ -145,14 +145,14 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates

for (final app in apps.requireData) {
try {
final check = await app.isSupported(_account, capabilities.requireData);
final check = await app.getVersionCheck(_account, capabilities.requireData);

if (check == null) {
continue;
}

if (!check.isSupported) {
notSupported[app.id] = check.minimumVersion;
notSupported[app.id] = check;
}
} catch (e, s) {
debugPrint(e.toString());
Expand All @@ -161,7 +161,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates
}

if (notSupported.isNotEmpty) {
appVersions.add(notSupported);
appVersionChecks.add(notSupported);
}
}

Expand Down Expand Up @@ -190,7 +190,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates
unawaited(notificationsAppImplementation.close());
unawaited(activeApp.close());
unawaited(openNotifications.close());
unawaited(appVersions.close());
unawaited(appVersionChecks.close());

super.dispose();
}
Expand All @@ -209,7 +209,7 @@ class AppsBloc extends InteractiveBloc implements AppsBlocEvents, AppsBlocStates
BehaviorSubject<void> openNotifications = BehaviorSubject();

@override
BehaviorSubject<Map<String, String?>> appVersions = BehaviorSubject();
BehaviorSubject<Map<String, VersionCheck>> appVersionChecks = BehaviorSubject();

@override
Future<void> refresh() async {
Expand Down
4 changes: 2 additions & 2 deletions packages/neon/neon/lib/src/models/app_implementation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import 'package:neon/src/settings/models/storage.dart';
import 'package:neon/src/utils/provider.dart';
import 'package:neon/src/widgets/drawer_destination.dart';
import 'package:nextcloud/core.dart' as core;
import 'package:nextcloud/nextcloud.dart' show VersionSupported;
import 'package:nextcloud/nextcloud.dart' show VersionCheck;
import 'package:provider/provider.dart';
import 'package:rxdart/rxdart.dart';
import 'package:vector_graphics/vector_graphics.dart';
Expand Down Expand Up @@ -61,7 +61,7 @@ abstract class AppImplementation<T extends Bloc, R extends NextcloudAppOptions>
/// A value of `null` means that it can not be known if the app is supported.
/// This is the case for apps that depend on the server version like files and we assume that the app is supported.
/// The server support is handled differently.
FutureOr<VersionSupported<String>?> isSupported(
FutureOr<VersionCheck?> getVersionCheck(
final Account account,
final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities,
) =>
Expand Down
17 changes: 7 additions & 10 deletions packages/neon/neon/lib/src/pages/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'package:neon/src/widgets/drawer.dart';
import 'package:neon/src/widgets/error.dart';
import 'package:neon/src/widgets/unified_search_results.dart';
import 'package:nextcloud/core.dart' as core;
import 'package:nextcloud/nextcloud.dart';
import 'package:provider/provider.dart';

/// The home page of Neon.
Expand All @@ -35,7 +36,7 @@ class _HomePageState extends State<HomePage> {
late global_options.GlobalOptions _globalOptions;
late AccountsBloc _accountsBloc;
late AppsBloc _appsBloc;
late StreamSubscription<Map<String, String?>> _versionCheckSubscription;
late StreamSubscription<Map<String, VersionCheck>> _versionCheckSubscription;

@override
void initState() {
Expand All @@ -45,23 +46,19 @@ class _HomePageState extends State<HomePage> {
_account = _accountsBloc.activeAccount.value!;
_appsBloc = _accountsBloc.activeAppsBloc;

_versionCheckSubscription = _appsBloc.appVersions.listen((final values) {
_versionCheckSubscription = _appsBloc.appVersionChecks.listen((final values) {
if (!mounted) {
return;
}

final l10n = NeonLocalizations.of(context);

final buffer = StringBuffer()..writeln();

for (final error in values.entries) {
final appId = error.key;
final minVersion = error.value;
final appName = l10n.appImplementationName(appId);
for (final entry in values.entries) {
final versionCheck = entry.value;
final appName = l10n.appImplementationName(entry.key);

if (appName.isNotEmpty && minVersion != null) {
buffer.writeln('- $appName $minVersion');
}
buffer.writeln('- $appName >=${versionCheck.minimumVersion} <${versionCheck.maximumMajor + 1}.0.0');
}

final message = l10n.errorUnsupportedAppVersions(buffer.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ class _LoginCheckServerStatusPageState extends State<LoginCheckServerStatusPage>
child: ResultBuilder.behaviorSubject(
subject: bloc.state,
builder: (final context, final state) {
final success = state.hasData && state.requireData.isSupported && !state.requireData.maintenance;
final success =
state.hasData && state.requireData.versionCheck.isSupported && !state.requireData.maintenance;

return Column(
mainAxisAlignment: MainAxisAlignment.center,
Expand Down Expand Up @@ -121,7 +122,7 @@ class _LoginCheckServerStatusPageState extends State<LoginCheckServerStatusPage>
);
}

if (result.requireData.isSupported) {
if (result.requireData.versionCheck.isSupported) {
return NeonValidationTile(
title: NeonLocalizations.of(context).loginSupportedServerVersion(result.requireData.versionstring),
state: ValidationState.success,
Expand Down
1 change: 1 addition & 0 deletions packages/neon/neon/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ dependencies:
universal_io: ^2.0.0
url_launcher: ^6.1.0
vector_graphics: ^1.0.0
version: ^3.0.0
window_manager: ^0.3.0
xml: ^6.0.0

Expand Down
4 changes: 2 additions & 2 deletions packages/neon/neon_news/lib/neon_news.dart
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ class NewsApp extends AppImplementation<NewsBloc, NewsAppSpecificOptions> {
BehaviorSubject<int> getUnreadCounter(final NewsBloc bloc) => bloc.unreadCounter;

@override
Future<VersionSupported<String>> isSupported(
Future<VersionCheck> getVersionCheck(
final Account account,
final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities,
) =>
account.client.news.isSupported();
account.client.news.getVersionCheck();
}
8 changes: 3 additions & 5 deletions packages/neon/neon_notes/lib/neon_notes.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,9 @@ class NotesApp extends AppImplementation<NotesBloc, NotesAppSpecificOptions> {
final RouteBase route = $notesAppRoute;

@override
VersionSupported<String> isSupported(
VersionCheck getVersionCheck(
final Account account,
final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities,
) {
final result = account.client.notes.isSupported(capabilities);
return (isSupported: result.isSupported, minimumVersion: result.minimumVersion.toString());
}
) =>
account.client.notes.getVersionCheck(capabilities);
}
44 changes: 42 additions & 2 deletions packages/nextcloud/lib/src/helpers/common.dart
Original file line number Diff line number Diff line change
@@ -1,2 +1,42 @@
/// The result of a version check.
typedef VersionSupported<T> = ({bool isSupported, T minimumVersion});
import 'package:meta/meta.dart';
import 'package:version/version.dart';

/// Holds the [versions], [minimumVersion] and [maximumMajor] of an app.
@immutable
class VersionCheck {
/// Creates a new [VersionCheck].
///
/// If the [maximumMajor] is `null` the compatibility of the major of the [minimumVersion] is checked.
VersionCheck({
required this.versions,
required this.minimumVersion,
required final int? maximumMajor,
}) : maximumMajor = maximumMajor ?? minimumVersion.major;

/// Current version of the app.
final List<Version>? versions;

/// Minimum version of the app.
final Version minimumVersion;

/// Maximum major version of the app.
late final int maximumMajor;

/// Whether the [versions] is allowed by the [minimumVersion] and [maximumMajor].
///
/// If [versions] is `null` or empty it is assumed that the app is supported.
/// Only one of the [versions] has to be supported to return `true`.
bool get isSupported {
if (versions == null || versions!.isEmpty) {
return true;
}

for (final version in versions!) {
if (version >= minimumVersion && version.major <= maximumMajor) {
return true;
}
}

return false;
}
}
36 changes: 23 additions & 13 deletions packages/nextcloud/lib/src/helpers/core.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,36 @@

import 'package:nextcloud/src/api/core.openapi.dart' as core;
import 'package:nextcloud/src/helpers/common.dart';
import 'package:version/version.dart';

/// Version of core/Server supported
const supportedVersion = 27;
/// Minimum version of core/Server supported
final minVersion = Version(27, 0, 0);

extension CoreVersionSupported on core.Client {
extension CoreVersionCheck on core.Client {
/// Check if the core/Server version is supported by this client
///
/// Also returns the supported version number
VersionSupported<int> isSupported(
final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities,
) =>
(
isSupported: capabilities.version.major == supportedVersion,
minimumVersion: supportedVersion,
);
/// Also returns the minimum supported version
VersionCheck getVersionCheck(final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities) {
final version = Version(
capabilities.version.major,
capabilities.version.minor,
capabilities.version.micro,
);
return VersionCheck(
versions: [version],
minimumVersion: minVersion,
maximumMajor: null,
);
}
}

extension CoreStatusVersionSupported on core.Status {
extension CoreStatusVersionCheck on core.Status {
/// Check if the core/Server version is supported
bool get isSupported => version.startsWith('$supportedVersion.');
VersionCheck get versionCheck => VersionCheck(
versions: [Version.parse(version)],
minimumVersion: minVersion,
maximumMajor: null,
);
}

enum ShareType {
Expand Down
17 changes: 10 additions & 7 deletions packages/nextcloud/lib/src/helpers/news.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,22 @@

import 'package:nextcloud/src/api/news.openapi.dart' as news;
import 'package:nextcloud/src/helpers/common.dart';
import 'package:version/version.dart';

/// API version of the news app supported
const supportedVersion = 'v1-3';
/// Minimum API version of the news app supported
final minVersion = Version(1, 3, 0);

extension NewsVersionSupported on news.Client {
extension NewsVersionCheck on news.Client {
/// Check if the news app version is supported by this client
///
/// Also returns the supported API version number
Future<VersionSupported<String>> isSupported() async {
Future<VersionCheck> getVersionCheck() async {
final response = await getSupportedApiVersions();
return (
isSupported: response.body.apiLevels!.contains(supportedVersion),
minimumVersion: supportedVersion,
final versions = response.body.apiLevels;
return VersionCheck(
versions: versions?.map((final version) => Version.parse(version.substring(1).replaceAll('-', '.'))).toList(),
minimumVersion: minVersion,
maximumMajor: null,
);
}
}
Expand Down
25 changes: 11 additions & 14 deletions packages/nextcloud/lib/src/helpers/notes.dart
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
import 'package:collection/collection.dart';
import 'package:nextcloud/src/api/core.openapi.dart' as core;
import 'package:nextcloud/src/api/notes.openapi.dart' as notes;
import 'package:nextcloud/src/helpers/common.dart';
import 'package:version/version.dart';

/// API version of the notes app supported
const supportedVersion = 1;
/// Minimum API version of the notes app supported
final minVersion = Version(1, 3, 0);

// ignore: public_member_api_docs
extension NotesVersionSupported on notes.Client {
extension NotesVersionCheck on notes.Client {
/// Check if the notes app version is supported by this client
///
/// Also returns the supported API version number
VersionSupported<int> isSupported(
final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities,
) =>
(
isSupported: capabilities.capabilities.notesCapabilities?.notes.apiVersion
?.map(Version.parse)
.firstWhereOrNull((final version) => version.major == supportedVersion) !=
null,
minimumVersion: supportedVersion,
);
VersionCheck getVersionCheck(final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities) {
final versions = capabilities.capabilities.notesCapabilities?.notes.apiVersion;
return VersionCheck(
versions: versions?.map(Version.parse).toList(),
minimumVersion: minVersion,
maximumMajor: null,
);
}
}
15 changes: 8 additions & 7 deletions packages/nextcloud/lib/src/helpers/spreed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@ import 'package:nextcloud/src/api/core.openapi.dart' as core;
import 'package:nextcloud/src/api/spreed.openapi.dart' as spreed;
import 'package:version/version.dart';

/// The version of the spreed app that is supported.
const supportedVersion = 17;
/// The minimum version of the spreed app that is supported.
final minVersion = Version(17, 0, 0);

/// Extension for checking whether spreed is supported.
extension SpreedVersionSupported on spreed.Client {
extension SpreedVersionCheck on spreed.Client {
/// Checks whether the spreed app installed on the server is supported by this client.
///
/// Also returns the supported version number.
VersionSupported<int> isSupported(final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities) {
VersionCheck getVersionCheck(final core.OcsGetCapabilitiesResponseApplicationJson_Ocs_Data capabilities) {
final version = capabilities.capabilities.spreedPublicCapabilities?.spreedPublicCapabilities0?.spreed.version;
return (
isSupported: version != null && Version.parse(version).major == supportedVersion,
minimumVersion: supportedVersion,
return VersionCheck(
versions: version != null ? [Version.parse(version)] : null,
minimumVersion: minVersion,
maximumMajor: null,
);
}
}
Expand Down
Loading

0 comments on commit 940678c

Please sign in to comment.