@@ -123,7 +123,10 @@ type XpLevelInfo = {
123123 xpInLevel : number ;
124124 xpRequiredForLevelUp : number ;
125125} ;
126- const xpLevelCache = new Map < string , { value : XpLevelInfo ; timestamp : number } > ( ) ;
126+ const xpLevelCache = new Map <
127+ string ,
128+ { value : XpLevelInfo ; timestamp : number }
129+ > ( ) ;
127130
128131/** ---------------- Utilities ---------------- */
129132function useIsTutor ( _frag : NavbarIsTutor$key ) {
@@ -573,68 +576,74 @@ function UserInfo({ tutor, userId }: { tutor: boolean; userId: string }) {
573576
574577 // central XP fetcher (Relay)
575578 const relayEnv = useRelayEnvironment ( ) ;
576- const fetchXP = useCallback ( async ( force = false ) => {
577- if ( ! userId ) return ;
578-
579- const now = Date . now ( ) ;
580- const cached = xpLevelCache . get ( userId ) ;
581- if ( ! force && cached && now - cached . timestamp < XP_CACHE_TTL_MS ) {
582- setLevelInfo ( cached . value ) ;
583- return ;
584- }
585- if ( ! force && xpFetchInFlightRef . current ) return ;
586- if ( ! force && now - xpLastFetchAtRef . current < 1500 ) return ;
587-
588- xpFetchInFlightRef . current = true ;
589- xpLastFetchAtRef . current = now ;
590-
591- try {
592- const query = graphql `
593- query NavbarGetUserXPQuery($userID: ID!) {
594- getUser(userID: $userID) {
595- refUserID
596- name
597- email
598- xpValue
599- requiredXP
600- exceedingXP
601- level
579+ const fetchXP = useCallback (
580+ async ( force = false ) => {
581+ if ( ! userId ) return ;
582+
583+ const now = Date . now ( ) ;
584+ const cached = xpLevelCache . get ( userId ) ;
585+ if ( ! force && cached && now - cached . timestamp < XP_CACHE_TTL_MS ) {
586+ setLevelInfo ( cached . value ) ;
587+ return ;
588+ }
589+ if ( ! force && xpFetchInFlightRef . current ) return ;
590+ if ( ! force && now - xpLastFetchAtRef . current < 1500 ) return ;
591+
592+ xpFetchInFlightRef . current = true ;
593+ xpLastFetchAtRef . current = now ;
594+
595+ try {
596+ const query = graphql `
597+ query NavbarGetUserXPQuery($userID: ID!) {
598+ getUser(userID: $userID) {
599+ refUserID
600+ name
601+ email
602+ xpValue
603+ requiredXP
604+ exceedingXP
605+ level
606+ }
602607 }
608+ ` ;
609+ const levelData = await fetchQuery ( relayEnv , query , {
610+ userID : userId ,
611+ } ) . toPromise ( ) ;
612+
613+ const rawUser = ( levelData as any ) ?. getUser ;
614+ const payload : any = Array . isArray ( rawUser )
615+ ? rawUser [ 0 ] ?? null
616+ : rawUser ?? null ;
617+
618+ if ( ! payload ) {
619+ const fallback = { level : 0 , xpInLevel : 0 , xpRequiredForLevelUp : 1 } ;
620+ setLevelInfo ( fallback ) ;
621+ xpLevelCache . set ( userId , { value : fallback , timestamp : Date . now ( ) } ) ;
622+ return ;
603623 }
604- ` ;
605- const levelData = await fetchQuery ( relayEnv , query , {
606- userID : userId ,
607- } ) . toPromise ( ) ;
608-
609- const rawUser = ( levelData as any ) ?. getUser ;
610- const payload : any = Array . isArray ( rawUser )
611- ? rawUser [ 0 ] ?? null
612- : rawUser ?? null ;
613-
614- if ( ! payload ) {
615- const fallback = { level : 0 , xpInLevel : 0 , xpRequiredForLevelUp : 1 } ;
616- setLevelInfo ( fallback ) ;
617- xpLevelCache . set ( userId , { value : fallback , timestamp : Date . now ( ) } ) ;
618- return ;
624+ const requiredXP = Number ( payload . requiredXP ?? 0 ) ;
625+ const exceedingXP = Number ( payload . exceedingXP ?? 0 ) ;
626+ const level = Number ( payload . level ?? 0 ) ;
627+ const nextLevelInfo = {
628+ level : Number . isFinite ( level ) ? level : 0 ,
629+ xpInLevel : Number . isFinite ( exceedingXP ) ? exceedingXP : 0 ,
630+ xpRequiredForLevelUp :
631+ Number . isFinite ( requiredXP ) && requiredXP > 0 ? requiredXP : 1 ,
632+ } ;
633+ setLevelInfo ( nextLevelInfo ) ;
634+ xpLevelCache . set ( userId , {
635+ value : nextLevelInfo ,
636+ timestamp : Date . now ( ) ,
637+ } ) ;
638+ } catch ( e ) {
639+ console . error ( "[Navbar XP] fetch failed" , e ) ;
640+ setLevelInfo ( { level : 0 , xpInLevel : 0 , xpRequiredForLevelUp : 1 } ) ;
641+ } finally {
642+ xpFetchInFlightRef . current = false ;
619643 }
620- const requiredXP = Number ( payload . requiredXP ?? 0 ) ;
621- const exceedingXP = Number ( payload . exceedingXP ?? 0 ) ;
622- const level = Number ( payload . level ?? 0 ) ;
623- const nextLevelInfo = {
624- level : Number . isFinite ( level ) ? level : 0 ,
625- xpInLevel : Number . isFinite ( exceedingXP ) ? exceedingXP : 0 ,
626- xpRequiredForLevelUp :
627- Number . isFinite ( requiredXP ) && requiredXP > 0 ? requiredXP : 1 ,
628- } ;
629- setLevelInfo ( nextLevelInfo ) ;
630- xpLevelCache . set ( userId , { value : nextLevelInfo , timestamp : Date . now ( ) } ) ;
631- } catch ( e ) {
632- console . error ( "[Navbar XP] fetch failed" , e ) ;
633- setLevelInfo ( { level : 0 , xpInLevel : 0 , xpRequiredForLevelUp : 1 } ) ;
634- } finally {
635- xpFetchInFlightRef . current = false ;
636- }
637- } , [ relayEnv , userId ] ) ;
644+ } ,
645+ [ relayEnv , userId ]
646+ ) ;
638647
639648 // initial fetch and on identity changes
640649 useEffect ( ( ) => {
0 commit comments