klaritics-react-native-sdk 1.0.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.
package/package.json ADDED
@@ -0,0 +1,65 @@
1
+ {
2
+ "name": "klaritics-react-native-sdk",
3
+ "version": "1.0.0",
4
+ "description": "React Native Klaritics SDK",
5
+ "homepage": "https://deeptaai.com/",
6
+ "keywords": [
7
+ "react-native",
8
+ "ios",
9
+ "android"
10
+ ],
11
+ "license": "UNLICENSED",
12
+ "author": {
13
+ "name": "Kousik Dasari",
14
+ "email": "kousik.d@deeptaai.com"
15
+ },
16
+ "main": "lib/commonjs/index.js",
17
+ "module": "lib/module/index.js",
18
+ "types": "lib/typescript/index.d.ts",
19
+ "react-native": "src/index.tsx",
20
+ "files": [
21
+ "src",
22
+ "lib",
23
+ "android",
24
+ "ios",
25
+ "cpp",
26
+ "react-native-anthra-sdk.podspec",
27
+ "!android/build",
28
+ "!android/bin",
29
+ "!android/.settings",
30
+ "!android/.gradle",
31
+ "!android/.project",
32
+ "!ios/build",
33
+ "!**/__tests__",
34
+ "!**/__fixtures__",
35
+ "!**/__mocks__"
36
+ ],
37
+ "scripts": {
38
+ "lint": "eslint . --ext .ts,.tsx,.js,.jsx",
39
+ "build": "bob build"
40
+ },
41
+ "devDependencies": {
42
+ "@types/react": "^18.3.3",
43
+ "@types/react-native": "^0.73.0",
44
+ "eslint": "^8.57.0",
45
+ "eslint-config-custom": "workspace:*",
46
+ "metro-react-native-babel-preset": "^0.77.0",
47
+ "react": "^18.3.1",
48
+ "react-native": "^0.74.3",
49
+ "react-native-builder-bob": "^0.24.0",
50
+ "tsconfig": "workspace:*",
51
+ "typescript": "^5.5.3"
52
+ },
53
+ "peerDependencies": {
54
+ "react": "*",
55
+ "react-native": "*"
56
+ },
57
+ "peerDependenciesMeta": {
58
+ "react": {
59
+ "optional": false
60
+ },
61
+ "react-native": {
62
+ "optional": false
63
+ }
64
+ }
65
+ }
@@ -0,0 +1,21 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "react-native-anthra-sdk"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => "11.0" }
14
+ s.source = { :git => "https://github.com/udaykoushik/react-native-anthra-sdk.git", :tag => "#{s.version}" }
15
+
16
+ s.source_files = "ios/**/*.{h,m,mm}"
17
+
18
+ s.dependency "React-Core"
19
+ s.dependency 'AnthraSDK'
20
+
21
+ end
package/src/index.tsx ADDED
@@ -0,0 +1,397 @@
1
+ import { useEffect, useMemo, type ReactNode } from 'react'
2
+ import { NativeModules, View, ViewProps } from 'react-native'
3
+ import { getTag, pruneAttributes } from './utils'
4
+
5
+ const { Klaritics } = NativeModules
6
+
7
+ interface KlariticsOptions {
8
+ /**
9
+ * Server URL to send data to, e.g. `https://server.example.klaritics.com`
10
+ * (no trailing slash). Required.
11
+ */
12
+ host: string
13
+ }
14
+
15
+ interface KlariticsProviderProps {
16
+ /**
17
+ * A unique application key obtained from the Klaritics dashboard.
18
+ */
19
+ apiKey: string
20
+ options: KlariticsOptions
21
+ children?: ReactNode
22
+ }
23
+
24
+ /**
25
+ *
26
+ * Initialise the Klaritics SDK. Maps to the native
27
+ * `Klaritics.setup(context, new KlariticsConfig(appId, host))`.
28
+ *
29
+ * @param {string} apiKey - A unique application key from the Klaritics dashboard.
30
+ * @param {KlariticsOptions} options - SDK options. `host` is required.
31
+ *
32
+ * @example
33
+ * setup('<klaritics_app_id>', { host: 'https://us.i.klaritics.com' })
34
+ *
35
+ */
36
+ export function setup(apiKey: string, options: KlariticsOptions) {
37
+ if (apiKey === undefined || apiKey === null || apiKey.trim() === '') {
38
+ console.error('apiKey cannot be null or empty')
39
+ return
40
+ }
41
+ if (!options || !options.host || options.host.trim() === '') {
42
+ console.error('options.host cannot be null or empty')
43
+ return
44
+ }
45
+
46
+ Klaritics.setup(apiKey, options.host)
47
+ }
48
+
49
+ /**
50
+ *
51
+ * Provider that initialises the Klaritics SDK when mounted and renders its
52
+ * children. Place it near the root of your app.
53
+ *
54
+ * @example
55
+ * <KlariticsProvider
56
+ * apiKey='<klaritics_app_id>'
57
+ * options={{ host: 'https://us.i.klaritics.com' }}
58
+ * >
59
+ * <MyComponent />
60
+ * </KlariticsProvider>
61
+ *
62
+ */
63
+ export function KlariticsProvider({
64
+ apiKey,
65
+ options,
66
+ children,
67
+ }: KlariticsProviderProps) {
68
+ /**
69
+ * **NOTE**: Disable exhaustive-deps rule as the SDK should be set up only
70
+ * once, when the provider first mounts.
71
+ */
72
+ // eslint-disable-next-line react-hooks/exhaustive-deps
73
+ useEffect(() => setup(apiKey, options), [])
74
+
75
+ return <>{children}</>
76
+ }
77
+
78
+ interface ApxorViewProps extends ViewProps {
79
+ tag?: string
80
+ }
81
+
82
+ /**
83
+ * A wrapper over React Native's `View` component to track the View changes in the app.
84
+ *
85
+ * **NOTE**:
86
+ * 1. Fundamentally, tags are meant to be constant and unique.
87
+ * 2. Even if they change at runtime, ApxorSDK ignores them.
88
+ *
89
+ * @example
90
+ * <ApxorView
91
+ * tag='<unique_constant_tag>'
92
+ * {...otherViewProps}
93
+ * >
94
+ * // Your code goes here
95
+ * </ApxorView>
96
+ *
97
+ */
98
+ export function ApxorView({ children, tag = '', ...others }: ApxorViewProps) {
99
+ /**
100
+ * **NOTE**: Disable exhaustive-deps rule as we don't want to recompute the `nativeID`.
101
+ */
102
+
103
+ // eslint-disable-next-line react-hooks/exhaustive-deps
104
+ const nativeID = useMemo(() => `tag_${getTag(tag)}`, [])
105
+
106
+ return (
107
+ <View
108
+ nativeID={nativeID}
109
+ {...others}
110
+ >
111
+ {children}
112
+ </View>
113
+ )
114
+ }
115
+
116
+ /**
117
+ *
118
+ * Set a Unique ID for each user. A User ID can be email, phone number etc.
119
+ * @param {string} userId - A Unique ID.
120
+ *
121
+ * @example
122
+ * RNApxorSDK.setUserIdentifier("john@doe.com")
123
+ *
124
+ */
125
+ export function setUserIdentifier(userId: string) {
126
+ if (userId === undefined || userId === null || userId.trim() === '') {
127
+ console.error('User Id cannot be null or empty')
128
+ return
129
+ }
130
+ Klaritics.setUserIdentifier(userId)
131
+ }
132
+
133
+ /**
134
+ *
135
+ * Set attributes with which we can identify and group users. User attributes can be gender, city, LoginMode/SignupMode etc.
136
+ * @param {object} userAttributes - User attributes in the form of key-value pairs.
137
+ *
138
+ * @example
139
+ * RNApxorSDK.setUserCustomInfo({
140
+ * gender: "Male",
141
+ * city: "New York",
142
+ * })
143
+ *
144
+ */
145
+ export function setUserCustomInfo(userAttributes: object) {
146
+
147
+ Klaritics.setUserCustomInfo(pruneAttributes(userAttributes))
148
+ }
149
+
150
+ /**
151
+ *
152
+ * Logs an app event.
153
+ * @param {string} eventName - Name of the event.
154
+ * @param {object|null} [attributes=null] - Attributes of the event in the form of key-value pairs.
155
+ * @param {boolean} [isAggregate=false] - Whether the event is an aggregate event or not. Defaults to `false`.
156
+ *
157
+ * @example
158
+ * RNApxorSDK.logAppEvent('ADD_TO_CART', {
159
+ * userId: 'john@doe.com',
160
+ * value: 1299,
161
+ * item: 'Sony Head Phones 1201',
162
+ * })
163
+ *
164
+ */
165
+ export function logAppEvent(
166
+ eventName: string,
167
+ attributes: object | null = null,
168
+ isAggregate: boolean = false,
169
+ ) {
170
+ if (
171
+ eventName === undefined ||
172
+ eventName === null ||
173
+ eventName.trim() === ''
174
+ ) {
175
+ console.error('Event name cannot be null or empty')
176
+ return
177
+ }
178
+
179
+ Klaritics.logAppEvent(eventName, pruneAttributes(attributes), isAggregate)
180
+ }
181
+
182
+ /**
183
+ * Logs an aggregate event.
184
+ * @param {string} eventName - Name of the event.
185
+ * @param {object} attributes - Attributes of the event in the form of key-value pairs.
186
+ *
187
+ * @example
188
+ * RNApxorSDK.logAggregateEvent("Login", {
189
+ * loginMode: "Google",
190
+ * id: "46Juzcyx",
191
+ * })
192
+ *
193
+ */
194
+ export function logAggregateEvent(eventName: string, attributes: object) {
195
+ if (
196
+ eventName === undefined ||
197
+ eventName === null ||
198
+ eventName.trim() === ''
199
+ ) {
200
+ console.error('Event name cannot be null or empty')
201
+ return
202
+ }
203
+
204
+ Klaritics.logAppEvent(eventName, pruneAttributes(attributes), true)
205
+ }
206
+
207
+ /**
208
+ * Logs a client event.
209
+ * @param {string} eventName - Name of the event.
210
+ * @param {object|null} [attributes=null] - Attributes of the event in the form of key-value pairs.
211
+ *
212
+ * @example
213
+ * RNApxorSDK.logClientEvent("SoftBackPressed", {
214
+ * Screen: "HomeScreen",
215
+ * })
216
+ *
217
+ */
218
+ export function logClientEvent(
219
+ eventName: string,
220
+ attributes: object | null = null,
221
+ ) {
222
+ if (
223
+ eventName === undefined ||
224
+ eventName === null ||
225
+ eventName.trim() === ''
226
+ ) {
227
+ console.error('Event name cannot be null or empty')
228
+ return
229
+ }
230
+
231
+ Klaritics.logClientEvent(eventName, pruneAttributes(attributes))
232
+ }
233
+
234
+ /**
235
+ *
236
+ * Set session attributes.
237
+ * @param {object} sessionAttributes - Session attributes in the form of key-value pairs.
238
+ *
239
+ * @example
240
+ * RNApxorSDK.setSessionCustomInfo({
241
+ * network: "4G",
242
+ * })
243
+ *
244
+ */
245
+ export function setSessionCustomInfo(sessionAttributes: object) {
246
+
247
+ Klaritics.setSessionCustomInfo(pruneAttributes(sessionAttributes))
248
+ }
249
+
250
+ /**
251
+ * Logs a navigation event.
252
+ * @param {string} screenName - Name of the screen.
253
+ *
254
+ * **NOTE** : Only use this method on every screen change callback if you are not using the [this snippet for automatic screen tracking](https://guides.apxor.com/getting-started-with-apxor/api-guides/react-native#track-navigation).
255
+ *
256
+ * @example
257
+ * RNApxorSDK.logNavigationEvent("HomeScreen")
258
+ *
259
+ */
260
+ export function logNavigationEvent(screenName: string) {
261
+ if (
262
+ screenName === undefined ||
263
+ screenName === null ||
264
+ screenName.trim() === ''
265
+ ) {
266
+ console.error('Screen name cannot be null or empty')
267
+ return
268
+ }
269
+
270
+ Klaritics.logNavigationEvent(screenName)
271
+ }
272
+
273
+ /**
274
+ *
275
+ * Track tabs inside your activity to analyze time spent on each tab.
276
+ * @param {string} screenName - Name of the screen.
277
+ *
278
+ * @example
279
+ * RNApxorSDK.trackScreen("SettingsScreen")
280
+ *
281
+ */
282
+ export function trackScreen(screenName: string) {
283
+ if (
284
+ screenName === undefined ||
285
+ screenName === null ||
286
+ screenName.trim() === ''
287
+ ) {
288
+ console.error('Screen name cannot be null or empty')
289
+ return
290
+ }
291
+
292
+ Klaritics.trackScreen(screenName)
293
+ }
294
+
295
+ /**
296
+ *
297
+ * Handle push notifications.
298
+ * @param {any|null} [remoteMessage=null] - Remote message object.
299
+ * @returns {boolean} - Returns `true` if the message is an APX push notification, `false` otherwise.
300
+ *
301
+ * @example
302
+ * RNApxorSDK.handlePushNotification(remoteMessage)
303
+ *
304
+ */
305
+ export function handlePushNotification(
306
+ remoteMessage: any | null = null,
307
+ ): boolean {
308
+ if (
309
+ remoteMessage &&
310
+ remoteMessage.data &&
311
+ Boolean(remoteMessage.data.is_apx)
312
+ ) {
313
+ Klaritics.handlePushNotification(remoteMessage.data)
314
+ return true
315
+ }
316
+ return false
317
+ }
318
+
319
+ /**
320
+ *
321
+ * Launch an event using native device emitter when a registered notification occurs.
322
+ * @param {string} notification - Name of the notification.
323
+ * @param {string} key - Key of the notification.
324
+ *
325
+ * @example
326
+ * RNApxorSDK.registerSimpleNotification("Notification", "key")
327
+ *
328
+ */
329
+ export function registerSimpleNotification(notification: string, key: string) {
330
+ if (notification == null || key == null) {
331
+ console.error('Notification, key must not be null')
332
+ return
333
+ }
334
+ console.log('notification recieved')
335
+
336
+ Klaritics.registerSimpleNotification(notification, key)
337
+ }
338
+
339
+ /**
340
+ *
341
+ * Launch a callback for a specified notificaion when it is removed.
342
+ * @param {string} notification - Name of the notification.
343
+ * @param {string} key - Key of the notification.
344
+ *
345
+ * @example
346
+ * RNApxorSDK.unregisterSimpleNotification("Notification", "key")
347
+ *
348
+ */
349
+ export function unregisterSimpleNotification(
350
+ notification: string,
351
+ key: string,
352
+ ) {
353
+ if (notification == null || key == null) {
354
+ console.error('Notification, key must not be null')
355
+ return
356
+ }
357
+
358
+ Klaritics.unregisterSimpleNotification(notification, key)
359
+ }
360
+
361
+ /**
362
+ *
363
+ * Provide key and value of a config option that will be used by the ApxorSDK to apply dynamic changes to campaign.
364
+ * @param {string} key - Key of the config.
365
+ * @param {string} value - Value of the config.
366
+ *
367
+ * @example
368
+ * RNApxorSDK.setApxorDynamicConfig("key", "value")
369
+ *
370
+ */
371
+ export function setApxorDynamicConfig(key: string, value: string) {
372
+ if (key == null || value == null) {
373
+ console.error('Key and Value must not be null')
374
+ return
375
+ }
376
+
377
+ Klaritics.setApxorDynamicConfig(key, value)
378
+ }
379
+
380
+ // Default export is an object with all the named exports (functions and components)
381
+ export default {
382
+ KlariticsProvider,
383
+ setup,
384
+ ApxorView,
385
+ setUserIdentifier,
386
+ setUserCustomInfo,
387
+ logAppEvent,
388
+ logAggregateEvent,
389
+ logClientEvent,
390
+ setSessionCustomInfo,
391
+ logNavigationEvent,
392
+ trackScreen,
393
+ handlePushNotification,
394
+ registerSimpleNotification,
395
+ unregisterSimpleNotification,
396
+ setApxorDynamicConfig,
397
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,44 @@
1
+ /**
2
+ * A utility function to get the formatted tag name from the given string.
3
+ * @param {string} tag Tag Name
4
+ * @returns Formatted Tag Name
5
+ */
6
+ export function getTag(tag: string) {
7
+ return tag.toLowerCase().replace(/[\s]+/g, '_')
8
+ }
9
+
10
+ /**
11
+ * A utility function to check if the given value is valid.
12
+ * @param aValue Value to check
13
+ * @returns `true` if valid, `false` otherwise
14
+ */
15
+ export function isValidValue(aValue: unknown) {
16
+ if (typeof aValue === 'number' && (isNaN(aValue) || !isFinite(aValue))) {
17
+ return false
18
+ } else if (aValue === undefined || aValue == null) {
19
+ return false
20
+ }
21
+ return true
22
+ }
23
+
24
+ /**
25
+ * A utility function to prune null, NaN, undefined or Inifinity values from an object.
26
+ * @param {any} obj Object to prune
27
+ * @returns The Pruned object
28
+ */
29
+ export function pruneAttributes(obj: any) {
30
+ if (typeof obj !== 'object') {
31
+ return {}
32
+ }
33
+ for (const key in obj) {
34
+ if (typeof obj[key] === 'object') {
35
+ obj[key] = pruneAttributes(obj[key]) // Recurse on nested object
36
+ if (Object.keys(obj[key]).length === 0) {
37
+ delete obj[key] // Remove empty object
38
+ }
39
+ } else if (!isValidValue(obj[key])) {
40
+ delete obj[key] // remove null, NaN, undefined or Inifinity
41
+ }
42
+ }
43
+ return obj
44
+ }