@@ -12,115 +12,81 @@ import { GithubUsername } from '@/app/utils/config';
1212import { Users } from 'lucide-react' ;
1313import { useEffect , useRef , useState , useCallback } from 'react' ;
1414
15+ const SESSION_KEY = `visitor_${ GithubUsername } ` ;
16+
17+ const getSessionData = ( ) : { count : number } | null => {
18+ try {
19+ const data = sessionStorage . getItem ( SESSION_KEY ) ;
20+ return data ? JSON . parse ( data ) : null ;
21+ } catch { return null ; }
22+ } ;
23+
24+ const setSessionData = ( count : number ) : void => {
25+ try {
26+ sessionStorage . setItem ( SESSION_KEY , JSON . stringify ( { count } ) ) ;
27+ } catch { }
28+ } ;
29+
1530const VisitorCounter = ( ) => {
16- const [ count , setCount ] = useState < number > ( 0 ) ;
31+ const [ count , setCount ] = useState < number | null > ( null ) ;
1732 const [ isUpdating , setIsUpdating ] = useState ( false ) ;
1833 const hasRun = useRef ( false ) ;
19-
2034 const counterId = GithubUsername ;
21- const SESSION_KEY = `visitor_${ counterId } ` ;
22-
23- /**
24- * Session storage operations - faster than cookies
25- */
26- const getSessionData = useCallback ( ( ) : { count : number ; fetched : boolean } | null => {
27- if ( typeof window === 'undefined' ) return null ;
28- try {
29- const data = sessionStorage . getItem ( SESSION_KEY ) ;
30- return data ? JSON . parse ( data ) : null ;
31- } catch {
32- return null ;
33- }
34- } , [ SESSION_KEY ] ) ;
35-
36- const setSessionData = useCallback ( ( count : number , fetched : boolean = true ) : void => {
37- if ( typeof window === 'undefined' ) return ;
38- try {
39- sessionStorage . setItem ( SESSION_KEY , JSON . stringify ( { count, fetched } ) ) ;
40- } catch {
41- // Ignore errors
42- }
43- } , [ SESSION_KEY ] ) ;
4435
4536 /**
4637 * Fast count fetch - API auto-increments, so we handle it properly
38+ * Note: This API is not my own and may break at any time. I am dependent on it.
4739 */
4840 const fetchCount = useCallback ( async ( ) : Promise < void > => {
41+ // Set updating state inside async function
42+ setIsUpdating ( true ) ;
4943 try {
50- const sessionData = getSessionData ( ) ;
51-
52- // If we already fetched this session, don't fetch again
53- if ( sessionData ?. fetched ) {
54- setCount ( sessionData . count ) ;
55- setIsUpdating ( false ) ;
56- return ;
57- }
58-
59- // Fetch with timeout
6044 const controller = new AbortController ( ) ;
61- const timeoutId = setTimeout ( ( ) => controller . abort ( ) , 2000 ) ;
45+ const timeoutId = setTimeout ( ( ) => controller . abort ( ) , 3000 ) ;
6246
63- // Note: This API is not my own and may break at any time. I am dependent on it.
6447 const response = await fetch ( `https://counterpro.vercel.app/api/count/id/${ counterId } ` , {
6548 signal : controller . signal ,
6649 headers : { 'Accept' : 'application/json' }
6750 } ) ;
68-
6951 clearTimeout ( timeoutId ) ;
7052
7153 if ( response . ok ) {
7254 const data = await response . json ( ) ;
7355 const apiCount = data ?. count || 0 ;
74-
7556 // API auto-incremented, so this is our new count
7657 setCount ( apiCount ) ;
77- setSessionData ( apiCount , true ) ;
78- } else {
79- // Fallback to cached or estimated
80- const fallbackCount = sessionData ?. count || Math . floor ( Math . random ( ) * 100 ) + 50 ;
81- setCount ( fallbackCount ) ;
58+ setSessionData ( apiCount ) ;
8259 }
8360 } catch {
84- // Use cached data or show estimated count
85- const sessionData = getSessionData ( ) ;
86- const fallbackCount = sessionData ?. count || Math . floor ( Math . random ( ) * 100 ) + 50 ;
87- setCount ( fallbackCount ) ;
61+ // API failed — keep showing cached value already set from sessionStorage
8862 } finally {
8963 setIsUpdating ( false ) ;
9064 }
91- } , [ counterId , getSessionData , setSessionData ] ) ;
65+ } , [ counterId ] ) ;
9266
9367 useEffect ( ( ) => {
9468 if ( hasRun . current ) return ;
9569 hasRun . current = true ;
9670
97- setTimeout ( ( ) => {
98- // IMMEDIATE: Show cached count from session
99- const sessionData = getSessionData ( ) ;
100- if ( sessionData ) {
101- setCount ( sessionData . count ) ;
102-
103- // If we already fetched this session, don't fetch again
104- if ( sessionData . fetched ) return ;
105- } else {
106- // Show estimated count for new sessions
107- setCount ( Math . floor ( Math . random ( ) * 100 ) + 50 ) ;
108- }
71+ const cached = getSessionData ( ) ;
72+ if ( cached ) {
73+ // Already visited this session — show cached count, skip API call
74+ setTimeout ( ( ) => setCount ( cached . count ) , 0 ) ;
75+ return ;
76+ }
10977
110- // BACKGROUND: Fetch real count if not already done this session
111- setIsUpdating ( true ) ;
112- fetchCount ( ) ;
113- } , 0 ) ;
114- } , [ counterId , fetchCount , getSessionData ] ) ;
78+ const id = setTimeout ( fetchCount , 0 ) ;
79+ return ( ) => clearTimeout ( id ) ;
80+ } , [ fetchCount ] ) ;
11581
11682 return (
11783 < div
11884 className = "flex items-center gap-1 text-xs text-gray-500 dark:text-gray-300 transition-all duration-200 scale-[0.95]"
119- aria-label = { `${ count . toLocaleString ( ) } total visitors` }
85+ aria-label = { count !== null ? `${ count . toLocaleString ( ) } total visitors` : 'Loading visitor count' }
12086 >
12187 < Users className = { `text-cyan-400 w-4 h-4 shrink-0 ${ isUpdating ? 'animate-pulse' : '' } ` } />
12288 < span className = "tabular-nums" >
123- { count . toLocaleString ( ) } visitors
89+ { count !== null ? ` ${ count . toLocaleString ( ) } visitors` : '... visitors' }
12490 </ span >
12591 </ div >
12692 ) ;
0 commit comments