@sudobility/sudojo_lib 0.0.200 → 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.
Files changed (263) hide show
  1. package/dist/admin/adminApi.d.ts +34 -0
  2. package/dist/admin/adminApi.d.ts.map +1 -0
  3. package/dist/admin/adminApi.js +59 -0
  4. package/dist/admin/adminApi.js.map +1 -0
  5. package/dist/admin/boardRules.d.ts +75 -0
  6. package/dist/admin/boardRules.d.ts.map +1 -0
  7. package/dist/admin/boardRules.js +127 -0
  8. package/dist/admin/boardRules.js.map +1 -0
  9. package/dist/admin/boardStrings.d.ts +55 -0
  10. package/dist/admin/boardStrings.d.ts.map +1 -0
  11. package/dist/admin/boardStrings.js +99 -0
  12. package/dist/admin/boardStrings.js.map +1 -0
  13. package/dist/admin/examples.d.ts +58 -0
  14. package/dist/admin/examples.d.ts.map +1 -0
  15. package/dist/admin/examples.js +104 -0
  16. package/dist/admin/examples.js.map +1 -0
  17. package/dist/admin/extraction.d.ts +87 -0
  18. package/dist/admin/extraction.d.ts.map +1 -0
  19. package/dist/admin/extraction.js +141 -0
  20. package/dist/admin/extraction.js.map +1 -0
  21. package/dist/admin/index.d.ts +32 -0
  22. package/dist/admin/index.d.ts.map +1 -0
  23. package/dist/admin/index.js +22 -0
  24. package/dist/admin/index.js.map +1 -0
  25. package/dist/admin/jobs.d.ts +108 -0
  26. package/dist/admin/jobs.d.ts.map +1 -0
  27. package/dist/admin/jobs.js +435 -0
  28. package/dist/admin/jobs.js.map +1 -0
  29. package/dist/admin/regenerateHints.d.ts +16 -0
  30. package/dist/admin/regenerateHints.d.ts.map +1 -0
  31. package/dist/admin/regenerateHints.js +43 -0
  32. package/dist/admin/regenerateHints.js.map +1 -0
  33. package/dist/admin/types.d.ts +42 -0
  34. package/dist/admin/types.d.ts.map +1 -0
  35. package/dist/admin/types.js +21 -0
  36. package/dist/admin/types.js.map +1 -0
  37. package/dist/admin/useAdminJob.d.ts +28 -0
  38. package/dist/admin/useAdminJob.d.ts.map +1 -0
  39. package/dist/admin/useAdminJob.js +99 -0
  40. package/dist/admin/useAdminJob.js.map +1 -0
  41. package/dist/admin/useAdminStats.d.ts +46 -0
  42. package/dist/admin/useAdminStats.d.ts.map +1 -0
  43. package/dist/admin/useAdminStats.js +91 -0
  44. package/dist/admin/useAdminStats.js.map +1 -0
  45. package/dist/admin/useBoardGenerator.d.ts +29 -0
  46. package/dist/admin/useBoardGenerator.d.ts.map +1 -0
  47. package/dist/admin/useBoardGenerator.js +38 -0
  48. package/dist/admin/useBoardGenerator.js.map +1 -0
  49. package/dist/admin/useBoardTechniquesUpdater.d.ts +14 -0
  50. package/dist/admin/useBoardTechniquesUpdater.d.ts.map +1 -0
  51. package/dist/admin/useBoardTechniquesUpdater.js +28 -0
  52. package/dist/admin/useBoardTechniquesUpdater.js.map +1 -0
  53. package/dist/admin/useExampleCreator.d.ts +29 -0
  54. package/dist/admin/useExampleCreator.d.ts.map +1 -0
  55. package/dist/admin/useExampleCreator.js +58 -0
  56. package/dist/admin/useExampleCreator.js.map +1 -0
  57. package/dist/admin/useSingleBoardExtractor.d.ts +18 -0
  58. package/dist/admin/useSingleBoardExtractor.d.ts.map +1 -0
  59. package/dist/admin/useSingleBoardExtractor.js +19 -0
  60. package/dist/admin/useSingleBoardExtractor.js.map +1 -0
  61. package/dist/admin/useTechniqueExtractor.d.ts +26 -0
  62. package/dist/admin/useTechniqueExtractor.d.ts.map +1 -0
  63. package/dist/admin/useTechniqueExtractor.js +37 -0
  64. package/dist/admin/useTechniqueExtractor.js.map +1 -0
  65. package/dist/context/SudojoApiContext.d.ts +58 -0
  66. package/dist/context/SudojoApiContext.d.ts.map +1 -0
  67. package/dist/context/SudojoApiContext.js +52 -0
  68. package/dist/context/SudojoApiContext.js.map +1 -0
  69. package/dist/context/SudojoApiProvider.d.ts +21 -0
  70. package/dist/context/SudojoApiProvider.d.ts.map +1 -0
  71. package/dist/context/SudojoApiProvider.js +15 -0
  72. package/dist/context/SudojoApiProvider.js.map +1 -0
  73. package/dist/context/index.d.ts +3 -0
  74. package/dist/context/index.d.ts.map +1 -1
  75. package/dist/context/index.js +2 -0
  76. package/dist/context/index.js.map +1 -1
  77. package/dist/hooks/index.d.ts +24 -1
  78. package/dist/hooks/index.d.ts.map +1 -1
  79. package/dist/hooks/index.js +22 -0
  80. package/dist/hooks/index.js.map +1 -1
  81. package/dist/hooks/useBoardEntry.d.ts +70 -6
  82. package/dist/hooks/useBoardEntry.d.ts.map +1 -1
  83. package/dist/hooks/useBoardEntry.js +128 -28
  84. package/dist/hooks/useBoardEntry.js.map +1 -1
  85. package/dist/hooks/useBoardScan.d.ts +27 -0
  86. package/dist/hooks/useBoardScan.d.ts.map +1 -0
  87. package/dist/hooks/useBoardScan.js +27 -0
  88. package/dist/hooks/useBoardScan.js.map +1 -0
  89. package/dist/hooks/useCommunities.d.ts +6 -4
  90. package/dist/hooks/useCommunities.d.ts.map +1 -1
  91. package/dist/hooks/useCommunities.js +6 -1
  92. package/dist/hooks/useCommunities.js.map +1 -1
  93. package/dist/hooks/useCompletionTrigger.d.ts +23 -0
  94. package/dist/hooks/useCompletionTrigger.d.ts.map +1 -0
  95. package/dist/hooks/useCompletionTrigger.js +39 -0
  96. package/dist/hooks/useCompletionTrigger.js.map +1 -0
  97. package/dist/hooks/useDailyGame.d.ts +11 -6
  98. package/dist/hooks/useDailyGame.d.ts.map +1 -1
  99. package/dist/hooks/useDailyGame.js +20 -61
  100. package/dist/hooks/useDailyGame.js.map +1 -1
  101. package/dist/hooks/useDeleteAccount.d.ts +72 -0
  102. package/dist/hooks/useDeleteAccount.d.ts.map +1 -0
  103. package/dist/hooks/useDeleteAccount.js +71 -0
  104. package/dist/hooks/useDeleteAccount.js.map +1 -0
  105. package/dist/hooks/useEnteredGameSession.d.ts +73 -0
  106. package/dist/hooks/useEnteredGameSession.d.ts.map +1 -0
  107. package/dist/hooks/useEnteredGameSession.js +155 -0
  108. package/dist/hooks/useEnteredGameSession.js.map +1 -0
  109. package/dist/hooks/useGameSession.d.ts +8 -4
  110. package/dist/hooks/useGameSession.d.ts.map +1 -1
  111. package/dist/hooks/useGameSession.js +4 -1
  112. package/dist/hooks/useGameSession.js.map +1 -1
  113. package/dist/hooks/useGameTeaching.d.ts.map +1 -1
  114. package/dist/hooks/useGameTeaching.js +11 -13
  115. package/dist/hooks/useGameTeaching.js.map +1 -1
  116. package/dist/hooks/useHint.d.ts +7 -7
  117. package/dist/hooks/useHint.d.ts.map +1 -1
  118. package/dist/hooks/useHint.js +18 -9
  119. package/dist/hooks/useHint.js.map +1 -1
  120. package/dist/hooks/useIsSiteAdmin.d.ts +39 -0
  121. package/dist/hooks/useIsSiteAdmin.d.ts.map +1 -0
  122. package/dist/hooks/useIsSiteAdmin.js +31 -0
  123. package/dist/hooks/useIsSiteAdmin.js.map +1 -0
  124. package/dist/hooks/useLearning.d.ts +8 -8
  125. package/dist/hooks/useLearning.d.ts.map +1 -1
  126. package/dist/hooks/useLearning.js +5 -2
  127. package/dist/hooks/useLearning.js.map +1 -1
  128. package/dist/hooks/useLevelGame.d.ts +7 -7
  129. package/dist/hooks/useLevelGame.d.ts.map +1 -1
  130. package/dist/hooks/useLevelGame.js +13 -49
  131. package/dist/hooks/useLevelGame.js.map +1 -1
  132. package/dist/hooks/useLevels.d.ts +8 -8
  133. package/dist/hooks/useLevels.d.ts.map +1 -1
  134. package/dist/hooks/useLevels.js +7 -3
  135. package/dist/hooks/useLevels.js.map +1 -1
  136. package/dist/hooks/usePracticeGame.d.ts +53 -0
  137. package/dist/hooks/usePracticeGame.d.ts.map +1 -0
  138. package/dist/hooks/usePracticeGame.js +72 -0
  139. package/dist/hooks/usePracticeGame.js.map +1 -0
  140. package/dist/hooks/usePuzzleDistribution.d.ts +2 -0
  141. package/dist/hooks/usePuzzleDistribution.d.ts.map +1 -1
  142. package/dist/hooks/usePuzzleDistribution.js +4 -14
  143. package/dist/hooks/usePuzzleDistribution.js.map +1 -1
  144. package/dist/hooks/usePuzzleProgress.d.ts +37 -0
  145. package/dist/hooks/usePuzzleProgress.d.ts.map +1 -0
  146. package/dist/hooks/usePuzzleProgress.js +48 -0
  147. package/dist/hooks/usePuzzleProgress.js.map +1 -0
  148. package/dist/hooks/usePuzzleSession.d.ts +215 -0
  149. package/dist/hooks/usePuzzleSession.d.ts.map +1 -0
  150. package/dist/hooks/usePuzzleSession.js +358 -0
  151. package/dist/hooks/usePuzzleSession.js.map +1 -0
  152. package/dist/hooks/useResumeGame.d.ts +70 -0
  153. package/dist/hooks/useResumeGame.d.ts.map +1 -0
  154. package/dist/hooks/useResumeGame.js +123 -0
  155. package/dist/hooks/useResumeGame.js.map +1 -0
  156. package/dist/hooks/useStrategies.d.ts +39 -0
  157. package/dist/hooks/useStrategies.d.ts.map +1 -0
  158. package/dist/hooks/useStrategies.js +34 -0
  159. package/dist/hooks/useStrategies.js.map +1 -0
  160. package/dist/hooks/useSudoku.d.ts +5 -0
  161. package/dist/hooks/useSudoku.d.ts.map +1 -1
  162. package/dist/hooks/useSudoku.js +7 -1
  163. package/dist/hooks/useSudoku.js.map +1 -1
  164. package/dist/hooks/useTechniqueByPath.d.ts +46 -0
  165. package/dist/hooks/useTechniqueByPath.d.ts.map +1 -0
  166. package/dist/hooks/useTechniqueByPath.js +47 -0
  167. package/dist/hooks/useTechniqueByPath.js.map +1 -0
  168. package/dist/hooks/useTechniqueExample.d.ts +60 -0
  169. package/dist/hooks/useTechniqueExample.d.ts.map +1 -0
  170. package/dist/hooks/useTechniqueExample.js +63 -0
  171. package/dist/hooks/useTechniqueExample.js.map +1 -0
  172. package/dist/hooks/useTechniques.d.ts +8 -8
  173. package/dist/hooks/useTechniques.d.ts.map +1 -1
  174. package/dist/hooks/useTechniques.js +9 -21
  175. package/dist/hooks/useTechniques.js.map +1 -1
  176. package/dist/index.d.ts +418 -11
  177. package/dist/index.d.ts.map +1 -1
  178. package/dist/index.js +276 -10
  179. package/dist/index.js.map +1 -1
  180. package/dist/types/currentGame.d.ts +16 -1
  181. package/dist/types/currentGame.d.ts.map +1 -1
  182. package/dist/types/currentGame.js.map +1 -1
  183. package/dist/utils/auth.d.ts +6 -0
  184. package/dist/utils/auth.d.ts.map +1 -1
  185. package/dist/utils/auth.js +8 -0
  186. package/dist/utils/auth.js.map +1 -1
  187. package/dist/utils/community.d.ts +64 -0
  188. package/dist/utils/community.d.ts.map +1 -0
  189. package/dist/utils/community.js +66 -0
  190. package/dist/utils/community.js.map +1 -0
  191. package/dist/utils/date.d.ts +37 -0
  192. package/dist/utils/date.d.ts.map +1 -0
  193. package/dist/utils/date.js +68 -0
  194. package/dist/utils/date.js.map +1 -0
  195. package/dist/utils/entityTranslate.d.ts +120 -0
  196. package/dist/utils/entityTranslate.d.ts.map +1 -0
  197. package/dist/utils/entityTranslate.js +146 -0
  198. package/dist/utils/entityTranslate.js.map +1 -0
  199. package/dist/utils/gameFetchStatus.d.ts +74 -0
  200. package/dist/utils/gameFetchStatus.d.ts.map +1 -0
  201. package/dist/utils/gameFetchStatus.js +118 -0
  202. package/dist/utils/gameFetchStatus.js.map +1 -0
  203. package/dist/utils/gamification.d.ts +36 -0
  204. package/dist/utils/gamification.d.ts.map +1 -0
  205. package/dist/utils/gamification.js +33 -0
  206. package/dist/utils/gamification.js.map +1 -0
  207. package/dist/utils/hintAccess.d.ts +64 -0
  208. package/dist/utils/hintAccess.d.ts.map +1 -0
  209. package/dist/utils/hintAccess.js +57 -0
  210. package/dist/utils/hintAccess.js.map +1 -0
  211. package/dist/utils/index.d.ts +31 -10
  212. package/dist/utils/index.d.ts.map +1 -1
  213. package/dist/utils/index.js +20 -9
  214. package/dist/utils/index.js.map +1 -1
  215. package/dist/utils/language.d.ts +123 -0
  216. package/dist/utils/language.d.ts.map +1 -0
  217. package/dist/utils/language.js +95 -0
  218. package/dist/utils/language.js.map +1 -0
  219. package/dist/utils/localizedHint.d.ts +60 -0
  220. package/dist/utils/localizedHint.d.ts.map +1 -1
  221. package/dist/utils/localizedHint.js +90 -0
  222. package/dist/utils/localizedHint.js.map +1 -1
  223. package/dist/utils/progress.d.ts +10 -1
  224. package/dist/utils/progress.d.ts.map +1 -1
  225. package/dist/utils/progress.js +24 -0
  226. package/dist/utils/progress.js.map +1 -1
  227. package/dist/utils/scannedBoard.d.ts +79 -0
  228. package/dist/utils/scannedBoard.d.ts.map +1 -0
  229. package/dist/utils/scannedBoard.js +96 -0
  230. package/dist/utils/scannedBoard.js.map +1 -0
  231. package/dist/utils/shareUrl.d.ts +38 -15
  232. package/dist/utils/shareUrl.d.ts.map +1 -1
  233. package/dist/utils/shareUrl.js +75 -26
  234. package/dist/utils/shareUrl.js.map +1 -1
  235. package/dist/utils/subscription.d.ts +118 -0
  236. package/dist/utils/subscription.d.ts.map +1 -1
  237. package/dist/utils/subscription.js +212 -0
  238. package/dist/utils/subscription.js.map +1 -1
  239. package/dist/utils/sudokuPresenter.d.ts +42 -0
  240. package/dist/utils/sudokuPresenter.d.ts.map +1 -1
  241. package/dist/utils/sudokuPresenter.js +114 -1
  242. package/dist/utils/sudokuPresenter.js.map +1 -1
  243. package/dist/utils/sudokuScrambler.d.ts +8 -0
  244. package/dist/utils/sudokuScrambler.d.ts.map +1 -1
  245. package/dist/utils/sudokuScrambler.js +12 -0
  246. package/dist/utils/sudokuScrambler.js.map +1 -1
  247. package/dist/utils/technique.d.ts +112 -1
  248. package/dist/utils/technique.d.ts.map +1 -1
  249. package/dist/utils/technique.js +293 -1
  250. package/dist/utils/technique.js.map +1 -1
  251. package/dist/utils/techniqueBitmask.d.ts +31 -0
  252. package/dist/utils/techniqueBitmask.d.ts.map +1 -0
  253. package/dist/utils/techniqueBitmask.js +53 -0
  254. package/dist/utils/techniqueBitmask.js.map +1 -0
  255. package/dist/utils/techniqueExample.d.ts +42 -0
  256. package/dist/utils/techniqueExample.d.ts.map +1 -0
  257. package/dist/utils/techniqueExample.js +57 -0
  258. package/dist/utils/techniqueExample.js.map +1 -0
  259. package/dist/utils/theme.d.ts +2 -0
  260. package/dist/utils/theme.d.ts.map +1 -1
  261. package/dist/utils/theme.js +2 -0
  262. package/dist/utils/theme.js.map +1 -1
  263. package/package.json +7 -7
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePuzzleSession.js","sourceRoot":"","sources":["../../src/hooks/usePuzzleSession.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Game session orchestration for the Daily and Level play screens.\n *\n * Combines the saved-game store (useGamePlay), the resume decision\n * (useResumeGame) and server gamification (useGameSession) into what a play\n * screen renders: which puzzle to show and how (fresh and scrambled, or\n * resumed with its saved input), when to save the game and start the server\n * session, and what to do on completion (clear the save, finish the session,\n * show achievements, record progress). Platform-neutral: no router, no\n * dialogs.\n */\n\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport type { GameStartRequest } from '@sudobility/sudojo_types';\nimport type { GameFetchStatus } from '../utils/gameFetchStatus';\nimport type { SudojoApiOptions } from '../context/SudojoApiContext';\nimport { useResolvedSudojoApi } from '../context/SudojoApiContext';\nimport type { CurrentGame, CurrentGameMeta } from '../types/currentGame';\nimport { type AuthUser, isRealUser } from '../utils/auth';\nimport type { PuzzleCompletion } from '../utils/progress';\nimport {\n techniqueBitmaskString,\n techniqueFieldsOf,\n} from '../utils/techniqueBitmask';\nimport { type GameFinishResult, useGameSession } from './useGameSession';\nimport { useGamePlay } from './useGamePlay';\nimport {\n type PuzzleSessionSource,\n useResumeGame,\n type UseResumeGameResult,\n} from './useResumeGame';\n\n/** The fetched puzzle a session plays: a Daily or a Board from the API. */\nexport interface SessionPuzzleInput {\n uuid: string;\n /** 81-char puzzle */\n board: string;\n /** 81-char solution */\n solution: string;\n level?: number | null;\n techniques?: number | null;\n techniques_bitmask?: string | null;\n difficulty_score?: number | null;\n /** A daily's date (YYYY-MM-DD) */\n date?: string | null;\n}\n\n/** The puzzle the screen shows (fetched, or rebuilt from the saved game). */\nexport interface ActivePuzzle {\n /** Board/daily uuid (for a resumed game: its boardUuid, else startedAt) */\n uuid: string;\n /** 81-char puzzle (a resumed game's is already scrambled) */\n puzzle: string;\n /** 81-char solution */\n solution: string;\n /** Difficulty level (1-12), when known */\n level: number | null;\n /** Solver difficulty score, when known */\n difficultyScore: number | null;\n /** A daily's date (YYYY-MM-DD) */\n dailyDate: string | null;\n /** Lossy techniques bitmask */\n techniques: number | null;\n /** Exact techniques bitmask (base-10 string) */\n techniques_bitmask: string | null;\n}\n\n/** Props for the game component (SudokuGame) of either app. */\nexport interface PuzzleGameProps {\n puzzle: string;\n solution: string;\n /** false when resuming (the saved puzzle is already scrambled) */\n scramble: boolean;\n initialInput?: string | undefined;\n initialPencilmarks?: string | undefined;\n initialElapsedTime?: number | undefined;\n initialAutoPencilmarks?: boolean | undefined;\n}\n\nexport interface UsePuzzleSessionOptions extends SudojoApiOptions {\n /** 'daily' or 'level' */\n source: PuzzleSessionSource;\n /** Level screens: the level number (1-12) */\n levelNumber?: number | null | undefined;\n /** Level screens: the level's title (saved in meta for \"Continue\") */\n levelTitle?: string | null | undefined;\n /**\n * The fetched puzzle (useDailyGame's `daily` / useLevelGame's `board`), or\n * null while loading. Ignored while resuming.\n */\n puzzle: SessionPuzzleInput | null | undefined;\n /** Dailies: useDailyGame's `dailyDate` (default: the puzzle's `date`) */\n dailyDate?: string | null | undefined;\n /** The fetch hook's status (default 'loading' until a puzzle arrives) */\n fetchStatus?: GameFetchStatus | undefined;\n /** The signed-in user: server sessions run only for a real (non-anonymous) user */\n user?: AuthUser | null | undefined;\n /** The resume decision from useResumeGame (default: computed here) */\n resume?: UseResumeGameResult | undefined;\n /** Forwarded to the internal useResumeGame when `resume` is not passed */\n isStoreHydrated?: boolean | undefined;\n /** Forwarded to the internal useResumeGame (default true) */\n clearStaleDaily?: boolean | undefined;\n /**\n * Whether a puzzle was already completed (progress). A completed daily is\n * not saved, started on the server, or recorded again (web DailyPage).\n */\n isPuzzleCompleted?: (type: 'daily' | 'level', id: string) => boolean;\n /** Record a completion in the player's progress (web markCompleted) */\n onPuzzleCompleted?: (completion: PuzzleCompletion) => void;\n /**\n * Start a server session for a resumed game too (RN DailyScreen did; the web\n * does not). Default false: the session from when the game was started is\n * still the server's active one, and restarting it resets its clock.\n */\n startSessionOnResume?: boolean;\n /**\n * Show the achievement modal even when nothing was earned (RN did).\n * Default false (web: only on level-up, new badges or points).\n */\n showEmptyAchievement?: boolean;\n}\n\nexport interface UsePuzzleSessionResult {\n /**\n * 'success' once a puzzle is pinned or resumed; otherwise the fetch status\n * ('loading' until the store is ready and while a fetched puzzle is not\n * pinned yet)\n */\n status: GameFetchStatus;\n /** Whether the resume decision is made (store hydrated) */\n isReady: boolean;\n /** Whether the screen resumes a saved game */\n isResuming: boolean;\n /** The saved game being resumed, or null */\n resumeGame: CurrentGame | null;\n /** The puzzle to show (pinned once loaded), or null */\n activePuzzle: ActivePuzzle | null;\n /**\n * Props for the game component, or null while there is no puzzle. Render\n * it with `key={gameKey}` so a new puzzle remounts the game.\n */\n gameProps: PuzzleGameProps | null;\n /** Remount key for the game component (the puzzle's uuid), or null */\n gameKey: string | null;\n /** The puzzle's progress id: the daily date, or the board uuid */\n puzzleId: string | null;\n /** Whether this puzzle was already completed (isPuzzleCompleted) */\n alreadyCompleted: boolean;\n /** Whether server gamification is active (real user with a token) */\n isAuthenticated: boolean;\n /** Web SudokuGame's onScrambledReady: saves the fresh game and starts the session */\n onScrambledReady: (scrambled: { puzzle: string; solution: string }) => void;\n /** RN SudokuGame's onBoardReady: same as onScrambledReady */\n onBoardReady: (scrambledPuzzle: string, scrambledSolution: string) => void;\n /** The game's onProgressUpdate: saves progress (throttled) */\n onProgress: (\n inputString: string,\n pencilmarksString: string,\n isPencilMode: boolean,\n autoPencilmarks: boolean,\n elapsedTime: number\n ) => void;\n /**\n * The game's onComplete: clears the save, finishes the server session\n * (sets the achievement state), records progress. Resolves to the finish\n * result (null when not authenticated).\n */\n onComplete: (timeSeconds: number) => Promise<GameFinishResult | null>;\n /**\n * Abandon the current puzzle: clear the save, stop resuming, unpin. Call the\n * fetch hook's nextPuzzle()/refetch() too; the old puzzle is not pinned again.\n */\n newGame: () => void;\n /** Achievement result to show, or null */\n achievementResult: GameFinishResult | null;\n /** Whether the achievement modal is open */\n showAchievement: boolean;\n /** Close the achievement modal */\n closeAchievement: () => void;\n}\n\nfunction hasAchievement(result: GameFinishResult): boolean {\n return (\n result.leveledUp ||\n result.newBadges.length > 0 ||\n result.totalPointsEarned > 0\n );\n}\n\nfunction puzzleFromResume(\n game: CurrentGame,\n source: PuzzleSessionSource,\n levelNumber: number | null | undefined\n): ActivePuzzle {\n const meta = game.meta;\n return {\n uuid: meta.boardUuid ?? game.startedAt ?? game.updatedAt ?? 'resume',\n puzzle: game.puzzle,\n solution: game.solution,\n level: meta.level ?? (source === 'level' ? (levelNumber ?? null) : null),\n difficultyScore: meta.difficultyScore ?? null,\n dailyDate: meta.dailyDate ?? null,\n ...techniqueFieldsOf(meta),\n };\n}\n\nfunction puzzleFromFetch(\n input: SessionPuzzleInput,\n source: PuzzleSessionSource,\n levelNumber: number | null | undefined,\n dailyDate: string | null | undefined\n): ActivePuzzle {\n return {\n uuid: input.uuid,\n puzzle: input.board,\n solution: input.solution,\n level: input.level ?? (source === 'level' ? (levelNumber ?? null) : null),\n difficultyScore: input.difficulty_score ?? null,\n dailyDate: source === 'daily' ? (dailyDate ?? input.date ?? null) : null,\n ...techniqueFieldsOf(input),\n };\n}\n\n/**\n * Orchestrate a Daily or Level play screen.\n *\n * Behavior (web DailyPage / LevelPlayPage, with RN's stale-daily rule):\n * - Resume: a saved game for this screen (see getResumeGame) is shown with\n * `scramble: false` and its saved input, pencilmarks, time and auto-pencil.\n * A saved daily from another (UTC) day is cleared, not resumed.\n * - Fresh puzzle: pinned once loaded (refetches cannot unmount the game).\n * When the game reports its scrambled board (onScrambledReady /\n * onBoardReady) the game is saved once per puzzle uuid with meta (dailyDate\n * or levelId/levelTitle, boardUuid, level, difficultyScore, techniques +\n * exact techniques_bitmask), and a server session is started once per uuid\n * for a real user (a daily only when it has a level). A daily already in\n * progress is skipped.\n * - Completion: clear the save, finish the server session (authenticated),\n * open the achievement modal when something was earned, then record\n * progress (`onPuzzleCompleted`, not again for a completed daily).\n *\n * @example Daily\n * ```tsx\n * const resume = useResumeGame({ source: 'daily' });\n * const { daily, dailyDate, status, refetch } = useDailyGame({ enabled: resume.isReady && !resume.resumeGame });\n * const { markCompleted, isCompleted } = usePuzzleProgress();\n * const session = usePuzzleSession({\n * source: 'daily', resume, puzzle: daily, dailyDate, fetchStatus: status, user,\n * isPuzzleCompleted: isCompleted, onPuzzleCompleted: markCompleted,\n * });\n * if (session.status === 'auth_required') return <SignInGate />;\n * return session.gameProps && (\n * <>\n * <SudokuGame key={session.gameKey} {...session.gameProps} onScrambledReady={session.onScrambledReady}\n * onProgressUpdate={session.onProgress} onComplete={session.onComplete} />\n * <AchievementModal show={session.showAchievement} onClose={session.closeAchievement}\n * {...session.achievementResult} />\n * </>\n * );\n * ```\n *\n * @example LevelPlay\n * ```tsx\n * const resume = useResumeGame({ source: 'level', levelNumber });\n * const { board, status, nextPuzzle } = useLevelGame({\n * level: levelNumber, symmetrical, userEntitlements, levelEntitlement,\n * enabled: resume.isReady && !resume.resumeGame,\n * });\n * const session = usePuzzleSession({\n * source: 'level', levelNumber, levelTitle: level?.title, resume,\n * puzzle: board, fetchStatus: status, user, onPuzzleCompleted: markCompleted,\n * });\n * const onNewGame = () => { session.newGame(); nextPuzzle(); };\n * // RN: <SudokuGame key={session.gameKey} {...session.gameProps} onBoardReady={session.onBoardReady} ... />\n * ```\n */\nexport function usePuzzleSession(\n options: UsePuzzleSessionOptions\n): UsePuzzleSessionResult {\n const {\n source,\n levelNumber,\n levelTitle,\n puzzle: fetched,\n dailyDate: fetchedDailyDate,\n fetchStatus,\n user,\n isPuzzleCompleted,\n onPuzzleCompleted,\n startSessionOnResume = false,\n showEmptyAchievement = false,\n } = options;\n const api = useResolvedSudojoApi(options, 'usePuzzleSession');\n const slot = source === 'daily' ? 'daily' : 'play';\n\n const internalResume = useResumeGame({\n source,\n levelNumber,\n slot,\n ...(options.clearStaleDaily !== undefined && {\n clearStaleDaily: options.clearStaleDaily,\n }),\n ...(options.isStoreHydrated !== undefined && {\n isStoreHydrated: options.isStoreHydrated,\n }),\n });\n const resume = options.resume ?? internalResume;\n const resumeGame = resume.resumeGame;\n const isResuming = resumeGame !== null;\n\n const { startGame, updateProgress, clearGame } = useGamePlay({ slot });\n const { startSession, finishSession, isAuthenticated } = useGameSession({\n networkClient: api.networkClient,\n baseUrl: api.baseUrl,\n token: api.token,\n userId: isRealUser(user) && user ? user.uid : null,\n });\n\n // --- Pinned fresh puzzle -------------------------------------------------\n const screenKey = `${source}|${levelNumber ?? ''}`;\n const [pinned, setPinned] = useState<{\n key: string;\n puzzle: ActivePuzzle;\n } | null>(null);\n const excludedUuidRef = useRef<string | null>(null);\n\n const fetchedPuzzle = useMemo(\n () =>\n fetched && fetched.board?.length === 81 && fetched.solution?.length === 81\n ? puzzleFromFetch(fetched, source, levelNumber, fetchedDailyDate)\n : null,\n [fetched, source, levelNumber, fetchedDailyDate]\n );\n\n useEffect(() => {\n if (isResuming || !fetchedPuzzle) return;\n if (fetchStatus !== undefined && fetchStatus !== 'success') return;\n if (pinned?.key === screenKey) return;\n if (excludedUuidRef.current === fetchedPuzzle.uuid) return;\n excludedUuidRef.current = null;\n setPinned({ key: screenKey, puzzle: fetchedPuzzle });\n }, [isResuming, fetchedPuzzle, fetchStatus, pinned, screenKey]);\n\n const resumedPuzzle = useMemo(\n () =>\n resumeGame ? puzzleFromResume(resumeGame, source, levelNumber) : null,\n [resumeGame, source, levelNumber]\n );\n const activePuzzle =\n resumedPuzzle ?? (pinned?.key === screenKey ? pinned.puzzle : null);\n\n // 'success' only with a puzzle to show: a fetched puzzle that is not\n // pinned yet (or was abandoned by newGame) still reads as loading.\n const status: GameFetchStatus =\n !resume.isReady || (!activePuzzle && fetchStatus === 'success')\n ? 'loading'\n : activePuzzle\n ? 'success'\n : (fetchStatus ?? 'loading');\n\n const puzzleId =\n source === 'daily'\n ? (activePuzzle?.dailyDate ?? null)\n : (activePuzzle?.uuid ?? null);\n const alreadyCompleted =\n source === 'daily' && puzzleId !== null\n ? (isPuzzleCompleted?.('daily', puzzleId) ?? false)\n : false;\n\n // --- Save the fresh game and start the server session -------------------\n const [scrambled, setScrambled] = useState<{\n uuid: string;\n puzzle: string;\n solution: string;\n } | null>(null);\n const activeUuid = activePuzzle?.uuid ?? null;\n\n const onScrambledReady = useCallback(\n (data: { puzzle: string; solution: string }) => {\n if (!activeUuid) return;\n if (data.puzzle.length !== 81 || data.solution.length !== 81) return;\n setScrambled(prev =>\n prev?.uuid === activeUuid &&\n prev.puzzle === data.puzzle &&\n prev.solution === data.solution\n ? prev\n : { uuid: activeUuid, ...data }\n );\n },\n [activeUuid]\n );\n\n const onBoardReady = useCallback(\n (scrambledPuzzle: string, scrambledSolution: string) => {\n onScrambledReady({\n puzzle: scrambledPuzzle,\n solution: scrambledSolution,\n });\n },\n [onScrambledReady]\n );\n\n const startedGameRef = useRef('');\n const startedSessionRef = useRef('');\n useEffect(() => {\n startedGameRef.current = '';\n startedSessionRef.current = '';\n }, [activeUuid, screenKey]);\n\n const sessionRequest = useCallback(\n (p: ActivePuzzle, board: string, solution: string) => {\n const level = source === 'level' ? (levelNumber ?? p.level) : p.level;\n if (!level) return null;\n const request: GameStartRequest = {\n board,\n solution,\n level,\n techniques: techniqueBitmaskString(p),\n puzzleType: source,\n };\n const id = source === 'daily' ? p.dailyDate : p.uuid;\n if (id) request.puzzleId = id;\n return request;\n },\n [source, levelNumber]\n );\n\n useEffect(() => {\n if (!activePuzzle || status !== 'success') return;\n const uuid = activePuzzle.uuid;\n\n if (isResuming) {\n // Optional (RN): restart the server session for a resumed game.\n if (\n startSessionOnResume &&\n isAuthenticated &&\n startedSessionRef.current !== uuid\n ) {\n const request = sessionRequest(\n activePuzzle,\n activePuzzle.puzzle,\n activePuzzle.solution\n );\n if (request) {\n startedSessionRef.current = uuid;\n void startSession(request);\n }\n }\n return;\n }\n\n if (!scrambled || scrambled.uuid !== uuid) return;\n if (source === 'daily' && (!activePuzzle.dailyDate || alreadyCompleted)) {\n return;\n }\n if (source === 'level' && levelNumber == null) return;\n if (startedGameRef.current === uuid) return;\n startedGameRef.current = uuid;\n\n const meta: CurrentGameMeta = {\n boardUuid: uuid,\n ...techniqueFieldsOf(activePuzzle),\n };\n if (source === 'daily' && activePuzzle.dailyDate) {\n meta.dailyDate = activePuzzle.dailyDate;\n }\n if (source === 'level') {\n meta.levelId = String(levelNumber);\n if (levelTitle) meta.levelTitle = levelTitle;\n }\n if (activePuzzle.level !== null) meta.level = activePuzzle.level;\n if (activePuzzle.difficultyScore !== null) {\n meta.difficultyScore = activePuzzle.difficultyScore;\n }\n startGame(\n source,\n {\n original: scrambled.puzzle,\n user: scrambled.puzzle,\n pencilmark: { numbers: '', autopencil: false },\n },\n scrambled.solution,\n meta\n );\n\n if (isAuthenticated && startedSessionRef.current !== uuid) {\n // The server session gets the board as played (scrambled): hints send\n // that board as `original`, and the API credits a hint to the session\n // only when the two match exactly.\n const request = sessionRequest(\n activePuzzle,\n scrambled.puzzle,\n scrambled.solution\n );\n if (request) {\n startedSessionRef.current = uuid;\n void startSession(request);\n }\n }\n }, [\n activePuzzle,\n status,\n isResuming,\n scrambled,\n source,\n levelNumber,\n levelTitle,\n alreadyCompleted,\n isAuthenticated,\n startSessionOnResume,\n startGame,\n startSession,\n sessionRequest,\n ]);\n\n // --- Completion ----------------------------------------------------------\n const [achievementResult, setAchievementResult] =\n useState<GameFinishResult | null>(null);\n const [showAchievement, setShowAchievement] = useState(false);\n\n const onComplete = useCallback(\n async (timeSeconds: number): Promise<GameFinishResult | null> => {\n clearGame();\n let result: GameFinishResult | null = null;\n if (isAuthenticated) {\n result = await finishSession(timeSeconds);\n if (showEmptyAchievement || hasAchievement(result)) {\n setAchievementResult(result);\n setShowAchievement(true);\n }\n }\n if (puzzleId && onPuzzleCompleted && !alreadyCompleted) {\n onPuzzleCompleted({ type: source, id: puzzleId, timeSeconds });\n }\n return result;\n },\n [\n clearGame,\n isAuthenticated,\n finishSession,\n showEmptyAchievement,\n puzzleId,\n onPuzzleCompleted,\n alreadyCompleted,\n source,\n ]\n );\n\n const closeAchievement = useCallback(() => {\n setShowAchievement(false);\n setAchievementResult(null);\n }, []);\n\n const discardResume = resume.discard;\n const newGame = useCallback(() => {\n clearGame();\n discardResume();\n excludedUuidRef.current = activeUuid;\n setPinned(null);\n setScrambled(null);\n startedGameRef.current = '';\n startedSessionRef.current = '';\n }, [clearGame, discardResume, activeUuid]);\n\n // --- Game props ----------------------------------------------------------\n const gameProps = useMemo((): PuzzleGameProps | null => {\n if (!activePuzzle) return null;\n if (resumeGame) {\n return {\n puzzle: activePuzzle.puzzle,\n solution: activePuzzle.solution,\n scramble: false,\n initialInput: resumeGame.inputString,\n initialPencilmarks: resumeGame.pencilmarksString,\n initialElapsedTime: resumeGame.elapsedTime,\n initialAutoPencilmarks: resumeGame.autoPencilmarks,\n };\n }\n return {\n puzzle: activePuzzle.puzzle,\n solution: activePuzzle.solution,\n scramble: true,\n };\n }, [activePuzzle, resumeGame]);\n\n return {\n status,\n isReady: resume.isReady,\n isResuming,\n resumeGame,\n activePuzzle,\n gameProps,\n gameKey: activeUuid,\n puzzleId,\n alreadyCompleted,\n isAuthenticated,\n onScrambledReady,\n onBoardReady,\n onProgress: updateProgress,\n onComplete,\n newGame,\n achievementResult,\n showAchievement,\n closeAchievement,\n };\n}\n"]}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Hook for deciding, once per puzzle screen, whether to resume the saved game
3
+ * from the game store instead of fetching a new puzzle.
4
+ */
5
+ import type { CurrentGame, GameSlot } from '../types/currentGame';
6
+ /** Which kind of puzzle screen a session belongs to. */
7
+ export type PuzzleSessionSource = 'daily' | 'level';
8
+ /** Which saved games useResumeGame / getResumeGame can resume. */
9
+ export type ResumeGameSource = PuzzleSessionSource | 'entered';
10
+ /**
11
+ * The saved game to resume for a screen, or null.
12
+ *
13
+ * A game resumes when its source matches, its puzzle and solution are both 81
14
+ * characters, and: for a daily, it has a dailyDate (and, unless
15
+ * `allowStaleDaily`, that date is today's UTC daily); for a level, its
16
+ * meta.levelId equals `String(levelNumber)`; an entered puzzle needs nothing
17
+ * more.
18
+ */
19
+ export declare function getResumeGame(game: CurrentGame | null | undefined, criteria: {
20
+ source: ResumeGameSource;
21
+ levelNumber?: number | null | undefined;
22
+ allowStaleDaily?: boolean;
23
+ now?: Date;
24
+ }): CurrentGame | null;
25
+ export interface UseResumeGameOptions {
26
+ /** 'daily', 'level' or 'entered' */
27
+ source: ResumeGameSource;
28
+ /** For a level screen: the level number (1-12) */
29
+ levelNumber?: number | null | undefined;
30
+ /** Store slot (default: 'daily' for dailies, 'play' for levels and entered puzzles) */
31
+ slot?: GameSlot;
32
+ /**
33
+ * Clear a saved daily that is not today's (UTC) daily instead of resuming
34
+ * it (default true; RN's rule, see usePuzzleSession)
35
+ */
36
+ clearStaleDaily?: boolean;
37
+ /**
38
+ * Whether the store has hydrated. Default: the store's own persist
39
+ * hydration. RN passes its onStoreReady state, because it swaps the store
40
+ * to AsyncStorage and rehydrates after start-up.
41
+ */
42
+ isStoreHydrated?: boolean;
43
+ }
44
+ export interface UseResumeGameResult {
45
+ /** The saved game to resume (snapshot taken once per screen), or null */
46
+ resumeGame: CurrentGame | null;
47
+ /** True once the store has hydrated and the resume decision is made */
48
+ isReady: boolean;
49
+ /** Stop resuming (after New game or completion) */
50
+ discard: () => void;
51
+ }
52
+ /**
53
+ * Decide once per screen (slot + source + level) whether there is a saved game
54
+ * to resume. The decision is a snapshot: starting a fresh game later (which
55
+ * writes to the same slot) does not flip the screen into "resuming", as on RN.
56
+ *
57
+ * Call it before the fetch hook, and disable the fetch while resuming:
58
+ *
59
+ * @example
60
+ * ```tsx
61
+ * const resume = useResumeGame({ source: 'level', levelNumber });
62
+ * const { board, status } = useLevelGame({
63
+ * level: levelNumber,
64
+ * enabled: resume.isReady && !resume.resumeGame,
65
+ * });
66
+ * const session = usePuzzleSession({ resume, source: 'level', levelNumber, puzzle: board, fetchStatus: status, user });
67
+ * ```
68
+ */
69
+ export declare function useResumeGame(options: UseResumeGameOptions): UseResumeGameResult;
70
+ //# sourceMappingURL=useResumeGame.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useResumeGame.d.ts","sourceRoot":"","sources":["../../src/hooks/useResumeGame.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAGlE,wDAAwD;AACxD,MAAM,MAAM,mBAAmB,GAAG,OAAO,GAAG,OAAO,CAAC;AAEpD,kEAAkE;AAClE,MAAM,MAAM,gBAAgB,GAAG,mBAAmB,GAAG,SAAS,CAAC;AAE/D;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,IAAI,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,EACpC,QAAQ,EAAE;IACR,MAAM,EAAE,gBAAgB,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACxC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ,GACA,WAAW,GAAG,IAAI,CA6BpB;AAqBD,MAAM,WAAW,oBAAoB;IACnC,oCAAoC;IACpC,MAAM,EAAE,gBAAgB,CAAC;IACzB,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACxC,uFAAuF;IACvF,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,yEAAyE;IACzE,UAAU,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/B,uEAAuE;IACvE,OAAO,EAAE,OAAO,CAAC;IACjB,mDAAmD;IACnD,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AASD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,oBAAoB,GAC5B,mBAAmB,CAsDrB"}
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Hook for deciding, once per puzzle screen, whether to resume the saved game
3
+ * from the game store instead of fetching a new puzzle.
4
+ */
5
+ import { useCallback, useEffect, useState } from 'react';
6
+ import { useGamePlayStore } from '../stores/gamePlayStore';
7
+ import { isStaleDaily } from '../utils/date';
8
+ /**
9
+ * The saved game to resume for a screen, or null.
10
+ *
11
+ * A game resumes when its source matches, its puzzle and solution are both 81
12
+ * characters, and: for a daily, it has a dailyDate (and, unless
13
+ * `allowStaleDaily`, that date is today's UTC daily); for a level, its
14
+ * meta.levelId equals `String(levelNumber)`; an entered puzzle needs nothing
15
+ * more.
16
+ */
17
+ export function getResumeGame(game, criteria) {
18
+ if (!game ||
19
+ game.source !== criteria.source ||
20
+ !game.puzzle ||
21
+ !game.solution ||
22
+ game.puzzle.length !== 81 ||
23
+ game.solution.length !== 81) {
24
+ return null;
25
+ }
26
+ if (criteria.source === 'daily') {
27
+ if (!game.meta.dailyDate)
28
+ return null;
29
+ if (!criteria.allowStaleDaily &&
30
+ isStaleDaily(game.meta.dailyDate, criteria.now)) {
31
+ return null;
32
+ }
33
+ return game;
34
+ }
35
+ if (criteria.source === 'entered')
36
+ return game;
37
+ if (criteria.levelNumber == null ||
38
+ game.meta.levelId !== String(criteria.levelNumber)) {
39
+ return null;
40
+ }
41
+ return game;
42
+ }
43
+ /** Whether the game store has finished hydrating from storage. */
44
+ function useGameStoreHydrated() {
45
+ const persistApi = useGamePlayStore.persist;
46
+ const [hydrated, setHydrated] = useState(() => persistApi?.hasHydrated?.() ?? true);
47
+ useEffect(() => {
48
+ if (!persistApi)
49
+ return undefined;
50
+ const offStart = persistApi.onHydrate?.(() => setHydrated(false));
51
+ const offFinish = persistApi.onFinishHydration?.(() => setHydrated(true));
52
+ setHydrated(persistApi.hasHydrated?.() ?? true);
53
+ return () => {
54
+ offStart?.();
55
+ offFinish?.();
56
+ };
57
+ }, [persistApi]);
58
+ return hydrated;
59
+ }
60
+ /**
61
+ * Decide once per screen (slot + source + level) whether there is a saved game
62
+ * to resume. The decision is a snapshot: starting a fresh game later (which
63
+ * writes to the same slot) does not flip the screen into "resuming", as on RN.
64
+ *
65
+ * Call it before the fetch hook, and disable the fetch while resuming:
66
+ *
67
+ * @example
68
+ * ```tsx
69
+ * const resume = useResumeGame({ source: 'level', levelNumber });
70
+ * const { board, status } = useLevelGame({
71
+ * level: levelNumber,
72
+ * enabled: resume.isReady && !resume.resumeGame,
73
+ * });
74
+ * const session = usePuzzleSession({ resume, source: 'level', levelNumber, puzzle: board, fetchStatus: status, user });
75
+ * ```
76
+ */
77
+ export function useResumeGame(options) {
78
+ const { source, levelNumber, slot = source === 'daily' ? 'daily' : 'play', clearStaleDaily = true, } = options;
79
+ const storeHydrated = useGameStoreHydrated();
80
+ const hydrated = options.isStoreHydrated ?? storeHydrated;
81
+ const key = `${slot}|${source}|${levelNumber ?? ''}`;
82
+ const [snapshot, setSnapshot] = useState(null);
83
+ // Take the snapshot during render (derived state), once per key.
84
+ let current = snapshot;
85
+ if (hydrated && snapshot?.key !== key) {
86
+ const state = useGamePlayStore.getState();
87
+ const saved = slot === 'daily' ? state.dailyGame : state.playGame;
88
+ const game = getResumeGame(saved, {
89
+ source,
90
+ levelNumber,
91
+ allowStaleDaily: !clearStaleDaily,
92
+ });
93
+ const stale = !game &&
94
+ clearStaleDaily &&
95
+ source === 'daily' &&
96
+ saved?.source === 'daily' &&
97
+ isStaleDaily(saved.meta.dailyDate)
98
+ ? saved
99
+ : null;
100
+ current = { key, game, stale };
101
+ setSnapshot(current);
102
+ }
103
+ // Clear a stale daily (if the slot still holds it).
104
+ const staleGame = current?.stale ?? null;
105
+ useEffect(() => {
106
+ if (!staleGame)
107
+ return;
108
+ const state = useGamePlayStore.getState();
109
+ const saved = slot === 'daily' ? state.dailyGame : state.playGame;
110
+ if (saved === staleGame)
111
+ state.clearGame(slot);
112
+ }, [staleGame, slot]);
113
+ const discard = useCallback(() => {
114
+ setSnapshot({ key, game: null, stale: null });
115
+ }, [key]);
116
+ const isReady = hydrated && current?.key === key;
117
+ return {
118
+ resumeGame: isReady ? (current?.game ?? null) : null,
119
+ isReady,
120
+ discard,
121
+ };
122
+ }
123
+ //# sourceMappingURL=useResumeGame.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useResumeGame.js","sourceRoot":"","sources":["../../src/hooks/useResumeGame.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Hook for deciding, once per puzzle screen, whether to resume the saved game\n * from the game store instead of fetching a new puzzle.\n */\n\nimport { useCallback, useEffect, useState } from 'react';\nimport { useGamePlayStore } from '../stores/gamePlayStore';\nimport type { CurrentGame, GameSlot } from '../types/currentGame';\nimport { isStaleDaily } from '../utils/date';\n\n/** Which kind of puzzle screen a session belongs to. */\nexport type PuzzleSessionSource = 'daily' | 'level';\n\n/** Which saved games useResumeGame / getResumeGame can resume. */\nexport type ResumeGameSource = PuzzleSessionSource | 'entered';\n\n/**\n * The saved game to resume for a screen, or null.\n *\n * A game resumes when its source matches, its puzzle and solution are both 81\n * characters, and: for a daily, it has a dailyDate (and, unless\n * `allowStaleDaily`, that date is today's UTC daily); for a level, its\n * meta.levelId equals `String(levelNumber)`; an entered puzzle needs nothing\n * more.\n */\nexport function getResumeGame(\n game: CurrentGame | null | undefined,\n criteria: {\n source: ResumeGameSource;\n levelNumber?: number | null | undefined;\n allowStaleDaily?: boolean;\n now?: Date;\n }\n): CurrentGame | null {\n if (\n !game ||\n game.source !== criteria.source ||\n !game.puzzle ||\n !game.solution ||\n game.puzzle.length !== 81 ||\n game.solution.length !== 81\n ) {\n return null;\n }\n if (criteria.source === 'daily') {\n if (!game.meta.dailyDate) return null;\n if (\n !criteria.allowStaleDaily &&\n isStaleDaily(game.meta.dailyDate, criteria.now)\n ) {\n return null;\n }\n return game;\n }\n if (criteria.source === 'entered') return game;\n if (\n criteria.levelNumber == null ||\n game.meta.levelId !== String(criteria.levelNumber)\n ) {\n return null;\n }\n return game;\n}\n\n/** Whether the game store has finished hydrating from storage. */\nfunction useGameStoreHydrated(): boolean {\n const persistApi = useGamePlayStore.persist;\n const [hydrated, setHydrated] = useState(\n () => persistApi?.hasHydrated?.() ?? true\n );\n useEffect(() => {\n if (!persistApi) return undefined;\n const offStart = persistApi.onHydrate?.(() => setHydrated(false));\n const offFinish = persistApi.onFinishHydration?.(() => setHydrated(true));\n setHydrated(persistApi.hasHydrated?.() ?? true);\n return () => {\n offStart?.();\n offFinish?.();\n };\n }, [persistApi]);\n return hydrated;\n}\n\nexport interface UseResumeGameOptions {\n /** 'daily', 'level' or 'entered' */\n source: ResumeGameSource;\n /** For a level screen: the level number (1-12) */\n levelNumber?: number | null | undefined;\n /** Store slot (default: 'daily' for dailies, 'play' for levels and entered puzzles) */\n slot?: GameSlot;\n /**\n * Clear a saved daily that is not today's (UTC) daily instead of resuming\n * it (default true; RN's rule, see usePuzzleSession)\n */\n clearStaleDaily?: boolean;\n /**\n * Whether the store has hydrated. Default: the store's own persist\n * hydration. RN passes its onStoreReady state, because it swaps the store\n * to AsyncStorage and rehydrates after start-up.\n */\n isStoreHydrated?: boolean;\n}\n\nexport interface UseResumeGameResult {\n /** The saved game to resume (snapshot taken once per screen), or null */\n resumeGame: CurrentGame | null;\n /** True once the store has hydrated and the resume decision is made */\n isReady: boolean;\n /** Stop resuming (after New game or completion) */\n discard: () => void;\n}\n\ninterface ResumeSnapshot {\n key: string;\n game: CurrentGame | null;\n /** A stale daily found in the slot, to clear from the store */\n stale: CurrentGame | null;\n}\n\n/**\n * Decide once per screen (slot + source + level) whether there is a saved game\n * to resume. The decision is a snapshot: starting a fresh game later (which\n * writes to the same slot) does not flip the screen into \"resuming\", as on RN.\n *\n * Call it before the fetch hook, and disable the fetch while resuming:\n *\n * @example\n * ```tsx\n * const resume = useResumeGame({ source: 'level', levelNumber });\n * const { board, status } = useLevelGame({\n * level: levelNumber,\n * enabled: resume.isReady && !resume.resumeGame,\n * });\n * const session = usePuzzleSession({ resume, source: 'level', levelNumber, puzzle: board, fetchStatus: status, user });\n * ```\n */\nexport function useResumeGame(\n options: UseResumeGameOptions\n): UseResumeGameResult {\n const {\n source,\n levelNumber,\n slot = source === 'daily' ? 'daily' : 'play',\n clearStaleDaily = true,\n } = options;\n const storeHydrated = useGameStoreHydrated();\n const hydrated = options.isStoreHydrated ?? storeHydrated;\n const key = `${slot}|${source}|${levelNumber ?? ''}`;\n\n const [snapshot, setSnapshot] = useState<ResumeSnapshot | null>(null);\n\n // Take the snapshot during render (derived state), once per key.\n let current = snapshot;\n if (hydrated && snapshot?.key !== key) {\n const state = useGamePlayStore.getState();\n const saved = slot === 'daily' ? state.dailyGame : state.playGame;\n const game = getResumeGame(saved, {\n source,\n levelNumber,\n allowStaleDaily: !clearStaleDaily,\n });\n const stale =\n !game &&\n clearStaleDaily &&\n source === 'daily' &&\n saved?.source === 'daily' &&\n isStaleDaily(saved.meta.dailyDate)\n ? saved\n : null;\n current = { key, game, stale };\n setSnapshot(current);\n }\n\n // Clear a stale daily (if the slot still holds it).\n const staleGame = current?.stale ?? null;\n useEffect(() => {\n if (!staleGame) return;\n const state = useGamePlayStore.getState();\n const saved = slot === 'daily' ? state.dailyGame : state.playGame;\n if (saved === staleGame) state.clearGame(slot);\n }, [staleGame, slot]);\n\n const discard = useCallback(() => {\n setSnapshot({ key, game: null, stale: null });\n }, [key]);\n\n const isReady = hydrated && current?.key === key;\n return {\n resumeGame: isReady ? (current?.game ?? null) : null,\n isReady,\n discard,\n };\n}\n"]}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Hook for fetching the solving strategies (technique groups)
3
+ */
4
+ import type { Strategy } from '@sudobility/sudojo_types';
5
+ import type { NetworkClient } from '@sudobility/types';
6
+ export interface UseStrategiesOptions {
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
+ /** Access token (optional for this public endpoint) */
12
+ token?: string | null | undefined;
13
+ /** Whether to enable the query */
14
+ enabled?: boolean;
15
+ }
16
+ export interface UseStrategiesResult {
17
+ /** All strategies ([] while loading or when the request failed) */
18
+ strategies: Strategy[];
19
+ /** Whether strategies are loading */
20
+ isLoading: boolean;
21
+ /** Error if loading failed */
22
+ error: Error | null;
23
+ /** Refetch strategies */
24
+ refetch: () => void;
25
+ /** Find a strategy by its URL stub */
26
+ findStrategyByStub: (stub: string | null | undefined) => Strategy | undefined;
27
+ }
28
+ /**
29
+ * Fetch all strategies, unwrapped the way the apps did
30
+ * (`success ? data ?? [] : []`).
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * const { strategies, findStrategyByStub } = useStrategies({ networkClient, baseUrl });
35
+ * const strategy = findStrategyByStub(stub);
36
+ * ```
37
+ */
38
+ export declare function useStrategies(options?: UseStrategiesOptions): UseStrategiesResult;
39
+ //# sourceMappingURL=useStrategies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useStrategies.d.ts","sourceRoot":"","sources":["../../src/hooks/useStrategies.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAKvD,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,aAAa,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC1C,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAClC,kCAAkC;IAClC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,mEAAmE;IACnE,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvB,qCAAqC;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,8BAA8B;IAC9B,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,yBAAyB;IACzB,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,sCAAsC;IACtC,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,KAAK,QAAQ,GAAG,SAAS,CAAC;CAC/E;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAC3B,OAAO,GAAE,oBAAyB,GACjC,mBAAmB,CAiCrB"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Hook for fetching the solving strategies (technique groups)
3
+ */
4
+ import { useCallback, useMemo } from 'react';
5
+ import { useSudojoStrategies } from '@sudobility/sudojo_client';
6
+ import { useResolvedSudojoApi } from '../context/SudojoApiContext';
7
+ import { findStrategyByStub as findByStub } from '../utils/technique';
8
+ /**
9
+ * Fetch all strategies, unwrapped the way the apps did
10
+ * (`success ? data ?? [] : []`).
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const { strategies, findStrategyByStub } = useStrategies({ networkClient, baseUrl });
15
+ * const strategy = findStrategyByStub(stub);
16
+ * ```
17
+ */
18
+ export function useStrategies(options = {}) {
19
+ const { enabled = true } = options;
20
+ const { networkClient, baseUrl, token } = useResolvedSudojoApi(options, 'useStrategies');
21
+ const { data, isLoading, error, refetch } = useSudojoStrategies(networkClient, baseUrl, token, { enabled });
22
+ const strategies = useMemo(() => (data?.success ? (data.data ?? []) : []), [data]);
23
+ const findStrategyByStub = useCallback((stub) => findByStub(strategies, stub), [strategies]);
24
+ return {
25
+ strategies,
26
+ isLoading,
27
+ error: error ?? null,
28
+ refetch: () => {
29
+ refetch();
30
+ },
31
+ findStrategyByStub,
32
+ };
33
+ }
34
+ //# sourceMappingURL=useStrategies.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useStrategies.js","sourceRoot":"","sources":["../../src/hooks/useStrategies.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAG7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,kBAAkB,IAAI,UAAU,EAAE,MAAM,oBAAoB,CAAC;AA0BtE;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAC3B,UAAgC,EAAE;IAElC,MAAM,EAAE,OAAO,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IACnC,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,oBAAoB,CAC5D,OAAO,EACP,eAAe,CAChB,CAAC;IAEF,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,mBAAmB,CAC7D,aAAa,EACb,OAAO,EACP,KAAK,EACL,EAAE,OAAO,EAAE,CACZ,CAAC;IAEF,MAAM,UAAU,GAAG,OAAO,CACxB,GAAe,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,EAC1D,CAAC,IAAI,CAAC,CACP,CAAC;IAEF,MAAM,kBAAkB,GAAG,WAAW,CACpC,CAAC,IAA+B,EAAE,EAAE,CAAC,UAAU,CAAC,UAAU,EAAE,IAAI,CAAC,EACjE,CAAC,UAAU,CAAC,CACb,CAAC;IAEF,OAAO;QACL,UAAU;QACV,SAAS;QACT,KAAK,EAAE,KAAK,IAAI,IAAI;QACpB,OAAO,EAAE,GAAG,EAAE;YACZ,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,kBAAkB;KACnB,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Hook for fetching the solving strategies (technique groups)\n */\n\nimport { useCallback, useMemo } from 'react';\nimport type { Strategy } from '@sudobility/sudojo_types';\nimport type { NetworkClient } from '@sudobility/types';\nimport { useSudojoStrategies } from '@sudobility/sudojo_client';\nimport { useResolvedSudojoApi } from '../context/SudojoApiContext';\nimport { findStrategyByStub as findByStub } from '../utils/technique';\n\nexport interface UseStrategiesOptions {\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 (optional for this public endpoint) */\n token?: string | null | undefined;\n /** Whether to enable the query */\n enabled?: boolean;\n}\n\nexport interface UseStrategiesResult {\n /** All strategies ([] while loading or when the request failed) */\n strategies: Strategy[];\n /** Whether strategies are loading */\n isLoading: boolean;\n /** Error if loading failed */\n error: Error | null;\n /** Refetch strategies */\n refetch: () => void;\n /** Find a strategy by its URL stub */\n findStrategyByStub: (stub: string | null | undefined) => Strategy | undefined;\n}\n\n/**\n * Fetch all strategies, unwrapped the way the apps did\n * (`success ? data ?? [] : []`).\n *\n * @example\n * ```tsx\n * const { strategies, findStrategyByStub } = useStrategies({ networkClient, baseUrl });\n * const strategy = findStrategyByStub(stub);\n * ```\n */\nexport function useStrategies(\n options: UseStrategiesOptions = {}\n): UseStrategiesResult {\n const { enabled = true } = options;\n const { networkClient, baseUrl, token } = useResolvedSudojoApi(\n options,\n 'useStrategies'\n );\n\n const { data, isLoading, error, refetch } = useSudojoStrategies(\n networkClient,\n baseUrl,\n token,\n { enabled }\n );\n\n const strategies = useMemo(\n (): Strategy[] => (data?.success ? (data.data ?? []) : []),\n [data]\n );\n\n const findStrategyByStub = useCallback(\n (stub: string | null | undefined) => findByStub(strategies, stub),\n [strategies]\n );\n\n return {\n strategies,\n isLoading,\n error: error ?? null,\n refetch: () => {\n refetch();\n },\n findStrategyByStub,\n };\n}\n"]}
@@ -86,6 +86,11 @@ export interface UseSudokuResult {
86
86
  getOriginalPuzzle: () => string;
87
87
  /** Get scrambled puzzle string (what the user is playing) - use this for solver API */
88
88
  getScrambledPuzzle: () => string;
89
+ /**
90
+ * Get scrambled solution string (same digit space as getScrambledPuzzle;
91
+ * '0' where unknown) - pair it with getScrambledPuzzle when saving a game
92
+ */
93
+ getScrambledSolution: () => string;
89
94
  /** Get input string (user entries only) */
90
95
  getInputString: () => string;
91
96
  /** Get pencilmarks string */
@@ -1 +1 @@
1
- {"version":3,"file":"useSudoku.d.ts","sourceRoot":"","sources":["../../src/hooks/useSudoku.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"useSudoku.d.ts","sourceRoot":"","sources":["../../src/hooks/useSudoku.ts"],"names":[],"mappings":"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"}
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import { useCallback, useMemo, useReducer } from 'react';
16
16
  import { blockOf, cellHasError, columnOf, DEFAULT_APP_SETTINGS, DEFAULT_PLAY_SETTINGS, rowOf, } from '../types/sudoku';
17
- import { cellsToInputString, cellsToPencilmarksString, cellsToPuzzleString, cellsToStateString, NonScrambler, parsePuzzleString, scrambleBoard, Scrambler, } from '../utils/sudokuScrambler';
17
+ import { cellsToInputString, cellsToPencilmarksString, cellsToPuzzleString, cellsToSolutionString, cellsToStateString, NonScrambler, parsePuzzleString, scrambleBoard, Scrambler, } from '../utils/sudokuScrambler';
18
18
  // =============================================================================
19
19
  // Initial State
20
20
  // =============================================================================
@@ -608,6 +608,11 @@ export function useSudoku(options = {}) {
608
608
  return '';
609
609
  return cellsToPuzzleString(board.cells);
610
610
  }, [board]);
611
+ const getScrambledSolution = useCallback(() => {
612
+ if (!board)
613
+ return '';
614
+ return cellsToSolutionString(board.cells);
615
+ }, [board]);
611
616
  const getInputString = useCallback(() => {
612
617
  if (!board)
613
618
  return '';
@@ -654,6 +659,7 @@ export function useSudoku(options = {}) {
654
659
  getBoardString,
655
660
  getOriginalPuzzle,
656
661
  getScrambledPuzzle,
662
+ getScrambledSolution,
657
663
  getInputString,
658
664
  getPencilmarksString,
659
665
  };