chore: simplify timeline switcher toggle (#21864)

chore: timeline switcher option simplify
This commit is contained in:
Alex 2025-09-12 22:32:15 -05:00 committed by GitHub
parent 994a770921
commit 913b3789cc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 66 additions and 235 deletions

View file

@ -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(
return SafeArea(
bottom: true,
top: false,
right: true,
child: Scaffold(
appBar: AppBar(centerTitle: false, title: Text(section.title).tr()),
body: section.widget,
),
);
}
}

View file

@ -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),
if (!Store.isBetaTimelineEnabled)
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
const BetaTimelineListTile(),
if (Store.isBetaTimelineEnabled)
SettingsSwitchListTile(
valueNotifier: readonlyModeEnabled,

View file

@ -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<BetaTimelineListTile> createState() => _BetaTimelineListTileState();
}
class _BetaTimelineListTileState extends ConsumerState<BetaTimelineListTile> with SingleTickerProviderStateMixin {
late AnimationController _animationController;
late Animation<double> _rotationAnimation;
late Animation<double> _pulseAnimation;
late Animation<double> _gradientAnimation;
@override
void initState() {
super.initState();
_animationController = AnimationController(duration: const Duration(seconds: 3), vsync: this);
_rotationAnimation = Tween<double>(
begin: 0,
end: 2 * math.pi,
).animate(CurvedAnimation(parent: _animationController, curve: Curves.linear));
_pulseAnimation = Tween<double>(
begin: 1,
end: 1.1,
).animate(CurvedAnimation(parent: _animationController, curve: Curves.easeInOut));
_gradientAnimation = Tween<double>(
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<bool>(AppSettingsEnum.betaTimeline);
final serverInfo = ref.watch(serverInfoProvider);
final auth = ref.watch(authProvider);
@ -64,9 +23,6 @@ class _BetaTimelineListTileState extends ConsumerState<BetaTimelineListTile> wit
return const SizedBox.shrink();
}
return AnimatedBuilder(
animation: _animationController,
builder: (context, child) {
void onSwitchChanged(bool value) {
showDialog(
context: context,
@ -99,133 +55,18 @@ class _BetaTimelineListTileState extends ConsumerState<BetaTimelineListTile> wit
);
}
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(
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),
),
),
),
),
),
);
},
);
}
}