@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":"useLearning.js","sourceRoot":"","sources":["../../src/hooks/useLearning.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAGhC,OAAO,EACL,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,2BAA2B,CAAC;AAoCnC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,WAAW,CAAC,OAA2B;IACrD,MAAM,
|
|
1
|
+
{"version":3,"file":"useLearning.js","sourceRoot":"","sources":["../../src/hooks/useLearning.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Hook for fetching and managing learning materials for techniques\n */\n\nimport { useMemo } from 'react';\nimport type { Learning } from '@sudobility/sudojo_types';\nimport type { NetworkClient } from '@sudobility/types';\nimport { useResolvedSudojoApi } from '../context/SudojoApiContext';\nimport {\n useSudojoLearning,\n useSudojoLearningItem,\n} from '@sudobility/sudojo_client';\n\nexport interface UseLearningOptions {\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 (optional for public data) */\n token?: string;\n /** Optional technique number to filter learning materials */\n technique?: number;\n /** Optional language code to filter learning materials */\n languageCode?: string;\n /** Whether to enable the query */\n enabled?: boolean;\n}\n\nexport interface UseLearningResult {\n /** All learning materials (optionally filtered) */\n learningMaterials: Learning[];\n /** Whether learning materials are loading */\n isLoading: boolean;\n /** Error if loading failed */\n error: Error | null;\n /** Refetch learning materials */\n refetch: () => void;\n /** Get learning material by UUID */\n getLearningByUuid: (uuid: string) => Learning | undefined;\n /** Learning materials sorted by index */\n sortedLearning: Learning[];\n /** Group learning materials by technique number */\n learningByTechnique: Map<number, Learning[]>;\n /** Group learning materials by language code */\n learningByLanguage: Map<string, Learning[]>;\n}\n\n/**\n * Hook for fetching and managing learning materials\n *\n * @param options - Hook options\n * @returns Learning materials data and utilities\n *\n * @example\n * ```tsx\n * function LearningContent({ technique }: { technique: number }) {\n * const { learningMaterials, isLoading, sortedLearning } = useLearning({\n * networkClient,\n * baseUrl: 'https://api.sudojo.com',\n * technique,\n * languageCode: 'en',\n * });\n *\n * if (isLoading) return <Loading />;\n *\n * return (\n * <div>\n * {sortedLearning.map(item => (\n * <div key={item.uuid}>\n * <p>{item.text}</p>\n * {item.image_url && <img src={item.image_url} alt=\"\" />}\n * </div>\n * ))}\n * </div>\n * );\n * }\n * ```\n */\nexport function useLearning(options: UseLearningOptions): UseLearningResult {\n const { technique, languageCode, enabled = true } = options;\n const { networkClient, baseUrl, token } = useResolvedSudojoApi(\n options,\n 'useLearning'\n );\n\n const queryParams = useMemo(() => {\n // Only return params if at least one filter is provided\n if (technique === undefined && !languageCode) return undefined;\n return {\n technique: technique ?? undefined,\n language_code: languageCode ?? undefined,\n };\n }, [technique, languageCode]);\n\n const { data, isLoading, error, refetch } = useSudojoLearning(\n networkClient,\n baseUrl,\n token,\n queryParams,\n { enabled }\n );\n\n const learningMaterials = useMemo(() => {\n if (!data?.success || !data.data) return [];\n return data.data;\n }, [data]);\n\n const sortedLearning = useMemo(() => {\n return [...learningMaterials].sort((a, b) => {\n // First sort by technique number, then by index\n if (a.technique !== b.technique) {\n return (a.technique ?? 0) - (b.technique ?? 0);\n }\n return a.index - b.index;\n });\n }, [learningMaterials]);\n\n const getLearningByUuid = useMemo(() => {\n const learningMap = new Map(learningMaterials.map(l => [l.uuid, l]));\n return (uuid: string) => learningMap.get(uuid);\n }, [learningMaterials]);\n\n const learningByTechnique = useMemo(() => {\n const byTechnique = new Map<number, Learning[]>();\n for (const learning of sortedLearning) {\n const key = learning.technique ?? 0;\n const existing = byTechnique.get(key) ?? [];\n existing.push(learning);\n byTechnique.set(key, existing);\n }\n return byTechnique;\n }, [sortedLearning]);\n\n const learningByLanguage = useMemo(() => {\n const byLanguage = new Map<string, Learning[]>();\n for (const learning of sortedLearning) {\n const key = learning.language_code;\n const existing = byLanguage.get(key) ?? [];\n existing.push(learning);\n byLanguage.set(key, existing);\n }\n return byLanguage;\n }, [sortedLearning]);\n\n return {\n learningMaterials,\n isLoading,\n error: error ?? null,\n refetch: () => {\n refetch();\n },\n getLearningByUuid,\n sortedLearning,\n learningByTechnique,\n learningByLanguage,\n };\n}\n\nexport interface UseLearningItemOptions {\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 (optional for public data) */\n token?: string;\n /** Learning item UUID to fetch */\n learningUuid: string;\n /** Whether to enable the query */\n enabled?: boolean;\n}\n\nexport interface UseLearningItemResult {\n /** The fetched learning item */\n learningItem: Learning | null;\n /** Whether learning item is loading */\n isLoading: boolean;\n /** Error if loading failed */\n error: Error | null;\n /** Refetch learning item */\n refetch: () => void;\n}\n\n/**\n * Hook for fetching a single learning item by UUID\n *\n * @param options - Hook options\n * @returns Learning item data\n */\nexport function useLearningItem(\n options: UseLearningItemOptions\n): UseLearningItemResult {\n const { learningUuid, enabled = true } = options;\n const { networkClient, baseUrl, token } = useResolvedSudojoApi(\n options,\n 'useLearningItem'\n );\n\n const { data, isLoading, error, refetch } = useSudojoLearningItem(\n networkClient,\n baseUrl,\n token,\n learningUuid,\n {\n enabled: enabled && !!learningUuid,\n }\n );\n\n const learningItem = useMemo(() => {\n if (!data?.success || !data.data) return null;\n return data.data;\n }, [data]);\n\n return {\n learningItem,\n isLoading,\n error: error ?? null,\n refetch: () => {\n refetch();\n },\n };\n}\n"]}
|
|
@@ -4,15 +4,15 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { Board } from '@sudobility/sudojo_types';
|
|
6
6
|
import type { NetworkClient } from '@sudobility/types';
|
|
7
|
-
|
|
8
|
-
export type GameFetchStatus
|
|
7
|
+
import { type GameFetchStatus } from '../utils/gameFetchStatus';
|
|
8
|
+
export type { GameFetchStatus } from '../utils/gameFetchStatus';
|
|
9
9
|
export interface UseLevelGameOptions {
|
|
10
|
-
/** Network client for API calls */
|
|
11
|
-
networkClient
|
|
12
|
-
/** Base URL for the Sudojo API */
|
|
13
|
-
baseUrl
|
|
10
|
+
/** Network client for API calls (default: SudojoApiProvider) */
|
|
11
|
+
networkClient?: NetworkClient | undefined;
|
|
12
|
+
/** Base URL for the Sudojo API (default: SudojoApiProvider) */
|
|
13
|
+
baseUrl?: string | undefined;
|
|
14
14
|
/** Access token for authentication */
|
|
15
|
-
token
|
|
15
|
+
token?: string | undefined;
|
|
16
16
|
/** Level number (1-12) to fetch game for */
|
|
17
17
|
level: number;
|
|
18
18
|
/** Whether to fetch only symmetrical puzzles */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLevelGame.d.ts","sourceRoot":"","sources":["../../src/hooks/useLevelGame.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAEtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIvD,
|
|
1
|
+
{"version":3,"file":"useLevelGame.d.ts","sourceRoot":"","sources":["../../src/hooks/useLevelGame.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAEtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIvD,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,0BAA0B,CAAC;AAElC,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE,MAAM,WAAW,mBAAmB;IAClC,gEAAgE;IAChE,aAAa,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC1C,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,4CAA4C;IAC5C,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+CAA+C;IAC/C,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,gFAAgF;IAChF,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,kCAAkC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,kBAAkB;IACjC,0BAA0B;IAC1B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,uCAAuC;IACvC,MAAM,EAAE,eAAe,CAAC;IACxB,8BAA8B;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,4BAA4B;IAC5B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,6BAA6B;IAC7B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,8CAA8C;IAC9C,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,2EAA2E;IAC3E,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,kBAAkB,CA8E7E"}
|
|
@@ -3,37 +3,11 @@
|
|
|
3
3
|
* Handles auth and subscription status with automatic refresh
|
|
4
4
|
*/
|
|
5
5
|
import { useMemo } from 'react';
|
|
6
|
-
import { hasRequiredEntitlement } from '@sudobility/sudojo_types';
|
|
6
|
+
import { hasRequiredEntitlement, isValidLevel } from '@sudobility/sudojo_types';
|
|
7
|
+
import { useResolvedSudojoApi } from '../context/SudojoApiContext';
|
|
7
8
|
import { useSudojoRandomBoard } from '@sudobility/sudojo_client';
|
|
8
9
|
import { useQueryClient } from '@tanstack/react-query';
|
|
9
|
-
|
|
10
|
-
function isAuthRequired(response, error) {
|
|
11
|
-
if (response?.success === false &&
|
|
12
|
-
response.action?.type === 'auth_required') {
|
|
13
|
-
return true;
|
|
14
|
-
}
|
|
15
|
-
if (error && typeof error === 'object') {
|
|
16
|
-
const err = error;
|
|
17
|
-
if (err.message?.includes('Account required'))
|
|
18
|
-
return true;
|
|
19
|
-
}
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
/** Check if response indicates subscription is required */
|
|
23
|
-
function isSubscriptionRequired(response, error) {
|
|
24
|
-
if (response?.success === false &&
|
|
25
|
-
response.action?.type === 'subscription_required') {
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
if (error && typeof error === 'object') {
|
|
29
|
-
const err = error;
|
|
30
|
-
if (err.message?.includes('Daily limit reached') ||
|
|
31
|
-
err.message?.includes('subscription')) {
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
10
|
+
import { getGameFetchStatus, } from '../utils/gameFetchStatus';
|
|
37
11
|
/**
|
|
38
12
|
* Hook for fetching a game board for a specific level
|
|
39
13
|
*
|
|
@@ -64,7 +38,8 @@ function isSubscriptionRequired(response, error) {
|
|
|
64
38
|
* ```
|
|
65
39
|
*/
|
|
66
40
|
export function useLevelGame(options) {
|
|
67
|
-
const {
|
|
41
|
+
const { level, symmetrical, userEntitlements, levelEntitlement, enabled = true, } = options;
|
|
42
|
+
const { networkClient, baseUrl, token } = useResolvedSudojoApi(options, 'useLevelGame');
|
|
68
43
|
// Client-side entitlement check before making API call
|
|
69
44
|
const entitlementDenied = useMemo(() => !hasRequiredEntitlement(levelEntitlement, userEntitlements ?? []), [levelEntitlement, userEntitlements]);
|
|
70
45
|
const queryClient = useQueryClient();
|
|
@@ -76,25 +51,14 @@ export function useLevelGame(options) {
|
|
|
76
51
|
techniques: undefined,
|
|
77
52
|
technique_bit: undefined,
|
|
78
53
|
}), [level, symmetrical]);
|
|
79
|
-
const { data, isLoading, error, refetch } = useSudojoRandomBoard(networkClient, baseUrl, token, queryParams, { enabled: enabled && level
|
|
80
|
-
// Determine status based on response
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return 'loading';
|
|
88
|
-
if (isAuthRequired(apiResponse, error))
|
|
89
|
-
return 'auth_required';
|
|
90
|
-
if (isSubscriptionRequired(apiResponse, error))
|
|
91
|
-
return 'subscription_required';
|
|
92
|
-
if (error)
|
|
93
|
-
return 'error';
|
|
94
|
-
if (apiResponse?.success && apiResponse.data)
|
|
95
|
-
return 'success';
|
|
96
|
-
return 'loading';
|
|
97
|
-
}, [entitlementDenied, isLoading, apiResponse, error]);
|
|
54
|
+
const { data, isLoading, error, refetch } = useSudojoRandomBoard(networkClient, baseUrl, token, queryParams, { enabled: enabled && isValidLevel(level) && !entitlementDenied });
|
|
55
|
+
// Determine status based on response (incl. the legacy `action` field)
|
|
56
|
+
const status = useMemo(() => getGameFetchStatus({
|
|
57
|
+
isLoading,
|
|
58
|
+
response: data,
|
|
59
|
+
error,
|
|
60
|
+
entitlementDenied,
|
|
61
|
+
}), [entitlementDenied, isLoading, data, error]);
|
|
98
62
|
const board = useMemo(() => {
|
|
99
63
|
if (data?.success && data.data) {
|
|
100
64
|
return data.data;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLevelGame.js","sourceRoot":"","sources":["../../src/hooks/useLevelGame.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAEhC,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"useLevelGame.js","sourceRoot":"","sources":["../../src/hooks/useLevelGame.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAEhC,OAAO,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAEhF,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAGL,kBAAkB,GACnB,MAAM,0BAA0B,CAAC;AA0ClC;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,YAAY,CAAC,OAA4B;IACvD,MAAM,EACJ,KAAK,EACL,WAAW,EACX,gBAAgB,EAChB,gBAAgB,EAChB,OAAO,GAAG,IAAI,GACf,GAAG,OAAO,CAAC;IACZ,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,oBAAoB,CAC5D,OAAO,EACP,cAAc,CACf,CAAC;IAEF,uDAAuD;IACvD,MAAM,iBAAiB,GAAG,OAAO,CAC/B,GAAG,EAAE,CAAC,CAAC,sBAAsB,CAAC,gBAAgB,EAAE,gBAAgB,IAAI,EAAE,CAAC,EACvE,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CACrC,CAAC;IAEF,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC,MAAM,WAAW,GAAG,OAAO,CACzB,GAAG,EAAE,CAAC,CAAC;QACL,KAAK;QACL,WAAW,EAAE,WAAW,IAAI,SAAS;QACrC,KAAK,EAAE,SAAS;QAChB,MAAM,EAAE,SAAS;QACjB,UAAU,EAAE,SAAS;QACrB,aAAa,EAAE,SAAS;KACzB,CAAC,EACF,CAAC,KAAK,EAAE,WAAW,CAAC,CACrB,CAAC;IAEF,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,oBAAoB,CAC9D,aAAa,EACb,OAAO,EACP,KAAK,EACL,WAAW,EACX,EAAE,OAAO,EAAE,OAAO,IAAI,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,EAAE,CAClE,CAAC;IAEF,uEAAuE;IACvE,MAAM,MAAM,GAAG,OAAO,CACpB,GAAoB,EAAE,CACpB,kBAAkB,CAAC;QACjB,SAAS;QACT,QAAQ,EAAE,IAAqC;QAC/C,KAAK;QACL,iBAAiB;KAClB,CAAC,EACJ,CAAC,iBAAiB,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,CAC5C,CAAC;IAEF,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,EAAE;QACzB,IAAI,IAAI,EAAE,OAAO,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAC/B,OAAO,IAAI,CAAC,IAAI,CAAC;QACnB,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE;QAC9B,OAAO,GAAG,EAAE;YACV,WAAW,CAAC,iBAAiB,CAAC;gBAC5B,QAAQ,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC;aACzC,CAAC,CAAC;YACH,OAAO,EAAE,CAAC;QACZ,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;IAE3B,OAAO;QACL,KAAK;QACL,MAAM;QACN,SAAS;QACT,KAAK,EAAE,KAAK,IAAI,IAAI;QACpB,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE;QACxB,UAAU;QACV,mBAAmB,EAAE,gBAAgB,IAAI,IAAI;KAC9C,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Hook for fetching and managing game board for a specific level\n * Handles auth and subscription status with automatic refresh\n */\n\nimport { useMemo } from 'react';\nimport type { Board } from '@sudobility/sudojo_types';\nimport { hasRequiredEntitlement, isValidLevel } from '@sudobility/sudojo_types';\nimport type { NetworkClient } from '@sudobility/types';\nimport { useResolvedSudojoApi } from '../context/SudojoApiContext';\nimport { useSudojoRandomBoard } from '@sudobility/sudojo_client';\nimport { useQueryClient } from '@tanstack/react-query';\nimport {\n type GameFetchResponse,\n type GameFetchStatus,\n getGameFetchStatus,\n} from '../utils/gameFetchStatus';\n\nexport type { GameFetchStatus } from '../utils/gameFetchStatus';\n\nexport interface UseLevelGameOptions {\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 */\n token?: string | undefined;\n /** Level number (1-12) to fetch game for */\n level: number;\n /** Whether to fetch only symmetrical puzzles */\n symmetrical?: boolean;\n /** Whether subscription is currently active */\n subscriptionActive?: boolean;\n /** The user's active entitlement IDs (e.g., ['blue_belt'] or ['red_belt']) */\n userEntitlements?: string[];\n /** The entitlement field from the current level (e.g., \"blue_belt,red_belt\") */\n levelEntitlement?: string | null;\n /** Whether to enable the query */\n enabled?: boolean;\n}\n\nexport interface UseLevelGameResult {\n /** The game board data */\n board: Board | null;\n /** Current status of the game fetch */\n status: GameFetchStatus;\n /** Whether data is loading */\n isLoading: boolean;\n /** Error if fetch failed */\n error: Error | null;\n /** Refetch the game board */\n refetch: () => void;\n /** Fetch a new random board for this level */\n nextPuzzle: () => void;\n /** The entitlement string required for this level (for paywall display) */\n requiredEntitlement: string | null;\n}\n\n/**\n * Hook for fetching a game board for a specific level\n *\n * Automatically refetches when auth token or subscription status changes.\n * Returns status indicating whether auth or subscription is required.\n *\n * @param options - Hook options\n * @returns Game board data and status\n *\n * @example\n * ```tsx\n * function LevelPlayPage({ level }: { level: number }) {\n * const { board, status, refetch, nextPuzzle } = useLevelGame({\n * networkClient,\n * config,\n * auth,\n * level,\n * subscriptionActive: subscription.isActive,\n * });\n *\n * if (status === 'loading') return <Loading />;\n * if (status === 'auth_required') return <AuthRequired onLogin={openAuthModal} />;\n * if (status === 'subscription_required') return <SubscriptionPaywall onSuccess={refetch} />;\n * if (status === 'error') return <Error />;\n *\n * return <SudokuGame puzzle={board.board} solution={board.solution} />;\n * }\n * ```\n */\nexport function useLevelGame(options: UseLevelGameOptions): UseLevelGameResult {\n const {\n level,\n symmetrical,\n userEntitlements,\n levelEntitlement,\n enabled = true,\n } = options;\n const { networkClient, baseUrl, token } = useResolvedSudojoApi(\n options,\n 'useLevelGame'\n );\n\n // Client-side entitlement check before making API call\n const entitlementDenied = useMemo(\n () => !hasRequiredEntitlement(levelEntitlement, userEntitlements ?? []),\n [levelEntitlement, userEntitlements]\n );\n\n const queryClient = useQueryClient();\n\n const queryParams = useMemo(\n () => ({\n level,\n symmetrical: symmetrical || undefined,\n limit: undefined,\n offset: undefined,\n techniques: undefined,\n technique_bit: undefined,\n }),\n [level, symmetrical]\n );\n\n const { data, isLoading, error, refetch } = useSudojoRandomBoard(\n networkClient,\n baseUrl,\n token,\n queryParams,\n { enabled: enabled && isValidLevel(level) && !entitlementDenied }\n );\n\n // Determine status based on response (incl. the legacy `action` field)\n const status = useMemo(\n (): GameFetchStatus =>\n getGameFetchStatus({\n isLoading,\n response: data as GameFetchResponse | undefined,\n error,\n entitlementDenied,\n }),\n [entitlementDenied, isLoading, data, error]\n );\n\n const board = useMemo(() => {\n if (data?.success && data.data) {\n return data.data;\n }\n return null;\n }, [data]);\n\n const nextPuzzle = useMemo(() => {\n return () => {\n queryClient.invalidateQueries({\n queryKey: ['sudojo', 'boards', 'random'],\n });\n refetch();\n };\n }, [queryClient, refetch]);\n\n return {\n board,\n status,\n isLoading,\n error: error ?? null,\n refetch: () => refetch(),\n nextPuzzle,\n requiredEntitlement: levelEntitlement ?? null,\n };\n}\n"]}
|
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
import type { Level } from '@sudobility/sudojo_types';
|
|
5
5
|
import type { NetworkClient } from '@sudobility/types';
|
|
6
6
|
export interface UseLevelsOptions {
|
|
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
|
/** Whether to enable the query */
|
|
@@ -59,10 +59,10 @@ export interface UseLevelsResult {
|
|
|
59
59
|
*/
|
|
60
60
|
export declare function useLevels(options: UseLevelsOptions): UseLevelsResult;
|
|
61
61
|
export interface UseLevelOptions {
|
|
62
|
-
/** Network client for API calls */
|
|
63
|
-
networkClient
|
|
64
|
-
/** Base URL for the Sudojo API */
|
|
65
|
-
baseUrl
|
|
62
|
+
/** Network client for API calls (default: SudojoApiProvider) */
|
|
63
|
+
networkClient?: NetworkClient | undefined;
|
|
64
|
+
/** Base URL for the Sudojo API (default: SudojoApiProvider) */
|
|
65
|
+
baseUrl?: string | undefined;
|
|
66
66
|
/** Access token for authentication (optional for public data) */
|
|
67
67
|
token?: string;
|
|
68
68
|
/** Level number (1-12) to fetch */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLevels.d.ts","sourceRoot":"","sources":["../../src/hooks/useLevels.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"useLevels.d.ts","sourceRoot":"","sources":["../../src/hooks/useLevels.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAEtD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIvD,MAAM,WAAW,gBAAgB;IAC/B,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,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,2BAA2B;IAC3B,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,iCAAiC;IACjC,SAAS,EAAE,OAAO,CAAC;IACnB,8BAA8B;IAC9B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,qBAAqB;IACrB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,qCAAqC;IACrC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,KAAK,GAAG,SAAS,CAAC;IAC/C,oCAAoC;IACpC,YAAY,EAAE,KAAK,EAAE,CAAC;IACtB,6CAA6C;IAC7C,UAAU,EAAE,KAAK,EAAE,CAAC;IACpB,6CAA6C;IAC7C,aAAa,EAAE,KAAK,EAAE,CAAC;CACxB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,GAAG,eAAe,CAgDpE;AAED,MAAM,WAAW,eAAe;IAC9B,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,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,wBAAwB;IACxB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,+BAA+B;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,8BAA8B;IAC9B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,oBAAoB;IACpB,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,cAAc,CA8BjE"}
|
package/dist/hooks/useLevels.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Hook for fetching and managing Sudoku difficulty levels
|
|
3
3
|
*/
|
|
4
4
|
import { useMemo } from 'react';
|
|
5
|
+
import { isValidLevel } from '@sudobility/sudojo_types';
|
|
6
|
+
import { useResolvedSudojoApi } from '../context/SudojoApiContext';
|
|
5
7
|
import { useSudojoLevel, useSudojoLevels } from '@sudobility/sudojo_client';
|
|
6
8
|
/**
|
|
7
9
|
* Hook for fetching and managing difficulty levels
|
|
@@ -30,7 +32,8 @@ import { useSudojoLevel, useSudojoLevels } from '@sudobility/sudojo_client';
|
|
|
30
32
|
* ```
|
|
31
33
|
*/
|
|
32
34
|
export function useLevels(options) {
|
|
33
|
-
const {
|
|
35
|
+
const { enabled = true } = options;
|
|
36
|
+
const { networkClient, baseUrl, token } = useResolvedSudojoApi(options, 'useLevels');
|
|
34
37
|
const { data, isLoading, error, refetch } = useSudojoLevels(networkClient, baseUrl, token, { enabled });
|
|
35
38
|
const levels = useMemo(() => {
|
|
36
39
|
if (!data?.success || !data.data)
|
|
@@ -70,9 +73,10 @@ export function useLevels(options) {
|
|
|
70
73
|
* @returns Level data
|
|
71
74
|
*/
|
|
72
75
|
export function useLevel(options) {
|
|
73
|
-
const {
|
|
76
|
+
const { level, enabled = true } = options;
|
|
77
|
+
const { networkClient, baseUrl, token } = useResolvedSudojoApi(options, 'useLevel');
|
|
74
78
|
const { data, isLoading, error, refetch } = useSudojoLevel(networkClient, baseUrl, token, level, {
|
|
75
|
-
enabled: enabled && level
|
|
79
|
+
enabled: enabled && isValidLevel(level),
|
|
76
80
|
});
|
|
77
81
|
const levelData = useMemo(() => {
|
|
78
82
|
if (!data?.success || !data.data)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useLevels.js","sourceRoot":"","sources":["../../src/hooks/useLevels.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"useLevels.js","sourceRoot":"","sources":["../../src/hooks/useLevels.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Hook for fetching and managing Sudoku difficulty levels\n */\n\nimport { useMemo } from 'react';\nimport type { Level } from '@sudobility/sudojo_types';\nimport { isValidLevel } from '@sudobility/sudojo_types';\nimport type { NetworkClient } from '@sudobility/types';\nimport { useResolvedSudojoApi } from '../context/SudojoApiContext';\nimport { useSudojoLevel, useSudojoLevels } from '@sudobility/sudojo_client';\n\nexport interface UseLevelsOptions {\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 (optional for public data) */\n token?: string;\n /** Whether to enable the query */\n enabled?: boolean;\n}\n\nexport interface UseLevelsResult {\n /** All available levels */\n levels: Level[];\n /** Whether levels are loading */\n isLoading: boolean;\n /** Error if loading failed */\n error: Error | null;\n /** Refetch levels */\n refetch: () => void;\n /** Get level by its number (1-12) */\n getLevel: (level: number) => Level | undefined;\n /** Levels sorted by level number */\n sortedLevels: Level[];\n /** Free levels (no subscription required) */\n freeLevels: Level[];\n /** Premium levels (subscription required) */\n premiumLevels: Level[];\n}\n\n/**\n * Hook for fetching and managing difficulty levels\n *\n * @param options - Hook options\n * @returns Levels data and utilities\n *\n * @example\n * ```tsx\n * function LevelSelector() {\n * const { levels, isLoading, sortedLevels } = useLevels({\n * networkClient,\n * baseUrl: 'https://api.sudojo.com',\n * });\n *\n * if (isLoading) return <Loading />;\n *\n * return (\n * <ul>\n * {sortedLevels.map(level => (\n * <li key={level.level}>{level.title}</li>\n * ))}\n * </ul>\n * );\n * }\n * ```\n */\nexport function useLevels(options: UseLevelsOptions): UseLevelsResult {\n const { enabled = true } = options;\n const { networkClient, baseUrl, token } = useResolvedSudojoApi(\n options,\n 'useLevels'\n );\n\n const { data, isLoading, error, refetch } = useSudojoLevels(\n networkClient,\n baseUrl,\n token,\n { enabled }\n );\n\n const levels = useMemo(() => {\n if (!data?.success || !data.data) return [];\n return data.data;\n }, [data]);\n\n const sortedLevels = useMemo(() => {\n return [...levels].sort((a, b) => a.level - b.level);\n }, [levels]);\n\n const freeLevels = useMemo(() => {\n return sortedLevels.filter(level => !level.requires_subscription);\n }, [sortedLevels]);\n\n const premiumLevels = useMemo(() => {\n return sortedLevels.filter(level => level.requires_subscription);\n }, [sortedLevels]);\n\n const getLevel = useMemo(() => {\n const levelMap = new Map(levels.map(l => [l.level, l]));\n return (level: number) => levelMap.get(level);\n }, [levels]);\n\n return {\n levels,\n isLoading,\n error: error ?? null,\n refetch: () => {\n refetch();\n },\n getLevel,\n sortedLevels,\n freeLevels,\n premiumLevels,\n };\n}\n\nexport interface UseLevelOptions {\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 (optional for public data) */\n token?: string;\n /** Level number (1-12) to fetch */\n level: number;\n /** Whether to enable the query */\n enabled?: boolean;\n}\n\nexport interface UseLevelResult {\n /** The fetched level */\n level: Level | null;\n /** Whether level is loading */\n isLoading: boolean;\n /** Error if loading failed */\n error: Error | null;\n /** Refetch level */\n refetch: () => void;\n}\n\n/**\n * Hook for fetching a single level by number\n *\n * @param options - Hook options\n * @returns Level data\n */\nexport function useLevel(options: UseLevelOptions): UseLevelResult {\n const { level, enabled = true } = options;\n const { networkClient, baseUrl, token } = useResolvedSudojoApi(\n options,\n 'useLevel'\n );\n\n const { data, isLoading, error, refetch } = useSudojoLevel(\n networkClient,\n baseUrl,\n token,\n level,\n {\n enabled: enabled && isValidLevel(level),\n }\n );\n\n const levelData = useMemo(() => {\n if (!data?.success || !data.data) return null;\n return data.data;\n }, [data]);\n\n return {\n level: levelData,\n isLoading,\n error: error ?? null,\n refetch: () => {\n refetch();\n },\n };\n}\n"]}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for fetching a random practice puzzle for a technique, with the
|
|
3
|
+
* auth / subscription / empty states the practice screens show.
|
|
4
|
+
*/
|
|
5
|
+
import type { TechniquePractice } from '@sudobility/sudojo_types';
|
|
6
|
+
import type { NetworkClient } from '@sudobility/types';
|
|
7
|
+
import { type PracticeFetchStatus } from '../utils/gameFetchStatus';
|
|
8
|
+
export interface UsePracticeGameOptions {
|
|
9
|
+
/** Network client for API calls (default: SudojoApiProvider) */
|
|
10
|
+
networkClient?: NetworkClient | undefined;
|
|
11
|
+
/** Base URL for the Sudojo API (default: SudojoApiProvider) */
|
|
12
|
+
baseUrl?: string | undefined;
|
|
13
|
+
/** Access token (default: SudojoApiProvider) */
|
|
14
|
+
token?: string | null | undefined;
|
|
15
|
+
/** Technique number (1-60); the query is idle for anything below 1 */
|
|
16
|
+
techniqueId: number | null | undefined;
|
|
17
|
+
/** Whether to enable the query (default true) */
|
|
18
|
+
enabled?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface UsePracticeGameResult {
|
|
21
|
+
/** The practice puzzle, when loaded */
|
|
22
|
+
practice: TechniquePractice | null;
|
|
23
|
+
/** Which screen to show */
|
|
24
|
+
status: PracticeFetchStatus;
|
|
25
|
+
/** Whether the request is in flight */
|
|
26
|
+
isLoading: boolean;
|
|
27
|
+
/** The request error, if any */
|
|
28
|
+
error: Error | null;
|
|
29
|
+
/** Ask again (e.g. after a paywall purchase) */
|
|
30
|
+
refetch: () => void;
|
|
31
|
+
/** Drop the cached practice and fetch another random one */
|
|
32
|
+
nextPractice: () => void;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Fetch a random practice for a technique.
|
|
36
|
+
*
|
|
37
|
+
* The practice query is not keyed on the token, so when the status is
|
|
38
|
+
* `auth_required` and the token changes (the reader signed in over the
|
|
39
|
+
* screen) it refetches, as both apps did.
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* ```tsx
|
|
43
|
+
* const { practice, status, refetch, nextPractice } = usePracticeGame({
|
|
44
|
+
* networkClient, baseUrl, token, techniqueId,
|
|
45
|
+
* });
|
|
46
|
+
* if (status === 'auth_required') return <SignInGate />;
|
|
47
|
+
* if (status === 'subscription_required') return <Paywall onSuccess={refetch} />;
|
|
48
|
+
* if (status === 'no_practices') return <Empty />;
|
|
49
|
+
* if (status === 'ready') return <SudokuGame puzzle={practice.board} ... />;
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export declare function usePracticeGame(options: UsePracticeGameOptions): UsePracticeGameResult;
|
|
53
|
+
//# sourceMappingURL=usePracticeGame.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePracticeGame.d.ts","sourceRoot":"","sources":["../../src/hooks/usePracticeGame.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAIvD,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,0BAA0B,CAAC;AAElC,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,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClC,sEAAsE;IACtE,WAAW,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACvC,iDAAiD;IACjD,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,qBAAqB;IACpC,uCAAuC;IACvC,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACnC,2BAA2B;IAC3B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,uCAAuC;IACvC,SAAS,EAAE,OAAO,CAAC;IACnB,gCAAgC;IAChC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,gDAAgD;IAChD,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,4DAA4D;IAC5D,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,GAC9B,qBAAqB,CAsDvB"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for fetching a random practice puzzle for a technique, with the
|
|
3
|
+
* auth / subscription / empty states the practice screens show.
|
|
4
|
+
*/
|
|
5
|
+
import { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
6
|
+
import { queryKeys, useSudojoRandomPractice } from '@sudobility/sudojo_client';
|
|
7
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
8
|
+
import { useResolvedSudojoApi } from '../context/SudojoApiContext';
|
|
9
|
+
import { getPracticeFetchStatus, } from '../utils/gameFetchStatus';
|
|
10
|
+
/**
|
|
11
|
+
* Fetch a random practice for a technique.
|
|
12
|
+
*
|
|
13
|
+
* The practice query is not keyed on the token, so when the status is
|
|
14
|
+
* `auth_required` and the token changes (the reader signed in over the
|
|
15
|
+
* screen) it refetches, as both apps did.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* const { practice, status, refetch, nextPractice } = usePracticeGame({
|
|
20
|
+
* networkClient, baseUrl, token, techniqueId,
|
|
21
|
+
* });
|
|
22
|
+
* if (status === 'auth_required') return <SignInGate />;
|
|
23
|
+
* if (status === 'subscription_required') return <Paywall onSuccess={refetch} />;
|
|
24
|
+
* if (status === 'no_practices') return <Empty />;
|
|
25
|
+
* if (status === 'ready') return <SudokuGame puzzle={practice.board} ... />;
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export function usePracticeGame(options) {
|
|
29
|
+
const { techniqueId, enabled = true } = options;
|
|
30
|
+
const { networkClient, baseUrl, token } = useResolvedSudojoApi(options, 'usePracticeGame');
|
|
31
|
+
const technique = techniqueId ?? 0;
|
|
32
|
+
const validTechnique = Number.isInteger(technique) && technique >= 1;
|
|
33
|
+
const queryClient = useQueryClient();
|
|
34
|
+
const { data: response, isLoading, error, refetch, } = useSudojoRandomPractice(networkClient, baseUrl, token, technique, {
|
|
35
|
+
enabled: enabled && validTechnique,
|
|
36
|
+
staleTime: 0, // Always fetch fresh for random
|
|
37
|
+
refetchOnWindowFocus: false, // Keep the current practice on focus
|
|
38
|
+
retry: false,
|
|
39
|
+
});
|
|
40
|
+
const practice = response?.success ? (response.data ?? null) : null;
|
|
41
|
+
const status = useMemo(() => {
|
|
42
|
+
if (!validTechnique)
|
|
43
|
+
return 'no_practices';
|
|
44
|
+
return getPracticeFetchStatus({ isLoading, response, error });
|
|
45
|
+
}, [validTechnique, isLoading, response, error]);
|
|
46
|
+
// Signing in from the auth_required state asks again with the new token.
|
|
47
|
+
const lastTokenRef = useRef(token);
|
|
48
|
+
useEffect(() => {
|
|
49
|
+
if (lastTokenRef.current === token)
|
|
50
|
+
return;
|
|
51
|
+
lastTokenRef.current = token;
|
|
52
|
+
if (status === 'auth_required')
|
|
53
|
+
refetch();
|
|
54
|
+
}, [token, status, refetch]);
|
|
55
|
+
const nextPractice = useCallback(() => {
|
|
56
|
+
queryClient.invalidateQueries({
|
|
57
|
+
queryKey: queryKeys.sudojo.practiceRandom(technique),
|
|
58
|
+
});
|
|
59
|
+
refetch();
|
|
60
|
+
}, [queryClient, technique, refetch]);
|
|
61
|
+
return {
|
|
62
|
+
practice,
|
|
63
|
+
status,
|
|
64
|
+
isLoading,
|
|
65
|
+
error: error ?? null,
|
|
66
|
+
refetch: () => {
|
|
67
|
+
refetch();
|
|
68
|
+
},
|
|
69
|
+
nextPractice,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=usePracticeGame.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePracticeGame.js","sourceRoot":"","sources":["../../src/hooks/usePracticeGame.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAGhE,OAAO,EAAE,SAAS,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAC/E,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EACL,sBAAsB,GAEvB,MAAM,0BAA0B,CAAC;AA8BlC;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,eAAe,CAC7B,OAA+B;IAE/B,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAChD,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,oBAAoB,CAC5D,OAAO,EACP,iBAAiB,CAClB,CAAC;IACF,MAAM,SAAS,GAAG,WAAW,IAAI,CAAC,CAAC;IACnC,MAAM,cAAc,GAAG,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC,CAAC;IACrE,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IAErC,MAAM,EACJ,IAAI,EAAE,QAAQ,EACd,SAAS,EACT,KAAK,EACL,OAAO,GACR,GAAG,uBAAuB,CAAC,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE;QACpE,OAAO,EAAE,OAAO,IAAI,cAAc;QAClC,SAAS,EAAE,CAAC,EAAE,gCAAgC;QAC9C,oBAAoB,EAAE,KAAK,EAAE,qCAAqC;QAClE,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;IAEH,MAAM,QAAQ,GAAG,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAEpE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAwB,EAAE;QAC/C,IAAI,CAAC,cAAc;YAAE,OAAO,cAAc,CAAC;QAC3C,OAAO,sBAAsB,CAAC,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,CAAC;IAChE,CAAC,EAAE,CAAC,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC;IAEjD,yEAAyE;IACzE,MAAM,YAAY,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IACnC,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,YAAY,CAAC,OAAO,KAAK,KAAK;YAAE,OAAO;QAC3C,YAAY,CAAC,OAAO,GAAG,KAAK,CAAC;QAC7B,IAAI,MAAM,KAAK,eAAe;YAAE,OAAO,EAAE,CAAC;IAC5C,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAE7B,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE;QACpC,WAAW,CAAC,iBAAiB,CAAC;YAC5B,QAAQ,EAAE,SAAS,CAAC,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC;SACrD,CAAC,CAAC;QACH,OAAO,EAAE,CAAC;IACZ,CAAC,EAAE,CAAC,WAAW,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;IAEtC,OAAO;QACL,QAAQ;QACR,MAAM;QACN,SAAS;QACT,KAAK,EAAE,KAAK,IAAI,IAAI;QACpB,OAAO,EAAE,GAAG,EAAE;YACZ,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,YAAY;KACb,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Hook for fetching a random practice puzzle for a technique, with the\n * auth / subscription / empty states the practice screens show.\n */\n\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\nimport type { TechniquePractice } from '@sudobility/sudojo_types';\nimport type { NetworkClient } from '@sudobility/types';\nimport { queryKeys, useSudojoRandomPractice } from '@sudobility/sudojo_client';\nimport { useQueryClient } from '@tanstack/react-query';\nimport { useResolvedSudojoApi } from '../context/SudojoApiContext';\nimport {\n getPracticeFetchStatus,\n type PracticeFetchStatus,\n} from '../utils/gameFetchStatus';\n\nexport interface UsePracticeGameOptions {\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 /** Technique number (1-60); the query is idle for anything below 1 */\n techniqueId: number | null | undefined;\n /** Whether to enable the query (default true) */\n enabled?: boolean;\n}\n\nexport interface UsePracticeGameResult {\n /** The practice puzzle, when loaded */\n practice: TechniquePractice | null;\n /** Which screen to show */\n status: PracticeFetchStatus;\n /** Whether the request is in flight */\n isLoading: boolean;\n /** The request error, if any */\n error: Error | null;\n /** Ask again (e.g. after a paywall purchase) */\n refetch: () => void;\n /** Drop the cached practice and fetch another random one */\n nextPractice: () => void;\n}\n\n/**\n * Fetch a random practice for a technique.\n *\n * The practice query is not keyed on the token, so when the status is\n * `auth_required` and the token changes (the reader signed in over the\n * screen) it refetches, as both apps did.\n *\n * @example\n * ```tsx\n * const { practice, status, refetch, nextPractice } = usePracticeGame({\n * networkClient, baseUrl, token, techniqueId,\n * });\n * if (status === 'auth_required') return <SignInGate />;\n * if (status === 'subscription_required') return <Paywall onSuccess={refetch} />;\n * if (status === 'no_practices') return <Empty />;\n * if (status === 'ready') return <SudokuGame puzzle={practice.board} ... />;\n * ```\n */\nexport function usePracticeGame(\n options: UsePracticeGameOptions\n): UsePracticeGameResult {\n const { techniqueId, enabled = true } = options;\n const { networkClient, baseUrl, token } = useResolvedSudojoApi(\n options,\n 'usePracticeGame'\n );\n const technique = techniqueId ?? 0;\n const validTechnique = Number.isInteger(technique) && technique >= 1;\n const queryClient = useQueryClient();\n\n const {\n data: response,\n isLoading,\n error,\n refetch,\n } = useSudojoRandomPractice(networkClient, baseUrl, token, technique, {\n enabled: enabled && validTechnique,\n staleTime: 0, // Always fetch fresh for random\n refetchOnWindowFocus: false, // Keep the current practice on focus\n retry: false,\n });\n\n const practice = response?.success ? (response.data ?? null) : null;\n\n const status = useMemo((): PracticeFetchStatus => {\n if (!validTechnique) return 'no_practices';\n return getPracticeFetchStatus({ isLoading, response, error });\n }, [validTechnique, isLoading, response, error]);\n\n // Signing in from the auth_required state asks again with the new token.\n const lastTokenRef = useRef(token);\n useEffect(() => {\n if (lastTokenRef.current === token) return;\n lastTokenRef.current = token;\n if (status === 'auth_required') refetch();\n }, [token, status, refetch]);\n\n const nextPractice = useCallback(() => {\n queryClient.invalidateQueries({\n queryKey: queryKeys.sudojo.practiceRandom(technique),\n });\n refetch();\n }, [queryClient, technique, refetch]);\n\n return {\n practice,\n status,\n isLoading,\n error: error ?? null,\n refetch: () => {\n refetch();\n },\n nextPractice,\n };\n}\n"]}
|
|
@@ -8,6 +8,8 @@ import type { Level } from '@sudobility/sudojo_types';
|
|
|
8
8
|
* - No offerId — only free levels (no subscription required)
|
|
9
9
|
* - `'1_blue_belt'` — free levels + blue_belt levels
|
|
10
10
|
* - Anything else — returns 1.0 (all puzzles)
|
|
11
|
+
*
|
|
12
|
+
* Memoized wrapper around the pure getPuzzleDistribution.
|
|
11
13
|
*/
|
|
12
14
|
export declare function usePuzzleDistribution(levels: Level[], offerId?: string | null): number;
|
|
13
15
|
//# sourceMappingURL=usePuzzleDistribution.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePuzzleDistribution.d.ts","sourceRoot":"","sources":["../../src/hooks/usePuzzleDistribution.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAGtD
|
|
1
|
+
{"version":3,"file":"usePuzzleDistribution.d.ts","sourceRoot":"","sources":["../../src/hooks/usePuzzleDistribution.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAGtD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,KAAK,EAAE,EACf,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,GACtB,MAAM,CAKR"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { getPuzzleDistribution } from '../utils/subscription';
|
|
3
3
|
/**
|
|
4
4
|
* Compute the fraction of puzzles accessible for a given offering.
|
|
5
5
|
*
|
|
@@ -9,20 +9,10 @@ import { getSubscriptionOfferId } from '@sudobility/sudojo_types';
|
|
|
9
9
|
* - No offerId — only free levels (no subscription required)
|
|
10
10
|
* - `'1_blue_belt'` — free levels + blue_belt levels
|
|
11
11
|
* - Anything else — returns 1.0 (all puzzles)
|
|
12
|
+
*
|
|
13
|
+
* Memoized wrapper around the pure getPuzzleDistribution.
|
|
12
14
|
*/
|
|
13
15
|
export function usePuzzleDistribution(levels, offerId) {
|
|
14
|
-
return useMemo(() =>
|
|
15
|
-
if (offerId && offerId !== '1_blue_belt')
|
|
16
|
-
return 1.0;
|
|
17
|
-
let total = 0;
|
|
18
|
-
for (const level of levels) {
|
|
19
|
-
const levelOfferId = getSubscriptionOfferId(level.entitlement);
|
|
20
|
-
if (!levelOfferId ||
|
|
21
|
-
(offerId === '1_blue_belt' && levelOfferId === '1_blue_belt')) {
|
|
22
|
-
total += level.percentage ?? 0;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return total;
|
|
26
|
-
}, [levels, offerId]);
|
|
16
|
+
return useMemo(() => getPuzzleDistribution(levels, offerId), [levels, offerId]);
|
|
27
17
|
}
|
|
28
18
|
//# sourceMappingURL=usePuzzleDistribution.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"usePuzzleDistribution.js","sourceRoot":"","sources":["../../src/hooks/usePuzzleDistribution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAEhC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"usePuzzleDistribution.js","sourceRoot":"","sources":["../../src/hooks/usePuzzleDistribution.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAEhC,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAE9D;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,qBAAqB,CACnC,MAAe,EACf,OAAuB;IAEvB,OAAO,OAAO,CACZ,GAAG,EAAE,CAAC,qBAAqB,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5C,CAAC,MAAM,EAAE,OAAO,CAAC,CAClB,CAAC;AACJ,CAAC","sourcesContent":["import { useMemo } from 'react';\nimport type { Level } from '@sudobility/sudojo_types';\nimport { getPuzzleDistribution } from '../utils/subscription';\n\n/**\n * Compute the fraction of puzzles accessible for a given offering.\n *\n * Sums the `percentage` field (decimal 0–1) of matching levels based\n * on their derived subscription offer ID:\n *\n * - No offerId — only free levels (no subscription required)\n * - `'1_blue_belt'` — free levels + blue_belt levels\n * - Anything else — returns 1.0 (all puzzles)\n *\n * Memoized wrapper around the pure getPuzzleDistribution.\n */\nexport function usePuzzleDistribution(\n levels: Level[],\n offerId?: string | null\n): number {\n return useMemo(\n () => getPuzzleDistribution(levels, offerId),\n [levels, offerId]\n );\n}\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook for the player's puzzle progress (completed puzzles, streak, stats),
|
|
3
|
+
* persisted in localStorage under PROGRESS_STORAGE_KEY.
|
|
4
|
+
*
|
|
5
|
+
* The web ProgressProvider's logic. RN does not track progress today; where
|
|
6
|
+
* it shims localStorage (as it does for the game store) the hook works there
|
|
7
|
+
* too.
|
|
8
|
+
*/
|
|
9
|
+
import type { UserProgress } from '../types/progress';
|
|
10
|
+
import { type PuzzleCompletion } from '../utils/progress';
|
|
11
|
+
export interface UsePuzzleProgressOptions {
|
|
12
|
+
/** localStorage key (default PROGRESS_STORAGE_KEY, 'sudojo-progress') */
|
|
13
|
+
storageKey?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface UsePuzzleProgressResult {
|
|
16
|
+
/** The stored progress */
|
|
17
|
+
progress: UserProgress;
|
|
18
|
+
/** Record a completion (no-op when the puzzle is already completed) */
|
|
19
|
+
markCompleted: (puzzle: PuzzleCompletion) => void;
|
|
20
|
+
/** Whether a puzzle has been completed */
|
|
21
|
+
isCompleted: (type: 'daily' | 'level', id: string) => boolean;
|
|
22
|
+
/** Ids of completed level puzzles */
|
|
23
|
+
getCompletedLevelIds: () => string[];
|
|
24
|
+
/** Dates (YYYY-MM-DD) of completed dailies */
|
|
25
|
+
getCompletedDailyDates: () => string[];
|
|
26
|
+
/** Reset to DEFAULT_PROGRESS */
|
|
27
|
+
resetProgress: () => void;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* @example
|
|
31
|
+
* ```tsx
|
|
32
|
+
* const { markCompleted, isCompleted } = usePuzzleProgress();
|
|
33
|
+
* markCompleted({ type: 'daily', id: '2026-10-05', timeSeconds: 312 });
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function usePuzzleProgress(options?: UsePuzzleProgressOptions): UsePuzzleProgressResult;
|
|
37
|
+
//# sourceMappingURL=usePuzzleProgress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePuzzleProgress.d.ts","sourceRoot":"","sources":["../../src/hooks/usePuzzleProgress.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,OAAO,EAKL,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAG3B,MAAM,WAAW,wBAAwB;IACvC,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC,0BAA0B;IAC1B,QAAQ,EAAE,YAAY,CAAC;IACvB,uEAAuE;IACvE,aAAa,EAAE,CAAC,MAAM,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,0CAA0C;IAC1C,WAAW,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9D,qCAAqC;IACrC,oBAAoB,EAAE,MAAM,MAAM,EAAE,CAAC;IACrC,8CAA8C;IAC9C,sBAAsB,EAAE,MAAM,MAAM,EAAE,CAAC;IACvC,gCAAgC;IAChC,aAAa,EAAE,MAAM,IAAI,CAAC;CAC3B;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,GAAE,wBAA6B,GACrC,uBAAuB,CAoDzB"}
|