react-fathom 0.1.11 → 0.2.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 (70) hide show
  1. package/README.md +886 -24
  2. package/dist/cjs/index.cjs +55 -9
  3. package/dist/cjs/index.cjs.map +1 -1
  4. package/dist/cjs/native/index.cjs +1079 -0
  5. package/dist/cjs/native/index.cjs.map +1 -0
  6. package/dist/cjs/next/index.cjs +51 -5
  7. package/dist/cjs/next/index.cjs.map +1 -1
  8. package/dist/es/index.js +55 -9
  9. package/dist/es/index.js.map +1 -1
  10. package/dist/es/native/index.js +1071 -0
  11. package/dist/es/native/index.js.map +1 -0
  12. package/dist/es/next/index.js +51 -5
  13. package/dist/es/next/index.js.map +1 -1
  14. package/dist/react-fathom.js +55 -9
  15. package/dist/react-fathom.js.map +1 -1
  16. package/dist/react-fathom.min.js +2 -2
  17. package/dist/react-fathom.min.js.map +1 -1
  18. package/package.json +27 -4
  19. package/src/FathomContext.tsx +30 -1
  20. package/src/FathomProvider.test.tsx +115 -15
  21. package/src/FathomProvider.tsx +10 -2
  22. package/src/components/TrackClick.test.tsx +7 -7
  23. package/src/components/TrackClick.tsx +1 -1
  24. package/src/components/TrackVisible.test.tsx +7 -7
  25. package/src/components/TrackVisible.tsx +1 -1
  26. package/src/hooks/useFathom.test.tsx +14 -3
  27. package/src/hooks/useTrackOnClick.test.tsx +4 -4
  28. package/src/hooks/useTrackOnClick.ts +1 -1
  29. package/src/hooks/useTrackOnVisible.test.tsx +4 -4
  30. package/src/hooks/useTrackOnVisible.ts +1 -1
  31. package/src/index.ts +1 -0
  32. package/src/native/FathomWebView.test.tsx +410 -0
  33. package/src/native/FathomWebView.tsx +297 -0
  34. package/src/native/NativeFathomProvider.test.tsx +372 -0
  35. package/src/native/NativeFathomProvider.tsx +113 -0
  36. package/src/native/createWebViewClient.test.ts +380 -0
  37. package/src/native/createWebViewClient.ts +271 -0
  38. package/src/native/index.ts +29 -0
  39. package/src/native/react-native.d.ts +74 -0
  40. package/src/native/types.ts +145 -0
  41. package/src/native/useAppStateTracking.test.ts +249 -0
  42. package/src/native/useAppStateTracking.ts +66 -0
  43. package/src/native/useNavigationTracking.test.ts +446 -0
  44. package/src/native/useNavigationTracking.ts +177 -0
  45. package/src/types.ts +36 -9
  46. package/types/FathomContext.d.ts +1 -1
  47. package/types/FathomContext.d.ts.map +1 -1
  48. package/types/FathomProvider.d.ts.map +1 -1
  49. package/types/components/TrackClick.d.ts +1 -1
  50. package/types/components/TrackVisible.d.ts +1 -1
  51. package/types/hooks/useTrackOnClick.d.ts +1 -1
  52. package/types/hooks/useTrackOnVisible.d.ts +1 -1
  53. package/types/index.d.ts +1 -0
  54. package/types/index.d.ts.map +1 -1
  55. package/types/native/FathomWebView.d.ts +59 -0
  56. package/types/native/FathomWebView.d.ts.map +1 -0
  57. package/types/native/NativeFathomProvider.d.ts +36 -0
  58. package/types/native/NativeFathomProvider.d.ts.map +1 -0
  59. package/types/native/createWebViewClient.d.ts +51 -0
  60. package/types/native/createWebViewClient.d.ts.map +1 -0
  61. package/types/native/index.d.ts +10 -0
  62. package/types/native/index.d.ts.map +1 -0
  63. package/types/native/types.d.ts +125 -0
  64. package/types/native/types.d.ts.map +1 -0
  65. package/types/native/useAppStateTracking.d.ts +25 -0
  66. package/types/native/useAppStateTracking.d.ts.map +1 -0
  67. package/types/native/useNavigationTracking.d.ts +30 -0
  68. package/types/native/useNavigationTracking.d.ts.map +1 -0
  69. package/types/types.d.ts +34 -9
  70. package/types/types.d.ts.map +1 -1
