Add a composer prompt on home

This commit is contained in:
Alex Benzer
2025-11-14 12:20:10 -08:00
parent b56ee74cdb
commit 93c53f3a15
4 changed files with 137 additions and 0 deletions
+1
View File
@@ -9,5 +9,6 @@ export type Gate =
| 'onboarding_add_video_feed'
| 'onboarding_suggested_starterpacks'
| 'remove_show_latest_button'
| 'show_composer_prompt'
| 'test_gate_1'
| 'test_gate_2'
+1
View File
@@ -202,6 +202,7 @@ export type MetricEvents = {
'composer:gif:open': {}
'composer:gif:select': {}
'postComposer:click': {}
// Data events
'account:create:begin': {}
+115
View File
@@ -0,0 +1,115 @@
import React, {useState} from 'react'
import {Pressable, StyleSheet, View} from 'react-native'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useOpenComposer} from '#/lib/hooks/useOpenComposer'
import {sanitizeDisplayName} from '#/lib/strings/display-names'
import {sanitizeHandle} from '#/lib/strings/handles'
import {logger} from '#/logger'
import {useCurrentAccountProfile} from '#/state/queries/useCurrentAccountProfile'
import {UserAvatar} from '#/view/com/util/UserAvatar'
import {atoms as a, native, useTheme, web} from '#/alf'
import {SubtleHover} from '#/components/SubtleHover'
import {Text} from '#/components/Typography'
export function FeedComposerPrompt() {
const {_} = useLingui()
const t = useTheme()
const {openComposer} = useOpenComposer()
const profile = useCurrentAccountProfile()
const [hover, setHover] = useState(false)
const onPress = React.useCallback(() => {
logger.metric('postComposer:click', {})
openComposer({})
}, [openComposer])
if (!profile) {
return null
}
const displayName = sanitizeDisplayName(
profile.displayName || sanitizeHandle(profile.handle),
)
const handle = sanitizeHandle(profile.handle, '@')
return (
<Pressable
onPress={onPress}
android_ripple={null}
accessibilityRole="button"
accessibilityLabel={_(msg`Compose new post`)}
accessibilityHint={_(msg`Opens the post composer`)}
onPointerEnter={() => setHover(true)}
onPointerLeave={() => setHover(false)}
style={({pressed}) => [
a.relative,
a.flex_row,
a.align_start,
{
paddingLeft: 18,
paddingRight: 15,
},
a.py_md,
native({
paddingTop: 15,
paddingBottom: 15,
}),
{
borderTopWidth: StyleSheet.hairlineWidth,
borderColor: t.atoms.border_contrast_low.borderColor,
},
web({
cursor: 'pointer',
outline: 'none',
}),
pressed && web({outline: 'none'}),
]}>
<SubtleHover hover={hover} />
<UserAvatar
avatar={profile.avatar}
size={40}
type={profile.associated?.labeler ? 'labeler' : 'user'}
/>
<View style={[a.flex_1, a.ml_md]}>
<View style={[a.flex_row, a.align_center, a.gap_xs, a.mb_sm]}>
<Text
emoji
style={[a.text_md, a.font_semi_bold, a.flex_shrink]}
numberOfLines={1}>
{displayName}
</Text>
<Text
style={[a.text_md, t.atoms.text_contrast_medium, a.flex_shrink]}
numberOfLines={1}>
{handle}
</Text>
</View>
<View
style={[
a.flex_row,
a.align_center,
a.px_md,
{
height: 40,
borderRadius: 20,
},
t.atoms.bg_contrast_50,
]}>
<Text
style={[
t.atoms.text_contrast_medium,
a.text_md,
{
lineHeight: a.text_md.fontSize,
includeFontPadding: false,
},
]}>
{_(msg`What's up?`)}
</Text>
</View>
</View>
</Pressable>
)
}
+20
View File
@@ -69,6 +69,7 @@ import {
} from '#/components/feeds/PostFeedVideoGridRow'
import {TrendingInterstitial} from '#/components/interstitials/Trending'
import {TrendingVideos as TrendingVideosInterstitial} from '#/components/interstitials/TrendingVideos'
import {FeedComposerPrompt} from '../feeds/FeedComposerPrompt'
import {DiscoverFallbackHeader} from './DiscoverFallbackHeader'
import {FeedShutdownMsg} from './FeedShutdownMsg'
import {PostFeedErrorMessage} from './PostFeedErrorMessage'
@@ -149,6 +150,10 @@ type FeedRow =
type: 'ageAssuranceBanner'
key: string
}
| {
type: 'composerPrompt'
key: string
}
export function getItemsForFeedback(feedRow: FeedRow): {
item: FeedPostSliceItem
@@ -501,6 +506,18 @@ let PostFeed = ({
'interstitial2-' + sliceIndex + '-' + lastFetchedAt,
})
}
// Add composer prompt for Discover and Following feeds (after trending bar if it exists)
// Feature gate disabled - always show
if (
hasSession &&
(feedUriOrActorDid === DISCOVER_FEED_URI ||
feed === 'following')
) {
arr.push({
type: 'composerPrompt',
key: 'composerPrompt-' + sliceIndex,
})
}
} else if (sliceIndex === 15) {
if (areVideoFeedsEnabled && !trendingVideoDisabled) {
arr.push({
@@ -622,6 +639,7 @@ let PostFeed = ({
isEmpty,
lastFetchedAt,
data,
feed,
feedType,
feedUriOrActorDid,
feedTab,
@@ -726,6 +744,8 @@ let PostFeed = ({
return <AgeAssuranceDismissibleFeedBanner />
} else if (row.type === 'interstitialTrending') {
return <TrendingInterstitial />
} else if (row.type === 'composerPrompt') {
return <FeedComposerPrompt />
} else if (row.type === 'interstitialTrendingVideos') {
return <TrendingVideosInterstitial />
} else if (row.type === 'fallbackMarker') {