chore: pull main

This commit is contained in:
shalong-tanwen 2023-10-23 02:35:25 +05:30
commit 844eaaa0f4
19 changed files with 316 additions and 159 deletions

View file

@ -20,7 +20,7 @@ class AssetStackNotifier extends StateNotifier<List<Asset>> {
} }
} }
removeChild(int index) { void removeChild(int index) {
if (index < state.length) { if (index < state.length) {
state.removeAt(index); state.removeAt(index);
} }
@ -46,5 +46,6 @@ final assetStackProvider =
.isArchivedEqualTo(false) .isArchivedEqualTo(false)
.isTrashedEqualTo(false) .isTrashedEqualTo(false)
.stackParentIdEqualTo(asset.remoteId) .stackParentIdEqualTo(asset.remoteId)
.sortByFileCreatedAtDesc()
.findAll(); .findAll();
}); });

View file

@ -10,7 +10,7 @@ class AssetStackService {
final ApiService _api; final ApiService _api;
updateStack( Future<void> updateStack(
Asset parentAsset, { Asset parentAsset, {
List<Asset>? childrenToAdd, List<Asset>? childrenToAdd,
List<Asset>? childrenToRemove, List<Asset>? childrenToRemove,
@ -46,7 +46,7 @@ class AssetStackService {
} }
} }
updateStackParent(Asset oldParent, Asset newParent) async { Future<void> updateStackParent(Asset oldParent, Asset newParent) async {
// Guard [local asset] // Guard [local asset]
if (oldParent.remoteId == null || newParent.remoteId == null) { if (oldParent.remoteId == null || newParent.remoteId == null) {
return; return;

View file

@ -1,6 +1,6 @@
import 'package:cancellation_token_http/http.dart'; import 'package:cancellation_token_http/http.dart';
import 'package:collection/collection.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:photo_manager/photo_manager.dart';
import 'package:immich_mobile/modules/backup/models/available_album.model.dart'; import 'package:immich_mobile/modules/backup/models/available_album.model.dart';
@ -20,7 +20,7 @@ class BackUpState {
final List<String> allAssetsInDatabase; final List<String> allAssetsInDatabase;
final double progressInPercentage; final double progressInPercentage;
final CancellationToken cancelToken; final CancellationToken cancelToken;
final ServerInfoResponseDto serverInfo; final ServerDiskInfo serverInfo;
final bool autoBackup; final bool autoBackup;
final bool backgroundBackup; final bool backgroundBackup;
final bool backupRequireWifi; final bool backupRequireWifi;
@ -65,7 +65,7 @@ class BackUpState {
List<String>? allAssetsInDatabase, List<String>? allAssetsInDatabase,
double? progressInPercentage, double? progressInPercentage,
CancellationToken? cancelToken, CancellationToken? cancelToken,
ServerInfoResponseDto? serverInfo, ServerDiskInfo? serverInfo,
bool? autoBackup, bool? autoBackup,
bool? backgroundBackup, bool? backgroundBackup,
bool? backupRequireWifi, bool? backupRequireWifi,

View file

@ -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/models/authentication_state.model.dart';
import 'package:immich_mobile/modules/login/providers/authentication.provider.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/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/models/store.dart';
import 'package:immich_mobile/shared/providers/app_state.provider.dart'; import 'package:immich_mobile/shared/providers/app_state.provider.dart';
import 'package:immich_mobile/shared/providers/db.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:immich_mobile/utils/diff.dart';
import 'package:isar/isar.dart'; import 'package:isar/isar.dart';
import 'package:logging/logging.dart'; import 'package:logging/logging.dart';
import 'package:openapi/api.dart';
import 'package:permission_handler/permission_handler.dart'; import 'package:permission_handler/permission_handler.dart';
import 'package:photo_manager/photo_manager.dart'; import 'package:photo_manager/photo_manager.dart';
@ -45,14 +45,11 @@ class BackupNotifier extends StateNotifier<BackUpState> {
backupRequireCharging: backupRequireCharging:
Store.get(StoreKey.backupRequireCharging, false), Store.get(StoreKey.backupRequireCharging, false),
backupTriggerDelay: Store.get(StoreKey.backupTriggerDelay, 5000), backupTriggerDelay: Store.get(StoreKey.backupTriggerDelay, 5000),
serverInfo: ServerInfoResponseDto( serverInfo: const ServerDiskInfo(
diskAvailable: "0", diskAvailable: "0",
diskAvailableRaw: 0,
diskSize: "0", diskSize: "0",
diskSizeRaw: 0,
diskUsagePercentage: 0,
diskUse: "0", diskUse: "0",
diskUseRaw: 0, diskUsagePercentage: 0,
), ),
availableAlbums: const [], availableAlbums: const [],
selectedBackupAlbums: const {}, selectedBackupAlbums: const {},

View file

@ -4,33 +4,38 @@ class SelectionAssetState {
final bool hasRemote; final bool hasRemote;
final bool hasLocal; final bool hasLocal;
final bool hasMerged; final bool hasMerged;
final int selectedCount;
const SelectionAssetState({ const SelectionAssetState({
this.hasRemote = false, this.hasRemote = false,
this.hasLocal = false, this.hasLocal = false,
this.hasMerged = false, this.hasMerged = false,
this.selectedCount = 0,
}); });
SelectionAssetState copyWith({ SelectionAssetState copyWith({
bool? hasRemote, bool? hasRemote,
bool? hasLocal, bool? hasLocal,
bool? hasMerged, bool? hasMerged,
int? selectedCount,
}) { }) {
return SelectionAssetState( return SelectionAssetState(
hasRemote: hasRemote ?? this.hasRemote, hasRemote: hasRemote ?? this.hasRemote,
hasLocal: hasLocal ?? this.hasLocal, hasLocal: hasLocal ?? this.hasLocal,
hasMerged: hasMerged ?? this.hasMerged, hasMerged: hasMerged ?? this.hasMerged,
selectedCount: selectedCount ?? this.selectedCount,
); );
} }
SelectionAssetState.fromSelection(Set<Asset> selection) SelectionAssetState.fromSelection(Set<Asset> selection)
: hasLocal = selection.any((e) => e.storage == AssetState.local), : hasLocal = selection.any((e) => e.storage == AssetState.local),
hasMerged = selection.any((e) => e.storage == AssetState.merged), 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 @override
String toString() => String toString() =>
'SelectionAssetState(hasRemote: $hasRemote, hasMerged: $hasMerged, hasMerged: $hasMerged)'; 'SelectionAssetState(hasRemote: $hasRemote, hasMerged: $hasMerged, hasMerged: $hasMerged, selectedCount: $selectedCount)';
@override @override
bool operator ==(covariant SelectionAssetState other) { bool operator ==(covariant SelectionAssetState other) {
@ -38,10 +43,14 @@ class SelectionAssetState {
return other.hasRemote == hasRemote && return other.hasRemote == hasRemote &&
other.hasLocal == hasLocal && other.hasLocal == hasLocal &&
other.hasMerged == hasMerged; other.hasMerged == hasMerged &&
other.selectedCount == selectedCount;
} }
@override @override
int get hashCode => int get hashCode =>
hasRemote.hashCode ^ hasLocal.hashCode ^ hasMerged.hashCode; hasRemote.hashCode ^
hasLocal.hashCode ^
hasMerged.hashCode ^
selectedCount.hashCode;
} }

View file

@ -118,7 +118,7 @@ class ControlBottomAppBar extends ConsumerWidget {
: null, : null,
), ),
), ),
if (!hasLocal) if (!hasLocal && selectionAssetState.selectedCount > 1)
ControlBoxButton( ControlBoxButton(
iconData: Icons.filter_none_rounded, iconData: Icons.filter_none_rounded,
label: "control_bottom_app_bar_stack".tr(), label: "control_bottom_app_bar_stack".tr(),

View file

@ -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/routing/router.dart';
import 'package:immich_mobile/modules/backup/models/backup_state.model.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/modules/backup/providers/backup.provider.dart';
import 'package:immich_mobile/shared/providers/server_info.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 BackUpState backupState = ref.watch(backupProvider);
final bool isEnableAutoBackup = final bool isEnableAutoBackup =
backupState.backgroundBackup || backupState.autoBackup; backupState.backgroundBackup || backupState.autoBackup;
final ServerInfoState serverInfoState = ref.watch(serverInfoProvider); final ServerInfo serverInfoState = ref.watch(serverInfoProvider);
AuthenticationState authState = ref.watch(authenticationProvider); AuthenticationState authState = ref.watch(authenticationProvider);
final user = Store.tryGet(StoreKey.currentUser); final user = Store.tryGet(StoreKey.currentUser);
buildProfilePhoto() { buildProfilePhoto() {

View file

@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.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:easy_localization/easy_localization.dart';
import 'package:immich_mobile/shared/providers/server_info.provider.dart'; import 'package:immich_mobile/shared/providers/server_info.provider.dart';
import 'package:package_info_plus/package_info_plus.dart'; import 'package:package_info_plus/package_info_plus.dart';
@ -13,7 +13,7 @@ class ServerInfoBox extends HookConsumerWidget {
@override @override
Widget build(BuildContext context, WidgetRef ref) { Widget build(BuildContext context, WidgetRef ref) {
ServerInfoState serverInfoState = ref.watch(serverInfoProvider); ServerInfo serverInfoState = ref.watch(serverInfoProvider);
final appInfo = useState({}); final appInfo = useState({});
@ -107,7 +107,7 @@ class ServerInfoBox extends HookConsumerWidget {
), ),
Text( Text(
serverInfoState.serverVersion.major > 0 serverInfoState.serverVersion.major > 0
? "${serverInfoState.serverVersion.major}.${serverInfoState.serverVersion.minor}.${serverInfoState.serverVersion.patch_}" ? "${serverInfoState.serverVersion.major}.${serverInfoState.serverVersion.minor}.${serverInfoState.serverVersion.patch}"
: "?", : "?",
style: TextStyle( style: TextStyle(
fontSize: 11, fontSize: 11,

View file

@ -7,12 +7,10 @@ import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:fluttertoast/fluttertoast.dart'; import 'package:fluttertoast/fluttertoast.dart';
import 'package:hooks_riverpod/hooks_riverpod.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.provider.dart';
import 'package:immich_mobile/modules/album/providers/album_detail.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/providers/shared_album.provider.dart';
import 'package:immich_mobile/modules/album/services/album.service.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/asset_viewer/services/asset_stack.service.dart';
import 'package:immich_mobile/modules/backup/providers/manual_upload.provider.dart'; import 'package:immich_mobile/modules/backup/providers/manual_upload.provider.dart';
import 'package:immich_mobile/modules/home/models/selection_state.dart'; import 'package:immich_mobile/modules/home/models/selection_state.dart';
@ -281,46 +279,15 @@ class HomePage extends HookConsumerWidget {
void onStack() async { void onStack() async {
try { try {
processing.value = true; processing.value = true;
if (!selectionEnabledHook.value) { if (!selectionEnabledHook.value || selection.value.length < 2) {
return; return;
} }
final parent = selection.value.elementAt(0);
final selectedAsset = selection.value.elementAt(0); selection.value.remove(parent);
await ref.read(assetStackServiceProvider).updateStack(
if (selection.value.length == 1) { parent,
final stackChildren = childrenToAdd: selection.value.toList(),
(await ref.read(assetStackProvider(selectedAsset).future)) );
.toSet();
AssetSelectionPageResult? returnPayload =
await AutoRouter.of(context).push<AssetSelectionPageResult?>(
AssetSelectionRoute(
existingAssets: stackChildren,
canDeselect: true,
query: getAssetStackSelectionQuery(ref, selectedAsset),
),
);
if (returnPayload != null) {
Set<Asset> 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(),
);
}
} finally { } finally {
processing.value = false; processing.value = false;
selectionEnabledHook.value = false; selectionEnabledHook.value = false;

View file

@ -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'; import 'package:immich_mobile/modules/settings/services/app_settings.service.dart';
class MapStateNotifier extends StateNotifier<MapState> { class MapStateNotifier extends StateNotifier<MapState> {
MapStateNotifier(this.appSettingsProvider) MapStateNotifier(this._appSettingsProvider)
: super( : super(
MapState( MapState(
isDarkTheme: appSettingsProvider isDarkTheme: _appSettingsProvider
.getSetting<bool>(AppSettingsEnum.mapThemeMode), .getSetting<bool>(AppSettingsEnum.mapThemeMode),
showFavoriteOnly: appSettingsProvider showFavoriteOnly: _appSettingsProvider
.getSetting<bool>(AppSettingsEnum.mapShowFavoriteOnly), .getSetting<bool>(AppSettingsEnum.mapShowFavoriteOnly),
includeArchived: appSettingsProvider includeArchived: _appSettingsProvider
.getSetting<bool>(AppSettingsEnum.mapIncludeArchived), .getSetting<bool>(AppSettingsEnum.mapIncludeArchived),
relativeTime: appSettingsProvider relativeTime: _appSettingsProvider
.getSetting<int>(AppSettingsEnum.mapRelativeDate), .getSetting<int>(AppSettingsEnum.mapRelativeDate),
), ),
); );
final AppSettingsService appSettingsProvider; final AppSettingsService _appSettingsProvider;
bool get isDarkTheme => state.isDarkTheme;
void switchTheme(bool isDarkTheme) { void switchTheme(bool isDarkTheme) {
appSettingsProvider.setSetting( _appSettingsProvider.setSetting(
AppSettingsEnum.mapThemeMode, AppSettingsEnum.mapThemeMode,
isDarkTheme, isDarkTheme,
); );
@ -31,7 +29,7 @@ class MapStateNotifier extends StateNotifier<MapState> {
} }
void switchFavoriteOnly(bool isFavoriteOnly) { void switchFavoriteOnly(bool isFavoriteOnly) {
appSettingsProvider.setSetting( _appSettingsProvider.setSetting(
AppSettingsEnum.mapShowFavoriteOnly, AppSettingsEnum.mapShowFavoriteOnly,
isFavoriteOnly, isFavoriteOnly,
); );
@ -39,7 +37,7 @@ class MapStateNotifier extends StateNotifier<MapState> {
} }
void switchIncludeArchived(bool isIncludeArchived) { void switchIncludeArchived(bool isIncludeArchived) {
appSettingsProvider.setSetting( _appSettingsProvider.setSetting(
AppSettingsEnum.mapIncludeArchived, AppSettingsEnum.mapIncludeArchived,
isIncludeArchived, isIncludeArchived,
); );
@ -47,7 +45,7 @@ class MapStateNotifier extends StateNotifier<MapState> {
} }
void setRelativeTime(int relativeTime) { void setRelativeTime(int relativeTime) {
appSettingsProvider.setSetting( _appSettingsProvider.setSetting(
AppSettingsEnum.mapRelativeDate, AppSettingsEnum.mapRelativeDate,
relativeTime, relativeTime,
); );

View file

@ -17,7 +17,7 @@ final mapServiceProvider = Provider(
class MapSerivce { class MapSerivce {
final ApiService _apiService; final ApiService _apiService;
final Isar _db; final Isar _db;
final log = Logger("MapService"); final _log = Logger("MapService");
MapSerivce(this._apiService, this._db); MapSerivce(this._apiService, this._db);
@ -37,7 +37,7 @@ class MapSerivce {
return markers ?? []; return markers ?? [];
} catch (error, stack) { } catch (error, stack) {
log.severe("Cannot get map markers ${error.toString()}", error, stack); _log.severe("Cannot get map markers ${error.toString()}", error, stack);
return []; return [];
} }
} }
@ -53,7 +53,7 @@ class MapSerivce {
} }
return Asset.remote(dto); return Asset.remote(dto);
} catch (error, stack) { } catch (error, stack) {
log.severe( _log.severe(
"Cannot get asset for marker ${error.toString()}", "Cannot get asset for marker ${error.toString()}",
error, error,
stack, stack,

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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 { class ServerInfo {
final ServerVersionResponseDto serverVersion; final ServerVersion serverVersion;
final ServerFeaturesDto serverFeatures; final ServerFeatures serverFeatures;
final ServerConfigDto serverConfig; final ServerConfig serverConfig;
final ServerDiskInfo serverDiskInfo;
final bool isVersionMismatch; final bool isVersionMismatch;
final String versionMismatchErrorMessage; final String versionMismatchErrorMessage;
ServerInfoState({ ServerInfo({
required this.serverVersion, required this.serverVersion,
required this.serverFeatures, required this.serverFeatures,
required this.serverConfig, required this.serverConfig,
required this.isVersionMismatch, required this.isVersionMismatch,
required this.serverDiskInfo,
required this.versionMismatchErrorMessage, required this.versionMismatchErrorMessage,
}); });
ServerInfoState copyWith({ ServerInfo copyWith({
ServerVersionResponseDto? serverVersion, ServerVersion? serverVersion,
ServerFeaturesDto? serverFeatures, ServerFeatures? serverFeatures,
ServerConfigDto? serverConfig, ServerConfig? serverConfig,
ServerDiskInfo? serverDiskInfo,
bool? isVersionMismatch, bool? isVersionMismatch,
String? versionMismatchErrorMessage, String? versionMismatchErrorMessage,
}) { }) {
return ServerInfoState( return ServerInfo(
serverVersion: serverVersion ?? this.serverVersion, serverVersion: serverVersion ?? this.serverVersion,
serverFeatures: serverFeatures ?? this.serverFeatures, serverFeatures: serverFeatures ?? this.serverFeatures,
serverConfig: serverConfig ?? this.serverConfig, serverConfig: serverConfig ?? this.serverConfig,
isVersionMismatch: isVersionMismatch ?? this.isVersionMismatch, isVersionMismatch: isVersionMismatch ?? this.isVersionMismatch,
versionMismatchErrorMessage: versionMismatchErrorMessage:
versionMismatchErrorMessage ?? this.versionMismatchErrorMessage, versionMismatchErrorMessage ?? this.versionMismatchErrorMessage,
serverDiskInfo: serverDiskInfo ?? this.serverDiskInfo,
); );
} }
@override @override
String toString() { 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 @override
bool operator ==(Object other) { bool operator ==(Object other) {
if (identical(this, other)) return true; if (identical(this, other)) return true;
return other is ServerInfoState && return other is ServerInfo &&
other.serverVersion == serverVersion && other.serverVersion == serverVersion &&
other.serverFeatures == serverFeatures && other.serverFeatures == serverFeatures &&
other.serverConfig == serverConfig && other.serverConfig == serverConfig &&
other.serverDiskInfo == serverDiskInfo &&
other.isVersionMismatch == isVersionMismatch && other.isVersionMismatch == isVersionMismatch &&
other.versionMismatchErrorMessage == versionMismatchErrorMessage; other.versionMismatchErrorMessage == versionMismatchErrorMessage;
} }
@ -55,6 +63,7 @@ class ServerInfoState {
serverFeatures.hashCode ^ serverFeatures.hashCode ^
serverConfig.hashCode ^ serverConfig.hashCode ^
isVersionMismatch.hashCode ^ isVersionMismatch.hashCode ^
versionMismatchErrorMessage.hashCode; versionMismatchErrorMessage.hashCode ^
serverDiskInfo.hashCode;
} }
} }

View file

@ -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;
}
}

View file

@ -282,31 +282,3 @@ QueryBuilder<Asset, Asset, QAfterSortBy>? getRemoteAssetQuery(WidgetRef ref) {
.stackParentIdIsNull() .stackParentIdIsNull()
.sortByFileCreatedAtDesc(); .sortByFileCreatedAtDesc();
} }
QueryBuilder<Asset, Asset, QAfterSortBy>? 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();
}

View file

@ -1,40 +1,36 @@
import 'package:easy_localization/easy_localization.dart'; import 'package:easy_localization/easy_localization.dart';
import 'package:hooks_riverpod/hooks_riverpod.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: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'; import 'package:package_info_plus/package_info_plus.dart';
class ServerInfoNotifier extends StateNotifier<ServerInfoState> { class ServerInfoNotifier extends StateNotifier<ServerInfo> {
ServerInfoNotifier(this._serverInfoService) ServerInfoNotifier(this._serverInfoService)
: super( : super(
ServerInfoState( ServerInfo(
serverVersion: ServerVersionResponseDto( serverVersion: const ServerVersion(
major: 0, major: 0,
patch_: 0,
minor: 0, minor: 0,
patch: 0,
), ),
serverFeatures: ServerFeaturesDto( serverFeatures: const ServerFeatures(
clipEncode: true,
configFile: false,
facialRecognition: true,
map: true, map: true,
oauth: false,
oauthAutoLaunch: false,
passwordLogin: true,
search: true,
sidecar: true,
tagImage: true,
trash: true, trash: true,
reverseGeocoding: true,
), ),
serverConfig: ServerConfigDto( serverConfig: const ServerConfig(
loginPageMessage: "",
mapTileUrl: "https://tile.openstreetmap.org/{z}/{x}/{y}.png", mapTileUrl: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
oauthButtonText: "",
trashDays: 30, trashDays: 30,
isInitialized: false, ),
serverDiskInfo: const ServerDiskInfo(
diskAvailable: "0",
diskSize: "0",
diskUse: "0",
diskUsagePercentage: 0,
), ),
isVersionMismatch: false, isVersionMismatch: false,
versionMismatchErrorMessage: "", versionMismatchErrorMessage: "",
@ -50,8 +46,7 @@ class ServerInfoNotifier extends StateNotifier<ServerInfoState> {
} }
getServerVersion() async { getServerVersion() async {
ServerVersionResponseDto? serverVersion = final serverVersion = await _serverInfoService.getServerVersion();
await _serverInfoService.getServerVersion();
if (serverVersion == null) { if (serverVersion == null) {
state = state.copyWith( state = state.copyWith(
@ -94,8 +89,7 @@ class ServerInfoNotifier extends StateNotifier<ServerInfoState> {
} }
getServerFeatures() async { getServerFeatures() async {
ServerFeaturesDto? serverFeatures = final serverFeatures = await _serverInfoService.getServerFeatures();
await _serverInfoService.getServerFeatures();
if (serverFeatures == null) { if (serverFeatures == null) {
return; return;
} }
@ -103,7 +97,7 @@ class ServerInfoNotifier extends StateNotifier<ServerInfoState> {
} }
getServerConfig() async { getServerConfig() async {
ServerConfigDto? serverConfig = await _serverInfoService.getServerConfig(); final serverConfig = await _serverInfoService.getServerConfig();
if (serverConfig == null) { if (serverConfig == null) {
return; return;
} }
@ -126,6 +120,6 @@ class ServerInfoNotifier extends StateNotifier<ServerInfoState> {
} }
final serverInfoProvider = final serverInfoProvider =
StateNotifierProvider<ServerInfoNotifier, ServerInfoState>((ref) { StateNotifierProvider<ServerInfoNotifier, ServerInfo>((ref) {
return ServerInfoNotifier(ref.watch(serverInfoServiceProvider)); return ServerInfoNotifier(ref.watch(serverInfoServiceProvider));
}); });

View file

@ -1,8 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.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/providers/api.provider.dart';
import 'package:immich_mobile/shared/services/api.service.dart'; import 'package:immich_mobile/shared/services/api.service.dart';
import 'package:openapi/api.dart';
final serverInfoServiceProvider = Provider( final serverInfoServiceProvider = Provider(
(ref) => ServerInfoService( (ref) => ServerInfoService(
@ -15,39 +18,51 @@ class ServerInfoService {
ServerInfoService(this._apiService); ServerInfoService(this._apiService);
Future<ServerInfoResponseDto?> getServerInfo() async { Future<ServerDiskInfo?> getServerInfo() async {
try { try {
return await _apiService.serverInfoApi.getServerInfo(); final dto = await _apiService.serverInfoApi.getServerInfo();
if (dto != null) {
return ServerDiskInfo.fromDto(dto);
}
} catch (e) { } catch (e) {
debugPrint("Error [getServerInfo] ${e.toString()}"); debugPrint("Error [getServerInfo] ${e.toString()}");
return null;
} }
return null;
} }
Future<ServerVersionResponseDto?> getServerVersion() async { Future<ServerVersion?> getServerVersion() async {
try { try {
return await _apiService.serverInfoApi.getServerVersion(); final dto = await _apiService.serverInfoApi.getServerVersion();
if (dto != null) {
return ServerVersion.fromDto(dto);
}
} catch (e) { } catch (e) {
debugPrint("Error [getServerVersion] ${e.toString()}"); debugPrint("Error [getServerVersion] ${e.toString()}");
return null;
} }
return null;
} }
Future<ServerFeaturesDto?> getServerFeatures() async { Future<ServerFeatures?> getServerFeatures() async {
try { try {
return await _apiService.serverInfoApi.getServerFeatures(); final dto = await _apiService.serverInfoApi.getServerFeatures();
if (dto != null) {
return ServerFeatures.fromDto(dto);
}
} catch (e) { } catch (e) {
debugPrint("Error [getServerFeatures] ${e.toString()}"); debugPrint("Error [getServerFeatures] ${e.toString()}");
return null;
} }
return null;
} }
Future<ServerConfigDto?> getServerConfig() async { Future<ServerConfig?> getServerConfig() async {
try { try {
return await _apiService.serverInfoApi.getServerConfig(); final dto = await _apiService.serverInfoApi.getServerConfig();
if (dto != null) {
return ServerConfig.fromDto(dto);
}
} catch (e) { } catch (e) {
debugPrint("Error [getServerConfig] ${e.toString()}"); debugPrint("Error [getServerConfig] ${e.toString()}");
return null;
} }
return null;
} }
} }