@@ -0,0 +1,1071 @@
1
+ /*! react-fathom - 0.2.0 !*/
2
+ import React, { forwardRef, useRef, useState, useCallback, useImperativeHandle, createContext, useContext, useMemo, useEffect } from 'react';
3
+ import { View, StyleSheet, AppState } from 'react-native';
4
+ import { WebView } from 'react-native-webview';
5
+
6
+ function _arrayWithHoles(r) {
7
+ if (Array.isArray(r)) return r;
8
+ }
9
+
10
+ function _iterableToArrayLimit(r, l) {
11
+ var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
12
+ if (null != t) {
13
+ var e,
14
+ n,
15
+ i,
16
+ u,
17
+ a = [],
18
+ f = true,
19
+ o = false;
20
+ try {
21
+ if (i = (t = t.call(r)).next, 0 === l) ; else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0);
22
+ } catch (r) {
23
+ o = true, n = r;
24
+ } finally {
25
+ try {
26
+ if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return;
27
+ } finally {
28
+ if (o) throw n;
29
+ }
30
+ }
31
+ return a;
32
+ }
33
+ }
34
+
35
+ function _arrayLikeToArray(r, a) {
36
+ (null == a || a > r.length) && (a = r.length);
37
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
38
+ return n;
39
+ }
40
+
41
+ function _unsupportedIterableToArray(r, a) {
42
+ if (r) {
43
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
44
+ var t = {}.toString.call(r).slice(8, -1);
45
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
46
+ }
47
+ }
48
+
49
+ function _nonIterableRest() {
50
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
51
+ }
52
+
53
+ function _slicedToArray(r, e) {
54
+ return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
55
+ }
56
+
57
+ /**
58
+ * Hidden WebView component that loads and manages the Fathom Analytics script.
59
+ *
60
+ * This component renders an invisible WebView that loads the official Fathom
61
+ * tracking script, allowing React Native apps to use Fathom's full functionality.
62
+ *
63
+ * @example
64
+ * ```tsx
65
+ * const fathomRef = useRef<FathomWebViewRef>(null)
66
+ *
67
+ * <FathomWebView
68
+ * ref={fathomRef}
69
+ * siteId="ABCDEFGH"
70
+ * onReady={() => console.log('Fathom ready!')}
71
+ * />
72
+ *
73
+ * // Later, track events:
74
+ * fathomRef.current?.trackPageview({ url: '/home' })
75
+ * ```
76
+ */
77
+ var FathomWebView = /*#__PURE__*/forwardRef(function FathomWebView(_ref, ref) {
78
+ var siteId = _ref.siteId,
79
+ _ref$loadOptions = _ref.loadOptions,
80
+ loadOptions = _ref$loadOptions === void 0 ? {} : _ref$loadOptions,
81
+ _ref$scriptDomain = _ref.scriptDomain,
82
+ scriptDomain = _ref$scriptDomain === void 0 ? 'cdn.usefathom.com' : _ref$scriptDomain,
83
+ onReady = _ref.onReady,
84
+ _onError = _ref.onError,
85
+ _ref$debug = _ref.debug,
86
+ debug = _ref$debug === void 0 ? false : _ref$debug;
87
+ var webViewRef = useRef(null);
88
+ var _useState = useState(false),
89
+ _useState2 = _slicedToArray(_useState, 2),
90
+ _isReady = _useState2[0],
91
+ setIsReady = _useState2[1];
92
+ var log = useCallback(function () {
93
+ if (debug) {
94
+ var _console;
95
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
96
+ args[_key] = arguments[_key];
97
+ }
98
+ (_console = console).log.apply(_console, ['[react-fathom/webview]'].concat(args));
99
+ }
100
+ }, [debug]);
101
+
102
+ // Build data attributes for load options
103
+ var buildDataAttributes = useCallback(function () {
104
+ var attrs = ["data-site=\"".concat(siteId, "\"")];
105
+ if (loadOptions.auto === false) {
106
+ attrs.push('data-auto="false"');
107
+ }
108
+ if (loadOptions.honorDNT) {
109
+ attrs.push('data-honor-dnt="true"');
110
+ }
111
+ if (loadOptions.canonical === false) {
112
+ attrs.push('data-canonical="false"');
113
+ }
114
+ if (loadOptions.spa) {
115
+ attrs.push("data-spa=\"".concat(loadOptions.spa, "\""));
116
+ }
117
+ return attrs.join(' ');
118
+ }, [siteId, loadOptions]);
119
+
120
+ // HTML content that loads the Fathom script
121
+ var htmlContent = "\n <!DOCTYPE html>\n <html>\n <head>\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <script src=\"https://".concat(scriptDomain, "/script.js\" ").concat(buildDataAttributes(), " defer></script>\n <script>\n // Wait for Fathom to be available\n function waitForFathom(callback, maxAttempts = 50) {\n let attempts = 0;\n const check = () => {\n attempts++;\n if (typeof window.fathom !== 'undefined') {\n callback();\n } else if (attempts < maxAttempts) {\n setTimeout(check, 100);\n } else {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'error',\n message: 'Fathom script failed to load after ' + (maxAttempts * 100) + 'ms'\n }));\n }\n };\n check();\n }\n\n // Initialize when DOM is ready\n document.addEventListener('DOMContentLoaded', () => {\n waitForFathom(() => {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'ready'\n }));\n });\n });\n\n // Handle commands from React Native\n window.handleCommand = function(command) {\n if (typeof window.fathom === 'undefined') {\n console.warn('Fathom not loaded yet');\n return;\n }\n\n try {\n switch (command.action) {\n case 'trackPageview':\n window.fathom.trackPageview(command.options || {});\n break;\n case 'trackEvent':\n window.fathom.trackEvent(command.eventName, command.options || {});\n break;\n case 'trackGoal':\n window.fathom.trackGoal(command.code, command.cents);\n break;\n case 'blockTrackingForMe':\n window.fathom.blockTrackingForMe();\n break;\n case 'enableTrackingForMe':\n window.fathom.enableTrackingForMe();\n break;\n default:\n console.warn('Unknown command:', command.action);\n }\n } catch (error) {\n window.ReactNativeWebView.postMessage(JSON.stringify({\n type: 'error',\n message: error.message\n }));\n }\n };\n </script>\n </head>\n <body></body>\n </html>\n ");
122
+ var injectCommand = useCallback(function (command) {
123
+ if (!webViewRef.current) {
124
+ log('WebView not available');
125
+ return;
126
+ }
127
+ var script = "window.handleCommand(".concat(JSON.stringify(command), "); true;");
128
+ webViewRef.current.injectJavaScript(script);
129
+ log('Injected command:', command);
130
+ }, [log]);
131
+ var handleMessage = useCallback(function (event) {
132
+ try {
133
+ var data = JSON.parse(event.nativeEvent.data);
134
+ switch (data.type) {
135
+ case 'ready':
136
+ log('Fathom script loaded and ready');
137
+ setIsReady(true);
138
+ onReady === null || onReady === void 0 || onReady();
139
+ break;
140
+ case 'error':
141
+ log('Error from WebView:', data.message);
142
+ _onError === null || _onError === void 0 || _onError(data.message);
143
+ break;
144
+ default:
145
+ log('Unknown message type:', data.type);
146
+ }
147
+ } catch (_unused) {
148
+ log('Failed to parse WebView message:', event.nativeEvent.data);
149
+ }
150
+ }, [log, onReady, _onError]);
151
+
152
+ // Expose methods via ref
153
+ useImperativeHandle(ref, function () {
154
+ return {
155
+ trackPageview: function trackPageview(opts) {
156
+ injectCommand({
157
+ action: 'trackPageview',
158
+ options: opts
159
+ });
160
+ },
161
+ trackEvent: function trackEvent(eventName, opts) {
162
+ injectCommand({
163
+ action: 'trackEvent',
164
+ eventName: eventName,
165
+ options: opts
166
+ });
167
+ },
168
+ trackGoal: function trackGoal(code, cents) {
169
+ injectCommand({
170
+ action: 'trackGoal',
171
+ code: code,
172
+ cents: cents
173
+ });
174
+ },
175
+ blockTrackingForMe: function blockTrackingForMe() {
176
+ injectCommand({
177
+ action: 'blockTrackingForMe'
178
+ });
179
+ },
180
+ enableTrackingForMe: function enableTrackingForMe() {
181
+ injectCommand({
182
+ action: 'enableTrackingForMe'
183
+ });
184
+ },
185
+ isReady: function isReady() {
186
+ return _isReady;
187
+ }
188
+ };
189
+ }, [injectCommand, _isReady]);
190
+ return /*#__PURE__*/React.createElement(View, {
191
+ style: styles.container
192
+ }, /*#__PURE__*/React.createElement(WebView, {
193
+ ref: webViewRef,
194
+ source: {
195
+ html: htmlContent
196
+ },
197
+ onMessage: handleMessage,
198
+ onError: function onError(syntheticEvent) {
199
+ var nativeEvent = syntheticEvent.nativeEvent;
200
+ log('WebView error:', nativeEvent);
201
+ _onError === null || _onError === void 0 || _onError(nativeEvent.description || 'WebView error');
202
+ },
203
+ javaScriptEnabled: true,
204
+ domStorageEnabled: true
205
+ // Hide the WebView completely
206
+ ,
207
+ style: styles.webview
208
+ // Prevent any user interaction
209
+ ,
210
+ scrollEnabled: false,
211
+ bounces: false
212
+ // Optimize for background operation
213
+ ,
214
+ cacheEnabled: true,
215
+ incognito: false
216
+ }));
217
+ });
218
+ var styles = StyleSheet.create({
219
+ container: {
220
+ position: 'absolute',
221
+ width: 0,
222
+ height: 0,
223
+ overflow: 'hidden'
224
+ },
225
+ webview: {
226
+ width: 1,
227
+ height: 1,
228
+ opacity: 0
229
+ }
230
+ });
231
+
232
+ /**
233
+ * Creates a Fathom client that communicates with a FathomWebView component.
234
+ *
235
+ * This client queues commands until the WebView is ready, then flushes them.
236
+ * It implements the standard FathomClient interface for compatibility with
237
+ * the FathomProvider component.
238
+ *
239
+ * @example
240
+ * ```tsx
241
+ * import { createWebViewClient, FathomWebView } from 'react-fathom/native'
242
+ *
243
+ * function App() {
244
+ * const webViewRef = useRef<FathomWebViewRef>(null)
245
+ * const client = useMemo(
246
+ * () => createWebViewClient(() => webViewRef.current, { debug: __DEV__ }),
247
+ * []
248
+ * )
249
+ *
250
+ * return (
251
+ * <FathomProvider client={client} siteId="YOUR_SITE_ID">
252
+ * <FathomWebView ref={webViewRef} siteId="YOUR_SITE_ID" />
253
+ * <YourApp />
254
+ * </FathomProvider>
255
+ * )
256
+ * }
257
+ * ```
258
+ */
259
+
260
+ function createWebViewClient(getWebViewRef) {
261
+ var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
262
+ var _options$debug = options.debug,
263
+ debug = _options$debug === void 0 ? false : _options$debug,
264
+ _options$enableQueue = options.enableQueue,
265
+ enableQueue = _options$enableQueue === void 0 ? true : _options$enableQueue,
266
+ _options$maxQueueSize = options.maxQueueSize,
267
+ maxQueueSize = _options$maxQueueSize === void 0 ? 100 : _options$maxQueueSize;
268
+ var isTrackingBlocked = false;
269
+
270
+ // Queue for commands sent before WebView is ready
271
+ var commandQueue = [];
272
+ var log = function log() {
273
+ if (debug) {
274
+ var _console;
275
+ for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
276
+ args[_key] = arguments[_key];
277
+ }
278
+ (_console = console).log.apply(_console, ['[react-fathom/webview-client]'].concat(args));
279
+ }
280
+ };
281
+ var warn = function warn() {
282
+ if (debug) {
283
+ var _console2;
284
+ for (var _len2 = arguments.length, args = new Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
285
+ args[_key2] = arguments[_key2];
286
+ }
287
+ (_console2 = console).warn.apply(_console2, ['[react-fathom/webview-client]'].concat(args));
288
+ }
289
+ };
290
+
291
+ /**
292
+ * Queue a command for later execution
293
+ */
294
+ var queueCommand = function queueCommand(type, args) {
295
+ if (!enableQueue) {
296
+ warn('Queue disabled, dropping command:', type);
297
+ return;
298
+ }
299
+ if (commandQueue.length >= maxQueueSize) {
300
+ commandQueue.shift();
301
+ log('Queue full, removed oldest command');
302
+ }
303
+ commandQueue.push({
304
+ type: type,
305
+ args: args,
306
+ timestamp: Date.now()
307
+ });
308
+ log("Command queued (".concat(commandQueue.length, "/").concat(maxQueueSize, "):"), type);
309
+ };
310
+
311
+ /**
312
+ * Process all queued commands
313
+ */
314
+ var processQueue = function processQueue() {
315
+ var ref = getWebViewRef();
316
+ if (!(ref !== null && ref !== void 0 && ref.isReady())) {
317
+ return 0;
318
+ }
319
+ log("Processing ".concat(commandQueue.length, " queued commands"));
320
+ var processed = 0;
321
+ while (commandQueue.length > 0) {
322
+ var command = commandQueue.shift();
323
+ switch (command.type) {
324
+ case 'pageview':
325
+ ref.trackPageview(command.args[0]);
326
+ break;
327
+ case 'event':
328
+ ref.trackEvent(command.args[0], command.args[1]);
329
+ break;
330
+ case 'goal':
331
+ ref.trackGoal(command.args[0], command.args[1]);
332
+ break;
333
+ case 'block':
334
+ ref.blockTrackingForMe();
335
+ break;
336
+ case 'enable':
337
+ ref.enableTrackingForMe();
338
+ break;
339
+ }
340
+ processed++;
341
+ }
342
+ log("Processed ".concat(processed, " queued commands"));
343
+ return processed;
344
+ };
345
+
346
+ /**
347
+ * Execute a command immediately or queue it
348
+ */
349
+ var executeOrQueue = function executeOrQueue(type, args, executor) {
350
+ var ref = getWebViewRef();
351
+ if (ref !== null && ref !== void 0 && ref.isReady()) {
352
+ executor();
353
+ } else {
354
+ queueCommand(type, args);
355
+ }
356
+ };
357
+ var client = {
358
+ load: function load(siteId, opts) {
359
+ log('Client loaded with site ID:', siteId);
360
+
361
+ // Process any queued commands now that we're "loaded"
362
+ // (actual WebView readiness is separate)
363
+ processQueue();
364
+ },
365
+ trackPageview: function trackPageview(opts) {
366
+ if (isTrackingBlocked) {
367
+ log('Tracking blocked, skipping pageview');
368
+ return;
369
+ }
370
+ executeOrQueue('pageview', [opts], function () {
371
+ var ref = getWebViewRef();
372
+ ref === null || ref === void 0 || ref.trackPageview(opts);
373
+ log('Tracked pageview:', opts);
374
+ });
375
+ },
376
+ trackEvent: function trackEvent(eventName, opts) {
377
+ if (isTrackingBlocked) {
378
+ log('Tracking blocked, skipping event');
379
+ return;
380
+ }
381
+ executeOrQueue('event', [eventName, opts], function () {
382
+ var ref = getWebViewRef();
383
+ ref === null || ref === void 0 || ref.trackEvent(eventName, opts);
384
+ log('Tracked event:', eventName, opts);
385
+ });
386
+ },
387
+ trackGoal: function trackGoal(code, cents) {
388
+ if (isTrackingBlocked) {
389
+ log('Tracking blocked, skipping goal');
390
+ return;
391
+ }
392
+ executeOrQueue('goal', [code, cents], function () {
393
+ var ref = getWebViewRef();
394
+ ref === null || ref === void 0 || ref.trackGoal(code, cents);
395
+ log('Tracked goal:', code, cents);
396
+ });
397
+ },
398
+ setSite: function setSite(id) {
399
+ log('Site ID changed to:', id);
400
+ // Note: The WebView loads with a specific site ID, so changing it
401
+ // at runtime would require reloading the WebView
402
+ warn('setSite() called but WebView was initialized with a different site ID. ' + 'Consider re-mounting the FathomWebView component.');
403
+ },
404
+ blockTrackingForMe: function blockTrackingForMe() {
405
+ isTrackingBlocked = true;
406
+ executeOrQueue('block', [], function () {
407
+ var ref = getWebViewRef();
408
+ ref === null || ref === void 0 || ref.blockTrackingForMe();
409
+ });
410
+ log('Tracking blocked');
411
+ },
412
+ enableTrackingForMe: function enableTrackingForMe() {
413
+ isTrackingBlocked = false;
414
+ executeOrQueue('enable', [], function () {
415
+ var ref = getWebViewRef();
416
+ ref === null || ref === void 0 || ref.enableTrackingForMe();
417
+ });
418
+ log('Tracking enabled');
419
+
420
+ // Process queue when tracking is re-enabled
421
+ processQueue();
422
+ },
423
+ isTrackingEnabled: function isTrackingEnabled() {
424
+ return !isTrackingBlocked;
425
+ },
426
+ // Additional methods for React Native
427
+ processQueue: processQueue,
428
+ getQueueLength: function getQueueLength() {
429
+ return commandQueue.length;
430
+ },
431
+ /**
432
+ * Call this when the WebView signals it's ready.
433
+ * This will flush any queued commands.
434
+ */
435
+ setWebViewReady: function setWebViewReady() {
436
+ log('WebView ready, processing queue');
437
+ processQueue();
438
+ }
439
+ };
440
+ return client;
441
+ }
442
+
443
+ function _typeof(o) {
444
+ "@babel/helpers - typeof";
445
+
446
+ return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
447
+ return typeof o;
448
+ } : function (o) {
449
+ return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
450
+ }, _typeof(o);
451
+ }
452
+
453
+ function toPrimitive(t, r) {
454
+ if ("object" != _typeof(t) || !t) return t;
455
+ var e = t[Symbol.toPrimitive];
456
+ if (void 0 !== e) {
457
+ var i = e.call(t, r);
458
+ if ("object" != _typeof(i)) return i;
459
+ throw new TypeError("@@toPrimitive must return a primitive value.");
460
+ }
461
+ return ("string" === r ? String : Number)(t);
462
+ }
463
+
464
+ function toPropertyKey(t) {
465
+ var i = toPrimitive(t, "string");
466
+ return "symbol" == _typeof(i) ? i : i + "";
467
+ }
468
+
469
+ function _defineProperty(e, r, t) {
470
+ return (r = toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
471
+ value: t,
472
+ enumerable: true,
473
+ configurable: true,
474
+ writable: true
475
+ }) : e[r] = t, e;
476
+ }
477
+
478
+ // src/index.ts
479
+ var enqueue = (command) => {
480
+ window.__fathomClientQueue = window.__fathomClientQueue || [];
481
+ window.__fathomClientQueue.push(command);
482
+ };
483
+ var flushQueue = () => {
484
+ window.__fathomIsLoading = false;
485
+ window.__fathomClientQueue = window.__fathomClientQueue || [];
486
+ window.__fathomClientQueue.forEach((command) => {
487
+ switch (command.type) {
488
+ case "trackPageview":
489
+ trackPageview(command.opts);
490
+ return;
491
+ case "trackGoal":
492
+ trackGoal(command.code, command.cents);
493
+ return;
494
+ case "trackEvent":
495
+ trackEvent(command.eventName, command.opts);
496
+ return;
497
+ case "enableTrackingForMe":
498
+ enableTrackingForMe();
499
+ return;
500
+ case "blockTrackingForMe":
501
+ blockTrackingForMe();
502
+ return;
503
+ case "setSite":
504
+ setSite(command.id);
505
+ return;
506
+ }
507
+ });
508
+ window.__fathomClientQueue = [];
509
+ };
510
+ var checkDomainsAndWarn = (domains) => {
511
+ const regex = /(https?)(?=:|\/|$)/;
512
+ domains.forEach((domain) => {
513
+ if (regex.exec(domain) !== null)
514
+ console.warn(
515
+ `The include domain ${domain} might fail to work as intended as it begins with a transfer protocol (http://, https://). Consider removing the protocol portion of the string.`
516
+ );
517
+ });
518
+ };
519
+ var load = (siteId, opts) => {
520
+ if (window.__fathomIsLoading || window.fathom) return;
521
+ window.__fathomIsLoading = true;
522
+ let tracker = document.createElement("script");
523
+ let firstScript = document.getElementsByTagName("script")[0] || document.querySelector("body");
524
+ tracker.id = "fathom-script";
525
+ tracker.async = true;
526
+ tracker.setAttribute("data-site", siteId);
527
+ tracker.src = opts && opts.url ? opts.url : "https://cdn.usefathom.com/script.js";
528
+ if (opts) {
529
+ if (opts.auto !== void 0)
530
+ tracker.setAttribute("data-auto", `${opts.auto}`);
531
+ if (opts.honorDNT !== void 0)
532
+ tracker.setAttribute("data-honor-dnt", `${opts.honorDNT}`);
533
+ if (opts.canonical !== void 0)
534
+ tracker.setAttribute("data-canonical", `${opts.canonical}`);
535
+ if (opts.includedDomains) {
536
+ checkDomainsAndWarn(opts.includedDomains);
537
+ tracker.setAttribute(
538
+ "data-included-domains",
539
+ opts.includedDomains.join(",")
540
+ );
541
+ }
542
+ if (opts.excludedDomains) {
543
+ checkDomainsAndWarn(opts.excludedDomains);
544
+ tracker.setAttribute(
545
+ "data-excluded-domains",
546
+ opts.excludedDomains.join(",")
547
+ );
548
+ }
549
+ if (opts.spa) tracker.setAttribute("data-spa", opts.spa);
550
+ }
551
+ tracker.onload = flushQueue;
552
+ firstScript.parentNode.insertBefore(tracker, firstScript);
553
+ };
554
+ var trackPageview = (opts) => {
555
+ if (window.fathom) {
556
+ if (opts) {
557
+ window.fathom.trackPageview(opts);
558
+ } else {
559
+ window.fathom.trackPageview();
560
+ }
561
+ } else {
562
+ enqueue({ type: "trackPageview", opts });
563
+ }
564
+ };
565
+ var trackGoal = (code, cents) => {
566
+ if (window.fathom) {
567
+ window.fathom.trackGoal(code, cents);
568
+ } else {
569
+ enqueue({ type: "trackGoal", code, cents });
570
+ }
571
+ };
572
+ var trackEvent = (eventName, opts) => {
573
+ if (window.fathom) {
574
+ window.fathom.trackEvent(eventName, opts);
575
+ } else {
576
+ enqueue({ type: "trackEvent", eventName, opts });
577
+ }
578
+ };
579
+ var blockTrackingForMe = () => {
580
+ if (window.fathom) {
581
+ window.fathom.blockTrackingForMe();
582
+ } else {
583
+ enqueue({ type: "blockTrackingForMe" });
584
+ }
585
+ };
586
+ var enableTrackingForMe = () => {
587
+ if (window.fathom) {
588
+ window.fathom.enableTrackingForMe();
589
+ } else {
590
+ enqueue({ type: "enableTrackingForMe" });
591
+ }
592
+ };
593
+ var isTrackingEnabled = () => {
594
+ const preferenceStorage = localStorage.getItem(
595
+ "blockFathomTracking"
596
+ );
597
+ return preferenceStorage !== null ? preferenceStorage !== "true" : true;
598
+ };
599
+ var setSite = (id) => {
600
+ if (window.fathom) {
601
+ window.fathom.setSite(id);
602
+ } else {
603
+ enqueue({ type: "setSite", id });
604
+ }
605
+ };
606
+
607
+ var Fathom = /*#__PURE__*/Object.freeze({
608
+ __proto__: null,
609
+ blockTrackingForMe: blockTrackingForMe,
610
+ enableTrackingForMe: enableTrackingForMe,
611
+ isTrackingEnabled: isTrackingEnabled,
612
+ load: load,
613
+ setSite: setSite,
614
+ trackEvent: trackEvent,
615
+ trackGoal: trackGoal,
616
+ trackPageview: trackPageview
617
+ });
618
+
619
+ var warnMissingProvider = function warnMissingProvider(methodName) {
620
+ if (process.env.NODE_ENV !== 'production') {
621
+ console.warn("[react-fathom] ".concat(methodName, "() called without a FathomProvider. ") + 'Wrap your app with <FathomProvider> to enable analytics tracking.');
622
+ }
623
+ };
624
+
625
+ /**
626
+ * Default context value with stub methods that warn in development.
627
+ * These allow useFathom() to be called without optional chaining,
628
+ * while still informing developers when the provider is missing.
629
+ */
630
+ var defaultContextValue = {
631
+ blockTrackingForMe: function blockTrackingForMe() {
632
+ return warnMissingProvider('blockTrackingForMe');
633
+ },
634
+ enableTrackingForMe: function enableTrackingForMe() {
635
+ return warnMissingProvider('enableTrackingForMe');
636
+ },
637
+ isTrackingEnabled: function isTrackingEnabled() {
638
+ warnMissingProvider('isTrackingEnabled');
639
+ return false;
640
+ },
641
+ load: function load() {
642
+ return warnMissingProvider('load');
643
+ },
644
+ setSite: function setSite() {
645
+ return warnMissingProvider('setSite');
646
+ },
647
+ trackPageview: function trackPageview() {
648
+ return warnMissingProvider('trackPageview');
649
+ },
650
+ trackEvent: function trackEvent() {
651
+ return warnMissingProvider('trackEvent');
652
+ },
653
+ trackGoal: function trackGoal() {
654
+ return warnMissingProvider('trackGoal');
655
+ }
656
+ };
657
+ var FathomContext = /*#__PURE__*/createContext(defaultContextValue);
658
+
659
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
660
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), true).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
661
+ var FathomProvider = function FathomProvider(_ref) {
662
+ var children = _ref.children,
663
+ providedClient = _ref.client,
664
+ clientRef = _ref.clientRef,
665
+ clientOptions = _ref.clientOptions,
666
+ siteId = _ref.siteId,
667
+ providedDefaultPageviewOptions = _ref.defaultPageviewOptions,
668
+ providedDefaultEventOptions = _ref.defaultEventOptions;
669
+ // Read parent context if it exists
670
+ var parentContext = useContext(FathomContext);
671
+
672
+ // Use provided client or fall back to parent client or default Fathom
673
+ var client = useMemo(function () {
674
+ var _ref2;
675
+ return (_ref2 = providedClient !== null && providedClient !== void 0 ? providedClient : parentContext.client) !== null && _ref2 !== void 0 ? _ref2 : Fathom;
676
+ }, [providedClient, parentContext.client]);
677
+
678
+ // Merge defaultPageviewOptions: provided > parent > undefined
679
+ var defaultPageviewOptions = useMemo(function () {
680
+ return providedDefaultPageviewOptions !== null && providedDefaultPageviewOptions !== void 0 ? providedDefaultPageviewOptions : parentContext.defaultPageviewOptions;
681
+ }, [providedDefaultPageviewOptions, parentContext.defaultPageviewOptions]);
682
+
683
+ // Merge defaultEventOptions: provided > parent > undefined
684
+ var defaultEventOptions = useMemo(function () {
685
+ return providedDefaultEventOptions !== null && providedDefaultEventOptions !== void 0 ? providedDefaultEventOptions : parentContext.defaultEventOptions;
686
+ }, [providedDefaultEventOptions, parentContext.defaultEventOptions]);
687
+ var blockTrackingForMe = useCallback(function () {
688
+ client.blockTrackingForMe();
689
+ }, [client]);
690
+ var enableTrackingForMe = useCallback(function () {
691
+ client.enableTrackingForMe();
692
+ }, [client]);
693
+ var isTrackingEnabled = useCallback(function () {
694
+ var _client$isTrackingEna;
695
+ return (_client$isTrackingEna = client.isTrackingEnabled()) !== null && _client$isTrackingEna !== void 0 ? _client$isTrackingEna : false;
696
+ }, [client]);
697
+ var load = useCallback(function (siteId, clientOptions) {
698
+ client.load(siteId, clientOptions);
699
+ }, [client]);
700
+ var setSite = useCallback(function (siteId) {
701
+ client.setSite(siteId);
702
+ }, [client]);
703
+ var trackEvent = useCallback(function (eventName, options) {
704
+ client.trackEvent(eventName, _objectSpread(_objectSpread({}, defaultEventOptions), options));
705
+ }, [client, defaultEventOptions]);
706
+ var trackPageview = useCallback(function (options) {
707
+ client.trackPageview(_objectSpread(_objectSpread({}, defaultPageviewOptions), options));
708
+ }, [client, defaultPageviewOptions]);
709
+ var trackGoal = useCallback(function (code, cents) {
710
+ client.trackGoal(code, cents);
711
+ }, [client]);
712
+ useEffect(function () {
713
+ if (siteId !== undefined) {
714
+ load(siteId, clientOptions);
715
+ }
716
+ }, [clientOptions, load, siteId]);
717
+
718
+ // Populate the clientRef so the parent component can access the client
719
+ useEffect(function () {
720
+ if (clientRef) {
721
+ clientRef.current = client;
722
+ }
723
+ }, [client, clientRef]);
724
+ return /*#__PURE__*/React.createElement(FathomContext.Provider, {
725
+ value: {
726
+ blockTrackingForMe: blockTrackingForMe,
727
+ enableTrackingForMe: enableTrackingForMe,
728
+ isTrackingEnabled: isTrackingEnabled,
729
+ load: load,
730
+ setSite: setSite,
731
+ trackEvent: trackEvent,
732
+ trackGoal: trackGoal,
733
+ trackPageview: trackPageview,
734
+ client: client,
735
+ defaultPageviewOptions: defaultPageviewOptions,
736
+ defaultEventOptions: defaultEventOptions
737
+ }
738
+ }, children);
739
+ };
740
+ FathomProvider.displayName = 'FathomProvider';
741
+
742
+ var useFathom = function useFathom() {
743
+ var context = useContext(FathomContext);
744
+ return context;
745
+ };
746
+ useFathom.displayName = 'useFathom';
747
+
748
+ /**
749
+ * Hook that tracks app state changes (foreground/background) as Fathom events.
750
+ *
751
+ * This is useful for understanding user engagement patterns and session behavior.
752
+ *
753
+ * @example
754
+ * ```tsx
755
+ * import { useAppStateTracking } from 'react-fathom/native'
756
+ *
757
+ * function App() {
758
+ * useAppStateTracking({
759
+ * foregroundEventName: 'app-resumed',
760
+ * backgroundEventName: 'app-paused',
761
+ * onStateChange: (state) => {
762
+ * console.log('App state:', state)
763
+ * },
764
+ * })
765
+ *
766
+ * return <YourApp />
767
+ * }
768
+ * ```
769
+ */
770
+ function useAppStateTracking() {
771
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
772
+ var _options$foregroundEv = options.foregroundEventName,
773
+ foregroundEventName = _options$foregroundEv === void 0 ? 'app-foreground' : _options$foregroundEv,
774
+ _options$backgroundEv = options.backgroundEventName,
775
+ backgroundEventName = _options$backgroundEv === void 0 ? 'app-background' : _options$backgroundEv,
776
+ eventOptions = options.eventOptions,
777
+ onStateChange = options.onStateChange;
778
+ var _useFathom = useFathom(),
779
+ trackEvent = _useFathom.trackEvent;
780
+ var appStateRef = useRef(AppState.currentState);
781
+ useEffect(function () {
782
+ var handleAppStateChange = function handleAppStateChange(nextAppState) {
783
+ var previousState = appStateRef.current;
784
+
785
+ // Track when app comes to foreground
786
+ if (previousState.match(/inactive|background/) && nextAppState === 'active') {
787
+ trackEvent === null || trackEvent === void 0 || trackEvent(foregroundEventName, eventOptions);
788
+ }
789
+
790
+ // Track when app goes to background
791
+ if (previousState === 'active' && nextAppState.match(/inactive|background/)) {
792
+ trackEvent === null || trackEvent === void 0 || trackEvent(backgroundEventName, eventOptions);
793
+ }
794
+
795
+ // Call the optional state change callback
796
+ onStateChange === null || onStateChange === void 0 || onStateChange(nextAppState);
797
+ appStateRef.current = nextAppState;
798
+ };
799
+ var subscription = AppState.addEventListener('change', handleAppStateChange);
800
+ return function () {
801
+ subscription.remove();
802
+ };
803
+ }, [trackEvent, foregroundEventName, backgroundEventName, eventOptions, onStateChange]);
804
+ }
805
+
806
+ /**
807
+ * Internal component that handles app state tracking
808
+ */
809
+ var AppStateTracker = function AppStateTracker() {
810
+ useAppStateTracking({
811
+ foregroundEventName: 'app-foreground',
812
+ backgroundEventName: 'app-background'
813
+ });
814
+ return null;
815
+ };
816
+
817
+ /**
818
+ * A convenience provider for React Native apps that uses a hidden WebView
819
+ * to load the official Fathom Analytics script.
820
+ *
821
+ * This approach ensures full compatibility with Fathom's tracking by using
822
+ * their official JavaScript client, while providing a native React API.
823
+ *
824
+ * @example
825
+ * ```tsx
826
+ * import { NativeFathomProvider } from 'react-fathom/native'
827
+ *
828
+ * function App() {
829
+ * return (
830
+ * <NativeFathomProvider
831
+ * siteId="YOUR_SITE_ID"
832
+ * debug={__DEV__}
833
+ * trackAppState
834
+ * >
835
+ * <YourApp />
836
+ * </NativeFathomProvider>
837
+ * )
838
+ * }
839
+ * ```
840
+ *
841
+ * @remarks
842
+ * This component renders a hidden WebView that loads Fathom's tracking script.
843
+ * Events are queued until the WebView is ready, then automatically flushed.
844
+ *
845
+ * The WebView approach is used because Fathom Analytics does not currently
846
+ * provide a public API for server-side or mobile event tracking. This ensures
847
+ * your analytics are recorded correctly using Fathom's official client.
848
+ */
849
+ var NativeFathomProvider = function NativeFathomProvider(_ref) {
850
+ var siteId = _ref.siteId,
851
+ loadOptions = _ref.loadOptions,
852
+ scriptDomain = _ref.scriptDomain,
853
+ defaultPageviewOptions = _ref.defaultPageviewOptions,
854
+ defaultEventOptions = _ref.defaultEventOptions,
855
+ _ref$trackAppState = _ref.trackAppState,
856
+ trackAppState = _ref$trackAppState === void 0 ? false : _ref$trackAppState,
857
+ _ref$debug = _ref.debug,
858
+ debug = _ref$debug === void 0 ? false : _ref$debug,
859
+ onReady = _ref.onReady,
860
+ onError = _ref.onError,
861
+ clientRef = _ref.clientRef,
862
+ children = _ref.children;
863
+ var webViewRef = useRef(null);
864
+
865
+ // Create the WebView-based client
866
+ var client = useMemo(function () {
867
+ return createWebViewClient(function () {
868
+ return webViewRef.current;
869
+ }, {
870
+ debug: debug,
871
+ enableQueue: true,
872
+ maxQueueSize: 100
873
+ });
874
+ }, [debug]);
875
+
876
+ // Populate the clientRef so the parent component can access the client
877
+ useEffect(function () {
878
+ if (clientRef) {
879
+ clientRef.current = client;
880
+ }
881
+ }, [client, clientRef]);
882
+
883
+ // Handle WebView ready event
884
+ var handleReady = useCallback(function () {
885
+ // Flush any queued commands
886
+ client.setWebViewReady();
887
+ onReady === null || onReady === void 0 || onReady();
888
+ }, [client, onReady]);
889
+ return /*#__PURE__*/React.createElement(FathomProvider, {
890
+ client: client,
891
+ siteId: siteId,
892
+ defaultPageviewOptions: defaultPageviewOptions,
893
+ defaultEventOptions: defaultEventOptions
894
+ }, /*#__PURE__*/React.createElement(FathomWebView, {
895
+ ref: webViewRef,
896
+ siteId: siteId,
897
+ loadOptions: loadOptions,
898
+ scriptDomain: scriptDomain,
899
+ debug: debug,
900
+ onReady: handleReady,
901
+ onError: onError
902
+ }), trackAppState && /*#__PURE__*/React.createElement(AppStateTracker, null), children);
903
+ };
904
+
905
+ /**
906
+ * Hook that tracks screen navigation as pageviews using React Navigation.
907
+ *
908
+ * This integrates with React Navigation's navigation container to automatically
909
+ * track screen changes as Fathom pageviews.
910
+ *
911
+ * @example
912
+ * ```tsx
913
+ * import { NavigationContainer } from '@react-navigation/native'
914
+ * import { useNavigationTracking } from 'react-fathom/native'
915
+ *
916
+ * function App() {
917
+ * const navigationRef = useNavigationContainerRef()
918
+ *
919
+ * useNavigationTracking({
920
+ * navigationRef,
921
+ * transformRouteName: (name) => `/screens/${name}`,
922
+ * })
923
+ *
924
+ * return (
925
+ * <NavigationContainer ref={navigationRef}>
926
+ * <Navigator />
927
+ * </NavigationContainer>
928
+ * )
929
+ * }
930
+ * ```
931
+ */
932
+ function useNavigationTracking(options) {
933
+ var navigationRef = options.navigationRef,
934
+ transformRouteName = options.transformRouteName,
935
+ shouldTrackRoute = options.shouldTrackRoute,
936
+ _options$includeParam = options.includeParams,
937
+ includeParams = _options$includeParam === void 0 ? false : _options$includeParam;
938
+ var _useFathom = useFathom(),
939
+ trackPageview = _useFathom.trackPageview;
940
+ var routeNameRef = useRef(undefined);
941
+
942
+ /**
943
+ * Get the current route name from the navigation state
944
+ */
945
+ var getCurrentRouteName = useCallback(function () {
946
+ var _navigationRef$curren, _navigationRef$curren2, _currentState$routes$2;
947
+ if (!navigationRef.current) {
948
+ return undefined;
949
+ }
950
+ var state = (_navigationRef$curren = (_navigationRef$curren2 = navigationRef.current).getRootState) === null || _navigationRef$curren === void 0 ? void 0 : _navigationRef$curren.call(_navigationRef$curren2);
951
+ if (!state) {
952
+ return undefined;
953
+ }
954
+
955
+ // Navigate through nested navigators to get the deepest route
956
+ var currentState = state;
957
+ while ((_currentState$routes$ = currentState.routes[currentState.index]) !== null && _currentState$routes$ !== void 0 && _currentState$routes$.state) {
958
+ var _currentState$routes$;
959
+ currentState = currentState.routes[currentState.index].state;
960
+ }
961
+ return (_currentState$routes$2 = currentState.routes[currentState.index]) === null || _currentState$routes$2 === void 0 ? void 0 : _currentState$routes$2.name;
962
+ }, [navigationRef]);
963
+
964
+ /**
965
+ * Get the current route params from the navigation state
966
+ */
967
+ var getCurrentRouteParams = useCallback(function () {
968
+ var _navigationRef$curren3, _navigationRef$curren4, _currentState$routes$4;
969
+ if (!navigationRef.current) {
970
+ return undefined;
971
+ }
972
+ var state = (_navigationRef$curren3 = (_navigationRef$curren4 = navigationRef.current).getRootState) === null || _navigationRef$curren3 === void 0 ? void 0 : _navigationRef$curren3.call(_navigationRef$curren4);
973
+ if (!state) {
974
+ return undefined;
975
+ }
976
+
977
+ // Navigate through nested navigators to get the deepest route
978
+ var currentState = state;
979
+ while ((_currentState$routes$3 = currentState.routes[currentState.index]) !== null && _currentState$routes$3 !== void 0 && _currentState$routes$3.state) {
980
+ var _currentState$routes$3;
981
+ currentState = currentState.routes[currentState.index].state;
982
+ }
983
+ return (_currentState$routes$4 = currentState.routes[currentState.index]) === null || _currentState$routes$4 === void 0 ? void 0 : _currentState$routes$4.params;
984
+ }, [navigationRef]);
985
+
986
+ /**
987
+ * Build the URL to track
988
+ */
989
+ var buildTrackingUrl = useCallback(function (routeName, params) {
990
+ var url = transformRouteName ? transformRouteName(routeName) : "/".concat(routeName);
991
+ if (includeParams && params && Object.keys(params).length > 0) {
992
+ var queryString = Object.entries(params).filter(function (_ref) {
993
+ var _ref2 = _slicedToArray(_ref, 2);
994
+ _ref2[0];
995
+ var value = _ref2[1];
996
+ return value !== undefined && value !== null;
997
+ }).map(function (_ref3) {
998
+ var _ref4 = _slicedToArray(_ref3, 2),
999
+ key = _ref4[0],
1000
+ value = _ref4[1];
1001
+ return "".concat(encodeURIComponent(key), "=").concat(encodeURIComponent(String(value)));
1002
+ }).join('&');
1003
+ if (queryString) {
1004
+ url += "?".concat(queryString);
1005
+ }
1006
+ }
1007
+ return url;
1008
+ }, [transformRouteName, includeParams]);
1009
+
1010
+ /**
1011
+ * Handle navigation state change
1012
+ */
1013
+ var handleStateChange = useCallback(function () {
1014
+ var currentRouteName = getCurrentRouteName();
1015
+ var previousRouteName = routeNameRef.current;
1016
+ if (currentRouteName && currentRouteName !== previousRouteName) {
1017
+ var params = includeParams ? getCurrentRouteParams() : undefined;
1018
+
1019
+ // Check if this route should be tracked
1020
+ if (shouldTrackRoute && !shouldTrackRoute(currentRouteName, params)) {
1021
+ routeNameRef.current = currentRouteName;
1022
+ return;
1023
+ }
1024
+ var url = buildTrackingUrl(currentRouteName, params);
1025
+ trackPageview === null || trackPageview === void 0 || trackPageview({
1026
+ url: url,
1027
+ referrer: previousRouteName ? buildTrackingUrl(previousRouteName) : undefined
1028
+ });
1029
+ routeNameRef.current = currentRouteName;
1030
+ }
1031
+ }, [getCurrentRouteName, getCurrentRouteParams, shouldTrackRoute, buildTrackingUrl, trackPageview, includeParams]);
1032
+
1033
+ // Track initial route on mount
1034
+ useEffect(function () {
1035
+ // Small delay to ensure navigation is ready
1036
+ var timeout = setTimeout(function () {
1037
+ var initialRoute = getCurrentRouteName();
1038
+ if (initialRoute) {
1039
+ var params = includeParams ? getCurrentRouteParams() : undefined;
1040
+ if (!shouldTrackRoute || shouldTrackRoute(initialRoute, params)) {
1041
+ var url = buildTrackingUrl(initialRoute, params);
1042
+ trackPageview === null || trackPageview === void 0 || trackPageview({
1043
+ url: url
1044
+ });
1045
+ routeNameRef.current = initialRoute;
1046
+ }
1047
+ }
1048
+ }, 0);
1049
+ return function () {
1050
+ return clearTimeout(timeout);
1051
+ };
1052
+ }, []); // Only on mount
1053
+
1054
+ // Set up navigation state change listener
1055
+ useEffect(function () {
1056
+ var _navigationRef$curren5, _navigationRef$curren6;
1057
+ if (!navigationRef.current) {
1058
+ return;
1059
+ }
1060
+
1061
+ // Listen for navigation state changes
1062
+ var unsubscribe = (_navigationRef$curren5 = (_navigationRef$curren6 = navigationRef.current).addListener) === null || _navigationRef$curren5 === void 0 ? void 0 : _navigationRef$curren5.call(_navigationRef$curren6, 'state', handleStateChange);
1063
+ return function () {
1064
+ unsubscribe === null || unsubscribe === void 0 || unsubscribe();
1065
+ };
1066
+ }, [navigationRef, handleStateChange]);
1067
+ }
1068
+
1069
+ export { FathomProvider, FathomWebView, NativeFathomProvider, createWebViewClient, useAppStateTracking, useFathom, useNavigationTracking };
1070
+ /* Copyright 2026 - Ryan Hefner <hi@ryanhefner.com> (https://www.ryanhefner.com) */
1071
+ //# sourceMappingURL=index.js.map