From ff76370abb33f83f6e4a839a27d0c0a782e85ee7 Mon Sep 17 00:00:00 2001 From: idubnori Date: Thu, 23 Oct 2025 14:25:56 +0900 Subject: [PATCH] refactor: use feature toggle --- i18n/en.json | 2 + mobile/lib/domain/models/store.model.dart | 3 +- .../pages/drift_activities.page.dart | 91 +++++++++++++++++++ mobile/lib/services/app_settings.service.dart | 1 + .../widgets/settings/advanced_settings.dart | 6 ++ 5 files changed, 102 insertions(+), 1 deletion(-) diff --git a/i18n/en.json b/i18n/en.json index 8669a048b8..7426ed6db0 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -398,6 +398,8 @@ "admin_password": "Admin Password", "administration": "Administration", "advanced": "Advanced", + "advanced_settings_beta_activities_style_subtitle": "Enable chat-style layout for album activities.", + "advanced_settings_beta_activities_style_title": "New activities style", "advanced_settings_enable_alternate_media_filter_subtitle": "Use this option to filter media during sync based on alternate criteria. Only try this if you have issues with the app detecting all albums.", "advanced_settings_enable_alternate_media_filter_title": "[EXPERIMENTAL] Use alternate device album sync filter", "advanced_settings_log_level_title": "Log level: {level}", diff --git a/mobile/lib/domain/models/store.model.dart b/mobile/lib/domain/models/store.model.dart index d8404db409..69c9ba150d 100644 --- a/mobile/lib/domain/models/store.model.dart +++ b/mobile/lib/domain/models/store.model.dart @@ -81,7 +81,8 @@ enum StoreKey { useWifiForUploadPhotos._(1005), needBetaMigration._(1006), // TODO: Remove this after patching open-api - shouldResetSync._(1007); + shouldResetSync._(1007), + betaActivitiesStyle._(1008); const StoreKey._(this.id); final int id; diff --git a/mobile/lib/presentation/pages/drift_activities.page.dart b/mobile/lib/presentation/pages/drift_activities.page.dart index e44dfd263b..2bc13c82cb 100644 --- a/mobile/lib/presentation/pages/drift_activities.page.dart +++ b/mobile/lib/presentation/pages/drift_activities.page.dart @@ -1,8 +1,11 @@ import 'package:auto_route/auto_route.dart'; +import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart' hide Store; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; +import 'package:immich_mobile/domain/models/store.model.dart'; +import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/asyncvalue_extensions.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/datetime_extensions.dart'; @@ -13,6 +16,7 @@ import 'package:immich_mobile/providers/activity.provider.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; import 'package:immich_mobile/providers/infrastructure/current_album.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; +import 'package:immich_mobile/widgets/activities/activity_tile.dart'; import 'package:immich_mobile/widgets/activities/dismissible_activity.dart'; // activity_tile and dismissible_activity are no longer used in this page import 'package:immich_mobile/widgets/common/user_circle_avatar.dart'; @@ -24,6 +28,93 @@ class DriftActivitiesPage extends HookConsumerWidget { const DriftActivitiesPage({super.key}); @override + Widget build(BuildContext context, WidgetRef ref) { + if (Store.get(StoreKey.betaActivitiesStyle, false)) { + return _BetaDriftActivities().build(context, ref); + } + + final album = ref.watch(currentRemoteAlbumProvider)!; + final asset = ref.watch(currentAssetNotifier) as RemoteAsset?; + final user = ref.watch(currentUserProvider); + + final activityNotifier = ref.read(albumActivityProvider(album.id, asset?.id).notifier); + final activities = ref.watch(albumActivityProvider(album.id, asset?.id)); + final listViewScrollController = useScrollController(); + + void scrollToBottom() { + listViewScrollController.animateTo( + listViewScrollController.position.maxScrollExtent + 80, + duration: const Duration(milliseconds: 600), + curve: Curves.fastOutSlowIn, + ); + } + + Future onAddComment(String comment) async { + await activityNotifier.addComment(comment); + scrollToBottom(); + } + + return Scaffold( + appBar: AppBar( + title: asset == null ? Text(album.name) : null, + actions: [const LikeActivityActionButton(menuItem: true)], + actionsPadding: const EdgeInsets.only(right: 8), + ), + body: activities.widgetWhen( + onData: (data) { + final liked = data.firstWhereOrNull( + (a) => a.type == ActivityType.like && a.user.id == user?.id && a.assetId == asset?.id, + ); + + return SafeArea( + child: Stack( + children: [ + ListView.builder( + controller: listViewScrollController, + itemCount: data.length + 1, + itemBuilder: (context, index) { + if (index == data.length) { + return const SizedBox(height: 80); + } + final activity = data[index]; + final canDelete = activity.user.id == user?.id || album.ownerId == user?.id; + return Padding( + padding: const EdgeInsets.all(5), + child: DismissibleActivity( + activity.id, + ActivityTile(activity), + onDismiss: canDelete + ? (activityId) async => await activityNotifier.removeActivity(activity.id) + : null, + ), + ); + }, + ), + Align( + alignment: Alignment.bottomCenter, + child: Container( + decoration: BoxDecoration( + color: context.scaffoldBackgroundColor, + border: Border(top: BorderSide(color: context.colorScheme.secondaryContainer, width: 1)), + ), + child: DriftActivityTextField( + isEnabled: album.isActivityEnabled, + likeId: liked?.id, + onSubmit: onAddComment, + ), + ), + ), + ], + ), + ); + }, + ), + resizeToAvoidBottomInset: true, + ); + } +} + +class _BetaDriftActivities { Widget build(BuildContext context, WidgetRef ref) { final album = ref.watch(currentRemoteAlbumProvider)!; final asset = ref.read(currentAssetNotifier) as RemoteAsset?; diff --git a/mobile/lib/services/app_settings.service.dart b/mobile/lib/services/app_settings.service.dart index 7149408e8a..0474e559de 100644 --- a/mobile/lib/services/app_settings.service.dart +++ b/mobile/lib/services/app_settings.service.dart @@ -48,6 +48,7 @@ enum AppSettingsEnum { autoEndpointSwitching(StoreKey.autoEndpointSwitching, null, false), photoManagerCustomFilter(StoreKey.photoManagerCustomFilter, null, true), betaTimeline(StoreKey.betaTimeline, null, true), + betaActivitiesStyle(StoreKey.betaActivitiesStyle, null, false), enableBackup(StoreKey.enableBackup, null, false), useCellularForUploadVideos(StoreKey.useWifiForUploadVideos, null, false), useCellularForUploadPhotos(StoreKey.useWifiForUploadPhotos, null, false), diff --git a/mobile/lib/widgets/settings/advanced_settings.dart b/mobile/lib/widgets/settings/advanced_settings.dart index 9255a7ae52..ddf749ca86 100644 --- a/mobile/lib/widgets/settings/advanced_settings.dart +++ b/mobile/lib/widgets/settings/advanced_settings.dart @@ -36,6 +36,7 @@ class AdvancedSettings extends HookConsumerWidget { final allowSelfSignedSSLCert = useAppSettingsState(AppSettingsEnum.allowSelfSignedSSLCert); final useAlternatePMFilter = useAppSettingsState(AppSettingsEnum.photoManagerCustomFilter); final readonlyModeEnabled = useAppSettingsState(AppSettingsEnum.readonlyModeEnabled); + final betaActivitiesStyle = useAppSettingsState(AppSettingsEnum.betaActivitiesStyle); final logLevel = Level.LEVELS[levelId.value].name; @@ -128,6 +129,11 @@ class AdvancedSettings extends HookConsumerWidget { ); }, ), + SettingsSwitchListTile( + valueNotifier: betaActivitiesStyle, + title: "advanced_settings_beta_activities_style_title".tr(), + subtitle: "advanced_settings_beta_activities_style_subtitle".tr(), + ), ]; return SettingsSubPageScaffold(settings: advancedSettings);