Skip to content

Commit 9262e6d

Browse files
committed
Don't clear timers until unmount
1 parent db8b751 commit 9262e6d

File tree

1 file changed

+36
-29
lines changed

1 file changed

+36
-29
lines changed

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

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export type GravityAdState = {
7171

7272
/**
7373
* Hook for fetching and rotating Gravity ads.
74-
*
74+
*
7575
* Behavior:
7676
* - Ads rotate every 60 seconds
7777
* - Next ad is pre-fetched 5 seconds before display for instant swap
@@ -85,17 +85,17 @@ export const useGravityAd = (): GravityAdState => {
8585

8686
// Pre-fetched next ad ready to display
8787
const nextAdRef = useRef<AdResponse | null>(null)
88-
88+
8989
// Counter: how many ads shown since last user activity
9090
const adsShownRef = useRef<number>(0)
91-
91+
9292
// Is rotation currently paused (shown 3 ads without activity)?
9393
const isPausedRef = useRef<boolean>(false)
94-
94+
9595
// Timers
9696
const prefetchTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
9797
const swapTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
98-
98+
9999
// Has the first ad been fetched?
100100
const isStartedRef = useRef<boolean>(false)
101101

@@ -106,21 +106,24 @@ export const useGravityAd = (): GravityAdState => {
106106
useEffect(() => {
107107
if (ad?.impUrl && !impressionFiredRef.current.has(ad.impUrl)) {
108108
impressionFiredRef.current.add(ad.impUrl)
109-
logger.info({ impUrl: ad.impUrl, payout: ad.payout }, '[gravity] Recording ad impression')
110-
109+
logger.info(
110+
{ impUrl: ad.impUrl, payout: ad.payout },
111+
'[gravity] Recording ad impression',
112+
)
113+
111114
const authToken = getAuthToken()
112115
if (!authToken) {
113116
logger.warn('[gravity] No auth token, skipping impression recording')
114117
return
115118
}
116-
119+
117120
// Call our web API to fire impression and grant credits
118121
// Only send impUrl - server looks up trusted ad data from database
119122
fetch(`${WEBSITE_URL}/api/v1/ads/impression`, {
120123
method: 'POST',
121124
headers: {
122125
'Content-Type': 'application/json',
123-
'Authorization': `Bearer ${authToken}`,
126+
Authorization: `Bearer ${authToken}`,
124127
},
125128
body: JSON.stringify({
126129
impUrl: ad.impUrl,
@@ -156,7 +159,7 @@ export const useGravityAd = (): GravityAdState => {
156159
// Fetch an ad via web API and return it (for pre-fetching)
157160
const fetchAdAsync = useCallback(async (): Promise<AdResponse | null> => {
158161
if (!getAdsEnabled()) return null
159-
162+
160163
const authToken = getAuthToken()
161164
if (!authToken) {
162165
logger.warn('[gravity] No auth token available')
@@ -168,28 +171,32 @@ export const useGravityAd = (): GravityAdState => {
168171
currentRunState?.sessionState?.mainAgentState?.messageHistory ?? []
169172
const adMessages = convertToAdMessages(messageHistory)
170173

171-
if (adMessages.length === 0) return null
174+
logger.info(
175+
{ messageCount: adMessages.length },
176+
'[gravity] Fetching ad from web API',
177+
)
172178

173-
logger.info('[gravity] Fetching ad from web API')
174-
175179
try {
176180
const response = await fetch(`${WEBSITE_URL}/api/v1/ads`, {
177181
method: 'POST',
178182
headers: {
179183
'Content-Type': 'application/json',
180-
'Authorization': `Bearer ${authToken}`,
184+
Authorization: `Bearer ${authToken}`,
181185
},
182186
body: JSON.stringify({ messages: adMessages }),
183187
})
184-
188+
185189
if (!response.ok) {
186-
logger.warn({ status: response.status }, '[gravity] Web API returned error')
190+
logger.warn(
191+
{ status: response.status },
192+
'[gravity] Web API returned error',
193+
)
187194
return null
188195
}
189-
196+
190197
const data = await response.json()
191198
const ad = data.ad as AdResponse | null
192-
199+
193200
logger.info(
194201
{
195202
hasAd: !!ad,
@@ -256,10 +263,10 @@ export const useGravityAd = (): GravityAdState => {
256263
// Report user activity - resets counter and resumes rotation if paused
257264
const reportActivity = useCallback(() => {
258265
const wasPaused = isPausedRef.current
259-
266+
260267
// Reset counter
261268
adsShownRef.current = 0
262-
269+
263270
if (wasPaused) {
264271
logger.info('[gravity] User active, resuming ad rotation')
265272
isPausedRef.current = false
@@ -268,29 +275,29 @@ export const useGravityAd = (): GravityAdState => {
268275
}
269276
}, [scheduleNextCycle])
270277

271-
// Start ad rotation when enabled and we have messages
278+
// Prefetch ad on startup (before any messages are sent)
272279
useEffect(() => {
273-
const messageHistory =
274-
runState?.sessionState?.mainAgentState?.messageHistory ?? []
275-
const hasMessages = messageHistory.length > 0
276280
const adsEnabled = getAdsEnabled()
277281
const hasAuth = !!getAuthToken()
278282

279-
if (hasMessages && adsEnabled && hasAuth && !isStartedRef.current) {
280-
logger.info('[gravity] Starting ad rotation')
283+
if (adsEnabled && hasAuth && !isStartedRef.current) {
284+
logger.info('[gravity] Prefetching ad on startup')
281285
isStartedRef.current = true
282286
setIsLoading(true)
283-
284-
// Fetch and display first ad
287+
288+
// Prefetch first ad immediately
285289
fetchAdAsync().then((firstAd) => {
286290
setAd(firstAd)
287291
setIsLoading(false)
288292
scheduleNextCycle()
289293
})
290294
}
295+
}, [fetchAdAsync, scheduleNextCycle])
291296

297+
// Clear timers only on unmount
298+
useEffect(() => {
292299
return () => clearTimers()
293-
}, [runState, fetchAdAsync, scheduleNextCycle, clearTimers])
300+
}, [clearTimers])
294301

295302
return { ad, isLoading, reportActivity }
296303
}

0 commit comments

Comments
 (0)