From 52edcc0c74c2430418f88fe94700b549dac84e90 Mon Sep 17 00:00:00 2001 From: shenlong <139912620+shenlong-tanwen@users.noreply.github.com> Date: Wed, 12 Aug 2026 21:12:11 +0530 Subject: [PATCH] refactor: unify person model (#30659) Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com> --- mobile/lib/domain/models/person.model.dart | 29 ++----------------- .../lib/domain/services/people.service.dart | 6 ++-- .../repositories/people.repository.dart | 21 +++----------- .../models/search/search_filter.model.dart | 4 +-- .../presentation/pages/drift_person.page.dart | 4 +-- .../pages/search/drift_search.page.dart | 2 +- .../asset_details/people_details.widget.dart | 4 +-- .../person_edit_birthday_modal.widget.dart | 2 +- .../people/person_edit_name_modal.widget.dart | 2 +- .../infrastructure/people.provider.dart | 4 +-- .../lib/providers/search/people.provider.dart | 2 +- .../repositories/person_api.repository.dart | 14 +++------ mobile/lib/services/person.service.dart | 4 +-- mobile/lib/utils/people.utils.dart | 4 +-- .../widgets/common/person_sliver_app_bar.dart | 4 +-- .../search/search_filter/people_picker.dart | 6 ++-- 16 files changed, 34 insertions(+), 78 deletions(-) diff --git a/mobile/lib/domain/models/person.model.dart b/mobile/lib/domain/models/person.model.dart index 56e6d3990d..94ead6a2e9 100644 --- a/mobile/lib/domain/models/person.model.dart +++ b/mobile/lib/domain/models/person.model.dart @@ -3,32 +3,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; part 'person.model.freezed.dart'; -// TODO: Remove PersonDto once Isar is removed @freezed -abstract class PersonDto with _$PersonDto { - const factory PersonDto({ - required String id, - DateTime? birthDate, - required bool isHidden, - required String name, - required String thumbnailPath, - DateTime? updatedAt, - }) = _PersonDto; -} - -// Model for a person stored in the server -@freezed -abstract class DriftPerson with _$DriftPerson { - const factory DriftPerson({ - required String id, - required DateTime createdAt, - required DateTime updatedAt, - required String ownerId, - required String name, - String? faceAssetId, - required bool isFavorite, - required bool isHidden, - required String? color, - DateTime? birthDate, - }) = _DriftPerson; +abstract class Person with _$Person { + const factory Person({required String id, required String name, DateTime? updatedAt, DateTime? birthDate}) = _Person; } diff --git a/mobile/lib/domain/services/people.service.dart b/mobile/lib/domain/services/people.service.dart index 73972dbdc0..e86d43b283 100644 --- a/mobile/lib/domain/services/people.service.dart +++ b/mobile/lib/domain/services/people.service.dart @@ -10,15 +10,15 @@ class DriftPeopleService { const DriftPeopleService(this._repository, this._personApiRepository); - Future get(String personId) { + Future get(String personId) { return _repository.get(personId); } - Future> getAssetPeople(String assetId) { + Future> getAssetPeople(String assetId) { return _repository.getAssetPeople(assetId); } - Future> getAllPeople({int minFaces = 3}) { + Future> getAllPeople({int minFaces = 3}) { return _repository.getAllPeople(minFaces: minFaces); } diff --git a/mobile/lib/infrastructure/repositories/people.repository.dart b/mobile/lib/infrastructure/repositories/people.repository.dart index 0dd73cc81d..b539c6b2fd 100644 --- a/mobile/lib/infrastructure/repositories/people.repository.dart +++ b/mobile/lib/infrastructure/repositories/people.repository.dart @@ -8,14 +8,14 @@ class DriftPeopleRepository extends DriftDatabaseRepository { final Drift _db; const DriftPeopleRepository(this._db) : super(_db); - Future get(String personId) async { + Future get(String personId) async { final query = _db.select(_db.personEntity)..where((row) => row.id.equals(personId)); final result = await query.getSingleOrNull(); return result?.toDto(); } - Future> getAssetPeople(String assetId) async { + Future> getAssetPeople(String assetId) async { // An asset can have multiple face records for the same person (e.g., metadata // imports alongside ML detections). Use a subquery instead of a join so each // person is returned once, regardless of how many of their faces are on the asset @@ -33,7 +33,7 @@ class DriftPeopleRepository extends DriftDatabaseRepository { return query.map((row) => row.toDto()).get(); } - Future> getAllPeople({int minFaces = 3}) async { + Future> getAllPeople({int minFaces = 3}) async { final people = _db.personEntity; final faces = _db.assetFaceEntity; final assets = _db.remoteAssetEntity; @@ -76,18 +76,5 @@ class DriftPeopleRepository extends DriftDatabaseRepository { } extension on PersonEntityData { - DriftPerson toDto() { - return DriftPerson( - id: id, - createdAt: createdAt, - updatedAt: updatedAt, - ownerId: ownerId, - name: name, - faceAssetId: faceAssetId, - isFavorite: isFavorite, - isHidden: isHidden, - color: color, - birthDate: birthDate, - ); - } + Person toDto() => Person(id: id, updatedAt: updatedAt, name: name, birthDate: birthDate); } diff --git a/mobile/lib/models/search/search_filter.model.dart b/mobile/lib/models/search/search_filter.model.dart index 7e5e1b819e..a9cc13ba6a 100644 --- a/mobile/lib/models/search/search_filter.model.dart +++ b/mobile/lib/models/search/search_filter.model.dart @@ -194,7 +194,7 @@ class SearchFilter { String? language; String? assetId; List? tagIds; - Set people; + Set people; SearchLocationFilter location; SearchCameraFilter camera; SearchDateFilter date; @@ -250,7 +250,7 @@ class SearchFilter { String? language, String? ocr, String? assetId, - Set? people, + Set? people, List? tagIds, SearchLocationFilter? location, SearchCameraFilter? camera, diff --git a/mobile/lib/presentation/pages/drift_person.page.dart b/mobile/lib/presentation/pages/drift_person.page.dart index 7389f506d5..cad3b7b6a6 100644 --- a/mobile/lib/presentation/pages/drift_person.page.dart +++ b/mobile/lib/presentation/pages/drift_person.page.dart @@ -14,7 +14,7 @@ import 'package:immich_mobile/widgets/common/person_sliver_app_bar.dart'; @RoutePage() class DriftPersonPage extends ConsumerStatefulWidget { - final DriftPerson person; + final Person person; const DriftPersonPage({super.key, required this.person}); @@ -23,7 +23,7 @@ class DriftPersonPage extends ConsumerStatefulWidget { } class _DriftPersonPageState extends ConsumerState { - late DriftPerson _person; + late Person _person; @override void initState() { diff --git a/mobile/lib/presentation/pages/search/drift_search.page.dart b/mobile/lib/presentation/pages/search/drift_search.page.dart index dfcb5abd4d..e364e0d2f8 100644 --- a/mobile/lib/presentation/pages/search/drift_search.page.dart +++ b/mobile/lib/presentation/pages/search/drift_search.page.dart @@ -129,7 +129,7 @@ class DriftSearchPage extends HookConsumerWidget { void showPeoplePicker() { var people = filter.value.people; - void handleOnSelect(Set value) { + void handleOnSelect(Set value) { people = value; } diff --git a/mobile/lib/presentation/widgets/asset_viewer/asset_details/people_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/asset_details/people_details.widget.dart index 739fe0a0d9..00c20f35bd 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/asset_details/people_details.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/asset_details/people_details.widget.dart @@ -30,7 +30,7 @@ class PeopleDetails extends ConsumerWidget { final peopleFuture = ref.watch(driftPeopleAssetProvider(asset.id)); - Future showNameEditModal(DriftPerson person) async { + Future showNameEditModal(Person person) async { await showDialog( context: context, useRootNavigator: false, @@ -96,7 +96,7 @@ class PeopleDetails extends ConsumerWidget { } class _Avatar extends StatelessWidget { - final DriftPerson person; + final Person person; final DateTime assetFileCreatedAt; final VoidCallback? onTap; final VoidCallback? onNameTap; diff --git a/mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart b/mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart index 0b8a08a5d8..a4903c7a29 100644 --- a/mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart +++ b/mobile/lib/presentation/widgets/people/person_edit_birthday_modal.widget.dart @@ -11,7 +11,7 @@ import 'package:immich_mobile/widgets/common/immich_toast.dart'; import 'package:scroll_date_picker/scroll_date_picker.dart'; class DriftPersonBirthdayEditForm extends ConsumerStatefulWidget { - final DriftPerson person; + final Person person; const DriftPersonBirthdayEditForm({super.key, required this.person}); diff --git a/mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart b/mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart index 3b0e62ab1a..5e8c634181 100644 --- a/mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart +++ b/mobile/lib/presentation/widgets/people/person_edit_name_modal.widget.dart @@ -9,7 +9,7 @@ import 'package:immich_mobile/utils/debug_print.dart'; import 'package:immich_mobile/widgets/common/immich_toast.dart'; class DriftPersonNameEditForm extends ConsumerStatefulWidget { - final DriftPerson person; + final Person person; const DriftPersonNameEditForm({super.key, required this.person}); diff --git a/mobile/lib/providers/infrastructure/people.provider.dart b/mobile/lib/providers/infrastructure/people.provider.dart index 1939940975..73a35958bd 100644 --- a/mobile/lib/providers/infrastructure/people.provider.dart +++ b/mobile/lib/providers/infrastructure/people.provider.dart @@ -14,12 +14,12 @@ final driftPeopleServiceProvider = Provider( (ref) => DriftPeopleService(ref.watch(driftPeopleRepositoryProvider), ref.watch(personApiRepositoryProvider)), ); -final driftPeopleAssetProvider = FutureProvider.family, String>((ref, assetId) async { +final driftPeopleAssetProvider = FutureProvider.family, String>((ref, assetId) async { final service = ref.watch(driftPeopleServiceProvider); return service.getAssetPeople(assetId); }); -final driftGetAllPeopleProvider = FutureProvider>((ref) async { +final driftGetAllPeopleProvider = FutureProvider>((ref) async { final service = ref.watch(driftPeopleServiceProvider); final prefs = await ref.watch(userMetadataPreferencesProvider.future); return service.getAllPeople(minFaces: prefs?.minimumFaces ?? 3); diff --git a/mobile/lib/providers/search/people.provider.dart b/mobile/lib/providers/search/people.provider.dart index 1bd58509f5..f9fc1ec133 100644 --- a/mobile/lib/providers/search/people.provider.dart +++ b/mobile/lib/providers/search/people.provider.dart @@ -2,7 +2,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/person.model.dart'; import 'package:immich_mobile/services/person.service.dart'; -final getAllPeopleProvider = FutureProvider.autoDispose>((ref) async { +final getAllPeopleProvider = FutureProvider.autoDispose>((ref) async { final PersonService personService = ref.read(personServiceProvider); final people = await personService.getAllPeople(); diff --git a/mobile/lib/repositories/person_api.repository.dart b/mobile/lib/repositories/person_api.repository.dart index 158e3ac333..5804e1da3b 100644 --- a/mobile/lib/repositories/person_api.repository.dart +++ b/mobile/lib/repositories/person_api.repository.dart @@ -11,12 +11,12 @@ class PersonApiRepository extends ApiRepository { PersonApiRepository(this._api); - Future> getAll() async { + Future> getAll() async { final dto = await checkNull(_api.getAllPeople()); return dto.people.map(_toPerson).toList(); } - Future update(String id, {String? name, DateTime? birthday}) async { + Future update(String id, {String? name, DateTime? birthday}) async { final birthdayUtc = birthday == null ? null : DateTime.utc(birthday.year, birthday.month, birthday.day); final dto = PersonUpdateDto( name: name == null ? const Optional.absent() : Optional.present(name), @@ -26,12 +26,6 @@ class PersonApiRepository extends ApiRepository { return _toPerson(response); } - static PersonDto _toPerson(PersonResponseDto dto) => PersonDto( - birthDate: dto.birthDate, - id: dto.id, - isHidden: dto.isHidden, - name: dto.name, - thumbnailPath: dto.thumbnailPath, - updatedAt: dto.updatedAt.orElse(null), - ); + static Person _toPerson(PersonResponseDto dto) => + .new(birthDate: dto.birthDate, id: dto.id, name: dto.name, updatedAt: dto.updatedAt.value); } diff --git a/mobile/lib/services/person.service.dart b/mobile/lib/services/person.service.dart index 0d589ea71d..6a9226a893 100644 --- a/mobile/lib/services/person.service.dart +++ b/mobile/lib/services/person.service.dart @@ -12,7 +12,7 @@ class PersonService { final PersonApiRepository _personApiRepository; PersonService(this._personApiRepository); - Future> getAllPeople() async { + Future> getAllPeople() async { try { return await _personApiRepository.getAll(); } catch (error, stack) { @@ -21,7 +21,7 @@ class PersonService { } } - Future updateName(String id, String name) async { + Future updateName(String id, String name) async { try { return await _personApiRepository.update(id, name: name); } catch (error, stack) { diff --git a/mobile/lib/utils/people.utils.dart b/mobile/lib/utils/people.utils.dart index 8da5a5a3eb..bd2b607e0b 100644 --- a/mobile/lib/utils/people.utils.dart +++ b/mobile/lib/utils/people.utils.dart @@ -34,7 +34,7 @@ int _calculateAgeInMonths(DateTime birthDate, DateTime referenceDate) { (referenceDate.day < birthDate.day ? 1 : 0); } -Future showNameEditModal(BuildContext context, DriftPerson person) { +Future showNameEditModal(BuildContext context, Person person) { return showDialog( context: context, useRootNavigator: false, @@ -44,7 +44,7 @@ Future showNameEditModal(BuildContext context, DriftPerson person) { ); } -Future showBirthdayEditModal(BuildContext context, DriftPerson person) { +Future showBirthdayEditModal(BuildContext context, Person person) { return showDialog( context: context, useRootNavigator: false, diff --git a/mobile/lib/widgets/common/person_sliver_app_bar.dart b/mobile/lib/widgets/common/person_sliver_app_bar.dart index 4b159447e6..56eb3c6094 100644 --- a/mobile/lib/widgets/common/person_sliver_app_bar.dart +++ b/mobile/lib/widgets/common/person_sliver_app_bar.dart @@ -28,7 +28,7 @@ class PersonSliverAppBar extends ConsumerStatefulWidget { required this.onBirthdayTap, }); - final DriftPerson person; + final Person person; final VoidCallback onNameTap; final VoidCallback onBirthdayTap; final VoidCallback onShowOptions; @@ -137,7 +137,7 @@ class _MesmerizingSliverAppBarState extends ConsumerState { class _ExpandedBackground extends ConsumerStatefulWidget { final double scrollProgress; - final DriftPerson person; + final Person person; final VoidCallback onNameTap; final VoidCallback onBirthdayTap; diff --git a/mobile/lib/widgets/search/search_filter/people_picker.dart b/mobile/lib/widgets/search/search_filter/people_picker.dart index b4e73e97f9..1995851904 100644 --- a/mobile/lib/widgets/search/search_filter/people_picker.dart +++ b/mobile/lib/widgets/search/search_filter/people_picker.dart @@ -15,8 +15,8 @@ import 'package:immich_mobile/widgets/common/search_field.dart'; class PeoplePicker extends HookConsumerWidget { const PeoplePicker({super.key, required this.onSelect, this.filter}); - final Function(Set) onSelect; - final Set? filter; + final Function(Set) onSelect; + final Set? filter; @override Widget build(BuildContext context, WidgetRef ref) { @@ -24,7 +24,7 @@ class PeoplePicker extends HookConsumerWidget { const imageSize = 60.0; final searchQuery = useState(''); final people = ref.watch(getAllPeopleProvider); - final selectedPeople = useState>(filter ?? {}); + final selectedPeople = useState>(filter ?? {}); return Column( children: [