@ingissa/navcore-react-native 1.0.4 → 1.0.7
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/LICENSE +3 -0
- package/dist/core/src/NavCore.d.ts +142 -0
- package/dist/core/src/NavCore.d.ts.map +1 -0
- package/dist/core/src/NavCore.js +390 -0
- package/dist/core/src/bearing/BearingEngine.d.ts +24 -0
- package/dist/core/src/bearing/BearingEngine.d.ts.map +1 -0
- package/dist/core/src/bearing/BearingEngine.js +50 -0
- package/dist/core/src/constants.d.ts +34 -0
- package/dist/core/src/constants.d.ts.map +1 -0
- package/dist/core/src/constants.js +41 -0
- package/dist/core/src/corridor/DeviationDetector.d.ts +33 -0
- package/dist/core/src/corridor/DeviationDetector.d.ts.map +1 -0
- package/dist/core/src/corridor/DeviationDetector.js +83 -0
- package/dist/core/src/corridor/DynamicCorridor.d.ts +13 -0
- package/dist/core/src/corridor/DynamicCorridor.d.ts.map +1 -0
- package/dist/core/src/corridor/DynamicCorridor.js +26 -0
- package/dist/core/src/dead-reckoning/DeadReckoning.d.ts +35 -0
- package/dist/core/src/dead-reckoning/DeadReckoning.d.ts.map +1 -0
- package/dist/core/src/dead-reckoning/DeadReckoning.js +49 -0
- package/dist/core/src/directions/DirectionsProvider.d.ts +70 -0
- package/dist/core/src/directions/DirectionsProvider.d.ts.map +1 -0
- package/dist/core/src/directions/DirectionsProvider.js +7 -0
- package/dist/core/src/directions/OSRMDirectionsProvider.d.ts +26 -0
- package/dist/core/src/directions/OSRMDirectionsProvider.d.ts.map +1 -0
- package/dist/core/src/directions/OSRMDirectionsProvider.js +90 -0
- package/dist/core/src/directions/OpenRouteServiceProvider.d.ts +30 -0
- package/dist/core/src/directions/OpenRouteServiceProvider.d.ts.map +1 -0
- package/dist/core/src/directions/OpenRouteServiceProvider.js +111 -0
- package/dist/core/src/directions/ValhallaDirectionsProvider.d.ts +33 -0
- package/dist/core/src/directions/ValhallaDirectionsProvider.d.ts.map +1 -0
- package/dist/core/src/directions/ValhallaDirectionsProvider.js +155 -0
- package/dist/core/src/eta/ETAEngine.d.ts +61 -0
- package/dist/core/src/eta/ETAEngine.d.ts.map +1 -0
- package/dist/core/src/eta/ETAEngine.js +74 -0
- package/dist/core/src/features.d.ts +76 -0
- package/dist/core/src/features.d.ts.map +1 -0
- package/dist/core/src/features.js +76 -0
- package/dist/core/src/geo/bearing.d.ts +11 -0
- package/dist/core/src/geo/bearing.d.ts.map +1 -0
- package/dist/core/src/geo/bearing.js +25 -0
- package/dist/core/src/geo/cumulative.d.ts +9 -0
- package/dist/core/src/geo/cumulative.d.ts.map +1 -0
- package/dist/core/src/geo/cumulative.js +20 -0
- package/dist/core/src/geo/haversine.d.ts +13 -0
- package/dist/core/src/geo/haversine.d.ts.map +1 -0
- package/dist/core/src/geo/haversine.js +39 -0
- package/dist/core/src/geo/projection.d.ts +7 -0
- package/dist/core/src/geo/projection.d.ts.map +1 -0
- package/dist/core/src/geo/projection.js +17 -0
- package/dist/core/src/geofencing/GeofencingEngine.d.ts +93 -0
- package/dist/core/src/geofencing/GeofencingEngine.d.ts.map +1 -0
- package/dist/core/src/geofencing/GeofencingEngine.js +154 -0
- package/dist/core/src/index.d.ts +66 -0
- package/dist/core/src/index.d.ts.map +1 -0
- package/dist/core/src/index.js +62 -0
- package/dist/core/src/kalman/KalmanFilter2D.d.ts +46 -0
- package/dist/core/src/kalman/KalmanFilter2D.d.ts.map +1 -0
- package/dist/core/src/kalman/KalmanFilter2D.js +77 -0
- package/dist/core/src/license/LicenseManager.d.ts +40 -0
- package/dist/core/src/license/LicenseManager.d.ts.map +1 -0
- package/dist/core/src/license/LicenseManager.js +119 -0
- package/dist/core/src/license/verifyP256Signature.d.ts +2 -0
- package/dist/core/src/license/verifyP256Signature.d.ts.map +1 -0
- package/dist/core/src/license/verifyP256Signature.js +26 -0
- package/dist/core/src/off-route/NavigationFSM.d.ts +42 -0
- package/dist/core/src/off-route/NavigationFSM.d.ts.map +1 -0
- package/dist/core/src/off-route/NavigationFSM.js +56 -0
- package/dist/core/src/off-route/OffRouteFSM.d.ts +70 -0
- package/dist/core/src/off-route/OffRouteFSM.d.ts.map +1 -0
- package/dist/core/src/off-route/OffRouteFSM.js +151 -0
- package/dist/core/src/predictor/RouteOffsetPredictor.d.ts +24 -0
- package/dist/core/src/predictor/RouteOffsetPredictor.d.ts.map +1 -0
- package/dist/core/src/predictor/RouteOffsetPredictor.js +55 -0
- package/dist/core/src/progress/InstructionEditor.d.ts +77 -0
- package/dist/core/src/progress/InstructionEditor.d.ts.map +1 -0
- package/dist/core/src/progress/InstructionEditor.js +151 -0
- package/dist/core/src/progress/InstructionResolver.d.ts +25 -0
- package/dist/core/src/progress/InstructionResolver.d.ts.map +1 -0
- package/dist/core/src/progress/InstructionResolver.js +78 -0
- package/dist/core/src/progress/ProgressTracker.d.ts +12 -0
- package/dist/core/src/progress/ProgressTracker.d.ts.map +1 -0
- package/dist/core/src/progress/ProgressTracker.js +22 -0
- package/dist/core/src/renderer/MapRendererAdapter.d.ts +72 -0
- package/dist/core/src/renderer/MapRendererAdapter.d.ts.map +1 -0
- package/dist/core/src/renderer/MapRendererAdapter.js +19 -0
- package/dist/core/src/route-builder/CustomRouteBuilder.d.ts +83 -0
- package/dist/core/src/route-builder/CustomRouteBuilder.d.ts.map +1 -0
- package/dist/core/src/route-builder/CustomRouteBuilder.js +160 -0
- package/dist/core/src/route-plan/RoutePlan.d.ts +38 -0
- package/dist/core/src/route-plan/RoutePlan.d.ts.map +1 -0
- package/dist/core/src/route-plan/RoutePlan.js +102 -0
- package/dist/core/src/session/NavigationSession.d.ts +81 -0
- package/dist/core/src/session/NavigationSession.d.ts.map +1 -0
- package/dist/core/src/session/NavigationSession.js +115 -0
- package/dist/core/src/snapper/ParallelRoadResolver.d.ts +86 -0
- package/dist/core/src/snapper/ParallelRoadResolver.d.ts.map +1 -0
- package/dist/core/src/snapper/ParallelRoadResolver.js +83 -0
- package/dist/core/src/snapper/RouteSnapper.d.ts +38 -0
- package/dist/core/src/snapper/RouteSnapper.d.ts.map +1 -0
- package/dist/core/src/snapper/RouteSnapper.js +80 -0
- package/dist/core/src/voice/VoiceTriggerEngine.d.ts +80 -0
- package/dist/core/src/voice/VoiceTriggerEngine.d.ts.map +1 -0
- package/dist/core/src/voice/VoiceTriggerEngine.js +108 -0
- package/dist/react-native/src/NavCoreProvider.d.ts +33 -0
- package/dist/react-native/src/NavCoreProvider.d.ts.map +1 -0
- package/dist/react-native/src/NavCoreProvider.js +41 -0
- package/dist/react-native/src/index.d.ts +10 -0
- package/dist/react-native/src/index.d.ts.map +1 -0
- package/dist/react-native/src/index.js +8 -0
- package/dist/{index.d.mts → react-native/src/useNavCore.d.ts} +12 -39
- package/dist/react-native/src/useNavCore.d.ts.map +1 -0
- package/dist/react-native/src/useNavCore.js +119 -0
- package/dist/react-native/src/useNavSelectors.d.ts +42 -0
- package/dist/react-native/src/useNavSelectors.d.ts.map +1 -0
- package/dist/react-native/src/useNavSelectors.js +103 -0
- package/package.json +20 -12
- package/src/NavCoreProvider.tsx +59 -0
- package/src/index.ts +18 -0
- package/src/useNavCore.ts +208 -0
- package/src/useNavSelectors.ts +143 -0
- package/dist/index.d.ts +0 -87
- package/dist/index.js +0 -161
- package/dist/index.mjs +0 -157
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VoiceTriggerEngine
|
|
3
|
+
*
|
|
4
|
+
* Converts NavCore instruction events into timed voice cues.
|
|
5
|
+
* Handles deduplication, look-ahead windows, and priority levels.
|
|
6
|
+
* Consumer calls TTS — this engine only computes WHEN to speak.
|
|
7
|
+
*
|
|
8
|
+
* FEATURE: VOICE_TRIGGERS
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const voice = new VoiceTriggerEngine({ earlyTriggerMeters: 150 });
|
|
13
|
+
* voice.setInstructions(instructions);
|
|
14
|
+
*
|
|
15
|
+
* engine.on('instruction', (instr) => {
|
|
16
|
+
* const cue = voice.onInstruction(instr, state);
|
|
17
|
+
* if (cue) speakText(cue.text);
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // Or poll each GPS tick:
|
|
21
|
+
* const cue = voice.update(state);
|
|
22
|
+
* if (cue) speakText(cue.text);
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
import type { NavInstruction } from '../progress/InstructionResolver';
|
|
26
|
+
import type { NavCoreState } from '../NavCore';
|
|
27
|
+
export type VoicePriority = 'low' | 'normal' | 'high' | 'urgent';
|
|
28
|
+
export interface VoiceCue {
|
|
29
|
+
/** The instruction that triggered this cue */
|
|
30
|
+
instruction: NavInstruction;
|
|
31
|
+
/** Text to pass to TTS */
|
|
32
|
+
text: string;
|
|
33
|
+
/** Distance to the instruction point in metres */
|
|
34
|
+
distanceMetres: number;
|
|
35
|
+
/** Priority hint for TTS queue management */
|
|
36
|
+
priority: VoicePriority;
|
|
37
|
+
/** Monotonic trigger ID — use this to deduplicate */
|
|
38
|
+
triggerId: string;
|
|
39
|
+
}
|
|
40
|
+
export interface VoiceTriggerOptions {
|
|
41
|
+
/**
|
|
42
|
+
* Trigger voice this many metres before the instruction point.
|
|
43
|
+
* Default: 120m (comfortable at 50 km/h).
|
|
44
|
+
*/
|
|
45
|
+
earlyTriggerMeters?: number;
|
|
46
|
+
/**
|
|
47
|
+
* Stop trying to trigger if vehicle is already past this many metres.
|
|
48
|
+
* Default: 20m.
|
|
49
|
+
*/
|
|
50
|
+
lateTriggerMeters?: number;
|
|
51
|
+
/**
|
|
52
|
+
* Re-announce if user is still approaching after this distance.
|
|
53
|
+
* Set to 0 to disable re-announcement. Default: 300m.
|
|
54
|
+
*/
|
|
55
|
+
reannounceMeters?: number;
|
|
56
|
+
}
|
|
57
|
+
export declare class VoiceTriggerEngine {
|
|
58
|
+
private instructions;
|
|
59
|
+
private triggeredIds;
|
|
60
|
+
private reannouncedIds;
|
|
61
|
+
private readonly options;
|
|
62
|
+
constructor(options?: VoiceTriggerOptions);
|
|
63
|
+
/** Load the instruction set (call after engine.setRoute) */
|
|
64
|
+
setInstructions(instructions: NavInstruction[]): void;
|
|
65
|
+
/**
|
|
66
|
+
* Poll on every GPS state update.
|
|
67
|
+
* Returns a `VoiceCue` when an instruction should be spoken, or null.
|
|
68
|
+
*/
|
|
69
|
+
update(state: NavCoreState): VoiceCue | null;
|
|
70
|
+
/**
|
|
71
|
+
* Called directly from engine's 'instruction' event.
|
|
72
|
+
* Always fires immediately regardless of distance window.
|
|
73
|
+
*/
|
|
74
|
+
onInstruction(instr: NavInstruction, state: NavCoreState): VoiceCue;
|
|
75
|
+
/** Reset triggered state (call on setRoute / new journey) */
|
|
76
|
+
reset(): void;
|
|
77
|
+
private instrId;
|
|
78
|
+
private makeCue;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=VoiceTriggerEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VoiceTriggerEngine.d.ts","sourceRoot":"","sources":["../../../../../core/src/voice/VoiceTriggerEngine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;AAEjE,MAAM,WAAW,QAAQ;IACvB,8CAA8C;IAC9C,WAAW,EAAE,cAAc,CAAC;IAC5B,0BAA0B;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,kDAAkD;IAClD,cAAc,EAAE,MAAM,CAAC;IACvB,6CAA6C;IAC7C,QAAQ,EAAE,aAAa,CAAC;IACxB,qDAAqD;IACrD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAQD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;gBAE5C,OAAO,CAAC,EAAE,mBAAmB;IAIzC,4DAA4D;IAC5D,eAAe,CAAC,YAAY,EAAE,cAAc,EAAE,GAAG,IAAI;IAMrD;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,YAAY,GAAG,QAAQ,GAAG,IAAI;IA6C5C;;;OAGG;IACH,aAAa,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,YAAY,GAAG,QAAQ;IAKnE,6DAA6D;IAC7D,KAAK,IAAI,IAAI;IAKb,OAAO,CAAC,OAAO;IAKf,OAAO,CAAC,OAAO;CAShB"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VoiceTriggerEngine
|
|
3
|
+
*
|
|
4
|
+
* Converts NavCore instruction events into timed voice cues.
|
|
5
|
+
* Handles deduplication, look-ahead windows, and priority levels.
|
|
6
|
+
* Consumer calls TTS — this engine only computes WHEN to speak.
|
|
7
|
+
*
|
|
8
|
+
* FEATURE: VOICE_TRIGGERS
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const voice = new VoiceTriggerEngine({ earlyTriggerMeters: 150 });
|
|
13
|
+
* voice.setInstructions(instructions);
|
|
14
|
+
*
|
|
15
|
+
* engine.on('instruction', (instr) => {
|
|
16
|
+
* const cue = voice.onInstruction(instr, state);
|
|
17
|
+
* if (cue) speakText(cue.text);
|
|
18
|
+
* });
|
|
19
|
+
*
|
|
20
|
+
* // Or poll each GPS tick:
|
|
21
|
+
* const cue = voice.update(state);
|
|
22
|
+
* if (cue) speakText(cue.text);
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
const DEFAULTS = {
|
|
26
|
+
earlyTriggerMeters: 120,
|
|
27
|
+
lateTriggerMeters: 20,
|
|
28
|
+
reannounceMeters: 300,
|
|
29
|
+
};
|
|
30
|
+
export class VoiceTriggerEngine {
|
|
31
|
+
constructor(options) {
|
|
32
|
+
this.instructions = [];
|
|
33
|
+
this.triggeredIds = new Set();
|
|
34
|
+
this.reannouncedIds = new Set();
|
|
35
|
+
this.options = { ...DEFAULTS, ...options };
|
|
36
|
+
}
|
|
37
|
+
/** Load the instruction set (call after engine.setRoute) */
|
|
38
|
+
setInstructions(instructions) {
|
|
39
|
+
this.instructions = [...instructions];
|
|
40
|
+
this.triggeredIds.clear();
|
|
41
|
+
this.reannouncedIds.clear();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Poll on every GPS state update.
|
|
45
|
+
* Returns a `VoiceCue` when an instruction should be spoken, or null.
|
|
46
|
+
*/
|
|
47
|
+
update(state) {
|
|
48
|
+
const { distanceToNextInstruction, nextInstructionIndex } = state;
|
|
49
|
+
if (nextInstructionIndex === null ||
|
|
50
|
+
distanceToNextInstruction === null ||
|
|
51
|
+
nextInstructionIndex >= this.instructions.length) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const instr = this.instructions[nextInstructionIndex];
|
|
55
|
+
const id = this.instrId(nextInstructionIndex);
|
|
56
|
+
const dist = distanceToNextInstruction;
|
|
57
|
+
// Already fully triggered — don't repeat
|
|
58
|
+
if (this.triggeredIds.has(id)) {
|
|
59
|
+
// Check re-announce window
|
|
60
|
+
if (this.options.reannounceMeters > 0 &&
|
|
61
|
+
!this.reannouncedIds.has(id) &&
|
|
62
|
+
dist <= this.options.reannounceMeters &&
|
|
63
|
+
dist > this.options.earlyTriggerMeters) {
|
|
64
|
+
this.reannouncedIds.add(id);
|
|
65
|
+
return this.makeCue(instr, dist, 'low');
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
// Too far — not yet
|
|
70
|
+
if (dist > this.options.earlyTriggerMeters)
|
|
71
|
+
return null;
|
|
72
|
+
// Already passed — too late
|
|
73
|
+
if (dist < -this.options.lateTriggerMeters) {
|
|
74
|
+
this.triggeredIds.add(id); // mark so we skip
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
// In the trigger window — fire!
|
|
78
|
+
this.triggeredIds.add(id);
|
|
79
|
+
const priority = dist < 30 ? 'urgent' : dist < 60 ? 'high' : 'normal';
|
|
80
|
+
return this.makeCue(instr, dist, priority);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Called directly from engine's 'instruction' event.
|
|
84
|
+
* Always fires immediately regardless of distance window.
|
|
85
|
+
*/
|
|
86
|
+
onInstruction(instr, state) {
|
|
87
|
+
const dist = state.distanceToNextInstruction ?? 0;
|
|
88
|
+
return this.makeCue(instr, dist, 'high');
|
|
89
|
+
}
|
|
90
|
+
/** Reset triggered state (call on setRoute / new journey) */
|
|
91
|
+
reset() {
|
|
92
|
+
this.triggeredIds.clear();
|
|
93
|
+
this.reannouncedIds.clear();
|
|
94
|
+
}
|
|
95
|
+
instrId(index) {
|
|
96
|
+
const instr = this.instructions[index];
|
|
97
|
+
return `${index}_${instr?.geometryIndex ?? 0}`;
|
|
98
|
+
}
|
|
99
|
+
makeCue(instr, dist, priority) {
|
|
100
|
+
return {
|
|
101
|
+
instruction: instr,
|
|
102
|
+
text: instr.text ?? instr.instruction ?? '',
|
|
103
|
+
distanceMetres: Math.max(0, dist),
|
|
104
|
+
priority,
|
|
105
|
+
triggerId: `${instr.geometryIndex}_${dist.toFixed(0)}`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NavCoreProvider — React Context for shared NavCore engine access.
|
|
3
|
+
*
|
|
4
|
+
* Use when multiple components need access to the same engine instance
|
|
5
|
+
* (e.g. map view, instruction banner, debug panel).
|
|
6
|
+
*/
|
|
7
|
+
import { type ReactNode } from 'react';
|
|
8
|
+
import { NavCore, type NavCoreOptions } from '@ingissa/navcore-core';
|
|
9
|
+
interface NavCoreContextValue {
|
|
10
|
+
engine: NavCore;
|
|
11
|
+
}
|
|
12
|
+
interface NavCoreProviderProps {
|
|
13
|
+
children: ReactNode;
|
|
14
|
+
options?: NavCoreOptions;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Wraps children with a shared NavCore engine instance.
|
|
18
|
+
*
|
|
19
|
+
* ```tsx
|
|
20
|
+
* <NavCoreProvider options={{ licenseKey: '...' }}>
|
|
21
|
+
* <MapScreen />
|
|
22
|
+
* <InstructionBanner />
|
|
23
|
+
* </NavCoreProvider>
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
export declare function NavCoreProvider({ children, options }: NavCoreProviderProps): import("react/jsx-runtime").JSX.Element;
|
|
27
|
+
/**
|
|
28
|
+
* Access the shared NavCore engine from context.
|
|
29
|
+
* Must be used within a <NavCoreProvider>.
|
|
30
|
+
*/
|
|
31
|
+
export declare function useNavCoreContext(): NavCoreContextValue;
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=NavCoreProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NavCoreProvider.d.ts","sourceRoot":"","sources":["../../../src/NavCoreProvider.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAA8C,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACnF,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAErE,UAAU,mBAAmB;IAC3B,MAAM,EAAE,OAAO,CAAC;CACjB;AAID,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,SAAS,CAAC;IACpB,OAAO,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,EAAE,oBAAoB,2CAe1E;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,mBAAmB,CAMvD"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* NavCoreProvider — React Context for shared NavCore engine access.
|
|
4
|
+
*
|
|
5
|
+
* Use when multiple components need access to the same engine instance
|
|
6
|
+
* (e.g. map view, instruction banner, debug panel).
|
|
7
|
+
*/
|
|
8
|
+
import { createContext, useContext, useRef, useMemo } from 'react';
|
|
9
|
+
import { NavCore } from '@ingissa/navcore-core';
|
|
10
|
+
const NavCoreContext = createContext(null);
|
|
11
|
+
/**
|
|
12
|
+
* Wraps children with a shared NavCore engine instance.
|
|
13
|
+
*
|
|
14
|
+
* ```tsx
|
|
15
|
+
* <NavCoreProvider options={{ licenseKey: '...' }}>
|
|
16
|
+
* <MapScreen />
|
|
17
|
+
* <InstructionBanner />
|
|
18
|
+
* </NavCoreProvider>
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export function NavCoreProvider({ children, options }) {
|
|
22
|
+
const engineRef = useRef(null);
|
|
23
|
+
if (!engineRef.current) {
|
|
24
|
+
engineRef.current = new NavCore(options);
|
|
25
|
+
}
|
|
26
|
+
const value = useMemo(() => ({
|
|
27
|
+
engine: engineRef.current,
|
|
28
|
+
}), []);
|
|
29
|
+
return (_jsx(NavCoreContext.Provider, { value: value, children: children }));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Access the shared NavCore engine from context.
|
|
33
|
+
* Must be used within a <NavCoreProvider>.
|
|
34
|
+
*/
|
|
35
|
+
export function useNavCoreContext() {
|
|
36
|
+
const ctx = useContext(NavCoreContext);
|
|
37
|
+
if (!ctx) {
|
|
38
|
+
throw new Error('useNavCoreContext must be used within a <NavCoreProvider>');
|
|
39
|
+
}
|
|
40
|
+
return ctx;
|
|
41
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @navcore/react-native — React Native bindings
|
|
3
|
+
*
|
|
4
|
+
* Provides hooks for integrating NavCore with React Native apps.
|
|
5
|
+
*/
|
|
6
|
+
export { useNavCore } from './useNavCore';
|
|
7
|
+
export type { UseNavCoreProps, UseNavCoreReturn } from './useNavCore';
|
|
8
|
+
export { NavCoreProvider, useNavCoreContext } from './NavCoreProvider';
|
|
9
|
+
export { useNavSelector, useNavInstruction, useNavAlert, useNavProgress, useNavIsOffRoute, useNavSpeed, type NavProgressInfo, } from './useNavSelectors';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACvE,OAAO,EACL,cAAc,EACd,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,WAAW,EACX,KAAK,eAAe,GACrB,MAAM,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @navcore/react-native — React Native bindings
|
|
3
|
+
*
|
|
4
|
+
* Provides hooks for integrating NavCore with React Native apps.
|
|
5
|
+
*/
|
|
6
|
+
export { useNavCore } from './useNavCore';
|
|
7
|
+
export { NavCoreProvider, useNavCoreContext } from './NavCoreProvider';
|
|
8
|
+
export { useNavSelector, useNavInstruction, useNavAlert, useNavProgress, useNavIsOffRoute, useNavSpeed, } from './useNavSelectors';
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { Coordinate, NavInstruction, NavCoreOptions, NavCoreState, NavCore } from '@ingissa/navcore-core';
|
|
2
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
-
import { ReactNode } from 'react';
|
|
4
|
-
|
|
5
1
|
/**
|
|
6
2
|
* useNavCore — React hook for the NavCore navigation engine.
|
|
7
3
|
*
|
|
@@ -10,8 +6,8 @@ import { ReactNode } from 'react';
|
|
|
10
6
|
*
|
|
11
7
|
* Drop-in replacement for the existing Go4Exams useNavCore hook.
|
|
12
8
|
*/
|
|
13
|
-
|
|
14
|
-
interface UseNavCoreProps {
|
|
9
|
+
import { NavCore, type NavCoreOptions, type NavCoreState, type NavInstruction, type Coordinate } from '@ingissa/navcore-core';
|
|
10
|
+
export interface UseNavCoreProps {
|
|
15
11
|
/** Current GPS coordinate [lng, lat] */
|
|
16
12
|
gpsCoord: Coordinate | null;
|
|
17
13
|
/** GPS bearing in degrees */
|
|
@@ -33,11 +29,13 @@ interface UseNavCoreProps {
|
|
|
33
29
|
/** Resume navigation at a specific route index (after rejoin) */
|
|
34
30
|
resumeAtIndex?: number | null;
|
|
35
31
|
/** Ref for external camera output (map camera integration) */
|
|
36
|
-
outputRef?:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
32
|
+
outputRef?: {
|
|
33
|
+
current: {
|
|
34
|
+
coord: Coordinate;
|
|
35
|
+
bearing: number;
|
|
36
|
+
speed: number;
|
|
37
|
+
} | null;
|
|
38
|
+
};
|
|
41
39
|
/** Called when deviation is confirmed */
|
|
42
40
|
onDeviation?: (event: {
|
|
43
41
|
anchorIndex: number;
|
|
@@ -54,34 +52,9 @@ interface UseNavCoreProps {
|
|
|
54
52
|
/** UI polling rate in Hz (default 30) */
|
|
55
53
|
pollRateHz?: number;
|
|
56
54
|
}
|
|
57
|
-
interface UseNavCoreReturn extends NavCoreState {
|
|
55
|
+
export interface UseNavCoreReturn extends NavCoreState {
|
|
58
56
|
/** The raw NavCore engine instance (for advanced usage) */
|
|
59
57
|
engine: NavCore;
|
|
60
58
|
}
|
|
61
|
-
declare function useNavCore({ gpsCoord, gpsBearing, gpsAccuracy, gpsTimestamp, speed, routeGeometry, instructions, isNavigating, options, resumeAtIndex, outputRef, onDeviation, onRejoin, onInstruction, onArrival, pollRateHz, }: UseNavCoreProps): UseNavCoreReturn;
|
|
62
|
-
|
|
63
|
-
interface NavCoreContextValue {
|
|
64
|
-
engine: NavCore;
|
|
65
|
-
}
|
|
66
|
-
interface NavCoreProviderProps {
|
|
67
|
-
children: ReactNode;
|
|
68
|
-
options?: NavCoreOptions;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Wraps children with a shared NavCore engine instance.
|
|
72
|
-
*
|
|
73
|
-
* ```tsx
|
|
74
|
-
* <NavCoreProvider options={{ licenseKey: '...' }}>
|
|
75
|
-
* <MapScreen />
|
|
76
|
-
* <InstructionBanner />
|
|
77
|
-
* </NavCoreProvider>
|
|
78
|
-
* ```
|
|
79
|
-
*/
|
|
80
|
-
declare function NavCoreProvider({ children, options }: NavCoreProviderProps): react_jsx_runtime.JSX.Element;
|
|
81
|
-
/**
|
|
82
|
-
* Access the shared NavCore engine from context.
|
|
83
|
-
* Must be used within a <NavCoreProvider>.
|
|
84
|
-
*/
|
|
85
|
-
declare function useNavCoreContext(): NavCoreContextValue;
|
|
86
|
-
|
|
87
|
-
export { NavCoreProvider, type UseNavCoreProps, type UseNavCoreReturn, useNavCore, useNavCoreContext };
|
|
59
|
+
export declare function useNavCore({ gpsCoord, gpsBearing, gpsAccuracy, gpsTimestamp, speed, routeGeometry, instructions, isNavigating, options, resumeAtIndex, outputRef, onDeviation, onRejoin, onInstruction, onArrival, pollRateHz, }: UseNavCoreProps): UseNavCoreReturn;
|
|
60
|
+
//# sourceMappingURL=useNavCore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useNavCore.d.ts","sourceRoot":"","sources":["../../../src/useNavCore.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EACL,OAAO,EACP,KAAK,cAAc,EAEnB,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,UAAU,EAChB,MAAM,uBAAuB,CAAC;AAI/B,MAAM,WAAW,eAAe;IAC9B,wCAAwC;IACxC,QAAQ,EAAE,UAAU,GAAG,IAAI,CAAC;IAC5B,6BAA6B;IAC7B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,6BAA6B;IAC7B,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,qCAAqC;IACrC,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,kCAAkC;IAClC,aAAa,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;IACnC,8BAA8B;IAC9B,YAAY,CAAC,EAAE,cAAc,EAAE,CAAC;IAChC,sDAAsD;IACtD,YAAY,EAAE,OAAO,CAAC;IACtB,2BAA2B;IAC3B,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,iEAAiE;IACjE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,8DAA8D;IAC9D,SAAS,CAAC,EAAE;QACV,OAAO,EAAE;YACP,KAAK,EAAE,UAAU,CAAC;YAClB,OAAO,EAAE,MAAM,CAAC;YAChB,KAAK,EAAE,MAAM,CAAC;SACf,GAAG,IAAI,CAAC;KACV,CAAC;IACF,yCAAyC;IACzC,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,UAAU,CAAA;KAAE,KAAK,IAAI,CAAC;IAC/E,mCAAmC;IACnC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,WAAW,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACpD,0CAA0C;IAC1C,aAAa,CAAC,EAAE,CAAC,WAAW,EAAE,cAAc,KAAK,IAAI,CAAC;IACtD,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACpD,2DAA2D;IAC3D,MAAM,EAAE,OAAO,CAAC;CACjB;AAID,wBAAgB,UAAU,CAAC,EACzB,QAAQ,EACR,UAAU,EACV,WAAW,EACX,YAAY,EACZ,KAAK,EACL,aAAa,EACb,YAAY,EACZ,YAAY,EACZ,OAAO,EACP,aAAa,EACb,SAAS,EACT,WAAW,EACX,QAAQ,EACR,aAAa,EACb,SAAS,EACT,UAAe,GAChB,EAAE,eAAe,GAAG,gBAAgB,CAyHpC"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useNavCore — React hook for the NavCore navigation engine.
|
|
3
|
+
*
|
|
4
|
+
* Manages engine lifecycle, GPS → engine → state pipeline,
|
|
5
|
+
* event wiring, and optimized re-render batching.
|
|
6
|
+
*
|
|
7
|
+
* Drop-in replacement for the existing Go4Exams useNavCore hook.
|
|
8
|
+
*/
|
|
9
|
+
import { useState, useEffect, useRef, useMemo } from 'react';
|
|
10
|
+
import { NavCore, } from '@ingissa/navcore-core';
|
|
11
|
+
// ── Hook ──────────────────────────────────────────────────────
|
|
12
|
+
export function useNavCore({ gpsCoord, gpsBearing, gpsAccuracy, gpsTimestamp, speed, routeGeometry, instructions, isNavigating, options, resumeAtIndex, outputRef, onDeviation, onRejoin, onInstruction, onArrival, pollRateHz = 30, }) {
|
|
13
|
+
// ── 1. Engine singleton ───────────────────────────────────────
|
|
14
|
+
const navRef = useRef(null);
|
|
15
|
+
if (!navRef.current) {
|
|
16
|
+
navRef.current = new NavCore(options);
|
|
17
|
+
}
|
|
18
|
+
const engine = navRef.current;
|
|
19
|
+
const [state, setState] = useState(() => engine.getState());
|
|
20
|
+
// ── 2. Event wiring ───────────────────────────────────────────
|
|
21
|
+
// 2a. Deviation
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (!onDeviation)
|
|
24
|
+
return;
|
|
25
|
+
const handler = (data) => onDeviation(data);
|
|
26
|
+
engine.on('deviation', handler);
|
|
27
|
+
return () => { engine.off('deviation', handler); };
|
|
28
|
+
}, [engine, onDeviation]);
|
|
29
|
+
// 2b. Rejoin
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!onRejoin)
|
|
32
|
+
return;
|
|
33
|
+
const handler = (data) => onRejoin(data);
|
|
34
|
+
engine.on('rejoined', handler);
|
|
35
|
+
return () => { engine.off('rejoined', handler); };
|
|
36
|
+
}, [engine, onRejoin]);
|
|
37
|
+
// 2c. Instruction
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!onInstruction)
|
|
40
|
+
return;
|
|
41
|
+
const handler = (data) => onInstruction(data);
|
|
42
|
+
engine.on('instruction', handler);
|
|
43
|
+
return () => { engine.off('instruction', handler); };
|
|
44
|
+
}, [engine, onInstruction]);
|
|
45
|
+
// 2d. Arrival
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (!onArrival)
|
|
48
|
+
return;
|
|
49
|
+
const handler = () => onArrival();
|
|
50
|
+
engine.on('arrival', handler);
|
|
51
|
+
return () => { engine.off('arrival', handler); };
|
|
52
|
+
}, [engine, onArrival]);
|
|
53
|
+
// ── 3. Route sync ────────────────────────────────────────────
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (routeGeometry && routeGeometry.length >= 2) {
|
|
56
|
+
engine.setRoute(routeGeometry, instructions);
|
|
57
|
+
}
|
|
58
|
+
}, [engine, routeGeometry]);
|
|
59
|
+
// ── 4. Instructions sync (can change independently of route) ──
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (instructions && instructions.length > 0 && routeGeometry && routeGeometry.length >= 2) {
|
|
62
|
+
engine.setRoute(routeGeometry, instructions);
|
|
63
|
+
}
|
|
64
|
+
}, [engine, instructions]);
|
|
65
|
+
// ── 5. GPS → engine pipeline ──────────────────────────────────
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
if (!gpsCoord || !isNavigating)
|
|
68
|
+
return;
|
|
69
|
+
const gpsUpdate = {
|
|
70
|
+
coord: gpsCoord,
|
|
71
|
+
accuracy: gpsAccuracy ?? null,
|
|
72
|
+
bearing: gpsBearing ?? null,
|
|
73
|
+
speed,
|
|
74
|
+
timestamp: gpsTimestamp ?? Date.now(),
|
|
75
|
+
};
|
|
76
|
+
const nextState = engine.update(gpsUpdate);
|
|
77
|
+
// Synchronous external camera/puck output with ZERO React re-renders
|
|
78
|
+
if (outputRef) {
|
|
79
|
+
outputRef.current = {
|
|
80
|
+
coord: nextState.snappedCoord ?? gpsCoord,
|
|
81
|
+
bearing: nextState.bearing,
|
|
82
|
+
speed: nextState.currentSpeed,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
}, [engine, gpsCoord, gpsAccuracy, gpsBearing, gpsTimestamp, speed, isNavigating, outputRef]);
|
|
86
|
+
// ── 6. Start navigation ───────────────────────────────────────
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (isNavigating) {
|
|
89
|
+
engine.startNavigation();
|
|
90
|
+
}
|
|
91
|
+
}, [engine, isNavigating]);
|
|
92
|
+
// ── 7. Push-based reactive state subscription ─────────────────
|
|
93
|
+
// Eliminates the 30 Hz setInterval loop. Updates state reactively
|
|
94
|
+
// only when new engine state is computed.
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (!isNavigating)
|
|
97
|
+
return;
|
|
98
|
+
const handleUpdate = (updatedState) => {
|
|
99
|
+
setState(updatedState);
|
|
100
|
+
};
|
|
101
|
+
engine.on('update', handleUpdate);
|
|
102
|
+
setState(engine.getState());
|
|
103
|
+
return () => {
|
|
104
|
+
engine.off('update', handleUpdate);
|
|
105
|
+
};
|
|
106
|
+
}, [engine, isNavigating]);
|
|
107
|
+
// ── 8. Cleanup ────────────────────────────────────────────────
|
|
108
|
+
useEffect(() => {
|
|
109
|
+
return () => {
|
|
110
|
+
engine.destroy();
|
|
111
|
+
navRef.current = null;
|
|
112
|
+
};
|
|
113
|
+
}, [engine]);
|
|
114
|
+
// ── 9. Stable return ──────────────────────────────────────────
|
|
115
|
+
return useMemo(() => ({
|
|
116
|
+
...state,
|
|
117
|
+
engine,
|
|
118
|
+
}), [state, engine]);
|
|
119
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Granular selector hooks for NavCore.
|
|
3
|
+
*
|
|
4
|
+
* Built with React 18 `useSyncExternalStore` for flicker-free, tearing-free
|
|
5
|
+
* state subscriptions. Components only re-render when the selected slice changes.
|
|
6
|
+
*/
|
|
7
|
+
import type { NavCore, NavCoreState, NavInstruction } from '@ingissa/navcore-core';
|
|
8
|
+
/**
|
|
9
|
+
* Generic reactive selector subscribing to NavCore state updates.
|
|
10
|
+
* Only triggers re-renders when the selected value actually changes.
|
|
11
|
+
*/
|
|
12
|
+
export declare function useNavSelector<T>(selector: (state: NavCoreState) => T, customEngine?: NavCore, isEqual?: (a: T, b: T) => boolean): T;
|
|
13
|
+
/**
|
|
14
|
+
* Hook to select the current upcoming turn-by-turn navigation instruction.
|
|
15
|
+
* Only re-renders when the instruction changes or triggers.
|
|
16
|
+
*/
|
|
17
|
+
export declare function useNavInstruction(customEngine?: NavCore): NavInstruction | null;
|
|
18
|
+
/**
|
|
19
|
+
* Hook to select current alert/deviation state ('NONE' | 'DEVIATING' | 'RECALCULATING' | 'REJOINING' | 'REJOINED').
|
|
20
|
+
* Only re-renders on alert state transitions.
|
|
21
|
+
*/
|
|
22
|
+
export declare function useNavAlert(customEngine?: NavCore): NavCoreState['alertState'];
|
|
23
|
+
export interface NavProgressInfo {
|
|
24
|
+
distanceToDestination: number | null;
|
|
25
|
+
distanceToNextInstruction: number | null;
|
|
26
|
+
progressIndex: number;
|
|
27
|
+
routeIndex: number;
|
|
28
|
+
hasArrived: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Hook to select navigation progress and remaining distance metrics.
|
|
32
|
+
*/
|
|
33
|
+
export declare function useNavProgress(customEngine?: NavCore): NavProgressInfo;
|
|
34
|
+
/**
|
|
35
|
+
* Hook to select whether the vehicle is currently off-route.
|
|
36
|
+
*/
|
|
37
|
+
export declare function useNavIsOffRoute(customEngine?: NavCore): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Hook to select current speed in m/s.
|
|
40
|
+
*/
|
|
41
|
+
export declare function useNavSpeed(customEngine?: NavCore): number;
|
|
42
|
+
//# sourceMappingURL=useNavSelectors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useNavSelectors.d.ts","sourceRoot":"","sources":["../../../src/useNavSelectors.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGnF;;;GAGG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,QAAQ,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,CAAC,EACpC,YAAY,CAAC,EAAE,OAAO,EACtB,OAAO,GAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,OAAmB,GAC3C,CAAC,CAsCH;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,cAAc,GAAG,IAAI,CAe/E;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,YAAY,CAAC,YAAY,CAAC,CAM9E;AAED,MAAM,WAAW,eAAe;IAC9B,qBAAqB,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,yBAAyB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,eAAe,CAiBtE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,OAAO,CAMhE;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,YAAY,CAAC,EAAE,OAAO,GAAG,MAAM,CAM1D"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Granular selector hooks for NavCore.
|
|
3
|
+
*
|
|
4
|
+
* Built with React 18 `useSyncExternalStore` for flicker-free, tearing-free
|
|
5
|
+
* state subscriptions. Components only re-render when the selected slice changes.
|
|
6
|
+
*/
|
|
7
|
+
import { useSyncExternalStore, useRef, useCallback } from 'react';
|
|
8
|
+
import { useNavCoreContext } from './NavCoreProvider';
|
|
9
|
+
/**
|
|
10
|
+
* Generic reactive selector subscribing to NavCore state updates.
|
|
11
|
+
* Only triggers re-renders when the selected value actually changes.
|
|
12
|
+
*/
|
|
13
|
+
export function useNavSelector(selector, customEngine, isEqual = Object.is) {
|
|
14
|
+
let contextEngine;
|
|
15
|
+
try {
|
|
16
|
+
contextEngine = useNavCoreContext().engine;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
contextEngine = undefined;
|
|
20
|
+
}
|
|
21
|
+
const engine = customEngine ?? contextEngine;
|
|
22
|
+
if (!engine) {
|
|
23
|
+
throw new Error('useNavSelector must be provided an engine or used within a NavCoreProvider');
|
|
24
|
+
}
|
|
25
|
+
const lastSelectedRef = useRef(undefined);
|
|
26
|
+
const lastStateRef = useRef(undefined);
|
|
27
|
+
const getSnapshot = useCallback(() => {
|
|
28
|
+
const currentState = engine.getState();
|
|
29
|
+
if (lastStateRef.current === currentState && lastSelectedRef.current !== undefined) {
|
|
30
|
+
return lastSelectedRef.current;
|
|
31
|
+
}
|
|
32
|
+
const nextSelected = selector(currentState);
|
|
33
|
+
if (lastSelectedRef.current !== undefined && isEqual(lastSelectedRef.current, nextSelected)) {
|
|
34
|
+
return lastSelectedRef.current;
|
|
35
|
+
}
|
|
36
|
+
lastStateRef.current = currentState;
|
|
37
|
+
lastSelectedRef.current = nextSelected;
|
|
38
|
+
return nextSelected;
|
|
39
|
+
}, [engine, selector, isEqual]);
|
|
40
|
+
const subscribe = useCallback((onStoreChange) => {
|
|
41
|
+
engine.on('update', onStoreChange);
|
|
42
|
+
return () => {
|
|
43
|
+
engine.off('update', onStoreChange);
|
|
44
|
+
};
|
|
45
|
+
}, [engine]);
|
|
46
|
+
return useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Hook to select the current upcoming turn-by-turn navigation instruction.
|
|
50
|
+
* Only re-renders when the instruction changes or triggers.
|
|
51
|
+
*/
|
|
52
|
+
export function useNavInstruction(customEngine) {
|
|
53
|
+
return useNavSelector((state) => {
|
|
54
|
+
if (state.nextInstructionIndex === null)
|
|
55
|
+
return null;
|
|
56
|
+
let engine = customEngine;
|
|
57
|
+
if (!engine) {
|
|
58
|
+
try {
|
|
59
|
+
engine = useNavCoreContext().engine;
|
|
60
|
+
}
|
|
61
|
+
catch { /* ignore */ }
|
|
62
|
+
}
|
|
63
|
+
if (!engine)
|
|
64
|
+
return null;
|
|
65
|
+
const list = engine.getInstructions();
|
|
66
|
+
return list[state.nextInstructionIndex] ?? null;
|
|
67
|
+
}, customEngine, (a, b) => a === b || (a?.geometryIndex === b?.geometryIndex && a?.instruction === b?.instruction));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Hook to select current alert/deviation state ('NONE' | 'DEVIATING' | 'RECALCULATING' | 'REJOINING' | 'REJOINED').
|
|
71
|
+
* Only re-renders on alert state transitions.
|
|
72
|
+
*/
|
|
73
|
+
export function useNavAlert(customEngine) {
|
|
74
|
+
return useNavSelector((state) => state.alertState, customEngine, Object.is);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Hook to select navigation progress and remaining distance metrics.
|
|
78
|
+
*/
|
|
79
|
+
export function useNavProgress(customEngine) {
|
|
80
|
+
return useNavSelector((state) => ({
|
|
81
|
+
distanceToDestination: state.distanceToDestination,
|
|
82
|
+
distanceToNextInstruction: state.distanceToNextInstruction,
|
|
83
|
+
progressIndex: state.progressIndex,
|
|
84
|
+
routeIndex: state.routeIndex,
|
|
85
|
+
hasArrived: state.hasArrived,
|
|
86
|
+
}), customEngine, (a, b) => a.distanceToDestination === b.distanceToDestination &&
|
|
87
|
+
a.distanceToNextInstruction === b.distanceToNextInstruction &&
|
|
88
|
+
a.progressIndex === b.progressIndex &&
|
|
89
|
+
a.routeIndex === b.routeIndex &&
|
|
90
|
+
a.hasArrived === b.hasArrived);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Hook to select whether the vehicle is currently off-route.
|
|
94
|
+
*/
|
|
95
|
+
export function useNavIsOffRoute(customEngine) {
|
|
96
|
+
return useNavSelector((state) => state.isOffRoute, customEngine, Object.is);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Hook to select current speed in m/s.
|
|
100
|
+
*/
|
|
101
|
+
export function useNavSpeed(customEngine) {
|
|
102
|
+
return useNavSelector((state) => state.currentSpeed, customEngine, Object.is);
|
|
103
|
+
}
|