react-native-nitro-geolocation 0.1.2 → 0.3.0

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 (127) hide show
  1. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/GetCurrentPosition.kt +4 -4
  2. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/NitroGeolocation.kt +642 -50
  3. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/NitroGeolocationCompat.kt +81 -0
  4. package/android/src/main/java/com/margelo/nitro/nitrogeolocation/WatchPosition.kt +4 -4
  5. package/ios/LocationManager.swift +4 -4
  6. package/ios/NitroGeolocation.swift +543 -68
  7. package/ios/NitroGeolocationCompat.swift +109 -0
  8. package/nitrogen/generated/android/c++/JAuthorizationLevel.hpp +61 -0
  9. package/nitrogen/generated/android/c++/JAuthorizationLevelInternal.hpp +3 -4
  10. package/nitrogen/generated/android/c++/JFunc_void.hpp +2 -1
  11. package/nitrogen/generated/android/c++/JFunc_void_GeolocationError.hpp +2 -1
  12. package/nitrogen/generated/android/c++/JFunc_void_GeolocationResponse.hpp +6 -1
  13. package/nitrogen/generated/android/c++/JFunc_void_LocationError.hpp +78 -0
  14. package/nitrogen/generated/android/c++/JGeolocationCoordinates.hpp +25 -17
  15. package/nitrogen/generated/android/c++/JGeolocationError.hpp +5 -1
  16. package/nitrogen/generated/android/c++/JGeolocationOptions.hpp +5 -1
  17. package/nitrogen/generated/android/c++/JGeolocationResponse.hpp +9 -1
  18. package/nitrogen/generated/android/c++/JHybridNitroGeolocationCompatSpec.cpp +109 -0
  19. package/nitrogen/generated/android/c++/JHybridNitroGeolocationCompatSpec.hpp +70 -0
  20. package/nitrogen/generated/android/c++/JHybridNitroGeolocationSpec.cpp +98 -42
  21. package/nitrogen/generated/android/c++/JHybridNitroGeolocationSpec.hpp +7 -5
  22. package/nitrogen/generated/android/c++/JLocationError.hpp +61 -0
  23. package/nitrogen/generated/android/c++/JLocationProvider.hpp +61 -0
  24. package/nitrogen/generated/android/c++/JLocationProviderInternal.hpp +3 -4
  25. package/nitrogen/generated/android/c++/JLocationRequestOptions.hpp +81 -0
  26. package/nitrogen/generated/android/c++/JModernGeolocationConfiguration.hpp +73 -0
  27. package/nitrogen/generated/android/c++/JNullableDouble.cpp +26 -0
  28. package/nitrogen/generated/android/c++/JNullableDouble.hpp +69 -0
  29. package/nitrogen/generated/android/c++/JPermissionStatus.hpp +64 -0
  30. package/nitrogen/generated/android/c++/JRNConfigurationInternal.hpp +5 -1
  31. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/AuthorizationLevel.kt +24 -0
  32. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/AuthorizationLevelInternal.kt +2 -0
  33. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/Func_void.kt +0 -1
  34. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/Func_void_GeolocationError.kt +0 -1
  35. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/Func_void_GeolocationResponse.kt +0 -1
  36. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/Func_void_LocationError.kt +80 -0
  37. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/GeolocationCoordinates.kt +34 -25
  38. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/GeolocationError.kt +27 -18
  39. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/GeolocationOptions.kt +33 -24
  40. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/GeolocationResponse.kt +18 -9
  41. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/HybridNitroGeolocationCompatSpec.kt +92 -0
  42. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/HybridNitroGeolocationSpec.kt +17 -17
  43. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/LocationError.kt +41 -0
  44. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/LocationProvider.kt +24 -0
  45. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/LocationProviderInternal.kt +2 -0
  46. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/LocationRequestOptions.kt +56 -0
  47. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/ModernGeolocationConfiguration.kt +47 -0
  48. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/NullableDouble.kt +59 -0
  49. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/PermissionStatus.kt +25 -0
  50. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitrogeolocation/RNConfigurationInternal.kt +24 -15
  51. package/nitrogen/generated/android/nitrogeolocation+autolinking.cmake +3 -0
  52. package/nitrogen/generated/android/nitrogeolocationOnLoad.cpp +14 -2
  53. package/nitrogen/generated/ios/NitroGeolocation+autolinking.rb +1 -1
  54. package/nitrogen/generated/ios/NitroGeolocation-Swift-Cxx-Bridge.cpp +55 -13
  55. package/nitrogen/generated/ios/NitroGeolocation-Swift-Cxx-Bridge.hpp +325 -68
  56. package/nitrogen/generated/ios/NitroGeolocation-Swift-Cxx-Umbrella.hpp +26 -0
  57. package/nitrogen/generated/ios/NitroGeolocationAutolinking.mm +8 -0
  58. package/nitrogen/generated/ios/NitroGeolocationAutolinking.swift +15 -0
  59. package/nitrogen/generated/ios/c++/HybridNitroGeolocationCompatSpecSwift.cpp +11 -0
  60. package/nitrogen/generated/ios/c++/HybridNitroGeolocationCompatSpecSwift.hpp +130 -0
  61. package/nitrogen/generated/ios/c++/HybridNitroGeolocationSpecSwift.hpp +47 -26
  62. package/nitrogen/generated/ios/swift/AuthorizationLevel.swift +44 -0
  63. package/nitrogen/generated/ios/swift/Func_void.swift +1 -1
  64. package/nitrogen/generated/ios/swift/Func_void_GeolocationError.swift +1 -1
  65. package/nitrogen/generated/ios/swift/Func_void_GeolocationResponse.swift +1 -1
  66. package/nitrogen/generated/ios/swift/Func_void_LocationError.swift +47 -0
  67. package/nitrogen/generated/ios/swift/Func_void_PermissionStatus.swift +47 -0
  68. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
  69. package/nitrogen/generated/ios/swift/GeolocationCoordinates.swift +132 -93
  70. package/nitrogen/generated/ios/swift/GeolocationError.swift +11 -40
  71. package/nitrogen/generated/ios/swift/GeolocationOptions.swift +29 -98
  72. package/nitrogen/generated/ios/swift/GeolocationResponse.swift +5 -16
  73. package/nitrogen/generated/ios/swift/HybridNitroGeolocationCompatSpec.swift +61 -0
  74. package/nitrogen/generated/ios/swift/HybridNitroGeolocationCompatSpec_cxx.swift +244 -0
  75. package/nitrogen/generated/ios/swift/HybridNitroGeolocationSpec.swift +13 -5
  76. package/nitrogen/generated/ios/swift/HybridNitroGeolocationSpec_cxx.swift +66 -64
  77. package/nitrogen/generated/ios/swift/LocationError.swift +35 -0
  78. package/nitrogen/generated/ios/swift/LocationProvider.swift +44 -0
  79. package/nitrogen/generated/ios/swift/LocationRequestOptions.swift +116 -0
  80. package/nitrogen/generated/ios/swift/ModernGeolocationConfiguration.swift +83 -0
  81. package/nitrogen/generated/ios/swift/NullableDouble.swift +18 -0
  82. package/nitrogen/generated/ios/swift/PermissionStatus.swift +48 -0
  83. package/nitrogen/generated/ios/swift/RNConfigurationInternal.swift +16 -50
  84. package/nitrogen/generated/shared/c++/AuthorizationLevel.hpp +80 -0
  85. package/nitrogen/generated/shared/c++/GeolocationCoordinates.hpp +45 -27
  86. package/nitrogen/generated/shared/c++/GeolocationError.hpp +32 -16
  87. package/nitrogen/generated/shared/c++/GeolocationOptions.hpp +38 -22
  88. package/nitrogen/generated/shared/c++/GeolocationResponse.hpp +23 -7
  89. package/nitrogen/generated/shared/c++/HybridNitroGeolocationCompatSpec.cpp +26 -0
  90. package/nitrogen/generated/shared/c++/HybridNitroGeolocationCompatSpec.hpp +79 -0
  91. package/nitrogen/generated/shared/c++/HybridNitroGeolocationSpec.cpp +4 -3
  92. package/nitrogen/generated/shared/c++/HybridNitroGeolocationSpec.hpp +22 -16
  93. package/nitrogen/generated/shared/c++/LocationError.hpp +87 -0
  94. package/nitrogen/generated/shared/c++/LocationProvider.hpp +80 -0
  95. package/nitrogen/generated/shared/c++/LocationRequestOptions.hpp +107 -0
  96. package/nitrogen/generated/shared/c++/ModernGeolocationConfiguration.hpp +100 -0
  97. package/nitrogen/generated/shared/c++/PermissionStatus.hpp +84 -0
  98. package/nitrogen/generated/shared/c++/RNConfigurationInternal.hpp +29 -13
  99. package/package.json +16 -7
  100. package/src/GeolocationClient.ts +116 -0
  101. package/src/NitroGeolocation.nitro.ts +177 -30
  102. package/src/NitroGeolocationCompat.nitro.ts +41 -0
  103. package/src/NitroGeolocationModule.ts +6 -0
  104. package/src/compat/clearWatch.ts +9 -0
  105. package/src/{getCurrentPosition.ts → compat/getCurrentPosition.ts} +7 -3
  106. package/src/compat/index.tsx +34 -0
  107. package/src/compat/requestAuthorization.ts +9 -0
  108. package/src/{setRNConfiguration.ts → compat/setRNConfiguration.ts} +6 -4
  109. package/src/{stopObserving.ts → compat/stopObserving.ts} +4 -2
  110. package/src/{watchPosition.ts → compat/watchPosition.ts} +7 -6
  111. package/src/components/GeolocationProvider.tsx +91 -0
  112. package/src/components/index.ts +13 -0
  113. package/src/hooks/index.ts +9 -0
  114. package/src/hooks/useCheckPermission.ts +46 -0
  115. package/src/hooks/useGetCurrentPosition.ts +140 -0
  116. package/src/hooks/useRequestPermission.ts +98 -0
  117. package/src/hooks/useWatchPosition.ts +130 -0
  118. package/src/index.tsx +72 -27
  119. package/src/types.ts +32 -4
  120. package/src/utils/cache.ts +25 -0
  121. package/src/utils/config.ts +93 -0
  122. package/src/utils/errors.ts +82 -0
  123. package/src/utils/index.ts +27 -0
  124. package/src/utils/provider.ts +61 -0
  125. package/src/utils/quality.ts +98 -0
  126. package/src/clearWatch.ts +0 -13
  127. package/src/requestAuthorization.ts +0 -9
