mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
feat(mobile): open assetviewer via album activities page
This commit is contained in:
@@ -33,6 +33,7 @@ enum TimelineOrigin {
|
|||||||
map,
|
map,
|
||||||
search,
|
search,
|
||||||
deepLink,
|
deepLink,
|
||||||
|
albumActivities,
|
||||||
}
|
}
|
||||||
|
|
||||||
class TimelineFactory {
|
class TimelineFactory {
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/asset.provider.dart';
|
||||||
|
import 'package:immich_mobile/providers/infrastructure/timeline.provider.dart';
|
||||||
import 'package:immich_mobile/repositories/activity_api.repository.dart';
|
import 'package:immich_mobile/repositories/activity_api.repository.dart';
|
||||||
import 'package:immich_mobile/services/activity.service.dart';
|
import 'package:immich_mobile/services/activity.service.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
@@ -6,4 +8,8 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|||||||
part 'activity_service.provider.g.dart';
|
part 'activity_service.provider.g.dart';
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
ActivityService activityService(Ref ref) => ActivityService(ref.watch(activityApiRepositoryProvider));
|
ActivityService activityService(Ref ref) => ActivityService(
|
||||||
|
ref.watch(activityApiRepositoryProvider),
|
||||||
|
ref.watch(timelineFactoryProvider),
|
||||||
|
ref.watch(assetServiceProvider),
|
||||||
|
);
|
||||||
|
|||||||
@@ -1,16 +1,24 @@
|
|||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/constants/errors.dart';
|
import 'package:immich_mobile/constants/errors.dart';
|
||||||
|
import 'package:immich_mobile/domain/services/asset.service.dart';
|
||||||
|
import 'package:immich_mobile/domain/services/timeline.service.dart';
|
||||||
import 'package:immich_mobile/mixins/error_logger.mixin.dart';
|
import 'package:immich_mobile/mixins/error_logger.mixin.dart';
|
||||||
import 'package:immich_mobile/models/activities/activity.model.dart';
|
import 'package:immich_mobile/models/activities/activity.model.dart';
|
||||||
|
import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.page.dart';
|
||||||
import 'package:immich_mobile/repositories/activity_api.repository.dart';
|
import 'package:immich_mobile/repositories/activity_api.repository.dart';
|
||||||
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
import 'package:immich_mobile/entities/store.entity.dart' as immich_store;
|
||||||
|
|
||||||
class ActivityService with ErrorLoggerMixin {
|
class ActivityService with ErrorLoggerMixin {
|
||||||
final ActivityApiRepository _activityApiRepository;
|
final ActivityApiRepository _activityApiRepository;
|
||||||
|
final TimelineFactory _timelineFactory;
|
||||||
|
final AssetService _assetService;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
final Logger logger = Logger("ActivityService");
|
final Logger logger = Logger("ActivityService");
|
||||||
|
|
||||||
ActivityService(this._activityApiRepository);
|
ActivityService(this._activityApiRepository, this._timelineFactory, this._assetService);
|
||||||
|
|
||||||
Future<List<Activity>> getAllActivities(String albumId, {String? assetId}) async {
|
Future<List<Activity>> getAllActivities(String albumId, {String? assetId}) async {
|
||||||
return logError(
|
return logError(
|
||||||
@@ -49,4 +57,21 @@ class ActivityService with ErrorLoggerMixin {
|
|||||||
errorMessage: "Failed to create $type for album $albumId",
|
errorMessage: "Failed to create $type for album $albumId",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<AssetViewerRoute?> buildAssetViewerRoute(String assetId, WidgetRef ref) async {
|
||||||
|
if (immich_store.Store.isBetaTimelineEnabled) {
|
||||||
|
final asset = await _assetService.getRemoteAsset(assetId);
|
||||||
|
if (asset == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
AssetViewer.setAsset(ref, asset);
|
||||||
|
return AssetViewerRoute(
|
||||||
|
initialIndex: 0,
|
||||||
|
timelineService: _timelineFactory.fromAssets([asset], TimelineOrigin.albumActivities),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
|
import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:immich_mobile/extensions/datetime_extensions.dart';
|
import 'package:immich_mobile/extensions/datetime_extensions.dart';
|
||||||
import 'package:immich_mobile/models/activities/activity.model.dart';
|
import 'package:immich_mobile/models/activities/activity.model.dart';
|
||||||
|
import 'package:immich_mobile/providers/activity_service.provider.dart';
|
||||||
import 'package:immich_mobile/providers/image/immich_remote_thumbnail_provider.dart';
|
import 'package:immich_mobile/providers/image/immich_remote_thumbnail_provider.dart';
|
||||||
import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
|
import 'package:immich_mobile/providers/asset_viewer/current_asset.provider.dart';
|
||||||
import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
|
import 'package:immich_mobile/widgets/common/user_circle_avatar.dart';
|
||||||
@@ -21,6 +23,14 @@ class ActivityTile extends HookConsumerWidget {
|
|||||||
// currentAssetProvider will not be set until we open the gallery viewer
|
// currentAssetProvider will not be set until we open the gallery viewer
|
||||||
final showAssetThumbnail = asset == null && activity.assetId != null && !isBottomSheet;
|
final showAssetThumbnail = asset == null && activity.assetId != null && !isBottomSheet;
|
||||||
|
|
||||||
|
onTap() async {
|
||||||
|
final activityService = ref.read(activityServiceProvider);
|
||||||
|
final route = await activityService.buildAssetViewerRoute(activity.assetId!, ref);
|
||||||
|
if (route != null) {
|
||||||
|
await context.pushRoute(route);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ListTile(
|
return ListTile(
|
||||||
minVerticalPadding: 15,
|
minVerticalPadding: 15,
|
||||||
leading: isLike
|
leading: isLike
|
||||||
@@ -39,7 +49,7 @@ class ActivityTile extends HookConsumerWidget {
|
|||||||
),
|
),
|
||||||
// No subtitle for like, so center title
|
// No subtitle for like, so center title
|
||||||
titleAlignment: !isLike ? ListTileTitleAlignment.top : ListTileTitleAlignment.center,
|
titleAlignment: !isLike ? ListTileTitleAlignment.top : ListTileTitleAlignment.center,
|
||||||
trailing: showAssetThumbnail ? _ActivityAssetThumbnail(activity.assetId!) : null,
|
trailing: showAssetThumbnail ? _ActivityAssetThumbnail(activity.assetId!, onTap) : null,
|
||||||
subtitle: !isLike ? Text(activity.comment!) : null,
|
subtitle: !isLike ? Text(activity.comment!) : null,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -78,22 +88,26 @@ class _ActivityTitle extends StatelessWidget {
|
|||||||
|
|
||||||
class _ActivityAssetThumbnail extends StatelessWidget {
|
class _ActivityAssetThumbnail extends StatelessWidget {
|
||||||
final String assetId;
|
final String assetId;
|
||||||
|
final GestureTapCallback? onTap;
|
||||||
|
|
||||||
const _ActivityAssetThumbnail(this.assetId);
|
const _ActivityAssetThumbnail(this.assetId, this.onTap);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return GestureDetector(
|
||||||
width: 40,
|
onTap: onTap,
|
||||||
height: 30,
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
width: 40,
|
||||||
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
height: 30,
|
||||||
image: DecorationImage(
|
decoration: BoxDecoration(
|
||||||
image: ImmichRemoteThumbnailProvider(assetId: assetId),
|
borderRadius: const BorderRadius.all(Radius.circular(4)),
|
||||||
fit: BoxFit.cover,
|
image: DecorationImage(
|
||||||
|
image: ImmichRemoteThumbnailProvider(assetId: assetId),
|
||||||
|
fit: BoxFit.cover,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
child: const SizedBox.shrink(),
|
||||||
),
|
),
|
||||||
child: const SizedBox.shrink(),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user