From 913b3789cc6a47a7c7f987ef2660e5371e301790 Mon Sep 17 00:00:00 2001 From: Alex Date: Fri, 12 Sep 2025 22:32:15 -0500 Subject: [PATCH] chore: simplify timeline switcher toggle (#21864) chore: timeline switcher option simplify --- mobile/lib/pages/common/settings.page.dart | 38 +-- .../widgets/settings/advanced_settings.dart | 16 +- .../settings/beta_timeline_list_tile.dart | 247 ++++-------------- 3 files changed, 66 insertions(+), 235 deletions(-) diff --git a/mobile/lib/pages/common/settings.page.dart b/mobile/lib/pages/common/settings.page.dart index 014136ddb4..b23c420971 100644 --- a/mobile/lib/pages/common/settings.page.dart +++ b/mobile/lib/pages/common/settings.page.dart @@ -12,7 +12,6 @@ import 'package:immich_mobile/widgets/settings/asset_viewer_settings/asset_viewe import 'package:immich_mobile/widgets/settings/backup_settings/backup_settings.dart'; import 'package:immich_mobile/widgets/settings/backup_settings/drift_backup_settings.dart'; import 'package:immich_mobile/widgets/settings/beta_sync_settings/sync_status_and_actions.dart'; -import 'package:immich_mobile/widgets/settings/beta_timeline_list_tile.dart'; import 'package:immich_mobile/widgets/settings/language_settings.dart'; import 'package:immich_mobile/widgets/settings/networking_settings/networking_settings.dart'; import 'package:immich_mobile/widgets/settings/notification_setting.dart'; @@ -20,7 +19,6 @@ import 'package:immich_mobile/widgets/settings/preference_settings/preference_se import 'package:immich_mobile/widgets/settings/settings_card.dart'; enum SettingSection { - beta('sync_status', Icons.sync_outlined, "sync_status_subtitle"), advanced('advanced', Icons.build_outlined, "advanced_settings_tile_subtitle"), assetViewer('asset_viewer_settings_title', Icons.image_outlined, "asset_viewer_settings_subtitle"), backup('backup', Icons.cloud_upload_outlined, "backup_settings_subtitle"), @@ -28,14 +26,14 @@ enum SettingSection { networking('networking_settings', Icons.wifi, "networking_subtitle"), notifications('notifications', Icons.notifications_none_rounded, "setting_notifications_subtitle"), preferences('preferences_settings_title', Icons.interests_outlined, "preferences_settings_subtitle"), - timeline('asset_list_settings_title', Icons.auto_awesome_mosaic_outlined, "asset_list_settings_subtitle"); + timeline('asset_list_settings_title', Icons.auto_awesome_mosaic_outlined, "asset_list_settings_subtitle"), + beta('sync_status', Icons.sync_outlined, "sync_status_subtitle"); final String title; final String subtitle; final IconData icon; Widget get widget => switch (this) { - SettingSection.beta => const _BetaLandscapeToggle(), SettingSection.advanced => const AdvancedSettings(), SettingSection.assetViewer => const AssetViewerSettings(), SettingSection.backup => @@ -45,6 +43,7 @@ enum SettingSection { SettingSection.notifications => const NotificationSetting(), SettingSection.preferences => const PreferenceSetting(), SettingSection.timeline => const AssetListSettings(), + SettingSection.beta => const SyncStatusAndActions(), }; const SettingSection(this.title, this.icon, this.subtitle); @@ -59,7 +58,7 @@ class SettingsPage extends StatelessWidget { context.locale; return Scaffold( appBar: AppBar(centerTitle: false, title: const Text('settings').tr()), - body: context.isMobile ? const _MobileLayout() : const _TabletLayout(), + body: context.isMobile ? const SafeArea(child: _MobileLayout()) : const SafeArea(child: _TabletLayout()), ); } } @@ -72,7 +71,6 @@ class _MobileLayout extends StatelessWidget { .expand( (setting) => setting == SettingSection.beta ? [ - const BetaTimelineListTile(), if (Store.isBetaTimelineEnabled) SettingsCard( icon: Icons.sync_outlined, @@ -93,7 +91,7 @@ class _MobileLayout extends StatelessWidget { .toList(); return ListView( physics: const ClampingScrollPhysics(), - padding: const EdgeInsets.only(top: 10.0, bottom: 56), + padding: const EdgeInsets.only(top: 10.0, bottom: 16), children: [...settings], ); } @@ -134,21 +132,6 @@ class _TabletLayout extends HookWidget { } } -class _BetaLandscapeToggle extends HookWidget { - const _BetaLandscapeToggle(); - - @override - Widget build(BuildContext context) { - return Column( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - const SizedBox(height: 100, child: BetaTimelineListTile()), - if (Store.isBetaTimelineEnabled) const Expanded(child: SyncStatusAndActions()), - ], - ); - } -} - @RoutePage() class SettingsSubPage extends StatelessWidget { const SettingsSubPage(this.section, {super.key}); @@ -158,9 +141,14 @@ class SettingsSubPage extends StatelessWidget { @override Widget build(BuildContext context) { context.locale; - return Scaffold( - appBar: AppBar(centerTitle: false, title: Text(section.title).tr()), - body: section.widget, + return SafeArea( + bottom: true, + top: false, + right: true, + child: Scaffold( + appBar: AppBar(centerTitle: false, title: Text(section.title).tr()), + body: section.widget, + ), ); } } diff --git a/mobile/lib/widgets/settings/advanced_settings.dart b/mobile/lib/widgets/settings/advanced_settings.dart index cd2fa93b85..7a107b47d8 100644 --- a/mobile/lib/widgets/settings/advanced_settings.dart +++ b/mobile/lib/widgets/settings/advanced_settings.dart @@ -14,6 +14,7 @@ import 'package:immich_mobile/repositories/local_files_manager.repository.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; import 'package:immich_mobile/utils/http_ssl_options.dart'; +import 'package:immich_mobile/widgets/settings/beta_timeline_list_tile.dart'; import 'package:immich_mobile/widgets/settings/custom_proxy_headers_settings/custome_proxy_headers_settings.dart'; import 'package:immich_mobile/widgets/settings/local_storage_settings.dart'; import 'package:immich_mobile/widgets/settings/settings_slider_list_tile.dart'; @@ -91,7 +92,7 @@ class AdvancedSettings extends HookConsumerWidget { title: "advanced_settings_prefer_remote_title".tr(), subtitle: "advanced_settings_prefer_remote_subtitle".tr(), ), - const LocalStorageSettings(), + if (!Store.isBetaTimelineEnabled) const LocalStorageSettings(), SettingsSwitchListTile( enabled: !isLoggedIn, valueNotifier: allowSelfSignedSSLCert, @@ -101,12 +102,13 @@ class AdvancedSettings extends HookConsumerWidget { ), const CustomeProxyHeaderSettings(), SslClientCertSettings(isLoggedIn: ref.read(currentUserProvider) != null), - SettingsSwitchListTile( - valueNotifier: useAlternatePMFilter, - title: "advanced_settings_enable_alternate_media_filter_title".tr(), - subtitle: "advanced_settings_enable_alternate_media_filter_subtitle".tr(), - ), - // TODO: Remove this check when beta timeline goes stable + if (!Store.isBetaTimelineEnabled) + SettingsSwitchListTile( + valueNotifier: useAlternatePMFilter, + title: "advanced_settings_enable_alternate_media_filter_title".tr(), + subtitle: "advanced_settings_enable_alternate_media_filter_subtitle".tr(), + ), + const BetaTimelineListTile(), if (Store.isBetaTimelineEnabled) SettingsSwitchListTile( valueNotifier: readonlyModeEnabled, diff --git a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart index a498145275..8786fe2c5f 100644 --- a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart +++ b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart @@ -1,5 +1,3 @@ -import 'dart:math' as math; - import 'package:auto_route/auto_route.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; @@ -12,50 +10,11 @@ import 'package:immich_mobile/providers/server_info.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; -class BetaTimelineListTile extends ConsumerStatefulWidget { +class BetaTimelineListTile extends ConsumerWidget { const BetaTimelineListTile({super.key}); @override - ConsumerState createState() => _BetaTimelineListTileState(); -} - -class _BetaTimelineListTileState extends ConsumerState with SingleTickerProviderStateMixin { - late AnimationController _animationController; - late Animation _rotationAnimation; - late Animation _pulseAnimation; - late Animation _gradientAnimation; - - @override - void initState() { - super.initState(); - _animationController = AnimationController(duration: const Duration(seconds: 3), vsync: this); - - _rotationAnimation = Tween( - begin: 0, - end: 2 * math.pi, - ).animate(CurvedAnimation(parent: _animationController, curve: Curves.linear)); - - _pulseAnimation = Tween( - begin: 1, - end: 1.1, - ).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut)); - - _gradientAnimation = Tween( - begin: 0, - end: 1, - ).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut)); - - _animationController.repeat(reverse: true); - } - - @override - void dispose() { - _animationController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { + Widget build(BuildContext context, WidgetRef ref) { final betaTimelineValue = ref.watch(appSettingsServiceProvider).getSetting(AppSettingsEnum.betaTimeline); final serverInfo = ref.watch(serverInfoProvider); final auth = ref.watch(authProvider); @@ -64,168 +23,50 @@ class _BetaTimelineListTileState extends ConsumerState wit return const SizedBox.shrink(); } - return AnimatedBuilder( - animation: _animationController, - builder: (context, child) { - void onSwitchChanged(bool value) { - showDialog( - context: context, - builder: (context) { - return AlertDialog( - title: value ? const Text("Enable Beta Timeline") : const Text("Disable Beta Timeline"), - content: value - ? const Text("Are you sure you want to enable the beta timeline?") - : const Text("Are you sure you want to disable the beta timeline?"), - actions: [ - TextButton( - onPressed: () { - context.pop(); - }, - child: Text( - "cancel".t(context: context), - style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: context.colorScheme.outline), - ), - ), - ElevatedButton( - onPressed: () async { - Navigator.of(context).pop(); - context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: value)]); - }, - child: Text("ok".t(context: context)), - ), - ], - ); - }, - ); - } - - final gradientColors = [ - Color.lerp( - context.primaryColor.withValues(alpha: 0.5), - context.primaryColor.withValues(alpha: 0.3), - _gradientAnimation.value, - )!, - Color.lerp( - context.logoPink.withValues(alpha: 0.2), - context.logoPink.withValues(alpha: 0.4), - _gradientAnimation.value, - )!, - Color.lerp( - context.logoRed.withValues(alpha: 0.3), - context.logoRed.withValues(alpha: 0.5), - _gradientAnimation.value, - )!, - ]; - - return Container( - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 4), - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(12)), - gradient: LinearGradient( - colors: gradientColors, - stops: const [0.0, 0.5, 1.0], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - transform: GradientRotation(_rotationAnimation.value * 0.5), - ), - boxShadow: [ - BoxShadow(color: context.primaryColor.withValues(alpha: 0.1), blurRadius: 8, offset: const Offset(0, 2)), - ], - ), - child: Container( - margin: const EdgeInsets.all(2), - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(10.5)), - color: context.scaffoldBackgroundColor, - ), - child: Material( - borderRadius: const BorderRadius.all(Radius.circular(10.5)), - child: InkWell( - borderRadius: const BorderRadius.all(Radius.circular(10.5)), - onTap: () => onSwitchChanged(!betaTimelineValue), - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 16), - child: Row( - children: [ - Transform.scale( - scale: _pulseAnimation.value, - child: Transform.rotate( - angle: _rotationAnimation.value * 0.02, - child: Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - shape: BoxShape.circle, - gradient: LinearGradient( - colors: [ - context.primaryColor.withValues(alpha: 0.2), - context.primaryColor.withValues(alpha: 0.1), - ], - ), - ), - child: Icon(Icons.auto_awesome, color: context.primaryColor, size: 20), - ), - ), - ), - const SizedBox(width: 28), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text( - "advanced_settings_beta_timeline_title".t(context: context), - style: context.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600), - ), - const SizedBox(width: 8), - Container( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), - decoration: BoxDecoration( - borderRadius: const BorderRadius.all(Radius.circular(8)), - gradient: LinearGradient( - colors: [ - context.primaryColor.withValues(alpha: 0.8), - context.primaryColor.withValues(alpha: 0.6), - ], - ), - ), - child: Text( - 'NEW', - style: context.textTheme.labelSmall?.copyWith( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 10, - height: 1.2, - ), - ), - ), - ], - ), - const SizedBox(height: 4), - Text( - "advanced_settings_beta_timeline_subtitle".t(context: context), - style: context.textTheme.labelLarge?.copyWith( - color: context.textTheme.labelLarge?.color?.withValues(alpha: 0.9), - ), - maxLines: 2, - ), - ], - ), - ), - Switch.adaptive( - value: betaTimelineValue, - onChanged: onSwitchChanged, - activeColor: context.primaryColor, - ), - ], - ), + void onSwitchChanged(bool value) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: value ? const Text("Enable Beta Timeline") : const Text("Disable Beta Timeline"), + content: value + ? const Text("Are you sure you want to enable the beta timeline?") + : const Text("Are you sure you want to disable the beta timeline?"), + actions: [ + TextButton( + onPressed: () { + context.pop(); + }, + child: Text( + "cancel".t(context: context), + style: TextStyle(fontSize: 16, fontWeight: FontWeight.w500, color: context.colorScheme.outline), ), ), - ), - ), - ); - }, + ElevatedButton( + onPressed: () async { + Navigator.of(context).pop(); + context.router.replaceAll([ChangeExperienceRoute(switchingToBeta: value)]); + }, + child: Text("ok".t(context: context)), + ), + ], + ); + }, + ); + } + + return Padding( + padding: const EdgeInsets.only(left: 4.0), + child: ListTile( + title: Text("advanced_settings_beta_timeline_title".t(context: context)), + subtitle: Text("advanced_settings_beta_timeline_subtitle".t(context: context)), + trailing: Switch.adaptive( + value: betaTimelineValue, + onChanged: onSwitchChanged, + activeColor: context.primaryColor, + ), + onTap: () => onSwitchChanged(!betaTimelineValue), + ), ); } }