mirror of
https://github.com/immich-app/immich.git
synced 2025-07-09 03:04:16 -04:00
feat(mobile): drift video page (#19784)
* feat(mobile): drift video page * filter motional parts * remove status indicator join --------- Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
parent
d149d6fa3f
commit
683af67344
@ -83,6 +83,13 @@ class TimelineFactory {
|
|||||||
groupBy: groupBy,
|
groupBy: groupBy,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
TimelineService video(String userId) => TimelineService(
|
||||||
|
assetSource: (offset, count) => _timelineRepository
|
||||||
|
.getVideoBucketAssets(userId, offset: offset, count: count),
|
||||||
|
bucketSource: () =>
|
||||||
|
_timelineRepository.watchVideoBucket(userId, groupBy: groupBy),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
class TimelineService {
|
class TimelineService {
|
||||||
|
@ -413,6 +413,62 @@ class DriftTimelineRepository extends DriftDatabaseRepository {
|
|||||||
|
|
||||||
return query.map((row) => row.toDto()).get();
|
return query.map((row) => row.toDto()).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stream<List<Bucket>> watchVideoBucket(
|
||||||
|
String userId, {
|
||||||
|
GroupAssetsBy groupBy = GroupAssetsBy.day,
|
||||||
|
}) {
|
||||||
|
if (groupBy == GroupAssetsBy.none) {
|
||||||
|
return _db.remoteAssetEntity
|
||||||
|
.count(
|
||||||
|
where: (row) =>
|
||||||
|
row.type.equalsValue(AssetType.video) &
|
||||||
|
row.visibility.equalsValue(AssetVisibility.timeline) &
|
||||||
|
row.ownerId.equals(userId),
|
||||||
|
)
|
||||||
|
.map(_generateBuckets)
|
||||||
|
.watchSingle();
|
||||||
|
}
|
||||||
|
|
||||||
|
final assetCountExp = _db.remoteAssetEntity.id.count();
|
||||||
|
final dateExp = _db.remoteAssetEntity.createdAt.dateFmt(groupBy);
|
||||||
|
|
||||||
|
final query = _db.remoteAssetEntity.selectOnly()
|
||||||
|
..addColumns([assetCountExp, dateExp])
|
||||||
|
..where(
|
||||||
|
_db.remoteAssetEntity.ownerId.equals(userId) &
|
||||||
|
_db.remoteAssetEntity.type.equalsValue(AssetType.video) &
|
||||||
|
_db.remoteAssetEntity.visibility
|
||||||
|
.equalsValue(AssetVisibility.timeline),
|
||||||
|
)
|
||||||
|
..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>> getVideoBucketAssets(
|
||||||
|
String userId, {
|
||||||
|
required int offset,
|
||||||
|
required int count,
|
||||||
|
}) {
|
||||||
|
final query = _db.remoteAssetEntity.select()
|
||||||
|
..where(
|
||||||
|
(row) =>
|
||||||
|
_db.remoteAssetEntity.type.equalsValue(AssetType.video) &
|
||||||
|
_db.remoteAssetEntity.visibility
|
||||||
|
.equalsValue(AssetVisibility.timeline) &
|
||||||
|
_db.remoteAssetEntity.ownerId.equals(userId),
|
||||||
|
)
|
||||||
|
..orderBy([(row) => OrderingTerm.desc(row.createdAt)])
|
||||||
|
..limit(count, offset: offset);
|
||||||
|
|
||||||
|
return query.map((row) => row.toDto()).get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension on Expression<DateTime> {
|
extension on Expression<DateTime> {
|
||||||
|
33
mobile/lib/presentation/pages/dev/drift_video.page.dart
Normal file
33
mobile/lib/presentation/pages/dev/drift_video.page.dart
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/timeline/timeline.widget.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/user.provider.dart';
|
||||||
|
|
||||||
|
@RoutePage()
|
||||||
|
class DriftVideoPage extends StatelessWidget {
|
||||||
|
const DriftVideoPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ProviderScope(
|
||||||
|
overrides: [
|
||||||
|
timelineServiceProvider.overrideWith(
|
||||||
|
(ref) {
|
||||||
|
final user = ref.watch(currentUserProvider);
|
||||||
|
if (user == null) {
|
||||||
|
throw Exception('User must be logged in to video');
|
||||||
|
}
|
||||||
|
|
||||||
|
final timelineService =
|
||||||
|
ref.watch(timelineFactoryProvider).video(user.id);
|
||||||
|
ref.onDispose(timelineService.dispose);
|
||||||
|
return timelineService;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
child: const Timeline(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -119,6 +119,11 @@ final _features = [
|
|||||||
icon: Icons.lock_outline_rounded,
|
icon: Icons.lock_outline_rounded,
|
||||||
onTap: (ctx, _) => ctx.pushRoute(const DriftLockedFolderRoute()),
|
onTap: (ctx, _) => ctx.pushRoute(const DriftLockedFolderRoute()),
|
||||||
),
|
),
|
||||||
|
_Feature(
|
||||||
|
name: 'Video',
|
||||||
|
icon: Icons.video_collection_outlined,
|
||||||
|
onTap: (ctx, _) => ctx.pushRoute(const DriftVideoRoute()),
|
||||||
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
|
@ -67,6 +67,7 @@ import 'package:immich_mobile/pages/search/recently_taken.page.dart';
|
|||||||
import 'package:immich_mobile/pages/search/search.page.dart';
|
import 'package:immich_mobile/pages/search/search.page.dart';
|
||||||
import 'package:immich_mobile/pages/share_intent/share_intent.page.dart';
|
import 'package:immich_mobile/pages/share_intent/share_intent.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/drift_favorite.page.dart';
|
import 'package:immich_mobile/presentation/pages/dev/drift_favorite.page.dart';
|
||||||
|
import 'package:immich_mobile/presentation/pages/dev/drift_video.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/drift_trash.page.dart';
|
import 'package:immich_mobile/presentation/pages/dev/drift_trash.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/drift_archive.page.dart';
|
import 'package:immich_mobile/presentation/pages/dev/drift_archive.page.dart';
|
||||||
import 'package:immich_mobile/presentation/pages/dev/drift_locked_folder.page.dart';
|
import 'package:immich_mobile/presentation/pages/dev/drift_locked_folder.page.dart';
|
||||||
@ -412,6 +413,10 @@ class AppRouter extends RootStackRouter {
|
|||||||
page: DriftLockedFolderRoute.page,
|
page: DriftLockedFolderRoute.page,
|
||||||
guards: [_authGuard, _duplicateGuard],
|
guards: [_authGuard, _duplicateGuard],
|
||||||
),
|
),
|
||||||
|
AutoRoute(
|
||||||
|
page: DriftVideoRoute.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: '/'),
|
||||||
|
@ -734,6 +734,22 @@ class DriftTrashRoute extends PageRouteInfo<void> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// generated route for
|
||||||
|
/// [DriftVideoPage]
|
||||||
|
class DriftVideoRoute extends PageRouteInfo<void> {
|
||||||
|
const DriftVideoRoute({List<PageRouteInfo>? children})
|
||||||
|
: super(DriftVideoRoute.name, initialChildren: children);
|
||||||
|
|
||||||
|
static const String name = 'DriftVideoRoute';
|
||||||
|
|
||||||
|
static PageInfo page = PageInfo(
|
||||||
|
name,
|
||||||
|
builder: (data) {
|
||||||
|
return const DriftVideoPage();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// generated route for
|
/// generated route for
|
||||||
/// [EditImagePage]
|
/// [EditImagePage]
|
||||||
class EditImageRoute extends PageRouteInfo<EditImageRouteArgs> {
|
class EditImageRoute extends PageRouteInfo<EditImageRouteArgs> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user