@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.
- package/dist/config/authProviders.d.ts +18 -0
- package/dist/config/authProviders.d.ts.map +1 -0
- package/dist/config/authProviders.js +12 -0
- package/dist/config/authProviders.js.map +1 -0
- package/dist/config/index.d.ts +3 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +2 -0
- package/dist/config/index.js.map +1 -0
- package/dist/hooks/index.d.ts +4 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +4 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/useBoardEntry.d.ts +45 -0
- package/dist/hooks/useBoardEntry.d.ts.map +1 -0
- package/dist/hooks/useBoardEntry.js +197 -0
- package/dist/hooks/useBoardEntry.js.map +1 -0
- package/dist/hooks/useGamePlay.d.ts +6 -3
- package/dist/hooks/useGamePlay.d.ts.map +1 -1
- package/dist/hooks/useGamePlay.js +29 -17
- package/dist/hooks/useGamePlay.js.map +1 -1
- package/dist/hooks/useGameSession.d.ts +45 -0
- package/dist/hooks/useGameSession.d.ts.map +1 -0
- package/dist/hooks/useGameSession.js +108 -0
- package/dist/hooks/useGameSession.js.map +1 -0
- package/dist/index.d.ts +8 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/stores/gamePlayStore.d.ts +15 -13
- package/dist/stores/gamePlayStore.d.ts.map +1 -1
- package/dist/stores/gamePlayStore.js +29 -9
- package/dist/stores/gamePlayStore.js.map +1 -1
- package/dist/types/currentGame.d.ts +2 -0
- package/dist/types/currentGame.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/utils/hintExplanation.d.ts +17 -0
- package/dist/utils/hintExplanation.d.ts.map +1 -0
- package/dist/utils/hintExplanation.js +882 -0
- package/dist/utils/hintExplanation.js.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/localizedHint.d.ts +17 -0
- package/dist/utils/localizedHint.d.ts.map +1 -0
- package/dist/utils/localizedHint.js +35 -0
- package/dist/utils/localizedHint.js.map +1 -0
- 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 @@
|
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/hooks/index.d.ts
CHANGED
|
@@ -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"}
|
package/dist/hooks/index.js
CHANGED
|
@@ -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
|
package/dist/hooks/index.js.map
CHANGED
|
@@ -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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
44
|
-
|
|
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
|
-
|
|
57
|
-
|
|
67
|
+
useGamePlayStore
|
|
68
|
+
.getState()
|
|
69
|
+
.updateProgress(slot, ...pendingUpdate.current);
|
|
58
70
|
}
|
|
59
71
|
};
|
|
60
|
-
}, []);
|
|
72
|
+
}, [slot]);
|
|
61
73
|
return {
|
|
62
|
-
currentGame
|
|
63
|
-
hasCurrentGame:
|
|
64
|
-
startGame
|
|
74
|
+
currentGame,
|
|
75
|
+
hasCurrentGame: currentGame !== null,
|
|
76
|
+
startGame,
|
|
65
77
|
updateProgress,
|
|
66
|
-
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
|
|
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
|