Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5f9b2dfe2b | ||
|
|
1bfde8fb25 | ||
|
|
48647e4fb5 | ||
|
|
318f9469a0 |
14
CHANGELOG.md
14
CHANGELOG.md
@@ -1,3 +1,17 @@
|
||||
## [4.48.1](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.48.0...v4.48.1) (2025-10-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **hero:** prevent SSR hydration mismatch for subtitle ([1bfde8f](https://github.com/antialias/soroban-abacus-flashcards/commit/1bfde8fb251b227ccd2528bfe1c47acffd79fa49))
|
||||
|
||||
## [4.48.0](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.47.2...v4.48.0) (2025-10-20)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **hero:** persist random subtitle per-session ([318f946](https://github.com/antialias/soroban-abacus-flashcards/commit/318f9469a0805c200c55ce4024a95fd7b8dbe6a2))
|
||||
|
||||
## [4.47.2](https://github.com/antialias/soroban-abacus-flashcards/compare/v4.47.1...v4.47.2) (2025-10-20)
|
||||
|
||||
|
||||
|
||||
@@ -6,7 +6,14 @@ import { css } from '../../styled-system/css'
|
||||
import { useHomeHero } from '../contexts/HomeHeroContext'
|
||||
|
||||
export function HeroAbacus() {
|
||||
const { subtitle, abacusValue, setAbacusValue, setIsHeroVisible, isAbacusLoaded } = useHomeHero()
|
||||
const {
|
||||
subtitle,
|
||||
abacusValue,
|
||||
setAbacusValue,
|
||||
setIsHeroVisible,
|
||||
isAbacusLoaded,
|
||||
isSubtitleLoaded,
|
||||
} = useHomeHero()
|
||||
const appConfig = useAbacusConfig()
|
||||
const heroRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
@@ -101,6 +108,8 @@ export function HeroAbacus() {
|
||||
color: 'purple.300',
|
||||
fontStyle: 'italic',
|
||||
marginBottom: '8',
|
||||
opacity: isSubtitleLoaded ? 1 : 0,
|
||||
transition: 'opacity 0.5s ease-in-out',
|
||||
})}
|
||||
>
|
||||
{subtitle.text}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import type React from 'react'
|
||||
import { createContext, useContext, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import type { Subtitle } from '../data/abaciOneSubtitles'
|
||||
import { getRandomSubtitle, subtitles } from '../data/abaciOneSubtitles'
|
||||
import { subtitles } from '../data/abaciOneSubtitles'
|
||||
|
||||
interface HomeHeroContextValue {
|
||||
subtitle: Subtitle
|
||||
@@ -12,6 +12,7 @@ interface HomeHeroContextValue {
|
||||
isHeroVisible: boolean
|
||||
setIsHeroVisible: (visible: boolean) => void
|
||||
isAbacusLoaded: boolean
|
||||
isSubtitleLoaded: boolean
|
||||
}
|
||||
|
||||
const HomeHeroContext = createContext<HomeHeroContextValue | null>(null)
|
||||
@@ -21,10 +22,28 @@ export { HomeHeroContext }
|
||||
export function HomeHeroProvider({ children }: { children: React.ReactNode }) {
|
||||
// Use first subtitle for SSR, then select random one on client mount
|
||||
const [subtitle, setSubtitle] = useState<Subtitle>(subtitles[0])
|
||||
const [isSubtitleLoaded, setIsSubtitleLoaded] = useState(false)
|
||||
|
||||
// Select random subtitle only on client side to avoid SSR mismatch
|
||||
// Select random subtitle only on client side, persist per-session
|
||||
useEffect(() => {
|
||||
setSubtitle(getRandomSubtitle())
|
||||
// Check if we have a stored subtitle index for this session
|
||||
const storedIndex = sessionStorage.getItem('heroSubtitleIndex')
|
||||
|
||||
if (storedIndex !== null) {
|
||||
// Use the stored subtitle index
|
||||
const index = parseInt(storedIndex, 10)
|
||||
if (!Number.isNaN(index) && index >= 0 && index < subtitles.length) {
|
||||
setSubtitle(subtitles[index])
|
||||
setIsSubtitleLoaded(true)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Generate a new random index and store it
|
||||
const randomIndex = Math.floor(Math.random() * subtitles.length)
|
||||
sessionStorage.setItem('heroSubtitleIndex', randomIndex.toString())
|
||||
setSubtitle(subtitles[randomIndex])
|
||||
setIsSubtitleLoaded(true)
|
||||
}, [])
|
||||
|
||||
// Shared abacus value - always start at 0 for SSR/hydration consistency
|
||||
@@ -91,8 +110,9 @@ export function HomeHeroProvider({ children }: { children: React.ReactNode }) {
|
||||
isHeroVisible,
|
||||
setIsHeroVisible,
|
||||
isAbacusLoaded,
|
||||
isSubtitleLoaded,
|
||||
}),
|
||||
[subtitle, abacusValue, isHeroVisible, isAbacusLoaded]
|
||||
[subtitle, abacusValue, isHeroVisible, isAbacusLoaded, isSubtitleLoaded]
|
||||
)
|
||||
|
||||
return <HomeHeroContext.Provider value={value}>{children}</HomeHeroContext.Provider>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "soroban-monorepo",
|
||||
"version": "4.47.2",
|
||||
"version": "4.48.1",
|
||||
"private": true,
|
||||
"description": "Beautiful Soroban Flashcard Generator - Monorepo",
|
||||
"workspaces": [
|
||||
|
||||
Reference in New Issue
Block a user