diff --git a/mobile/lib/modules/asset_viewer/providers/asset_stack.provider.dart b/mobile/lib/modules/asset_viewer/providers/asset_stack.provider.dart index 8f8e9bbe0..5c20e1479 100644 --- a/mobile/lib/modules/asset_viewer/providers/asset_stack.provider.dart +++ b/mobile/lib/modules/asset_viewer/providers/asset_stack.provider.dart @@ -20,7 +20,7 @@ class AssetStackNotifier extends StateNotifier> { } } - removeChild(int index) { + void removeChild(int index) { if (index < state.length) { state.removeAt(index); } @@ -46,5 +46,6 @@ final assetStackProvider = .isArchivedEqualTo(false) .isTrashedEqualTo(false) .stackParentIdEqualTo(asset.remoteId) + .sortByFileCreatedAtDesc() .findAll(); }); diff --git a/mobile/lib/modules/asset_viewer/services/asset_stack.service.dart b/mobile/lib/modules/asset_viewer/services/asset_stack.service.dart index 8efee425c..0f9c864c2 100644 --- a/mobile/lib/modules/asset_viewer/services/asset_stack.service.dart +++ b/mobile/lib/modules/asset_viewer/services/asset_stack.service.dart @@ -10,7 +10,7 @@ class AssetStackService { final ApiService _api; - updateStack( + Future updateStack( Asset parentAsset, { List? childrenToAdd, List? childrenToRemove, @@ -46,7 +46,7 @@ class AssetStackService { } } - updateStackParent(Asset oldParent, Asset newParent) async { + Future updateStackParent(Asset oldParent, Asset newParent) async { // Guard [local asset] if (oldParent.remoteId == null || newParent.remoteId == null) { return; diff --git a/mobile/lib/modules/backup/models/backup_state.model.dart b/mobile/lib/modules/backup/models/backup_state.model.dart index e573600fe..e557e05be 100644 --- a/mobile/lib/modules/backup/models/backup_state.model.dart +++ b/mobile/lib/modules/backup/models/backup_state.model.dart @@ -1,6 +1,6 @@ import 'package:cancellation_token_http/http.dart'; import 'package:collection/collection.dart'; -import 'package:openapi/api.dart'; +import 'package:immich_mobile/shared/models/server_info/server_disk_info.model.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:immich_mobile/modules/backup/models/available_album.model.dart'; @@ -20,7 +20,7 @@ class BackUpState { final List allAssetsInDatabase; final double progressInPercentage; final CancellationToken cancelToken; - final ServerInfoResponseDto serverInfo; + final ServerDiskInfo serverInfo; final bool autoBackup; final bool backgroundBackup; final bool backupRequireWifi; @@ -65,7 +65,7 @@ class BackUpState { List? allAssetsInDatabase, double? progressInPercentage, CancellationToken? cancelToken, - ServerInfoResponseDto? serverInfo, + ServerDiskInfo? serverInfo, bool? autoBackup, bool? backgroundBackup, bool? backupRequireWifi, diff --git a/mobile/lib/modules/backup/providers/backup.provider.dart b/mobile/lib/modules/backup/providers/backup.provider.dart index 95f0e3230..673991c5d 100644 --- a/mobile/lib/modules/backup/providers/backup.provider.dart +++ b/mobile/lib/modules/backup/providers/backup.provider.dart @@ -13,6 +13,7 @@ import 'package:immich_mobile/modules/backup/services/backup.service.dart'; import 'package:immich_mobile/modules/login/models/authentication_state.model.dart'; import 'package:immich_mobile/modules/login/providers/authentication.provider.dart'; import 'package:immich_mobile/modules/onboarding/providers/gallery_permission.provider.dart'; +import 'package:immich_mobile/shared/models/server_info/server_disk_info.model.dart'; import 'package:immich_mobile/shared/models/store.dart'; import 'package:immich_mobile/shared/providers/app_state.provider.dart'; import 'package:immich_mobile/shared/providers/db.provider.dart'; @@ -20,7 +21,6 @@ import 'package:immich_mobile/shared/services/server_info.service.dart'; import 'package:immich_mobile/utils/diff.dart'; import 'package:isar/isar.dart'; import 'package:logging/logging.dart'; -import 'package:openapi/api.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:photo_manager/photo_manager.dart'; @@ -45,14 +45,11 @@ class BackupNotifier extends StateNotifier { backupRequireCharging: Store.get(StoreKey.backupRequireCharging, false), backupTriggerDelay: Store.get(StoreKey.backupTriggerDelay, 5000), - serverInfo: ServerInfoResponseDto( + serverInfo: const ServerDiskInfo( diskAvailable: "0", - diskAvailableRaw: 0, diskSize: "0", - diskSizeRaw: 0, - diskUsagePercentage: 0, diskUse: "0", - diskUseRaw: 0, + diskUsagePercentage: 0, ), availableAlbums: const [], selectedBackupAlbums: const {}, diff --git a/mobile/lib/modules/home/models/selection_state.dart b/mobile/lib/modules/home/models/selection_state.dart index 291b59068..e5a7b60a3 100644 --- a/mobile/lib/modules/home/models/selection_state.dart +++ b/mobile/lib/modules/home/models/selection_state.dart @@ -4,33 +4,38 @@ class SelectionAssetState { final bool hasRemote; final bool hasLocal; final bool hasMerged; + final int selectedCount; const SelectionAssetState({ this.hasRemote = false, this.hasLocal = false, this.hasMerged = false, + this.selectedCount = 0, }); SelectionAssetState copyWith({ bool? hasRemote, bool? hasLocal, bool? hasMerged, + int? selectedCount, }) { return SelectionAssetState( hasRemote: hasRemote ?? this.hasRemote, hasLocal: hasLocal ?? this.hasLocal, hasMerged: hasMerged ?? this.hasMerged, + selectedCount: selectedCount ?? this.selectedCount, ); } SelectionAssetState.fromSelection(Set selection) : hasLocal = selection.any((e) => e.storage == AssetState.local), hasMerged = selection.any((e) => e.storage == AssetState.merged), - hasRemote = selection.any((e) => e.storage == AssetState.remote); + hasRemote = selection.any((e) => e.storage == AssetState.remote), + selectedCount = selection.length; @override String toString() => - 'SelectionAssetState(hasRemote: $hasRemote, hasMerged: $hasMerged, hasMerged: $hasMerged)'; + 'SelectionAssetState(hasRemote: $hasRemote, hasMerged: $hasMerged, hasMerged: $hasMerged, selectedCount: $selectedCount)'; @override bool operator ==(covariant SelectionAssetState other) { @@ -38,10 +43,14 @@ class SelectionAssetState { return other.hasRemote == hasRemote && other.hasLocal == hasLocal && - other.hasMerged == hasMerged; + other.hasMerged == hasMerged && + other.selectedCount == selectedCount; } @override int get hashCode => - hasRemote.hashCode ^ hasLocal.hashCode ^ hasMerged.hashCode; + hasRemote.hashCode ^ + hasLocal.hashCode ^ + hasMerged.hashCode ^ + selectedCount.hashCode; } diff --git a/mobile/lib/modules/home/ui/control_bottom_app_bar.dart b/mobile/lib/modules/home/ui/control_bottom_app_bar.dart index 57fadc440..c356dddf7 100644 --- a/mobile/lib/modules/home/ui/control_bottom_app_bar.dart +++ b/mobile/lib/modules/home/ui/control_bottom_app_bar.dart @@ -118,7 +118,7 @@ class ControlBottomAppBar extends ConsumerWidget { : null, ), ), - if (!hasLocal) + if (!hasLocal && selectionAssetState.selectedCount > 1) ControlBoxButton( iconData: Icons.filter_none_rounded, label: "control_bottom_app_bar_stack".tr(), diff --git a/mobile/lib/modules/home/ui/home_page_app_bar.dart b/mobile/lib/modules/home/ui/home_page_app_bar.dart index ce038d327..cbe1846ce 100644 --- a/mobile/lib/modules/home/ui/home_page_app_bar.dart +++ b/mobile/lib/modules/home/ui/home_page_app_bar.dart @@ -8,7 +8,7 @@ import 'package:immich_mobile/modules/login/providers/authentication.provider.da import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/modules/backup/models/backup_state.model.dart'; -import 'package:immich_mobile/shared/models/server_info_state.model.dart'; +import 'package:immich_mobile/shared/models/server_info/server_info.model.dart'; import 'package:immich_mobile/modules/backup/providers/backup.provider.dart'; import 'package:immich_mobile/shared/providers/server_info.provider.dart'; @@ -28,7 +28,7 @@ class HomePageAppBar extends ConsumerWidget implements PreferredSizeWidget { final BackUpState backupState = ref.watch(backupProvider); final bool isEnableAutoBackup = backupState.backgroundBackup || backupState.autoBackup; - final ServerInfoState serverInfoState = ref.watch(serverInfoProvider); + final ServerInfo serverInfoState = ref.watch(serverInfoProvider); AuthenticationState authState = ref.watch(authenticationProvider); final user = Store.tryGet(StoreKey.currentUser); buildProfilePhoto() { diff --git a/mobile/lib/modules/home/ui/profile_drawer/server_info_box.dart b/mobile/lib/modules/home/ui/profile_drawer/server_info_box.dart index a9ad51471..fe050b542 100644 --- a/mobile/lib/modules/home/ui/profile_drawer/server_info_box.dart +++ b/mobile/lib/modules/home/ui/profile_drawer/server_info_box.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/shared/models/server_info_state.model.dart'; +import 'package:immich_mobile/shared/models/server_info/server_info.model.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:immich_mobile/shared/providers/server_info.provider.dart'; import 'package:package_info_plus/package_info_plus.dart'; @@ -13,7 +13,7 @@ class ServerInfoBox extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - ServerInfoState serverInfoState = ref.watch(serverInfoProvider); + ServerInfo serverInfoState = ref.watch(serverInfoProvider); final appInfo = useState({}); @@ -107,7 +107,7 @@ class ServerInfoBox extends HookConsumerWidget { ), Text( serverInfoState.serverVersion.major > 0 - ? "${serverInfoState.serverVersion.major}.${serverInfoState.serverVersion.minor}.${serverInfoState.serverVersion.patch_}" + ? "${serverInfoState.serverVersion.major}.${serverInfoState.serverVersion.minor}.${serverInfoState.serverVersion.patch}" : "?", style: TextStyle( fontSize: 11, diff --git a/mobile/lib/modules/home/views/home_page.dart b/mobile/lib/modules/home/views/home_page.dart index eca78d74f..d4e306a4b 100644 --- a/mobile/lib/modules/home/views/home_page.dart +++ b/mobile/lib/modules/home/views/home_page.dart @@ -7,12 +7,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:fluttertoast/fluttertoast.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/modules/album/models/asset_selection_page_result.model.dart'; import 'package:immich_mobile/modules/album/providers/album.provider.dart'; import 'package:immich_mobile/modules/album/providers/album_detail.provider.dart'; import 'package:immich_mobile/modules/album/providers/shared_album.provider.dart'; import 'package:immich_mobile/modules/album/services/album.service.dart'; -import 'package:immich_mobile/modules/asset_viewer/providers/asset_stack.provider.dart'; import 'package:immich_mobile/modules/asset_viewer/services/asset_stack.service.dart'; import 'package:immich_mobile/modules/backup/providers/manual_upload.provider.dart'; import 'package:immich_mobile/modules/home/models/selection_state.dart'; @@ -281,46 +279,15 @@ class HomePage extends HookConsumerWidget { void onStack() async { try { processing.value = true; - if (!selectionEnabledHook.value) { + if (!selectionEnabledHook.value || selection.value.length < 2) { return; } - - final selectedAsset = selection.value.elementAt(0); - - if (selection.value.length == 1) { - final stackChildren = - (await ref.read(assetStackProvider(selectedAsset).future)) - .toSet(); - AssetSelectionPageResult? returnPayload = - await AutoRouter.of(context).push( - AssetSelectionRoute( - existingAssets: stackChildren, - canDeselect: true, - query: getAssetStackSelectionQuery(ref, selectedAsset), - ), - ); - - if (returnPayload != null) { - Set selectedAssets = returnPayload.selectedAssets; - // Do not add itself as its stack child - selectedAssets.remove(selectedAsset); - final removedChildren = stackChildren.difference(selectedAssets); - final addedChildren = selectedAssets.difference(stackChildren); - await ref.read(assetStackServiceProvider).updateStack( - selectedAsset, - childrenToAdd: addedChildren.toList(), - childrenToRemove: removedChildren.toList(), - ); - } - } else { - // Merge assets - selection.value.remove(selectedAsset); - final selectedAssets = selection.value; - await ref.read(assetStackServiceProvider).updateStack( - selectedAsset, - childrenToAdd: selectedAssets.toList(), - ); - } + final parent = selection.value.elementAt(0); + selection.value.remove(parent); + await ref.read(assetStackServiceProvider).updateStack( + parent, + childrenToAdd: selection.value.toList(), + ); } finally { processing.value = false; selectionEnabledHook.value = false; diff --git a/mobile/lib/modules/map/providers/map_state.provider.dart b/mobile/lib/modules/map/providers/map_state.provider.dart index 46f49bdaf..8f4356198 100644 --- a/mobile/lib/modules/map/providers/map_state.provider.dart +++ b/mobile/lib/modules/map/providers/map_state.provider.dart @@ -4,26 +4,24 @@ import 'package:immich_mobile/modules/settings/providers/app_settings.provider.d import 'package:immich_mobile/modules/settings/services/app_settings.service.dart'; class MapStateNotifier extends StateNotifier { - MapStateNotifier(this.appSettingsProvider) + MapStateNotifier(this._appSettingsProvider) : super( MapState( - isDarkTheme: appSettingsProvider + isDarkTheme: _appSettingsProvider .getSetting(AppSettingsEnum.mapThemeMode), - showFavoriteOnly: appSettingsProvider + showFavoriteOnly: _appSettingsProvider .getSetting(AppSettingsEnum.mapShowFavoriteOnly), - includeArchived: appSettingsProvider + includeArchived: _appSettingsProvider .getSetting(AppSettingsEnum.mapIncludeArchived), - relativeTime: appSettingsProvider + relativeTime: _appSettingsProvider .getSetting(AppSettingsEnum.mapRelativeDate), ), ); - final AppSettingsService appSettingsProvider; - - bool get isDarkTheme => state.isDarkTheme; + final AppSettingsService _appSettingsProvider; void switchTheme(bool isDarkTheme) { - appSettingsProvider.setSetting( + _appSettingsProvider.setSetting( AppSettingsEnum.mapThemeMode, isDarkTheme, ); @@ -31,7 +29,7 @@ class MapStateNotifier extends StateNotifier { } void switchFavoriteOnly(bool isFavoriteOnly) { - appSettingsProvider.setSetting( + _appSettingsProvider.setSetting( AppSettingsEnum.mapShowFavoriteOnly, isFavoriteOnly, ); @@ -39,7 +37,7 @@ class MapStateNotifier extends StateNotifier { } void switchIncludeArchived(bool isIncludeArchived) { - appSettingsProvider.setSetting( + _appSettingsProvider.setSetting( AppSettingsEnum.mapIncludeArchived, isIncludeArchived, ); @@ -47,7 +45,7 @@ class MapStateNotifier extends StateNotifier { } void setRelativeTime(int relativeTime) { - appSettingsProvider.setSetting( + _appSettingsProvider.setSetting( AppSettingsEnum.mapRelativeDate, relativeTime, ); diff --git a/mobile/lib/modules/map/services/map.service.dart b/mobile/lib/modules/map/services/map.service.dart index 468669bc7..8b4e8064c 100644 --- a/mobile/lib/modules/map/services/map.service.dart +++ b/mobile/lib/modules/map/services/map.service.dart @@ -17,7 +17,7 @@ final mapServiceProvider = Provider( class MapSerivce { final ApiService _apiService; final Isar _db; - final log = Logger("MapService"); + final _log = Logger("MapService"); MapSerivce(this._apiService, this._db); @@ -37,7 +37,7 @@ class MapSerivce { return markers ?? []; } catch (error, stack) { - log.severe("Cannot get map markers ${error.toString()}", error, stack); + _log.severe("Cannot get map markers ${error.toString()}", error, stack); return []; } } @@ -53,7 +53,7 @@ class MapSerivce { } return Asset.remote(dto); } catch (error, stack) { - log.severe( + _log.severe( "Cannot get asset for marker ${error.toString()}", error, stack, diff --git a/mobile/lib/shared/models/server_info/server_config.model.dart b/mobile/lib/shared/models/server_info/server_config.model.dart new file mode 100644 index 000000000..227625dcb --- /dev/null +++ b/mobile/lib/shared/models/server_info/server_config.model.dart @@ -0,0 +1,44 @@ +import 'package:openapi/api.dart'; + +class ServerConfig { + final int trashDays; + final String mapTileUrl; + + const ServerConfig({ + required this.trashDays, + required this.mapTileUrl, + }); + + ServerConfig copyWith({ + int? trashDays, + String? mapTileUrl, + }) { + return ServerConfig( + trashDays: trashDays ?? this.trashDays, + mapTileUrl: mapTileUrl ?? this.mapTileUrl, + ); + } + + @override + String toString() { + return 'ServerConfig(trashDays: $trashDays, mapTileUrl: $mapTileUrl)'; + } + + ServerConfig.fromDto(ServerConfigDto dto) + : trashDays = dto.trashDays, + mapTileUrl = dto.mapTileUrl; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is ServerConfig && + other.trashDays == trashDays && + other.mapTileUrl == mapTileUrl; + } + + @override + int get hashCode { + return trashDays.hashCode ^ mapTileUrl.hashCode; + } +} diff --git a/mobile/lib/shared/models/server_info/server_disk_info.model.dart b/mobile/lib/shared/models/server_info/server_disk_info.model.dart new file mode 100644 index 000000000..95e94e369 --- /dev/null +++ b/mobile/lib/shared/models/server_info/server_disk_info.model.dart @@ -0,0 +1,59 @@ +import 'package:openapi/api.dart'; + +class ServerDiskInfo { + final String diskAvailable; + final String diskSize; + final String diskUse; + final double diskUsagePercentage; + + const ServerDiskInfo({ + required this.diskAvailable, + required this.diskSize, + required this.diskUse, + required this.diskUsagePercentage, + }); + + ServerDiskInfo copyWith({ + String? diskAvailable, + String? diskSize, + String? diskUse, + double? diskUsagePercentage, + }) { + return ServerDiskInfo( + diskAvailable: diskAvailable ?? this.diskAvailable, + diskSize: diskSize ?? this.diskSize, + diskUse: diskUse ?? this.diskUse, + diskUsagePercentage: diskUsagePercentage ?? this.diskUsagePercentage, + ); + } + + @override + String toString() { + return 'ServerDiskInfo(diskAvailable: $diskAvailable, diskSize: $diskSize, diskUse: $diskUse, diskUsagePercentage: $diskUsagePercentage)'; + } + + ServerDiskInfo.fromDto(ServerInfoResponseDto dto) + : diskAvailable = dto.diskAvailable, + diskSize = dto.diskSize, + diskUse = dto.diskUse, + diskUsagePercentage = dto.diskUsagePercentage; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is ServerDiskInfo && + other.diskAvailable == diskAvailable && + other.diskSize == diskSize && + other.diskUse == diskUse && + other.diskUsagePercentage == diskUsagePercentage; + } + + @override + int get hashCode { + return diskAvailable.hashCode ^ + diskSize.hashCode ^ + diskUse.hashCode ^ + diskUsagePercentage.hashCode; + } +} diff --git a/mobile/lib/shared/models/server_info/server_features.model.dart b/mobile/lib/shared/models/server_info/server_features.model.dart new file mode 100644 index 000000000..acf4c3d2d --- /dev/null +++ b/mobile/lib/shared/models/server_info/server_features.model.dart @@ -0,0 +1,42 @@ +import 'package:openapi/api.dart'; + +class ServerFeatures { + final bool trash; + final bool map; + + const ServerFeatures({ + required this.trash, + required this.map, + }); + + ServerFeatures copyWith({ + bool? trash, + bool? map, + }) { + return ServerFeatures( + trash: trash ?? this.trash, + map: map ?? this.map, + ); + } + + @override + String toString() { + return 'ServerFeatures(trash: $trash, map: $map)'; + } + + ServerFeatures.fromDto(ServerFeaturesDto dto) + : trash = dto.trash, + map = dto.map; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is ServerFeatures && other.trash == trash && other.map == map; + } + + @override + int get hashCode { + return trash.hashCode ^ map.hashCode; + } +} diff --git a/mobile/lib/shared/models/server_info_state.model.dart b/mobile/lib/shared/models/server_info/server_info.model.dart similarity index 51% rename from mobile/lib/shared/models/server_info_state.model.dart rename to mobile/lib/shared/models/server_info/server_info.model.dart index 61879a6c0..db8150768 100644 --- a/mobile/lib/shared/models/server_info_state.model.dart +++ b/mobile/lib/shared/models/server_info/server_info.model.dart @@ -1,50 +1,58 @@ -import 'package:openapi/api.dart'; +import 'package:immich_mobile/shared/models/server_info/server_config.model.dart'; +import 'package:immich_mobile/shared/models/server_info/server_disk_info.model.dart'; +import 'package:immich_mobile/shared/models/server_info/server_features.model.dart'; +import 'package:immich_mobile/shared/models/server_info/server_version.model.dart'; -class ServerInfoState { - final ServerVersionResponseDto serverVersion; - final ServerFeaturesDto serverFeatures; - final ServerConfigDto serverConfig; +class ServerInfo { + final ServerVersion serverVersion; + final ServerFeatures serverFeatures; + final ServerConfig serverConfig; + final ServerDiskInfo serverDiskInfo; final bool isVersionMismatch; final String versionMismatchErrorMessage; - ServerInfoState({ + ServerInfo({ required this.serverVersion, required this.serverFeatures, required this.serverConfig, required this.isVersionMismatch, + required this.serverDiskInfo, required this.versionMismatchErrorMessage, }); - ServerInfoState copyWith({ - ServerVersionResponseDto? serverVersion, - ServerFeaturesDto? serverFeatures, - ServerConfigDto? serverConfig, + ServerInfo copyWith({ + ServerVersion? serverVersion, + ServerFeatures? serverFeatures, + ServerConfig? serverConfig, + ServerDiskInfo? serverDiskInfo, bool? isVersionMismatch, String? versionMismatchErrorMessage, }) { - return ServerInfoState( + return ServerInfo( serverVersion: serverVersion ?? this.serverVersion, serverFeatures: serverFeatures ?? this.serverFeatures, serverConfig: serverConfig ?? this.serverConfig, isVersionMismatch: isVersionMismatch ?? this.isVersionMismatch, versionMismatchErrorMessage: versionMismatchErrorMessage ?? this.versionMismatchErrorMessage, + serverDiskInfo: serverDiskInfo ?? this.serverDiskInfo, ); } @override String toString() { - return 'ServerInfoState( serverVersion: $serverVersion, serverFeatures: $serverFeatures, serverConfig: $serverConfig, isVersionMismatch: $isVersionMismatch, versionMismatchErrorMessage: $versionMismatchErrorMessage)'; + return 'ServerInfo(serverVersion: $serverVersion, serverFeatures: $serverFeatures, serverConfig: $serverConfig, isVersionMismatch: $isVersionMismatch, versionMismatchErrorMessage: $versionMismatchErrorMessage, serverDiskInfo: $serverDiskInfo)'; } @override bool operator ==(Object other) { if (identical(this, other)) return true; - return other is ServerInfoState && + return other is ServerInfo && other.serverVersion == serverVersion && other.serverFeatures == serverFeatures && other.serverConfig == serverConfig && + other.serverDiskInfo == serverDiskInfo && other.isVersionMismatch == isVersionMismatch && other.versionMismatchErrorMessage == versionMismatchErrorMessage; } @@ -55,6 +63,7 @@ class ServerInfoState { serverFeatures.hashCode ^ serverConfig.hashCode ^ isVersionMismatch.hashCode ^ - versionMismatchErrorMessage.hashCode; + versionMismatchErrorMessage.hashCode ^ + serverDiskInfo.hashCode; } } diff --git a/mobile/lib/shared/models/server_info/server_version.model.dart b/mobile/lib/shared/models/server_info/server_version.model.dart new file mode 100644 index 000000000..1995edb98 --- /dev/null +++ b/mobile/lib/shared/models/server_info/server_version.model.dart @@ -0,0 +1,50 @@ +import 'package:openapi/api.dart'; + +class ServerVersion { + final int major; + final int minor; + final int patch; + + const ServerVersion({ + required this.major, + required this.minor, + required this.patch, + }); + + ServerVersion copyWith({ + int? major, + int? minor, + int? patch, + }) { + return ServerVersion( + major: major ?? this.major, + minor: minor ?? this.minor, + patch: patch ?? this.patch, + ); + } + + @override + String toString() { + return 'ServerVersion(major: $major, minor: $minor, patch: $patch)'; + } + + ServerVersion.fromDto(ServerVersionResponseDto dto) + : major = dto.major, + minor = dto.minor, + patch = dto.patch_; + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + + return other is ServerVersion && + other.major == major && + other.minor == minor && + other.patch == patch; + } + + @override + int get hashCode { + return major.hashCode ^ minor.hashCode ^ patch.hashCode; + } +} diff --git a/mobile/lib/shared/providers/asset.provider.dart b/mobile/lib/shared/providers/asset.provider.dart index 78c1c5968..31a11639b 100644 --- a/mobile/lib/shared/providers/asset.provider.dart +++ b/mobile/lib/shared/providers/asset.provider.dart @@ -282,31 +282,3 @@ QueryBuilder? getRemoteAssetQuery(WidgetRef ref) { .stackParentIdIsNull() .sortByFileCreatedAtDesc(); } - -QueryBuilder? getAssetStackSelectionQuery( - WidgetRef ref, - Asset parentAsset, -) { - final userId = ref.watch(currentUserProvider)?.isarId; - if (userId == null || !parentAsset.isRemote) { - return null; - } - return ref - .watch(dbProvider) - .assets - .where() - .remoteIdIsNotNull() - .filter() - .isArchivedEqualTo(false) - .ownerIdEqualTo(userId) - .not() - .remoteIdEqualTo(parentAsset.remoteId) - // Show existing stack children in selection page - .group( - (q) => q - .stackParentIdIsNull() - .or() - .stackParentIdEqualTo(parentAsset.remoteId), - ) - .sortByFileCreatedAtDesc(); -} diff --git a/mobile/lib/shared/providers/server_info.provider.dart b/mobile/lib/shared/providers/server_info.provider.dart index ce431e7ab..514b7b0e1 100644 --- a/mobile/lib/shared/providers/server_info.provider.dart +++ b/mobile/lib/shared/providers/server_info.provider.dart @@ -1,40 +1,36 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/shared/models/server_info/server_disk_info.model.dart'; -import 'package:immich_mobile/shared/models/server_info_state.model.dart'; +import 'package:immich_mobile/shared/models/server_info/server_info.model.dart'; import 'package:immich_mobile/shared/services/server_info.service.dart'; -import 'package:openapi/api.dart'; +import 'package:immich_mobile/shared/models/server_info/server_config.model.dart'; +import 'package:immich_mobile/shared/models/server_info/server_features.model.dart'; +import 'package:immich_mobile/shared/models/server_info/server_version.model.dart'; import 'package:package_info_plus/package_info_plus.dart'; -class ServerInfoNotifier extends StateNotifier { +class ServerInfoNotifier extends StateNotifier { ServerInfoNotifier(this._serverInfoService) : super( - ServerInfoState( - serverVersion: ServerVersionResponseDto( + ServerInfo( + serverVersion: const ServerVersion( major: 0, - patch_: 0, minor: 0, + patch: 0, ), - serverFeatures: ServerFeaturesDto( - clipEncode: true, - configFile: false, - facialRecognition: true, + serverFeatures: const ServerFeatures( map: true, - oauth: false, - oauthAutoLaunch: false, - passwordLogin: true, - search: true, - sidecar: true, - tagImage: true, trash: true, - reverseGeocoding: true, ), - serverConfig: ServerConfigDto( - loginPageMessage: "", + serverConfig: const ServerConfig( mapTileUrl: "https://tile.openstreetmap.org/{z}/{x}/{y}.png", - oauthButtonText: "", trashDays: 30, - isInitialized: false, + ), + serverDiskInfo: const ServerDiskInfo( + diskAvailable: "0", + diskSize: "0", + diskUse: "0", + diskUsagePercentage: 0, ), isVersionMismatch: false, versionMismatchErrorMessage: "", @@ -50,8 +46,7 @@ class ServerInfoNotifier extends StateNotifier { } getServerVersion() async { - ServerVersionResponseDto? serverVersion = - await _serverInfoService.getServerVersion(); + final serverVersion = await _serverInfoService.getServerVersion(); if (serverVersion == null) { state = state.copyWith( @@ -94,8 +89,7 @@ class ServerInfoNotifier extends StateNotifier { } getServerFeatures() async { - ServerFeaturesDto? serverFeatures = - await _serverInfoService.getServerFeatures(); + final serverFeatures = await _serverInfoService.getServerFeatures(); if (serverFeatures == null) { return; } @@ -103,7 +97,7 @@ class ServerInfoNotifier extends StateNotifier { } getServerConfig() async { - ServerConfigDto? serverConfig = await _serverInfoService.getServerConfig(); + final serverConfig = await _serverInfoService.getServerConfig(); if (serverConfig == null) { return; } @@ -126,6 +120,6 @@ class ServerInfoNotifier extends StateNotifier { } final serverInfoProvider = - StateNotifierProvider((ref) { + StateNotifierProvider((ref) { return ServerInfoNotifier(ref.watch(serverInfoServiceProvider)); }); diff --git a/mobile/lib/shared/services/server_info.service.dart b/mobile/lib/shared/services/server_info.service.dart index 3973dd9fb..10cb1eb0d 100644 --- a/mobile/lib/shared/services/server_info.service.dart +++ b/mobile/lib/shared/services/server_info.service.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/shared/models/server_info/server_config.model.dart'; +import 'package:immich_mobile/shared/models/server_info/server_disk_info.model.dart'; +import 'package:immich_mobile/shared/models/server_info/server_features.model.dart'; +import 'package:immich_mobile/shared/models/server_info/server_version.model.dart'; import 'package:immich_mobile/shared/providers/api.provider.dart'; import 'package:immich_mobile/shared/services/api.service.dart'; -import 'package:openapi/api.dart'; final serverInfoServiceProvider = Provider( (ref) => ServerInfoService( @@ -15,39 +18,51 @@ class ServerInfoService { ServerInfoService(this._apiService); - Future getServerInfo() async { + Future getServerInfo() async { try { - return await _apiService.serverInfoApi.getServerInfo(); + final dto = await _apiService.serverInfoApi.getServerInfo(); + if (dto != null) { + return ServerDiskInfo.fromDto(dto); + } } catch (e) { debugPrint("Error [getServerInfo] ${e.toString()}"); - return null; } + return null; } - Future getServerVersion() async { + Future getServerVersion() async { try { - return await _apiService.serverInfoApi.getServerVersion(); + final dto = await _apiService.serverInfoApi.getServerVersion(); + if (dto != null) { + return ServerVersion.fromDto(dto); + } } catch (e) { debugPrint("Error [getServerVersion] ${e.toString()}"); - return null; } + return null; } - Future getServerFeatures() async { + Future getServerFeatures() async { try { - return await _apiService.serverInfoApi.getServerFeatures(); + final dto = await _apiService.serverInfoApi.getServerFeatures(); + if (dto != null) { + return ServerFeatures.fromDto(dto); + } } catch (e) { debugPrint("Error [getServerFeatures] ${e.toString()}"); - return null; } + return null; } - Future getServerConfig() async { + Future getServerConfig() async { try { - return await _apiService.serverInfoApi.getServerConfig(); + final dto = await _apiService.serverInfoApi.getServerConfig(); + if (dto != null) { + return ServerConfig.fromDto(dto); + } } catch (e) { debugPrint("Error [getServerConfig] ${e.toString()}"); - return null; } + return null; } }