@sudobility/sudojo_lib 0.0.68 → 0.0.73

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 (49) hide show
  1. package/dist/config/authProviders.d.ts +18 -0
  2. package/dist/config/authProviders.d.ts.map +1 -0
  3. package/dist/config/authProviders.js +12 -0
  4. package/dist/config/authProviders.js.map +1 -0
  5. package/dist/config/index.d.ts +3 -0
  6. package/dist/config/index.d.ts.map +1 -0
  7. package/dist/config/index.js +2 -0
  8. package/dist/config/index.js.map +1 -0
  9. package/dist/hooks/index.d.ts +4 -0
  10. package/dist/hooks/index.d.ts.map +1 -1
  11. package/dist/hooks/index.js +4 -0
  12. package/dist/hooks/index.js.map +1 -1
  13. package/dist/hooks/useBoardEntry.d.ts +45 -0
  14. package/dist/hooks/useBoardEntry.d.ts.map +1 -0
  15. package/dist/hooks/useBoardEntry.js +197 -0
  16. package/dist/hooks/useBoardEntry.js.map +1 -0
  17. package/dist/hooks/useGamePlay.d.ts +6 -3
  18. package/dist/hooks/useGamePlay.d.ts.map +1 -1
  19. package/dist/hooks/useGamePlay.js +29 -17
  20. package/dist/hooks/useGamePlay.js.map +1 -1
  21. package/dist/hooks/useGameSession.d.ts +45 -0
  22. package/dist/hooks/useGameSession.d.ts.map +1 -0
  23. package/dist/hooks/useGameSession.js +108 -0
  24. package/dist/hooks/useGameSession.js.map +1 -0
  25. package/dist/index.d.ts +8 -3
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +9 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/stores/gamePlayStore.d.ts +15 -13
  30. package/dist/stores/gamePlayStore.d.ts.map +1 -1
  31. package/dist/stores/gamePlayStore.js +29 -9
  32. package/dist/stores/gamePlayStore.js.map +1 -1
  33. package/dist/types/currentGame.d.ts +2 -0
  34. package/dist/types/currentGame.d.ts.map +1 -1
  35. package/dist/types/index.d.ts +1 -1
  36. package/dist/types/index.d.ts.map +1 -1
  37. package/dist/utils/hintExplanation.d.ts +17 -0
  38. package/dist/utils/hintExplanation.d.ts.map +1 -0
  39. package/dist/utils/hintExplanation.js +882 -0
  40. package/dist/utils/hintExplanation.js.map +1 -0
  41. package/dist/utils/index.d.ts +3 -0
  42. package/dist/utils/index.d.ts.map +1 -1
  43. package/dist/utils/index.js +3 -0
  44. package/dist/utils/index.js.map +1 -1
  45. package/dist/utils/localizedHint.d.ts +17 -0
  46. package/dist/utils/localizedHint.d.ts.map +1 -0
  47. package/dist/utils/localizedHint.js +35 -0
  48. package/dist/utils/localizedHint.js.map +1 -0
  49. package/package.json +5 -5
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Shared auth provider configuration
3
+ *
4
+ * Single source of truth for which authentication providers are enabled
5
+ * across all Sudojo apps (web and mobile).
6
+ */
7
+ /** Available authentication provider types */
8
+ export type AuthProviderType = 'google' | 'apple' | 'email';
9
+ /** Configuration for enabled auth providers */
10
+ export interface AuthProvidersConfig {
11
+ /** Which providers to enable */
12
+ providers: AuthProviderType[];
13
+ /** Enable anonymous sign-in fallback */
14
+ enableAnonymous?: boolean;
15
+ }
16
+ /** Default auth providers config used by all apps */
17
+ export declare const DEFAULT_AUTH_PROVIDERS: AuthProvidersConfig;
18
+ //# sourceMappingURL=authProviders.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authProviders.d.ts","sourceRoot":"","sources":["../../src/config/authProviders.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,8CAA8C;AAC9C,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,CAAC;AAE5D,+CAA+C;AAC/C,MAAM,WAAW,mBAAmB;IAClC,gCAAgC;IAChC,SAAS,EAAE,gBAAgB,EAAE,CAAC;IAC9B,wCAAwC;IACxC,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,qDAAqD;AACrD,eAAO,MAAM,sBAAsB,EAAE,mBAGpC,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Shared auth provider configuration
3
+ *
4
+ * Single source of truth for which authentication providers are enabled
5
+ * across all Sudojo apps (web and mobile).
6
+ */
7
+ /** Default auth providers config used by all apps */
8
+ export const DEFAULT_AUTH_PROVIDERS = {
9
+ providers: ['google', 'email'],
10
+ enableAnonymous: true,
11
+ };
12
+ //# sourceMappingURL=authProviders.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authProviders.js","sourceRoot":"","sources":["../../src/config/authProviders.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,qDAAqD;AACrD,MAAM,CAAC,MAAM,sBAAsB,GAAwB;IACzD,SAAS,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC9B,eAAe,EAAE,IAAI;CACtB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export type { AuthProviderType, AuthProvidersConfig } from './authProviders';
2
+ export { DEFAULT_AUTH_PROVIDERS } from './authProviders';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { DEFAULT_AUTH_PROVIDERS } from './authProviders';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC"}
@@ -26,4 +26,8 @@ export { useHint } from './useHint';
26
26
  export type { UseHintOptions, UseHintResult, HintBoardData, HintReceivedData, HintAccessError, } from './useHint';
