From 5bbd7df137471453f8e96faa47f6189c2f147fbc Mon Sep 17 00:00:00 2001 From: bwees Date: Tue, 20 Jan 2026 10:23:43 -0600 Subject: [PATCH] feat: fix discriminated type parsing --- mobile/lib/domain/services/asset.service.dart | 9 +++ .../repositories/remote_asset.repository.dart | 12 +++- .../pages/editing/drift_edit.page.dart | 9 +++ .../infrastructure/asset.provider.dart | 3 +- ...sset_edit_action_list_dto_edits_inner.dart | 55 ++++++++++++++----- open-api/bin/generate-open-api.sh | 1 + ...dit_action_list_dto_edits_inner.dart.patch | 44 +++++++++++++++ 7 files changed, 116 insertions(+), 17 deletions(-) create mode 100644 open-api/patch/asset_edit_action_list_dto_edits_inner.dart.patch diff --git a/mobile/lib/domain/services/asset.service.dart b/mobile/lib/domain/services/asset.service.dart index 198733b3c8..b9d02f0367 100644 --- a/mobile/lib/domain/services/asset.service.dart +++ b/mobile/lib/domain/services/asset.service.dart @@ -4,6 +4,7 @@ import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/extensions/platform_extensions.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart'; +import 'package:openapi/api.dart'; typedef _AssetVideoDimension = ({double? width, double? height, bool isFlipped}); @@ -116,4 +117,12 @@ class AssetService { Future> getSourceAlbums(String localAssetId, {BackupSelection? backupSelection}) { return _localAssetRepository.getSourceAlbums(localAssetId, backupSelection: backupSelection); } + + Future getAssetEdits(String assetId) { + return _remoteAssetRepository.getAssetEdits(assetId); + } + + Future editAsset(String assetId, AssetEditActionListDto edits) { + return _remoteAssetRepository.editAsset(assetId, edits); + } } diff --git a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart index 96c204ea0e..6d3522ee3e 100644 --- a/mobile/lib/infrastructure/repositories/remote_asset.repository.dart +++ b/mobile/lib/infrastructure/repositories/remote_asset.repository.dart @@ -9,11 +9,13 @@ import 'package:immich_mobile/infrastructure/entities/remote_asset.entity.drift. import 'package:immich_mobile/infrastructure/entities/stack.entity.drift.dart'; import 'package:immich_mobile/infrastructure/repositories/db.repository.dart'; import 'package:maplibre_gl/maplibre_gl.dart'; +import 'package:openapi/api.dart' hide AssetVisibility; class RemoteAssetRepository extends DriftDatabaseRepository { final Drift _db; + final AssetsApi _api; - const RemoteAssetRepository(this._db) : super(_db); + const RemoteAssetRepository(this._db, this._api) : super(_db); /// For testing purposes Future> getSome(String userId) { @@ -258,4 +260,12 @@ class RemoteAssetRepository extends DriftDatabaseRepository { Future getCount() { return _db.managers.remoteAssetEntity.count(); } + + Future getAssetEdits(String assetId) async { + return _api.getAssetEdits(assetId); + } + + Future editAsset(String assetId, AssetEditActionListDto edits) { + return _api.editAsset(assetId, edits); + } } diff --git a/mobile/lib/presentation/pages/editing/drift_edit.page.dart b/mobile/lib/presentation/pages/editing/drift_edit.page.dart index 7e49348e19..af51eedce9 100644 --- a/mobile/lib/presentation/pages/editing/drift_edit.page.dart +++ b/mobile/lib/presentation/pages/editing/drift_edit.page.dart @@ -11,6 +11,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/entities/asset.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/providers/background_sync.provider.dart'; +import 'package:immich_mobile/providers/infrastructure/asset.provider.dart'; import 'package:immich_mobile/repositories/file_media.repository.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/foreground_upload.service.dart'; @@ -91,6 +92,8 @@ class DriftEditImagePage extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final edits = ref.read(assetServiceProvider).getAssetEdits(asset.remoteId!); + return Scaffold( appBar: AppBar( title: Text("edit".tr()), @@ -139,6 +142,12 @@ class DriftEditImagePage extends ConsumerWidget { child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ + FutureBuilder( + future: edits, + builder: (ctx, data) { + return Text(data.hasData ? data.data?.edits.length.toString() ?? "" : "..."); + }, + ), Column( mainAxisAlignment: MainAxisAlignment.center, children: [ diff --git a/mobile/lib/providers/infrastructure/asset.provider.dart b/mobile/lib/providers/infrastructure/asset.provider.dart index 70cb200bf1..585c9b678d 100644 --- a/mobile/lib/providers/infrastructure/asset.provider.dart +++ b/mobile/lib/providers/infrastructure/asset.provider.dart @@ -3,6 +3,7 @@ import 'package:immich_mobile/domain/services/asset.service.dart'; import 'package:immich_mobile/infrastructure/repositories/local_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/remote_asset.repository.dart'; import 'package:immich_mobile/infrastructure/repositories/trashed_local_asset.repository.dart'; +import 'package:immich_mobile/providers/api.provider.dart'; import 'package:immich_mobile/providers/infrastructure/db.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; @@ -11,7 +12,7 @@ final localAssetRepository = Provider( ); final remoteAssetRepositoryProvider = Provider( - (ref) => RemoteAssetRepository(ref.watch(driftProvider)), + (ref) => RemoteAssetRepository(ref.watch(driftProvider), ref.watch(apiServiceProvider).assetsApi), ); final trashedLocalAssetRepository = Provider( diff --git a/mobile/openapi/lib/model/asset_edit_action_list_dto_edits_inner.dart b/mobile/openapi/lib/model/asset_edit_action_list_dto_edits_inner.dart index c4c0496631..d36b396358 100644 --- a/mobile/openapi/lib/model/asset_edit_action_list_dto_edits_inner.dart +++ b/mobile/openapi/lib/model/asset_edit_action_list_dto_edits_inner.dart @@ -19,26 +19,26 @@ class AssetEditActionListDtoEditsInner { AssetEditAction action; - MirrorParameters parameters; + /// Union type: can be MirrorParameters, RotateParameters, or CropParameters + Object parameters; @override - bool operator ==(Object other) => identical(this, other) || other is AssetEditActionListDtoEditsInner && - other.action == action && - other.parameters == parameters; + bool operator ==(Object other) => + identical(this, other) || + other is AssetEditActionListDtoEditsInner && other.action == action && other.parameters == parameters; @override int get hashCode => - // ignore: unnecessary_parenthesis - (action.hashCode) + - (parameters.hashCode); + // ignore: unnecessary_parenthesis + (action.hashCode) + (parameters.hashCode); @override String toString() => 'AssetEditActionListDtoEditsInner[action=$action, parameters=$parameters]'; Map toJson() { final json = {}; - json[r'action'] = this.action; - json[r'parameters'] = this.parameters; + json[r'action'] = this.action; + json[r'parameters'] = this.parameters; return json; } @@ -50,15 +50,35 @@ class AssetEditActionListDtoEditsInner { if (value is Map) { final json = value.cast(); + final action = AssetEditAction.fromJson(json[r'action'])!; + final parametersJson = json[r'parameters']; + + // Deserialize parameters based on action type + Object parameters = {}; + switch (action) { + case AssetEditAction.crop: + parameters = CropParameters.fromJson(parametersJson)!; + break; + case AssetEditAction.rotate: + parameters = RotateParameters.fromJson(parametersJson)!; + break; + case AssetEditAction.mirror: + parameters = MirrorParameters.fromJson(parametersJson)!; + break; + } + return AssetEditActionListDtoEditsInner( - action: AssetEditAction.fromJson(json[r'action'])!, - parameters: MirrorParameters.fromJson(json[r'parameters'])!, + action: action, + parameters: parameters, ); } return null; } - static List listFromJson(dynamic json, {bool growable = false,}) { + static List listFromJson( + dynamic json, { + bool growable = false, + }) { final result = []; if (json is List && json.isNotEmpty) { for (final row in json) { @@ -86,13 +106,19 @@ class AssetEditActionListDtoEditsInner { } // maps a json object with a list of AssetEditActionListDtoEditsInner-objects as value to a dart map - static Map> mapListFromJson(dynamic json, {bool growable = false,}) { + static Map> mapListFromJson( + dynamic json, { + bool growable = false, + }) { final map = >{}; if (json is Map && json.isNotEmpty) { // ignore: parameter_assignments json = json.cast(); for (final entry in json.entries) { - map[entry.key] = AssetEditActionListDtoEditsInner.listFromJson(entry.value, growable: growable,); + map[entry.key] = AssetEditActionListDtoEditsInner.listFromJson( + entry.value, + growable: growable, + ); } } return map; @@ -104,4 +130,3 @@ class AssetEditActionListDtoEditsInner { 'parameters', }; } - diff --git a/open-api/bin/generate-open-api.sh b/open-api/bin/generate-open-api.sh index 43292089d7..7caf30f714 100755 --- a/open-api/bin/generate-open-api.sh +++ b/open-api/bin/generate-open-api.sh @@ -21,6 +21,7 @@ function dart { patch --no-backup-if-mismatch -u ../mobile/openapi/lib/api_client.dart <./patch/api_client.dart.patch patch --no-backup-if-mismatch -u ../mobile/openapi/lib/api.dart <./patch/api.dart.patch patch --no-backup-if-mismatch -u ../mobile/openapi/pubspec.yaml <./patch/pubspec_immich_mobile.yaml.patch + patch --no-backup-if-mismatch -u ../mobile/openapi/lib/model/asset_edit_action_list_dto_edits_inner.dart <./patch/asset_edit_action_list_dto_edits_inner.dart.patch # Don't include analysis_options.yaml for the generated openapi files # so that language servers can properly exclude the mobile/openapi directory rm ../mobile/openapi/analysis_options.yaml diff --git a/open-api/patch/asset_edit_action_list_dto_edits_inner.dart.patch b/open-api/patch/asset_edit_action_list_dto_edits_inner.dart.patch new file mode 100644 index 0000000000..5e1d39e300 --- /dev/null +++ b/open-api/patch/asset_edit_action_list_dto_edits_inner.dart.patch @@ -0,0 +1,44 @@ +@@ -19,7 +19,8 @@ + + AssetEditAction action; + +- MirrorParameters parameters; ++ /// Union type: can be MirrorParameters, RotateParameters, or CropParameters ++ Object parameters; + + @override + bool operator ==(Object other) => identical(this, other) || other is AssetEditActionListDtoEditsInner && +@@ -50,9 +51,26 @@ + if (value is Map) { + final json = value.cast(); + ++ final action = AssetEditAction.fromJson(json[r'action'])!; ++ final parametersJson = json[r'parameters']; ++ ++ // Deserialize parameters based on action type ++ Object parameters = {}; ++ switch (action) { ++ case AssetEditAction.crop: ++ parameters = CropParameters.fromJson(parametersJson)!; ++ break; ++ case AssetEditAction.rotate: ++ parameters = RotateParameters.fromJson(parametersJson)!; ++ break; ++ case AssetEditAction.mirror: ++ parameters = MirrorParameters.fromJson(parametersJson)!; ++ break; ++ } ++ + return AssetEditActionListDtoEditsInner( +- action: AssetEditAction.fromJson(json[r'action'])!, +- parameters: MirrorParameters.fromJson(json[r'parameters'])!, ++ action: action, ++ parameters: parameters, + ); + } + return null; +@@ -104,4 +122,3 @@ + 'parameters', + }; + } +-