@sudobility/sudojo_lib 0.0.201 → 0.0.203
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/adminApi.d.ts +34 -0
- package/dist/admin/adminApi.d.ts.map +1 -0
- package/dist/admin/adminApi.js +59 -0
- package/dist/admin/adminApi.js.map +1 -0
- package/dist/admin/boardRules.d.ts +75 -0
- package/dist/admin/boardRules.d.ts.map +1 -0
- package/dist/admin/boardRules.js +127 -0
- package/dist/admin/boardRules.js.map +1 -0
- package/dist/admin/boardStrings.d.ts +55 -0
- package/dist/admin/boardStrings.d.ts.map +1 -0
- package/dist/admin/boardStrings.js +99 -0
- package/dist/admin/boardStrings.js.map +1 -0
- package/dist/admin/examples.d.ts +58 -0
- package/dist/admin/examples.d.ts.map +1 -0
- package/dist/admin/examples.js +104 -0
- package/dist/admin/examples.js.map +1 -0
- package/dist/admin/extraction.d.ts +87 -0
- package/dist/admin/extraction.d.ts.map +1 -0
- package/dist/admin/extraction.js +141 -0
- package/dist/admin/extraction.js.map +1 -0
- package/dist/admin/index.d.ts +32 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +22 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/admin/jobs.d.ts +108 -0
- package/dist/admin/jobs.d.ts.map +1 -0
- package/dist/admin/jobs.js +435 -0
- package/dist/admin/jobs.js.map +1 -0
- package/dist/admin/regenerateHints.d.ts +16 -0
- package/dist/admin/regenerateHints.d.ts.map +1 -0
- package/dist/admin/regenerateHints.js +43 -0
- package/dist/admin/regenerateHints.js.map +1 -0
- package/dist/admin/types.d.ts +42 -0
- package/dist/admin/types.d.ts.map +1 -0
- package/dist/admin/types.js +21 -0
- package/dist/admin/types.js.map +1 -0
- package/dist/admin/useAdminJob.d.ts +28 -0
- package/dist/admin/useAdminJob.d.ts.map +1 -0
- package/dist/admin/useAdminJob.js +99 -0
- package/dist/admin/useAdminJob.js.map +1 -0
- package/dist/admin/useAdminStats.d.ts +46 -0
- package/dist/admin/useAdminStats.d.ts.map +1 -0
- package/dist/admin/useAdminStats.js +91 -0
- package/dist/admin/useAdminStats.js.map +1 -0
- package/dist/admin/useBoardGenerator.d.ts +29 -0
- package/dist/admin/useBoardGenerator.d.ts.map +1 -0
- package/dist/admin/useBoardGenerator.js +38 -0
- package/dist/admin/useBoardGenerator.js.map +1 -0
- package/dist/admin/useBoardTechniquesUpdater.d.ts +14 -0
- package/dist/admin/useBoardTechniquesUpdater.d.ts.map +1 -0
- package/dist/admin/useBoardTechniquesUpdater.js +28 -0
- package/dist/admin/useBoardTechniquesUpdater.js.map +1 -0
- package/dist/admin/useExampleCreator.d.ts +29 -0
- package/dist/admin/useExampleCreator.d.ts.map +1 -0
- package/dist/admin/useExampleCreator.js +58 -0
- package/dist/admin/useExampleCreator.js.map +1 -0
- package/dist/admin/useSingleBoardExtractor.d.ts +18 -0
- package/dist/admin/useSingleBoardExtractor.d.ts.map +1 -0
- package/dist/admin/useSingleBoardExtractor.js +19 -0
- package/dist/admin/useSingleBoardExtractor.js.map +1 -0
- package/dist/admin/useTechniqueExtractor.d.ts +26 -0
- package/dist/admin/useTechniqueExtractor.d.ts.map +1 -0
- package/dist/admin/useTechniqueExtractor.js +37 -0
- package/dist/admin/useTechniqueExtractor.js.map +1 -0
- package/dist/context/SudojoApiContext.d.ts +58 -0
- package/dist/context/SudojoApiContext.d.ts.map +1 -0
- package/dist/context/SudojoApiContext.js +52 -0
- package/dist/context/SudojoApiContext.js.map +1 -0
- package/dist/context/SudojoApiProvider.d.ts +21 -0
- package/dist/context/SudojoApiProvider.d.ts.map +1 -0
- package/dist/context/SudojoApiProvider.js +15 -0
- package/dist/context/SudojoApiProvider.js.map +1 -0
- package/dist/context/index.d.ts +3 -0
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +2 -0
- package/dist/context/index.js.map +1 -1
- package/dist/hooks/index.d.ts +24 -1
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +22 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/useBoardEntry.d.ts +65 -5
- package/dist/hooks/useBoardEntry.d.ts.map +1 -1
- package/dist/hooks/useBoardEntry.js +124 -27
- package/dist/hooks/useBoardEntry.js.map +1 -1
- package/dist/hooks/useBoardScan.d.ts +27 -0
- package/dist/hooks/useBoardScan.d.ts.map +1 -0
- package/dist/hooks/useBoardScan.js +27 -0
- package/dist/hooks/useBoardScan.js.map +1 -0
- package/dist/hooks/useCommunities.d.ts +6 -4
- package/dist/hooks/useCommunities.d.ts.map +1 -1
- package/dist/hooks/useCommunities.js +6 -1
- package/dist/hooks/useCommunities.js.map +1 -1
- package/dist/hooks/useCompletionTrigger.d.ts +23 -0
- package/dist/hooks/useCompletionTrigger.d.ts.map +1 -0
- package/dist/hooks/useCompletionTrigger.js +39 -0
- package/dist/hooks/useCompletionTrigger.js.map +1 -0
- package/dist/hooks/useDailyGame.d.ts +11 -6
- package/dist/hooks/useDailyGame.d.ts.map +1 -1
- package/dist/hooks/useDailyGame.js +20 -61
- package/dist/hooks/useDailyGame.js.map +1 -1
- package/dist/hooks/useDeleteAccount.d.ts +72 -0
- package/dist/hooks/useDeleteAccount.d.ts.map +1 -0
- package/dist/hooks/useDeleteAccount.js +71 -0
- package/dist/hooks/useDeleteAccount.js.map +1 -0
- package/dist/hooks/useEnteredGameSession.d.ts +73 -0
- package/dist/hooks/useEnteredGameSession.d.ts.map +1 -0
- package/dist/hooks/useEnteredGameSession.js +155 -0
- package/dist/hooks/useEnteredGameSession.js.map +1 -0
- package/dist/hooks/useGameSession.d.ts +8 -4
- package/dist/hooks/useGameSession.d.ts.map +1 -1
- package/dist/hooks/useGameSession.js +4 -1
- package/dist/hooks/useGameSession.js.map +1 -1
- package/dist/hooks/useGameTeaching.d.ts.map +1 -1
- package/dist/hooks/useGameTeaching.js +11 -13
- package/dist/hooks/useGameTeaching.js.map +1 -1
- package/dist/hooks/useHint.d.ts +7 -7
- package/dist/hooks/useHint.d.ts.map +1 -1
- package/dist/hooks/useHint.js +18 -9
- package/dist/hooks/useHint.js.map +1 -1
- package/dist/hooks/useIsSiteAdmin.d.ts +39 -0
- package/dist/hooks/useIsSiteAdmin.d.ts.map +1 -0
- package/dist/hooks/useIsSiteAdmin.js +31 -0
- package/dist/hooks/useIsSiteAdmin.js.map +1 -0
- package/dist/hooks/useLearning.d.ts +8 -8
- package/dist/hooks/useLearning.d.ts.map +1 -1
- package/dist/hooks/useLearning.js +5 -2
- package/dist/hooks/useLearning.js.map +1 -1
- package/dist/hooks/useLevelGame.d.ts +7 -7
- package/dist/hooks/useLevelGame.d.ts.map +1 -1
- package/dist/hooks/useLevelGame.js +13 -49
- package/dist/hooks/useLevelGame.js.map +1 -1
- package/dist/hooks/useLevels.d.ts +8 -8
- package/dist/hooks/useLevels.d.ts.map +1 -1
- package/dist/hooks/useLevels.js +7 -3
- package/dist/hooks/useLevels.js.map +1 -1
- package/dist/hooks/usePracticeGame.d.ts +53 -0
- package/dist/hooks/usePracticeGame.d.ts.map +1 -0
- package/dist/hooks/usePracticeGame.js +72 -0
- package/dist/hooks/usePracticeGame.js.map +1 -0
- package/dist/hooks/usePuzzleDistribution.d.ts +2 -0
- package/dist/hooks/usePuzzleDistribution.d.ts.map +1 -1
- package/dist/hooks/usePuzzleDistribution.js +4 -14
- package/dist/hooks/usePuzzleDistribution.js.map +1 -1
- package/dist/hooks/usePuzzleProgress.d.ts +37 -0
- package/dist/hooks/usePuzzleProgress.d.ts.map +1 -0
- package/dist/hooks/usePuzzleProgress.js +48 -0
- package/dist/hooks/usePuzzleProgress.js.map +1 -0
- package/dist/hooks/usePuzzleSession.d.ts +215 -0
- package/dist/hooks/usePuzzleSession.d.ts.map +1 -0
- package/dist/hooks/usePuzzleSession.js +358 -0
- package/dist/hooks/usePuzzleSession.js.map +1 -0
- package/dist/hooks/useResumeGame.d.ts +70 -0
- package/dist/hooks/useResumeGame.d.ts.map +1 -0
- package/dist/hooks/useResumeGame.js +123 -0
- package/dist/hooks/useResumeGame.js.map +1 -0
- package/dist/hooks/useStrategies.d.ts +39 -0
- package/dist/hooks/useStrategies.d.ts.map +1 -0
- package/dist/hooks/useStrategies.js +34 -0
- package/dist/hooks/useStrategies.js.map +1 -0
- package/dist/hooks/useSudoku.d.ts +5 -0
- package/dist/hooks/useSudoku.d.ts.map +1 -1
- package/dist/hooks/useSudoku.js +7 -1
- package/dist/hooks/useSudoku.js.map +1 -1
- package/dist/hooks/useTechniqueByPath.d.ts +46 -0
- package/dist/hooks/useTechniqueByPath.d.ts.map +1 -0
- package/dist/hooks/useTechniqueByPath.js +47 -0
- package/dist/hooks/useTechniqueByPath.js.map +1 -0
- package/dist/hooks/useTechniqueExample.d.ts +60 -0
- package/dist/hooks/useTechniqueExample.d.ts.map +1 -0
- package/dist/hooks/useTechniqueExample.js +63 -0
- package/dist/hooks/useTechniqueExample.js.map +1 -0
- package/dist/hooks/useTechniques.d.ts +8 -8
- package/dist/hooks/useTechniques.d.ts.map +1 -1
- package/dist/hooks/useTechniques.js +9 -21
- package/dist/hooks/useTechniques.js.map +1 -1
- package/dist/index.d.ts +418 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +276 -10
- package/dist/index.js.map +1 -1
- package/dist/types/currentGame.d.ts +9 -1
- package/dist/types/currentGame.d.ts.map +1 -1
- package/dist/types/currentGame.js.map +1 -1
- package/dist/utils/auth.d.ts +6 -0
- package/dist/utils/auth.d.ts.map +1 -1
- package/dist/utils/auth.js +8 -0
- package/dist/utils/auth.js.map +1 -1
- package/dist/utils/community.d.ts +64 -0
- package/dist/utils/community.d.ts.map +1 -0
- package/dist/utils/community.js +66 -0
- package/dist/utils/community.js.map +1 -0
- package/dist/utils/date.d.ts +37 -0
- package/dist/utils/date.d.ts.map +1 -0
- package/dist/utils/date.js +68 -0
- package/dist/utils/date.js.map +1 -0
- package/dist/utils/entityTranslate.d.ts +120 -0
- package/dist/utils/entityTranslate.d.ts.map +1 -0
- package/dist/utils/entityTranslate.js +146 -0
- package/dist/utils/entityTranslate.js.map +1 -0
- package/dist/utils/gameFetchStatus.d.ts +74 -0
- package/dist/utils/gameFetchStatus.d.ts.map +1 -0
- package/dist/utils/gameFetchStatus.js +118 -0
- package/dist/utils/gameFetchStatus.js.map +1 -0
- package/dist/utils/gamification.d.ts +36 -0
- package/dist/utils/gamification.d.ts.map +1 -0
- package/dist/utils/gamification.js +33 -0
- package/dist/utils/gamification.js.map +1 -0
- package/dist/utils/hintAccess.d.ts +64 -0
- package/dist/utils/hintAccess.d.ts.map +1 -0
- package/dist/utils/hintAccess.js +57 -0
- package/dist/utils/hintAccess.js.map +1 -0
- package/dist/utils/index.d.ts +31 -10
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +20 -9
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/language.d.ts +123 -0
- package/dist/utils/language.d.ts.map +1 -0
- package/dist/utils/language.js +95 -0
- package/dist/utils/language.js.map +1 -0
- package/dist/utils/localizedHint.d.ts +60 -0
- package/dist/utils/localizedHint.d.ts.map +1 -1
- package/dist/utils/localizedHint.js +90 -0
- package/dist/utils/localizedHint.js.map +1 -1
- package/dist/utils/progress.d.ts +10 -1
- package/dist/utils/progress.d.ts.map +1 -1
- package/dist/utils/progress.js +24 -0
- package/dist/utils/progress.js.map +1 -1
- package/dist/utils/scannedBoard.d.ts +79 -0
- package/dist/utils/scannedBoard.d.ts.map +1 -0
- package/dist/utils/scannedBoard.js +96 -0
- package/dist/utils/scannedBoard.js.map +1 -0
- package/dist/utils/shareUrl.d.ts +38 -15
- package/dist/utils/shareUrl.d.ts.map +1 -1
- package/dist/utils/shareUrl.js +75 -26
- package/dist/utils/shareUrl.js.map +1 -1
- package/dist/utils/subscription.d.ts +118 -0
- package/dist/utils/subscription.d.ts.map +1 -1
- package/dist/utils/subscription.js +212 -0
- package/dist/utils/subscription.js.map +1 -1
- package/dist/utils/sudokuPresenter.d.ts +42 -0
- package/dist/utils/sudokuPresenter.d.ts.map +1 -1
- package/dist/utils/sudokuPresenter.js +114 -1
- package/dist/utils/sudokuPresenter.js.map +1 -1
- package/dist/utils/sudokuScrambler.d.ts +8 -0
- package/dist/utils/sudokuScrambler.d.ts.map +1 -1
- package/dist/utils/sudokuScrambler.js +12 -0
- package/dist/utils/sudokuScrambler.js.map +1 -1
- package/dist/utils/technique.d.ts +112 -1
- package/dist/utils/technique.d.ts.map +1 -1
- package/dist/utils/technique.js +293 -1
- package/dist/utils/technique.js.map +1 -1
- package/dist/utils/techniqueBitmask.d.ts +31 -0
- package/dist/utils/techniqueBitmask.d.ts.map +1 -0
- package/dist/utils/techniqueBitmask.js +53 -0
- package/dist/utils/techniqueBitmask.js.map +1 -0
- package/dist/utils/techniqueExample.d.ts +42 -0
- package/dist/utils/techniqueExample.d.ts.map +1 -0
- package/dist/utils/techniqueExample.js +57 -0
- package/dist/utils/techniqueExample.js.map +1 -0
- package/dist/utils/theme.d.ts +2 -0
- package/dist/utils/theme.d.ts.map +1 -1
- package/dist/utils/theme.js +2 -0
- package/dist/utils/theme.js.map +1 -1
- package/package.json +7 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGameSession.js","sourceRoot":"","sources":["../../src/hooks/useGameSession.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjE,OAAO,EACL,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"useGameSession.js","sourceRoot":"","sources":["../../src/hooks/useGameSession.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Hook for managing game sessions with gamification\n *\n * Handles:\n * - Starting game sessions on the server\n * - Finishing game sessions and receiving rewards\n * - Tracking achievement results for display\n */\n\nimport { useCallback, useEffect, useRef, useState } from 'react';\nimport {\n useSudojoPlayFinish,\n useSudojoPlayStart,\n} from '@sudobility/sudojo_client';\nimport type {\n GameFinishResponse,\n GameStartRequest,\n LocalizedHint,\n} from '@sudobility/sudojo_types';\nimport type { NetworkClient } from '@sudobility/types';\nimport { useResolvedSudojoApi } from '../context/SudojoApiContext';\n\nexport interface UseGameSessionOptions {\n /** Network client for API calls (default: SudojoApiProvider) */\n networkClient?: NetworkClient | undefined;\n /** Base URL for the Sudojo API (default: SudojoApiProvider) */\n baseUrl?: string | undefined;\n /** Access token; null = signed out (default: SudojoApiProvider) */\n token?: string | null | undefined;\n /** Signed-in (non-anonymous) user id, or null */\n userId: string | null;\n}\n\nexport interface GameSessionResult {\n /** Whether a server session was started */\n sessionStarted: boolean;\n /** Session ID from server (if started) */\n sessionId?: string;\n}\n\nexport interface GameFinishResult {\n /** The full response from the server */\n response?: GameFinishResponse;\n /** Whether the user leveled up */\n leveledUp: boolean;\n /** New badges earned */\n newBadges: Array<{\n badgeKey: string;\n title: string;\n description: string | null;\n localization?: {\n title?: LocalizedHint;\n description?: LocalizedHint;\n };\n }>;\n /** Total points earned */\n totalPointsEarned: number;\n}\n\nexport function useGameSession(options: UseGameSessionOptions) {\n const { userId } = options;\n const { networkClient, baseUrl, token } = useResolvedSudojoApi(\n options,\n 'useGameSession'\n );\n const [isStarting, setIsStarting] = useState(false);\n const [isFinishing, setIsFinishing] = useState(false);\n const sessionIdRef = useRef<string | null>(null);\n\n const playStartMutation = useSudojoPlayStart(networkClient, baseUrl);\n const playFinishMutation = useSudojoPlayFinish(networkClient, baseUrl);\n\n // Use refs for mutations so callbacks have stable identity\n const playStartRef = useRef(playStartMutation);\n const playFinishRef = useRef(playFinishMutation);\n const tokenRef = useRef(token);\n const userIdRef = useRef(userId);\n useEffect(() => {\n playStartRef.current = playStartMutation;\n playFinishRef.current = playFinishMutation;\n tokenRef.current = token;\n userIdRef.current = userId;\n });\n\n /**\n * Start a game session on the server\n * Only works if user is authenticated\n */\n const startSession = useCallback(\n async (data: GameStartRequest): Promise<GameSessionResult> => {\n // Skip if not authenticated\n if (!userIdRef.current || !tokenRef.current) {\n return { sessionStarted: false };\n }\n\n setIsStarting(true);\n try {\n const response = await playStartRef.current.mutateAsync({\n token: tokenRef.current,\n data,\n });\n\n if (response.success && response.data) {\n sessionIdRef.current = response.data.sessionId;\n return {\n sessionStarted: true,\n sessionId: response.data.sessionId,\n };\n }\n\n return { sessionStarted: false };\n } catch (error) {\n // Non-critical: gamification tracking only\n console.log('[game-session] Failed to start session:', error);\n return { sessionStarted: false };\n } finally {\n setIsStarting(false);\n }\n },\n []\n );\n\n /**\n * Finish the game session and get rewards\n * Only works if user is authenticated and session was started\n */\n const finishSession = useCallback(\n async (elapsedTime: number): Promise<GameFinishResult> => {\n // Skip if not authenticated\n if (!userIdRef.current || !tokenRef.current) {\n return {\n leveledUp: false,\n newBadges: [],\n totalPointsEarned: 0,\n };\n }\n\n setIsFinishing(true);\n try {\n const response = await playFinishRef.current.mutateAsync({\n token: tokenRef.current,\n data: { elapsedTime },\n });\n\n sessionIdRef.current = null;\n\n if (response.success && response.data) {\n return {\n response: response.data,\n leveledUp: !!response.data.level,\n newBadges: response.data.badges ?? [],\n totalPointsEarned: response.data.points.totalPoints,\n };\n }\n\n return {\n leveledUp: false,\n newBadges: [],\n totalPointsEarned: 0,\n };\n } catch (error) {\n console.error('Failed to finish game session:', error);\n return {\n leveledUp: false,\n newBadges: [],\n totalPointsEarned: 0,\n };\n } finally {\n setIsFinishing(false);\n }\n },\n []\n );\n\n /**\n * Check if user is authenticated (can use gamification)\n */\n const isAuthenticated = !!userId && !!token;\n\n return {\n startSession,\n finishSession,\n isStarting,\n isFinishing,\n isAuthenticated,\n sessionId: sessionIdRef.current,\n };\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGameTeaching.d.ts","sourceRoot":"","sources":["../../src/hooks/useGameTeaching.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAOvD,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"useGameTeaching.d.ts","sourceRoot":"","sources":["../../src/hooks/useGameTeaching.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAOvD,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGxD,MAAM,WAAW,sBAAsB;IACrC,mCAAmC;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,kCAAkC;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,sCAAsC;IACtC,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,6BAA6B;IAC7B,aAAa,EAAE,aAAa,CAAC;IAC7B,6CAA6C;IAC7C,OAAO,EAAE,CACP,cAAc,EAAE,MAAM,EACtB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,KAC1D,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,QAAQ,GAAG,IAAI,CAAC;IACjC,iDAAiD;IACjD,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,qDAAqD;IACrD,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,6BAA6B;IAC7B,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,kCAAkC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,uDAAuD;IACvD,YAAY,EAAE,OAAO,CAAC;IACtB,yCAAyC;IACzC,eAAe,EAAE,OAAO,CAAC;CAC1B;AAyCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,GAC9B,qBAAqB,CAkLvB"}
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
* Hook for integrating the Sudoku solver for teaching and hints
|
|
3
3
|
*/
|
|
4
4
|
import { useCallback, useMemo, useState } from 'react';
|
|
5
|
-
import {
|
|
5
|
+
import { useSolverSolveMutation } from '@sudobility/sudojo_client';
|
|
6
|
+
import { parseHintDigits } from '../utils/sudokuPresenter';
|
|
6
7
|
/**
|
|
7
8
|
* Converts a solver SolverHintStep to our GameHint format
|
|
8
9
|
*/
|
|
@@ -16,14 +17,10 @@ function convertHintStep(step) {
|
|
|
16
17
|
action.select = parseInt(cell.actions.select, 10);
|
|
17
18
|
}
|
|
18
19
|
if (cell.actions.add) {
|
|
19
|
-
action.addPencilmarks = cell.actions.add
|
|
20
|
-
.split('')
|
|
21
|
-
.map(d => parseInt(d, 10));
|
|
20
|
+
action.addPencilmarks = parseHintDigits(cell.actions.add);
|
|
22
21
|
}
|
|
23
22
|
if (cell.actions.remove) {
|
|
24
|
-
action.removePencilmarks = cell.actions.remove
|
|
25
|
-
.split('')
|
|
26
|
-
.map(d => parseInt(d, 10));
|
|
23
|
+
action.removePencilmarks = parseHintDigits(cell.actions.remove);
|
|
27
24
|
}
|
|
28
25
|
return {
|
|
29
26
|
row: cell.row,
|
|
@@ -104,10 +101,8 @@ export function useGameTeaching(options) {
|
|
|
104
101
|
});
|
|
105
102
|
// Store all hint steps for multi-step hints
|
|
106
103
|
const [allSteps, setAllSteps] = useState([]);
|
|
107
|
-
//
|
|
108
|
-
const
|
|
109
|
-
return createSudojoClient(networkClient, baseUrl);
|
|
110
|
-
}, [networkClient, baseUrl]);
|
|
104
|
+
// Solver requests go through sudojo_client's mutation hook
|
|
105
|
+
const { mutateAsync: solverSolve } = useSolverSolveMutation(networkClient, baseUrl);
|
|
111
106
|
const getHint = useCallback(async (originalPuzzle, userBoard, hintOptions) => {
|
|
112
107
|
setTeachingState(prev => ({
|
|
113
108
|
...prev,
|
|
@@ -125,7 +120,10 @@ export function useGameTeaching(options) {
|
|
|
125
120
|
if (hintOptions?.pencilmarks !== undefined) {
|
|
126
121
|
solveOptions.pencilmarks = hintOptions.pencilmarks;
|
|
127
122
|
}
|
|
128
|
-
const response = await
|
|
123
|
+
const response = await solverSolve({
|
|
124
|
+
token,
|
|
125
|
+
options: solveOptions,
|
|
126
|
+
});
|
|
129
127
|
if (!response.success || !response.data?.hints?.steps?.length) {
|
|
130
128
|
const errorMessage = response.error ?? 'No hints available for the current state';
|
|
131
129
|
setTeachingState(prev => ({
|
|
@@ -178,7 +176,7 @@ export function useGameTeaching(options) {
|
|
|
178
176
|
}));
|
|
179
177
|
setAllSteps([]);
|
|
180
178
|
}
|
|
181
|
-
}, [
|
|
179
|
+
}, [solverSolve, token]);
|
|
182
180
|
const applyHint = useCallback(() => {
|
|
183
181
|
return teachingState.currentHint;
|
|
184
182
|
}, [teachingState.currentHint]);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useGameTeaching.js","sourceRoot":"","sources":["../../src/hooks/useGameTeaching.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAOvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAqC/D;;GAEG;AACH,SAAS,eAAe,CAAC,IAAoB;IAC3C,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EACH,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE;YACrB,MAAM,MAAM,GAIR,EAAE,CAAC;YACP,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBACxB,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YACpD,CAAC;YACD,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;gBACrB,MAAM,CAAC,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG;qBACrC,KAAK,CAAC,EAAE,CAAC;qBACT,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC/B,CAAC;YACD,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;gBACxB,MAAM,CAAC,iBAAiB,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM;qBAC3C,KAAK,CAAC,EAAE,CAAC;qBACT,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;YAC/B,CAAC;YACD,OAAO;gBACL,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,MAAM,EAAE,IAAI,CAAC,MAAM;gBACnB,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACtD,CAAC;QACJ,CAAC,CAAC,IAAI,EAAE;QACV,KAAK,EACH,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACvB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,KAAK,EAAE,IAAI,CAAC,KAAK;SAClB,CAAC,CAAC,IAAI,EAAE;KACZ,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,MAAM,UAAU,eAAe,CAC7B,OAA+B;IAE/B,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAElD,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAgB;QAChE,QAAQ,EAAE,KAAK;QACf,WAAW,EAAE,IAAI;QACjB,WAAW,EAAE,CAAC;QACd,UAAU,EAAE,CAAC;QACb,SAAS,EAAE,KAAK;QAChB,KAAK,EAAE,IAAI;KACZ,CAAC,CAAC;IAEH,4CAA4C;IAC5C,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAa,EAAE,CAAC,CAAC;IAEzD,uBAAuB;IACvB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE;QAC1B,OAAO,kBAAkB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;IACpD,CAAC,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAC;IAE7B,MAAM,OAAO,GAAG,WAAW,CACzB,KAAK,EACH,cAAsB,EACtB,SAAiB,EACjB,WAAiE,EACjE,EAAE;QACF,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACxB,GAAG,IAAI;YACP,SAAS,EAAE,IAAI;YACf,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC,CAAC;QAEJ,IAAI,CAAC;YACH,MAAM,YAAY,GAKd;gBACF,QAAQ,EAAE,cAAc;gBACxB,IAAI,EAAE,SAAS;aAChB,CAAC;YACF,IAAI,WAAW,EAAE,eAAe,KAAK,SAAS,EAAE,CAAC;gBAC/C,YAAY,CAAC,eAAe,GAAG,WAAW,CAAC,eAAe,CAAC;YAC7D,CAAC;YACD,IAAI,WAAW,EAAE,WAAW,KAAK,SAAS,EAAE,CAAC;gBAC3C,YAAY,CAAC,WAAW,GAAG,WAAW,CAAC,WAAW,CAAC;YACrD,CAAC;YACD,MAAM,QAAQ,GAA4B,MAAM,MAAM,CAAC,WAAW,CAChE,KAAK,EACL,YAAY,CACb,CAAC;YAEF,IAAI,CAAC,QAAQ,CAAC,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;gBAC9D,MAAM,YAAY,GAChB,QAAQ,CAAC,KAAK,IAAI,0CAA0C,CAAC;gBAC/D,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBACxB,GAAG,IAAI;oBACP,SAAS,EAAE,KAAK;oBAChB,KAAK,EAAE,YAAY;oBACnB,QAAQ,EAAE,KAAK;oBACf,WAAW,EAAE,IAAI;oBACjB,WAAW,EAAE,CAAC;oBACd,UAAU,EAAE,CAAC;iBACd,CAAC,CAAC,CAAC;gBACJ,WAAW,CAAC,EAAE,CAAC,CAAC;gBAChB,OAAO;YACT,CAAC;YAED,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;YAC5C,MAAM,cAAc,GAAG,SAAS,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;YAEtD,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAChC,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;oBACxB,GAAG,IAAI;oBACP,SAAS,EAAE,KAAK;oBAChB,KAAK,EAAE,oBAAoB;oBAC3B,QAAQ,EAAE,KAAK;oBACf,WAAW,EAAE,IAAI;oBACjB,WAAW,EAAE,CAAC;oBACd,UAAU,EAAE,CAAC;iBACd,CAAC,CAAC,CAAC;gBACJ,WAAW,CAAC,EAAE,CAAC,CAAC;gBAChB,OAAO;YACT,CAAC;YAED,WAAW,CAAC,cAAc,CAAC,CAAC;YAC5B,gBAAgB,CAAC;gBACf,QAAQ,EAAE,IAAI;gBACd,WAAW,EAAE,cAAc,CAAC,CAAC,CAAC,IAAI,IAAI;gBACtC,WAAW,EAAE,CAAC;gBACd,UAAU,EAAE,cAAc,CAAC,MAAM;gBACjC,SAAS,EAAE,KAAK;gBAChB,KAAK,EAAE,IAAI;aACZ,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,YAAY,GAChB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC;YAC5D,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACxB,GAAG,IAAI;gBACP,SAAS,EAAE,KAAK;gBAChB,KAAK,EAAE,YAAY;gBACnB,QAAQ,EAAE,KAAK;gBACf,WAAW,EAAE,IAAI;gBACjB,WAAW,EAAE,CAAC;gBACd,UAAU,EAAE,CAAC;aACd,CAAC,CAAC,CAAC;YACJ,WAAW,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,EACD,CAAC,MAAM,EAAE,KAAK,CAAC,CAChB,CAAC;IAEF,MAAM,SAAS,GAAG,WAAW,CAAC,GAAoB,EAAE;QAClD,OAAO,aAAa,CAAC,WAAW,CAAC;IACnC,CAAC,EAAE,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC;IAEhC,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;QAChC,IAAI,aAAa,CAAC,WAAW,GAAG,aAAa,CAAC,UAAU,GAAG,CAAC,EAAE,CAAC;YAC7D,MAAM,aAAa,GAAG,aAAa,CAAC,WAAW,GAAG,CAAC,CAAC;YACpD,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACxB,GAAG,IAAI;gBACP,WAAW,EAAE,aAAa;gBAC1B,WAAW,EAAE,QAAQ,CAAC,aAAa,CAAC,IAAI,IAAI;aAC7C,CAAC,CAAC,CAAC;QACN,CAAC;IACH,CAAC,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,aAAa,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;IAEpE,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE;QACpC,IAAI,aAAa,CAAC,WAAW,GAAG,CAAC,EAAE,CAAC;YAClC,MAAM,aAAa,GAAG,aAAa,CAAC,WAAW,GAAG,CAAC,CAAC;YACpD,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACxB,GAAG,IAAI;gBACP,WAAW,EAAE,aAAa;gBAC1B,WAAW,EAAE,QAAQ,CAAC,aAAa,CAAC,IAAI,IAAI;aAC7C,CAAC,CAAC,CAAC;QACN,CAAC;IACH,CAAC,EAAE,CAAC,aAAa,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAC,CAAC;IAE1C,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE;QACjC,gBAAgB,CAAC;YACf,QAAQ,EAAE,KAAK;YACf,WAAW,EAAE,IAAI;YACjB,WAAW,EAAE,CAAC;YACd,UAAU,EAAE,CAAC;YACb,SAAS,EAAE,KAAK;YAChB,KAAK,EAAE,IAAI;SACZ,CAAC,CAAC;QACH,WAAW,CAAC,EAAE,CAAC,CAAC;IAClB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,OAAO,GAAG,OAAO,CACrB,GAAG,EAAE,CAAC,aAAa,CAAC,QAAQ,IAAI,aAAa,CAAC,WAAW,KAAK,IAAI,EAClE,CAAC,aAAa,CAAC,QAAQ,EAAE,aAAa,CAAC,WAAW,CAAC,CACpD,CAAC;IAEF,MAAM,YAAY,GAAG,OAAO,CAC1B,GAAG,EAAE,CAAC,aAAa,CAAC,WAAW,GAAG,aAAa,CAAC,UAAU,GAAG,CAAC,EAC9D,CAAC,aAAa,CAAC,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,CACtD,CAAC;IAEF,MAAM,eAAe,GAAG,OAAO,CAC7B,GAAG,EAAE,CAAC,aAAa,CAAC,WAAW,GAAG,CAAC,EACnC,CAAC,aAAa,CAAC,WAAW,CAAC,CAC5B,CAAC;IAEF,OAAO;QACL,aAAa;QACb,OAAO;QACP,SAAS;QACT,QAAQ;QACR,YAAY;QACZ,SAAS;QACT,OAAO;QACP,YAAY;QACZ,eAAe;KAChB,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Hook for integrating the Sudoku solver for teaching and hints\n */\n\nimport { useCallback, useMemo, useState } from 'react';\nimport type { NetworkClient } from '@sudobility/types';\nimport type {\n BaseResponse,\n SolveData,\n SolverHintStep,\n} from '@sudobility/sudojo_types';\nimport { createSudojoClient } from '@sudobility/sudojo_client';\nimport type { GameHint, TeachingState } from '../types';\n\nexport interface UseGameTeachingOptions {\n /** Network client for API calls */\n networkClient: NetworkClient;\n /** Base URL for the Sudojo API */\n baseUrl: string;\n /** Access token for authentication */\n token: string;\n}\n\nexport interface UseGameTeachingResult {\n /** Current teaching state */\n teachingState: TeachingState;\n /** Get a hint for the current board state */\n getHint: (\n originalPuzzle: string,\n userBoard: string,\n options?: { autoPencilmarks?: boolean; pencilmarks?: string }\n ) => Promise<void>;\n /** Apply the current hint to the board (returns the action to perform) */\n applyHint: () => GameHint | null;\n /** Move to the next step in a multi-step hint */\n nextStep: () => void;\n /** Move to the previous step in a multi-step hint */\n previousStep: () => void;\n /** Clear the current hint */\n clearHint: () => void;\n /** Whether a hint is available */\n hasHint: boolean;\n /** Whether there are more steps in the current hint */\n hasMoreSteps: boolean;\n /** Whether we can go to previous step */\n hasPreviousStep: boolean;\n}\n\n/**\n * Converts a solver SolverHintStep to our GameHint format\n */\nfunction convertHintStep(step: SolverHintStep): GameHint {\n return {\n title: step.title,\n text: step.text,\n cells:\n step.cells?.map(cell => {\n const action: {\n select?: number;\n addPencilmarks?: number[];\n removePencilmarks?: number[];\n } = {};\n if (cell.actions.select) {\n action.select = parseInt(cell.actions.select, 10);\n }\n if (cell.actions.add) {\n action.addPencilmarks = cell.actions.add\n .split('')\n .map(d => parseInt(d, 10));\n }\n if (cell.actions.remove) {\n action.removePencilmarks = cell.actions.remove\n .split('')\n .map(d => parseInt(d, 10));\n }\n return {\n row: cell.row,\n column: cell.column,\n color: cell.color,\n ...(Object.keys(action).length > 0 ? { action } : {}),\n };\n }) ?? [],\n areas:\n step.areas?.map(area => ({\n type: area.type,\n index: area.index,\n color: area.color,\n })) ?? [],\n };\n}\n\n/**\n * Hook for teaching/hint functionality using the solver API\n *\n * @param options - Hook options\n * @returns Teaching state and actions\n *\n * @example\n * ```tsx\n * function GameWithHints() {\n * const game = useGame();\n * const { teachingState, getHint, applyHint } = useGameTeaching({\n * networkClient,\n * baseUrl: 'https://api.sudojo.com',\n * token: 'user-access-token',\n * });\n *\n * const handleGetHint = async () => {\n * await getHint(\n * game.getOriginalPuzzle(),\n * game.getBoardString(),\n * { autoPencilmarks: game.state.settings.autoPencilmarks }\n * );\n * };\n *\n * const handleApplyHint = () => {\n * const hint = applyHint();\n * if (hint) {\n * // Apply the hint actions to the game\n * for (const cell of hint.cells) {\n * if (cell.action?.select) {\n * game.selectCell(cell.row, cell.column);\n * game.setValue(cell.action.select);\n * }\n * }\n * }\n * };\n *\n * return (\n * <div>\n * <Board cells={game.state.board} />\n * <button onClick={handleGetHint}>Get Hint</button>\n * {teachingState.currentHint && (\n * <div>\n * <h3>{teachingState.currentHint.title}</h3>\n * <p>{teachingState.currentHint.text}</p>\n * <button onClick={handleApplyHint}>Apply</button>\n * </div>\n * )}\n * </div>\n * );\n * }\n * ```\n */\nexport function useGameTeaching(\n options: UseGameTeachingOptions\n): UseGameTeachingResult {\n const { networkClient, baseUrl, token } = options;\n\n const [teachingState, setTeachingState] = useState<TeachingState>({\n isActive: false,\n currentHint: null,\n currentStep: 0,\n totalSteps: 0,\n isLoading: false,\n error: null,\n });\n\n // Store all hint steps for multi-step hints\n const [allSteps, setAllSteps] = useState<GameHint[]>([]);\n\n // Create Sudojo client\n const client = useMemo(() => {\n return createSudojoClient(networkClient, baseUrl);\n }, [networkClient, baseUrl]);\n\n const getHint = useCallback(\n async (\n originalPuzzle: string,\n userBoard: string,\n hintOptions?: { autoPencilmarks?: boolean; pencilmarks?: string }\n ) => {\n setTeachingState(prev => ({\n ...prev,\n isLoading: true,\n error: null,\n }));\n\n try {\n const solveOptions: {\n original: string;\n user: string;\n autoPencilmarks?: boolean;\n pencilmarks?: string;\n } = {\n original: originalPuzzle,\n user: userBoard,\n };\n if (hintOptions?.autoPencilmarks !== undefined) {\n solveOptions.autoPencilmarks = hintOptions.autoPencilmarks;\n }\n if (hintOptions?.pencilmarks !== undefined) {\n solveOptions.pencilmarks = hintOptions.pencilmarks;\n }\n const response: BaseResponse<SolveData> = await client.solverSolve(\n token,\n solveOptions\n );\n\n if (!response.success || !response.data?.hints?.steps?.length) {\n const errorMessage =\n response.error ?? 'No hints available for the current state';\n setTeachingState(prev => ({\n ...prev,\n isLoading: false,\n error: errorMessage,\n isActive: false,\n currentHint: null,\n currentStep: 0,\n totalSteps: 0,\n }));\n setAllSteps([]);\n return;\n }\n\n const hintSteps = response.data.hints.steps;\n const convertedSteps = hintSteps.map(convertHintStep);\n\n if (convertedSteps.length === 0) {\n setTeachingState(prev => ({\n ...prev,\n isLoading: false,\n error: 'No hints available',\n isActive: false,\n currentHint: null,\n currentStep: 0,\n totalSteps: 0,\n }));\n setAllSteps([]);\n return;\n }\n\n setAllSteps(convertedSteps);\n setTeachingState({\n isActive: true,\n currentHint: convertedSteps[0] ?? null,\n currentStep: 0,\n totalSteps: convertedSteps.length,\n isLoading: false,\n error: null,\n });\n } catch (err) {\n const errorMessage =\n err instanceof Error ? err.message : 'Failed to get hint';\n setTeachingState(prev => ({\n ...prev,\n isLoading: false,\n error: errorMessage,\n isActive: false,\n currentHint: null,\n currentStep: 0,\n totalSteps: 0,\n }));\n setAllSteps([]);\n }\n },\n [client, token]\n );\n\n const applyHint = useCallback((): GameHint | null => {\n return teachingState.currentHint;\n }, [teachingState.currentHint]);\n\n const nextStep = useCallback(() => {\n if (teachingState.currentStep < teachingState.totalSteps - 1) {\n const nextStepIndex = teachingState.currentStep + 1;\n setTeachingState(prev => ({\n ...prev,\n currentStep: nextStepIndex,\n currentHint: allSteps[nextStepIndex] ?? null,\n }));\n }\n }, [teachingState.currentStep, teachingState.totalSteps, allSteps]);\n\n const previousStep = useCallback(() => {\n if (teachingState.currentStep > 0) {\n const prevStepIndex = teachingState.currentStep - 1;\n setTeachingState(prev => ({\n ...prev,\n currentStep: prevStepIndex,\n currentHint: allSteps[prevStepIndex] ?? null,\n }));\n }\n }, [teachingState.currentStep, allSteps]);\n\n const clearHint = useCallback(() => {\n setTeachingState({\n isActive: false,\n currentHint: null,\n currentStep: 0,\n totalSteps: 0,\n isLoading: false,\n error: null,\n });\n setAllSteps([]);\n }, []);\n\n const hasHint = useMemo(\n () => teachingState.isActive && teachingState.currentHint !== null,\n [teachingState.isActive, teachingState.currentHint]\n );\n\n const hasMoreSteps = useMemo(\n () => teachingState.currentStep < teachingState.totalSteps - 1,\n [teachingState.currentStep, teachingState.totalSteps]\n );\n\n const hasPreviousStep = useMemo(\n () => teachingState.currentStep > 0,\n [teachingState.currentStep]\n );\n\n return {\n teachingState,\n getHint,\n applyHint,\n nextStep,\n previousStep,\n clearHint,\n hasHint,\n hasMoreSteps,\n hasPreviousStep,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"useGameTeaching.js","sourceRoot":"","sources":["../../src/hooks/useGameTeaching.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Hook for integrating the Sudoku solver for teaching and hints\n */\n\nimport { useCallback, useMemo, useState } from 'react';\nimport type { NetworkClient } from '@sudobility/types';\nimport type {\n BaseResponse,\n SolveData,\n SolverHintStep,\n} from '@sudobility/sudojo_types';\nimport { useSolverSolveMutation } from '@sudobility/sudojo_client';\nimport type { GameHint, TeachingState } from '../types';\nimport { parseHintDigits } from '../utils/sudokuPresenter';\n\nexport interface UseGameTeachingOptions {\n /** Network client for API calls */\n networkClient: NetworkClient;\n /** Base URL for the Sudojo API */\n baseUrl: string;\n /** Access token for authentication */\n token: string;\n}\n\nexport interface UseGameTeachingResult {\n /** Current teaching state */\n teachingState: TeachingState;\n /** Get a hint for the current board state */\n getHint: (\n originalPuzzle: string,\n userBoard: string,\n options?: { autoPencilmarks?: boolean; pencilmarks?: string }\n ) => Promise<void>;\n /** Apply the current hint to the board (returns the action to perform) */\n applyHint: () => GameHint | null;\n /** Move to the next step in a multi-step hint */\n nextStep: () => void;\n /** Move to the previous step in a multi-step hint */\n previousStep: () => void;\n /** Clear the current hint */\n clearHint: () => void;\n /** Whether a hint is available */\n hasHint: boolean;\n /** Whether there are more steps in the current hint */\n hasMoreSteps: boolean;\n /** Whether we can go to previous step */\n hasPreviousStep: boolean;\n}\n\n/**\n * Converts a solver SolverHintStep to our GameHint format\n */\nfunction convertHintStep(step: SolverHintStep): GameHint {\n return {\n title: step.title,\n text: step.text,\n cells:\n step.cells?.map(cell => {\n const action: {\n select?: number;\n addPencilmarks?: number[];\n removePencilmarks?: number[];\n } = {};\n if (cell.actions.select) {\n action.select = parseInt(cell.actions.select, 10);\n }\n if (cell.actions.add) {\n action.addPencilmarks = parseHintDigits(cell.actions.add);\n }\n if (cell.actions.remove) {\n action.removePencilmarks = parseHintDigits(cell.actions.remove);\n }\n return {\n row: cell.row,\n column: cell.column,\n color: cell.color,\n ...(Object.keys(action).length > 0 ? { action } : {}),\n };\n }) ?? [],\n areas:\n step.areas?.map(area => ({\n type: area.type,\n index: area.index,\n color: area.color,\n })) ?? [],\n };\n}\n\n/**\n * Hook for teaching/hint functionality using the solver API\n *\n * @param options - Hook options\n * @returns Teaching state and actions\n *\n * @example\n * ```tsx\n * function GameWithHints() {\n * const game = useGame();\n * const { teachingState, getHint, applyHint } = useGameTeaching({\n * networkClient,\n * baseUrl: 'https://api.sudojo.com',\n * token: 'user-access-token',\n * });\n *\n * const handleGetHint = async () => {\n * await getHint(\n * game.getOriginalPuzzle(),\n * game.getBoardString(),\n * { autoPencilmarks: game.state.settings.autoPencilmarks }\n * );\n * };\n *\n * const handleApplyHint = () => {\n * const hint = applyHint();\n * if (hint) {\n * // Apply the hint actions to the game\n * for (const cell of hint.cells) {\n * if (cell.action?.select) {\n * game.selectCell(cell.row, cell.column);\n * game.setValue(cell.action.select);\n * }\n * }\n * }\n * };\n *\n * return (\n * <div>\n * <Board cells={game.state.board} />\n * <button onClick={handleGetHint}>Get Hint</button>\n * {teachingState.currentHint && (\n * <div>\n * <h3>{teachingState.currentHint.title}</h3>\n * <p>{teachingState.currentHint.text}</p>\n * <button onClick={handleApplyHint}>Apply</button>\n * </div>\n * )}\n * </div>\n * );\n * }\n * ```\n */\nexport function useGameTeaching(\n options: UseGameTeachingOptions\n): UseGameTeachingResult {\n const { networkClient, baseUrl, token } = options;\n\n const [teachingState, setTeachingState] = useState<TeachingState>({\n isActive: false,\n currentHint: null,\n currentStep: 0,\n totalSteps: 0,\n isLoading: false,\n error: null,\n });\n\n // Store all hint steps for multi-step hints\n const [allSteps, setAllSteps] = useState<GameHint[]>([]);\n\n // Solver requests go through sudojo_client's mutation hook\n const { mutateAsync: solverSolve } = useSolverSolveMutation(\n networkClient,\n baseUrl\n );\n\n const getHint = useCallback(\n async (\n originalPuzzle: string,\n userBoard: string,\n hintOptions?: { autoPencilmarks?: boolean; pencilmarks?: string }\n ) => {\n setTeachingState(prev => ({\n ...prev,\n isLoading: true,\n error: null,\n }));\n\n try {\n const solveOptions: {\n original: string;\n user: string;\n autoPencilmarks?: boolean;\n pencilmarks?: string;\n } = {\n original: originalPuzzle,\n user: userBoard,\n };\n if (hintOptions?.autoPencilmarks !== undefined) {\n solveOptions.autoPencilmarks = hintOptions.autoPencilmarks;\n }\n if (hintOptions?.pencilmarks !== undefined) {\n solveOptions.pencilmarks = hintOptions.pencilmarks;\n }\n const response: BaseResponse<SolveData> = await solverSolve({\n token,\n options: solveOptions,\n });\n\n if (!response.success || !response.data?.hints?.steps?.length) {\n const errorMessage =\n response.error ?? 'No hints available for the current state';\n setTeachingState(prev => ({\n ...prev,\n isLoading: false,\n error: errorMessage,\n isActive: false,\n currentHint: null,\n currentStep: 0,\n totalSteps: 0,\n }));\n setAllSteps([]);\n return;\n }\n\n const hintSteps = response.data.hints.steps;\n const convertedSteps = hintSteps.map(convertHintStep);\n\n if (convertedSteps.length === 0) {\n setTeachingState(prev => ({\n ...prev,\n isLoading: false,\n error: 'No hints available',\n isActive: false,\n currentHint: null,\n currentStep: 0,\n totalSteps: 0,\n }));\n setAllSteps([]);\n return;\n }\n\n setAllSteps(convertedSteps);\n setTeachingState({\n isActive: true,\n currentHint: convertedSteps[0] ?? null,\n currentStep: 0,\n totalSteps: convertedSteps.length,\n isLoading: false,\n error: null,\n });\n } catch (err) {\n const errorMessage =\n err instanceof Error ? err.message : 'Failed to get hint';\n setTeachingState(prev => ({\n ...prev,\n isLoading: false,\n error: errorMessage,\n isActive: false,\n currentHint: null,\n currentStep: 0,\n totalSteps: 0,\n }));\n setAllSteps([]);\n }\n },\n [solverSolve, token]\n );\n\n const applyHint = useCallback((): GameHint | null => {\n return teachingState.currentHint;\n }, [teachingState.currentHint]);\n\n const nextStep = useCallback(() => {\n if (teachingState.currentStep < teachingState.totalSteps - 1) {\n const nextStepIndex = teachingState.currentStep + 1;\n setTeachingState(prev => ({\n ...prev,\n currentStep: nextStepIndex,\n currentHint: allSteps[nextStepIndex] ?? null,\n }));\n }\n }, [teachingState.currentStep, teachingState.totalSteps, allSteps]);\n\n const previousStep = useCallback(() => {\n if (teachingState.currentStep > 0) {\n const prevStepIndex = teachingState.currentStep - 1;\n setTeachingState(prev => ({\n ...prev,\n currentStep: prevStepIndex,\n currentHint: allSteps[prevStepIndex] ?? null,\n }));\n }\n }, [teachingState.currentStep, allSteps]);\n\n const clearHint = useCallback(() => {\n setTeachingState({\n isActive: false,\n currentHint: null,\n currentStep: 0,\n totalSteps: 0,\n isLoading: false,\n error: null,\n });\n setAllSteps([]);\n }, []);\n\n const hasHint = useMemo(\n () => teachingState.isActive && teachingState.currentHint !== null,\n [teachingState.isActive, teachingState.currentHint]\n );\n\n const hasMoreSteps = useMemo(\n () => teachingState.currentStep < teachingState.totalSteps - 1,\n [teachingState.currentStep, teachingState.totalSteps]\n );\n\n const hasPreviousStep = useMemo(\n () => teachingState.currentStep > 0,\n [teachingState.currentStep]\n );\n\n return {\n teachingState,\n getHint,\n applyHint,\n nextStep,\n previousStep,\n clearHint,\n hasHint,\n hasMoreSteps,\n hasPreviousStep,\n };\n}\n"]}
|
package/dist/hooks/useHint.d.ts
CHANGED
|
@@ -42,12 +42,12 @@ export interface HintAccessError {
|
|
|
42
42
|
userState: HintAccessUserState;
|
|
43
43
|
}
|
|
44
44
|
export interface UseHintOptions {
|
|
45
|
-
/** Network client for API calls */
|
|
46
|
-
networkClient
|
|
47
|
-
/** Base URL for the Sudojo API */
|
|
48
|
-
baseUrl
|
|
49
|
-
/** Access token for authentication */
|
|
50
|
-
token
|
|
45
|
+
/** Network client for API calls (default: SudojoApiProvider) */
|
|
46
|
+
networkClient?: NetworkClient | undefined;
|
|
47
|
+
/** Base URL for the Sudojo API (default: SudojoApiProvider) */
|
|
48
|
+
baseUrl?: string | undefined;
|
|
49
|
+
/** Access token for authentication (default: SudojoApiProvider) */
|
|
50
|
+
token?: string | null | undefined;
|
|
51
51
|
/** Original puzzle string (81 chars) */
|
|
52
52
|
puzzle: string;
|
|
53
53
|
/** Current user input string (81 chars) */
|
|
@@ -155,5 +155,5 @@ export interface UseHintResult {
|
|
|
155
155
|
* };
|
|
156
156
|
* ```
|
|
157
157
|
*/
|
|
158
|
-
export declare function useHint(
|
|
158
|
+
export declare function useHint(options: UseHintOptions): UseHintResult;
|
|
159
159
|
//# sourceMappingURL=useHint.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHint.d.ts","sourceRoot":"","sources":["../../src/hooks/useHint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAEV,mBAAmB,EACnB,eAAe,EAGf,cAAc,EACf,MAAM,0BAA0B,CAAC;AASlC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"useHint.d.ts","sourceRoot":"","sources":["../../src/hooks/useHint.ts"],"names":[],"mappings":"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"}
|
package/dist/hooks/useHint.js
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { useCallback, useMemo, useRef, useState } from 'react';
|
|
12
12
|
import { hasRequiredEntitlement, parseEntitlements, } from '@sudobility/sudojo_types';
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
import { HintAccessDeniedError, useSolverSolveMutation, } from '@sudobility/sudojo_client';
|
|
14
|
+
import { useResolvedSudojoApi } from '../context/SudojoApiContext';
|
|
15
|
+
import { FREE_HINT_STEP_LIMIT } from '../utils/hintAccess';
|
|
16
16
|
/**
|
|
17
17
|
* Hook for getting hints from the solver API
|
|
18
18
|
*
|
|
@@ -52,7 +52,12 @@ const FREE_HINT_STEP_LIMIT = 2;
|
|
|
52
52
|
* };
|
|
53
53
|
* ```
|
|
54
54
|
*/
|
|
55
|
-
export function useHint(
|
|
55
|
+
export function useHint(options) {
|
|
56
|
+
const { puzzle, userInput, pencilmarks, autoPencilmarks = false, techniqueFilter, onHintReceived, userEntitlements, levelEntitlement, } = options;
|
|
57
|
+
const { networkClient, baseUrl, token } = useResolvedSudojoApi(options, 'useHint');
|
|
58
|
+
// Solver requests go through sudojo_client's mutation hook; it rejects
|
|
59
|
+
// with HintAccessDeniedError on a 402, like SudojoClient.solverSolve.
|
|
60
|
+
const { mutateAsync: solverSolve } = useSolverSolveMutation(networkClient, baseUrl);
|
|
56
61
|
const [hints, setHints] = useState(null);
|
|
57
62
|
const [stepIndex, setStepIndex] = useState(0);
|
|
58
63
|
const [isLoading, setIsLoading] = useState(false);
|
|
@@ -141,7 +146,6 @@ export function useHint({ networkClient, baseUrl, token, puzzle, userInput, penc
|
|
|
141
146
|
setAccessError(null);
|
|
142
147
|
setIsTargetTechnique(false);
|
|
143
148
|
try {
|
|
144
|
-
const client = createSudojoClient(networkClient, baseUrl);
|
|
145
149
|
// Phase 1: If techniqueFilter is set, first try to find that specific technique
|
|
146
150
|
if (techniqueFilter !== undefined) {
|
|
147
151
|
const filteredOptions = {
|
|
@@ -151,7 +155,10 @@ export function useHint({ networkClient, baseUrl, token, puzzle, userInput, penc
|
|
|
151
155
|
...(pencilmarks !== undefined && { pencilmarks }),
|
|
152
156
|
techniques: techniqueFilter.toString(),
|
|
153
157
|
};
|
|
154
|
-
const filteredResponse = await
|
|
158
|
+
const filteredResponse = await solverSolve({
|
|
159
|
+
token,
|
|
160
|
+
options: filteredOptions,
|
|
161
|
+
});
|
|
155
162
|
// If we found hints for the target technique, use them
|
|
156
163
|
if (processHintResponse(filteredResponse, true)) {
|
|
157
164
|
setIsLoading(false);
|
|
@@ -166,7 +173,10 @@ export function useHint({ networkClient, baseUrl, token, puzzle, userInput, penc
|
|
|
166
173
|
autoPencilmarks,
|
|
167
174
|
...(pencilmarks !== undefined && { pencilmarks }),
|
|
168
175
|
};
|
|
169
|
-
const response = await
|
|
176
|
+
const response = await solverSolve({
|
|
177
|
+
token,
|
|
178
|
+
options: solveOptions,
|
|
179
|
+
});
|
|
170
180
|
if (processHintResponse(response, false)) {
|
|
171
181
|
// Successfully got hints (not the target technique)
|
|
172
182
|
}
|
|
@@ -201,8 +211,7 @@ export function useHint({ networkClient, baseUrl, token, puzzle, userInput, penc
|
|
|
201
211
|
setIsLoading(false);
|
|
202
212
|
}
|
|
203
213
|
}, [
|
|
204
|
-
|
|
205
|
-
baseUrl,
|
|
214
|
+
solverSolve,
|
|
206
215
|
token,
|
|
207
216
|
puzzle,
|
|
208
217
|
userInput,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHint.js","sourceRoot":"","sources":["../../src/hooks/useHint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAS/D,OAAO,EACL,sBAAsB,EACtB,iBAAiB,GAClB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,kBAAkB,EAClB,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AAGnC,yDAAyD;AACzD,MAAM,oBAAoB,GAAG,CAAC,CAAC;AAiH/B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,MAAM,UAAU,OAAO,CAAC,EACtB,aAAa,EACb,OAAO,EACP,KAAK,EACL,MAAM,EACN,SAAS,EACT,WAAW,EACX,eAAe,GAAG,KAAK,EACvB,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,gBAAgB,GACD;IACf,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAA0B,IAAI,CAAC,CAAC;IAClE,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC9C,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAClD,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACxD,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAyB,IAAI,CAAC,CAAC;IAC7E,MAAM,CAAC,iBAAiB,EAAE,oBAAoB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAElE,wDAAwD;IACxD,MAAM,kBAAkB,GAAG,MAAM,CAAS,EAAE,CAAC,CAAC;IAE9C,uDAAuD;IACvD,MAAM,YAAY,GAAG,MAAM,CAAqB,IAAI,CAAC,CAAC;IAEtD,sEAAsE;IACtE,MAAM,YAAY,GAAG,MAAM,CAAS,CAAC,CAAC,CAAC;IAEvC,iFAAiF;IACjF,MAAM,SAAS,GAAG,OAAO,CACvB,GAAG,EAAE,CAAC,sBAAsB,CAAC,gBAAgB,EAAE,gBAAgB,IAAI,EAAE,CAAC,EACtE,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CACrC,CAAC;IAEF,4BAA4B;IAC5B,MAAM,IAAI,GAAG,KAAK,EAAE,CAAC,SAAS,CAAC,IAAI,IAAI,CAAC;IACxC,MAAM,UAAU,GAAG,KAAK,EAAE,MAAM,IAAI,CAAC,CAAC;IACtC,MAAM,WAAW,GAAG,CAAC,SAAS,IAAI,UAAU,GAAG,oBAAoB,CAAC;IACpE,MAAM,cAAc,GAAG,SAAS,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,oBAAoB,GAAG,CAAC,CAAC;IAC7E,MAAM,WAAW,GAAG,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,GAAG,CAAC,EAAE,cAAc,CAAC,CAAC;IACzE,MAAM,eAAe,GAAG,SAAS,GAAG,CAAC,CAAC;IACtC,iFAAiF;IACjF,MAAM,QAAQ,GACZ,KAAK,KAAK,IAAI;QACd,SAAS,KAAK,UAAU,GAAG,CAAC;QAC5B,SAAS;QACT,YAAY,CAAC,OAAO,KAAK,IAAI,CAAC;IAEhC,+CAA+C;IAC/C,MAAM,mBAAmB,GAAG,WAAW,CACrC,CAAC,QAAiC,EAAE,QAAiB,EAAW,EAAE;QAChE,IAAI,QAAQ,CAAC,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;YAC5D,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;YAC5C,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC;YAE1C,sEAAsE;YACtE,IACE,cAAc;gBACd,SAAS,CAAC,CAAC,CAAC;gBACZ,KAAK,EAAE,IAAI,KAAK,IAAI;gBACpB,KAAK,EAAE,IAAI,KAAK,SAAS,EACzB,CAAC;gBACD,cAAc,CAAC;oBACb,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC;oBAClB,KAAK,EAAE,SAAS;oBAChB,SAAS,EAAE;wBACT,IAAI,EAAE,KAAK,CAAC,IAAI;wBAChB,WAAW,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO,IAAI,IAAI;wBAC9C,eAAe,EAAE,KAAK,CAAC,UAAU,EAAE,UAAU,IAAI,KAAK;qBACvD;oBACD,SAAS,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK;oBACpC,aAAa,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS;iBAC7C,CAAC,CAAC;YACL,CAAC;YAED,QAAQ,CAAC,SAAS,CAAC,CAAC;YACpB,YAAY,CAAC,CAAC,CAAC,CAAC;YAChB,oBAAoB,CAAC,QAAQ,CAAC,CAAC;YAC/B,2CAA2C;YAC3C,YAAY,CAAC,OAAO,GAAG,KAAK,CAAC;YAC7B,4CAA4C;YAC5C,YAAY,CAAC,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;YACjD,wCAAwC;YACxC,kBAAkB,CAAC,OAAO,GAAG,GAAG,MAAM,IAAI,SAAS,IAAI,WAAW,IAAI,EAAE,EAAE,CAAC;YAE3E,kFAAkF;YAClF,+DAA+D;YAC/D,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,oBAAoB,EAAE,CAAC;gBAC1D,MAAM,QAAQ,GAAG,iBAAiB,CAAC,gBAAgB,CAAC,CAAC;gBACrD,cAAc,CAAC;oBACb,SAAS,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK;oBACpC,mBAAmB,EAAE,QAAQ,CAAC,CAAC,CAAC,IAAI,WAAW;oBAC/C,SAAS,EACP,CAAC,gBAAgB,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC;wBACjC,CAAC,CAAC,mBAAmB;wBACrB,CAAC,CAAC,iBAAiB;iBACxB,CAAC,CAAC;YACL,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,EACD;QACE,cAAc;QACd,MAAM;QACN,SAAS;QACT,WAAW;QACX,SAAS;QACT,gBAAgB;QAChB,gBAAgB;KACjB,CACF,CAAC;IAEF,uBAAuB;IACvB,MAAM,UAAU,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACxC,YAAY,CAAC,IAAI,CAAC,CAAC;QACnB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,cAAc,CAAC,IAAI,CAAC,CAAC;QACrB,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAE5B,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,kBAAkB,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;YAE1D,gFAAgF;YAChF,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;gBAClC,MAAM,eAAe,GAAG;oBACtB,QAAQ,EAAE,MAAM;oBAChB,IAAI,EAAE,SAAS;oBACf,eAAe;oBACf,GAAG,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,CAAC;oBACjD,UAAU,EAAE,eAAe,CAAC,QAAQ,EAAE;iBACvC,CAAC;gBAEF,MAAM,gBAAgB,GAAG,MAAM,MAAM,CAAC,WAAW,CAC/C,KAAK,EACL,eAAe,CAChB,CAAC;gBAEF,uDAAuD;gBACvD,IAAI,mBAAmB,CAAC,gBAAgB,EAAE,IAAI,CAAC,EAAE,CAAC;oBAChD,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,OAAO;gBACT,CAAC;gBACD,yDAAyD;YAC3D,CAAC;YAED,gEAAgE;YAChE,MAAM,YAAY,GAAG;gBACnB,QAAQ,EAAE,MAAM;gBAChB,IAAI,EAAE,SAAS;gBACf,eAAe;gBACf,GAAG,CAAC,WAAW,KAAK,SAAS,IAAI,EAAE,WAAW,EAAE,CAAC;aAClD,CAAC;YACF,MAAM,QAAQ,GAA4B,MAAM,MAAM,CAAC,WAAW,CAChE,KAAK,EACL,YAAY,CACb,CAAC;YAEF,IAAI,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC;gBACzC,oDAAoD;YACtD,CAAC;iBAAM,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;gBAC1B,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;gBACzB,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACf,YAAY,CAAC,OAAO,GAAG,IAAI,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,oBAAoB,CAAC,CAAC;gBAC/B,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACf,YAAY,CAAC,OAAO,GAAG,IAAI,CAAC;YAC9B,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,wCAAwC;YACxC,IAAI,qBAAqB,CAAC,uBAAuB,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvD,cAAc,CAAC;oBACb,SAAS,EAAE,GAAG,CAAC,SAAS;oBACxB,mBAAmB,EAAE,GAAG,CAAC,mBAAmB;oBAC5C,SAAS,EAAE,GAAG,CAAC,SAAS;iBACzB,CAAC,CAAC;gBACH,QAAQ,CAAC,IAAI,CAAC,CAAC;YACjB,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC;YACtE,CAAC;YACD,QAAQ,CAAC,IAAI,CAAC,CAAC;YACf,YAAY,CAAC,OAAO,GAAG,IAAI,CAAC;QAC9B,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;IACH,CAAC,EAAE;QACD,aAAa;QACb,OAAO;QACP,KAAK;QACL,MAAM;QACN,SAAS;QACT,WAAW;QACX,eAAe;QACf,eAAe;QACf,mBAAmB;KACpB,CAAC,CAAC;IAEH,mEAAmE;IACnE,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACrC,MAAM,kBAAkB,GAAG,GAAG,MAAM,IAAI,SAAS,IAAI,WAAW,IAAI,EAAE,EAAE,CAAC;QAEzE,IAAI,KAAK,KAAK,IAAI,IAAI,kBAAkB,CAAC,OAAO,KAAK,kBAAkB,EAAE,CAAC;YACxE,qDAAqD;YACrD,MAAM,UAAU,EAAE,CAAC;QACrB,CAAC;aAAM,IAAI,WAAW,EAAE,CAAC;YACvB,8CAA8C;YAC9C,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QACjC,CAAC;QACD,kEAAkE;IACpE,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;IAErE,wBAAwB;IACxB,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;QAChC,IAAI,WAAW,EAAE,CAAC;YAChB,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QACjC,CAAC;IACH,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC;IAElB,4BAA4B;IAC5B,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE;QACpC,IAAI,eAAe,EAAE,CAAC;YACpB,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;QACjC,CAAC;IACH,CAAC,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;IAEtB,kBAAkB;IAClB,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE;QACjC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,YAAY,CAAC,CAAC,CAAC,CAAC;QAChB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,cAAc,CAAC,IAAI,CAAC,CAAC;QACrB,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC5B,kBAAkB,CAAC,OAAO,GAAG,EAAE,CAAC;QAChC,YAAY,CAAC,OAAO,GAAG,IAAI,CAAC;IAC9B,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,mEAAmE;IACnE,MAAM,SAAS,GAAG,WAAW,CAAC,GAAyB,EAAE;QACvD,MAAM,KAAK,GAAG,YAAY,CAAC,OAAO,CAAC;QACnC,sEAAsE;QACtE,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC9D,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,MAAM,GAAkB;YAC5B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,WAAW,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO,IAAI,IAAI;YAC9C,eAAe,EAAE,KAAK,CAAC,UAAU,EAAE,UAAU,IAAI,KAAK;SACvD,CAAC;QAEF,6DAA6D;QAC7D,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,YAAY,CAAC,CAAC,CAAC,CAAC;QAChB,QAAQ,CAAC,IAAI,CAAC,CAAC;QACf,kBAAkB,CAAC,OAAO,GAAG,EAAE,CAAC;QAChC,YAAY,CAAC,OAAO,GAAG,IAAI,CAAC;QAE5B,OAAO,MAAM,CAAC;IAChB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO;QACL,IAAI;QACJ,KAAK;QACL,SAAS;QACT,UAAU;QACV,SAAS;QACT,KAAK;QACL,WAAW;QACX,OAAO;QACP,QAAQ;QACR,YAAY;QACZ,SAAS;QACT,SAAS;QACT,WAAW;QACX,eAAe;QACf,QAAQ;QACR,iBAAiB;QACjB,WAAW;KACZ,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Hook for getting hints from the Sudoku solver\n *\n * Matches Kotlin HintInteractor behavior:\n * - Stores all hint steps from the API response\n * - Tracks current step index\n * - next() advances to next step (if available)\n * - previous() goes back to previous step\n * - getHint() fetches new hints OR advances to next step if hints exist\n */\n\nimport { useCallback, useMemo, useRef, useState } from 'react';\nimport type {\n BaseResponse,\n HintAccessUserState,\n HintEntitlement,\n SolveData,\n SolverBoard,\n SolverHintStep,\n} from '@sudobility/sudojo_types';\nimport {\n hasRequiredEntitlement,\n parseEntitlements,\n} from '@sudobility/sudojo_types';\nimport {\n createSudojoClient,\n HintAccessDeniedError,\n} from '@sudobility/sudojo_client';\nimport type { NetworkClient } from '@sudobility/types';\n\n/** Number of hint steps shown for free before paywall */\nconst FREE_HINT_STEP_LIMIT = 2;\n\n/** Board data returned when applying a hint */\nexport interface HintBoardData {\n /** 81-character user input string */\n user: string;\n /** Pencilmarks string (comma-separated) */\n pencilmarks: string | null;\n /** Whether auto-pencilmarks are enabled */\n autoPencilmarks: boolean;\n}\n\n/** Data passed to onHintReceived callback */\nexport interface HintReceivedData {\n /** The first hint step */\n hint: SolverHintStep;\n /** All hint steps */\n hints: SolverHintStep[];\n /** Board data that will be applied */\n boardData: HintBoardData;\n /** Difficulty level for this hint (from SolverHints.level) */\n hintLevel: number;\n /** Technique ID for this hint (from SolverHints.technique) */\n hintTechnique: number;\n}\n\n/** Access denied error info when hint level exceeds subscription tier */\nexport interface HintAccessError {\n /** The difficulty level of the requested hint */\n hintLevel: number;\n /** The entitlement required to access this hint level */\n requiredEntitlement: HintEntitlement;\n /** The user's current state */\n userState: HintAccessUserState;\n}\n\nexport interface UseHintOptions {\n /** Network client for API calls */\n networkClient: NetworkClient;\n /** Base URL for the Sudojo API */\n baseUrl: string;\n /** Access token for authentication */\n token: string;\n /** Original puzzle string (81 chars) */\n puzzle: string;\n /** Current user input string (81 chars) */\n userInput: string;\n /** Current pencilmarks (comma-separated or empty) */\n pencilmarks?: string;\n /** Whether auto-pencilmarks were generated */\n autoPencilmarks?: boolean;\n /**\n * Optional technique number to filter for.\n * When set, will first try to find hints for this specific technique.\n * If no hints found for the target technique, will retry without filter\n * to get the next available hint (allowing puzzle progression).\n */\n techniqueFilter?: number;\n /**\n * Callback fired when hints are received from the API.\n * Use this to intercept hints for logging, saving examples, etc.\n * Called before state is updated.\n */\n onHintReceived?: (data: HintReceivedData) => void;\n /**\n * The user's active entitlement IDs (e.g., ['blue_belt'] or ['red_belt']).\n * Used for client-side hint step gating.\n */\n userEntitlements?: string[];\n /**\n * The entitlement field from the current level (e.g., \"blue_belt,red_belt\").\n * If null/empty, the level is free and all hint steps are visible.\n */\n levelEntitlement?: string | null;\n}\n\nexport interface UseHintResult {\n /** Current hint step being displayed */\n hint: SolverHintStep | null;\n /** All hint steps (for showing progress like \"Step 1 of 3\") */\n hints: SolverHintStep[] | null;\n /** Current step index (0-based) */\n stepIndex: number;\n /** Total number of steps */\n totalSteps: number;\n /** Whether a hint request is in progress */\n isLoading: boolean;\n /** Error message if hint request failed */\n error: string | null;\n /** Access denied error info when hint level exceeds subscription tier */\n accessError: HintAccessError | null;\n /** Request hints or advance to next step */\n getHint: () => Promise<void>;\n /** Go to next hint step */\n nextStep: () => void;\n /** Go to previous hint step */\n previousStep: () => void;\n /** Clear all hints */\n clearHint: () => void;\n /** Apply the hint to the board - returns board data and clears hint */\n applyHint: () => HintBoardData | null;\n /** Whether there are more steps after current */\n hasNextStep: boolean;\n /** Whether there are steps before current */\n hasPreviousStep: boolean;\n /** Whether the hint can be applied (on last step) */\n canApply: boolean;\n /** Whether the current hint matches the techniqueFilter (if one was set) */\n isTargetTechnique: boolean;\n /** Whether the hint has steps gated behind a paywall */\n isHintGated: boolean;\n}\n\n/**\n * Hook for getting hints from the solver API\n *\n * Matches Kotlin HintInteractor behavior:\n * - First call to getHint() fetches hints from API\n * - Subsequent calls advance to next step (if available)\n * - Use nextStep()/previousStep() to navigate manually\n * - clearHint() resets everything\n *\n * @param options - Puzzle state for hint generation\n * @returns Hint state and controls\n *\n * @example\n * ```tsx\n * const { hint, isLoading, getHint, nextStep, clearHint, applyHint } = useHint({\n * networkClient,\n * baseUrl: 'https://api.sudojo.com',\n * token: 'user-access-token',\n * puzzle: originalPuzzle,\n * userInput: currentInput,\n * pencilmarks: currentPencilmarks,\n * });\n *\n * // Get hint button\n * <Button onClick={getHint} disabled={isLoading}>Get Hint</Button>\n *\n * // Display hint\n * {hint && <HintDisplay step={hint} />}\n *\n * // Apply hint to board\n * const handleApply = () => {\n * const boardData = applyHint();\n * if (boardData) {\n * setUserInput(boardData.user);\n * setPencilmarks(boardData.pencilmarks);\n * }\n * };\n * ```\n */\nexport function useHint({\n networkClient,\n baseUrl,\n token,\n puzzle,\n userInput,\n pencilmarks,\n autoPencilmarks = false,\n techniqueFilter,\n onHintReceived,\n userEntitlements,\n levelEntitlement,\n}: UseHintOptions): UseHintResult {\n const [hints, setHints] = useState<SolverHintStep[] | null>(null);\n const [stepIndex, setStepIndex] = useState(0);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [accessError, setAccessError] = useState<HintAccessError | null>(null);\n const [isTargetTechnique, setIsTargetTechnique] = useState(false);\n\n // Track puzzle state to detect when we need to re-fetch\n const lastPuzzleStateRef = useRef<string>('');\n\n // Store board data from API response for applying hint\n const boardDataRef = useRef<SolverBoard | null>(null);\n\n // Store the hint level from the API response for entitlement checking\n const hintLevelRef = useRef<number>(0);\n\n // Client-side entitlement check: does the user have access to this hint's level?\n const hasAccess = useMemo(\n () => hasRequiredEntitlement(levelEntitlement, userEntitlements ?? []),\n [levelEntitlement, userEntitlements]\n );\n\n // Compute current hint step\n const hint = hints?.[stepIndex] ?? null;\n const totalSteps = hints?.length ?? 0;\n const isHintGated = !hasAccess && totalSteps > FREE_HINT_STEP_LIMIT;\n const maxVisibleStep = hasAccess ? totalSteps - 1 : FREE_HINT_STEP_LIMIT - 1;\n const hasNextStep = stepIndex < Math.min(totalSteps - 1, maxVisibleStep);\n const hasPreviousStep = stepIndex > 0;\n // Can apply when on last step (of all steps) and have board data and have access\n const canApply =\n hints !== null &&\n stepIndex === totalSteps - 1 &&\n hasAccess &&\n boardDataRef.current !== null;\n\n // Helper to process a successful hint response\n const processHintResponse = useCallback(\n (response: BaseResponse<SolveData>, isTarget: boolean): boolean => {\n if (response.success && response.data?.hints?.steps?.length) {\n const hintSteps = response.data.hints.steps;\n const board = response.data.board ?? null;\n\n // Call onHintReceived callback if provided (for interception/logging)\n if (\n onHintReceived &&\n hintSteps[0] &&\n board?.user !== null &&\n board?.user !== undefined\n ) {\n onHintReceived({\n hint: hintSteps[0],\n hints: hintSteps,\n boardData: {\n user: board.user,\n pencilmarks: board.pencilmark?.numbers ?? null,\n autoPencilmarks: board.pencilmark?.autopencil ?? false,\n },\n hintLevel: response.data.hints.level,\n hintTechnique: response.data.hints.technique,\n });\n }\n\n setHints(hintSteps);\n setStepIndex(0);\n setIsTargetTechnique(isTarget);\n // Store board data for applying hint later\n boardDataRef.current = board;\n // Store hint level for entitlement checking\n hintLevelRef.current = response.data.hints.level;\n // Track the puzzle state we fetched for\n lastPuzzleStateRef.current = `${puzzle}|${userInput}|${pencilmarks ?? ''}`;\n\n // If user doesn't have access and there are more than FREE_HINT_STEP_LIMIT steps,\n // set access error so UI can show paywall after the free steps\n if (!hasAccess && hintSteps.length > FREE_HINT_STEP_LIMIT) {\n const required = parseEntitlements(levelEntitlement);\n setAccessError({\n hintLevel: response.data.hints.level,\n requiredEntitlement: required[0] ?? 'blue_belt',\n userState:\n (userEntitlements?.length ?? 0) > 0\n ? 'insufficient_tier'\n : 'no_subscription',\n });\n }\n\n return true;\n }\n return false;\n },\n [\n onHintReceived,\n puzzle,\n userInput,\n pencilmarks,\n hasAccess,\n levelEntitlement,\n userEntitlements,\n ]\n );\n\n // Fetch hints from API\n const fetchHints = useCallback(async () => {\n setIsLoading(true);\n setError(null);\n setAccessError(null);\n setIsTargetTechnique(false);\n\n try {\n const client = createSudojoClient(networkClient, baseUrl);\n\n // Phase 1: If techniqueFilter is set, first try to find that specific technique\n if (techniqueFilter !== undefined) {\n const filteredOptions = {\n original: puzzle,\n user: userInput,\n autoPencilmarks,\n ...(pencilmarks !== undefined && { pencilmarks }),\n techniques: techniqueFilter.toString(),\n };\n\n const filteredResponse = await client.solverSolve(\n token,\n filteredOptions\n );\n\n // If we found hints for the target technique, use them\n if (processHintResponse(filteredResponse, true)) {\n setIsLoading(false);\n return;\n }\n // No hints for target technique, fall through to Phase 2\n }\n\n // Phase 2: Regular (unfiltered) call to get next available hint\n const solveOptions = {\n original: puzzle,\n user: userInput,\n autoPencilmarks,\n ...(pencilmarks !== undefined && { pencilmarks }),\n };\n const response: BaseResponse<SolveData> = await client.solverSolve(\n token,\n solveOptions\n );\n\n if (processHintResponse(response, false)) {\n // Successfully got hints (not the target technique)\n } else if (response.error) {\n setError(response.error);\n setHints(null);\n boardDataRef.current = null;\n } else {\n setError('No hints available');\n setHints(null);\n boardDataRef.current = null;\n }\n } catch (err) {\n // Handle hint access denied error (402)\n if (HintAccessDeniedError.isHintAccessDeniedError(err)) {\n setAccessError({\n hintLevel: err.hintLevel,\n requiredEntitlement: err.requiredEntitlement,\n userState: err.userState,\n });\n setError(null);\n } else {\n setError(err instanceof Error ? err.message : 'Failed to get hint');\n }\n setHints(null);\n boardDataRef.current = null;\n } finally {\n setIsLoading(false);\n }\n }, [\n networkClient,\n baseUrl,\n token,\n puzzle,\n userInput,\n pencilmarks,\n autoPencilmarks,\n techniqueFilter,\n processHintResponse,\n ]);\n\n // Get hint: fetch if no hints or puzzle changed, otherwise advance\n const getHint = useCallback(async () => {\n const currentPuzzleState = `${puzzle}|${userInput}|${pencilmarks ?? ''}`;\n\n if (hints === null || lastPuzzleStateRef.current !== currentPuzzleState) {\n // No hints or puzzle state changed - fetch new hints\n await fetchHints();\n } else if (hasNextStep) {\n // Have hints and more steps - advance to next\n setStepIndex(prev => prev + 1);\n }\n // If at last step and hints exist, do nothing (stay on last step)\n }, [hints, hasNextStep, fetchHints, puzzle, userInput, pencilmarks]);\n\n // Navigate to next step\n const nextStep = useCallback(() => {\n if (hasNextStep) {\n setStepIndex(prev => prev + 1);\n }\n }, [hasNextStep]);\n\n // Navigate to previous step\n const previousStep = useCallback(() => {\n if (hasPreviousStep) {\n setStepIndex(prev => prev - 1);\n }\n }, [hasPreviousStep]);\n\n // Clear all hints\n const clearHint = useCallback(() => {\n setHints(null);\n setStepIndex(0);\n setError(null);\n setAccessError(null);\n setIsTargetTechnique(false);\n lastPuzzleStateRef.current = '';\n boardDataRef.current = null;\n }, []);\n\n // Apply the hint to the board - returns board data and clears hint\n const applyHint = useCallback((): HintBoardData | null => {\n const board = boardDataRef.current;\n // Check for null/undefined board or user, but allow empty string user\n if (!board || board.user === null || board.user === undefined) {\n return null;\n }\n\n const result: HintBoardData = {\n user: board.user,\n pencilmarks: board.pencilmark?.numbers ?? null,\n autoPencilmarks: board.pencilmark?.autopencil ?? false,\n };\n\n // Clear hints after applying (matches Kotlin: result = null)\n setHints(null);\n setStepIndex(0);\n setError(null);\n lastPuzzleStateRef.current = '';\n boardDataRef.current = null;\n\n return result;\n }, []);\n\n return {\n hint,\n hints,\n stepIndex,\n totalSteps,\n isLoading,\n error,\n accessError,\n getHint,\n nextStep,\n previousStep,\n clearHint,\n applyHint,\n hasNextStep,\n hasPreviousStep,\n canApply,\n isTargetTechnique,\n isHintGated,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"useHint.js","sourceRoot":"","sources":["../../src/hooks/useHint.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Hook for getting hints from the Sudoku solver\n *\n * Matches Kotlin HintInteractor behavior:\n * - Stores all hint steps from the API response\n * - Tracks current step index\n * - next() advances to next step (if available)\n * - previous() goes back to previous step\n * - getHint() fetches new hints OR advances to next step if hints exist\n */\n\nimport { useCallback, useMemo, useRef, useState } from 'react';\nimport type {\n BaseResponse,\n HintAccessUserState,\n HintEntitlement,\n SolveData,\n SolverBoard,\n SolverHintStep,\n} from '@sudobility/sudojo_types';\nimport {\n hasRequiredEntitlement,\n parseEntitlements,\n} from '@sudobility/sudojo_types';\nimport {\n HintAccessDeniedError,\n useSolverSolveMutation,\n} from '@sudobility/sudojo_client';\nimport type { NetworkClient } from '@sudobility/types';\nimport { useResolvedSudojoApi } from '../context/SudojoApiContext';\nimport { FREE_HINT_STEP_LIMIT } from '../utils/hintAccess';\n\n/** Board data returned when applying a hint */\nexport interface HintBoardData {\n /** 81-character user input string */\n user: string;\n /** Pencilmarks string (comma-separated) */\n pencilmarks: string | null;\n /** Whether auto-pencilmarks are enabled */\n autoPencilmarks: boolean;\n}\n\n/** Data passed to onHintReceived callback */\nexport interface HintReceivedData {\n /** The first hint step */\n hint: SolverHintStep;\n /** All hint steps */\n hints: SolverHintStep[];\n /** Board data that will be applied */\n boardData: HintBoardData;\n /** Difficulty level for this hint (from SolverHints.level) */\n hintLevel: number;\n /** Technique ID for this hint (from SolverHints.technique) */\n hintTechnique: number;\n}\n\n/** Access denied error info when hint level exceeds subscription tier */\nexport interface HintAccessError {\n /** The difficulty level of the requested hint */\n hintLevel: number;\n /** The entitlement required to access this hint level */\n requiredEntitlement: HintEntitlement;\n /** The user's current state */\n userState: HintAccessUserState;\n}\n\nexport interface UseHintOptions {\n /** Network client for API calls (default: SudojoApiProvider) */\n networkClient?: NetworkClient | undefined;\n /** Base URL for the Sudojo API (default: SudojoApiProvider) */\n baseUrl?: string | undefined;\n /** Access token for authentication (default: SudojoApiProvider) */\n token?: string | null | undefined;\n /** Original puzzle string (81 chars) */\n puzzle: string;\n /** Current user input string (81 chars) */\n userInput: string;\n /** Current pencilmarks (comma-separated or empty) */\n pencilmarks?: string;\n /** Whether auto-pencilmarks were generated */\n autoPencilmarks?: boolean;\n /**\n * Optional technique number to filter for.\n * When set, will first try to find hints for this specific technique.\n * If no hints found for the target technique, will retry without filter\n * to get the next available hint (allowing puzzle progression).\n */\n techniqueFilter?: number;\n /**\n * Callback fired when hints are received from the API.\n * Use this to intercept hints for logging, saving examples, etc.\n * Called before state is updated.\n */\n onHintReceived?: (data: HintReceivedData) => void;\n /**\n * The user's active entitlement IDs (e.g., ['blue_belt'] or ['red_belt']).\n * Used for client-side hint step gating.\n */\n userEntitlements?: string[];\n /**\n * The entitlement field from the current level (e.g., \"blue_belt,red_belt\").\n * If null/empty, the level is free and all hint steps are visible.\n */\n levelEntitlement?: string | null;\n}\n\nexport interface UseHintResult {\n /** Current hint step being displayed */\n hint: SolverHintStep | null;\n /** All hint steps (for showing progress like \"Step 1 of 3\") */\n hints: SolverHintStep[] | null;\n /** Current step index (0-based) */\n stepIndex: number;\n /** Total number of steps */\n totalSteps: number;\n /** Whether a hint request is in progress */\n isLoading: boolean;\n /** Error message if hint request failed */\n error: string | null;\n /** Access denied error info when hint level exceeds subscription tier */\n accessError: HintAccessError | null;\n /** Request hints or advance to next step */\n getHint: () => Promise<void>;\n /** Go to next hint step */\n nextStep: () => void;\n /** Go to previous hint step */\n previousStep: () => void;\n /** Clear all hints */\n clearHint: () => void;\n /** Apply the hint to the board - returns board data and clears hint */\n applyHint: () => HintBoardData | null;\n /** Whether there are more steps after current */\n hasNextStep: boolean;\n /** Whether there are steps before current */\n hasPreviousStep: boolean;\n /** Whether the hint can be applied (on last step) */\n canApply: boolean;\n /** Whether the current hint matches the techniqueFilter (if one was set) */\n isTargetTechnique: boolean;\n /** Whether the hint has steps gated behind a paywall */\n isHintGated: boolean;\n}\n\n/**\n * Hook for getting hints from the solver API\n *\n * Matches Kotlin HintInteractor behavior:\n * - First call to getHint() fetches hints from API\n * - Subsequent calls advance to next step (if available)\n * - Use nextStep()/previousStep() to navigate manually\n * - clearHint() resets everything\n *\n * @param options - Puzzle state for hint generation\n * @returns Hint state and controls\n *\n * @example\n * ```tsx\n * const { hint, isLoading, getHint, nextStep, clearHint, applyHint } = useHint({\n * networkClient,\n * baseUrl: 'https://api.sudojo.com',\n * token: 'user-access-token',\n * puzzle: originalPuzzle,\n * userInput: currentInput,\n * pencilmarks: currentPencilmarks,\n * });\n *\n * // Get hint button\n * <Button onClick={getHint} disabled={isLoading}>Get Hint</Button>\n *\n * // Display hint\n * {hint && <HintDisplay step={hint} />}\n *\n * // Apply hint to board\n * const handleApply = () => {\n * const boardData = applyHint();\n * if (boardData) {\n * setUserInput(boardData.user);\n * setPencilmarks(boardData.pencilmarks);\n * }\n * };\n * ```\n */\nexport function useHint(options: UseHintOptions): UseHintResult {\n const {\n puzzle,\n userInput,\n pencilmarks,\n autoPencilmarks = false,\n techniqueFilter,\n onHintReceived,\n userEntitlements,\n levelEntitlement,\n } = options;\n const { networkClient, baseUrl, token } = useResolvedSudojoApi(\n options,\n 'useHint'\n );\n // Solver requests go through sudojo_client's mutation hook; it rejects\n // with HintAccessDeniedError on a 402, like SudojoClient.solverSolve.\n const { mutateAsync: solverSolve } = useSolverSolveMutation(\n networkClient,\n baseUrl\n );\n const [hints, setHints] = useState<SolverHintStep[] | null>(null);\n const [stepIndex, setStepIndex] = useState(0);\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [accessError, setAccessError] = useState<HintAccessError | null>(null);\n const [isTargetTechnique, setIsTargetTechnique] = useState(false);\n\n // Track puzzle state to detect when we need to re-fetch\n const lastPuzzleStateRef = useRef<string>('');\n\n // Store board data from API response for applying hint\n const boardDataRef = useRef<SolverBoard | null>(null);\n\n // Store the hint level from the API response for entitlement checking\n const hintLevelRef = useRef<number>(0);\n\n // Client-side entitlement check: does the user have access to this hint's level?\n const hasAccess = useMemo(\n () => hasRequiredEntitlement(levelEntitlement, userEntitlements ?? []),\n [levelEntitlement, userEntitlements]\n );\n\n // Compute current hint step\n const hint = hints?.[stepIndex] ?? null;\n const totalSteps = hints?.length ?? 0;\n const isHintGated = !hasAccess && totalSteps > FREE_HINT_STEP_LIMIT;\n const maxVisibleStep = hasAccess ? totalSteps - 1 : FREE_HINT_STEP_LIMIT - 1;\n const hasNextStep = stepIndex < Math.min(totalSteps - 1, maxVisibleStep);\n const hasPreviousStep = stepIndex > 0;\n // Can apply when on last step (of all steps) and have board data and have access\n const canApply =\n hints !== null &&\n stepIndex === totalSteps - 1 &&\n hasAccess &&\n boardDataRef.current !== null;\n\n // Helper to process a successful hint response\n const processHintResponse = useCallback(\n (response: BaseResponse<SolveData>, isTarget: boolean): boolean => {\n if (response.success && response.data?.hints?.steps?.length) {\n const hintSteps = response.data.hints.steps;\n const board = response.data.board ?? null;\n\n // Call onHintReceived callback if provided (for interception/logging)\n if (\n onHintReceived &&\n hintSteps[0] &&\n board?.user !== null &&\n board?.user !== undefined\n ) {\n onHintReceived({\n hint: hintSteps[0],\n hints: hintSteps,\n boardData: {\n user: board.user,\n pencilmarks: board.pencilmark?.numbers ?? null,\n autoPencilmarks: board.pencilmark?.autopencil ?? false,\n },\n hintLevel: response.data.hints.level,\n hintTechnique: response.data.hints.technique,\n });\n }\n\n setHints(hintSteps);\n setStepIndex(0);\n setIsTargetTechnique(isTarget);\n // Store board data for applying hint later\n boardDataRef.current = board;\n // Store hint level for entitlement checking\n hintLevelRef.current = response.data.hints.level;\n // Track the puzzle state we fetched for\n lastPuzzleStateRef.current = `${puzzle}|${userInput}|${pencilmarks ?? ''}`;\n\n // If user doesn't have access and there are more than FREE_HINT_STEP_LIMIT steps,\n // set access error so UI can show paywall after the free steps\n if (!hasAccess && hintSteps.length > FREE_HINT_STEP_LIMIT) {\n const required = parseEntitlements(levelEntitlement);\n setAccessError({\n hintLevel: response.data.hints.level,\n requiredEntitlement: required[0] ?? 'blue_belt',\n userState:\n (userEntitlements?.length ?? 0) > 0\n ? 'insufficient_tier'\n : 'no_subscription',\n });\n }\n\n return true;\n }\n return false;\n },\n [\n onHintReceived,\n puzzle,\n userInput,\n pencilmarks,\n hasAccess,\n levelEntitlement,\n userEntitlements,\n ]\n );\n\n // Fetch hints from API\n const fetchHints = useCallback(async () => {\n setIsLoading(true);\n setError(null);\n setAccessError(null);\n setIsTargetTechnique(false);\n\n try {\n // Phase 1: If techniqueFilter is set, first try to find that specific technique\n if (techniqueFilter !== undefined) {\n const filteredOptions = {\n original: puzzle,\n user: userInput,\n autoPencilmarks,\n ...(pencilmarks !== undefined && { pencilmarks }),\n techniques: techniqueFilter.toString(),\n };\n\n const filteredResponse = await solverSolve({\n token,\n options: filteredOptions,\n });\n\n // If we found hints for the target technique, use them\n if (processHintResponse(filteredResponse, true)) {\n setIsLoading(false);\n return;\n }\n // No hints for target technique, fall through to Phase 2\n }\n\n // Phase 2: Regular (unfiltered) call to get next available hint\n const solveOptions = {\n original: puzzle,\n user: userInput,\n autoPencilmarks,\n ...(pencilmarks !== undefined && { pencilmarks }),\n };\n const response: BaseResponse<SolveData> = await solverSolve({\n token,\n options: solveOptions,\n });\n\n if (processHintResponse(response, false)) {\n // Successfully got hints (not the target technique)\n } else if (response.error) {\n setError(response.error);\n setHints(null);\n boardDataRef.current = null;\n } else {\n setError('No hints available');\n setHints(null);\n boardDataRef.current = null;\n }\n } catch (err) {\n // Handle hint access denied error (402)\n if (HintAccessDeniedError.isHintAccessDeniedError(err)) {\n setAccessError({\n hintLevel: err.hintLevel,\n requiredEntitlement: err.requiredEntitlement,\n userState: err.userState,\n });\n setError(null);\n } else {\n setError(err instanceof Error ? err.message : 'Failed to get hint');\n }\n setHints(null);\n boardDataRef.current = null;\n } finally {\n setIsLoading(false);\n }\n }, [\n solverSolve,\n token,\n puzzle,\n userInput,\n pencilmarks,\n autoPencilmarks,\n techniqueFilter,\n processHintResponse,\n ]);\n\n // Get hint: fetch if no hints or puzzle changed, otherwise advance\n const getHint = useCallback(async () => {\n const currentPuzzleState = `${puzzle}|${userInput}|${pencilmarks ?? ''}`;\n\n if (hints === null || lastPuzzleStateRef.current !== currentPuzzleState) {\n // No hints or puzzle state changed - fetch new hints\n await fetchHints();\n } else if (hasNextStep) {\n // Have hints and more steps - advance to next\n setStepIndex(prev => prev + 1);\n }\n // If at last step and hints exist, do nothing (stay on last step)\n }, [hints, hasNextStep, fetchHints, puzzle, userInput, pencilmarks]);\n\n // Navigate to next step\n const nextStep = useCallback(() => {\n if (hasNextStep) {\n setStepIndex(prev => prev + 1);\n }\n }, [hasNextStep]);\n\n // Navigate to previous step\n const previousStep = useCallback(() => {\n if (hasPreviousStep) {\n setStepIndex(prev => prev - 1);\n }\n }, [hasPreviousStep]);\n\n // Clear all hints\n const clearHint = useCallback(() => {\n setHints(null);\n setStepIndex(0);\n setError(null);\n setAccessError(null);\n setIsTargetTechnique(false);\n lastPuzzleStateRef.current = '';\n boardDataRef.current = null;\n }, []);\n\n // Apply the hint to the board - returns board data and clears hint\n const applyHint = useCallback((): HintBoardData | null => {\n const board = boardDataRef.current;\n // Check for null/undefined board or user, but allow empty string user\n if (!board || board.user === null || board.user === undefined) {\n return null;\n }\n\n const result: HintBoardData = {\n user: board.user,\n pencilmarks: board.pencilmark?.numbers ?? null,\n autoPencilmarks: board.pencilmark?.autopencil ?? false,\n };\n\n // Clear hints after applying (matches Kotlin: result = null)\n setHints(null);\n setStepIndex(0);\n setError(null);\n lastPuzzleStateRef.current = '';\n boardDataRef.current = null;\n\n return result;\n }, []);\n\n return {\n hint,\n hints,\n stepIndex,\n totalSteps,\n isLoading,\n error,\n accessError,\n getHint,\n nextStep,\n previousStep,\n clearHint,\n applyHint,\n hasNextStep,\n hasPreviousStep,\n canApply,\n isTargetTechnique,\n isHintGated,\n };\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User account status hooks: real (non-anonymous) user and site admin.
|
|
3
|
+
*/
|
|
4
|
+
import type { NetworkClient } from '@sudobility/types';
|
|
5
|
+
import { type AuthUser } from '../utils/auth';
|
|
6
|
+
/**
|
|
7
|
+
* Whether the user is a real (signed-in, non-anonymous) account
|
|
8
|
+
* (`!!user && !user.isAnonymous`).
|
|
9
|
+
*/
|
|
10
|
+
export declare function useIsRealUser(user: Pick<AuthUser, 'isAnonymous'> | null | undefined): boolean;
|
|
11
|
+
export interface UseIsSiteAdminOptions {
|
|
12
|
+
/** Network client for API calls (default: SudojoApiProvider) */
|
|
13
|
+
networkClient?: NetworkClient | undefined;
|
|
14
|
+
/** Base URL for the Sudojo API (default: SudojoApiProvider) */
|
|
15
|
+
baseUrl?: string | undefined;
|
|
16
|
+
/** Access token (default: SudojoApiProvider) */
|
|
17
|
+
token?: string | null | undefined;
|
|
18
|
+
/** The signed-in user (null/undefined when signed out) */
|
|
19
|
+
user: AuthUser | null | undefined;
|
|
20
|
+
}
|
|
21
|
+
export interface UseIsSiteAdminResult {
|
|
22
|
+
/** True once the API's user record says this user is a site admin. */
|
|
23
|
+
isSiteAdmin: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* True once the user record has loaded or failed. Stays false while the
|
|
26
|
+
* request is pending and while it is disabled (no real user or token), as
|
|
27
|
+
* in the web hook, so a guard can keep waiting during auth start-up.
|
|
28
|
+
*/
|
|
29
|
+
isResolved: boolean;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Whether the signed-in user is a site admin, from the API's user record
|
|
33
|
+
* (`GET /users/:uid` -> `siteAdmin`). The request only runs for a real user
|
|
34
|
+
* with a token (isAuthenticatedUser): the endpoint returns 403 for anonymous
|
|
35
|
+
* users. The API still authorizes every admin call; this only decides what
|
|
36
|
+
* the UI shows.
|
|
37
|
+
*/
|
|
38
|
+
export declare function useIsSiteAdmin(options: UseIsSiteAdminOptions): UseIsSiteAdminResult;
|
|
39
|
+
//# sourceMappingURL=useIsSiteAdmin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIsSiteAdmin.d.ts","sourceRoot":"","sources":["../../src/hooks/useIsSiteAdmin.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,EAAE,KAAK,QAAQ,EAAmC,MAAM,eAAe,CAAC;AAE/E;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,GAAG,IAAI,GAAG,SAAS,GACrD,OAAO,CAET;AAED,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,aAAa,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC1C,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClC,0DAA0D;IAC1D,IAAI,EAAE,QAAQ,GAAG,IAAI,GAAG,SAAS,CAAC;CACnC;AAED,MAAM,WAAW,oBAAoB;IACnC,sEAAsE;IACtE,WAAW,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,qBAAqB,GAC7B,oBAAoB,CAiBtB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User account status hooks: real (non-anonymous) user and site admin.
|
|
3
|
+
*/
|
|
4
|
+
import { useMemo } from 'react';
|
|
5
|
+
import { useSudojoUser } from '@sudobility/sudojo_client';
|
|
6
|
+
import { useResolvedSudojoApi } from '../context/SudojoApiContext';
|
|
7
|
+
import { isAuthenticatedUser, isRealUser } from '../utils/auth';
|
|
8
|
+
/**
|
|
9
|
+
* Whether the user is a real (signed-in, non-anonymous) account
|
|
10
|
+
* (`!!user && !user.isAnonymous`).
|
|
11
|
+
*/
|
|
12
|
+
export function useIsRealUser(user) {
|
|
13
|
+
return useMemo(() => isRealUser(user), [user]);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Whether the signed-in user is a site admin, from the API's user record
|
|
17
|
+
* (`GET /users/:uid` -> `siteAdmin`). The request only runs for a real user
|
|
18
|
+
* with a token (isAuthenticatedUser): the endpoint returns 403 for anonymous
|
|
19
|
+
* users. The API still authorizes every admin call; this only decides what
|
|
20
|
+
* the UI shows.
|
|
21
|
+
*/
|
|
22
|
+
export function useIsSiteAdmin(options) {
|
|
23
|
+
const { user } = options;
|
|
24
|
+
const { networkClient, baseUrl, token } = useResolvedSudojoApi(options, 'useIsSiteAdmin');
|
|
25
|
+
const { data, isSuccess, isError } = useSudojoUser(networkClient, baseUrl, token, user?.uid ?? '', { enabled: isAuthenticatedUser(user, token) });
|
|
26
|
+
return {
|
|
27
|
+
isSiteAdmin: data?.data?.siteAdmin ?? false,
|
|
28
|
+
isResolved: isSuccess || isError,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=useIsSiteAdmin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useIsSiteAdmin.js","sourceRoot":"","sources":["../../src/hooks/useIsSiteAdmin.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAEhC,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAiB,mBAAmB,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE/E;;;GAGG;AACH,MAAM,UAAU,aAAa,CAC3B,IAAsD;IAEtD,OAAO,OAAO,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;AACjD,CAAC;AAwBD;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,OAA8B;IAE9B,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IACzB,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,oBAAoB,CAC5D,OAAO,EACP,gBAAgB,CACjB,CAAC;IACF,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,aAAa,CAChD,aAAa,EACb,OAAO,EACP,KAAK,EACL,IAAI,EAAE,GAAG,IAAI,EAAE,EACf,EAAE,OAAO,EAAE,mBAAmB,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAC9C,CAAC;IACF,OAAO;QACL,WAAW,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,IAAI,KAAK;QAC3C,UAAU,EAAE,SAAS,IAAI,OAAO;KACjC,CAAC;AACJ,CAAC","sourcesContent":["/**\n * User account status hooks: real (non-anonymous) user and site admin.\n */\n\nimport { useMemo } from 'react';\nimport type { NetworkClient } from '@sudobility/types';\nimport { useSudojoUser } from '@sudobility/sudojo_client';\nimport { useResolvedSudojoApi } from '../context/SudojoApiContext';\nimport { type AuthUser, isAuthenticatedUser, isRealUser } from '../utils/auth';\n\n/**\n * Whether the user is a real (signed-in, non-anonymous) account\n * (`!!user && !user.isAnonymous`).\n */\nexport function useIsRealUser(\n user: Pick<AuthUser, 'isAnonymous'> | null | undefined\n): boolean {\n return useMemo(() => isRealUser(user), [user]);\n}\n\nexport interface UseIsSiteAdminOptions {\n /** Network client for API calls (default: SudojoApiProvider) */\n networkClient?: NetworkClient | undefined;\n /** Base URL for the Sudojo API (default: SudojoApiProvider) */\n baseUrl?: string | undefined;\n /** Access token (default: SudojoApiProvider) */\n token?: string | null | undefined;\n /** The signed-in user (null/undefined when signed out) */\n user: AuthUser | null | undefined;\n}\n\nexport interface UseIsSiteAdminResult {\n /** True once the API's user record says this user is a site admin. */\n isSiteAdmin: boolean;\n /**\n * True once the user record has loaded or failed. Stays false while the\n * request is pending and while it is disabled (no real user or token), as\n * in the web hook, so a guard can keep waiting during auth start-up.\n */\n isResolved: boolean;\n}\n\n/**\n * Whether the signed-in user is a site admin, from the API's user record\n * (`GET /users/:uid` -> `siteAdmin`). The request only runs for a real user\n * with a token (isAuthenticatedUser): the endpoint returns 403 for anonymous\n * users. The API still authorizes every admin call; this only decides what\n * the UI shows.\n */\nexport function useIsSiteAdmin(\n options: UseIsSiteAdminOptions\n): UseIsSiteAdminResult {\n const { user } = options;\n const { networkClient, baseUrl, token } = useResolvedSudojoApi(\n options,\n 'useIsSiteAdmin'\n );\n const { data, isSuccess, isError } = useSudojoUser(\n networkClient,\n baseUrl,\n token,\n user?.uid ?? '',\n { enabled: isAuthenticatedUser(user, token) }\n );\n return {\n isSiteAdmin: data?.data?.siteAdmin ?? false,\n isResolved: isSuccess || isError,\n };\n}\n"]}
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
import type { Learning } from '@sudobility/sudojo_types';
|
|
5
5
|
import type { NetworkClient } from '@sudobility/types';
|
|
6
6
|
export interface UseLearningOptions {
|
|
7
|
-
/** Network client for API calls */
|
|
8
|
-
networkClient
|
|
9
|
-
/** Base URL for the Sudojo API */
|
|
10
|
-
baseUrl
|
|
7
|
+
/** Network client for API calls (default: SudojoApiProvider) */
|
|
8
|
+
networkClient?: NetworkClient | undefined;
|
|
9
|
+
/** Base URL for the Sudojo API (default: SudojoApiProvider) */
|
|
10
|
+
baseUrl?: string | undefined;
|
|
11
11
|
/** Access token for authentication (optional for public data) */
|
|
12
12
|
token?: string;
|
|
13
13
|
/** Optional technique number to filter learning materials */
|
|
@@ -68,10 +68,10 @@ export interface UseLearningResult {
|
|
|
68
68
|
*/
|
|
69
69
|
export declare function useLearning(options: UseLearningOptions): UseLearningResult;
|
|
70
70
|
export interface UseLearningItemOptions {
|
|
71
|
-
/** Network client for API calls */
|
|
72
|
-
networkClient
|
|
73
|
-
/** Base URL for the Sudojo API */
|
|
74
|
-
baseUrl
|
|
71
|
+
/** Network client for API calls (default: SudojoApiProvider) */
|
|
72
|
+
networkClient?: NetworkClient | undefined;
|
|
73
|
+
/** Base URL for the Sudojo API (default: SudojoApiProvider) */
|
|
74
|
+
baseUrl?: string | undefined;
|
|
75
75
|
/** Access token for authentication (optional for public data) */
|
|
76
76
|
token?: string;
|
|
77
77
|
/** Learning item UUID to fetch */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLearning.d.ts","sourceRoot":"","sources":["../../src/hooks/useLearning.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"useLearning.d.ts","sourceRoot":"","sources":["../../src/hooks/useLearning.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAOvD,MAAM,WAAW,kBAAkB;IACjC,gEAAgE;IAChE,aAAa,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC1C,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0DAA0D;IAC1D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,mDAAmD;IACnD,iBAAiB,EAAE,QAAQ,EAAE,CAAC;IAC9B,6CAA6C;IAC7C,SAAS,EAAE,OAAO,CAAC;IACnB,8BAA8B;IAC9B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,iCAAiC;IACjC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,oCAAoC;IACpC,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,QAAQ,GAAG,SAAS,CAAC;IAC1D,yCAAyC;IACzC,cAAc,EAAE,QAAQ,EAAE,CAAC;IAC3B,mDAAmD;IACnD,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC7C,gDAAgD;IAChD,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;CAC7C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,iBAAiB,CA8E1E;AAED,MAAM,WAAW,sBAAsB;IACrC,gEAAgE;IAChE,aAAa,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC1C,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,YAAY,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,gCAAgC;IAChC,YAAY,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC9B,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,8BAA8B;IAC9B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,4BAA4B;IAC5B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,GAC9B,qBAAqB,CA8BvB"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Hook for fetching and managing learning materials for techniques
|
|
3
3
|
*/
|
|
4
4
|
import { useMemo } from 'react';
|
|
5
|
+
import { useResolvedSudojoApi } from '../context/SudojoApiContext';
|
|
5
6
|
import { useSudojoLearning, useSudojoLearningItem, } from '@sudobility/sudojo_client';
|
|
6
7
|
/**
|
|
7
8
|
* Hook for fetching and managing learning materials
|
|
@@ -35,7 +36,8 @@ import { useSudojoLearning, useSudojoLearningItem, } from '@sudobility/sudojo_cl
|
|
|
35
36
|
* ```
|
|
36
37
|
*/
|
|
37
38
|
export function useLearning(options) {
|
|
38
|
-
const {
|
|
39
|
+
const { technique, languageCode, enabled = true } = options;
|
|
40
|
+
const { networkClient, baseUrl, token } = useResolvedSudojoApi(options, 'useLearning');
|
|
39
41
|
const queryParams = useMemo(() => {
|
|
40
42
|
// Only return params if at least one filter is provided
|
|
41
43
|
if (technique === undefined && !languageCode)
|
|
@@ -104,7 +106,8 @@ export function useLearning(options) {
|
|
|
104
106
|
* @returns Learning item data
|
|
105
107
|
*/
|
|
106
108
|
export function useLearningItem(options) {
|
|
107
|
-
const {
|
|
109
|
+
const { learningUuid, enabled = true } = options;
|
|
110
|
+
const { networkClient, baseUrl, token } = useResolvedSudojoApi(options, 'useLearningItem');
|
|
108
111
|
const { data, isLoading, error, refetch } = useSudojoLearningItem(networkClient, baseUrl, token, learningUuid, {
|
|
109
112
|
enabled: enabled && !!learningUuid,
|
|
110
113
|
});
|