mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-01-22 08:46:18 +01:00
Cache server version for use when server is down
This commit is contained in:
parent
d363d544d6
commit
2374fe83df
9 changed files with 109 additions and 20 deletions
|
@ -63,6 +63,7 @@ class AccountController {
|
||||||
ServerController get serverController =>
|
ServerController get serverController =>
|
||||||
_serverController ??= ServerController(
|
_serverController ??= ServerController(
|
||||||
account: _account!,
|
account: _account!,
|
||||||
|
accountPrefController: accountPrefController,
|
||||||
);
|
);
|
||||||
|
|
||||||
AccountPrefController get accountPrefController =>
|
AccountPrefController get accountPrefController =>
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:nc_photos/account.dart';
|
import 'package:nc_photos/account.dart';
|
||||||
import 'package:nc_photos/entity/person.dart';
|
import 'package:nc_photos/entity/person.dart';
|
||||||
import 'package:nc_photos/entity/pref.dart';
|
import 'package:nc_photos/entity/pref.dart';
|
||||||
|
import 'package:nc_photos/entity/server_status.dart';
|
||||||
import 'package:np_codegen/np_codegen.dart';
|
import 'package:np_codegen/np_codegen.dart';
|
||||||
|
import 'package:np_common/object_util.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
|
|
||||||
part 'account_pref_controller.g.dart';
|
part 'account_pref_controller.g.dart';
|
||||||
|
@ -52,6 +56,13 @@ class AccountPrefController {
|
||||||
value: value,
|
value: value,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Future<void> setServerStatus(ServerStatus value) => _set(
|
||||||
|
controller: _serverStatusController,
|
||||||
|
setter: (pref, value) =>
|
||||||
|
pref.setServerStatus(jsonEncode(value!.toJson())),
|
||||||
|
value: value,
|
||||||
|
);
|
||||||
|
|
||||||
Future<void> _set<T>({
|
Future<void> _set<T>({
|
||||||
required BehaviorSubject<T> controller,
|
required BehaviorSubject<T> controller,
|
||||||
required Future<bool> Function(AccountPref pref, T value) setter,
|
required Future<bool> Function(AccountPref pref, T value) setter,
|
||||||
|
@ -89,4 +100,8 @@ class AccountPrefController {
|
||||||
@npSubjectAccessor
|
@npSubjectAccessor
|
||||||
late final _hasNewSharedAlbumController =
|
late final _hasNewSharedAlbumController =
|
||||||
BehaviorSubject.seeded(_accountPref.hasNewSharedAlbum() ?? false);
|
BehaviorSubject.seeded(_accountPref.hasNewSharedAlbum() ?? false);
|
||||||
|
@npSubjectAccessor
|
||||||
|
late final _serverStatusController = BehaviorSubject.seeded(_accountPref
|
||||||
|
.getServerStatus()
|
||||||
|
?.let((e) => ServerStatus.fromJson(jsonDecode(e))));
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,4 +50,10 @@ extension $AccountPrefControllerNpSubjectAccessor on AccountPrefController {
|
||||||
Stream<bool> get hasNewSharedAlbumChange =>
|
Stream<bool> get hasNewSharedAlbumChange =>
|
||||||
hasNewSharedAlbum.distinct().skip(1);
|
hasNewSharedAlbum.distinct().skip(1);
|
||||||
bool get hasNewSharedAlbumValue => _hasNewSharedAlbumController.value;
|
bool get hasNewSharedAlbumValue => _hasNewSharedAlbumController.value;
|
||||||
|
// _serverStatusController
|
||||||
|
ValueStream<ServerStatus?> get serverStatus => _serverStatusController.stream;
|
||||||
|
Stream<ServerStatus?> get serverStatusNew => serverStatus.skip(1);
|
||||||
|
Stream<ServerStatus?> get serverStatusChange =>
|
||||||
|
serverStatus.distinct().skip(1);
|
||||||
|
ServerStatus? get serverStatusValue => _serverStatusController.value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,4 +5,13 @@ extension on AccountPref {
|
||||||
provider.getBool(AccountPrefKey.hasNewSharedAlbum);
|
provider.getBool(AccountPrefKey.hasNewSharedAlbum);
|
||||||
// Future<bool> setNewSharedAlbum(bool value) =>
|
// Future<bool> setNewSharedAlbum(bool value) =>
|
||||||
// provider.setBool(AccountPrefKey.hasNewSharedAlbum, value);
|
// provider.setBool(AccountPrefKey.hasNewSharedAlbum, value);
|
||||||
|
|
||||||
|
String? getServerStatus() => provider.getString(AccountPrefKey.serverStatus);
|
||||||
|
Future<bool> setServerStatus(String? value) {
|
||||||
|
if (value == null) {
|
||||||
|
return provider.remove(AccountPrefKey.serverStatus);
|
||||||
|
} else {
|
||||||
|
return provider.setString(AccountPrefKey.serverStatus, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'dart:async';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:nc_photos/account.dart';
|
import 'package:nc_photos/account.dart';
|
||||||
import 'package:nc_photos/api/entity_converter.dart';
|
import 'package:nc_photos/api/entity_converter.dart';
|
||||||
|
import 'package:nc_photos/controller/account_pref_controller.dart';
|
||||||
import 'package:nc_photos/entity/server_status.dart';
|
import 'package:nc_photos/entity/server_status.dart';
|
||||||
import 'package:nc_photos/np_api_util.dart';
|
import 'package:nc_photos/np_api_util.dart';
|
||||||
import 'package:np_api/np_api.dart' as api;
|
import 'package:np_api/np_api.dart' as api;
|
||||||
|
@ -20,6 +21,7 @@ enum ServerFeature {
|
||||||
class ServerController {
|
class ServerController {
|
||||||
ServerController({
|
ServerController({
|
||||||
required this.account,
|
required this.account,
|
||||||
|
required this.accountPrefController,
|
||||||
});
|
});
|
||||||
|
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
@ -33,17 +35,6 @@ class ServerController {
|
||||||
return _statusStreamContorller.stream;
|
return _statusStreamContorller.stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSupported(ServerFeature feature) {
|
|
||||||
switch (feature) {
|
|
||||||
case ServerFeature.ncAlbum:
|
|
||||||
return !_statusStreamContorller.hasValue ||
|
|
||||||
_statusStreamContorller.value.majorVersion >= 25;
|
|
||||||
case ServerFeature.ncMetadata:
|
|
||||||
return !_statusStreamContorller.hasValue ||
|
|
||||||
_statusStreamContorller.value.majorVersion >= 28;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _load() => _getStatus();
|
Future<void> _load() => _getStatus();
|
||||||
|
|
||||||
Future<void> _getStatus() async {
|
Future<void> _getStatus() async {
|
||||||
|
@ -51,19 +42,49 @@ class ServerController {
|
||||||
final response = await ApiUtil.fromAccount(account).status().get();
|
final response = await ApiUtil.fromAccount(account).status().get();
|
||||||
if (!response.isGood) {
|
if (!response.isGood) {
|
||||||
_log.severe("[_getStatus] Failed requesting server: $response");
|
_log.severe("[_getStatus] Failed requesting server: $response");
|
||||||
|
_loadStatus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final apiStatus = await api.StatusParser().parse(response.body);
|
final apiStatus = await api.StatusParser().parse(response.body);
|
||||||
final status = ApiStatusConverter.fromApi(apiStatus);
|
final status = ApiStatusConverter.fromApi(apiStatus);
|
||||||
_log.info("[_getStatus] Server status: $status");
|
_log.info("[_getStatus] Server status: $status");
|
||||||
_statusStreamContorller.add(status);
|
_statusStreamContorller.add(status);
|
||||||
|
_saveStatus(status);
|
||||||
} catch (e, stackTrace) {
|
} catch (e, stackTrace) {
|
||||||
_log.severe("[_getStatus] Failed while get", e, stackTrace);
|
_log.severe("[_getStatus] Failed while get", e, stackTrace);
|
||||||
|
_loadStatus();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _loadStatus() {
|
||||||
|
final cache = accountPrefController.serverStatusValue;
|
||||||
|
if (cache != null) {
|
||||||
|
_statusStreamContorller.add(cache);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _saveStatus(ServerStatus status) {
|
||||||
|
final cache = accountPrefController.serverStatusValue;
|
||||||
|
if (cache != status) {
|
||||||
|
accountPrefController.setServerStatus(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
final Account account;
|
final Account account;
|
||||||
|
final AccountPrefController accountPrefController;
|
||||||
|
|
||||||
final _statusStreamContorller = BehaviorSubject<ServerStatus>();
|
final _statusStreamContorller = BehaviorSubject<ServerStatus>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension ServerControllerExtension on ServerController {
|
||||||
|
bool isSupported(ServerFeature feature) {
|
||||||
|
final status = _statusStreamContorller.valueOrNull;
|
||||||
|
switch (feature) {
|
||||||
|
case ServerFeature.ncAlbum:
|
||||||
|
return status == null || status.majorVersion >= 25;
|
||||||
|
case ServerFeature.ncMetadata:
|
||||||
|
return status != null && status.majorVersion >= 28;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -230,6 +230,7 @@ enum AccountPrefKey implements PrefKeyInterface {
|
||||||
accountLabel,
|
accountLabel,
|
||||||
lastNewCollectionType,
|
lastNewCollectionType,
|
||||||
personProvider,
|
personProvider,
|
||||||
|
serverStatus,
|
||||||
;
|
;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -249,6 +250,8 @@ enum AccountPrefKey implements PrefKeyInterface {
|
||||||
return "lastNewCollectionType";
|
return "lastNewCollectionType";
|
||||||
case AccountPrefKey.personProvider:
|
case AccountPrefKey.personProvider:
|
||||||
return "personProvider";
|
return "personProvider";
|
||||||
|
case AccountPrefKey.serverStatus:
|
||||||
|
return "serverStatus";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
import 'package:equatable/equatable.dart';
|
||||||
|
import 'package:np_common/type.dart';
|
||||||
import 'package:to_string/to_string.dart';
|
import 'package:to_string/to_string.dart';
|
||||||
|
|
||||||
part 'server_status.g.dart';
|
part 'server_status.g.dart';
|
||||||
|
|
||||||
@toString
|
@toString
|
||||||
class ServerStatus {
|
class ServerStatus with EquatableMixin {
|
||||||
const ServerStatus({
|
const ServerStatus({
|
||||||
required this.versionRaw,
|
required this.versionRaw,
|
||||||
required this.versionName,
|
required this.versionName,
|
||||||
|
@ -13,6 +15,25 @@ class ServerStatus {
|
||||||
@override
|
@override
|
||||||
String toString() => _$toString();
|
String toString() => _$toString();
|
||||||
|
|
||||||
|
factory ServerStatus.fromJson(JsonObj json) {
|
||||||
|
return ServerStatus(
|
||||||
|
versionRaw: json["versionRaw"],
|
||||||
|
versionName: json["versionName"],
|
||||||
|
productName: json["productName"],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonObj toJson() {
|
||||||
|
return {
|
||||||
|
"versionRaw": versionRaw,
|
||||||
|
"versionName": versionName,
|
||||||
|
"productName": productName,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
List<Object?> get props => [versionRaw, versionName, productName];
|
||||||
|
|
||||||
final String versionRaw;
|
final String versionRaw;
|
||||||
final String versionName;
|
final String versionName;
|
||||||
final String productName;
|
final String productName;
|
||||||
|
|
|
@ -9,10 +9,10 @@ import 'package:kiwi/kiwi.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:nc_photos/app_init.dart' as app_init;
|
import 'package:nc_photos/app_init.dart' as app_init;
|
||||||
import 'package:nc_photos/app_localizations.dart';
|
import 'package:nc_photos/app_localizations.dart';
|
||||||
|
import 'package:nc_photos/controller/account_pref_controller.dart';
|
||||||
import 'package:nc_photos/controller/pref_controller.dart';
|
import 'package:nc_photos/controller/pref_controller.dart';
|
||||||
import 'package:nc_photos/di_container.dart';
|
import 'package:nc_photos/di_container.dart';
|
||||||
import 'package:nc_photos/entity/file_descriptor.dart';
|
import 'package:nc_photos/entity/file_descriptor.dart';
|
||||||
import 'package:nc_photos/entity/pref.dart';
|
|
||||||
import 'package:nc_photos/event/native_event.dart';
|
import 'package:nc_photos/event/native_event.dart';
|
||||||
import 'package:nc_photos/language_util.dart' as language_util;
|
import 'package:nc_photos/language_util.dart' as language_util;
|
||||||
import 'package:nc_photos/use_case/battery_ensurer.dart';
|
import 'package:nc_photos/use_case/battery_ensurer.dart';
|
||||||
|
@ -95,13 +95,14 @@ class _Service {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _doWork() async {
|
Future<void> _doWork() async {
|
||||||
final prefController = PrefController(Pref());
|
final c = KiwiContainer().resolve<DiContainer>();
|
||||||
|
final prefController = PrefController(c.pref);
|
||||||
final account = prefController.currentAccountValue;
|
final account = prefController.currentAccountValue;
|
||||||
if (account == null) {
|
if (account == null) {
|
||||||
_log.shout("[_doWork] account == null");
|
_log.shout("[_doWork] account == null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final c = KiwiContainer().resolve<DiContainer>();
|
final accountPrefController = AccountPrefController(account: account);
|
||||||
|
|
||||||
final wifiEnsurer = WifiEnsurer(
|
final wifiEnsurer = WifiEnsurer(
|
||||||
interrupter: _shouldRun.stream,
|
interrupter: _shouldRun.stream,
|
||||||
|
@ -143,7 +144,7 @@ class _Service {
|
||||||
batteryEnsurer: batteryEnsurer,
|
batteryEnsurer: batteryEnsurer,
|
||||||
);
|
);
|
||||||
final processedIds = <int>[];
|
final processedIds = <int>[];
|
||||||
await for (final f in syncOp.syncAccount(account)) {
|
await for (final f in syncOp.syncAccount(account, accountPrefController)) {
|
||||||
processedIds.add(f.fdId);
|
processedIds.add(f.fdId);
|
||||||
service.setNotificationInfo(
|
service.setNotificationInfo(
|
||||||
title: _L10n.global().metadataTaskProcessingNotification,
|
title: _L10n.global().metadataTaskProcessingNotification,
|
||||||
|
|
|
@ -2,6 +2,7 @@ import 'dart:async';
|
||||||
|
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:nc_photos/account.dart';
|
import 'package:nc_photos/account.dart';
|
||||||
|
import 'package:nc_photos/controller/account_pref_controller.dart';
|
||||||
import 'package:nc_photos/controller/server_controller.dart';
|
import 'package:nc_photos/controller/server_controller.dart';
|
||||||
import 'package:nc_photos/db/entity_converter.dart';
|
import 'package:nc_photos/db/entity_converter.dart';
|
||||||
import 'package:nc_photos/entity/exif_util.dart';
|
import 'package:nc_photos/entity/exif_util.dart';
|
||||||
|
@ -10,6 +11,7 @@ import 'package:nc_photos/entity/file/file_cache_manager.dart';
|
||||||
import 'package:nc_photos/entity/file/repo.dart';
|
import 'package:nc_photos/entity/file/repo.dart';
|
||||||
import 'package:nc_photos/entity/file_util.dart' as file_util;
|
import 'package:nc_photos/entity/file_util.dart' as file_util;
|
||||||
import 'package:nc_photos/geocoder_util.dart';
|
import 'package:nc_photos/geocoder_util.dart';
|
||||||
|
import 'package:nc_photos/service/service.dart';
|
||||||
import 'package:nc_photos/use_case/battery_ensurer.dart';
|
import 'package:nc_photos/use_case/battery_ensurer.dart';
|
||||||
import 'package:nc_photos/use_case/get_file_binary.dart';
|
import 'package:nc_photos/use_case/get_file_binary.dart';
|
||||||
import 'package:nc_photos/use_case/load_metadata.dart';
|
import 'package:nc_photos/use_case/load_metadata.dart';
|
||||||
|
@ -38,10 +40,14 @@ class SyncMetadata {
|
||||||
required this.batteryEnsurer,
|
required this.batteryEnsurer,
|
||||||
});
|
});
|
||||||
|
|
||||||
Stream<File> syncAccount(Account account) async* {
|
Stream<File> syncAccount(
|
||||||
|
Account account,
|
||||||
|
AccountPrefController accountPrefController,
|
||||||
|
) async* {
|
||||||
final bool isNcMetadataSupported;
|
final bool isNcMetadataSupported;
|
||||||
try {
|
try {
|
||||||
isNcMetadataSupported = await _isNcMetadataSupported(account);
|
isNcMetadataSupported =
|
||||||
|
(await _isNcMetadataSupported(account, accountPrefController))!;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
_log.severe("[syncAccount] Failed to get server version", e);
|
_log.severe("[syncAccount] Failed to get server version", e);
|
||||||
return;
|
return;
|
||||||
|
@ -111,8 +117,14 @@ class SyncMetadata {
|
||||||
yield* stream;
|
yield* stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _isNcMetadataSupported(Account account) async {
|
Future<bool?> _isNcMetadataSupported(
|
||||||
final serverController = ServerController(account: account);
|
Account account,
|
||||||
|
AccountPrefController accountPrefController,
|
||||||
|
) async {
|
||||||
|
final serverController = ServerController(
|
||||||
|
account: account,
|
||||||
|
accountPrefController: accountPrefController,
|
||||||
|
);
|
||||||
await serverController.status.first.timeout(const Duration(seconds: 15));
|
await serverController.status.first.timeout(const Duration(seconds: 15));
|
||||||
return serverController.isSupported(ServerFeature.ncMetadata);
|
return serverController.isSupported(ServerFeature.ncMetadata);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue