From fa9231704876f7ef37602b6a745d7b793b200a45 Mon Sep 17 00:00:00 2001 From: uwx Date: Mon, 8 Dec 2025 02:27:32 +0100 Subject: [PATCH] feat: hide unreplyable posts --- src/screens/Settings/DeerSettings.tsx | 25 +++++++++ src/state/persisted/schema.ts | 2 + .../preferences/hide-unreplyable-posts.tsx | 51 +++++++++++++++++++ src/state/preferences/index.tsx | 17 ++++--- src/view/com/posts/PostFeed.tsx | 24 +++++++++ 5 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 src/state/preferences/hide-unreplyable-posts.tsx diff --git a/src/screens/Settings/DeerSettings.tsx b/src/screens/Settings/DeerSettings.tsx index 7e8ac1f16..cb92cc512 100644 --- a/src/screens/Settings/DeerSettings.tsx +++ b/src/screens/Settings/DeerSettings.tsx @@ -88,6 +88,10 @@ import { useHideSimilarAccountsRecomm, useSetHideSimilarAccountsRecomm, } from '#/state/preferences/hide-similar-accounts-recommendations' +import { + useHideUnreplyablePosts, + useSetHideUnreplyablePosts, +} from '#/state/preferences/hide-unreplyable-posts' import { useHighQualityImages, useSetHighQualityImages, @@ -443,6 +447,9 @@ export function DeerSettingsScreen({}: Props) { const hideSimilarAccountsRecomm = useHideSimilarAccountsRecomm() const setHideSimilarAccountsRecomm = useSetHideSimilarAccountsRecomm() + const hideUnreplyablePosts = useHideUnreplyablePosts() + const setHideUnreplyablePosts = useSetHideUnreplyablePosts() + const disableVerifyEmailReminder = useDisableVerifyEmailReminder() const setDisableVerifyEmailReminder = useSetDisableVerifyEmailReminder() @@ -742,6 +749,24 @@ export function DeerSettingsScreen({}: Props) { + setHideUnreplyablePosts(value)} + style={[a.w_full]}> + + Hide posts that cannot be replied to from feeds + + + + + + Hides posts from feeds where replies are disabled (e.g. due to + postgates or other restrictions). Does not affect thread views. + + + void + +const stateContext = React.createContext( + persisted.defaults.hideUnreplyablePosts, +) +const setContext = React.createContext( + (_: persisted.Schema['hideUnreplyablePosts']) => {}, +) + +export function Provider({children}: React.PropsWithChildren<{}>) { + const [state, setState] = React.useState( + persisted.get('hideUnreplyablePosts'), + ) + + const setStateWrapped = React.useCallback( + (hideUnreplyablePosts: persisted.Schema['hideUnreplyablePosts']) => { + setState(hideUnreplyablePosts) + persisted.write('hideUnreplyablePosts', hideUnreplyablePosts) + }, + [setState], + ) + + React.useEffect(() => { + return persisted.onUpdate('hideUnreplyablePosts', nextValue => { + setState(nextValue) + }) + }, [setStateWrapped]) + + return ( + + + {children} + + + ) +} + +export function useHideUnreplyablePosts() { + return ( + React.useContext(stateContext) ?? persisted.defaults.hideUnreplyablePosts + ) +} + +export function useSetHideUnreplyablePosts() { + return React.useContext(setContext) +} diff --git a/src/state/preferences/index.tsx b/src/state/preferences/index.tsx index e7a852ede..b7df1b568 100644 --- a/src/state/preferences/index.tsx +++ b/src/state/preferences/index.tsx @@ -26,6 +26,7 @@ import {Provider as GoLinksProvider} from './go-links-enabled' import {Provider as HiddenPostsProvider} from './hidden-posts' import {Provider as HideFeedsPromoTabProvider} from './hide-feeds-promo-tab' import {Provider as HideSimilarAccountsRecommProvider} from './hide-similar-accounts-recommendations' +import {Provider as HideUnreplyablePostsProvider} from './hide-unreplyable-posts' import {Provider as HighQualityImagesProvider} from './high-quality-images' import {Provider as InAppBrowserProvider} from './in-app-browser' import {Provider as KawaiiProvider} from './kawaii' @@ -96,13 +97,15 @@ export function Provider({children}: React.PropsWithChildren<{}>) { - - - - {children} - - - + + + + + {children} + + + + diff --git a/src/view/com/posts/PostFeed.tsx b/src/view/com/posts/PostFeed.tsx index 75cf11d15..894201356 100644 --- a/src/view/com/posts/PostFeed.tsx +++ b/src/view/com/posts/PostFeed.tsx @@ -37,6 +37,7 @@ import {logger} from '#/logger' import {isIOS, isNative, isWeb} from '#/platform/detection' import {listenPostCreated} from '#/state/events' import {useFeedFeedbackContext} from '#/state/feed-feedback' +import {useHideUnreplyablePosts} from '#/state/preferences/hide-unreplyable-posts' import {useRepostCarouselEnabled} from '#/state/preferences/repost-carousel-enabled' import {useTrendingSettings} from '#/state/preferences/trending' import {STALE} from '#/state/queries' @@ -426,6 +427,7 @@ let PostFeed = ({ const {trendingDisabled, trendingVideoDisabled} = useTrendingSettings() const repostCarouselEnabled = useRepostCarouselEnabled() + const hideUnreplyablePosts = useHideUnreplyablePosts() if (feedType === 'following') { useRepostCarousel = repostCarouselEnabled @@ -546,6 +548,27 @@ let PostFeed = ({ ? groupReposts(page.slices) : (page.slices as FeedPostSliceOrGroup[]) + // Filter out posts that cannot be replied to if the setting is enabled + if (hideUnreplyablePosts) { + slices = slices.filter(slice => { + if (slice.isRepostSlice) { + // For repost slices, filter the inner slices + slice.slices = slice.slices.filter(innerSlice => { + // Check if any item in the slice has replyDisabled + return !innerSlice.items.some( + item => item.post.viewer?.replyDisabled === true, + ) + }) + return slice.slices.length > 0 + } else { + // For regular slices, check if any item has replyDisabled + return !slice.items.some( + item => item.post.viewer?.replyDisabled === true, + ) + } + }) + } + for (const slice of slices) { sliceIndex++ @@ -722,6 +745,7 @@ let PostFeed = ({ hasPressedShowLessUris, ageAssuranceBannerState, isCurrentFeedAtStartupSelected, + hideUnreplyablePosts, ]) // events -- 2.46.2.windows.1