All files / web/src/arcade-games/know-your-world/features/hint useStruggleDetection.ts

45.88% Statements 39/85
100% Branches 0/0
0% Functions 0/1
45.88% Lines 39/85

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 861x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                                                                                              
/**
 * Struggle Detection Hook
 *
 * Monitors user search behavior and automatically advances to the next hint
 * when the user appears to be struggling (based on time spent searching).
 */
 
import { useEffect, useRef } from 'react'
 
// Thresholds for triggering next hint
const STRUGGLE_TIME_THRESHOLD = 30000 // 30 seconds per hint level
const STRUGGLE_CHECK_INTERVAL = 5000 // Check every 5 seconds
 
export interface UseStruggleDetectionOptions {
  /** Whether hot/cold feedback is enabled (implies we're tracking search metrics) */
  effectiveHotColdEnabled: boolean
  /** Whether there are more hints available to show */
  hasMoreHints: boolean
  /** Current region prompt ID */
  currentPrompt: string | null
  /** Function to get search metrics */
  getSearchMetrics: (startTime: number) => { timeToFind: number }
  /** Function to advance to next hint */
  nextHint: () => void
  /** Ref to prompt start time */
  promptStartTime: React.RefObject<number>
}
 
/**
 * Hook that detects when a user is struggling to find a region and
 * automatically advances to the next hint level.
 *
 * Hint levels are based on time:
 * - Level 0 = first 30 seconds
 * - Level 1 = 30-60 seconds
 * - Level 2 = 60-90 seconds
 * - etc.
 */
export function useStruggleDetection({
  effectiveHotColdEnabled,
  hasMoreHints,
  currentPrompt,
  getSearchMetrics,
  nextHint,
  promptStartTime,
}: UseStruggleDetectionOptions): void {
  // Track which hint level triggered last hint
  const lastHintLevelRef = useRef(0)

  // Check for struggle and advance hint
  useEffect(() => {
    // Only run if hot/cold is enabled (means we're tracking search metrics)
    if (!effectiveHotColdEnabled || !hasMoreHints || !currentPrompt) return

    const checkStruggle = () => {
      const metrics = getSearchMetrics(promptStartTime.current ?? Date.now())

      // Calculate which hint level we should be at based on time
      // Level 0 = first 30 seconds, Level 1 = 30-60 seconds, etc.
      const expectedHintLevel = Math.floor(metrics.timeToFind / STRUGGLE_TIME_THRESHOLD)

      // If we should be at a higher hint level than last triggered, give next hint
      if (expectedHintLevel > lastHintLevelRef.current && hasMoreHints) {
        lastHintLevelRef.current = expectedHintLevel
        nextHint()
      }
    }

    const intervalId = setInterval(checkStruggle, STRUGGLE_CHECK_INTERVAL)

    return () => clearInterval(intervalId)
  }, [
    effectiveHotColdEnabled,
    hasMoreHints,
    currentPrompt,
    getSearchMetrics,
    nextHint,
    promptStartTime,
  ])

  // Reset hint level tracking when prompt changes
  useEffect(() => {
    lastHintLevelRef.current = 0
  }, [currentPrompt])
}