feat: prevent search engines from indexing Discover feed content as page descriptions

This commit is contained in:
Caidan Williams
2025-08-22 18:25:35 -07:00
parent 0517768cd3
commit 6ddc268b91
2 changed files with 72 additions and 38 deletions
+43 -38
View File
@@ -29,6 +29,7 @@ import {FAB} from '../util/fab/FAB'
import {type ListMethods} from '../util/List' import {type ListMethods} from '../util/List'
import {LoadLatestBtn} from '../util/load-latest/LoadLatestBtn' import {LoadLatestBtn} from '../util/load-latest/LoadLatestBtn'
import {MainScrollProvider} from '../util/MainScrollProvider' import {MainScrollProvider} from '../util/MainScrollProvider'
import {NoSnippetWrapper} from '../util/NoSnippetWrapper'
const POLL_FREQ = 60e3 // 60sec const POLL_FREQ = 60e3 // 60sec
@@ -132,45 +133,49 @@ export function FeedPage({
const shouldPrefetch = isNative && isPageAdjacent const shouldPrefetch = isNative && isPageAdjacent
return ( return (
<View testID={testID} {...(isDiscoverFeed && {'data-nosnippet': true})}> <NoSnippetWrapper enabled={isDiscoverFeed}>
<MainScrollProvider> <View testID={testID}>
<FeedFeedbackProvider value={feedFeedback}> <MainScrollProvider>
<PostFeed <FeedFeedbackProvider value={feedFeedback}>
testID={testID ? `${testID}-feed` : undefined} <PostFeed
enabled={isPageFocused || shouldPrefetch} testID={testID ? `${testID}-feed` : undefined}
feed={feed} enabled={isPageFocused || shouldPrefetch}
feedParams={feedParams} feed={feed}
pollInterval={POLL_FREQ} feedParams={feedParams}
disablePoll={hasNew || !isPageFocused} pollInterval={POLL_FREQ}
scrollElRef={scrollElRef} disablePoll={hasNew || !isPageFocused}
onScrolledDownChange={setIsScrolledDown} scrollElRef={scrollElRef}
onHasNew={setHasNew} onScrolledDownChange={setIsScrolledDown}
renderEmptyState={renderEmptyState} onHasNew={setHasNew}
renderEndOfFeed={renderEndOfFeed} renderEmptyState={renderEmptyState}
headerOffset={headerOffset} renderEndOfFeed={renderEndOfFeed}
savedFeedConfig={savedFeedConfig} headerOffset={headerOffset}
isVideoFeed={isVideoFeed} savedFeedConfig={savedFeedConfig}
isVideoFeed={isVideoFeed}
/>
</FeedFeedbackProvider>
</MainScrollProvider>
{(isScrolledDown || hasNew) && (
<LoadLatestBtn
onPress={onPressLoadLatest}
label={_(msg`Load new posts`)}
showIndicator={hasNew}
/> />
</FeedFeedbackProvider> )}
</MainScrollProvider>
{(isScrolledDown || hasNew) && (
<LoadLatestBtn
onPress={onPressLoadLatest}
label={_(msg`Load new posts`)}
showIndicator={hasNew}
/>
)}
{hasSession && ( {hasSession && (
<FAB <FAB
testID="composeFAB" testID="composeFAB"
onPress={onPressCompose} onPress={onPressCompose}
icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />} icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />}
accessibilityRole="button" accessibilityRole="button"
accessibilityLabel={_(msg({message: `New post`, context: 'action'}))} accessibilityLabel={_(
accessibilityHint="" msg({message: `New post`, context: 'action'}),
/> )}
)} accessibilityHint=""
</View> />
)}
</View>
</NoSnippetWrapper>
) )
} }
+29
View File
@@ -0,0 +1,29 @@
import {type ViewProps} from 'react-native'
// @ts-expect-error untyped
import {unstable_createElement} from 'react-native-web'
import {isWeb} from '#/platform/detection'
interface Props extends ViewProps {
enabled: boolean
}
/**
* NoSnippetWrapper prevents search engines from displaying snippets of its content.
*
* If running on web and enabled, wraps children in a <div> with data-nosnippet attribute.
* Otherwise, renders children directly.
*
* @param enabled - Whether to apply the data-nosnippet attribute.
* @param viewProps - Additional props for the wrapper element.
*/
export function NoSnippetWrapper({enabled, ...viewProps}: Props) {
if (isWeb && enabled) {
return unstable_createElement('div', {
...viewProps,
'data-nosnippet': '',
})
}
return <>{viewProps.children}</>
}