From 2b177d76798202330054ac1285a24df18933363d Mon Sep 17 00:00:00 2001 From: Ming Ming Date: Thu, 11 May 2023 00:42:56 +0800 Subject: [PATCH] Don't try to query ncalbum when Nextcloud <25 --- app/lib/api/entity_converter.dart | 11 +++++ app/lib/bloc/home_search_suggestion.dart | 9 +++- app/lib/controller/account_controller.dart | 9 ++++ .../controller/collections_controller.dart | 5 +- app/lib/controller/server_controller.dart | 49 +++++++++++++++++++ app/lib/controller/server_controller.g.dart | 14 ++++++ app/lib/entity/server_status.dart | 24 +++++++++ app/lib/entity/server_status.g.dart | 14 ++++++ .../use_case/collection/list_collection.dart | 43 ++++++++++------ app/lib/widget/home.dart | 14 ++++++ app/lib/widget/home_search_suggestion.dart | 1 + np_api/lib/np_api.dart | 1 + np_api/lib/src/api.dart | 2 + np_api/lib/src/entity/entity.dart | 3 ++ np_api/lib/src/entity/status_parser.dart | 1 + np_api/test/entity/status_parser_test.dart | 34 +++++++++++++ 16 files changed, 215 insertions(+), 19 deletions(-) create mode 100644 app/lib/controller/server_controller.dart create mode 100644 app/lib/controller/server_controller.g.dart create mode 100644 app/lib/entity/server_status.dart create mode 100644 app/lib/entity/server_status.g.dart create mode 100644 np_api/test/entity/status_parser_test.dart diff --git a/app/lib/api/entity_converter.dart b/app/lib/api/entity_converter.dart index 52b21522..cb83fce3 100644 --- a/app/lib/api/entity_converter.dart +++ b/app/lib/api/entity_converter.dart @@ -7,6 +7,7 @@ import 'package:nc_photos/entity/file.dart'; import 'package:nc_photos/entity/nc_album.dart'; import 'package:nc_photos/entity/nc_album_item.dart'; import 'package:nc_photos/entity/person.dart'; +import 'package:nc_photos/entity/server_status.dart'; import 'package:nc_photos/entity/share.dart'; import 'package:nc_photos/entity/sharee.dart'; import 'package:nc_photos/entity/tag.dart'; @@ -179,6 +180,16 @@ class ApiShareeConverter { }; } +class ApiStatusConverter { + static ServerStatus fromApi(api.Status status) { + return ServerStatus( + versionRaw: status.version, + versionName: status.versionString, + productName: status.productName, + ); + } +} + class ApiTagConverter { static Tag fromApi(api.Tag tag) { return Tag( diff --git a/app/lib/bloc/home_search_suggestion.dart b/app/lib/bloc/home_search_suggestion.dart index 4792e86f..6575e447 100644 --- a/app/lib/bloc/home_search_suggestion.dart +++ b/app/lib/bloc/home_search_suggestion.dart @@ -5,6 +5,7 @@ import 'package:kiwi/kiwi.dart'; import 'package:logging/logging.dart'; import 'package:nc_photos/account.dart'; import 'package:nc_photos/controller/collections_controller.dart'; +import 'package:nc_photos/controller/server_controller.dart'; import 'package:nc_photos/di_container.dart'; import 'package:nc_photos/entity/collection.dart'; import 'package:nc_photos/entity/person.dart'; @@ -126,7 +127,8 @@ class HomeSearchSuggestionBlocFailure extends HomeSearchSuggestionBlocState { @npLog class HomeSearchSuggestionBloc extends Bloc { - HomeSearchSuggestionBloc(this.account, this.collectionsController) + HomeSearchSuggestionBloc( + this.account, this.collectionsController, this.serverController) : super(const HomeSearchSuggestionBlocInit()) { final c = KiwiContainer().resolve(); assert(require(c)); @@ -194,7 +196,9 @@ class HomeSearchSuggestionBloc .map((e) => e.collection) .toList(); if (collections.isEmpty) { - collections = await ListCollection(_c)(account).last; + collections = await ListCollection(_c, + serverController: serverController)(account) + .last; } product.addAll(collections.map(_CollectionSearcheable.new)); _log.info( @@ -247,6 +251,7 @@ class HomeSearchSuggestionBloc final Account account; final CollectionsController collectionsController; + final ServerController serverController; late final DiContainer _c; final _search = Woozy<_Searcheable>(limit: 10); diff --git a/app/lib/controller/account_controller.dart b/app/lib/controller/account_controller.dart index bad7aa10..3470340d 100644 --- a/app/lib/controller/account_controller.dart +++ b/app/lib/controller/account_controller.dart @@ -1,6 +1,7 @@ import 'package:kiwi/kiwi.dart'; import 'package:nc_photos/account.dart'; import 'package:nc_photos/controller/collections_controller.dart'; +import 'package:nc_photos/controller/server_controller.dart'; import 'package:nc_photos/di_container.dart'; class AccountController { @@ -8,6 +9,7 @@ class AccountController { _account = account; _collectionsController?.dispose(); _collectionsController = null; + _serverController = null; } Account get account => _account!; @@ -16,8 +18,15 @@ class AccountController { _collectionsController ??= CollectionsController( KiwiContainer().resolve(), account: _account!, + serverController: serverController, + ); + + ServerController get serverController => + _serverController ??= ServerController( + account: _account!, ); Account? _account; CollectionsController? _collectionsController; + ServerController? _serverController; } diff --git a/app/lib/controller/collections_controller.dart b/app/lib/controller/collections_controller.dart index 41f4467f..5bda97f2 100644 --- a/app/lib/controller/collections_controller.dart +++ b/app/lib/controller/collections_controller.dart @@ -6,6 +6,7 @@ import 'package:logging/logging.dart'; import 'package:mutex/mutex.dart'; import 'package:nc_photos/account.dart'; import 'package:nc_photos/controller/collection_items_controller.dart'; +import 'package:nc_photos/controller/server_controller.dart'; import 'package:nc_photos/di_container.dart'; import 'package:nc_photos/entity/collection.dart'; import 'package:nc_photos/entity/collection/util.dart'; @@ -64,6 +65,7 @@ class CollectionsController { CollectionsController( this._c, { required this.account, + required this.serverController, }); void dispose() { @@ -264,7 +266,7 @@ class CollectionsController { hasNext: false, ); final completer = Completer(); - ListCollection(_c)(account).listen( + ListCollection(_c, serverController: serverController)(account).listen( (c) { lastData = CollectionStreamEvent( data: _prepareDataFor(c), @@ -325,6 +327,7 @@ class CollectionsController { final DiContainer _c; final Account account; + final ServerController serverController; var _isDataStreamInited = false; final _dataStreamController = BehaviorSubject.seeded( diff --git a/app/lib/controller/server_controller.dart b/app/lib/controller/server_controller.dart new file mode 100644 index 00000000..9df55e28 --- /dev/null +++ b/app/lib/controller/server_controller.dart @@ -0,0 +1,49 @@ +import 'dart:async'; + +import 'package:logging/logging.dart'; +import 'package:nc_photos/account.dart'; +import 'package:nc_photos/api/entity_converter.dart'; +import 'package:nc_photos/entity/server_status.dart'; +import 'package:nc_photos/np_api_util.dart'; +import 'package:np_api/np_api.dart' as api; +import 'package:np_codegen/np_codegen.dart'; +import 'package:rxdart/rxdart.dart'; + +part 'server_controller.g.dart'; + +@npLog +class ServerController { + ServerController({ + required this.account, + }); + + ValueStream get status { + if (!_statusStreamContorller.hasValue) { + unawaited(_load()); + } + return _statusStreamContorller.stream; + } + + Future _load() => _getStatus(); + + Future _getStatus() async { + try { + final response = await ApiUtil.fromAccount(account).status().get(); + if (!response.isGood) { + _log.severe("[_getStatus] Failed requesting server: $response"); + return; + } + final apiStatus = await api.StatusParser().parse(response.body); + final status = ApiStatusConverter.fromApi(apiStatus); + _log.info("[_getStatus] Server status: $status"); + _statusStreamContorller.add(status); + } catch (e, stackTrace) { + _log.severe("[_getStatus] Failed while get", e, stackTrace); + return; + } + } + + final Account account; + + final _statusStreamContorller = BehaviorSubject(); +} diff --git a/app/lib/controller/server_controller.g.dart b/app/lib/controller/server_controller.g.dart new file mode 100644 index 00000000..a1bc8ed2 --- /dev/null +++ b/app/lib/controller/server_controller.g.dart @@ -0,0 +1,14 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'server_controller.dart'; + +// ************************************************************************** +// NpLogGenerator +// ************************************************************************** + +extension _$ServerControllerNpLog on ServerController { + // ignore: unused_element + Logger get _log => log; + + static final log = Logger("controller.server_controller.ServerController"); +} diff --git a/app/lib/entity/server_status.dart b/app/lib/entity/server_status.dart new file mode 100644 index 00000000..8e27ed81 --- /dev/null +++ b/app/lib/entity/server_status.dart @@ -0,0 +1,24 @@ +import 'package:to_string/to_string.dart'; + +part 'server_status.g.dart'; + +@toString +class ServerStatus { + const ServerStatus({ + required this.versionRaw, + required this.versionName, + required this.productName, + }); + + @override + String toString() => _$toString(); + + final String versionRaw; + final String versionName; + final String productName; +} + +extension ServerStatusExtension on ServerStatus { + List get versionNumber => versionRaw.split(".").map(int.parse).toList(); + int get majorVersion => versionNumber[0]; +} diff --git a/app/lib/entity/server_status.g.dart b/app/lib/entity/server_status.g.dart new file mode 100644 index 00000000..a5079609 --- /dev/null +++ b/app/lib/entity/server_status.g.dart @@ -0,0 +1,14 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'server_status.dart'; + +// ************************************************************************** +// ToStringGenerator +// ************************************************************************** + +extension _$ServerStatusToString on ServerStatus { + String _$toString() { + // ignore: unnecessary_string_interpolations + return "ServerStatus {versionRaw: $versionRaw, versionName: $versionName, productName: $productName}"; + } +} diff --git a/app/lib/use_case/collection/list_collection.dart b/app/lib/use_case/collection/list_collection.dart index 633730d2..d30702be 100644 --- a/app/lib/use_case/collection/list_collection.dart +++ b/app/lib/use_case/collection/list_collection.dart @@ -1,16 +1,21 @@ import 'dart:async'; import 'package:nc_photos/account.dart'; +import 'package:nc_photos/controller/server_controller.dart'; import 'package:nc_photos/di_container.dart'; import 'package:nc_photos/entity/album.dart'; import 'package:nc_photos/entity/collection.dart'; import 'package:nc_photos/entity/collection/builder.dart'; import 'package:nc_photos/entity/nc_album.dart'; +import 'package:nc_photos/entity/server_status.dart'; import 'package:nc_photos/use_case/album/list_album2.dart'; import 'package:nc_photos/use_case/nc_album/list_nc_album.dart'; class ListCollection { - ListCollection(this._c) : assert(require(_c)); + ListCollection( + this._c, { + required this.serverController, + }) : assert(require(_c)); static bool require(DiContainer c) => DiContainer.has(c, DiType.albumRepo2) && @@ -51,23 +56,29 @@ class ListCollection { onDone(); }, ); - ListNcAlbum(_c)(account).listen( - (event) { - ncAlbums = event; - notify(); - }, - onDone: () { - isNcAlbumDone = true; - onDone(); - }, - onError: (e, stackTrace) { - controller.addError(e, stackTrace); - isNcAlbumDone = true; - onDone(); - }, - ); + if (serverController.status.hasValue && + serverController.status.value.majorVersion < 25) { + isNcAlbumDone = true; + } else { + ListNcAlbum(_c)(account).listen( + (event) { + ncAlbums = event; + notify(); + }, + onDone: () { + isNcAlbumDone = true; + onDone(); + }, + onError: (e, stackTrace) { + controller.addError(e, stackTrace); + isNcAlbumDone = true; + onDone(); + }, + ); + } return controller.stream; } final DiContainer _c; + final ServerController serverController; } diff --git a/app/lib/widget/home.dart b/app/lib/widget/home.dart index 5a1a4638..f04b8457 100644 --- a/app/lib/widget/home.dart +++ b/app/lib/widget/home.dart @@ -1,9 +1,13 @@ +import 'dart:async'; + import 'package:event_bus/event_bus.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:kiwi/kiwi.dart'; import 'package:logging/logging.dart'; import 'package:nc_photos/account.dart'; import 'package:nc_photos/app_localizations.dart'; +import 'package:nc_photos/controller/account_controller.dart'; import 'package:nc_photos/di_container.dart'; import 'package:nc_photos/entity/album.dart'; import 'package:nc_photos/entity/album/data_source.dart'; @@ -64,6 +68,16 @@ class _HomeState extends State with TickerProviderStateMixin { }); } _animationController.value = 1; + + // call once to pre-cache the value + unawaited(context + .read() + .serverController + .status + .first + .then((value) { + _log.info("Server status: $value"); + })); } @override diff --git a/app/lib/widget/home_search_suggestion.dart b/app/lib/widget/home_search_suggestion.dart index 3ebfbb33..0daa5620 100644 --- a/app/lib/widget/home_search_suggestion.dart +++ b/app/lib/widget/home_search_suggestion.dart @@ -79,6 +79,7 @@ class _HomeSearchSuggestionState extends State _bloc = (widget.controller._bloc ??= HomeSearchSuggestionBloc( widget.account, context.read().collectionsController, + context.read().serverController, )); if (_bloc.state is! HomeSearchSuggestionBlocInit) { // process the current state diff --git a/np_api/lib/np_api.dart b/np_api/lib/np_api.dart index 5354a393..d3f7bf6e 100644 --- a/np_api/lib/np_api.dart +++ b/np_api/lib/np_api.dart @@ -8,6 +8,7 @@ export 'src/entity/nc_album_parser.dart'; export 'src/entity/person_parser.dart'; export 'src/entity/share_parser.dart'; export 'src/entity/sharee_parser.dart'; +export 'src/entity/status_parser.dart'; export 'src/entity/tag_parser.dart'; export 'src/entity/tagged_file_parser.dart'; export 'src/type.dart'; diff --git a/np_api/lib/src/api.dart b/np_api/lib/src/api.dart index dcf0ac95..f28bc238 100644 --- a/np_api/lib/src/api.dart +++ b/np_api/lib/src/api.dart @@ -27,6 +27,8 @@ class Api { ApiPhotos photos(String userId) => ApiPhotos(this, userId); + ApiStatus status() => ApiStatus(this); + ApiSystemtags systemtags() => ApiSystemtags(this); ApiSystemtagsRelations systemtagsRelations() => ApiSystemtagsRelations(this); diff --git a/np_api/lib/src/entity/entity.dart b/np_api/lib/src/entity/entity.dart index 90a484f2..cfa59726 100644 --- a/np_api/lib/src/entity/entity.dart +++ b/np_api/lib/src/entity/entity.dart @@ -299,6 +299,7 @@ class Status with EquatableMixin { const Status({ required this.version, required this.versionString, + required this.productName, }); @override @@ -308,10 +309,12 @@ class Status with EquatableMixin { List get props => [ version, versionString, + productName, ]; final String version; final String versionString; + final String productName; } @toString diff --git a/np_api/lib/src/entity/status_parser.dart b/np_api/lib/src/entity/status_parser.dart index e30080f7..ca91d1ba 100644 --- a/np_api/lib/src/entity/status_parser.dart +++ b/np_api/lib/src/entity/status_parser.dart @@ -13,6 +13,7 @@ class StatusParser { return Status( version: json["version"], versionString: json["versionstring"], + productName: json["productname"], ); } } diff --git a/np_api/test/entity/status_parser_test.dart b/np_api/test/entity/status_parser_test.dart new file mode 100644 index 00000000..f66f069d --- /dev/null +++ b/np_api/test/entity/status_parser_test.dart @@ -0,0 +1,34 @@ +import 'package:np_api/np_api.dart'; +import 'package:test/test.dart'; + +void main() { + group("StatusParser", () { + group("parse", () { + test("Nextcloud 25", _nextcloud25); + }); + }); +} + +Future _nextcloud25() async { + const json = """ +{ + "installed": true, + "maintenance": false, + "needsDbUpgrade": false, + "version": "25.0.2.3", + "versionstring": "25.0.2", + "edition": "", + "productname": "Nextcloud", + "extendedSupport": false +} +"""; + final results = await StatusParser().parse(json); + expect( + results, + const Status( + version: "25.0.2.3", + versionString: "25.0.2", + productName: "Nextcloud", + ), + ); +}