react-native-nitro-compass 1.1.0 → 1.2.1

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 (47) hide show
  1. package/README.md +381 -163
  2. package/android/src/main/java/com/margelo/nitro/nitrocompass/HybridNitroCompass.kt +654 -133
  3. package/ios/HybridNitroCompass.swift +106 -3
  4. package/lib/commonjs/hook.js +98 -11
  5. package/lib/commonjs/hook.js.map +1 -1
  6. package/lib/commonjs/index.js.map +1 -1
  7. package/lib/commonjs/multiplex.js +23 -2
  8. package/lib/commonjs/multiplex.js.map +1 -1
  9. package/lib/module/hook.js +99 -12
  10. package/lib/module/hook.js.map +1 -1
  11. package/lib/module/index.js.map +1 -1
  12. package/lib/module/multiplex.js +23 -2
  13. package/lib/module/multiplex.js.map +1 -1
  14. package/lib/typescript/src/hook.d.ts +39 -1
  15. package/lib/typescript/src/hook.d.ts.map +1 -1
  16. package/lib/typescript/src/index.d.ts +2 -2
  17. package/lib/typescript/src/index.d.ts.map +1 -1
  18. package/lib/typescript/src/multiplex.d.ts.map +1 -1
  19. package/lib/typescript/src/specs/NitroCompass.nitro.d.ts +142 -18
  20. package/lib/typescript/src/specs/NitroCompass.nitro.d.ts.map +1 -1
  21. package/nitrogen/generated/android/c++/JCompassSample.hpp +7 -3
  22. package/nitrogen/generated/android/c++/JDebugInfo.hpp +85 -0
  23. package/nitrogen/generated/android/c++/JHybridNitroCompassSpec.cpp +17 -0
  24. package/nitrogen/generated/android/c++/JHybridNitroCompassSpec.hpp +3 -0
  25. package/nitrogen/generated/android/c++/JSensorKind.hpp +6 -3
  26. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrocompass/CompassSample.kt +9 -4
  27. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrocompass/DebugInfo.kt +86 -0
  28. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrocompass/HybridNitroCompassSpec.kt +12 -0
  29. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrocompass/SensorKind.kt +4 -3
  30. package/nitrogen/generated/ios/NitroCompass-Swift-Cxx-Bridge.hpp +12 -0
  31. package/nitrogen/generated/ios/NitroCompass-Swift-Cxx-Umbrella.hpp +3 -0
  32. package/nitrogen/generated/ios/c++/HybridNitroCompassSpecSwift.hpp +23 -0
  33. package/nitrogen/generated/ios/swift/CompassSample.swift +7 -2
  34. package/nitrogen/generated/ios/swift/DebugInfo.swift +64 -0
  35. package/nitrogen/generated/ios/swift/HybridNitroCompassSpec.swift +3 -0
  36. package/nitrogen/generated/ios/swift/HybridNitroCompassSpec_cxx.swift +34 -0
  37. package/nitrogen/generated/ios/swift/SensorKind.swift +8 -4
  38. package/nitrogen/generated/shared/c++/CompassSample.hpp +6 -2
  39. package/nitrogen/generated/shared/c++/DebugInfo.hpp +111 -0
  40. package/nitrogen/generated/shared/c++/HybridNitroCompassSpec.cpp +3 -0
  41. package/nitrogen/generated/shared/c++/HybridNitroCompassSpec.hpp +6 -0
  42. package/nitrogen/generated/shared/c++/SensorKind.hpp +10 -6
  43. package/package.json +2 -2
  44. package/src/hook.ts +146 -12
  45. package/src/index.ts +2 -0
  46. package/src/multiplex.ts +23 -2
  47. package/src/specs/NitroCompass.nitro.ts +147 -18
@@ -43,6 +43,18 @@ class HybridNitroCompass: HybridNitroCompassSpec {
43
43
  return q
44
44
  }()
45
45
  private var lastInterference: Bool?
46
+ // Most recent calibrated magnetic-field magnitude (µT). Surfaced on
47
+ // every CompassSample so consumers can render a strength meter; -1
48
+ // until CMDeviceMotion produces its first calibrated reading.
49
+ private var lastFieldUt: Double = -1
50
+ // Counter of consecutive non-uncalibrated motion samples. CoreMotion's
51
+ // bias estimate takes a second or two of normal motion to converge
52
+ // after subscribe; emitting interference transitions before then
53
+ // produces false positives because the early "calibrated" field is
54
+ // dominated by the device's own internal bias. Wait for N stable
55
+ // samples before trusting the magnitude reading.
56
+ private var motionStableSampleCount: Int = 0
57
+ private static let motionStableSampleThreshold: Int = 5
46
58
  private var pauseOnBackground: Bool = true
47
59
  private var started: Bool = false
48
60
  private var isSubscribed: Bool = false
