Add toggle to hide posts that can't be replied to from feeds #25

open
opened by maxine.puppykitty.racing targeting main

Adds a toggle to hide posts that can't be replied to (e.g they are postgated to you). I have no idea how to test this one.

Changed files
+112 -7
src
screens
Settings
state
view
com
posts
+25
src/screens/Settings/DeerSettings.tsx
···
useHideSimilarAccountsRecomm,
useSetHideSimilarAccountsRecomm,
} from '#/state/preferences/hide-similar-accounts-recommendations'
import {
useHighQualityImages,
useSetHighQualityImages,
···
const hideSimilarAccountsRecomm = useHideSimilarAccountsRecomm()
const setHideSimilarAccountsRecomm = useSetHideSimilarAccountsRecomm()
const disableVerifyEmailReminder = useDisableVerifyEmailReminder()
const setDisableVerifyEmailReminder = useSetDisableVerifyEmailReminder()
···
<Toggle.Platform />
</Toggle.Item>
<Toggle.Item
name="disable_verify_email_reminder"
label={_(msg`Disable verify email reminder`)}
···
useHideSimilarAccountsRecomm,
useSetHideSimilarAccountsRecomm,
} from '#/state/preferences/hide-similar-accounts-recommendations'
+
import {
+
useHideUnreplyablePosts,
+
useSetHideUnreplyablePosts,
+
} from '#/state/preferences/hide-unreplyable-posts'
import {
useHighQualityImages,
useSetHighQualityImages,
···
const hideSimilarAccountsRecomm = useHideSimilarAccountsRecomm()
const setHideSimilarAccountsRecomm = useSetHideSimilarAccountsRecomm()
+
const hideUnreplyablePosts = useHideUnreplyablePosts()
+
const setHideUnreplyablePosts = useSetHideUnreplyablePosts()
+
const disableVerifyEmailReminder = useDisableVerifyEmailReminder()
const setDisableVerifyEmailReminder = useSetDisableVerifyEmailReminder()
···
<Toggle.Platform />
</Toggle.Item>
+
<Toggle.Item
+
name="hide_unreplyable_posts"
+
label={_(msg`Hide posts that cannot be replied to from feeds`)}
+
value={hideUnreplyablePosts}
+
onChange={value => setHideUnreplyablePosts(value)}
+
style={[a.w_full]}>
+
<Toggle.LabelText style={[a.flex_1]}>
+
<Trans>Hide posts that cannot be replied to from feeds</Trans>
+
</Toggle.LabelText>
+
<Toggle.Platform />
+
</Toggle.Item>
+
<Admonition type="info" style={[a.flex_1]}>
+
<Trans>
+
Hides posts from feeds where replies are disabled (e.g. due to
+
postgates or other restrictions). Does not affect thread views.
+
</Trans>
+
</Admonition>
+
<Toggle.Item
name="disable_verify_email_reminder"
label={_(msg`Disable verify email reminder`)}
+2
src/state/persisted/schema.ts
···
})
.optional(),
highQualityImages: z.boolean().optional(),
showExternalShareButtons: z.boolean().optional(),
···
],
},
highQualityImages: false,
showExternalShareButtons: false,
}
···
})
.optional(),
highQualityImages: z.boolean().optional(),
+
hideUnreplyablePosts: z.boolean().optional(),
showExternalShareButtons: z.boolean().optional(),
···
],
},
highQualityImages: false,
+
hideUnreplyablePosts: false,
showExternalShareButtons: false,
}
+51
src/state/preferences/hide-unreplyable-posts.tsx
···
···
+
import React from 'react'
+
+
import * as persisted from '#/state/persisted'
+
+
type StateContext = persisted.Schema['hideUnreplyablePosts']
+
type SetContext = (v: persisted.Schema['hideUnreplyablePosts']) => void
+
+
const stateContext = React.createContext<StateContext>(
+
persisted.defaults.hideUnreplyablePosts,
+
)
+
const setContext = React.createContext<SetContext>(
+
(_: 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 (
+
<stateContext.Provider value={state}>
+
<setContext.Provider value={setStateWrapped}>
+
{children}
+
</setContext.Provider>
+
</stateContext.Provider>
+
)
+
}
+
+
export function useHideUnreplyablePosts() {
+
return (
+
React.useContext(stateContext) ?? persisted.defaults.hideUnreplyablePosts
+
)
+
}
+
+
export function useSetHideUnreplyablePosts() {
+
return React.useContext(setContext)
+
}
+10 -7
src/state/preferences/index.tsx
···
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 HighQualityImagesProvider} from './high-quality-images'
import {Provider as InAppBrowserProvider} from './in-app-browser'
import {Provider as KawaiiProvider} from './kawaii'
···
<DisableFollowedByMetricsProvider>
<DisablePostsMetricsProvider>
<HideSimilarAccountsRecommProvider>
-
<EnableSquareAvatarsProvider>
-
<EnableSquareButtonsProvider>
-
<DisableVerifyEmailReminderProvider>
-
{children}
-
</DisableVerifyEmailReminderProvider>
-
</EnableSquareButtonsProvider>
-
</EnableSquareAvatarsProvider>
</HideSimilarAccountsRecommProvider>
</DisablePostsMetricsProvider>
</DisableFollowedByMetricsProvider>
···
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'
···
<DisableFollowedByMetricsProvider>
<DisablePostsMetricsProvider>
<HideSimilarAccountsRecommProvider>
+
<HideUnreplyablePostsProvider>
+
<EnableSquareAvatarsProvider>
+
<EnableSquareButtonsProvider>
+
<DisableVerifyEmailReminderProvider>
+
{children}
+
</DisableVerifyEmailReminderProvider>
+
</EnableSquareButtonsProvider>
+
</EnableSquareAvatarsProvider>
+
</HideUnreplyablePostsProvider>
</HideSimilarAccountsRecommProvider>
</DisablePostsMetricsProvider>
</DisableFollowedByMetricsProvider>
+24
src/view/com/posts/PostFeed.tsx
···
import {isIOS, isNative, isWeb} from '#/platform/detection'
import {listenPostCreated} from '#/state/events'
import {useFeedFeedbackContext} from '#/state/feed-feedback'
import {useRepostCarouselEnabled} from '#/state/preferences/repost-carousel-enabled'
import {useTrendingSettings} from '#/state/preferences/trending'
import {STALE} from '#/state/queries'
···
const {trendingDisabled, trendingVideoDisabled} = useTrendingSettings()
const repostCarouselEnabled = useRepostCarouselEnabled()
if (feedType === 'following') {
useRepostCarousel = repostCarouselEnabled
···
? groupReposts(page.slices)
: (page.slices as FeedPostSliceOrGroup[])
for (const slice of slices) {
sliceIndex++
···
hasPressedShowLessUris,
ageAssuranceBannerState,
isCurrentFeedAtStartupSelected,
])
// events
···
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'
···
const {trendingDisabled, trendingVideoDisabled} = useTrendingSettings()
const repostCarouselEnabled = useRepostCarouselEnabled()
+
const hideUnreplyablePosts = useHideUnreplyablePosts()
if (feedType === 'following') {
useRepostCarousel = repostCarouselEnabled
···
? 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++
···
hasPressedShowLessUris,
ageAssuranceBannerState,
isCurrentFeedAtStartupSelected,
+
hideUnreplyablePosts,
])
// events