@@ -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