All files / web/src/contexts HomeHeroContext.tsx

98.97% Statements 97/98
100% Branches 12/12
100% Functions 2/2
98.97% Lines 97/98

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 991x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 25x 25x 25x 25x 25x 10x 10x 10x   10x 10x 10x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 10x 10x 10x 10x 10x 1x 1x 1x 1x 1x 10x 10x 10x 10x 10x 10x 25x 25x 25x 25x 13x 1x 1x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 25x 1x 1x 29x 29x 4x 4x 25x 25x  
'use client'
 
import type React from 'react'
import { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react'
import type { Subtitle } from '../data/abaciOneSubtitles'
import { subtitles } from '../data/abaciOneSubtitles'
 
const ROTATION_INTERVAL = 5000 // 5 seconds
 
interface HomeHeroContextValue {
  subtitle: Subtitle
  abacusValue: number
  setAbacusValue: (value: number) => void
  isHeroVisible: boolean
  setIsHeroVisible: (visible: boolean) => void
  isAbacusLoaded: boolean
  isSubtitleLoaded: boolean
}
 
const HomeHeroContext = createContext<HomeHeroContextValue | null>(null)
 
export { HomeHeroContext }
 
export function HomeHeroProvider({ children }: { children: React.ReactNode }) {
  const [subtitleIndex, setSubtitleIndex] = useState(0)
  const [isSubtitleLoaded, setIsSubtitleLoaded] = useState(false)
 
  // Rotate subtitles on a 5-second interval
  useEffect(() => {
    setIsSubtitleLoaded(true)
 
    const timer = setInterval(() => {
      setSubtitleIndex((prev) => (prev + 1) % subtitles.length)
    }, ROTATION_INTERVAL)
 
    return () => clearInterval(timer)
  }, [])
 
  const subtitle = subtitles[subtitleIndex]
 
  // Shared abacus value - always start at 0 for SSR/hydration consistency
  const [abacusValue, setAbacusValue] = useState(0)
  const [isAbacusLoaded, setIsAbacusLoaded] = useState(false)
  const isLoadingFromStorage = useRef(false)
 
  // Load from sessionStorage after mount (client-only, no hydration mismatch)
  useEffect(() => {
    isLoadingFromStorage.current = true // Block saves during load
 
    const saved = sessionStorage.getItem('heroAbacusValue')
 
    if (saved) {
      const parsedValue = parseInt(saved, 10)
      if (!Number.isNaN(parsedValue)) {
        setAbacusValue(parsedValue)
      }
    }
 
    // Use setTimeout to ensure the value has been set before we allow saves
    setTimeout(() => {
      isLoadingFromStorage.current = false
      setIsAbacusLoaded(true)
    }, 0)
  }, [])
 
  // Persist value to sessionStorage when it changes (but skip during load)
  useEffect(() => {
    if (!isLoadingFromStorage.current) {
      sessionStorage.setItem('heroAbacusValue', abacusValue.toString())
    }
  }, [abacusValue])
 
  // Track hero visibility for nav branding
  const [isHeroVisible, setIsHeroVisible] = useState(true)
 
  const value = useMemo(
    () => ({
      subtitle,
      abacusValue,
      setAbacusValue,
      isHeroVisible,
      setIsHeroVisible,
      isAbacusLoaded,
      isSubtitleLoaded,
    }),
    [subtitle, abacusValue, isHeroVisible, isAbacusLoaded, isSubtitleLoaded]
  )
 
  return <HomeHeroContext.Provider value={value}>{children}</HomeHeroContext.Provider>
}
 
export function useHomeHero() {
  const context = useContext(HomeHeroContext)
  if (!context) {
    throw new Error('useHomeHero must be used within HomeHeroProvider')
  }
  return context
}