Skip to content

Commit 29f16c6

Browse files
authored
feat: people page/sheet/detail (#20309)
1 parent 268b411 commit 29f16c6

34 files changed

+1562
-97
lines changed

i18n/en.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"add_a_location": "Add a location",
1515
"add_a_name": "Add a name",
1616
"add_a_title": "Add a title",
17+
"add_birthday": "Add a birthday",
1718
"add_endpoint": "Add endpoint",
1819
"add_exclusion_pattern": "Add exclusion pattern",
1920
"add_import_path": "Add import path",
@@ -828,6 +829,7 @@
828829
"edit": "Edit",
829830
"edit_album": "Edit album",
830831
"edit_avatar": "Edit avatar",
832+
"edit_birthday": "Edit Birthday",
831833
"edit_date": "Edit date",
832834
"edit_date_and_time": "Edit date and time",
833835
"edit_description": "Edit description",

mobile/lib/domain/models/person.model.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class PersonDto {
9191
}
9292

9393
// Model for a person stored in the server
94-
class Person {
94+
class DriftPerson {
9595
final String id;
9696
final DateTime createdAt;
9797
final DateTime updatedAt;
@@ -103,7 +103,7 @@ class Person {
103103
final String? color;
104104
final DateTime? birthDate;
105105

106-
const Person({
106+
const DriftPerson({
107107
required this.id,
108108
required this.createdAt,
109109
required this.updatedAt,
@@ -116,7 +116,7 @@ class Person {
116116
this.birthDate,
117117
});
118118

119-
Person copyWith({
119+
DriftPerson copyWith({
120120
String? id,
121121
DateTime? createdAt,
122122
DateTime? updatedAt,
@@ -128,7 +128,7 @@ class Person {
128128
String? color,
129129
DateTime? birthDate,
130130
}) {
131-
return Person(
131+
return DriftPerson(
132132
id: id ?? this.id,
133133
createdAt: createdAt ?? this.createdAt,
134134
updatedAt: updatedAt ?? this.updatedAt,
@@ -159,7 +159,7 @@ class Person {
159159
}
160160

161161
@override
162-
bool operator ==(covariant Person other) {
162+
bool operator ==(covariant DriftPerson other) {
163163
if (identical(this, other)) return true;
164164

165165
return other.id == id &&
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import 'dart:async';
2+
3+
import 'package:immich_mobile/domain/models/person.model.dart';
4+
import 'package:immich_mobile/infrastructure/repositories/people.repository.dart';
5+
import 'package:immich_mobile/repositories/person_api.repository.dart';
6+
7+
class DriftPeopleService {
8+
final DriftPeopleRepository _repository;
9+
final PersonApiRepository _personApiRepository;
10+
11+
const DriftPeopleService(this._repository, this._personApiRepository);
12+
13+
Future<List<DriftPerson>> getAssetPeople(String assetId) {
14+
return _repository.getAssetPeople(assetId);
15+
}
16+
17+
Future<List<DriftPerson>> getAllPeople() {
18+
return _repository.getAllPeople();
19+
}
20+
21+
Future<int> updateName(String personId, String name) async {
22+
await _personApiRepository.update(personId, name: name);
23+
return _repository.updateName(personId, name);
24+
}
25+
26+
Future<int> updateBrithday(String personId, DateTime birthday) async {
27+
await _personApiRepository.update(personId, birthday: birthday);
28+
return _repository.updateBirthday(personId, birthday);
29+
}
30+
}

mobile/lib/domain/services/timeline.service.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ class TimelineFactory {
5353

5454
TimelineService place(String place) => TimelineService(_timelineRepository.place(place, groupBy));
5555

56+
TimelineService person(String userId, String personId) =>
57+
TimelineService(_timelineRepository.person(userId, personId, groupBy));
58+
5659
TimelineService fromAssets(List<BaseAsset> assets) => TimelineService(_timelineRepository.fromAssets(assets));
5760
}
5861

mobile/lib/infrastructure/repositories/asset_face.repository.dart

Lines changed: 0 additions & 30 deletions
This file was deleted.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import 'package:drift/drift.dart';
2+
import 'package:immich_mobile/domain/models/person.model.dart';
3+
import 'package:immich_mobile/infrastructure/entities/person.entity.drift.dart';
4+
import 'package:immich_mobile/infrastructure/repositories/db.repository.dart';
5+
6+
class DriftPeopleRepository extends DriftDatabaseRepository {
7+
final Drift _db;
8+
const DriftPeopleRepository(this._db) : super(_db);
9+
10+
Future<List<DriftPerson>> getAssetPeople(String assetId) async {
11+
final query = _db.select(_db.assetFaceEntity).join([
12+
innerJoin(_db.personEntity, _db.personEntity.id.equalsExp(_db.assetFaceEntity.personId)),
13+
])..where(_db.assetFaceEntity.assetId.equals(assetId) & _db.personEntity.isHidden.equals(false));
14+
15+
return query.map((row) {
16+
final person = row.readTable(_db.personEntity);
17+
return person.toDto();
18+
}).get();
19+
}
20+
21+
Future<List<DriftPerson>> getAllPeople() async {
22+
final query =
23+
_db.select(_db.personEntity).join([
24+
leftOuterJoin(_db.assetFaceEntity, _db.assetFaceEntity.personId.equalsExp(_db.personEntity.id)),
25+
])
26+
..where(_db.personEntity.isHidden.equals(false))
27+
..groupBy([_db.personEntity.id])
28+
..orderBy([
29+
OrderingTerm(expression: _db.personEntity.name.equals('').not(), mode: OrderingMode.desc),
30+
OrderingTerm(expression: _db.assetFaceEntity.id.count(), mode: OrderingMode.desc),
31+
]);
32+
33+
return query.map((row) {
34+
final person = row.readTable(_db.personEntity);
35+
return person.toDto();
36+
}).get();
37+
}
38+
39+
Future<int> updateName(String personId, String name) {
40+
final query = _db.update(_db.personEntity)..where((row) => row.id.equals(personId));
41+
42+
return query.write(PersonEntityCompanion(name: Value(name), updatedAt: Value(DateTime.now())));
43+
}
44+
45+
Future<int> updateBirthday(String personId, DateTime birthday) {
46+
final query = _db.update(_db.personEntity)..where((row) => row.id.equals(personId));
47+
48+
return query.write(PersonEntityCompanion(birthDate: Value(birthday), updatedAt: Value(DateTime.now())));
49+
}
50+
}
51+
52+
extension on PersonEntityData {
53+
DriftPerson toDto() {
54+
return DriftPerson(
55+
id: id,
56+
createdAt: createdAt,
57+
updatedAt: updatedAt,
58+
ownerId: ownerId,
59+
name: name,
60+
faceAssetId: faceAssetId,
61+
isFavorite: isFavorite,
62+
isHidden: isHidden,
63+
color: color,
64+
birthDate: birthDate,
65+
);
66+
}
67+
}

mobile/lib/infrastructure/repositories/person.repository.dart

Lines changed: 0 additions & 34 deletions
This file was deleted.

mobile/lib/infrastructure/repositories/timeline.repository.dart

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,11 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
292292
assetSource: (offset, count) => _getPlaceBucketAssets(place, offset: offset, count: count),
293293
);
294294

295+
TimelineQuery person(String userId, String personId, GroupAssetsBy groupBy) => (
296+
bucketSource: () => _watchPersonBucket(userId, personId, groupBy: groupBy),
297+
assetSource: (offset, count) => _getPersonBucketAssets(userId, personId, offset: offset, count: count),
298+
);
299+
295300
Stream<List<Bucket>> _watchPlaceBucket(String place, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
296301
if (groupBy == GroupAssetsBy.none) {
297302
// TODO: implement GroupAssetBy for place
@@ -344,6 +349,84 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
344349
return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get();
345350
}
346351

352+
Stream<List<Bucket>> _watchPersonBucket(String userId, String personId, {GroupAssetsBy groupBy = GroupAssetsBy.day}) {
353+
if (groupBy == GroupAssetsBy.none) {
354+
final query = _db.remoteAssetEntity.selectOnly()
355+
..addColumns([_db.remoteAssetEntity.id.count()])
356+
..join([
357+
innerJoin(
358+
_db.assetFaceEntity,
359+
_db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
360+
useColumns: false,
361+
),
362+
])
363+
..where(
364+
_db.remoteAssetEntity.deletedAt.isNull() &
365+
_db.remoteAssetEntity.ownerId.equals(userId) &
366+
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
367+
_db.assetFaceEntity.personId.equals(personId),
368+
);
369+
370+
return query.map((row) {
371+
final count = row.read(_db.remoteAssetEntity.id.count())!;
372+
return _generateBuckets(count);
373+
}).watchSingle();
374+
}
375+
376+
final assetCountExp = _db.remoteAssetEntity.id.count();
377+
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
378+
379+
final query = _db.remoteAssetEntity.selectOnly()
380+
..addColumns([assetCountExp, dateExp])
381+
..join([
382+
innerJoin(
383+
_db.assetFaceEntity,
384+
_db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
385+
useColumns: false,
386+
),
387+
])
388+
..where(
389+
_db.remoteAssetEntity.deletedAt.isNull() &
390+
_db.remoteAssetEntity.ownerId.equals(userId) &
391+
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
392+
_db.assetFaceEntity.personId.equals(personId),
393+
)
394+
..groupBy([dateExp])
395+
..orderBy([OrderingTerm.desc(dateExp)]);
396+
397+
return query.map((row) {
398+
final timeline = row.read(dateExp)!.dateFmt(groupBy);
399+
final assetCount = row.read(assetCountExp)!;
400+
return TimeBucket(date: timeline, assetCount: assetCount);
401+
}).watch();
402+
}
403+
404+
Future<List<BaseAsset>> _getPersonBucketAssets(
405+
String userId,
406+
String personId, {
407+
required int offset,
408+
required int count,
409+
}) {
410+
final query =
411+
_db.remoteAssetEntity.select().join([
412+
innerJoin(
413+
_db.assetFaceEntity,
414+
_db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
415+
useColumns: false,
416+
),
417+
])
418+
..where(
419+
_db.remoteAssetEntity.deletedAt.isNull() &
420+
_db.remoteAssetEntity.ownerId.equals(userId) &
421+
_db.remoteAssetEntity.visibility.equalsValue(AssetVisibility.timeline) &
422+
_db.assetFaceEntity.personId.equals(personId),
423+
)
424+
..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)])
425+
..limit(count, offset: offset);
426+
427+
return query.map((row) => row.readTable(_db.remoteAssetEntity).toDto()).get();
428+
}
429+
347430
TimelineQuery _remoteQueryBuilder({
348431
required Expression<bool> Function($RemoteAssetEntityTable row) filter,
349432
GroupAssetsBy groupBy = GroupAssetsBy.day,

mobile/lib/pages/library/partner/drift_partner.page.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
55
import 'package:immich_mobile/domain/models/user.model.dart';
66
import 'package:immich_mobile/extensions/build_context_extensions.dart';
77
import 'package:immich_mobile/extensions/translate_extensions.dart';
8-
import 'package:immich_mobile/presentation/widgets/partner_user_avatar.widget.dart';
8+
import 'package:immich_mobile/presentation/widgets/people/partner_user_avatar.widget.dart';
99
import 'package:immich_mobile/providers/infrastructure/partner.provider.dart';
1010
import 'package:immich_mobile/providers/infrastructure/user.provider.dart';
1111
import 'package:immich_mobile/widgets/common/confirm_dialog.dart';

mobile/lib/presentation/pages/drift_library.page.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import 'package:immich_mobile/extensions/asyncvalue_extensions.dart';
66
import 'package:immich_mobile/extensions/build_context_extensions.dart';
77
import 'package:immich_mobile/extensions/translate_extensions.dart';
88
import 'package:immich_mobile/presentation/widgets/images/local_album_thumbnail.widget.dart';
9-
import 'package:immich_mobile/presentation/widgets/partner_user_avatar.widget.dart';
9+
import 'package:immich_mobile/presentation/widgets/people/partner_user_avatar.widget.dart';
1010
import 'package:immich_mobile/providers/infrastructure/album.provider.dart';
1111
import 'package:immich_mobile/providers/infrastructure/partner.provider.dart';
12-
import 'package:immich_mobile/providers/search/people.provider.dart';
12+
import 'package:immich_mobile/providers/infrastructure/people.provider.dart';
1313
import 'package:immich_mobile/providers/server_info.provider.dart';
1414
import 'package:immich_mobile/routing/router.dart';
1515
import 'package:immich_mobile/services/api.service.dart';
@@ -144,7 +144,7 @@ class _PeopleCollectionCard extends ConsumerWidget {
144144

145145
@override
146146
Widget build(BuildContext context, WidgetRef ref) {
147-
final people = ref.watch(getAllPeopleProvider);
147+
final people = ref.watch(driftGetAllPeopleProvider);
148148

149149
return LayoutBuilder(
150150
builder: (context, constraints) {
@@ -153,7 +153,7 @@ class _PeopleCollectionCard extends ConsumerWidget {
153153
final size = context.width * widthFactor - 20.0;
154154

155155
return GestureDetector(
156-
onTap: () => context.pushRoute(const PeopleCollectionRoute()),
156+
onTap: () => context.pushRoute(const DriftPeopleCollectionRoute()),
157157
child: Column(
158158
crossAxisAlignment: CrossAxisAlignment.start,
159159
children: [

0 commit comments

Comments
 (0)