From 01a27f87bd07c06002f87a658db5ff4864dc0d82 Mon Sep 17 00:00:00 2001 From: Hamlet Jiang Su <30667958+hjiangsu@users.noreply.github.com> Date: Sat, 23 Mar 2024 09:32:18 -0700 Subject: [PATCH] added customizable feed card divider --- lib/community/widgets/post_card.dart | 11 +- .../enums/feed_card_divider_thickness.dart | 16 +++ lib/core/enums/local_settings.dart | 6 + lib/feed/view/feed_comment_list.dart | 44 ++++--- lib/feed/widgets/feed_card_divider.dart | 24 ++++ lib/feed/widgets/widgets.dart | 1 + lib/l10n/app_en.arb | 28 +++++ lib/modlog/widgets/modlog_filter_picker.dart | 10 +- .../pages/post_appearance_settings_page.dart | 117 +++++++++++++++++- lib/settings/widgets/list_option.dart | 16 +-- lib/shared/comment_sort_picker.dart | 2 +- lib/shared/sort_picker.dart | 2 +- lib/thunder/bloc/thunder_bloc.dart | 6 + lib/thunder/bloc/thunder_state.dart | 10 ++ lib/utils/bottom_sheet_list_picker.dart | 41 ++++-- 15 files changed, 281 insertions(+), 53 deletions(-) create mode 100644 lib/core/enums/feed_card_divider_thickness.dart create mode 100644 lib/feed/widgets/feed_card_divider.dart diff --git a/lib/community/widgets/post_card.dart b/lib/community/widgets/post_card.dart index 43e64719d..700e44764 100644 --- a/lib/community/widgets/post_card.dart +++ b/lib/community/widgets/post_card.dart @@ -12,6 +12,7 @@ import 'package:thunder/core/auth/bloc/auth_bloc.dart'; import 'package:thunder/core/enums/swipe_action.dart'; import 'package:thunder/core/models/post_view_media.dart'; import 'package:thunder/feed/bloc/feed_bloc.dart'; +import 'package:thunder/feed/widgets/widgets.dart'; import 'package:thunder/post/enums/post_action.dart'; import 'package:thunder/thunder/bloc/thunder_bloc.dart'; import 'package:thunder/post/utils/navigate_post.dart'; @@ -132,15 +133,6 @@ class _PostCardState extends State { }, child: Column( children: [ - Divider( - height: 1.0, - thickness: 4.0, - color: ElevationOverlay.applySurfaceTint( - Theme.of(context).colorScheme.surface, - Theme.of(context).colorScheme.surfaceTint, - 10, - ), - ), Dismissible( direction: isOverridingSwipeGestureAction == true ? DismissDirection.none : determinePostSwipeDirection(isUserLoggedIn, state), key: ObjectKey(widget.postViewMedia.postView.post.id), @@ -244,6 +236,7 @@ class _PostCardState extends State { }, ), ), + const FeedCardDivider(), ], ), ); diff --git a/lib/core/enums/feed_card_divider_thickness.dart b/lib/core/enums/feed_card_divider_thickness.dart new file mode 100644 index 000000000..531552d25 --- /dev/null +++ b/lib/core/enums/feed_card_divider_thickness.dart @@ -0,0 +1,16 @@ +enum FeedCardDividerThickness { + compact, + standard, + comfortable; + + double get value { + switch (this) { + case FeedCardDividerThickness.compact: + return 2.0; + case FeedCardDividerThickness.standard: + return 6.0; + case FeedCardDividerThickness.comfortable: + return 10.0; + } + } +} diff --git a/lib/core/enums/local_settings.dart b/lib/core/enums/local_settings.dart index 134ce734d..f578829bc 100644 --- a/lib/core/enums/local_settings.dart +++ b/lib/core/enums/local_settings.dart @@ -126,6 +126,11 @@ enum LocalSettings { cardPostCardMetadataItems(name: 'setting_card_post_card_metadata_items', key: 'cardPostCardMetadataItems', category: LocalSettingsCategories.posts, subCategory: LocalSettingsSubCategories.posts), showFullPostDate(name: 'setting_general_show_full_post_date', key: 'showFullPostDate', category: LocalSettingsCategories.posts, subCategory: LocalSettingsSubCategories.posts), dateFormat(name: 'setting_general_date_format', key: 'dateFormat', category: LocalSettingsCategories.posts, subCategory: LocalSettingsSubCategories.posts), + // This setting exists purely for the searching function + dividerAppearance(name: '', key: 'dividerAppearance', category: LocalSettingsCategories.posts, subCategory: LocalSettingsSubCategories.posts), + feedCardDividerThickness( + name: 'setting_feed_card_divider_thickness', key: 'feedCardDividerThickness', category: LocalSettingsCategories.posts, subCategory: LocalSettingsSubCategories.posts, searchable: false), + feedCardDividerColor(name: 'setting_feed_card_divider_color', key: 'feedCardDividerColor', category: LocalSettingsCategories.posts, subCategory: LocalSettingsSubCategories.posts, searchable: false), // Advanced Settings userFormat(name: 'user_format', key: 'userFormat', category: LocalSettingsCategories.general, subCategory: LocalSettingsSubCategories.advanced), @@ -314,6 +319,7 @@ extension LocalizationExt on AppLocalizations { 'dimReadPosts': dimReadPosts, 'showFullPostDate': showFullDate, 'dateFormat': dateFormat, + 'dividerAppearance': dividerAppearance, 'showCrossPosts': showCrossPosts, 'keywordFilters': keywordFilters, 'hideTopBarOnScroll': hideTopBarOnScroll, diff --git a/lib/feed/view/feed_comment_list.dart b/lib/feed/view/feed_comment_list.dart index 1be384637..4a9366e0c 100644 --- a/lib/feed/view/feed_comment_list.dart +++ b/lib/feed/view/feed_comment_list.dart @@ -4,6 +4,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lemmy_api_client/v3.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:thunder/core/auth/bloc/auth_bloc.dart'; +import 'package:thunder/feed/feed.dart'; import 'package:thunder/shared/comment_reference.dart'; @@ -27,25 +28,30 @@ class FeedCommentList extends StatelessWidget { crossAxisSpacing: 40, mainAxisSpacing: 0, itemBuilder: (BuildContext context, int index) { - return CommentReference( - comment: commentViews[index], - now: DateTime.now(), - onVoteAction: (int commentId, int voteType) => { - // TODO: Implement action - }, - onSaveAction: (int commentId, bool save) => { - // TODO: Implement action - }, - onDeleteAction: (int commentId, bool deleted) => { - // TODO: Implement action - }, - onReportAction: (int commentId) { - // TODO: Implement action - }, - onReplyEditAction: (CommentView commentView, bool isEdit) { - // TODO: Implement action - }, - isOwnComment: commentViews[index].comment.creatorId == state.account?.userId, + return Column( + children: [ + CommentReference( + comment: commentViews[index], + now: DateTime.now(), + onVoteAction: (int commentId, int voteType) => { + // TODO: Implement action + }, + onSaveAction: (int commentId, bool save) => { + // TODO: Implement action + }, + onDeleteAction: (int commentId, bool deleted) => { + // TODO: Implement action + }, + onReportAction: (int commentId) { + // TODO: Implement action + }, + onReplyEditAction: (CommentView commentView, bool isEdit) { + // TODO: Implement action + }, + isOwnComment: commentViews[index].comment.creatorId == state.account?.userId, + ), + const FeedCardDivider(), + ], ); }, childCount: commentViews.length, diff --git a/lib/feed/widgets/feed_card_divider.dart b/lib/feed/widgets/feed_card_divider.dart new file mode 100644 index 000000000..dc2b58e66 --- /dev/null +++ b/lib/feed/widgets/feed_card_divider.dart @@ -0,0 +1,24 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:thunder/thunder/bloc/thunder_bloc.dart'; + +class FeedCardDivider extends StatelessWidget { + const FeedCardDivider({super.key}); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final state = context.watch().state; + + final feedCardDividerThickness = state.feedCardDividerThickness; + final feedCardDividerColor = state.feedCardDividerColor; + + return Divider( + height: feedCardDividerThickness.value, + thickness: feedCardDividerThickness.value, + color: feedCardDividerColor == Colors.transparent + ? ElevationOverlay.applySurfaceTint(theme.colorScheme.surface, theme.colorScheme.surfaceTint, 10) + : Color.alphaBlend(theme.colorScheme.primaryContainer.withOpacity(0.6), feedCardDividerColor).withOpacity(0.2), + ); + } +} diff --git a/lib/feed/widgets/widgets.dart b/lib/feed/widgets/widgets.dart index cd99b631b..15f271284 100644 --- a/lib/feed/widgets/widgets.dart +++ b/lib/feed/widgets/widgets.dart @@ -1 +1,2 @@ export 'feed_page_app_bar.dart'; +export 'feed_card_divider.dart'; diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index b516a1ce2..5c6b48df2 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -211,6 +211,10 @@ "@collapseSpoiler": { "description": "Label for collapsing spoiler" }, + "color": "Color", + "@color": { + "description": "Describes a color" + }, "colorizeCommunityName": "Colorize Community Name", "@colorizeCommunityName": { "description": "Setting for colorizing community name" @@ -235,6 +239,10 @@ "@combineNavAndFab": {}, "combineNavAndFabDescription": "Floating Action Button will be shown between navigation buttons.", "@combineNavAndFabDescription": {}, + "comfortable": "Comfortable", + "@comfortable": { + "description": "Describes a comfortable visual density" + }, "comment": "Comment", "@comment": {}, "commentBehaviourSettings": "Comments", @@ -287,6 +295,10 @@ "@communityStyle": { "description": "Heading for community style setting" }, + "compact": "Compact", + "@compact": { + "description": "Describes a compact visual density" + }, "compactPostCardMetadataItems": "Compact View Metadata", "@compactPostCardMetadataItems": { "description": "Name of setting for compact post card metadata items" @@ -455,6 +467,10 @@ "@displayUserScore": { "description": "Option for displaying user scores or karma." }, + "dividerAppearance": "Divider Appearance", + "@dividerAppearance": { + "description": "Divider appearance category" + }, "doNotShowAgain": "Do Not Show Again", "@doNotShowAgain": { "description": "Action for hiding something permanently" @@ -569,6 +585,10 @@ "@feedBehaviourSettings": { "description": "Subcategory in Setting -> General" }, + "feedSettings": "Feed Settings", + "@feedSettings": { + "description": "Feed settings category." + }, "feedTypeAndSorts": "Default Feed Type and Sorting", "@feedTypeAndSorts": { "description": "Subcategory in Setting -> General" @@ -1521,6 +1541,10 @@ "@spoiler": { "description": "Placeholder label for spoiler" }, + "standard": "Standard", + "@standard": { + "description": "Describes a standard visual density" + }, "submit": "Submit", "@submit": {}, "subscribe": "Subscribe", @@ -1581,6 +1605,10 @@ "@theming": { "description": "Title of Theming in Settings -> Appearance -> Theming" }, + "thickness": "Thickness", + "@thickness": { + "description": "Describes a thickness (e.g., divider)" + }, "thunderHasBeenUpdated": "Thunder updated to {version}!", "@thunderHasBeenUpdated": { "description": "Heading for changelog when Thunder has been updated" diff --git a/lib/modlog/widgets/modlog_filter_picker.dart b/lib/modlog/widgets/modlog_filter_picker.dart index 2264969d2..2e34c6436 100644 --- a/lib/modlog/widgets/modlog_filter_picker.dart +++ b/lib/modlog/widgets/modlog_filter_picker.dart @@ -110,28 +110,28 @@ class _ModlogActionTypePickerState extends State { title: AppLocalizations.of(GlobalContext.context)!.posts, items: postModlogActionTypeItems, onNavigateBack: () => setState(() => category = ModlogActionTypeFilterCategory.all), - onSelect: (item) => widget.onSelect(item), + onSelect: (item) => widget.onSelect?.call(item), previouslySelectedItem: widget.previouslySelected, ), ModlogActionTypeFilterCategory.comment => ModlogSubFilterPicker( title: AppLocalizations.of(GlobalContext.context)!.comments, items: commentModlogActionTypeItems, onNavigateBack: () => setState(() => category = ModlogActionTypeFilterCategory.all), - onSelect: (item) => widget.onSelect(item), + onSelect: (item) => widget.onSelect?.call(item), previouslySelectedItem: widget.previouslySelected, ), ModlogActionTypeFilterCategory.community => ModlogSubFilterPicker( title: AppLocalizations.of(GlobalContext.context)!.community, items: communityModlogActionTypeItems, onNavigateBack: () => setState(() => category = ModlogActionTypeFilterCategory.all), - onSelect: (item) => widget.onSelect(item), + onSelect: (item) => widget.onSelect?.call(item), previouslySelectedItem: widget.previouslySelected, ), ModlogActionTypeFilterCategory.instance => ModlogSubFilterPicker( title: AppLocalizations.of(GlobalContext.context)!.instance(1), items: instanceModlogActionTypeItems, onNavigateBack: () => setState(() => category = ModlogActionTypeFilterCategory.all), - onSelect: (item) => widget.onSelect(item), + onSelect: (item) => widget.onSelect?.call(item), previouslySelectedItem: widget.previouslySelected, ), }, @@ -168,7 +168,7 @@ class _ModlogActionTypePickerState extends State { onSelected: () { HapticFeedback.mediumImpact(); Navigator.of(context).pop(); - widget.onSelect(item); + widget.onSelect?.call(item); }, isSelected: widget.previouslySelected == item.payload, ), diff --git a/lib/settings/pages/post_appearance_settings_page.dart b/lib/settings/pages/post_appearance_settings_page.dart index 2a9ed80b8..d9b936495 100644 --- a/lib/settings/pages/post_appearance_settings_page.dart +++ b/lib/settings/pages/post_appearance_settings_page.dart @@ -13,10 +13,13 @@ import 'package:smooth_highlight/smooth_highlight.dart'; import 'package:thunder/community/widgets/post_card_view_comfortable.dart'; import 'package:thunder/community/widgets/post_card_view_compact.dart'; +import 'package:thunder/core/enums/custom_theme_type.dart'; +import 'package:thunder/core/enums/feed_card_divider_thickness.dart'; import 'package:thunder/core/enums/local_settings.dart'; import 'package:thunder/core/enums/post_body_view_type.dart'; import 'package:thunder/core/models/post_view_media.dart'; import 'package:thunder/core/singletons/preferences.dart'; +import 'package:thunder/feed/feed.dart'; import 'package:thunder/feed/utils/post.dart'; import 'package:thunder/post/enums/post_card_metadata_item.dart'; import 'package:thunder/settings/widgets/list_option.dart'; @@ -86,6 +89,12 @@ class _PostAppearanceSettingsPageState extends State /// The selected date format. This is only used when `showFullPostDate` is enabled DateFormat? selectedDateFormat; + /// The thickness of the divider between the post cards + FeedCardDividerThickness feedCardDividerThickness = FeedCardDividerThickness.compact; + + /// The color of the divider between the post cards + Color feedCardDividerColor = Colors.transparent; + /// List of available date formats to select from List dateFormats = [DateFormat.yMMMMd(Intl.systemLocale).add_jm()]; @@ -122,6 +131,8 @@ class _PostAppearanceSettingsPageState extends State dimReadPosts = prefs.getBool(LocalSettings.dimReadPosts.name) ?? true; showFullPostDate = prefs.getBool(LocalSettings.showFullPostDate.name) ?? false; selectedDateFormat = prefs.getString(LocalSettings.dateFormat.name) != null ? DateFormat(prefs.getString(LocalSettings.dateFormat.name)) : dateFormats.first; + feedCardDividerThickness = FeedCardDividerThickness.values.byName(prefs.getString(LocalSettings.feedCardDividerThickness.name) ?? FeedCardDividerThickness.compact.name); + feedCardDividerColor = Color(prefs.getInt(LocalSettings.feedCardDividerColor.name) ?? Colors.transparent.value); // Compact View Settings compactPostCardMetadataItems = @@ -178,6 +189,14 @@ class _PostAppearanceSettingsPageState extends State await prefs.setString(LocalSettings.dateFormat.name, (value as DateFormat).pattern ?? dateFormats.first.pattern!); setState(() => selectedDateFormat = value); break; + case LocalSettings.feedCardDividerThickness: + await prefs.setString(LocalSettings.feedCardDividerThickness.name, (value as FeedCardDividerThickness).name); + setState(() => feedCardDividerThickness = value); + break; + case LocalSettings.feedCardDividerColor: + await prefs.setInt(LocalSettings.feedCardDividerColor.name, value.value); + setState(() => feedCardDividerColor = value); + break; case LocalSettings.compactPostCardMetadataItems: await prefs.setStringList(LocalSettings.compactPostCardMetadataItems.name, value); @@ -248,6 +267,8 @@ class _PostAppearanceSettingsPageState extends State await prefs.remove(LocalSettings.dimReadPosts.name); await prefs.remove(LocalSettings.showFullPostDate.name); await prefs.remove(LocalSettings.dateFormat.name); + await prefs.remove(LocalSettings.feedCardDividerThickness.name); + await prefs.remove(LocalSettings.feedCardDividerColor.name); await prefs.remove(LocalSettings.compactPostCardMetadataItems.name); await prefs.remove(LocalSettings.showThumbnailPreviewOnRight.name); await prefs.remove(LocalSettings.showTextPostIndicator.name); @@ -476,7 +497,7 @@ class _PostAppearanceSettingsPageState extends State onSaveAction: (saved) {}, ), ), - const Divider(), + const FeedCardDivider(), ], ); }, @@ -493,7 +514,7 @@ class _PostAppearanceSettingsPageState extends State SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), - child: Text(l10n.generalSettings, style: theme.textTheme.titleMedium), + child: Text(l10n.feedSettings, style: theme.textTheme.titleMedium), ), ), SliverToBoxAdapter( @@ -586,6 +607,98 @@ class _PostAppearanceSettingsPageState extends State highlightKey: settingToHighlight == LocalSettings.dateFormat ? settingToHighlightKey : null, ), ), + SliverToBoxAdapter( + child: ListOption( + description: l10n.dividerAppearance, + value: const ListPickerItem(payload: -1), + icon: Icons.splitscreen_rounded, + highlightKey: settingToHighlight == LocalSettings.dividerAppearance ? settingToHighlightKey : null, + customListPicker: StatefulBuilder( + builder: (context, setState) { + return BottomSheetListPicker( + title: l10n.dividerAppearance, + heading: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(l10n.preview, style: theme.textTheme.titleMedium), + const SizedBox(height: 20.0), + const FeedCardDivider(), + const SizedBox(height: 16.0), + ], + ), + items: [ + ListPickerItem( + customWidget: ListTile( + title: Text(l10n.thickness), + contentPadding: const EdgeInsets.only(left: 24.0, right: 20.0), + trailing: DropdownButton( + value: feedCardDividerThickness, + underline: const SizedBox(), + items: [ + DropdownMenuItem( + value: FeedCardDividerThickness.compact, + child: Text(l10n.compact), + ), + DropdownMenuItem( + value: FeedCardDividerThickness.standard, + child: Text(l10n.standard), + ), + DropdownMenuItem( + value: FeedCardDividerThickness.comfortable, + child: Text(l10n.comfortable), + ) + ], + onChanged: (FeedCardDividerThickness? value) { + setPreferences(LocalSettings.feedCardDividerThickness, value); + setState(() {}); // Trigger rebuild + }, + ), + ), + payload: -1, + ), + ListPickerItem( + customWidget: ListTile( + title: Text(l10n.color), + contentPadding: const EdgeInsets.only(left: 24.0, right: 20.0), + trailing: DropdownButton( + menuMaxHeight: 500.0, + value: feedCardDividerColor, + underline: const SizedBox(), + items: CustomThemeType.values + .map((CustomThemeType customThemeType) => DropdownMenuItem( + alignment: Alignment.center, + value: Color(customThemeType.primaryColor.value), + child: CircleAvatar( + radius: 16.0, + backgroundColor: Color.alphaBlend( + theme.colorScheme.primaryContainer.withOpacity(0.6), + Color(customThemeType.primaryColor.value), + ), + ), + )) + .toList() + ..insert( + 0, + const DropdownMenuItem( + alignment: Alignment.center, + value: Colors.transparent, + child: CircleAvatar(radius: 16.0, child: Text('D')), + ), + ), + onChanged: (Color? value) { + setPreferences(LocalSettings.feedCardDividerColor, value); + setState(() {}); // Trigger rebuild + }, + ), + ), + payload: -1, + ), + ], + ); + }, + ), + ), + ), const SliverToBoxAdapter(child: SizedBox(height: 32.0)), SliverToBoxAdapter( child: Padding( diff --git a/lib/settings/widgets/list_option.dart b/lib/settings/widgets/list_option.dart index d26b3c1df..49d91db1e 100644 --- a/lib/settings/widgets/list_option.dart +++ b/lib/settings/widgets/list_option.dart @@ -7,7 +7,7 @@ import 'package:thunder/utils/bottom_sheet_list_picker.dart'; class ListOption extends StatelessWidget { // Appearance - final IconData icon; + final IconData? icon; // General final String description; @@ -16,9 +16,9 @@ class ListOption extends StatelessWidget { final List> options; // Callback - final Future Function(ListPickerItem) onChanged; + final Future Function(ListPickerItem)? onChanged; - final BottomSheetListPicker? customListPicker; + final Widget? customListPicker; final bool? isBottomModalScrollControlled; final bool disabled; @@ -31,12 +31,12 @@ class ListOption extends StatelessWidget { const ListOption({ super.key, - required this.description, + this.description = '', this.bottomSheetHeading, required this.value, - required this.options, - required this.icon, - required this.onChanged, + this.options = const [], + this.icon, + this.onChanged, this.customListPicker, this.isBottomModalScrollControlled, this.disabled = false, @@ -73,7 +73,7 @@ class ListOption extends StatelessWidget { heading: bottomSheetHeading, onUpdateHeading: onUpdateHeading, items: options, - onSelect: onChanged, + onSelect: onChanged ?? (value) async {}, previouslySelected: value.payload, closeOnSelect: closeOnSelect, ), diff --git a/lib/shared/comment_sort_picker.dart b/lib/shared/comment_sort_picker.dart index 025a014e5..0da9d06d5 100644 --- a/lib/shared/comment_sort_picker.dart +++ b/lib/shared/comment_sort_picker.dart @@ -112,7 +112,7 @@ class _SortPickerState extends State { icon: item.icon, onSelected: () { Navigator.of(context).pop(); - widget.onSelect(item); + widget.onSelect?.call(item); }, isSelected: widget.previouslySelected == item.payload, ), diff --git a/lib/shared/sort_picker.dart b/lib/shared/sort_picker.dart index 1a861962d..b00075d5c 100644 --- a/lib/shared/sort_picker.dart +++ b/lib/shared/sort_picker.dart @@ -253,7 +253,7 @@ class _SortPickerState extends State { icon: item.icon, onSelected: () { Navigator.of(context).pop(); - widget.onSelect(item); + widget.onSelect?.call(item); }, isSelected: widget.previouslySelected == item.payload)) .toList(); diff --git a/lib/thunder/bloc/thunder_bloc.dart b/lib/thunder/bloc/thunder_bloc.dart index 81673b459..b23385681 100644 --- a/lib/thunder/bloc/thunder_bloc.dart +++ b/lib/thunder/bloc/thunder_bloc.dart @@ -1,6 +1,7 @@ import 'package:bloc/bloc.dart'; import 'package:equatable/equatable.dart'; import 'package:bloc_concurrency/bloc_concurrency.dart'; +import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:lemmy_api_client/v3.dart'; @@ -11,6 +12,7 @@ import 'package:thunder/core/enums/browser_mode.dart'; import 'package:thunder/core/enums/custom_theme_type.dart'; import 'package:thunder/core/enums/fab_action.dart'; +import 'package:thunder/core/enums/feed_card_divider_thickness.dart'; import 'package:thunder/core/enums/font_scale.dart'; import 'package:thunder/core/enums/full_name.dart'; import 'package:thunder/core/enums/image_caching_mode.dart'; @@ -154,6 +156,8 @@ class ThunderBloc extends Bloc { bool dimReadPosts = prefs.getBool(LocalSettings.dimReadPosts.name) ?? true; bool showFullPostDate = prefs.getBool(LocalSettings.showFullPostDate.name) ?? false; DateFormat dateFormat = DateFormat(prefs.getString(LocalSettings.dateFormat.name) ?? DateFormat.yMMMMd(Intl.systemLocale).add_jm().pattern); + FeedCardDividerThickness feedCardDividerThickness = FeedCardDividerThickness.values.byName(prefs.getString(LocalSettings.feedCardDividerThickness.name) ?? FeedCardDividerThickness.compact.name); + Color feedCardDividerColor = Color(prefs.getInt(LocalSettings.feedCardDividerColor.name) ?? Colors.transparent.value); bool showCrossPosts = prefs.getBool(LocalSettings.showCrossPosts.name) ?? true; List compactPostCardMetadataItems = prefs.getStringList(LocalSettings.compactPostCardMetadataItems.name)?.map((e) => PostCardMetadataItem.values.byName(e)).toList() ?? DEFAULT_COMPACT_POST_CARD_METADATA; @@ -300,6 +304,8 @@ class ThunderBloc extends Bloc { dimReadPosts: dimReadPosts, showFullPostDate: showFullPostDate, dateFormat: dateFormat, + feedCardDividerThickness: feedCardDividerThickness, + feedCardDividerColor: feedCardDividerColor, showCrossPosts: showCrossPosts, compactPostCardMetadataItems: compactPostCardMetadataItems, cardPostCardMetadataItems: cardPostCardMetadataItems, diff --git a/lib/thunder/bloc/thunder_state.dart b/lib/thunder/bloc/thunder_state.dart index f8e18906e..efc7fa809 100644 --- a/lib/thunder/bloc/thunder_state.dart +++ b/lib/thunder/bloc/thunder_state.dart @@ -67,6 +67,8 @@ class ThunderState extends Equatable { this.dimReadPosts = true, this.showFullPostDate = false, this.dateFormat, + this.feedCardDividerThickness = FeedCardDividerThickness.compact, + this.feedCardDividerColor = Colors.transparent, this.showCrossPosts = true, this.compactPostCardMetadataItems = const [], this.cardPostCardMetadataItems = const [], @@ -210,6 +212,8 @@ class ThunderState extends Equatable { final bool dimReadPosts; final bool showFullPostDate; final DateFormat? dateFormat; + final FeedCardDividerThickness feedCardDividerThickness; + final Color feedCardDividerColor; final bool showCrossPosts; final List compactPostCardMetadataItems; final List cardPostCardMetadataItems; @@ -359,6 +363,8 @@ class ThunderState extends Equatable { bool? dimReadPosts, bool? showFullPostDate, DateFormat? dateFormat, + FeedCardDividerThickness? feedCardDividerThickness, + Color? feedCardDividerColor, bool? showCrossPosts, List? compactPostCardMetadataItems, List? cardPostCardMetadataItems, @@ -502,6 +508,8 @@ class ThunderState extends Equatable { dimReadPosts: dimReadPosts ?? this.dimReadPosts, showFullPostDate: showFullPostDate ?? this.showFullPostDate, dateFormat: dateFormat ?? this.dateFormat, + feedCardDividerThickness: feedCardDividerThickness ?? this.feedCardDividerThickness, + feedCardDividerColor: feedCardDividerColor ?? this.feedCardDividerColor, showCrossPosts: showCrossPosts ?? this.showCrossPosts, compactPostCardMetadataItems: compactPostCardMetadataItems ?? this.compactPostCardMetadataItems, cardPostCardMetadataItems: cardPostCardMetadataItems ?? this.cardPostCardMetadataItems, @@ -649,6 +657,8 @@ class ThunderState extends Equatable { dimReadPosts, showFullPostDate, dateFormat, + feedCardDividerThickness, + feedCardDividerColor, showCrossPosts, compactPostCardMetadataItems, cardPostCardMetadataItems, diff --git a/lib/utils/bottom_sheet_list_picker.dart b/lib/utils/bottom_sheet_list_picker.dart index d3ddb2c8b..5698a33b0 100644 --- a/lib/utils/bottom_sheet_list_picker.dart +++ b/lib/utils/bottom_sheet_list_picker.dart @@ -6,7 +6,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart'; class BottomSheetListPicker extends StatefulWidget { final String title; final List> items; - final Future Function(ListPickerItem) onSelect; + final Future Function(ListPickerItem)? onSelect; final T? previouslySelected; final bool closeOnSelect; final Widget? heading; @@ -16,7 +16,7 @@ class BottomSheetListPicker extends StatefulWidget { super.key, required this.title, required this.items, - required this.onSelect, + this.onSelect, this.previouslySelected, this.closeOnSelect = true, this.heading, @@ -73,6 +73,10 @@ class _BottomSheetListPickerState extends State> { physics: const NeverScrollableScrollPhysics(), children: widget.items.map( (item) { + if (item.customWidget != null) { + return item.customWidget!; + } + return PickerItem( label: item.capitalizeLabel ? item.label.capitalize : item.label, labelWidget: item.labelWidget, @@ -93,7 +97,7 @@ class _BottomSheetListPickerState extends State> { } }); } - await widget.onSelect(item); + await widget.onSelect?.call(item); setState(() => heading = widget.onUpdateHeading?.call()); }, isSelected: currentlySelected != null ? currentlySelected == item.payload : widget.previouslySelected == item.payload, @@ -176,25 +180,46 @@ class _BottomSheetListPickerState extends State> { } class ListPickerItem { + /// Icon shown on the left final IconData? icon; + + /// When passed in, the left icon will show a color palette final List? colors; + + /// The label of the item final String label; + + /// The theme of the label + final TextTheme? textTheme; + + /// The subtitle of the item final String? subtitle; + + /// Whether to capitalize the label + final bool capitalizeLabel; + + /// A custom widget to show instead of the label final Widget? labelWidget; + + /// A custom widget to use instead of the default + final Widget? customWidget; + + /// The payload of the item final T payload; - final bool capitalizeLabel; - final TextTheme? textTheme; + + /// Whether the item is selected final bool? isChecked; const ListPickerItem({ this.icon, this.colors, - required this.label, + this.label = "", + this.textTheme, this.subtitle, + this.capitalizeLabel = true, this.labelWidget, + this.customWidget, required this.payload, - this.capitalizeLabel = true, - this.textTheme, this.isChecked, }); }