27
27
  export { useGamePlay } from './useGamePlay';
28
28
  export type { UseGamePlayOptions, UseGamePlayResult } from './useGamePlay';
29
+ export { useBoardEntry } from './useBoardEntry';
30
+ export type { UseBoardEntryOptions, UseBoardEntryReturn, ValidatedPuzzle, } from './useBoardEntry';
31
+ export { useGameSession } from './useGameSession';
32
+ export type { UseGameSessionOptions, GameSessionResult, GameFinishResult, } from './useGameSession';
29
33
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAClD,YAAY,EACV,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,eAAe,GAChB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC9D,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC7D,YAAY,EACV,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG/D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EACV,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGrE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAG9E,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAG9E,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACvE,YAAY,EACV,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EACV,cAAc,EACd,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,GAChB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAClD,YAAY,EACV,eAAe,EACf,cAAc,EACd,gBAAgB,EAChB,eAAe,GAChB,MAAM,aAAa,CAAC;AAGrB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC9D,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC7D,YAAY,EACV,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,iBAAiB,GAClB,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAG/D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,YAAY,EACV,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAGrE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EACV,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,GAChB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAG9E,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,YAAY,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAG9E,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACvE,YAAY,EACV,yBAAyB,EACzB,wBAAwB,GACzB,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAGpD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EACV,cAAc,EACd,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,GAChB,MAAM,WAAW,CAAC;AAGnB,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAG3E,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,YAAY,EACV,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,YAAY,EACV,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,kBAAkB,CAAC"}
@@ -26,4 +26,8 @@ export { useLocalStorage } from './useLocalStorage';
26
26
  export { useHint } from './useHint';
27
27
  // Game play hook (current game management)
28
28
  export { useGamePlay } from './useGamePlay';
