mirror of
https://github.com/immich-app/immich.git
synced 2025-08-11 09:16:31 -04:00
feat(mobile): drift person detail page
This commit is contained in:
parent
ac44f6d1e0
commit
f58db3be88
@ -68,6 +68,9 @@ class TimelineFactory {
|
|||||||
|
|
||||||
TimelineService fromAssets(List<BaseAsset> assets) =>
|
TimelineService fromAssets(List<BaseAsset> assets) =>
|
||||||
TimelineService(_timelineRepository.fromAssets(assets));
|
TimelineService(_timelineRepository.fromAssets(assets));
|
||||||
|
|
||||||
|
TimelineService person(String personId) =>
|
||||||
|
TimelineService(_timelineRepository.person(personId, groupBy));
|
||||||
}
|
}
|
||||||
|
|
||||||
class TimelineService {
|
class TimelineService {
|
||||||
|
@ -441,6 +441,78 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
.get();
|
.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TimelineQuery person(String personId, GroupAssetsBy groupBy) => (
|
||||||
|
bucketSource: () => _watchPersonBucket(personId, groupBy: groupBy),
|
||||||
|
assetSource: (offset, count) =>
|
||||||
|
_getPersonBucketAssets(personId, offset: offset, count: count),
|
||||||
|
);
|
||||||
|
|
||||||
|
Stream<List<Bucket>> _watchPersonBucket(
|
||||||
|
String personId, {
|
||||||
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
|
}) {
|
||||||
|
if (groupBy == GroupAssetsBy.none) {
|
||||||
|
// TODO: implement GroupAssetBy for person
|
||||||
|
throw UnsupportedError(
|
||||||
|
"GroupAssetsBy.none is not supported for watchPersonBucket",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
final assetCountExp = _db.remoteAssetEntity.id.count();
|
||||||
|
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
|
||||||
|
|
||||||
|
final query = _db.remoteAssetEntity.selectOnly()
|
||||||
|
..addColumns([assetCountExp, dateExp])
|
||||||
|
..join([
|
||||||
|
innerJoin(
|
||||||
|
_db.assetFaceEntity,
|
||||||
|
_db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||||
|
useColumns: false,
|
||||||
|
),
|
||||||
|
])
|
||||||
|
..where(
|
||||||
|
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||||
|
_db.remoteAssetEntity.visibility
|
||||||
|
.equalsValue(AssetVisibility.timeline) &
|
||||||
|
_db.assetFaceEntity.personId.equals(personId),
|
||||||
|
)
|
||||||
|
..groupBy([dateExp])
|
||||||
|
..orderBy([OrderingTerm.desc(dateExp)]);
|
||||||
|
|
||||||
|
return query.map((row) {
|
||||||
|
final timeline = row.read(dateExp)!.dateFmt(groupBy);
|
||||||
|
final assetCount = row.read(assetCountExp)!;
|
||||||
|
return TimeBucket(date: timeline, assetCount: assetCount);
|
||||||
|
}).watch();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<List<BaseAsset>> _getPersonBucketAssets(
|
||||||
|
String personId, {
|
||||||
|
required int offset,
|
||||||
|
required int count,
|
||||||
|
}) {
|
||||||
|
final query = _db.remoteAssetEntity.select().join(
|
||||||
|
[
|
||||||
|
innerJoin(
|
||||||
|
_db.assetFaceEntity,
|
||||||
|
_db.assetFaceEntity.assetId.equalsExp(_db.remoteAssetEntity.id),
|
||||||
|
useColumns: false,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
..where(
|
||||||
|
_db.remoteAssetEntity.deletedAt.isNull() &
|
||||||
|
_db.remoteAssetEntity.visibility
|
||||||
|
.equalsValue(AssetVisibility.timeline) &
|
||||||
|
_db.assetFaceEntity.personId.equals(personId),
|
||||||
|
)
|
||||||
|
..orderBy([OrderingTerm.desc(_db.remoteAssetEntity.createdAt)])
|
||||||
|
..limit(count, offset: offset);
|
||||||
|
return query
|
||||||
|
.map((row) => row.readTable(_db.remoteAssetEntity).toDto())
|
||||||
|
.get();
|
||||||
|
}
|
||||||
|
|
||||||
TimelineQuery _remoteQueryBuilder({
|
TimelineQuery _remoteQueryBuilder({
|
||||||
required Expression<bool> Function($RemoteAssetEntityTable row) filter,
|
required Expression<bool> Function($RemoteAssetEntityTable row) filter,
|
||||||
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
|
36
mobile/lib/presentation/pages/drift_person_detail.dart
Normal file
36
mobile/lib/presentation/pages/drift_person_detail.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/person.model.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
|
import 'package:immich_mobile/widgets/common/mesmerizing_sliver_app_bar.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class DriftPersonDetailPage extends StatelessWidget {
|
||||||
|
final Person person;
|
||||||
|
|
||||||
|
const DriftPersonDetailPage({super.key, required this.person});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ProviderScope(
|
||||||
|
overrides: [
|
||||||
|
timelineServiceProvider.overrideWith(
|
||||||
|
(ref) {
|
||||||
|
final timelineService =
|
||||||
|
ref.watch(timelineFactoryProvider).person(person.id);
|
||||||
|
ref.onDispose(timelineService.dispose);
|
||||||
|
return timelineService;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: Timeline(
|
||||||
|
appBar: MesmerizingSliverAppBar(
|
||||||
|
title: person.name,
|
||||||
|
icon: Icons.person_outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import 'package:immich_mobile/domain/models/album/local_album.model.dart';
|
|||||||
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
import 'package:immich_mobile/domain/models/asset/base_asset.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/log.model.dart';
|
import 'package:immich_mobile/domain/models/log.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/memory.model.dart';
|
import 'package:immich_mobile/domain/models/memory.model.dart';
|
||||||
|
import 'package:immich_mobile/domain/models/person.model.dart';
|
||||||
import 'package:immich_mobile/domain/models/user.model.dart';
|
import 'package:immich_mobile/domain/models/user.model.dart';
|
||||||
import 'package:immich_mobile/domain/services/timeline.service.dart';
|
import 'package:immich_mobile/domain/services/timeline.service.dart';
|
||||||
import 'package:immich_mobile/entities/album.entity.dart';
|
import 'package:immich_mobile/entities/album.entity.dart';
|
||||||
@ -95,6 +96,7 @@ import 'package:immich_mobile/presentation/pages/drift_user_selection.page.dart'
|
|||||||
import 'package:immich_mobile/presentation/pages/drift_remote_album.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_remote_album.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_trash.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_trash.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/drift_video.page.dart';
|
import 'package:immich_mobile/presentation/pages/drift_video.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/drift_person_detail.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/local_timeline.page.dart';
|
import 'package:immich_mobile/presentation/pages/local_timeline.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/search/drift_search.page.dart';
|
import 'package:immich_mobile/presentation/pages/search/drift_search.page.dart';
|
||||||
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart';
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart';
|
||||||
@ -486,7 +488,6 @@ class AppRouter extends RootStackRouter {
|
|||||||
page: ChangeExperienceRoute.page,
|
page: ChangeExperienceRoute.page,
|
||||||
guards: [_authGuard, _duplicateGuard],
|
guards: [_authGuard, _duplicateGuard],
|
||||||
),
|
),
|
||||||
|
|
||||||
AutoRoute(
|
AutoRoute(
|
||||||
page: DriftPartnerRoute.page,
|
page: DriftPartnerRoute.page,
|
||||||
guards: [_authGuard, _duplicateGuard],
|
guards: [_authGuard, _duplicateGuard],
|
||||||
@ -499,6 +500,10 @@ class AppRouter extends RootStackRouter {
|
|||||||
page: BetaSyncSettingsRoute.page,
|
page: BetaSyncSettingsRoute.page,
|
||||||
guards: [_authGuard, _duplicateGuard],
|
guards: [_authGuard, _duplicateGuard],
|
||||||
),
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: DriftPersonDetailRoute.page,
|
||||||
|
guards: [_authGuard, _duplicateGuard],
|
||||||
|
),
|
||||||
// required to handle all deeplinks in deep_link.service.dart
|
// required to handle all deeplinks in deep_link.service.dart
|
||||||
// auto_route_library#1722
|
// auto_route_library#1722
|
||||||
RedirectRoute(path: '*', redirectTo: '/'),
|
RedirectRoute(path: '*', redirectTo: '/'),
|
||||||
|
@ -960,6 +960,43 @@ class DriftPartnerRoute extends PageRouteInfo<void> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [DriftPersonDetailPage]
|
||||||
|
class DriftPersonDetailRoute extends PageRouteInfo<DriftPersonDetailRouteArgs> {
|
||||||
|
DriftPersonDetailRoute({
|
||||||
|
Key? key,
|
||||||
|
required Person person,
|
||||||
|
List<PageRouteInfo>? children,
|
||||||
|
}) : super(
|
||||||
|
DriftPersonDetailRoute.name,
|
||||||
|
args: DriftPersonDetailRouteArgs(key: key, person: person),
|
||||||
|
initialChildren: children,
|
||||||
|
);
|
||||||
|
|
||||||
|
static const String name = 'DriftPersonDetailRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
final args = data.argsAs<DriftPersonDetailRouteArgs>();
|
||||||
|
return DriftPersonDetailPage(key: args.key, person: args.person);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class DriftPersonDetailRouteArgs {
|
||||||
|
const DriftPersonDetailRouteArgs({this.key, required this.person});
|
||||||
|
|
||||||
|
final Key? key;
|
||||||
|
|
||||||
|
final Person person;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'DriftPersonDetailRouteArgs{key: $key, person: $person}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [DriftPlaceDetailPage]
|
/// [DriftPlaceDetailPage]
|
||||||
class DriftPlaceDetailRoute extends PageRouteInfo<DriftPlaceDetailRouteArgs> {
|
class DriftPlaceDetailRoute extends PageRouteInfo<DriftPlaceDetailRouteArgs> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user