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 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 13x 13x 13x 13x 13x 13x 1x 1x 65x 65x 65x 65x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 26x 26x 26x 13x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x 26x | 'use client'
import { useCallback, useMemo, useSyncExternalStore } from 'react'
/**
* Observer co-play profile for joining practice game breaks as a participant.
* Stored in localStorage so it persists across page reloads.
*/
export interface ObserverCoPlayProfile {
name: string
emoji: string
color: string
isReady: boolean
}
const STORAGE_KEY = 'observer-coplay-profile'
// ============================================================================
// External store for cross-component reactivity
// ============================================================================
let listeners: Array<() => void> = []
function subscribe(listener: () => void) {
listeners = [...listeners, listener]
return () => {
listeners = listeners.filter((l) => l !== listener)
}
}
function emitChange() {
for (const listener of listeners) {
listener()
}
}
function getSnapshot(): string | null {
if (typeof window === 'undefined') return null
return localStorage.getItem(STORAGE_KEY)
}
function getServerSnapshot(): string | null {
return null
}
// ============================================================================
// Hook
// ============================================================================
/**
* Manage the observer's co-play profile for game break participation.
*
* Profile is stored in localStorage and shared across components
* via useSyncExternalStore for instant reactivity.
*/
export function useObserverCoPlayProfile() {
const raw = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot)
const profile = useMemo<ObserverCoPlayProfile | null>(() => {
if (!raw) return null
try {
return JSON.parse(raw) as ObserverCoPlayProfile
} catch {
return null
}
}, [raw])
const setProfile = useCallback((update: ObserverCoPlayProfile | null) => {
if (update) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(update))
} else {
localStorage.removeItem(STORAGE_KEY)
}
emitChange()
}, [])
const updateProfile = useCallback(
(partial: Partial<ObserverCoPlayProfile>) => {
const current = profile ?? { name: '', emoji: '😊', color: '#6366f1', isReady: false }
setProfile({ ...current, ...partial })
},
[profile, setProfile]
)
return {
profile,
isReady: profile?.isReady ?? false,
setProfile,
updateProfile,
clearProfile: useCallback(() => setProfile(null), [setProfile]),
}
}
|