From fe1d0edf4c8cb4d9d76753f0d9d4c127f3a79996 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 19 Jan 2026 14:56:35 -0600 Subject: [PATCH] chore: mobile font tuning (#25349) * chore: mobile font tuning * chore: fix some paddings * setting page tune * chore: album sort dropdown button styling * pr feedback * tweak sync status card * chore: refactor --- i18n/en.json | 4 +- mobile/lib/pages/common/settings.page.dart | 4 +- .../lib/pages/settings/sync_status.page.dart | 1 + .../widgets/album/album_selector.widget.dart | 12 +- .../asset_viewer/bottom_sheet.widget.dart | 33 +-- .../sheet_location_details.widget.dart | 12 +- .../sheet_people_details.widget.dart | 8 +- mobile/lib/theme/theme_data.dart | 7 +- .../asset_list_group_settings.dart | 24 ++- .../asset_list_layout_settings.dart | 10 +- .../image_viewer_quality_setting.dart | 21 +- .../video_viewer_settings.dart | 21 +- .../drift_backup_settings.dart | 199 ++++++++---------- .../beta_sync_settings/entity_count_tile.dart | 37 ++-- .../sync_status_and_actions.dart | 75 +++---- .../settings/beta_timeline_list_tile.dart | 5 +- .../settings/free_up_space_settings.dart | 27 +-- .../networking_settings/endpoint_input.dart | 2 +- .../external_network_preference.dart | 6 +- .../local_network_preference.dart | 6 +- .../networking_settings.dart | 61 ++---- .../preference_settings/haptic_setting.dart | 11 +- .../preference_settings/theme_setting.dart | 17 +- .../widgets/settings/setting_group_title.dart | 39 ++++ .../widgets/settings/setting_list_tile.dart | 38 ++++ .../lib/widgets/settings/settings_card.dart | 7 +- .../settings/settings_sub_page_scaffold.dart | 6 +- 27 files changed, 350 insertions(+), 343 deletions(-) create mode 100644 mobile/lib/widgets/settings/setting_group_title.dart create mode 100644 mobile/lib/widgets/settings/setting_list_tile.dart diff --git a/i18n/en.json b/i18n/en.json index c62ad97e7d..ddaabb286c 100644 --- a/i18n/en.json +++ b/i18n/en.json @@ -603,7 +603,7 @@ "backup_album_selection_page_select_albums": "Select albums", "backup_album_selection_page_selection_info": "Selection Info", "backup_album_selection_page_total_assets": "Total unique assets", - "backup_albums_sync": "Backup albums synchronization", + "backup_albums_sync": "Backup Albums Synchronization", "backup_all": "All", "backup_background_service_backup_failed_message": "Failed to backup assets. Retrying…", "backup_background_service_complete_notification": "Asset backup complete", @@ -2257,7 +2257,7 @@ "url": "URL", "usage": "Usage", "use_biometric": "Use biometric", - "use_current_connection": "use current connection", + "use_current_connection": "Use current connection", "use_custom_date_range": "Use custom date range instead", "user": "User", "user_has_been_deleted": "This user has been deleted.", diff --git a/mobile/lib/pages/common/settings.page.dart b/mobile/lib/pages/common/settings.page.dart index a1d7e55f32..22bc893cac 100644 --- a/mobile/lib/pages/common/settings.page.dart +++ b/mobile/lib/pages/common/settings.page.dart @@ -92,7 +92,7 @@ class _MobileLayout extends StatelessWidget { ], ) .toList(); - return ListView(padding: const EdgeInsets.only(top: 10.0, bottom: 16), children: [...settings]); + return ListView(padding: const EdgeInsets.only(top: 10.0, bottom: 60), children: [...settings]); } } @@ -142,7 +142,7 @@ class SettingsSubPage extends StatelessWidget { context.locale; return Scaffold( appBar: AppBar(centerTitle: false, title: Text(section.title).tr()), - body: section.widget, + body: Padding(padding: const EdgeInsets.only(bottom: 60.0), child: section.widget), ); } } diff --git a/mobile/lib/pages/settings/sync_status.page.dart b/mobile/lib/pages/settings/sync_status.page.dart index d54ba89e5d..58750e9e30 100644 --- a/mobile/lib/pages/settings/sync_status.page.dart +++ b/mobile/lib/pages/settings/sync_status.page.dart @@ -18,6 +18,7 @@ class SyncStatusPage extends StatelessWidget { splashRadius: 24, icon: const Icon(Icons.arrow_back_ios_rounded), ), + centerTitle: false, ), body: const SyncStatusAndActions(), ); diff --git a/mobile/lib/presentation/widgets/album/album_selector.widget.dart b/mobile/lib/presentation/widgets/album/album_selector.widget.dart index 4f37f88344..318e9894f2 100644 --- a/mobile/lib/presentation/widgets/album/album_selector.widget.dart +++ b/mobile/lib/presentation/widgets/album/album_selector.widget.dart @@ -311,18 +311,17 @@ class _SortButtonState extends ConsumerState<_SortButton> { : const Icon(Icons.abc, color: Colors.transparent), onPressed: () => onMenuTapped(sortMode), style: ButtonStyle( - padding: WidgetStateProperty.all(const EdgeInsets.fromLTRB(16, 16, 32, 16)), + padding: WidgetStateProperty.all(const EdgeInsets.fromLTRB(12, 12, 24, 12)), backgroundColor: WidgetStateProperty.all( albumSortOption == sortMode ? context.colorScheme.primary : Colors.transparent, ), shape: WidgetStateProperty.all( - const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(24))), + const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(12))), ), ), child: Text( sortMode.label.t(context: context), - style: context.textTheme.titleSmall?.copyWith( - fontWeight: FontWeight.w600, + style: context.textTheme.labelLarge?.copyWith( color: albumSortOption == sortMode ? context.colorScheme.onPrimary : context.colorScheme.onSurface.withAlpha(185), @@ -350,10 +349,7 @@ class _SortButtonState extends ConsumerState<_SortButton> { ), Text( albumSortOption.label.t(context: context), - style: context.textTheme.bodyLarge?.copyWith( - fontWeight: FontWeight.w500, - color: context.colorScheme.onSurface.withAlpha(225), - ), + style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurface.withAlpha(225)), ), isSorting ? SizedBox( diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart index ed3873b510..771d518bba 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet.widget.dart @@ -10,6 +10,7 @@ import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; import 'package:immich_mobile/extensions/duration_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/album/album_tile.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/asset_viewer.state.dart'; @@ -164,11 +165,8 @@ class _AssetDetailBottomSheet extends ConsumerWidget { children: [ if (albums.isNotEmpty) SheetTile( - title: 'appears_in'.t(context: context).toUpperCase(), - titleStyle: context.textTheme.labelMedium?.copyWith( - color: context.textTheme.labelMedium?.color?.withAlpha(200), - fontWeight: FontWeight.w600, - ), + title: 'appears_in'.t(context: context), + titleStyle: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), Padding( padding: const EdgeInsets.only(left: 24), @@ -224,9 +222,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget { color: context.textTheme.labelLarge?.color, ), subtitle: _getFileInfo(asset, exifInfo), - subtitleStyle: context.textTheme.labelMedium?.copyWith( - color: context.textTheme.labelMedium?.color?.withAlpha(200), - ), + subtitleStyle: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ); }, ); @@ -241,9 +237,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget { color: context.textTheme.labelLarge?.color, ), subtitle: _getFileInfo(asset, exifInfo), - subtitleStyle: context.textTheme.labelMedium?.copyWith( - color: context.textTheme.labelMedium?.color?.withAlpha(200), - ), + subtitleStyle: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ); } } @@ -262,11 +256,8 @@ class _AssetDetailBottomSheet extends ConsumerWidget { const SheetLocationDetails(), // Details header SheetTile( - title: 'details'.t(context: context).toUpperCase(), - titleStyle: context.textTheme.labelMedium?.copyWith( - color: context.textTheme.labelMedium?.color?.withAlpha(200), - fontWeight: FontWeight.w600, - ), + title: 'details'.t(context: context), + titleStyle: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), // File info buildFileInfoTile(), @@ -278,9 +269,7 @@ class _AssetDetailBottomSheet extends ConsumerWidget { titleStyle: context.textTheme.labelLarge, leading: Icon(Icons.camera_alt_outlined, size: 24, color: context.textTheme.labelLarge?.color), subtitle: _getCameraInfoSubtitle(exifInfo), - subtitleStyle: context.textTheme.labelMedium?.copyWith( - color: context.textTheme.labelMedium?.color?.withAlpha(200), - ), + subtitleStyle: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ], // Lens info @@ -291,15 +280,13 @@ class _AssetDetailBottomSheet extends ConsumerWidget { titleStyle: context.textTheme.labelLarge, leading: Icon(Icons.camera_outlined, size: 24, color: context.textTheme.labelLarge?.color), subtitle: _getLensInfoSubtitle(exifInfo), - subtitleStyle: context.textTheme.labelMedium?.copyWith( - color: context.textTheme.labelMedium?.color?.withAlpha(200), - ), + subtitleStyle: context.textTheme.bodyMedium?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ], // Appears in (Albums) Padding(padding: const EdgeInsets.only(top: 16.0), child: _buildAppearsInList(ref, context)), // padding at the bottom to avoid cut-off - const SizedBox(height: 30), + const SizedBox(height: 60), ], ); } diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart index c0343d03ca..ce561c4016 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_location_details.widget.dart @@ -4,6 +4,7 @@ import 'package:immich_mobile/constants/enums.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/asset_viewer/sheet_tile.widget.dart'; import 'package:immich_mobile/providers/infrastructure/action.provider.dart'; @@ -77,11 +78,8 @@ class _SheetLocationDetailsState extends ConsumerState { crossAxisAlignment: CrossAxisAlignment.start, children: [ SheetTile( - title: 'location'.t(context: context).toUpperCase(), - titleStyle: context.textTheme.labelMedium?.copyWith( - color: context.textTheme.labelMedium?.color?.withAlpha(200), - fontWeight: FontWeight.w600, - ), + title: 'location'.t(context: context), + titleStyle: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceSecondary), trailing: hasCoordinates ? const Icon(Icons.edit_location_alt, size: 20) : null, onTap: editLocation, ), @@ -105,9 +103,7 @@ class _SheetLocationDetailsState extends ConsumerState { ), Text( coordinates, - style: context.textTheme.labelMedium?.copyWith( - color: context.textTheme.labelMedium?.color?.withAlpha(200), - ), + style: context.textTheme.bodySmall?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ], ), diff --git a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart index 64f22eca92..d62a964401 100644 --- a/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart +++ b/mobile/lib/presentation/widgets/asset_viewer/bottom_sheet/sheet_people_details.widget.dart @@ -4,6 +4,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/domain/models/asset/base_asset.model.dart'; import 'package:immich_mobile/domain/models/person.model.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/presentation/widgets/people/person_edit_name_modal.widget.dart'; import 'package:immich_mobile/providers/infrastructure/asset_viewer/current_asset.provider.dart'; @@ -53,11 +54,8 @@ class _SheetPeopleDetailsState extends ConsumerState { Padding( padding: const EdgeInsets.only(left: 16, top: 16, bottom: 16), child: Text( - "people".t(context: context).toUpperCase(), - style: context.textTheme.labelMedium?.copyWith( - color: context.textTheme.labelMedium?.color?.withAlpha(200), - fontWeight: FontWeight.w600, - ), + "people".t(context: context), + style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceSecondary), ), ), SizedBox( diff --git a/mobile/lib/theme/theme_data.dart b/mobile/lib/theme/theme_data.dart index 2a6ca3a8da..a633a04d7f 100644 --- a/mobile/lib/theme/theme_data.dart +++ b/mobile/lib/theme/theme_data.dart @@ -61,7 +61,12 @@ ThemeData getThemeData({required ColorScheme colorScheme, required Locale locale ), ), chipTheme: const ChipThemeData(side: BorderSide.none), - sliderTheme: const SliderThemeData(thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7), trackHeight: 2.0), + sliderTheme: const SliderThemeData( + thumbShape: RoundSliderThumbShape(enabledThumbRadius: 7), + trackHeight: 2.0, + // ignore: deprecated_member_use + year2023: false, + ), bottomNavigationBarTheme: const BottomNavigationBarThemeData(type: BottomNavigationBarType.fixed), popupMenuTheme: const PopupMenuThemeData( shape: RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(10))), diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart index 04786bf916..08e66df48d 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_group_settings.dart @@ -1,14 +1,14 @@ import 'dart:async'; -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/app_settings.provider.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/widgets/asset_grid/asset_grid_data_structure.dart'; +import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/settings_radio_list_tile.dart'; -import 'package:immich_mobile/widgets/settings/settings_sub_title.dart'; class GroupSettings extends HookConsumerWidget { const GroupSettings({super.key}); @@ -33,12 +33,24 @@ class GroupSettings extends HookConsumerWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SettingsSubTitle(title: "asset_list_group_by_sub_title".tr()), + SettingGroupTitle( + title: "asset_list_group_by_sub_title".t(context: context), + icon: Icons.group_work_outlined, + ), SettingsRadioListTile( groups: [ - SettingsRadioGroup(title: 'asset_list_layout_settings_group_by_month_day'.tr(), value: GroupAssetsBy.day), - SettingsRadioGroup(title: 'month'.tr(), value: GroupAssetsBy.month), - SettingsRadioGroup(title: 'asset_list_layout_settings_group_automatically'.tr(), value: GroupAssetsBy.auto), + SettingsRadioGroup( + title: 'asset_list_layout_settings_group_by_month_day'.t(context: context), + value: GroupAssetsBy.day, + ), + SettingsRadioGroup( + title: 'month'.t(context: context), + value: GroupAssetsBy.month, + ), + SettingsRadioGroup( + title: 'asset_list_layout_settings_group_automatically'.t(context: context), + value: GroupAssetsBy.auto, + ), ], groupBy: groupBy, onRadioChanged: changeGroupValue, diff --git a/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart b/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart index bcb4a5ec9c..5d82630fc6 100644 --- a/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart +++ b/mobile/lib/widgets/settings/asset_list_settings/asset_list_layout_settings.dart @@ -1,11 +1,12 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/app_settings.provider.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/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/settings_slider_list_tile.dart'; -import 'package:immich_mobile/widgets/settings/settings_sub_title.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; class LayoutSettings extends HookConsumerWidget { @@ -19,10 +20,13 @@ class LayoutSettings extends HookConsumerWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SettingsSubTitle(title: "asset_list_layout_sub_title".tr()), + SettingGroupTitle( + title: "asset_list_layout_sub_title".t(context: context), + icon: Icons.view_module_outlined, + ), SettingsSwitchListTile( valueNotifier: useDynamicLayout, - title: "asset_list_layout_settings_dynamic_layout_title".tr(), + title: "asset_list_layout_settings_dynamic_layout_title".t(context: context), onChanged: (_) => ref.invalidate(appSettingsServiceProvider), ), SettingsSliderListTile( diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart b/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart index aed88b90b0..e437b82dd4 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/image_viewer_quality_setting.dart @@ -1,10 +1,9 @@ -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; -import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:immich_mobile/widgets/settings/settings_sub_title.dart'; +import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; @@ -19,21 +18,21 @@ class ImageViewerQualitySetting extends HookConsumerWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SettingsSubTitle(title: "setting_image_viewer_title".tr()), - ListTile( - contentPadding: const EdgeInsets.symmetric(horizontal: 20), - title: Text('setting_image_viewer_help', style: context.textTheme.bodyMedium).tr(), + SettingGroupTitle( + title: "photos".t(context: context), + icon: Icons.image_outlined, + subtitle: "setting_image_viewer_help".t(context: context), ), SettingsSwitchListTile( valueNotifier: isPreview, - title: "setting_image_viewer_preview_title".tr(), - subtitle: "setting_image_viewer_preview_subtitle".tr(), + title: "setting_image_viewer_preview_title".t(context: context), + subtitle: "setting_image_viewer_preview_subtitle".t(context: context), onChanged: (_) => ref.invalidate(appSettingsServiceProvider), ), SettingsSwitchListTile( valueNotifier: isOriginal, - title: "setting_image_viewer_original_title".tr(), - subtitle: "setting_image_viewer_original_subtitle".tr(), + title: "setting_image_viewer_original_title".t(context: context), + subtitle: "setting_image_viewer_original_subtitle".t(context: context), onChanged: (_) => ref.invalidate(appSettingsServiceProvider), ), ], diff --git a/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart b/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart index 9a89b7e1e3..c03dcc51b4 100644 --- a/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart +++ b/mobile/lib/widgets/settings/asset_viewer_settings/video_viewer_settings.dart @@ -1,9 +1,9 @@ -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:immich_mobile/widgets/settings/settings_sub_title.dart'; +import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; @@ -19,23 +19,26 @@ class VideoViewerSettings extends HookConsumerWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SettingsSubTitle(title: "videos".tr()), + SettingGroupTitle( + title: "videos".t(context: context), + icon: Icons.video_camera_back_outlined, + ), SettingsSwitchListTile( valueNotifier: useAutoPlayVideo, - title: "setting_video_viewer_auto_play_title".tr(), - subtitle: "setting_video_viewer_auto_play_subtitle".tr(), + title: "setting_video_viewer_auto_play_title".t(context: context), + subtitle: "setting_video_viewer_auto_play_subtitle".t(context: context), onChanged: (_) => ref.invalidate(appSettingsServiceProvider), ), SettingsSwitchListTile( valueNotifier: useLoopVideo, - title: "setting_video_viewer_looping_title".tr(), - subtitle: "loop_videos_description".tr(), + title: "setting_video_viewer_looping_title".t(context: context), + subtitle: "loop_videos_description".t(context: context), onChanged: (_) => ref.invalidate(appSettingsServiceProvider), ), SettingsSwitchListTile( valueNotifier: useOriginalVideo, - title: "setting_video_viewer_original_video_title".tr(), - subtitle: "setting_video_viewer_original_video_subtitle".tr(), + title: "setting_video_viewer_original_video_title".t(context: context), + subtitle: "setting_video_viewer_original_video_subtitle".t(context: context), onChanged: (_) => ref.invalidate(appSettingsServiceProvider), ), ], diff --git a/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart b/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart index 743d38fc48..b44971a135 100644 --- a/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart +++ b/mobile/lib/widgets/settings/backup_settings/drift_backup_settings.dart @@ -16,6 +16,8 @@ import 'package:immich_mobile/providers/backup/backup_album.provider.dart'; import 'package:immich_mobile/providers/infrastructure/platform.provider.dart'; import 'package:immich_mobile/providers/user.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; +import 'package:immich_mobile/widgets/settings/setting_list_tile.dart'; import 'package:immich_mobile/widgets/settings/settings_sub_page_scaffold.dart'; class DriftBackupSettings extends ConsumerWidget { @@ -25,36 +27,25 @@ class DriftBackupSettings extends ConsumerWidget { Widget build(BuildContext context, WidgetRef ref) { return SettingsSubPageScaffold( settings: [ - Padding( - padding: const EdgeInsets.only(left: 16.0), - child: Text( - "network_requirements".t(context: context).toUpperCase(), - style: context.textTheme.labelSmall?.copyWith(color: context.colorScheme.onSurface.withValues(alpha: 0.7)), - ), + SettingGroupTitle( + title: "network_requirements".t(context: context), + icon: Icons.cell_tower, ), const _UseWifiForUploadVideosButton(), const _UseWifiForUploadPhotosButton(), if (CurrentPlatform.isAndroid) ...[ const Divider(), - Padding( - padding: const EdgeInsets.only(left: 16.0), - child: Text( - "background_options".t(context: context).toUpperCase(), - style: context.textTheme.labelSmall?.copyWith( - color: context.colorScheme.onSurface.withValues(alpha: 0.7), - ), - ), + SettingGroupTitle( + title: "background_options".t(context: context), + icon: Icons.charging_station_rounded, ), const _BackupOnlyWhenChargingButton(), const _BackupDelaySlider(), ], const Divider(), - Padding( - padding: const EdgeInsets.only(left: 16.0), - child: Text( - "backup_albums_sync".t(context: context).toUpperCase(), - style: context.textTheme.labelSmall?.copyWith(color: context.colorScheme.onSurface.withValues(alpha: 0.7)), - ), + SettingGroupTitle( + title: "backup_albums_sync".t(context: context), + icon: Icons.sync, ), const _AlbumSyncActionButton(), ], @@ -105,81 +96,67 @@ class _AlbumSyncActionButtonState extends ConsumerState<_AlbumSyncActionButton> @override Widget build(BuildContext context) { - return ListView( - shrinkWrap: true, - children: [ - StreamBuilder( - stream: Store.watch(StoreKey.syncAlbums), - initialData: Store.tryGet(StoreKey.syncAlbums) ?? false, - builder: (context, snapshot) { - final albumSyncEnable = snapshot.data ?? false; - return Column( - children: [ - ListTile( - title: Text( - "sync_albums".t(context: context), - style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor), - ), - subtitle: Text( - "sync_upload_album_setting_subtitle".t(context: context), - style: context.textTheme.labelLarge, - ), - trailing: Switch( - value: albumSyncEnable, - onChanged: (bool newValue) async { - await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.syncAlbums, newValue); + return Padding( + padding: const EdgeInsets.only(left: 8.0), + child: ListView( + shrinkWrap: true, + children: [ + StreamBuilder( + stream: Store.watch(StoreKey.syncAlbums), + initialData: Store.tryGet(StoreKey.syncAlbums) ?? false, + builder: (context, snapshot) { + final albumSyncEnable = snapshot.data ?? false; + return Column( + children: [ + SettingListTile( + title: "sync_albums".t(context: context), + subtitle: "sync_upload_album_setting_subtitle".t(context: context), + trailing: Switch( + value: albumSyncEnable, + onChanged: (bool newValue) async { + await ref.read(appSettingsServiceProvider).setSetting(AppSettingsEnum.syncAlbums, newValue); - if (newValue == true) { - await _manageLinkedAlbums(); - } - }, + if (newValue == true) { + await _manageLinkedAlbums(); + } + }, + ), ), - ), - AnimatedSize( - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - child: AnimatedOpacity( - duration: const Duration(milliseconds: 200), - opacity: albumSyncEnable ? 1.0 : 0.0, - child: albumSyncEnable - ? ListTile( - onTap: _manualSyncAlbums, - contentPadding: const EdgeInsets.only(left: 32, right: 16), - title: Text( - "organize_into_albums".t(context: context), - style: context.textTheme.titleSmall?.copyWith( - color: context.colorScheme.onSurface, - fontWeight: FontWeight.normal, - ), - ), - subtitle: Text( - "organize_into_albums_description".t(context: context), - style: context.textTheme.bodyMedium?.copyWith( - color: context.colorScheme.onSurface.withValues(alpha: 0.7), - ), - ), - trailing: isAlbumSyncInProgress - ? const SizedBox( - width: 32, - height: 32, - child: CircularProgressIndicator.adaptive(strokeWidth: 2), - ) - : IconButton( - onPressed: _manualSyncAlbums, - icon: const Icon(Icons.sync_rounded), - color: context.colorScheme.onSurface.withValues(alpha: 0.7), - iconSize: 20, - constraints: const BoxConstraints(minWidth: 32, minHeight: 32), - ), - ) - : const SizedBox.shrink(), + AnimatedSize( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + child: AnimatedOpacity( + duration: const Duration(milliseconds: 200), + opacity: albumSyncEnable ? 1.0 : 0.0, + child: albumSyncEnable + ? SettingListTile( + onTap: _manualSyncAlbums, + contentPadding: const EdgeInsets.only(left: 32, right: 16), + title: "organize_into_albums".t(context: context), + subtitle: "organize_into_albums_description".t(context: context), + trailing: isAlbumSyncInProgress + ? const SizedBox( + width: 32, + height: 32, + child: CircularProgressIndicator.adaptive(strokeWidth: 2), + ) + : IconButton( + onPressed: _manualSyncAlbums, + icon: const Icon(Icons.sync_rounded), + color: context.colorScheme.onSurface.withValues(alpha: 0.7), + iconSize: 20, + constraints: const BoxConstraints(minWidth: 32, minHeight: 32), + ), + ) + : const SizedBox.shrink(), + ), ), - ), - ], - ); - }, - ), - ], + ], + ); + }, + ), + ], + ), ); } } @@ -222,24 +199,24 @@ class _SettingsSwitchTileState extends ConsumerState<_SettingsSwitchTile> { @override Widget build(BuildContext context) { - return ListTile( - title: Text( - widget.titleKey.t(context: context), - style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor), - ), - subtitle: Text(widget.subtitleKey.t(context: context), style: context.textTheme.labelLarge), - trailing: StreamBuilder( - stream: valueStream, - initialData: Store.tryGet(widget.appSettingsEnum.storeKey) ?? widget.appSettingsEnum.defaultValue, - builder: (context, snapshot) { - final value = snapshot.data ?? false; - return Switch( - value: value, - onChanged: (bool newValue) async { - await ref.read(appSettingsServiceProvider).setSetting(widget.appSettingsEnum, newValue); - }, - ); - }, + return Padding( + padding: const EdgeInsets.only(left: 8.0), + child: SettingListTile( + title: widget.titleKey.t(context: context), + subtitle: widget.subtitleKey.t(context: context), + trailing: StreamBuilder( + stream: valueStream, + initialData: Store.tryGet(widget.appSettingsEnum.storeKey) ?? widget.appSettingsEnum.defaultValue, + builder: (context, snapshot) { + final value = snapshot.data ?? false; + return Switch( + value: value, + onChanged: (bool newValue) async { + await ref.read(appSettingsServiceProvider).setSetting(widget.appSettingsEnum, newValue); + }, + ); + }, + ), ), ); } @@ -354,7 +331,7 @@ class _BackupDelaySliderState extends ConsumerState<_BackupDelaySlider> { 'backup_controller_page_background_delay'.tr( namedArgs: {'duration': formatBackupDelaySliderValue(currentValue)}, ), - style: context.textTheme.titleMedium?.copyWith(color: context.primaryColor), + style: context.textTheme.bodyLarge?.copyWith(fontWeight: FontWeight.w500), ), ), Slider( diff --git a/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart b/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart index 6120524d5b..be28162b98 100644 --- a/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart +++ b/mobile/lib/widgets/settings/beta_sync_settings/entity_count_tile.dart @@ -34,33 +34,36 @@ class EntityCountTile extends StatelessWidget { children: [ // Icon and Label Row( - mainAxisAlignment: MainAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, children: [ - Icon(icon, color: context.primaryColor), - const SizedBox(width: 8), + Icon(icon, color: context.primaryColor, size: 14), + const SizedBox(width: 4), Flexible( child: Text( label, - style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.bold, fontSize: 16), + style: TextStyle(color: context.primaryColor, fontWeight: FontWeight.w500), ), ), ], ), // Number const Spacer(), - RichText( - text: TextSpan( - style: const TextStyle(fontSize: 18, fontFamily: 'GoogleSansCode', fontWeight: FontWeight.w600), - children: [ - TextSpan( - text: zeroPadding(count, maxDigits), - style: TextStyle(color: context.colorScheme.onSurfaceSecondary.withAlpha(75)), - ), - TextSpan( - text: count.toString(), - style: TextStyle(color: context.primaryColor), - ), - ], + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: RichText( + text: TextSpan( + style: const TextStyle(fontSize: 18, fontFamily: 'GoogleSansCode'), + children: [ + TextSpan( + text: zeroPadding(count, maxDigits), + style: TextStyle(color: context.colorScheme.onSurfaceSecondary.withAlpha(75)), + ), + TextSpan( + text: count.toString(), + style: TextStyle(color: context.colorScheme.onSurface), + ), + ], + ), ), ), ], diff --git a/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart b/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart index 1f3fa14c62..ada2aacd66 100644 --- a/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart +++ b/mobile/lib/widgets/settings/beta_sync_settings/sync_status_and_actions.dart @@ -16,6 +16,8 @@ import 'package:immich_mobile/providers/infrastructure/trash_sync.provider.dart' import 'package:immich_mobile/providers/sync_status.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/widgets/settings/beta_sync_settings/entity_count_tile.dart'; +import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; +import 'package:immich_mobile/widgets/settings/setting_list_tile.dart'; import 'package:path/path.dart' as path; import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; @@ -112,48 +114,39 @@ class SyncStatusAndActions extends HookConsumerWidget { padding: const EdgeInsets.only(top: 16, bottom: 96), children: [ const _SyncStatsCounts(), - const Divider(height: 1, indent: 16, endIndent: 16), - const SizedBox(height: 24), - _SectionHeaderText(text: "jobs".t(context: context)), - ListTile( - title: Text( - "sync_local".t(context: context), - style: const TextStyle(fontWeight: FontWeight.w500), - ), - subtitle: Text("tap_to_run_job".t(context: context)), + const Divider(height: 10), + const SizedBox(height: 16), + SettingGroupTitle(title: "jobs".t(context: context)), + SettingListTile( + title: "sync_local".t(context: context), + subtitle: "tap_to_run_job".t(context: context), leading: const Icon(Icons.sync), trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).localSyncStatus), onTap: () { ref.read(backgroundSyncProvider).syncLocal(full: true); }, ), - ListTile( - title: Text( - "sync_remote".t(context: context), - style: const TextStyle(fontWeight: FontWeight.w500), - ), - subtitle: Text("tap_to_run_job".t(context: context)), + SettingListTile( + title: "sync_remote".t(context: context), + subtitle: "tap_to_run_job".t(context: context), leading: const Icon(Icons.cloud_sync), trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).remoteSyncStatus), onTap: () { ref.read(backgroundSyncProvider).syncRemote(); }, ), - ListTile( - title: Text( - "hash_asset".t(context: context), - style: const TextStyle(fontWeight: FontWeight.w500), - ), + SettingListTile( + title: "hash_asset".t(context: context), leading: const Icon(Icons.tag), - subtitle: Text("tap_to_run_job".t(context: context)), + subtitle: "tap_to_run_job".t(context: context), trailing: _SyncStatusIcon(status: ref.watch(syncStatusProvider).hashJobStatus), onTap: () { ref.read(backgroundSyncProvider).hashAssets(); }, ), - const Divider(height: 1, indent: 16, endIndent: 16), - const SizedBox(height: 24), - _SectionHeaderText(text: "actions".t(context: context)), + const Divider(height: 1), + const SizedBox(height: 16), + SettingGroupTitle(title: "actions".t(context: context)), ListTile( title: Text( "clear_file_cache".t(context: context), @@ -202,26 +195,6 @@ class _SyncStatusIcon extends StatelessWidget { } } -class _SectionHeaderText extends StatelessWidget { - final String text; - - const _SectionHeaderText({required this.text}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(left: 16.0), - child: Text( - text.toUpperCase(), - style: context.textTheme.labelLarge?.copyWith( - fontWeight: FontWeight.w500, - color: context.colorScheme.onSurface.withAlpha(200), - ), - ), - ); - } -} - class _SyncStatsCounts extends ConsumerWidget { const _SyncStatsCounts(); @@ -279,9 +252,9 @@ class _SyncStatsCounts extends ConsumerWidget { mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - _SectionHeaderText(text: "assets".t(context: context)), + SettingGroupTitle(title: "assets".t(context: context)), Padding( - padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), // 1. Wrap in IntrinsicHeight child: IntrinsicHeight( child: Flex( @@ -309,9 +282,9 @@ class _SyncStatsCounts extends ConsumerWidget { ), ), ), - _SectionHeaderText(text: "albums".t(context: context)), + SettingGroupTitle(title: "albums".t(context: context)), Padding( - padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), child: IntrinsicHeight( child: Flex( direction: Axis.horizontal, @@ -337,9 +310,9 @@ class _SyncStatsCounts extends ConsumerWidget { ), ), ), - _SectionHeaderText(text: "other".t(context: context)), + SettingGroupTitle(title: "other".t(context: context)), Padding( - padding: const EdgeInsets.fromLTRB(16, 8, 16, 16), + padding: const EdgeInsets.fromLTRB(16, 0, 16, 16), child: IntrinsicHeight( child: Flex( direction: Axis.horizontal, @@ -368,7 +341,7 @@ class _SyncStatsCounts extends ConsumerWidget { // To be removed once the experimental feature is stable if (CurrentPlatform.isAndroid && appSettingsService.getSetting(AppSettingsEnum.manageLocalMediaAndroid)) ...[ - _SectionHeaderText(text: "trash".t(context: context)), + SettingGroupTitle(title: "trash".t(context: context)), Consumer( builder: (context, ref, _) { final counts = ref.watch(trashedAssetsCountProvider); diff --git a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart index 480665e614..21e0edb34c 100644 --- a/mobile/lib/widgets/settings/beta_timeline_list_tile.dart +++ b/mobile/lib/widgets/settings/beta_timeline_list_tile.dart @@ -9,6 +9,7 @@ import 'package:immich_mobile/providers/app_settings.provider.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/routing/router.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; +import 'package:immich_mobile/widgets/settings/setting_list_tile.dart'; class BetaTimelineListTile extends ConsumerWidget { const BetaTimelineListTile({super.key}); @@ -56,8 +57,8 @@ class BetaTimelineListTile extends ConsumerWidget { return Padding( padding: const EdgeInsets.only(left: 4.0), - child: ListTile( - title: Text("new_timeline".t(context: context)), + child: SettingListTile( + title: "new_timeline".t(context: context), trailing: Switch.adaptive( value: betaTimelineValue, onChanged: onSwitchChanged, diff --git a/mobile/lib/widgets/settings/free_up_space_settings.dart b/mobile/lib/widgets/settings/free_up_space_settings.dart index 7acb04686b..e24a4d481a 100644 --- a/mobile/lib/widgets/settings/free_up_space_settings.dart +++ b/mobile/lib/widgets/settings/free_up_space_settings.dart @@ -142,7 +142,9 @@ class _FreeUpSpaceSettingsState extends ConsumerState { final state = ref.watch(cleanupProvider); final hasDate = state.selectedDate != null; final hasAssets = _hasScanned && state.assetsToDelete.isNotEmpty; - + final subtitleStyle = context.textTheme.bodyMedium!.copyWith( + color: context.textTheme.bodyMedium!.color!.withAlpha(215), + ); StepStyle styleForState(StepState stepState, {bool isDestructive = false}) { switch (stepState) { case StepState.complete: @@ -214,10 +216,7 @@ class _FreeUpSpaceSettingsState extends ConsumerState { borderRadius: const BorderRadius.all(Radius.circular(12)), border: Border.all(color: context.primaryColor.withValues(alpha: 0.25)), ), - child: Text( - 'free_up_space_description'.t(context: context), - style: context.textTheme.labelLarge?.copyWith(fontSize: 15), - ), + child: Text('free_up_space_description'.t(context: context), style: context.textTheme.bodyMedium), ), ), @@ -256,7 +255,7 @@ class _FreeUpSpaceSettingsState extends ConsumerState { content: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Text('cutoff_date_description'.t(context: context), style: context.textTheme.labelLarge), + Text('cutoff_date_description'.t(context: context), style: subtitleStyle), const SizedBox(height: 16), GridView.count( shrinkWrap: true, @@ -352,7 +351,7 @@ class _FreeUpSpaceSettingsState extends ConsumerState { content: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - Text('cleanup_filter_description'.t(context: context), style: context.textTheme.labelLarge), + Text('cleanup_filter_description'.t(context: context), style: subtitleStyle), const SizedBox(height: 16), SegmentedButton( segments: [ @@ -381,10 +380,15 @@ class _FreeUpSpaceSettingsState extends ConsumerState { const SizedBox(height: 16), SwitchListTile( contentPadding: EdgeInsets.zero, - title: Text('keep_favorites'.t(context: context), style: context.textTheme.titleSmall), + title: Text( + 'keep_favorites'.t(context: context), + style: context.textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.w500, height: 1.5), + ), subtitle: Text( 'keep_favorites_description'.t(context: context), - style: context.textTheme.labelLarge, + style: context.textTheme.bodyMedium!.copyWith( + color: context.textTheme.bodyMedium!.color!.withAlpha(215), + ), ), value: state.keepFavorites, onChanged: (value) { @@ -435,10 +439,7 @@ class _FreeUpSpaceSettingsState extends ConsumerState { : null, content: Column( children: [ - Text( - 'cleanup_step3_description'.t(context: context), - style: context.textTheme.labelLarge?.copyWith(fontSize: 15), - ), + Text('cleanup_step3_description'.t(context: context), style: subtitleStyle), if (CurrentPlatform.isIOS) ...[ const SizedBox(height: 12), Container( diff --git a/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart b/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart index 47e85fd7cc..735971e0c2 100644 --- a/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart +++ b/mobile/lib/widgets/settings/networking_settings/endpoint_input.dart @@ -117,7 +117,7 @@ class EndpointInputState extends ConsumerState { autovalidateMode: AutovalidateMode.onUserInteraction, validator: validateUrl, keyboardType: TextInputType.url, - style: const TextStyle(fontFamily: 'GoogleSansCode', fontWeight: FontWeight.w600, fontSize: 14), + style: const TextStyle(fontFamily: 'GoogleSansCode', fontSize: 14), decoration: InputDecoration( hintText: 'http(s)://immich.domain.com', contentPadding: const EdgeInsets.all(16), diff --git a/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart b/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart index 8cc6079961..da5ecab684 100644 --- a/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart +++ b/mobile/lib/widgets/settings/networking_settings/external_network_preference.dart @@ -1,12 +1,12 @@ import 'dart:convert'; -import 'package:easy_localization/easy_localization.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/store.model.dart'; import 'package:immich_mobile/entities/store.entity.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart'; import 'package:immich_mobile/widgets/settings/networking_settings/endpoint_input.dart'; @@ -103,7 +103,7 @@ class ExternalNetworkPreference extends HookConsumerWidget { children: [ Padding( padding: const EdgeInsets.symmetric(vertical: 4.0, horizontal: 24), - child: Text("external_network_sheet_info".tr(), style: context.textTheme.bodyMedium), + child: Text("external_network_sheet_info".t(context: context), style: context.textTheme.bodyMedium), ), const SizedBox(height: 4), Divider(color: context.colorScheme.surfaceContainerHighest), @@ -135,7 +135,7 @@ class ExternalNetworkPreference extends HookConsumerWidget { height: 48, child: OutlinedButton.icon( icon: const Icon(Icons.add), - label: Text('add_endpoint'.tr().toUpperCase()), + label: Text('add_endpoint'.t(context: context)), onPressed: enabled ? () { entries.value = [ diff --git a/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart b/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart index 3f47233eec..c89c8e149e 100644 --- a/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart +++ b/mobile/lib/widgets/settings/networking_settings/local_network_preference.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/auth.provider.dart'; import 'package:immich_mobile/providers/network.provider.dart'; @@ -167,13 +168,12 @@ class LocalNetworkPreference extends HookConsumerWidget { enabled: enabled, contentPadding: const EdgeInsets.only(left: 24, right: 8), leading: const Icon(Icons.lan_rounded), - title: Text("server_endpoint".tr()), + title: Text("server_endpoint".t(context: context)), subtitle: localEndpointText.value.isEmpty ? const Text("http://local-ip:2283") : Text( localEndpointText.value, style: context.textTheme.labelLarge?.copyWith( - fontWeight: FontWeight.bold, color: enabled ? context.primaryColor : context.colorScheme.onSurface.withAlpha(100), fontFamily: 'GoogleSansCode', ), @@ -190,7 +190,7 @@ class LocalNetworkPreference extends HookConsumerWidget { height: 48, child: OutlinedButton.icon( icon: const Icon(Icons.wifi_find_rounded), - label: Text('use_current_connection'.tr().toUpperCase()), + label: Text('use_current_connection'.t(context: context)), onPressed: enabled ? autofillCurrentNetwork : null, ), ), diff --git a/mobile/lib/widgets/settings/networking_settings/networking_settings.dart b/mobile/lib/widgets/settings/networking_settings/networking_settings.dart index 4acf80af84..981bec2c0c 100644 --- a/mobile/lib/widgets/settings/networking_settings/networking_settings.dart +++ b/mobile/lib/widgets/settings/networking_settings/networking_settings.dart @@ -3,6 +3,7 @@ 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/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/models/auth/auxilary_endpoint.model.dart'; import 'package:immich_mobile/providers/network.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; @@ -10,6 +11,7 @@ import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; import 'package:immich_mobile/utils/url_helper.dart'; import 'package:immich_mobile/widgets/settings/networking_settings/external_network_preference.dart'; import 'package:immich_mobile/widgets/settings/networking_settings/local_network_preference.dart'; +import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; class NetworkingSettings extends HookConsumerWidget { @@ -87,12 +89,10 @@ class NetworkingSettings extends HookConsumerWidget { return ListView( padding: const EdgeInsets.only(bottom: 96), children: [ - Padding( - padding: const EdgeInsets.only(top: 8, left: 16, bottom: 8), - child: NetworkPreferenceTitle( - title: "current_server_address".tr().toUpperCase(), - icon: (currentEndpoint?.startsWith('https') ?? false) ? Icons.https_outlined : Icons.http_outlined, - ), + const SizedBox(height: 8), + SettingGroupTitle( + title: "current_server_address".t(context: context), + icon: (currentEndpoint?.startsWith('https') ?? false) ? Icons.https_outlined : Icons.http_outlined, ), Padding( padding: const EdgeInsets.symmetric(horizontal: 8), @@ -108,12 +108,7 @@ class NetworkingSettings extends HookConsumerWidget { : const Icon(Icons.circle_outlined), title: Text( currentEndpoint ?? "--", - style: TextStyle( - fontSize: 16, - fontFamily: 'GoogleSansCode', - fontWeight: FontWeight.bold, - color: context.primaryColor, - ), + style: TextStyle(fontSize: 14, fontFamily: 'GoogleSansCode', color: context.primaryColor), ), ), ), @@ -128,14 +123,16 @@ class NetworkingSettings extends HookConsumerWidget { title: "automatic_endpoint_switching_title".tr(), subtitle: "automatic_endpoint_switching_subtitle".tr(), ), - Padding( - padding: const EdgeInsets.only(top: 8, left: 16, bottom: 16), - child: NetworkPreferenceTitle(title: "local_network".tr().toUpperCase(), icon: Icons.home_outlined), + const SizedBox(height: 8), + SettingGroupTitle( + title: "local_network".t(context: context), + icon: Icons.home_outlined, ), LocalNetworkPreference(enabled: featureEnabled.value), - Padding( - padding: const EdgeInsets.only(top: 32, left: 16, bottom: 16), - child: NetworkPreferenceTitle(title: "external_network".tr().toUpperCase(), icon: Icons.dns_outlined), + const SizedBox(height: 16), + SettingGroupTitle( + title: "external_network".t(context: context), + icon: Icons.dns_outlined, ), ExternalNetworkPreference(enabled: featureEnabled.value), ], @@ -143,30 +140,6 @@ class NetworkingSettings extends HookConsumerWidget { } } -class NetworkPreferenceTitle extends StatelessWidget { - const NetworkPreferenceTitle({super.key, required this.icon, required this.title}); - - final IconData icon; - final String title; - - @override - Widget build(BuildContext context) { - return Row( - children: [ - Icon(icon, color: context.colorScheme.onSurface.withAlpha(150)), - const SizedBox(width: 8), - Text( - title, - style: context.textTheme.displaySmall?.copyWith( - color: context.colorScheme.onSurface.withAlpha(200), - fontWeight: FontWeight.w500, - ), - ), - ], - ); - } -} - class NetworkStatusIcon extends StatelessWidget { const NetworkStatusIcon({super.key, required this.status, this.enabled = true}) : super(); @@ -175,10 +148,10 @@ class NetworkStatusIcon extends StatelessWidget { @override Widget build(BuildContext context) { - return AnimatedSwitcher(duration: const Duration(milliseconds: 200), child: _buildIcon(context)); + return AnimatedSwitcher(duration: const Duration(milliseconds: 200), child: buildIcon(context)); } - Widget _buildIcon(BuildContext context) => switch (status) { + Widget buildIcon(BuildContext context) => switch (status) { AuxCheckStatus.loading => Padding( padding: const EdgeInsets.only(left: 4.0), child: SizedBox( diff --git a/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart b/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart index 49f57a5e94..5e745dd61d 100644 --- a/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/haptic_setting.dart @@ -1,9 +1,9 @@ -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; -import 'package:immich_mobile/widgets/settings/settings_sub_title.dart'; +import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; @@ -22,10 +22,13 @@ class HapticSetting extends HookConsumerWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SettingsSubTitle(title: "haptic_feedback_title".tr()), + SettingGroupTitle( + title: "haptic_feedback_title".t(context: context), + icon: Icons.vibration_outlined, + ), SettingsSwitchListTile( valueNotifier: isHapticFeedbackEnabled, - title: 'haptic_feedback_switch'.tr(), + title: 'enabled'.t(context: context), onChanged: onHapticFeedbackChange, ), ], diff --git a/mobile/lib/widgets/settings/preference_settings/theme_setting.dart b/mobile/lib/widgets/settings/preference_settings/theme_setting.dart index 123f7c9921..fc20fb7bed 100644 --- a/mobile/lib/widgets/settings/preference_settings/theme_setting.dart +++ b/mobile/lib/widgets/settings/preference_settings/theme_setting.dart @@ -1,12 +1,12 @@ -import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/translate_extensions.dart'; import 'package:immich_mobile/providers/theme.provider.dart'; import 'package:immich_mobile/services/app_settings.service.dart'; import 'package:immich_mobile/widgets/settings/preference_settings/primary_color_setting.dart'; -import 'package:immich_mobile/widgets/settings/settings_sub_title.dart'; +import 'package:immich_mobile/widgets/settings/setting_group_title.dart'; import 'package:immich_mobile/widgets/settings/settings_switch_list_tile.dart'; import 'package:immich_mobile/utils/hooks/app_settings_update_hook.dart'; @@ -74,23 +74,26 @@ class ThemeSetting extends HookConsumerWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SettingsSubTitle(title: "theme".tr()), + SettingGroupTitle( + title: "theme".t(context: context), + icon: Icons.color_lens_outlined, + ), SettingsSwitchListTile( valueNotifier: isSystemTheme, - title: 'theme_setting_system_theme_switch'.tr(), + title: 'theme_setting_system_theme_switch'.t(context: context), onChanged: onSystemThemeChange, ), if (currentTheme.value != ThemeMode.system) SettingsSwitchListTile( valueNotifier: isDarkTheme, - title: 'map_settings_dark_mode'.tr(), + title: 'map_settings_dark_mode'.t(context: context), onChanged: onThemeChange, ), const PrimaryColorSetting(), SettingsSwitchListTile( valueNotifier: applyThemeToBackgroundProvider, - title: "theme_setting_colorful_interface_title".tr(), - subtitle: 'theme_setting_colorful_interface_subtitle'.tr(), + title: "theme_setting_colorful_interface_title".t(context: context), + subtitle: 'theme_setting_colorful_interface_subtitle'.t(context: context), onChanged: onSurfaceColorSettingChange, ), ], diff --git a/mobile/lib/widgets/settings/setting_group_title.dart b/mobile/lib/widgets/settings/setting_group_title.dart new file mode 100644 index 0000000000..48b1a9bfba --- /dev/null +++ b/mobile/lib/widgets/settings/setting_group_title.dart @@ -0,0 +1,39 @@ +import 'package:flutter/widgets.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; +import 'package:immich_mobile/extensions/theme_extensions.dart'; + +class SettingGroupTitle extends StatelessWidget { + final String title; + final String? subtitle; + final IconData? icon; + final EdgeInsetsGeometry? contentPadding; + + const SettingGroupTitle({super.key, required this.title, this.icon, this.subtitle, this.contentPadding}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: contentPadding ?? const EdgeInsets.only(left: 20.0, right: 20.0, bottom: 8.0), + child: Column( + children: [ + Row( + children: [ + if (icon != null) ...[ + Icon(icon, color: context.colorScheme.onSurfaceSecondary, size: 20), + const SizedBox(width: 8), + ], + Text(title, style: context.textTheme.labelLarge?.copyWith(color: context.colorScheme.onSurfaceSecondary)), + ], + ), + if (subtitle != null) ...[ + const SizedBox(height: 8), + Text( + subtitle!, + style: context.textTheme.bodyMedium!.copyWith(color: context.colorScheme.onSurface.withAlpha(200)), + ), + ], + ], + ), + ); + } +} diff --git a/mobile/lib/widgets/settings/setting_list_tile.dart b/mobile/lib/widgets/settings/setting_list_tile.dart new file mode 100644 index 0000000000..17f44f8a85 --- /dev/null +++ b/mobile/lib/widgets/settings/setting_list_tile.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:immich_mobile/extensions/build_context_extensions.dart'; + +class SettingListTile extends StatelessWidget { + final String title; + final String? subtitle; + final Widget? leading; + final Widget? trailing; + final VoidCallback? onTap; + final EdgeInsetsGeometry? contentPadding; + + const SettingListTile({ + required this.title, + this.subtitle, + this.leading, + this.trailing, + this.onTap, + this.contentPadding, + super.key, + }); + + @override + Widget build(BuildContext context) { + return ListTile( + title: Text(title, style: context.textTheme.bodyLarge!.copyWith(fontWeight: FontWeight.w500, height: 1.5)), + subtitle: subtitle != null + ? Text( + subtitle!, + style: context.textTheme.bodyMedium!.copyWith(color: context.textTheme.bodyMedium!.color!.withAlpha(215)), + ) + : null, + leading: leading, + trailing: trailing, + onTap: onTap, + contentPadding: contentPadding, + ); + } +} diff --git a/mobile/lib/widgets/settings/settings_card.dart b/mobile/lib/widgets/settings/settings_card.dart index 36eff7bae1..b5dcaac1ca 100644 --- a/mobile/lib/widgets/settings/settings_card.dart +++ b/mobile/lib/widgets/settings/settings_card.dart @@ -36,11 +36,8 @@ class SettingsCard extends StatelessWidget { padding: const EdgeInsets.all(16.0), child: Icon(icon, color: context.primaryColor), ), - title: Text( - title, - style: context.textTheme.titleMedium!.copyWith(fontWeight: FontWeight.w600, color: context.primaryColor), - ), - subtitle: Text(subtitle, style: context.textTheme.labelLarge), + title: Text(title, style: context.textTheme.titleMedium!.copyWith(color: context.primaryColor)), + subtitle: Text(subtitle, style: context.textTheme.bodyMedium), onTap: () => context.pushRoute(settingRoute), ), ), diff --git a/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart b/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart index b4cb67239e..78f483f0a9 100644 --- a/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart +++ b/mobile/lib/widgets/settings/settings_sub_page_scaffold.dart @@ -9,13 +9,11 @@ class SettingsSubPageScaffold extends StatelessWidget { @override Widget build(BuildContext context) { return ListView.separated( - padding: const EdgeInsets.symmetric(vertical: 20), + padding: const EdgeInsets.symmetric(vertical: 16), itemCount: settings.length, itemBuilder: (ctx, index) => settings[index], separatorBuilder: (context, index) => showDivider - ? const Column( - children: [SizedBox(height: 5), Divider(height: 10, indent: 15, endIndent: 15), SizedBox(height: 15)], - ) + ? const Column(children: [SizedBox(height: 5), Divider(height: 10), SizedBox(height: 15)]) : const SizedBox(height: 10), ); }