@@ -0,0 +1,9 @@
1
+ import { NitroGeolocationHybridCompatObject } from "../NitroGeolocationModule";
2
+ import type { GeolocationError } from "../types";
3
+
4
+ export function requestAuthorization(
5
+ success?: () => void,
6
+ error?: (error: GeolocationError) => void
7
+ ): void {
8
+ NitroGeolocationHybridCompatObject.requestAuthorization(success, error);
9
+ }
@@ -1,6 +1,6 @@
1
- import type { RNConfigurationInternal } from "./NitroGeolocation.nitro";
2
- import { NitroGeolocationHybridObject } from "./NitroGeolocationModule";
3
- import type { GeolocationConfiguration } from "./types";
1
+ import type { RNConfigurationInternal } from "../NitroGeolocationCompat.nitro";
2
+ import { NitroGeolocationHybridCompatObject } from "../NitroGeolocationModule";
3
+ import type { GeolocationConfiguration } from "../types";
4
4
 
5
5
  // Mapping layer: convert "android" to "android_platform" for C++
6
6
  function mapConfigToInternal(
@@ -18,5 +18,7 @@ function mapConfigToInternal(
18
18
  }
19
19
 
20
20
  export function setRNConfiguration(config: GeolocationConfiguration): void {
21
- NitroGeolocationHybridObject.setRNConfiguration(mapConfigToInternal(config));
21
+ NitroGeolocationHybridCompatObject.setRNConfiguration(
22
+ mapConfigToInternal(config)
23
+ );
22
24
  }
@@ -1,5 +1,5 @@
1
1
  import { NitroModules } from "react-native-nitro-modules";
2
- import type { NitroGeolocation } from "./NitroGeolocation.nitro";
2
+ import type { NitroGeolocationCompat } from "../NitroGeolocationCompat.nitro";
3
3
 
4
4
  /**
5
5
  * Stops observing all location updates.
@@ -7,6 +7,8 @@ import type { NitroGeolocation } from "./NitroGeolocation.nitro";
7
7
  */
8
8
  export function stopObserving(): void {
9
9
  const nitroGeolocation =
10
- NitroModules.createHybridObject<NitroGeolocation>("NitroGeolocation");
10
+ NitroModules.createHybridObject<NitroGeolocationCompat>(
11
+ "NitroGeolocationCompat"
12
+ );
11
13
  nitroGeolocation.stopObserving();
12
14
  }
@@ -1,10 +1,9 @@
1
- import { NitroModules } from "react-native-nitro-modules";
2
- import type { NitroGeolocation } from "./NitroGeolocation.nitro";
1
+ import { NitroGeolocationHybridCompatObject } from "../NitroGeolocationModule";
3
2
  import type {
4
3
  GeolocationError,
5
4
  GeolocationOptions,
6
5
  GeolocationResponse
7
- } from "./types";
6
+ } from "../types";
8
7
 
