From 1cffc948789ef7faf3500c2b97719741956dc0df Mon Sep 17 00:00:00 2001 From: Livinglist Date: Wed, 2 Sep 2026 02:08:55 -0700 Subject: [PATCH] feat: option to ignore collapsed comments --- lib/cubits/comments/comments_cubit.dart | 12 ++++--- lib/cubits/preference/preference_state.dart | 3 ++ lib/models/preference.dart | 35 ++++++++++++++++--- lib/screens/settings/settings_screen.dart | 32 +++++++++-------- .../link_preview/image_wrapped_text.dart | 2 +- 5 files changed, 61 insertions(+), 23 deletions(-) diff --git a/lib/cubits/comments/comments_cubit.dart b/lib/cubits/comments/comments_cubit.dart index 8e1d6dc7..af9fa90d 100644 --- a/lib/cubits/comments/comments_cubit.dart +++ b/lib/cubits/comments/comments_cubit.dart @@ -979,8 +979,10 @@ comments length is ${state.comments.length} for (int i = startIndex; i < totalComments; i++) { final Comment cmt = state.comments.elementAt(i); - - if (cmt.isRoot && (cmt.deleted || cmt.dead) == false) { + final bool isIgnored = + _preferenceCubit.state.areCollapsedCommentsIgnored && + cmt.isCollapsedByUser; + if (!isIgnored && cmt.isRoot && (cmt.deleted || cmt.dead) == false) { unawaited( scrollTo(index: i + 1, alignment: 0.15, duration: AppDurations.ms400), ); @@ -1018,8 +1020,10 @@ comments length is ${state.comments.length} for (int i = startIndex; i >= 0; i--) { final Comment cmt = state.comments.elementAt(i); - - if (cmt.isRoot && (cmt.deleted || cmt.dead) == false) { + final bool isIgnored = + _preferenceCubit.state.areCollapsedCommentsIgnored && + cmt.isCollapsedByUser; + if (!isIgnored && cmt.isRoot && (cmt.deleted || cmt.dead) == false) { unawaited( scrollTo(index: i + 1, alignment: 0.15, duration: AppDurations.ms400), ); diff --git a/lib/cubits/preference/preference_state.dart b/lib/cubits/preference/preference_state.dart index d333de63..a211a7fb 100644 --- a/lib/cubits/preference/preference_state.dart +++ b/lib/cubits/preference/preference_state.dart @@ -57,6 +57,9 @@ class PreferenceState extends Equatable { bool get areSkipButtonsEnabled => _isOn(); + bool get areCollapsedCommentsIgnored => + _isOn(); + bool get isNotificationEnabled => _isOn(); bool get isRichStoryTileEnabled => _isOn(); diff --git a/lib/models/preference.dart b/lib/models/preference.dart index e47757a3..efeb590c 100644 --- a/lib/models/preference.dart +++ b/lib/models/preference.dart @@ -78,11 +78,13 @@ abstract final class Preference extends Equatable with SettingsDisplayable { const PersistCollapseStateAcrossSessions(), const PreserveCollapseStateAfterScreenExit(), const ReaderModePreference(), - const SkipButtonsPreference(), const SplitViewPreference(), const CollapseModePreference(), const CustomTabPreference(), const WebViewBottomSheetPreference(), + const DividerPlaceholder(label: 'Thread Navigation'), + const SkipButtonsPreference(), + const SkipButtonsIgnoreCollapsedCommentsPreference(), const DividerPlaceholder(label: 'Look And Feel'), const EyeCandyPreference(), const DynamicColorPreference(), @@ -199,7 +201,32 @@ final class SkipButtonsPreference extends BooleanPreference { @override String get subtitle => - '''display floating buttons on the thread screen that let you jump to the previous or next root comment.'''; + '''show floating buttons on the thread screen to navigate to the previous or next root comment.'''; +} + +final class SkipButtonsIgnoreCollapsedCommentsPreference + extends BooleanPreference { + const SkipButtonsIgnoreCollapsedCommentsPreference({bool? val}) + : super( + val: val ?? _skipButtonsIgnoreCollapsedCommentsPreferenceDefaultValue, + ); + + static const bool _skipButtonsIgnoreCollapsedCommentsPreferenceDefaultValue = + true; + + @override + SkipButtonsIgnoreCollapsedCommentsPreference copyWith({required bool? val}) { + return SkipButtonsIgnoreCollapsedCommentsPreference(val: val); + } + + @override + String get key => 'skipButtonsIgnoreCollapsedCommentsPreference'; + + @override + String get title => 'Skip over Collapsed Comments'; + + @override + String get subtitle => '''skip over collapsed comments during navigation.'''; } final class SplitViewPreference extends BooleanPreference { @@ -615,7 +642,7 @@ final class MarkReadStoriesModePreference extends BooleanPreference { String get key => 'markReadStoriesMode'; @override - String get title => 'Mark Read Stories'; + String get title => 'Gray Out Read Stories'; @override String get subtitle => 'grey out stories you have read.'; @@ -638,7 +665,7 @@ final class HideStoryInsteadOfMarkingGray extends BooleanPreference { String get key => 'hideStoryInsteadOfMarkingGray'; @override - String get title => 'Hide Story Instead Of Marking Gray'; + String get title => 'Hide Read Stories'; @override String get subtitle => diff --git a/lib/screens/settings/settings_screen.dart b/lib/screens/settings/settings_screen.dart index b6d9f779..0d438d16 100644 --- a/lib/screens/settings/settings_screen.dart +++ b/lib/screens/settings/settings_screen.dart @@ -244,20 +244,24 @@ class _SettingsViewState extends State const TabBarSettings(), const TextScaleFactorSettings(), const Divider(), - StoryTile( - shouldShowWebPreview: preferenceState.isRichStoryTileEnabled, - shouldShowMetadata: preferenceState.isMetadataEnabled, - shouldShowUrl: preferenceState.isUrlEnabled, - shouldShowFavicon: preferenceState.isFaviconEnabled, - shouldShowPreviewImage: - preferenceState.isStoryTilePreviewImageEnabled, - isExpandedTileEnabled: preferenceState.isExpandedTileEnabled, - isIndexedStoryTileEnabled: - preferenceState.isIndexedStoryTileEnabled, - isImageLeftAligned: preferenceState.isPreviewImageLeftAligned, - index: 0, - story: Story.placeholder(), - onTap: () => LinkUtils.launch(Constants.guidelineLink, context), + AnimatedSize( + duration: AppDurations.ms300, + child: StoryTile( + shouldShowWebPreview: preferenceState.isRichStoryTileEnabled, + shouldShowMetadata: preferenceState.isMetadataEnabled, + shouldShowUrl: preferenceState.isUrlEnabled, + shouldShowFavicon: preferenceState.isFaviconEnabled, + shouldShowPreviewImage: + preferenceState.isStoryTilePreviewImageEnabled, + isExpandedTileEnabled: preferenceState.isExpandedTileEnabled, + isIndexedStoryTileEnabled: + preferenceState.isIndexedStoryTileEnabled, + isImageLeftAligned: preferenceState.isPreviewImageLeftAligned, + index: 0, + story: Story.placeholder(), + onTap: () => + LinkUtils.launch(Constants.guidelineLink, context), + ), ), const Divider(), for (final Preference preference diff --git a/lib/screens/widgets/link_preview/image_wrapped_text.dart b/lib/screens/widgets/link_preview/image_wrapped_text.dart index 2c9951a0..9ba392c1 100644 --- a/lib/screens/widgets/link_preview/image_wrapped_text.dart +++ b/lib/screens/widgets/link_preview/image_wrapped_text.dart @@ -90,7 +90,7 @@ class ImageWrapText extends StatelessWidget { child: Container( color: Theme.of(context).colorScheme.surfaceContainerLow, width: imageWidth, - height: imageHeight - 8, + height: imageHeight - 10, child: image, ), ),