29
+ // Board entry hook
30
+ export { useBoardEntry } from './useBoardEntry';
31
+ // Game session hook
32
+ export { useGameSession } from './useGameSession';
29
33
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAe;AACf,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAQlD,mBAAmB;AACnB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAQ9D,iBAAiB;AACjB,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAQ7D,sBAAsB;AACtB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,iBAAiB;AACjB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAMpD,wCAAwC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,wDAAwD;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAO9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,kBAAkB;AAClB,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,yBAAyB;AACzB,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAMvE,qBAAqB;AACrB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,YAAY;AACZ,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,2CAA2C;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAe;AACf,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAQlD,mBAAmB;AACnB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAQ9D,iBAAiB;AACjB,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAQ7D,sBAAsB;AACtB,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,iBAAiB;AACjB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAMpD,wCAAwC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,wDAAwD;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAO9C,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,kBAAkB;AAClB,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAG9C,yBAAyB;AACzB,OAAO,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAMvE,qBAAqB;AACrB,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,YAAY;AACZ,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AASpC,2CAA2C;AAC3C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,mBAAmB;AACnB,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAOhD,oBAAoB;AACpB,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Hook for managing Sudoku board entry mode state
3
+ * Allows users to enter given clues for a custom puzzle
4
+ */
5
+ import type { SudokuCell } from '../types';
6
+ import type { NetworkClient } from '@sudobility/types';
7
+ export interface ValidatedPuzzle {
8
+ puzzle: string;
9
+ solution: string;
10
+ }
11
+ export interface UseBoardEntryOptions {
12
+ networkClient: NetworkClient;
13
+ baseUrl: string;
14
+ token: string;
15
+ }
16
+ export interface UseBoardEntryReturn {
17
+ /** 81 cells with given values only */
18
+ cells: SudokuCell[];
19
+ /** Currently selected cell index */
20
+ selectedIndex: number | null;
21
+ /** Whether validation is in progress */
22
+ isValidating: boolean;
23
+ /** Validation error message key */
24
+ validationError: string | null;
25
+ /** Validated puzzle data (puzzle + solution) */
26
+ validatedPuzzle: ValidatedPuzzle | null;
27
+ /** Select a cell by index */
28
+ selectCell: (index: number) => void;
29
+ /** Set a given value at the selected cell */
30
+ setGiven: (value: number) => void;
31
+ /** Erase the selected cell */
32
+ erase: () => void;
33
+ /** Get the puzzle as an 81-character string */
34
+ getPuzzleString: () => string;
35
+ /** Trigger validation */
36
+ validate: () => void;
37
+ /** Reset to empty board */
38
+ reset: () => void;
39
+ /** Number of clues entered */
40
+ clueCount: number;
41
+ /** Set cells from an 81-character puzzle string */
42
+ setCellsFromPuzzle: (puzzle: string) => void;
43
+ }
44
+ export declare function useBoardEntry({ networkClient, baseUrl, token, }: UseBoardEntryOptions): UseBoardEntryReturn;
45
+ //# sourceMappingURL=useBoardEntry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBoardEntry.d.ts","sourceRoot":"","sources":["../../src/hooks/useBoardEntry.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,aAAa,EAAE,aAAa,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,mBAAmB;IAClC,sCAAsC;IACtC,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,oCAAoC;IACpC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,wCAAwC;IACxC,YAAY,EAAE,OAAO,CAAC;IACtB,mCAAmC;IACnC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,gDAAgD;IAChD,eAAe,EAAE,eAAe,GAAG,IAAI,CAAC;IACxC,6BAA6B;IAC7B,UAAU,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,6CAA6C;IAC7C,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,8BAA8B;IAC9B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,+CAA+C;IAC/C,eAAe,EAAE,MAAM,MAAM,CAAC;IAC9B,yBAAyB;IACzB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,2BAA2B;IAC3B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,8BAA8B;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,kBAAkB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C;AAsDD,wBAAgB,aAAa,CAAC,EAC5B,aAAa,EACb,OAAO,EACP,KAAK,GACN,EAAE,oBAAoB,GAAG,mBAAmB,CAoK5C"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Hook for managing Sudoku board entry mode state
3
+ * Allows users to enter given clues for a custom puzzle
4
+ */
5
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
6
+ import { useSolverValidate } from '@sudobility/sudojo_client';
7
+ /**
8
+ * Create an empty 81-cell board for entry mode
9
+ */
10
+ function createEmptyBoard() {
11
+ return Array.from({ length: 81 }, (_, index) => ({
12
+ index,
13
+ solution: null,
14
+ given: null,
15
+ input: null,
16
+ pencilmarks: null,
17
+ }));
18
+ }
19
+ /**
20
+ * Convert cells to 81-character puzzle string
21
+ * '0' for empty, '1'-'9' for given values
22
+ */
23
+ function cellsToPuzzleString(cells) {
24
+ return cells.map(cell => cell.given?.toString() ?? '0').join('');
25
+ }
26
+ /**
27
+ * Count the number of non-empty cells (clues)
28
+ */
29
+ function countClues(cells) {
30
+ return cells.filter(cell => cell.given !== null).length;
31
+ }
32
+ /**
33
+ * Create a board from an 81-character puzzle string
34
+ * '0' or '.' for empty, '1'-'9' for given values
35
+ */
36
+ function puzzleStringToCells(puzzle) {
37
+ if (puzzle.length !== 81) {
38
+ return createEmptyBoard();
39
+ }
40
+ return Array.from({ length: 81 }, (_, index) => {
41
+ const char = puzzle.charAt(index);
42
+ const value = parseInt(char, 10);
43
+ const given = value >= 1 && value <= 9 ? value : null;
44
+ return {
45
+ index,
46
+ solution: null,
47
+ given,
48
+ input: null,
49
+ pencilmarks: null,
50
+ };
51
+ });
52
+ }
53
+ export function useBoardEntry({ networkClient, baseUrl, token, }) {
54
+ const [cells, setCells] = useState(createEmptyBoard);
55
+ const [selectedIndex, setSelectedIndex] = useState(null);
56
+ const [validationError, setValidationError] = useState(null);
57
+ const [validatedPuzzle, setValidatedPuzzle] = useState(null);
58
+ const [shouldValidate, setShouldValidate] = useState(false);
59
+ // Compute puzzle string for validation
60
+ const puzzleString = useMemo(() => cellsToPuzzleString(cells), [cells]);
61
+ const clueCount = useMemo(() => countClues(cells), [cells]);
62
+ // Use the solver validate hook
63
+ const { data: validateData, isLoading: isValidating } = useSolverValidate(networkClient, baseUrl, token, { original: puzzleString }, {
64
+ enabled: shouldValidate && clueCount >= 17,
65
+ retry: false,
66
+ });
67
+ // Track whether we've already processed this validation result
68
+ const processedDataRef = useRef(null);
69
+ // Handle validation result
70
+ useEffect(() => {
71
+ if (!shouldValidate || isValidating)
72
+ return;
73
+ if (!validateData)
74
+ return;
75
+ // Avoid re-processing the same result
76
+ if (processedDataRef.current === validateData)
77
+ return;
78
+ processedDataRef.current = validateData;
79
+ // ValidateData has board.solution
80
+ const solution = validateData.data?.board?.solution;
81
+ if (validateData.success && solution) {
82
+ setValidatedPuzzle({
83
+ puzzle: puzzleString,
84
+ solution,
85
+ });
86
+ setValidationError(null);
87
+ }
88
+ else if (validateData.error) {
89
+ // Validation failed with specific error
90
+ const errorMsg = validateData.error.toLowerCase();
91
+ if (errorMsg.includes('multiple') || errorMsg.includes('not unique')) {
92
+ setValidationError('enter.errors.multipleSolutions');
93
+ }
94
+ else if (errorMsg.includes('cannot solve') ||
95
+ errorMsg.includes('no solution')) {
96
+ setValidationError('enter.errors.noSolution');
97
+ }
98
+ else {
99
+ setValidationError('enter.errors.validationFailed');
100
+ }
101
+ setValidatedPuzzle(null);
102
+ }
103
+ else {
104
+ // Unexpected response - success but no solution
105
+ console.error('Unexpected validation response:', validateData);
106
+ setValidationError('enter.errors.validationFailed');
107
+ setValidatedPuzzle(null);
108
+ }
109
+ setShouldValidate(false);
110
+ }, [validateData, shouldValidate, isValidating, puzzleString]);
111
+ // Select a cell
112
+ const selectCell = useCallback((index) => {
113
+ if (index >= 0 && index < 81) {
114
+ setSelectedIndex(index);
115
+ }
116
+ }, []);
117
+ // Set a given value at the selected cell
118
+ const setGiven = useCallback((value) => {
119
+ if (selectedIndex === null || value < 1 || value > 9)
120
+ return;
121
+ setCells(prevCells => {
122
+ const newCells = [...prevCells];
123
+ const existing = newCells[selectedIndex];
124
+ if (existing) {
125
+ newCells[selectedIndex] = { ...existing, given: value };
126
+ }
127
+ return newCells;
128
+ });
129
+ // Clear validation state when board changes
130
+ setValidationError(null);
131
+ setValidatedPuzzle(null);
132
+ }, [selectedIndex]);
133
+ // Erase the selected cell
134
+ const erase = useCallback(() => {
135
+ if (selectedIndex === null)
136
+ return;
137
+ setCells(prevCells => {
138
+ const newCells = [...prevCells];
139
+ const existing = newCells[selectedIndex];
140
+ if (existing) {
141
+ newCells[selectedIndex] = { ...existing, given: null };
142
+ }
143
+ return newCells;
144
+ });
145
+ // Clear validation state when board changes
146
+ setValidationError(null);
147
+ setValidatedPuzzle(null);
148
+ }, [selectedIndex]);
149
+ // Get puzzle string
150
+ const getPuzzleString = useCallback(() => {
151
+ return puzzleString;
152
+ }, [puzzleString]);
153
+ // Trigger validation
154
+ const validate = useCallback(() => {
155
+ // Check minimum clues
156
+ if (clueCount < 17) {
157
+ setValidationError('enter.errors.notEnoughClues');
158
+ return;
159
+ }
160
+ // Clear previous state and trigger validation
161
+ setValidationError(null);
162
+ setValidatedPuzzle(null);
163
+ setShouldValidate(true);
164
+ }, [clueCount]);
165
+ // Reset to empty board
166
+ const reset = useCallback(() => {
167
+ setCells(createEmptyBoard());
168
+ setSelectedIndex(null);
169
+ setValidationError(null);
170
+ setValidatedPuzzle(null);
171
+ setShouldValidate(false);
172
+ }, []);
173
+ // Set cells from puzzle string
174
+ const setCellsFromPuzzle = useCallback((puzzle) => {
175
+ setCells(puzzleStringToCells(puzzle));
176
+ setSelectedIndex(null);
177
+ setValidationError(null);
178
+ setValidatedPuzzle(null);
179
+ setShouldValidate(false);
180
+ }, []);
181
+ return {
182
+ cells,
183
+ selectedIndex,
184
+ isValidating,
185
+ validationError,
186
+ validatedPuzzle,
187
+ selectCell,
188
+ setGiven,
189
+ erase,
190
+ getPuzzleString,
191
+ validate,
192
+ reset,
193
+ clueCount,
194
+ setCellsFromPuzzle,
195
+ };
196
+ }
197
+ //# sourceMappingURL=useBoardEntry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBoardEntry.js","sourceRoot":"","sources":["../../src/hooks/useBoardEntry.ts"],"names":[],"mappings":"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"}
@@ -2,11 +2,12 @@
2
2
  * Hook for managing the current game
