From 1e17b38db656acb3c769a81d1323b23930b33d21 Mon Sep 17 00:00:00 2001 From: Robert Eggl Date: Mon, 16 Dec 2024 17:49:41 +0100 Subject: [PATCH 1/6] feat: add react native web support --- src/client.ts | 13 +++++-- src/dispatcher.spec.ts | 70 ++++++++++++++++++++++++++++++--- src/dispatcher.ts | 88 ++++++++++++++++++++++++++++++++++-------- src/env.ts | 36 +++++++++-------- src/types.d.ts | 4 +- src/validate.spec.ts | 8 +++- src/validate.ts | 6 ++- src/version.ts | 22 +++++++---- 8 files changed, 193 insertions(+), 54 deletions(-) diff --git a/src/client.ts b/src/client.ts index 4ad5b55..ad71092 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,12 +1,12 @@ -import type { Platform } from "react-native"; +import { Platform } from "react-native"; import type { AptabaseOptions } from "./types"; import type { EnvironmentInfo } from "./env"; -import { EventDispatcher } from "./dispatcher"; +import { NativeEventDispatcher, WebEventDispatcher } from "./dispatcher"; import { newSessionId } from "./session"; import { HOSTS, SESSION_TIMEOUT } from "./constants"; export class AptabaseClient { - private readonly _dispatcher: EventDispatcher; + private readonly _dispatcher: WebEventDispatcher | NativeEventDispatcher; private readonly _env: EnvironmentInfo; private _sessionId = newSessionId(); private _lastTouched = new Date(); @@ -21,7 +21,12 @@ export class AptabaseClient { this._env.appVersion = options.appVersion; } - this._dispatcher = new EventDispatcher(appKey, baseUrl, env); + const dispatcher = + Platform.OS === "web" + ? new WebEventDispatcher(appKey, baseUrl, env) + : new NativeEventDispatcher(appKey, baseUrl, env); + + this._dispatcher = dispatcher; } public trackEvent( diff --git a/src/dispatcher.spec.ts b/src/dispatcher.spec.ts index 345b7d6..9c00fde 100644 --- a/src/dispatcher.spec.ts +++ b/src/dispatcher.spec.ts @@ -1,7 +1,7 @@ import "vitest-fetch-mock"; -import { EventDispatcher } from "./dispatcher"; +import { NativeEventDispatcher, WebEventDispatcher } from "./dispatcher"; import { beforeEach, describe, expect, it } from "vitest"; -import { EnvironmentInfo } from "./env"; +import type { EnvironmentInfo } from "./env"; const env: EnvironmentInfo = { isDebug: false, @@ -32,11 +32,11 @@ const expectEventsCount = async ( expect(body.length).toEqual(expectedNumOfEvents); }; -describe("EventDispatcher", () => { - let dispatcher: EventDispatcher; +describe("NativeEventDispatcher", () => { + let dispatcher: NativeEventDispatcher; beforeEach(() => { - dispatcher = new EventDispatcher( + dispatcher = new NativeEventDispatcher( "A-DEV-000", "https://localhost:3000", env @@ -138,3 +138,63 @@ describe("EventDispatcher", () => { expectRequestCount(1); }); }); + +describe("WebEventDispatcher", () => { + let dispatcher: WebEventDispatcher; + + beforeEach(() => { + dispatcher = new WebEventDispatcher( + "A-DEV-000", + "https://localhost:3000", + env + ); + fetchMock.resetMocks(); + }); + + it("should send event with correct headers", async () => { + dispatcher.enqueue(createEvent("app_started")); + + const request = await fetchMock.requests().at(0); + expect(request).not.toBeUndefined(); + expect(request?.url).toEqual("https://localhost:3000/api/v0/event"); + expect(request?.headers.get("Content-Type")).toEqual("application/json"); + expect(request?.headers.get("App-Key")).toEqual("A-DEV-000"); + }); + + it("should dispatch single event", async () => { + fetchMock.mockResponseOnce("{}"); + + dispatcher.enqueue(createEvent("app_started")); + + expectRequestCount(1); + const body = await fetchMock.requests().at(0)?.json(); + expect(body.eventName).toEqual("app_started"); + }); + + it("should dispatch multiple events individually", async () => { + fetchMock.mockResponseOnce("{}"); + fetchMock.mockResponseOnce("{}"); + + dispatcher.enqueue([createEvent("app_started"), createEvent("app_exited")]); + + expectRequestCount(2); + const body1 = await fetchMock.requests().at(0)?.json(); + const body2 = await fetchMock.requests().at(1)?.json(); + expect(body1.eventName).toEqual("app_started"); + expect(body2.eventName).toEqual("app_exited"); + }); + + it("should not retry requests that failed with 4xx", async () => { + fetchMock.mockResponseOnce("{}", { status: 400 }); + + dispatcher.enqueue(createEvent("hello_world")); + + expectRequestCount(1); + const body = await fetchMock.requests().at(0)?.json(); + expect(body.eventName).toEqual("hello_world"); + + dispatcher.enqueue(createEvent("hello_world")); + + expectRequestCount(2); + }); +}); diff --git a/src/dispatcher.ts b/src/dispatcher.ts index 96fc476..046f145 100644 --- a/src/dispatcher.ts +++ b/src/dispatcher.ts @@ -1,11 +1,11 @@ import type { Event } from "./types"; -import { EnvironmentInfo } from "./env"; +import type { EnvironmentInfo } from "./env"; -export class EventDispatcher { - private _events: Event[] = []; - private MAX_BATCH_SIZE = 25; - private headers: Headers; - private apiUrl: string; +export abstract class EventDispatcher { + protected _events: Event[] = []; + protected MAX_BATCH_SIZE = 25; + protected headers: Headers; + protected apiUrl: string; constructor(appKey: string, baseUrl: string, env: EnvironmentInfo) { this.apiUrl = `${baseUrl}/api/v0/events`; @@ -16,14 +16,7 @@ export class EventDispatcher { }); } - public enqueue(evt: Event | Event[]) { - if (Array.isArray(evt)) { - this._events.push(...evt); - return; - } - - this._events.push(evt); - } + public abstract enqueue(evt: Event | Event[]): void; public async flush(): Promise { if (this._events.length === 0) { @@ -45,7 +38,7 @@ export class EventDispatcher { } } - private async _sendEvents(events: Event[]): Promise { + protected async _sendEvents(events: Event[]): Promise { try { const res = await fetch(this.apiUrl, { method: "POST", @@ -54,7 +47,7 @@ export class EventDispatcher { body: JSON.stringify(events), }); - if (res.status < 300) { + if (res.ok) { return Promise.resolve(); } @@ -74,4 +67,67 @@ export class EventDispatcher { throw e; } } + + protected async _sendEvent(event: Event): Promise { + try { + const res = await fetch(this.apiUrl, { + method: "POST", + headers: this.headers, + credentials: "omit", + body: JSON.stringify(event), + }); + + if (res.ok) { + return Promise.resolve(); + } + + const reason = `${res.status} ${await res.text()}`; + if (res.status < 500) { + console.warn( + `Aptabase: Failed to send event because of ${reason}. Will not retry.` + ); + return Promise.resolve(); + } + + throw new Error(reason); + } catch (e) { + console.error(`Aptabase: Failed to send event. Reason: ${e}`); + throw e; + } + } +} + +export class WebEventDispatcher extends EventDispatcher { + constructor(appKey: string, baseUrl: string, env: EnvironmentInfo) { + super(appKey, baseUrl, env); + this.apiUrl = `${baseUrl}/api/v0/event`; + this.headers = new Headers({ + "Content-Type": "application/json", + "App-Key": appKey, + // No User-Agent header for web + }); + } + + public enqueue(evt: Event | Event[]): void { + if (Array.isArray(evt)) { + evt.forEach((event) => this._sendEvent(event)); + } else { + this._sendEvent(evt); + } + } +} + +export class NativeEventDispatcher extends EventDispatcher { + constructor(appKey: string, baseUrl: string, env: EnvironmentInfo) { + super(appKey, baseUrl, env); + this.apiUrl = `${baseUrl}/api/v0/events`; + } + + public enqueue(evt: Event | Event[]): void { + if (Array.isArray(evt)) { + this._events.push(...evt); + } else { + this._events.push(evt); + } + } } diff --git a/src/env.ts b/src/env.ts index 0e62d76..794d06d 100644 --- a/src/env.ts +++ b/src/env.ts @@ -10,8 +10,8 @@ export interface EnvironmentInfo { appVersion: string; appBuildNumber: string; sdkVersion: string; - osName: string; - osVersion: string; + osName: string | undefined; + osVersion: string | undefined; } export function getEnvironmentInfo(): EnvironmentInfo { @@ -19,27 +19,29 @@ export function getEnvironmentInfo(): EnvironmentInfo { const locale = "en-US"; - return { + const envInfo: EnvironmentInfo = { appVersion: version.appVersion, appBuildNumber: version.appBuildNumber, isDebug: __DEV__, locale, - osName, - osVersion, + osName: osName, + osVersion: osVersion, sdkVersion, }; -} -function getOperatingSystem(): [string, string] { - switch (Platform.OS) { - case "android": - return ["Android", Platform.constants.Release]; - case "ios": - if (Platform.isPad) { - return ["iPadOS", Platform.Version]; - } - return ["iOS", Platform.Version]; - default: - return ["", ""]; + return envInfo; + + function getOperatingSystem(): [string, string] { + switch (Platform.OS) { + case "android": + return ["Android", Platform.constants.Release]; + case "ios": + if (Platform.isPad) { + return ["iPadOS", Platform.Version]; + } + return ["iOS", Platform.Version]; + default: + return ["", ""]; + } } } diff --git a/src/types.d.ts b/src/types.d.ts index 910bc0a..bee4798 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -23,8 +23,8 @@ export type Event = { systemProps: { isDebug: boolean; locale: string; - osName: string; - osVersion: string; + osName: string | undefined; + osVersion: string | undefined; appVersion: string; appBuildNumber: string; sdkVersion: string; diff --git a/src/validate.spec.ts b/src/validate.spec.ts index 36a5471..503eb20 100644 --- a/src/validate.spec.ts +++ b/src/validate.spec.ts @@ -21,7 +21,13 @@ describe("Validate", () => { platform: "web" as const, appKey: "A-DEV-000", options: undefined, - expected: [false, "This SDK is only supported on Android and iOS"], + expected: [true, ""], + }, + { + platform: "windows" as const, + appKey: "A-DEV-000", + options: undefined, + expected: [false, "This SDK is only supported on Android, iOS and web"], }, { platform: "ios" as const, diff --git a/src/validate.ts b/src/validate.ts index e561586..540b8ea 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -3,13 +3,15 @@ import { HOSTS } from "./constants"; import type { AptabaseOptions } from "./types"; +const SUPPORTED_PLATFORMS = ["android", "ios", "web"]; + export function validate( platform: typeof Platform.OS, appKey: string, options?: AptabaseOptions ): [boolean, string] { - if (platform !== "android" && platform !== "ios") { - return [false, "This SDK is only supported on Android and iOS"]; + if (!SUPPORTED_PLATFORMS.includes(platform)) { + return [false, "This SDK is only supported on Android, iOS and web"]; } const parts = appKey.split("-"); diff --git a/src/version.ts b/src/version.ts index a66663d..083fb62 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1,15 +1,23 @@ -import { NativeModules } from "react-native"; - -const { RNAptabaseModule } = NativeModules; +import { Platform, NativeModules } from "react-native"; type VersionObject = { appVersion: string; appBuildNumber: string; }; -const Version: VersionObject = { - appVersion: RNAptabaseModule?.appVersion?.toString() ?? "", - appBuildNumber: RNAptabaseModule?.appBuildNumber?.toString() ?? "", -}; +let Version: VersionObject; + +if (Platform.OS === "web") { + Version = { + appVersion: "", // can be manually set in AptabaseOptions + appBuildNumber: "" + }; +} else { + const { RNAptabaseModule } = NativeModules; + Version = { + appVersion: RNAptabaseModule?.appVersion?.toString() ?? "", + appBuildNumber: RNAptabaseModule?.appBuildNumber?.toString() ?? "", + }; +} export default Version; From ac1444063018c6925a9144fea13616fff4abbc5b Mon Sep 17 00:00:00 2001 From: Robert Eggl Date: Sat, 24 May 2025 16:20:51 +0200 Subject: [PATCH 2/6] chore: update dependencies and enhance web tracking support --- README.md | 15 +++++++++++++++ src/client.ts | 23 +++++++++++++++++++---- src/types.d.ts | 3 +++ src/validate.ts | 7 +++++++ src/version.ts | 4 ++-- 5 files changed, 46 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index fe58e95..a35fa3f 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,19 @@ If you're targeting Android, you'll need to add the following permissions to you ``` +## Web Support + +This SDK also supports React Native Web! + +> [!NOTE] +> This feature is disabled by default. To enable it, you need to pass the `enableWeb` option when initializing the SDK. + +```js +Aptabase.init("", { enableWeb: true }); +``` + +When enabled, the SDK will track events in web environments using the same behavior as the web SDKs. Which means that events will be sent immediately to the `/event` endpoint instead of grouped to the `/events` endpoint. + ## Usage First, you need to get your `App Key` from Aptabase, you can find it in the `Instructions` menu on the left side menu. @@ -64,7 +77,9 @@ export function Counter() { ); } ``` + To disable tracking events, you can call the `dispose` function. This will stop and deinitalize the SDK. + ```js import Aptabase from "@aptabase/react-native"; diff --git a/src/client.ts b/src/client.ts index ad71092..4dd22c7 100644 --- a/src/client.ts +++ b/src/client.ts @@ -6,7 +6,10 @@ import { newSessionId } from "./session"; import { HOSTS, SESSION_TIMEOUT } from "./constants"; export class AptabaseClient { - private readonly _dispatcher: WebEventDispatcher | NativeEventDispatcher; + private readonly _dispatcher: + | WebEventDispatcher + | NativeEventDispatcher + | null; private readonly _env: EnvironmentInfo; private _sessionId = newSessionId(); private _lastTouched = new Date(); @@ -21,10 +24,19 @@ export class AptabaseClient { this._env.appVersion = options.appVersion; } - const dispatcher = - Platform.OS === "web" + const isWeb = Platform.OS === "web"; + const isWebTrackingEnabled = isWeb && options?.enableWeb === true; + + console.log("isWebTrackingEnabled", isWebTrackingEnabled); + console.log("isWeb", isWeb); + console.log("options?.enableWeb", options?.enableWeb); + + const shouldEnableTracking = !isWeb || isWebTrackingEnabled; + const dispatcher = shouldEnableTracking + ? isWeb ? new WebEventDispatcher(appKey, baseUrl, env) - : new NativeEventDispatcher(appKey, baseUrl, env); + : new NativeEventDispatcher(appKey, baseUrl, env) + : null; this._dispatcher = dispatcher; } @@ -33,6 +45,8 @@ export class AptabaseClient { eventName: string, props?: Record ) { + if (!this._dispatcher) return; + this._dispatcher.enqueue({ timestamp: new Date().toISOString(), sessionId: this.evalSessionId(), @@ -64,6 +78,7 @@ export class AptabaseClient { } public flush(): Promise { + if (!this._dispatcher) return Promise.resolve(); return this._dispatcher.flush(); } diff --git a/src/types.d.ts b/src/types.d.ts index bee4798..11df99d 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -11,6 +11,9 @@ export type AptabaseOptions = { // Override the default flush interval (in milliseconds) flushInterval?: number; + + // Enable tracking for web platform (disabled by default) + enableWeb?: boolean; }; /** diff --git a/src/validate.ts b/src/validate.ts index 540b8ea..668fe15 100644 --- a/src/validate.ts +++ b/src/validate.ts @@ -26,5 +26,12 @@ export function validate( ]; } + // If platform is web but web tracking is not enabled, log a warning + if (platform === "web" && !options?.enableWeb) { + console.warn( + "Aptabase: Web tracking is disabled by default. Set enableWeb: true in options to enable it." + ); + } + return [true, ""]; } diff --git a/src/version.ts b/src/version.ts index 083fb62..50b2965 100644 --- a/src/version.ts +++ b/src/version.ts @@ -9,8 +9,8 @@ let Version: VersionObject; if (Platform.OS === "web") { Version = { - appVersion: "", // can be manually set in AptabaseOptions - appBuildNumber: "" + appVersion: "", // can be overrided in AptabaseOptions + appBuildNumber: "", }; } else { const { RNAptabaseModule } = NativeModules; From 77e5c44b579e3f2743c7d46e330899d3e8c99b1b Mon Sep 17 00:00:00 2001 From: Robert Eggl Date: Sat, 24 May 2025 16:24:57 +0200 Subject: [PATCH 3/6] docs: clarify web environment handling in getEnvironmentInfo function --- src/env.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/env.ts b/src/env.ts index 794d06d..3b958af 100644 --- a/src/env.ts +++ b/src/env.ts @@ -40,6 +40,7 @@ export function getEnvironmentInfo(): EnvironmentInfo { return ["iPadOS", Platform.Version]; } return ["iOS", Platform.Version]; + // Web needs to be empty as the server computes these values using the user agent default: return ["", ""]; } From 358a9177a116cffbdfb57712832e27a508bb3b1c Mon Sep 17 00:00:00 2001 From: Robert Eggl Date: Sat, 24 May 2025 16:54:57 +0200 Subject: [PATCH 4/6] test: enhance tracking tests for web and native environments --- src/client.spec.ts | 75 ++++++++++++++++++++++++++++++++++++++++++++++ src/client.ts | 9 +++--- src/env.ts | 3 +- 3 files changed, 82 insertions(+), 5 deletions(-) diff --git a/src/client.spec.ts b/src/client.spec.ts index b2b79dd..4c4ead9 100644 --- a/src/client.spec.ts +++ b/src/client.spec.ts @@ -119,4 +119,79 @@ describe("AptabaseClient", () => { expect(sessionId3).toBeDefined(); expect(sessionId3).not.toBe(sessionId1); }); + + describe("Web tracking", () => { + const webEnv: EnvironmentInfo = { + ...env, + osName: "web", + osVersion: "web", + }; + + it("should not track events when web tracking is disabled", async () => { + const client = new AptabaseClient("A-DEV-000", webEnv); + client.trackEvent("test_event"); + await client.flush(); + expect(fetchMock.requests().length).toEqual(0); + }); + + it("should track events when web tracking is enabled", async () => { + const client = new AptabaseClient("A-DEV-000", webEnv, { + enableWeb: true, + }); + client.trackEvent("test_event"); + await client.flush(); + expect(fetchMock.requests().length).toEqual(1); + const body = await fetchMock.requests().at(0)?.json(); + expect(body.eventName).toEqual("test_event"); + expect(body.systemProps.osName).toBeUndefined(); + expect(body.systemProps.osVersion).toBeUndefined(); + }); + + it("should use correct endpoint for web events", async () => { + const client = new AptabaseClient("A-DEV-000", webEnv, { + enableWeb: true, + }); + client.trackEvent("test_event"); + await client.flush(); + const request = fetchMock.requests().at(0); + expect(request?.url).toContain("/api/v0/event"); + }); + + it("should use correct endpoint for native events", async () => { + const client = new AptabaseClient("A-DEV-000", env); + client.trackEvent("test_event"); + await client.flush(); + const request = fetchMock.requests().at(0); + expect(request?.url).toContain("/api/v0/events"); + }); + }); + + describe("Native tracking", () => { + it("should track events on iOS", async () => { + const client = new AptabaseClient("A-DEV-000", env); + client.trackEvent("test_event"); + await client.flush(); + expect(fetchMock.requests().length).toEqual(1); + const body = await fetchMock.requests().at(0)?.json(); + expect(body[0].eventName).toEqual("test_event"); + expect(body[0].systemProps.osName).toEqual("iOS"); + expect(body[0].systemProps.osVersion).toEqual("14.3"); + }); + + it("should track events on Android", async () => { + const androidEnv: EnvironmentInfo = { + ...env, + osName: "Android", + osVersion: "13", + }; + const client = new AptabaseClient("A-DEV-000", androidEnv); + client.trackEvent("test_event"); + await client.flush(); + expect(fetchMock.requests().length).toEqual(1); + const body = await fetchMock.requests().at(0)?.json(); + expect(body[0].eventName).toEqual("test_event"); + expect(body[0].systemProps.osName).toEqual("Android"); + expect(body[0].systemProps.osVersion).toEqual("13"); + }); + }); }); diff --git a/src/client.ts b/src/client.ts index 4dd22c7..17d0462 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,4 +1,3 @@ -import { Platform } from "react-native"; import type { AptabaseOptions } from "./types"; import type { EnvironmentInfo } from "./env"; import { NativeEventDispatcher, WebEventDispatcher } from "./dispatcher"; @@ -24,7 +23,7 @@ export class AptabaseClient { this._env.appVersion = options.appVersion; } - const isWeb = Platform.OS === "web"; + const isWeb = this._env.osName === "web"; const isWebTrackingEnabled = isWeb && options?.enableWeb === true; console.log("isWebTrackingEnabled", isWebTrackingEnabled); @@ -47,6 +46,8 @@ export class AptabaseClient { ) { if (!this._dispatcher) return; + const isWeb = this._env.osName === "web"; + this._dispatcher.enqueue({ timestamp: new Date().toISOString(), sessionId: this.evalSessionId(), @@ -54,8 +55,8 @@ export class AptabaseClient { systemProps: { isDebug: this._env.isDebug, locale: this._env.locale, - osName: this._env.osName, - osVersion: this._env.osVersion, + osName: isWeb ? undefined : this._env.osName, + osVersion: isWeb ? undefined : this._env.osVersion, appVersion: this._env.appVersion, appBuildNumber: this._env.appBuildNumber, sdkVersion: this._env.sdkVersion, diff --git a/src/env.ts b/src/env.ts index 3b958af..477a244 100644 --- a/src/env.ts +++ b/src/env.ts @@ -40,7 +40,8 @@ export function getEnvironmentInfo(): EnvironmentInfo { return ["iPadOS", Platform.Version]; } return ["iOS", Platform.Version]; - // Web needs to be empty as the server computes these values using the user agent + case "web": + return ["web", ""]; default: return ["", ""]; } From 8f9b27116f722d4a26a52b4151863cb765756d6c Mon Sep 17 00:00:00 2001 From: Robert Eggl Date: Sat, 24 May 2025 16:57:37 +0200 Subject: [PATCH 5/6] refactor: remove debug logging for web tracking in AptabaseClient --- src/client.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/client.ts b/src/client.ts index 17d0462..150bf4c 100644 --- a/src/client.ts +++ b/src/client.ts @@ -26,10 +26,6 @@ export class AptabaseClient { const isWeb = this._env.osName === "web"; const isWebTrackingEnabled = isWeb && options?.enableWeb === true; - console.log("isWebTrackingEnabled", isWebTrackingEnabled); - console.log("isWeb", isWeb); - console.log("options?.enableWeb", options?.enableWeb); - const shouldEnableTracking = !isWeb || isWebTrackingEnabled; const dispatcher = shouldEnableTracking ? isWeb From 4903197ec1ded23d9a07d99e84443e1be49f3dd7 Mon Sep 17 00:00:00 2001 From: Robert Eggl Date: Tue, 16 Jun 2026 19:58:18 +0200 Subject: [PATCH 6/6] fix: harden web tracking and skip native polling on web Prevent unhandled rejections from fire-and-forget web sends, only run batch flush polling for the native dispatcher, and document appVersion for React Native Web in the README. Co-authored-by: Cursor --- README.md | 7 +++++-- src/client.ts | 4 ++++ src/dispatcher.ts | 8 ++++++-- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index a35fa3f..f39b947 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,10 @@ This SDK also supports React Native Web! > This feature is disabled by default. To enable it, you need to pass the `enableWeb` option when initializing the SDK. ```js -Aptabase.init("", { enableWeb: true }); +Aptabase.init("", { + enableWeb: true, + appVersion: "1.0.0", // required on web — no native module provides it +}); ``` When enabled, the SDK will track events in web environments using the same behavior as the web SDKs. Which means that events will be sent immediately to the `/event` endpoint instead of grouped to the `/events` endpoint. @@ -78,7 +81,7 @@ export function Counter() { } ``` -To disable tracking events, you can call the `dispose` function. This will stop and deinitalize the SDK. +To disable tracking events, you can call the `dispose` function. This will stop and deinitialize the SDK. ```js import Aptabase from "@aptabase/react-native"; diff --git a/src/client.ts b/src/client.ts index 150bf4c..c6c178c 100644 --- a/src/client.ts +++ b/src/client.ts @@ -62,6 +62,10 @@ export class AptabaseClient { } public startPolling(flushInterval: number) { + if (!(this._dispatcher instanceof NativeEventDispatcher)) { + return; + } + this.stopPolling(); this._flushTimer = setInterval(this.flush.bind(this), flushInterval); diff --git a/src/dispatcher.ts b/src/dispatcher.ts index 046f145..f29aee6 100644 --- a/src/dispatcher.ts +++ b/src/dispatcher.ts @@ -110,11 +110,15 @@ export class WebEventDispatcher extends EventDispatcher { public enqueue(evt: Event | Event[]): void { if (Array.isArray(evt)) { - evt.forEach((event) => this._sendEvent(event)); + evt.forEach((event) => this.dispatchEvent(event)); } else { - this._sendEvent(evt); + this.dispatchEvent(evt); } } + + private dispatchEvent(event: Event): void { + void this._sendEvent(event).catch(() => undefined); + } } export class NativeEventDispatcher extends EventDispatcher {