@@ -59,7 +71,10 @@ class HybridNitroCompass: HybridNitroCompassSpec {
59
71
  super.init()
60
72
  let setup = {
61
73
  let m = CLLocationManager()
62
- m.headingFilter = 1
74
+ // headingFilter is overwritten by subscribe() each start(); the
75
+ // init value is only relevant if a caller queried headingFilter
76
+ // before start, which nothing does. Pick the default that matches
77
+ // activeFilterDegrees so a stray query is consistent.
63
78
  m.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
64
79
  self.manager = m
65
80
  }
@@ -193,6 +208,25 @@ class HybridNitroCompass: HybridNitroCompassSpec {
193
208
  return SensorDiagnostics(sensor: .corelocation)
194
209
  }
195
210
 
211
+ // iOS lets CLLocationManager handle the gyro+mag fusion + bias
212
+ // estimation internally, so most of the Android-side debug fields
213
+ // have no analogue on this side. We surface the two we *can*
214
+ // observe (current interference state, last field strength) and
215
+ // report sentinel "n/a" values for the rest, matching the iOS
216
+ // notes in the DebugInfo doc.
217
+ func getDebugInfo() throws -> DebugInfo {
218
+ return DebugInfo(
219
+ interferenceActive: lastInterference == true,
220
+ msSinceLastBiasJump: -1,
221
+ expectedFieldMicroTesla: -1,
222
+ lastFieldMicroTesla: lastFieldUt,
223
+ fusedYawDeg: Double.nan,
224
+ lastYawRateDegPerS: 0,
225
+ hasGameRotationVector: false,
226
+ usingUncalibratedMag: false
227
+ )
228
+ }
229
+
196
230
  func getCurrentHeading() throws -> CompassSample? {
197
231
  return lastSample
198
232
  }
@@ -201,8 +235,18 @@ class HybridNitroCompass: HybridNitroCompassSpec {
201
235
  declinationDeg = degrees
202
236
  }
203
237
 
238
+ // No-op on iOS — `CLLocationManager` already uses GPS-derived location
239
+ // internally for all field-related reasoning (true-north, declination,
240
+ // expected field strength). Layering our own lookup on top would be
241
+ // redundant. Provided for cross-platform JS callers.
242
+ func setLocation(latitude: Double, longitude: Double) throws {}
243
+
204
244
  func setOnCalibrationNeeded(onChange: @escaping (_ quality: AccuracyQuality) -> Void) throws {
205
245
  calibrationCb = onChange
246
+ // Replay current bucket so a late-registering consumer sees the
247
+ // truth instead of waiting for the next transition. Mirrors the
248
+ // setOnInterferenceDetected behavior.
249
+ if let last = lastQuality { onChange(last) }
206
250
  }
207
251
 
208
252
  func setOnInterferenceDetected(onChange: @escaping (_ interferenceDetected: Bool) -> Void) throws {
@@ -222,6 +266,30 @@ class HybridNitroCompass: HybridNitroCompassSpec {
222
266
  }
223
267
  }
224
268
 
269
+ func recalibrate() throws {
270
+ // Stop/start the heading subscription and reset every cached
271
+ // signal. Apple's CLLocationManager handles soft/hard-iron
272
+ // calibration internally — the recovery itself still requires the
273
+ // user to move the device through varying orientations — but
274
+ // dropping our own cached state ensures the next callback isn't
275
+ // suppressed by stale `lastQuality` / `lastInterference`
276
+ // comparisons, and dismisses any pending heading-calibration
277
+ // overlay so the consumer can prompt fresh.
278
+ guard started else { return }
279
+ // dismissHeadingCalibrationDisplay touches UIKit-adjacent state and
280
+ // must run on main. recalibrate() may be called from the JS thread.
281
+ DispatchQueue.main.async { [weak self] in
282
+ self?.manager.dismissHeadingCalibrationDisplay()
283
+ }
284
+ let wasSubscribed = isSubscribed
285
+ if wasSubscribed { unsubscribe() }
286
+ lastSample = nil
287
+ lastQuality = nil
288
+ lastInterference = nil
289
+ lastFieldUt = -1
290
+ if wasSubscribed { subscribe() }
291
+ }
292
+
225
293
  func getPermissionStatus() throws -> PermissionStatus {
226
294
  return Self.mapAuthStatus(manager.authorizationStatus)
227
295
  }
@@ -296,12 +364,28 @@ class HybridNitroCompass: HybridNitroCompassSpec {
296
364
  guard motionManager.isDeviceMotionAvailable,
297
365
  !motionManager.isDeviceMotionActive else { return }
298
366
  motionManager.deviceMotionUpdateInterval = 0.2 // 5Hz
367
+ motionStableSampleCount = 0
299
368
  motionManager.startDeviceMotionUpdates(to: motionQueue) { [weak self] motion, _ in
300
369
  guard let self = self, let m = motion else { return }
301
370
  let cal = m.magneticField
302
- if cal.accuracy == .uncalibrated { return }
371
+ if cal.accuracy == .uncalibrated {
372
+ // Bias estimate not yet usable. Reset stability counter so the
373
+ // post-convergence count starts fresh.
374
+ self.motionStableSampleCount = 0
375
+ return
376
+ }
303
377
  let f = cal.field
304
378
  let magnitude = sqrt(f.x * f.x + f.y * f.y + f.z * f.z)
379
+ self.lastFieldUt = magnitude
380
+
381
+ // Wait for N consecutive non-uncalibrated samples before letting
382
+ // magnitude drive interference transitions. Without this, the
383
+ // first second post-subscribe routinely fires false positives
384
+ // because the bias estimate hasn't converged yet.
385
+ if self.motionStableSampleCount < Self.motionStableSampleThreshold {
386
+ self.motionStableSampleCount += 1
387
+ return
388
+ }
305
389
  self.evaluateInterference(magnitude: magnitude)
306
390
  }
307
391
  }
@@ -310,6 +394,7 @@ class HybridNitroCompass: HybridNitroCompassSpec {
310
394
  if motionManager.isDeviceMotionActive {
311
395
  motionManager.stopDeviceMotionUpdates()
312
396
  }
397
+ motionStableSampleCount = 0
313
398
  }
314
399
 
315
400
  private func evaluateInterference(magnitude: Double) {
@@ -359,13 +444,18 @@ class HybridNitroCompass: HybridNitroCompassSpec {
359
444
  lastSample = nil
360
445
  lastQuality = nil
361
446
  lastInterference = nil
447
+ lastFieldUt = -1
362
448
  }
363
449
 
364
450
  private func deliver(heading magnetic: Double, accuracy: Double) {
365
451
  var heading = magnetic + declinationDeg
366
452
  heading = heading.truncatingRemainder(dividingBy: 360)
367
453
  if heading < 0 { heading += 360 }
368
- let sample = CompassSample(heading: heading, accuracy: accuracy)
454
+ let sample = CompassSample(
455
+ heading: heading,
456
+ accuracy: accuracy,
457
+ fieldStrengthMicroTesla: lastFieldUt
458
+ )
369
459
  lastSample = sample
370
460
 
371
461
  // CLLocationManager.headingAccuracy is conservative — even a
@@ -397,6 +487,19 @@ class HybridNitroCompass: HybridNitroCompassSpec {
397
487
  }
398
488
 
399
489
  private func applyHeadingOrientation() {
490
+ // Prefer device orientation for face-up / face-down — those don't
491
+ // appear in interfaceOrientation but matter for headings on tablets
492
+ // held flat. Fall back to interfaceOrientation for the standard
493
+ // four cases (which respects app rotation locks).
494
+ let device = UIDevice.current.orientation
495
+ if device == .faceUp {
496
+ manager.headingOrientation = .faceUp
497
+ return
498
+ }
499
+ if device == .faceDown {
500
+ manager.headingOrientation = .faceDown
501
+ return
502
+ }
400
503
  let scene = UIApplication.shared.connectedScenes
401
504
  .compactMap { $0 as? UIWindowScene }
402
505
  .first
@@ -25,14 +25,40 @@ function useCompass(options = {}) {
25
25
  const [reading, setReading] = (0, _react.useState)(null);
26
26
  const [quality, setQuality] = (0, _react.useState)(null);
27
27
  const [interfering, setInterfering] = (0, _react.useState)(false);
28
- const [hasCompass] = (0, _react.useState)(() => _native.NitroCompass.hasCompass());
29
- const [diagnostics] = (0, _react.useState)(() => _native.NitroCompass.getDiagnostics());
30
28
 
31
- // Tracked via ref so the heading-subscription effect can re-apply
32
- // the user's filter after a stop/start cycle without restarting on
33
- // every filterDegrees change.
29
+ // Wrap in try/catch so a missing/misconfigured native module doesn't
30
+ // throw during render return safe defaults and let the host UI
31
+ // surface "no compass". Without this, the throw bubbles up and
32
+ // becomes a render error that blanks the screen.
33
+ const [hasCompass] = (0, _react.useState)(() => {
34
+ try {
35
+ return _native.NitroCompass.hasCompass();
36
+ } catch {
37
+ return false;
38
+ }
39
+ });
40
+ const [diagnostics] = (0, _react.useState)(() => {
41
+ try {
42
+ return _native.NitroCompass.getDiagnostics();
43
+ } catch {
44
+ return undefined;
45
+ }
46
+ });
47
+ const [permission, setPermission] = (0, _react.useState)(() => {
48
+ try {
49
+ return _native.NitroCompass.getPermissionStatus();
50
+ } catch {
51
+ return 'unknown';
52
+ }
53
+ });
54
+
55
+ // Tracked via refs so the heading-subscription effect can re-apply
56
+ // the user's filter and smoothing after a stop/start cycle without
57
+ // restarting on every option change.
34
58
  const filterRef = (0, _react.useRef)(filterDegrees);
35
59
  filterRef.current = filterDegrees;
60
+ const smoothingRef = (0, _react.useRef)(smoothingAlpha);
61
+ smoothingRef.current = smoothingAlpha;
36
62
  (0, _react.useEffect)(() => {
37
63
  _native.NitroCompass.setFilter(filterDegrees);
38
64
  }, [filterDegrees]);
@@ -54,20 +80,81 @@ function useCompass(options = {}) {
54
80
  return (0, _multiplex.addInterferenceListener)(setInterfering);
55
81
  }, [hasCompass]);
56
82
  (0, _react.useEffect)(() => {
57
- if (!hasCompass || !enabled) return;
58
- const off = (0, _multiplex.addHeadingListener)(setReading);
59
- // Multiplex starts the sensor with a default filter; re-apply the
60
- // current option after subscribing.
61
- _native.NitroCompass.setFilter(filterRef.current);
83
+ if (!hasCompass || !enabled) {
84
+ // When the user disables the hook, clear stale UI state.
85
+ // Without this, `reading` / `interfering` keep their last value
86
+ // forever, so the consumer's UI can't tell "subscription is off"
87
+ // from "compass is currently quiet".
88
+ setReading(null);
89
+ setInterfering(false);
90
+ return;
91
+ }
92
+ let off;
93
+ try {
94
+ off = (0, _multiplex.addHeadingListener)(setReading);
95
+ // Multiplex starts the sensor with a default filter; re-apply
96
+ // the current options after subscribing. recalibrate() can also
97
+ // reset native smoothing state, so we pin our chosen alpha back
98
+ // here as well.
99
+ _native.NitroCompass.setFilter(filterRef.current);
100
+ _native.NitroCompass.setSmoothing(smoothingRef.current);
101
+ } catch (e) {
102
+ // start() throws on iOS when location authorization is denied.
103
+ // Swallow it here so the hook tree doesn't unmount; consumers
104
+ // should call NitroCompass.requestPermission() explicitly before
105
+ // setting enabled=true if they want to recover.
106
+ // eslint-disable-next-line no-console
107
+ console.warn('[NitroCompass] failed to start heading subscription:', e);
108
+ }
62
109
  return off;
63
110
  // eslint-disable-next-line react-hooks/exhaustive-deps
64
111
  }, [hasCompass, enabled]);
112
+
113
+ // Stable callbacks so consumers' useEffect deps don't churn on every
114
+ // render. All four are thin wrappers around NitroCompass; the hook's
115
+ // own state isn't reactive to recalibrate/setLocation since neither
116
+ // changes any field returned here.
117
+ const getCurrentHeading = (0, _react.useCallback)(() => {
118
+ try {
119
+ return _native.NitroCompass.getCurrentHeading();
120
+ } catch {
121
+ return undefined;
122
+ }
123
+ }, []);
124
+ const recalibrate = (0, _react.useCallback)(() => {
125
+ try {
126
+ _native.NitroCompass.recalibrate();
127
+ } catch {
128
+ // Native missing — nothing to do.
129
+ }
130
+ }, []);
131
+ const setLocation = (0, _react.useCallback)((latitude, longitude) => {
132
+ try {
133
+ _native.NitroCompass.setLocation(latitude, longitude);
134
+ } catch {
135
+ // Native missing — silently ignored.
136
+ }
137
+ }, []);
138
+ const requestPermission = (0, _react.useCallback)(async () => {
139
+ try {
140
+ const status = await _native.NitroCompass.requestPermission();
141
+ setPermission(status);
142
+ return status;
143
+ } catch {
144
+ return 'denied';
145
+ }
146
+ }, []);
65
147
  return {
66
148
  reading,
67
149
  quality,
68
150
  interfering,
69
151
  hasCompass,
70
- diagnostics
152
+ diagnostics,
153
+ permission,
154
+ getCurrentHeading,
155
+ recalibrate,
156
+ setLocation,
157
+ requestPermission
71
158
  };
72
159
  }
73
160
  //# sourceMappingURL=hook.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["_react","require","_native","_multiplex","useCompass","options","filterDegrees","smoothingAlpha","declination","pauseOnBackground","enabled","reading","setReading","useState","quality","setQuality","interfering","setInterfering","hasCompass","NitroCompass","diagnostics","getDiagnostics","filterRef","useRef","current","useEffect","setFilter","setSmoothing","setDeclination","setPauseOnBackground","addCalibrationListener","addInterferenceListener","off","addHeadingListener"],"sourceRoot":"../../src","sources":["hook.ts"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,OAAA;AAMA,IAAAC,OAAA,GAAAD,OAAA;AACA,IAAAE,UAAA,GAAAF,OAAA;AAyDA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASG,UAAUA,CACxBC,OAA0B,GAAG,CAAC,CAAC,EACb;EAClB,MAAM;IACJC,aAAa,GAAG,CAAC;IACjBC,cAAc,GAAG,GAAG;IACpBC,WAAW,GAAG,CAAC;IACfC,iBAAiB,GAAG,IAAI;IACxBC,OAAO,GAAG;EACZ,CAAC,GAAGL,OAAO;EAEX,MAAM,CAACM,OAAO,EAAEC,UAAU,CAAC,GAAG,IAAAC,eAAQ,EAAuB,IAAI,CAAC;EAClE,MAAM,CAACC,OAAO,EAAEC,UAAU,CAAC,GAAG,IAAAF,eAAQ,EAAyB,IAAI,CAAC;EACpE,MAAM,CAACG,WAAW,EAAEC,cAAc,CAAC,GAAG,IAAAJ,eAAQ,EAAC,KAAK,CAAC;EAErD,MAAM,CAACK,UAAU,CAAC,GAAG,IAAAL,eAAQ,EAAC,MAAMM,oBAAY,CAACD,UAAU,CAAC,CAAC,CAAC;EAC9D,MAAM,CAACE,WAAW,CAAC,GAAG,IAAAP,eAAQ,EAAC,MAAMM,oBAAY,CAACE,cAAc,CAAC,CAAC,CAAC;;EAEnE;EACA;EACA;EACA,MAAMC,SAAS,GAAG,IAAAC,aAAM,EAACjB,aAAa,CAAC;EACvCgB,SAAS,CAACE,OAAO,GAAGlB,aAAa;EAEjC,IAAAmB,gBAAS,EAAC,MAAM;IACdN,oBAAY,CAACO,SAAS,CAACpB,aAAa,CAAC;EACvC,CAAC,EAAE,CAACA,aAAa,CAAC,CAAC;EAEnB,IAAAmB,gBAAS,EAAC,MAAM;IACdN,oBAAY,CAACQ,YAAY,CAACpB,cAAc,CAAC;EAC3C,CAAC,EAAE,CAACA,cAAc,CAAC,CAAC;EAEpB,IAAAkB,gBAAS,EAAC,MAAM;IACdN,oBAAY,CAACS,cAAc,CAACpB,WAAW,CAAC;EAC1C,CAAC,EAAE,CAACA,WAAW,CAAC,CAAC;EAEjB,IAAAiB,gBAAS,EAAC,MAAM;IACdN,oBAAY,CAACU,oBAAoB,CAACpB,iBAAiB,CAAC;EACtD,CAAC,EAAE,CAACA,iBAAiB,CAAC,CAAC;EAEvB,IAAAgB,gBAAS,EAAC,MAAM;IACd,IAAI,CAACP,UAAU,EAAE;IACjB,OAAO,IAAAY,iCAAsB,EAACf,UAAU,CAAC;EAC3C,CAAC,EAAE,CAACG,UAAU,CAAC,CAAC;EAEhB,IAAAO,gBAAS,EAAC,MAAM;IACd,IAAI,CAACP,UAAU,EAAE;IACjB,OAAO,IAAAa,kCAAuB,EAACd,cAAc,CAAC;EAChD,CAAC,EAAE,CAACC,UAAU,CAAC,CAAC;EAEhB,IAAAO,gBAAS,EAAC,MAAM;IACd,IAAI,CAACP,UAAU,IAAI,CAACR,OAAO,EAAE;IAC7B,MAAMsB,GAAG,GAAG,IAAAC,6BAAkB,EAACrB,UAAU,CAAC;IAC1C;IACA;IACAO,oBAAY,CAACO,SAAS,CAACJ,SAAS,CAACE,OAAO,CAAC;IACzC,OAAOQ,GAAG;IACV;EACF,CAAC,EAAE,CAACd,UAAU,EAAER,OAAO,CAAC,CAAC;EAEzB,OAAO;IAAEC,OAAO;IAAEG,OAAO;IAAEE,WAAW;IAAEE,UAAU;IAAEE;EAAY,CAAC;AACnE","ignoreList":[]}
1
+ {"version":3,"names":["_react","require","_native","_multiplex","useCompass","options","filterDegrees","smoothingAlpha","declination","pauseOnBackground","enabled","reading","setReading","useState","quality","setQuality","interfering","setInterfering","hasCompass","NitroCompass","diagnostics","getDiagnostics","undefined","permission","setPermission","getPermissionStatus","filterRef","useRef","current","smoothingRef","useEffect","setFilter","setSmoothing","setDeclination","setPauseOnBackground","addCalibrationListener","addInterferenceListener","off","addHeadingListener","e","console","warn","getCurrentHeading","useCallback","recalibrate","setLocation","latitude","longitude","requestPermission","status"],"sourceRoot":"../../src","sources":["hook.ts"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":["_native","require","_multiplex","_hook"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AASA,IAAAA,OAAA,GAAAC,OAAA;AAWA,IAAAC,UAAA,GAAAD,OAAA;AAMA,IAAAE,KAAA,GAAAF,OAAA","ignoreList":[]}
1
+ {"version":3,"names":["_native","require","_multiplex","_hook"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAUA,IAAAA,OAAA,GAAAC,OAAA;AAYA,IAAAC,UAAA,GAAAD,OAAA;AAMA,IAAAE,KAAA,GAAAF,OAAA","ignoreList":[]}
@@ -77,6 +77,13 @@ function addHeadingListener(cb) {
77
77
  };
78
78
  }
79
79
 
80
+ // Module-level no-op kept stable so we can swap it back into the native
81
+ // callback slot when the last listener leaves — releasing references
82
+ // to old dispatcher closures, which matters when the JS module is
83
+ // re-evaluated (Metro Fast Refresh, jest module reset).
84
+ const NOOP_CALIBRATION = _ => {};
85
+ const NOOP_INTERFERENCE = _ => {};
86
+
80
87
  /**
81
88
  * Subscribe to calibration-bucket transitions. Only fires while a
82
89
  * heading subscription is active. Returns the unsubscribe function.
@@ -88,7 +95,17 @@ function addCalibrationListener(cb) {
88
95
  }
89
96
  calibrationListeners.add(cb);
90
97
  return () => {
91
- calibrationListeners.delete(cb);
98
+ if (!calibrationListeners.delete(cb)) return;
99
+ if (calibrationListeners.size === 0) {
100
+ // Detach our dispatcher from the native side. Without this, a
101
+ // module reload (Metro Fast Refresh, jest resetModules) leaves
102
+ // the old dispatcher pinned in native memory while a new module
103
+ // load installs a *second* dispatcher pointing at a fresh
104
+ // listener Set — splitting events between the two and silently
105
+ // dropping the now-orphaned listeners.
106
+ _native.NitroCompass.setOnCalibrationNeeded(NOOP_CALIBRATION);
107
+ calibrationRegistered = false;
108
+ }
92
109
  };
93
110
  }
94
111
 
@@ -103,7 +120,11 @@ function addInterferenceListener(cb) {
103
120
  }
104
121
  interferenceListeners.add(cb);
105
122
  return () => {
106
- interferenceListeners.delete(cb);
123
+ if (!interferenceListeners.delete(cb)) return;
124
+ if (interferenceListeners.size === 0) {
125
+ _native.NitroCompass.setOnInterferenceDetected(NOOP_INTERFERENCE);
126
+ interferenceRegistered = false;
127
+ }
107
128
  };
108
129
  }
109
130
  //# sourceMappingURL=multiplex.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["_native","require","headingListeners","Set","calibrationListeners","interferenceListeners","calibrationRegistered","interferenceRegistered","DEFAULT_FILTER_DEG","dispatchHeading","sample","cb","Array","from","e","console","error","dispatchCalibration","quality","dispatchInterference","detected","addHeadingListener","wasEmpty","size","add","NitroCompass","start","delete","stop","addCalibrationListener","setOnCalibrationNeeded","addInterferenceListener","setOnInterferenceDetected"],"sourceRoot":"../../src","sources":["multiplex.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["_native","require","headingListeners","Set","calibrationListeners","interferenceListeners","calibrationRegistered","interferenceRegistered","DEFAULT_FILTER_DEG","dispatchHeading","sample","cb","Array","from","e","console","error","dispatchCalibration","quality","dispatchInterference","detected","addHeadingListener","wasEmpty","size","add","NitroCompass","start","delete","stop","NOOP_CALIBRATION","_","NOOP_INTERFERENCE","addCalibrationListener","setOnCalibrationNeeded","addInterferenceListener","setOnInterferenceDetected"],"sourceRoot":"../../src","sources":["multiplex.ts"],"mappings":"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","ignoreList":[]}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- import { useEffect, useRef, useState } from 'react';
3
+ import { useCallback, useEffect, useRef, useState } from 'react';
4
4
  import { NitroCompass } from './native';
5
5
  import { addCalibrationListener, addHeadingListener, addInterferenceListener } from './multiplex';
6
6
  /**
@@ -21,14 +21,40 @@ export function useCompass(options = {}) {
21
21
  const [reading, setReading] = useState(null);
22
22
  const [quality, setQuality] = useState(null);
23
23
  const [interfering, setInterfering] = useState(false);
24
- const [hasCompass] = useState(() => NitroCompass.hasCompass());
25
- const [diagnostics] = useState(() => NitroCompass.getDiagnostics());
26
24
 
27
- // Tracked via ref so the heading-subscription effect can re-apply
28
- // the user's filter after a stop/start cycle without restarting on
29
- // every filterDegrees change.
25
+ // Wrap in try/catch so a missing/misconfigured native module doesn't
26
+ // throw during render return safe defaults and let the host UI
27
+ // surface "no compass". Without this, the throw bubbles up and
28
+ // becomes a render error that blanks the screen.
29
+ const [hasCompass] = useState(() => {
30
+ try {
31
+ return NitroCompass.hasCompass();
32
+ } catch {
33
+ return false;
34
+ }
35
+ });
36
+ const [diagnostics] = useState(() => {
37
+ try {
38
+ return NitroCompass.getDiagnostics();
39
+ } catch {
40
+ return undefined;
41
+ }
42
+ });
43
+ const [permission, setPermission] = useState(() => {
44
+ try {
45
+ return NitroCompass.getPermissionStatus();
46
+ } catch {
47
+ return 'unknown';
48
+ }
49
+ });
50
+
51
+ // Tracked via refs so the heading-subscription effect can re-apply
52
+ // the user's filter and smoothing after a stop/start cycle without
53
+ // restarting on every option change.
30
54
  const filterRef = useRef(filterDegrees);
31
55
  filterRef.current = filterDegrees;
56
+ const smoothingRef = useRef(smoothingAlpha);
57
+ smoothingRef.current = smoothingAlpha;
32
58
  useEffect(() => {
33
59
  NitroCompass.setFilter(filterDegrees);
34
60
  }, [filterDegrees]);
@@ -50,20 +76,81 @@ export function useCompass(options = {}) {
50
76
  return addInterferenceListener(setInterfering);
51
77
  }, [hasCompass]);
52
78
  useEffect(() => {
53
- if (!hasCompass || !enabled) return;
54
- const off = addHeadingListener(setReading);
55
- // Multiplex starts the sensor with a default filter; re-apply the
56
- // current option after subscribing.
57
- NitroCompass.setFilter(filterRef.current);
79
+ if (!hasCompass || !enabled) {
80
+ // When the user disables the hook, clear stale UI state.
81
+ // Without this, `reading` / `interfering` keep their last value
82
+ // forever, so the consumer's UI can't tell "subscription is off"
83
+ // from "compass is currently quiet".
84
+ setReading(null);
85
+ setInterfering(false);
86
+ return;
87
+ }
88
+ let off;
89
+ try {
90
+ off = addHeadingListener(setReading);
91
+ // Multiplex starts the sensor with a default filter; re-apply
92
+ // the current options after subscribing. recalibrate() can also
93
+ // reset native smoothing state, so we pin our chosen alpha back
94
+ // here as well.
95
+ NitroCompass.setFilter(filterRef.current);
96
+ NitroCompass.setSmoothing(smoothingRef.current);
97
+ } catch (e) {
98
+ // start() throws on iOS when location authorization is denied.
99
+ // Swallow it here so the hook tree doesn't unmount; consumers
100
+ // should call NitroCompass.requestPermission() explicitly before
101
+ // setting enabled=true if they want to recover.
102
+ // eslint-disable-next-line no-console
103
+ console.warn('[NitroCompass] failed to start heading subscription:', e);
104
+ }
58
105
  return off;
59
106
  // eslint-disable-next-line react-hooks/exhaustive-deps
60
107
  }, [hasCompass, enabled]);
108
+
109
+ // Stable callbacks so consumers' useEffect deps don't churn on every
110
+ // render. All four are thin wrappers around NitroCompass; the hook's
111
+ // own state isn't reactive to recalibrate/setLocation since neither
112
+ // changes any field returned here.
113
+ const getCurrentHeading = useCallback(() => {
114
+ try {
115
+ return NitroCompass.getCurrentHeading();
116
+ } catch {
117
+ return undefined;
118
+ }
119
+ }, []);
120
+ const recalibrate = useCallback(() => {
121
+ try {
122
+ NitroCompass.recalibrate();
123
+ } catch {
124
+ // Native missing — nothing to do.
125
+ }
126
+ }, []);
127
+ const setLocation = useCallback((latitude, longitude) => {
128
+ try {
129
+ NitroCompass.setLocation(latitude, longitude);
130
+ } catch {
131
+ // Native missing — silently ignored.
132
+ }
133
+ }, []);
134
+ const requestPermission = useCallback(async () => {
135
+ try {
136
+ const status = await NitroCompass.requestPermission();
137
+ setPermission(status);
138
+ return status;
139
+ } catch {
140
+ return 'denied';
141
+ }
142
+ }, []);
61
143
  return {
62
144
  reading,
63
145
  quality,
64
146
  interfering,
65
147
  hasCompass,
66
- diagnostics
148
+ diagnostics,
149
+ permission,
150
+ getCurrentHeading,
151
+ recalibrate,
152
+ setLocation,
153
+ requestPermission
67
154
  };
68
155
  }
69
156
  //# sourceMappingURL=hook.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["useEffect","useRef","useState","NitroCompass","addCalibrationListener","addHeadingListener","addInterferenceListener","useCompass","options","filterDegrees","smoothingAlpha","declination","pauseOnBackground","enabled","reading","setReading","quality","setQuality","interfering","setInterfering","hasCompass","diagnostics","getDiagnostics","filterRef","current","setFilter","setSmoothing","setDeclination","setPauseOnBackground","off"],"sourceRoot":"../../src","sources":["hook.ts"],"mappings":";;AAAA,SAASA,SAAS,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,OAAO;AAMnD,SAASC,YAAY,QAAQ,UAAU;AACvC,SACEC,sBAAsB,EACtBC,kBAAkB,EAClBC,uBAAuB,QAClB,aAAa;AAqDpB;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,UAAUA,CACxBC,OAA0B,GAAG,CAAC,CAAC,EACb;EAClB,MAAM;IACJC,aAAa,GAAG,CAAC;IACjBC,cAAc,GAAG,GAAG;IACpBC,WAAW,GAAG,CAAC;IACfC,iBAAiB,GAAG,IAAI;IACxBC,OAAO,GAAG;EACZ,CAAC,GAAGL,OAAO;EAEX,MAAM,CAACM,OAAO,EAAEC,UAAU,CAAC,GAAGb,QAAQ,CAAuB,IAAI,CAAC;EAClE,MAAM,CAACc,OAAO,EAAEC,UAAU,CAAC,GAAGf,QAAQ,CAAyB,IAAI,CAAC;EACpE,MAAM,CAACgB,WAAW,EAAEC,cAAc,CAAC,GAAGjB,QAAQ,CAAC,KAAK,CAAC;EAErD,MAAM,CAACkB,UAAU,CAAC,GAAGlB,QAAQ,CAAC,MAAMC,YAAY,CAACiB,UAAU,CAAC,CAAC,CAAC;EAC9D,MAAM,CAACC,WAAW,CAAC,GAAGnB,QAAQ,CAAC,MAAMC,YAAY,CAACmB,cAAc,CAAC,CAAC,CAAC;;EAEnE;EACA;EACA;EACA,MAAMC,SAAS,GAAGtB,MAAM,CAACQ,aAAa,CAAC;EACvCc,SAAS,CAACC,OAAO,GAAGf,aAAa;EAEjCT,SAAS,CAAC,MAAM;IACdG,YAAY,CAACsB,SAAS,CAAChB,aAAa,CAAC;EACvC,CAAC,EAAE,CAACA,aAAa,CAAC,CAAC;EAEnBT,SAAS,CAAC,MAAM;IACdG,YAAY,CAACuB,YAAY,CAAChB,cAAc,CAAC;EAC3C,CAAC,EAAE,CAACA,cAAc,CAAC,CAAC;EAEpBV,SAAS,CAAC,MAAM;IACdG,YAAY,CAACwB,cAAc,CAAChB,WAAW,CAAC;EAC1C,CAAC,EAAE,CAACA,WAAW,CAAC,CAAC;EAEjBX,SAAS,CAAC,MAAM;IACdG,YAAY,CAACyB,oBAAoB,CAAChB,iBAAiB,CAAC;EACtD,CAAC,EAAE,CAACA,iBAAiB,CAAC,CAAC;EAEvBZ,SAAS,CAAC,MAAM;IACd,IAAI,CAACoB,UAAU,EAAE;IACjB,OAAOhB,sBAAsB,CAACa,UAAU,CAAC;EAC3C,CAAC,EAAE,CAACG,UAAU,CAAC,CAAC;EAEhBpB,SAAS,CAAC,MAAM;IACd,IAAI,CAACoB,UAAU,EAAE;IACjB,OAAOd,uBAAuB,CAACa,cAAc,CAAC;EAChD,CAAC,EAAE,CAACC,UAAU,CAAC,CAAC;EAEhBpB,SAAS,CAAC,MAAM;IACd,IAAI,CAACoB,UAAU,IAAI,CAACP,OAAO,EAAE;IAC7B,MAAMgB,GAAG,GAAGxB,kBAAkB,CAACU,UAAU,CAAC;IAC1C;IACA;IACAZ,YAAY,CAACsB,SAAS,CAACF,SAAS,CAACC,OAAO,CAAC;IACzC,OAAOK,GAAG;IACV;EACF,CAAC,EAAE,CAACT,UAAU,EAAEP,OAAO,CAAC,CAAC;EAEzB,OAAO;IAAEC,OAAO;IAAEE,OAAO;IAAEE,WAAW;IAAEE,UAAU;IAAEC;EAAY,CAAC;AACnE","ignoreList":[]}
1
+ {"version":3,"names":["useCallback","useEffect","useRef","useState","NitroCompass","addCalibrationListener","addHeadingListener","addInterferenceListener","useCompass","options","filterDegrees","smoothingAlpha","declination","pauseOnBackground","enabled","reading","setReading","quality","setQuality","interfering","setInterfering","hasCompass","diagnostics","getDiagnostics","undefined","permission","setPermission","getPermissionStatus","filterRef","current","smoothingRef","setFilter","setSmoothing","setDeclination","setPauseOnBackground","off","e","console","warn","getCurrentHeading","recalibrate","setLocation","latitude","longitude","requestPermission","status"],"sourceRoot":"../../src","sources":["hook.ts"],"mappings":"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","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"names":["NitroCompass","addCalibrationListener","addHeadingListener","addInterferenceListener","useCompass"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AASA,SAASA,YAAY,QAAQ,UAAU;AAWvC,SACEC,sBAAsB,EACtBC,kBAAkB,EAClBC,uBAAuB,QAClB,aAAa;AAEpB,SAASC,UAAU,QAAQ,QAAQ","ignoreList":[]}
1
+ {"version":3,"names":["NitroCompass","addCalibrationListener","addHeadingListener","addInterferenceListener","useCompass"],"sourceRoot":"../../src","sources":["index.ts"],"mappings":";;AAUA,SAASA,YAAY,QAAQ,UAAU;AAYvC,SACEC,sBAAsB,EACtBC,kBAAkB,EAClBC,uBAAuB,QAClB,aAAa;AAEpB,SAASC,UAAU,QAAQ,QAAQ","ignoreList":[]}
@@ -71,6 +71,13 @@ export function addHeadingListener(cb) {
71
71
  };
72
72
  }
73
73
 
74
+ // Module-level no-op kept stable so we can swap it back into the native
75
+ // callback slot when the last listener leaves — releasing references
76
+ // to old dispatcher closures, which matters when the JS module is
77
+ // re-evaluated (Metro Fast Refresh, jest module reset).
78
+ const NOOP_CALIBRATION = _ => {};
79
+ const NOOP_INTERFERENCE = _ => {};
80
+
74
81
  /**
75
82
  * Subscribe to calibration-bucket transitions. Only fires while a
76
83
  * heading subscription is active. Returns the unsubscribe function.
@@ -82,7 +89,17 @@ export function addCalibrationListener(cb) {
82
89
  }
83
90
  calibrationListeners.add(cb);
84
91
  return () => {
85
- calibrationListeners.delete(cb);
92
+ if (!calibrationListeners.delete(cb)) return;
93
+ if (calibrationListeners.size === 0) {
94
+ // Detach our dispatcher from the native side. Without this, a
95
+ // module reload (Metro Fast Refresh, jest resetModules) leaves
96
+ // the old dispatcher pinned in native memory while a new module
97
+ // load installs a *second* dispatcher pointing at a fresh
98
+ // listener Set — splitting events between the two and silently
99
+ // dropping the now-orphaned listeners.
100
+ NitroCompass.setOnCalibrationNeeded(NOOP_CALIBRATION);
101
+ calibrationRegistered = false;
102
+ }
86
103
  };
87
104
  }
88
105
 
@@ -97,7 +114,11 @@ export function addInterferenceListener(cb) {
97
114
  }
98
115
  interferenceListeners.add(cb);
99
116
  return () => {
100
- interferenceListeners.delete(cb);
117
+ if (!interferenceListeners.delete(cb)) return;
118
+ if (interferenceListeners.size === 0) {
119
+ NitroCompass.setOnInterferenceDetected(NOOP_INTERFERENCE);
120
+ interferenceRegistered = false;
121
+ }
101
122
  };
102
123
  }
103
124
  //# sourceMappingURL=multiplex.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["NitroCompass","headingListeners","Set","calibrationListeners","interferenceListeners","calibrationRegistered","interferenceRegistered","DEFAULT_FILTER_DEG","dispatchHeading","sample","cb","Array","from","e","console","error","dispatchCalibration","quality","dispatchInterference","detected","addHeadingListener","wasEmpty","size","add","start","delete","stop","addCalibrationListener","setOnCalibrationNeeded","addInterferenceListener","setOnInterferenceDetected"],"sourceRoot":"../../src","sources":["multiplex.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["NitroCompass","headingListeners","Set","calibrationListeners","interferenceListeners","calibrationRegistered","interferenceRegistered","DEFAULT_FILTER_DEG","dispatchHeading","sample","cb","Array","from","e","console","error","dispatchCalibration","quality","dispatchInterference","detected","addHeadingListener","wasEmpty","size","add","start","delete","stop","NOOP_CALIBRATION","_","NOOP_INTERFERENCE","addCalibrationListener","setOnCalibrationNeeded","addInterferenceListener","setOnInterferenceDetected"],"sourceRoot":"../../src","sources":["multiplex.ts"],"mappings":"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","ignoreList":[]}