3
3
  *
4
4
  * Provides a clean API for starting, updating, and clearing the current game.
5
- * Includes debounced auto-save to avoid excessive localStorage writes.
5
+ * The `slot` option selects which independent store slot to use ('daily' or 'play'),
6
+ * keeping the returned function signatures identical to before.
6
7
  *
7
8
  * @example
8
9
  * ```tsx
9
- * const { currentGame, startGame, updateProgress, clearGame } = useGamePlay();
10
+ * const { currentGame, startGame, updateProgress, clearGame } = useGamePlay({ slot: 'daily' });
10
11
  *
11
12
  * // Start a new game
12
13
  * startGame('daily', puzzle, solution, { dailyDate: '2024-01-15' });
@@ -18,8 +19,10 @@
18
19
  * clearGame();
19
20
  * ```
20
21
  */
21
- import type { CurrentGame, CurrentGameMeta, GameSource } from '../types/currentGame';
22
+ import type { CurrentGame, CurrentGameMeta, GameSlot, GameSource } from '../types/currentGame';
22
23
  export interface UseGamePlayOptions {
24
+ /** Which game slot to use (default: 'play') */
25
+ slot?: GameSlot;
23
26
  /** Debounce delay for auto-save in ms (default: 2000) */
24
27
  autoSaveDelay?: number;
25
28
  }
