Skip to content

Commit 93f506a

Browse files
committed
Only show ad after first user message
1 parent a44a7a4 commit 93f506a

File tree

1 file changed

+35
-3
lines changed

1 file changed

+35
-3
lines changed

cli/src/hooks/use-gravity-ad.ts

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export type GravityAdState = {
3939
export const useGravityAd = (): GravityAdState => {
4040
const [ad, setAd] = useState<AdResponse | null>(null)
4141
const [isLoading, setIsLoading] = useState(false)
42+
const [shouldShowAd, setShouldShowAd] = useState(false)
4243
const impressionFiredRef = useRef<Set<string>>(new Set())
4344

4445
// Pre-fetched next ad ready to display
@@ -58,8 +59,9 @@ export const useGravityAd = (): GravityAdState => {
5859
const isStartedRef = useRef<boolean>(false)
5960

6061
// Fire impression via web API when ad changes (grants credits)
62+
// Only fire impressions when ad is actually being shown
6163
useEffect(() => {
62-
if (ad?.impUrl && !impressionFiredRef.current.has(ad.impUrl)) {
64+
if (shouldShowAd && ad?.impUrl && !impressionFiredRef.current.has(ad.impUrl)) {
6365
const currentImpUrl = ad.impUrl
6466
impressionFiredRef.current.add(currentImpUrl)
6567
logger.info(
@@ -104,7 +106,7 @@ export const useGravityAd = (): GravityAdState => {
104106
logger.debug({ err }, '[gravity] Failed to record ad impression')
105107
})
106108
}
107-
}, [ad])
109+
}, [ad, shouldShowAd])
108110

109111
// Clear all timers
110112
const clearTimers = useCallback(() => {
@@ -250,10 +252,40 @@ export const useGravityAd = (): GravityAdState => {
250252
}
251253
}, [fetchAdAsync, scheduleNextCycle])
252254

255+
// Subscribe to UI messages to detect first user message
256+
// Only show ads after the user has sent at least one message (clean startup UX)
257+
// We use UI messages instead of runState.messageHistory because UI messages
258+
// update immediately when the user sends a message
259+
useEffect(() => {
260+
if (shouldShowAd || !getAdsEnabled()) {
261+
return
262+
}
263+
264+
// Check initial state
265+
const initialMessages = useChatStore.getState().messages
266+
if (initialMessages.some((msg) => msg.variant === 'user')) {
267+
setShouldShowAd(true)
268+
return
269+
}
270+
271+
const unsubscribe = useChatStore.subscribe((state) => {
272+
const hasUserMessage = state.messages.some((msg) => msg.variant === 'user')
273+
274+
if (hasUserMessage) {
275+
unsubscribe()
276+
logger.info('[gravity] First user message detected, showing ads')
277+
setShouldShowAd(true)
278+
}
279+
})
280+
281+
return unsubscribe
282+
}, [shouldShowAd])
283+
253284
// Clear timers only on unmount
254285
useEffect(() => {
255286
return () => clearTimers()
256287
}, [clearTimers])
257288

258-
return { ad, isLoading, reportActivity }
289+
// Only return the ad if we should show it (after first user message)
290+
return { ad: shouldShowAd ? ad : null, isLoading, reportActivity }
259291
}

0 commit comments

Comments
 (0)