9
8
  /**
10
9
  * Invokes the success callback whenever the location changes.
@@ -20,7 +19,9 @@ export function watchPosition(
20
19
  error?: (error: GeolocationError) => void,
21
20
  options?: GeolocationOptions
22
21
  ): number {
23
- const nitroGeolocation =
24
- NitroModules.createHybridObject<NitroGeolocation>("NitroGeolocation");
25
- return nitroGeolocation.watchPosition(success, error, options);
22
+ return NitroGeolocationHybridCompatObject.watchPosition(
23
+ success,
24
+ error,
25
+ options
26
+ );
26
27
  }
@@ -0,0 +1,91 @@
1
+ import React, { createContext, useContext } from "react";
2
+ import type { GeolocationClient } from "../GeolocationClient";
3
+
4
+ /**
5
+ * Geolocation context value.
6
+ */
7
+ export interface GeolocationContextValue {
8
+ client: GeolocationClient;
9
+ }
10
+
11
+ /**
12
+ * Geolocation context.
13
+ */
14
+ const GeolocationContext = createContext<GeolocationContextValue | null>(null);
15
+
16
+ /**
17
+ * Hook to access GeolocationClient from context.
18
+ * Throws error if used outside GeolocationClientProvider.
19
+ */
20
+ export function useGeolocationClient(): GeolocationClient {
21
+ const context = useContext(GeolocationContext);
22
+
23
+ if (!context) {
24
+ throw new Error(
25
+ "useGeolocationClient must be used within GeolocationClientProvider. " +
26
+ "Wrap your component with <GeolocationClientProvider client={...}> at the root level."
27
+ );
28
+ }
29
+
30
+ return context.client;
31
+ }
32
+
33
+ /**
34
+ * Props for GeolocationProvider component.
35
+ */
36
+ export interface GeolocationProviderProps {
37
+ /**
38
+ * GeolocationClient instance (required).
39
+ * Create with: new GeolocationClient({...config})
40
+ */
41
+ client: GeolocationClient;
42
+
43
+ /**
44
+ * Child components that can use geolocation hooks.
45
+ */
46
+ children: React.ReactNode;
47
+ }
48
+
49
+ /**
50
+ * Provider component for GeolocationClient.
51
+ *
52
+ * This component should wrap your app at the root level.
53
+ * It provides a GeolocationClient instance to all child components via context.
54
+ *
55
+ * @example
56
+ * ```tsx
57
+ * // Create client instance
58
+ * const geolocationClient = new GeolocationClient({
59
+ * authorizationLevel: 'whenInUse',
60
+ * enableBackgroundLocationUpdates: false,
61
+ * locationProvider: 'auto'
62
+ * });
63
+ *
64
+ * function App() {
65
+ * return (
66
+ * <GeolocationClientProvider client={geolocationClient}>
67
+ * <NavigationContainer>
68
+ * <YourApp />
69
+ * </NavigationContainer>
70
+ * </GeolocationClientProvider>
71
+ * );
72
+ * }
73
+ * ```
74
+ */
75
+ export function GeolocationProvider({
76
+ client,
77
+ children
78
+ }: GeolocationProviderProps) {
79
+ const value: GeolocationContextValue = {
80
+ client
81
+ };
82
+
83
+ return (
84
+ <GeolocationContext.Provider value={value}>
85
+ {children}
86
+ </GeolocationContext.Provider>
87
+ );
88
+ }
89
+
90
+ // Modern alias (preferred)
91
+ export const GeolocationClientProvider = GeolocationProvider;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * React components for geolocation.
3
+ */
4
+
5
+ export {
6
+ GeolocationProvider,
7
+ GeolocationClientProvider,
8
+ useGeolocationClient
9
+ } from "./GeolocationProvider";
10
+ export type {
11
+ GeolocationProviderProps,
12
+ GeolocationContextValue
13
+ } from "./GeolocationProvider";
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Modern React hooks for geolocation.
3
+ * These hooks provide a declarative, React-friendly API for location services.
4
+ */
5
+
6
+ export * from "./useCheckPermission";
7
+ export * from "./useRequestPermission";
8
+ export * from "./useGetCurrentPosition";
9
+ export * from "./useWatchPosition";
@@ -0,0 +1,46 @@
1
+ import type { PermissionStatus } from "../NitroGeolocation.nitro";
2
+ import { useGeolocationClient } from "../components/GeolocationProvider";
3
+
4
+ /**
5
+ * Hook that returns a function to check current location permission status.
6
+ * Does NOT request permission, only checks current state.
7
+ *
8
+ * Must be used within GeolocationClientProvider.
9
+ *
10
+ * @returns Object containing checkPermission function
11
+ *
12
+ * @example
13
+ * ```tsx
14
+ * const geolocationClient = new GeolocationClient({...config});
15
+ *
16
+ * function App() {
17
+ * return (
18
+ * <GeolocationClientProvider client={geolocationClient}>
19
+ * <MyComponent />
20
+ * </GeolocationClientProvider>
21
+ * );
22
+ * }
23
+ *
24
+ * function MyComponent() {
25
+ * const { checkPermission } = useCheckPermission();
26
+ *
27
+ * const handleCheck = async () => {
28
+ * const status = await checkPermission();
29
+ * if (status === 'granted') {
30
+ * // Can use location
31
+ * }
32
+ * };
33
+ *
34
+ * return <Button onPress={handleCheck} />;
35
+ * }
36
+ * ```
37
+ */
38
+ export function useCheckPermission() {
39
+ const client = useGeolocationClient();
40
+
41
+ return {
42
+ checkPermission: (): Promise<PermissionStatus> => {
43
+ return client.checkPermission();
44
+ }
45
+ };
46
+ }
@@ -0,0 +1,140 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import type {
3
+ LocationError,
4
+ LocationRequestOptions
5
+ } from "../NitroGeolocation.nitro";
6
+ import { useGeolocationClient } from "../components/GeolocationProvider";
7
+ import type { GeolocationResponse } from "../types";
8
+
9
+ /**
10
+ * Options for useGetCurrentPosition hook (Query style).
11
+ */
12
+ export interface UseGetCurrentPositionOptions extends LocationRequestOptions {
13
+ /**
14
+ * Whether to automatically fetch the current position.
15
+ * When false, only manual refetch() will trigger the request.
16
+ * @default true
17
+ */
18
+ enabled?: boolean;
19
+ }
20
+
21
+ /**
22
+ * Hook for getting current location (one-time request) in Query style.
23
+ * Provides loading, error states, and data similar to TanStack Query.
24
+ *
25
+ * @param options - Location request options and enabled flag
26
+ * @returns Object containing position, loading/error states, and refetch function
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * // Auto-fetch on mount
31
+ * function MyComponent() {
32
+ * const {
33
+ * position,
34
+ * isLoading,
35
+ * isError,
36
+ * error,
37
+ * refetch
38
+ * } = useGetCurrentPosition({
39
+ * enabled: true,
40
+ * enableHighAccuracy: true,
41
+ * });
42
+ *
43
+ * if (isLoading) return <Text>Loading...</Text>;
44
+ * if (isError) return <Text>Error: {error?.message}</Text>;
45
+ * if (!position) return null;
46
+ *
47
+ * return (
48
+ * <View>
49
+ * <Text>Lat: {position.coords.latitude}</Text>
50
+ * <Text>Lng: {position.coords.longitude}</Text>
51
+ * <Button title="Refresh" onPress={() => refetch()} />
52
+ * </View>
53
+ * );
54
+ * }
55
+ *
56
+ * // Manual trigger only
57
+ * function ManualComponent() {
58
+ * const { position, isLoading, refetch } = useGetCurrentPosition({
59
+ * enabled: false
60
+ * });
61
+ *
62
+ * return (
63
+ * <View>
64
+ * <Button
65
+ * title="Get Location"
66
+ * onPress={() => refetch()}
67
+ * disabled={isLoading}
68
+ * />
69
+ * {position && <Text>Lat: {position.coords.latitude}</Text>}
70
+ * </View>
71
+ * );
72
+ * }
73
+ * ```
74
+ */
75
+ export function useGetCurrentPosition(options?: UseGetCurrentPositionOptions) {
76
+ const client = useGeolocationClient();
77
+
78
+ const [position, setPosition] = useState<GeolocationResponse | null>(null);
79
+ const [isLoading, setIsLoading] = useState(false);
80
+ const [isError, setIsError] = useState(false);
81
+ const [error, setError] = useState<LocationError | null>(null);
82
+
83
+ const isMountedRef = useRef(true);
84
+ const optionsRef = useRef(options);
85
+
86
+ // Update options ref when they change
87
+ useEffect(() => {
88
+ optionsRef.current = options;
89
+ }, [options]);
90
+
91
+ const fetchPosition = useCallback(async () => {
92
+ if (!isMountedRef.current) return;
93
+
94
+ setIsLoading(true);
95
+ setIsError(false);
96
+ setError(null);
97
+
98
+ try {
99
+ const result = await client.getCurrentPosition(optionsRef.current);
100
+ if (!isMountedRef.current) return;
101
+
102
+ setPosition(result);
103
+ setIsLoading(false);
104
+ } catch (err) {
105
+ if (!isMountedRef.current) return;
106
+
107
+ setIsError(true);
108
+ setError(err as LocationError);
109
+ setIsLoading(false);
110
+ }
111
+ }, [client]);
112
+
113
+ // Extract enabled flag for reactive dependency
114
+ const enabled = options?.enabled ?? true;
115
+
116
+ // Auto-fetch on mount if enabled
117
+ useEffect(() => {
118
+ if (enabled) {
119
+ fetchPosition();
120
+ }
121
+ // Only run when enabled changes, not when fetchPosition changes
122
+ // eslint-disable-next-line react-hooks/exhaustive-deps
123
+ }, [enabled]);
124
+
125
+ // Track mount status
126
+ useEffect(() => {
127
+ isMountedRef.current = true;
128
+ return () => {
129
+ isMountedRef.current = false;
130
+ };
131
+ }, []);
132
+
133
+ return {
134
+ position,
135
+ isLoading,
136
+ isError,
137
+ error,
138
+ refetch: fetchPosition
139
+ };
140
+ }
@@ -0,0 +1,98 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import type {
3
+ LocationError,
4
+ PermissionStatus
5
+ } from "../NitroGeolocation.nitro";
6
+ import { useGeolocationClient } from "../components/GeolocationProvider";
7
+
8
+ /**
9
+ * Hook for requesting location permission in Mutation style.
10
+ * Provides pending, error states, and status similar to TanStack Query mutations.
11
+ *
12
+ * @returns Object containing requestPermission function, status, and state flags
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * function MyComponent() {
17
+ * const {
18
+ * requestPermission,
19
+ * status,
20
+ * isPending,
21
+ * isError,
22
+ * error
23
+ * } = useRequestPermission();
24
+ *
25
+ * const handleRequest = async () => {
26
+ * const result = await requestPermission();
27
+ * if (result === 'granted') {
28
+ * console.log('Permission granted!');
29
+ * }
30
+ * };
31
+ *
32
+ * return (
33
+ * <View>
34
+ * <Button
35
+ * onPress={handleRequest}
36
+ * disabled={isPending}
37
+ * >
38
+ * {isPending ? 'Requesting...' : 'Request Permission'}
39
+ * </Button>
40
+ * {isError && <Text>Error: {error?.message}</Text>}
41
+ * {status && <Text>Status: {status}</Text>}
42
+ * </View>
43
+ * );
44
+ * }
45
+ * ```
46
+ */
47
+ export function useRequestPermission() {
48
+ const client = useGeolocationClient();
49
+
50
+ const [status, setStatus] = useState<PermissionStatus | null>(null);
51
+ const [isPending, setIsPending] = useState(false);
52
+ const [isError, setIsError] = useState(false);
53
+ const [error, setError] = useState<LocationError | null>(null);
54
+
55
+ const isMountedRef = useRef(true);
56
+
57
+ const requestPermission = useCallback(async (): Promise<PermissionStatus> => {
58
+ if (!isMountedRef.current) {
59
+ throw new Error("Component unmounted");
60
+ }
61
+
62
+ setIsPending(true);
63
+ setIsError(false);
64
+ setError(null);
65
+
66
+ try {
67
+ const result = await client.requestPermission();
68
+ if (!isMountedRef.current) return result;
69
+
70
+ setStatus(result);
71
+ setIsPending(false);
72
+ return result;
73
+ } catch (err) {
74
+ if (!isMountedRef.current) throw err;
75
+
76
+ setIsError(true);
77
+ setError(err as LocationError);
78
+ setIsPending(false);
79
+ throw err;
80
+ }
81
+ }, [client]);
82
+
83
+ // Track mount status
84
+ useEffect(() => {
85
+ isMountedRef.current = true;
86
+ return () => {
87
+ isMountedRef.current = false;
88
+ };
89
+ }, []);
90
+
91
+ return {
92
+ requestPermission,
93
+ status,
94
+ isPending,
95
+ isError,
96
+ error
97
+ };
98
+ }
@@ -0,0 +1,130 @@
1
+ import { useEffect, useRef, useState } from "react";
2
+ import type {
3
+ LocationError,
4
+ LocationRequestOptions
5
+ } from "../NitroGeolocation.nitro";
6
+ import { useGeolocationClient } from "../components/GeolocationProvider";
7
+ import type { GeolocationResponse } from "../types";
8
+
9
+ /**
10
+ * Options for useWatchPosition hook.
11
+ */
12
+ export interface UseWatchPositionOptions extends LocationRequestOptions {
13
+ /**
14
+ * Whether to actively watch for location updates.
15
+ * When false, watching is paused and cleanup is performed.
16
+ * @default false
17
+ */
18
+ enabled?: boolean;
19
+ }
20
+
21
+ /**
22
+ * Hook for continuous location tracking.
23
+ * Automatically subscribes/unsubscribes based on component lifecycle.
24
+ *
25
+ * The subscription token is completely hidden from the user.
26
+ * Cleanup is automatic via useEffect.
27
+ *
28
+ * @param options - Location request options
29
+ * @returns Object containing current position, error, and watching status
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * function LiveTracking() {
34
+ * const { position, error, isWatching } = useWatchPosition({
35
+ * enabled: true,
36
+ * enableHighAccuracy: true,
37
+ * distanceFilter: 10 // Update every 10 meters
38
+ * });
39
+ *
40
+ * if (!isWatching) return <Text>Not watching</Text>;
41
+ * if (error) return <Text>Error: {error.message}</Text>;
42
+ * if (!position) return <Text>Waiting for location...</Text>;
43
+ *
44
+ * return (
45
+ * <Text>
46
+ * Current: {position.coords.latitude}, {position.coords.longitude}
47
+ * Accuracy: {position.coords.accuracy}m
48
+ * </Text>
49
+ * );
50
+ * }
51
+ * ```
52
+ */
53
+ export function useWatchPosition(options?: UseWatchPositionOptions) {
54
+ const client = useGeolocationClient();
55
+
56
+ const [position, setPosition] = useState<GeolocationResponse | null>(null);
57
+ const [isWatching, setIsWatching] = useState(false);
58
+ const [error, setError] = useState<LocationError | null>(null);
59
+
60
+ // Store subscription token (hidden from user!)
61
+ const tokenRef = useRef<string | null>(null);
62
+
63
+ // Track if component is mounted to prevent state updates after unmount
64
+ const isMountedRef = useRef(true);
65
+
66
+ // Store latest options in ref to avoid unnecessary re-subscriptions
67
+ const optionsRef = useRef(options);
68
+
69
+ // Update options ref whenever options change
70
+ useEffect(() => {
71
+ optionsRef.current = options;
72
+ }, [options]);
73
+
74
+ // Extract enabled flag for reactive dependency
75
+ const enabled = options?.enabled ?? false;
76
+
77
+ useEffect(() => {
78
+ if (!enabled) {
79
+ // Not enabled, ensure cleanup
80
+ if (tokenRef.current) {
81
+ client.unwatch(tokenRef.current);
82
+ tokenRef.current = null;
83
+ }
84
+ setIsWatching(false);
85
+ return;
86
+ }
87
+
88
+ // Start watching with latest options
89
+ setIsWatching(true);
90
+ setError(null);
91
+
92
+ const token = client.watchPosition(
93
+ (result: GeolocationResponse) => {
94
+ // Success callback
95
+ if (!isMountedRef.current) return;
96
+ setPosition(result);
97
+ setError(null);
98
+ },
99
+ (err: LocationError) => {
100
+ // Error callback
101
+ if (!isMountedRef.current) return;
102
+ setError(err);
103
+ },
104
+ optionsRef.current
105
+ );
106
+
107
+ tokenRef.current = token;
108
+
109
+ // Cleanup function
110
+ return () => {
111
+ if (token) {
112
+ client.unwatch(token);
113
+ }
114
+ };
115
+ }, [enabled, client]); // Only re-subscribe when enabled/client changes
116
+
117
+ // Track mount status
118
+ useEffect(() => {
119
+ isMountedRef.current = true;
120
+ return () => {
121
+ isMountedRef.current = false;
122
+ };
123
+ }, []);
124
+
125
+ return {
126
+ position,
127
+ error,
128
+ isWatching
129
+ };
130
+ }