@@ -1 +1 @@
1
- {"version":3,"file":"useGamePlay.d.ts","sourceRoot":"","sources":["../../src/hooks/useGamePlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAIH,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,UAAU,EACX,MAAM,sBAAsB,CAAC;AAK9B,MAAM,WAAW,kBAAkB;IACjC,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,wCAAwC;IACxC,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,qCAAqC;IACrC,cAAc,EAAE,OAAO,CAAC;IACxB,uBAAuB;IACvB,SAAS,EAAE,CACT,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,eAAe,KACnB,IAAI,CAAC;IACV,6CAA6C;IAC7C,cAAc,EAAE,CACd,WAAW,EAAE,MAAM,EACnB,iBAAiB,EAAE,MAAM,EACzB,YAAY,EAAE,OAAO,EACrB,WAAW,EAAE,MAAM,KAChB,IAAI,CAAC;IACV,8CAA8C;IAC9C,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,wBAAgB,WAAW,CACzB,OAAO,GAAE,kBAAuB,GAC/B,iBAAiB,CAwDnB"}
1
+ {"version":3,"file":"useGamePlay.d.ts","sourceRoot":"","sources":["../../src/hooks/useGamePlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,QAAQ,EACR,UAAU,EACX,MAAM,sBAAsB,CAAC;AAE9B,MAAM,WAAW,kBAAkB;IACjC,+CAA+C;IAC/C,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,iBAAiB;IAChC,wCAAwC;IACxC,WAAW,EAAE,WAAW,GAAG,IAAI,CAAC;IAChC,qCAAqC;IACrC,cAAc,EAAE,OAAO,CAAC;IACxB,uBAAuB;IACvB,SAAS,EAAE,CACT,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,eAAe,KACnB,IAAI,CAAC;IACV,6CAA6C;IAC7C,cAAc,EAAE,CACd,WAAW,EAAE,MAAM,EACnB,iBAAiB,EAAE,MAAM,EACzB,YAAY,EAAE,OAAO,EACrB,WAAW,EAAE,MAAM,KAChB,IAAI,CAAC;IACV,8CAA8C;IAC9C,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED,wBAAgB,WAAW,CACzB,OAAO,GAAE,kBAAuB,GAC/B,iBAAiB,CAmFnB"}
@@ -2,11 +2,12 @@
2
2
  * Hook for managing the current game
3
3
  *
4
4
  * Provides a clean API for starting, updating, and clearing the current game.
5
- * Includes debounced auto-save to avoid excessive localStorage writes.
5
+ * The `slot` option selects which independent store slot to use ('daily' or 'play'),
6
+ * keeping the returned function signatures identical to before.
6
7
  *
7
8
  * @example
8
9
  * ```tsx
9
- * const { currentGame, startGame, updateProgress, clearGame } = useGamePlay();
10
+ * const { currentGame, startGame, updateProgress, clearGame } = useGamePlay({ slot: 'daily' });
10
11
  *
11
12
  * // Start a new game
12
13
  * startGame('daily', puzzle, solution, { dailyDate: '2024-01-15' });
@@ -20,14 +21,23 @@
20
21
  */
21
22
  import { useCallback, useEffect, useRef } from 'react';
22
23
  import { useGamePlayStore } from '../stores/gamePlayStore';
23
- // Get stable reference to store actions (doesn't change on state updates)
24
- const getStoreActions = () => useGamePlayStore.getState();
25
24
  export function useGamePlay(options = {}) {
26
- const { autoSaveDelay = 2000 } = options;
27
- const store = useGamePlayStore();
25
+ const { slot = 'play', autoSaveDelay = 2000 } = options;
26
+ // Select the correct slot from the store
27
+ const currentGame = useGamePlayStore(s => slot === 'daily' ? s.dailyGame : s.playGame);
28
28
  const timeoutRef = useRef(null);
29
29
  const pendingUpdate = useRef(null);
30
- // Debounced update - uses getState() for stable reference
30
+ // Curry slot into startGame so callers keep the same signature
31
+ const startGame = useCallback((source, puzzle, solution, meta) => {
32
+ useGamePlayStore
33
+ .getState()
34
+ .startGame(slot, source, puzzle, solution, meta);
35
+ }, [slot]);
36
+ // Curry slot into clearGame
37
+ const clearGame = useCallback(() => {
38
+ useGamePlayStore.getState().clearGame(slot);
39
+ }, [slot]);
40
+ // Debounced update — curries slot
31
41
  const updateProgress = useCallback((inputString, pencilmarksString, isPencilMode, elapsedTime) => {
32
42
  pendingUpdate.current = [
33
43
  inputString,
@@ -40,12 +50,13 @@ export function useGamePlay(options = {}) {
40
50
  }
41
51
  timeoutRef.current = setTimeout(() => {
42
52
  if (pendingUpdate.current) {
43
- // Use getState() to get stable reference that doesn't trigger re-renders
44
- getStoreActions().updateProgress(...pendingUpdate.current);
53
+ useGamePlayStore
54
+ .getState()
55
+ .updateProgress(slot, ...pendingUpdate.current);
45
56
  pendingUpdate.current = null;
46
57
  }
47
58
  }, autoSaveDelay);
48
- }, [autoSaveDelay]);
59
+ }, [slot, autoSaveDelay]);
49
60
  // Flush pending updates on unmount only
50
61
  useEffect(() => {
51
62
  return () => {
@@ -53,17 +64,18 @@ export function useGamePlay(options = {}) {
53
64
  clearTimeout(timeoutRef.current);
54
65
  }
55
66
  if (pendingUpdate.current) {
56
- // Use getState() to get stable reference that doesn't trigger re-renders
57
- getStoreActions().updateProgress(...pendingUpdate.current);
67
+ useGamePlayStore
68
+ .getState()
69
+ .updateProgress(slot, ...pendingUpdate.current);
58
70
  }
59
71
  };
60
- }, []); // Empty deps - only run cleanup on actual unmount
72
+ }, [slot]);
61
73
  return {
62
- currentGame: store.currentGame,
63
- hasCurrentGame: store.currentGame !== null,
64
- startGame: store.startGame,
74
+ currentGame,
75
+ hasCurrentGame: currentGame !== null,
76
+ startGame,
65
77
  updateProgress,
66
- clearGame: store.clearGame,
78
+ clearGame,
67
79
  };
68
80
  }
69
81
  //# sourceMappingURL=useGamePlay.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useGamePlay.js","sourceRoot":"","sources":["../../src/hooks/useGamePlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAO3D,0EAA0E;AAC1E,MAAM,eAAe,GAAG,GAAG,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,CAAC;AA8B1D,MAAM,UAAU,WAAW,CACzB,UAA8B,EAAE;IAEhC,MAAM,EAAE,aAAa,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IACzC,MAAM,KAAK,GAAG,gBAAgB,EAAE,CAAC;IACjC,MAAM,UAAU,GAAG,MAAM,CAAuC,IAAI,CAAC,CAAC;IACtE,MAAM,aAAa,GAAG,MAAM,CAA2C,IAAI,CAAC,CAAC;IAE7E,0DAA0D;IAC1D,MAAM,cAAc,GAAG,WAAW,CAChC,CACE,WAAmB,EACnB,iBAAyB,EACzB,YAAqB,EACrB,WAAmB,EACnB,EAAE;QACF,aAAa,CAAC,OAAO,GAAG;YACtB,WAAW;YACX,iBAAiB;YACjB,YAAY;YACZ,WAAW;SACZ,CAAC;QAEF,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;YACvB,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;QAED,UAAU,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YACnC,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;gBAC1B,yEAAyE;gBACzE,eAAe,EAAE,CAAC,cAAc,CAAC,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;gBAC3D,aAAa,CAAC,OAAO,GAAG,IAAI,CAAC;YAC/B,CAAC;QACH,CAAC,EAAE,aAAa,CAAC,CAAC;IACpB,CAAC,EACD,CAAC,aAAa,CAAC,CAChB,CAAC;IAEF,wCAAwC;IACxC,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,GAAG,EAAE;YACV,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;gBACvB,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YACnC,CAAC;YACD,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;gBAC1B,yEAAyE;gBACzE,eAAe,EAAE,CAAC,cAAc,CAAC,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,kDAAkD;IAE1D,OAAO;QACL,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,cAAc,EAAE,KAAK,CAAC,WAAW,KAAK,IAAI;QAC1C,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,cAAc;QACd,SAAS,EAAE,KAAK,CAAC,SAAS;KAC3B,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"useGamePlay.js","sourceRoot":"","sources":["../../src/hooks/useGamePlay.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAsC3D,MAAM,UAAU,WAAW,CACzB,UAA8B,EAAE;IAEhC,MAAM,EAAE,IAAI,GAAG,MAAM,EAAE,aAAa,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAExD,yCAAyC;IACzC,MAAM,WAAW,GAAG,gBAAgB,CAAC,CAAC,CAAC,EAAE,CACvC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAC5C,CAAC;IAEF,MAAM,UAAU,GAAG,MAAM,CAAuC,IAAI,CAAC,CAAC;IACtE,MAAM,aAAa,GAAG,MAAM,CAA2C,IAAI,CAAC,CAAC;IAE7E,+DAA+D;IAC/D,MAAM,SAAS,GAAG,WAAW,CAC3B,CACE,MAAkB,EAClB,MAAc,EACd,QAAgB,EAChB,IAAsB,EACtB,EAAE;QACF,gBAAgB;aACb,QAAQ,EAAE;aACV,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,CAAC;IACrD,CAAC,EACD,CAAC,IAAI,CAAC,CACP,CAAC;IAEF,4BAA4B;IAC5B,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE;QACjC,gBAAgB,CAAC,QAAQ,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC9C,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,kCAAkC;IAClC,MAAM,cAAc,GAAG,WAAW,CAChC,CACE,WAAmB,EACnB,iBAAyB,EACzB,YAAqB,EACrB,WAAmB,EACnB,EAAE;QACF,aAAa,CAAC,OAAO,GAAG;YACtB,WAAW;YACX,iBAAiB;YACjB,YAAY;YACZ,WAAW;SACZ,CAAC;QAEF,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;YACvB,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;QAED,UAAU,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;YACnC,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;gBAC1B,gBAAgB;qBACb,QAAQ,EAAE;qBACV,cAAc,CAAC,IAAI,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;gBAClD,aAAa,CAAC,OAAO,GAAG,IAAI,CAAC;YAC/B,CAAC;QACH,CAAC,EAAE,aAAa,CAAC,CAAC;IACpB,CAAC,EACD,CAAC,IAAI,EAAE,aAAa,CAAC,CACtB,CAAC;IAEF,wCAAwC;IACxC,SAAS,CAAC,GAAG,EAAE;QACb,OAAO,GAAG,EAAE;YACV,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;gBACvB,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;YACnC,CAAC;YACD,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;gBAC1B,gBAAgB;qBACb,QAAQ,EAAE;qBACV,cAAc,CAAC,IAAI,EAAE,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;YACpD,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;IAEX,OAAO;QACL,WAAW;QACX,cAAc,EAAE,WAAW,KAAK,IAAI;QACpC,SAAS;QACT,cAAc;QACd,SAAS;KACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Hook for managing game sessions with gamification
3
+ *
4
+ * Handles:
5
+ * - Starting game sessions on the server
6
+ * - Finishing game sessions and receiving rewards
7
+ * - Tracking achievement results for display
8
+ */
9
+ import type { GameFinishResponse, GameStartRequest } from '@sudobility/sudojo_types';
10
+ import type { NetworkClient } from '@sudobility/types';
11
+ export interface UseGameSessionOptions {
12
+ networkClient: NetworkClient;
13
+ baseUrl: string;
14
+ token: string | null;
15
+ userId: string | null;
16
+ }
17
+ export interface GameSessionResult {
18
+ /** Whether a server session was started */
19
+ sessionStarted: boolean;
20
+ /** Session ID from server (if started) */
21
+ sessionId?: string;
22
+ }
23
+ export interface GameFinishResult {
24
+ /** The full response from the server */
25
+ response?: GameFinishResponse;
26
+ /** Whether the user leveled up */
27
+ leveledUp: boolean;
28
+ /** New badges earned */
29
+ newBadges: Array<{
30
+ badgeKey: string;
31
+ title: string;
32
+ description: string | null;
33
+ }>;
34
+ /** Total points earned */
35
+ totalPointsEarned: number;
36
+ }
37
+ export declare function useGameSession({ networkClient, baseUrl, token, userId, }: UseGameSessionOptions): {
38
+ startSession: (data: GameStartRequest) => Promise<GameSessionResult>;
39
+ finishSession: (elapsedTime: number) => Promise<GameFinishResult>;
40
+ isStarting: boolean;
41
+ isFinishing: boolean;
42
+ isAuthenticated: boolean;
43
+ sessionId: string | null;
44
+ };
45
+ //# sourceMappingURL=useGameSession.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useGameSession.d.ts","sourceRoot":"","sources":["../../src/hooks/useGameSession.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EACV,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,WAAW,qBAAqB;IACpC,aAAa,EAAE,aAAa,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,2CAA2C;IAC3C,cAAc,EAAE,OAAO,CAAC;IACxB,0CAA0C;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,wCAAwC;IACxC,QAAQ,CAAC,EAAE,kBAAkB,CAAC;IAC9B,kCAAkC;IAClC,SAAS,EAAE,OAAO,CAAC;IACnB,wBAAwB;IACxB,SAAS,EAAE,KAAK,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;KAC5B,CAAC,CAAC;IACH,0BAA0B;IAC1B,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,cAAc,CAAC,EAC7B,aAAa,EACb,OAAO,EACP,KAAK,EACL,MAAM,GACP,EAAE,qBAAqB;yBAaP,gBAAgB,KAAG,OAAO,CAAC,iBAAiB,CAAC;iCAqCtC,MAAM,KAAG,OAAO,CAAC,gBAAgB,CAAC;;;;;EA4DzD"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Hook for managing game sessions with gamification
3
+ *
4
+ * Handles:
5
+ * - Starting game sessions on the server
6
+ * - Finishing game sessions and receiving rewards
7
+ * - Tracking achievement results for display
8
+ */
9
+ import { useCallback, useRef, useState } from 'react';
10
+ import { useSudojoPlayFinish, useSudojoPlayStart, } from '@sudobility/sudojo_client';
11
+ export function useGameSession({ networkClient, baseUrl, token, userId, }) {
12
+ const [isStarting, setIsStarting] = useState(false);
13
+ const [isFinishing, setIsFinishing] = useState(false);
14
+ const sessionIdRef = useRef(null);
15
+ const playStartMutation = useSudojoPlayStart(networkClient, baseUrl);
16
+ const playFinishMutation = useSudojoPlayFinish(networkClient, baseUrl);
17
+ /**
18
+ * Start a game session on the server
19
+ * Only works if user is authenticated
20
+ */
21
+ const startSession = useCallback(async (data) => {
22
+ // Skip if not authenticated
23
+ if (!userId || !token) {
24
+ return { sessionStarted: false };
25
+ }
26
+ setIsStarting(true);
27
+ try {
28
+ const response = await playStartMutation.mutateAsync({
29
+ token,
30
+ data,
31
+ });
32
+ if (response.success && response.data) {
33
+ sessionIdRef.current = response.data.sessionId;
34
+ return {
35
+ sessionStarted: true,
36
+ sessionId: response.data.sessionId,
37
+ };
38
+ }
39
+ return { sessionStarted: false };
40
+ }
41
+ catch (error) {
42
+ console.error('Failed to start game session:', error);
43
+ return { sessionStarted: false };
44
+ }
45
+ finally {
46
+ setIsStarting(false);
47
+ }
48
+ }, [userId, token, playStartMutation]);
49
+ /**
50
+ * Finish the game session and get rewards
51
+ * Only works if user is authenticated and session was started
52
+ */
53
+ const finishSession = useCallback(async (elapsedTime) => {
54
+ // Skip if not authenticated
55
+ if (!userId || !token) {
56
+ return {
57
+ leveledUp: false,
58
+ newBadges: [],
59
+ totalPointsEarned: 0,
60
+ };
61
+ }
62
+ setIsFinishing(true);
63
+ try {
64
+ const response = await playFinishMutation.mutateAsync({
65
+ token,
66
+ data: { elapsedTime },
67
+ });
68
+ sessionIdRef.current = null;
69
+ if (response.success && response.data) {
70
+ return {
71
+ response: response.data,
72
+ leveledUp: !!response.data.level,
73
+ newBadges: response.data.badges ?? [],
74
+ totalPointsEarned: response.data.points.totalPoints,
75
+ };
76
+ }
77
+ return {
78
+ leveledUp: false,
79
+ newBadges: [],
80
+ totalPointsEarned: 0,
81
+ };
82
+ }
83
+ catch (error) {
84
+ console.error('Failed to finish game session:', error);
85
+ return {
86
+ leveledUp: false,
87
+ newBadges: [],
88
+ totalPointsEarned: 0,
89
+ };
90
+ }
91
+ finally {
92
+ setIsFinishing(false);
93
+ }
94
+ }, [userId, token, playFinishMutation]);
95
+ /**
96
+ * Check if user is authenticated (can use gamification)
97
+ */
98
+ const isAuthenticated = !!userId && !!token;
99
+ return {
100
+ startSession,
101
+ finishSession,
102
+ isStarting,
103
+ isFinishing,
104
+ isAuthenticated,
105
+ sessionId: sessionIdRef.current,
106
+ };
107
+ }
108
+ //# sourceMappingURL=useGameSession.js.map