com-tapp-so-sdk 0.1.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 (35) hide show
  1. package/ComTappSoSdk.podspec +50 -0
  2. package/LICENSE +20 -0
  3. package/README.md +263 -0
  4. package/android/build.gradle +114 -0
  5. package/android/gradle.properties +5 -0
  6. package/android/src/main/AndroidManifest.xml +2 -0
  7. package/android/src/main/java/com/comtappsosdk/ComTappSoSdkModule.kt +354 -0
  8. package/android/src/main/java/com/comtappsosdk/ComTappSoSdkPackage.kt +45 -0
  9. package/ios/ComTappSoSdk.h +62 -0
  10. package/ios/ComTappSoSdk.mm +343 -0
  11. package/ios/generated/ComTappSoSdkSpec/ComTappSoSdkSpec-generated.mm +81 -0
  12. package/ios/generated/ComTappSoSdkSpec/ComTappSoSdkSpec.h +84 -0
  13. package/ios/generated/ComTappSoSdkSpecJSI-generated.cpp +76 -0
  14. package/ios/generated/ComTappSoSdkSpecJSI.h +125 -0
  15. package/lib/module/NativeComTappSoSdk.js +5 -0
  16. package/lib/module/NativeComTappSoSdk.js.map +1 -0
  17. package/lib/module/Types.js +47 -0
  18. package/lib/module/Types.js.map +1 -0
  19. package/lib/module/events.js +17 -0
  20. package/lib/module/events.js.map +1 -0
  21. package/lib/module/index.js +58 -0
  22. package/lib/module/index.js.map +1 -0
  23. package/lib/typescript/src/NativeComTappSoSdk.d.ts +37 -0
  24. package/lib/typescript/src/NativeComTappSoSdk.d.ts.map +1 -0
  25. package/lib/typescript/src/Types.d.ts +145 -0
  26. package/lib/typescript/src/Types.d.ts.map +1 -0
  27. package/lib/typescript/src/events.d.ts +10 -0
  28. package/lib/typescript/src/events.d.ts.map +1 -0
  29. package/lib/typescript/src/index.d.ts +13 -0
  30. package/lib/typescript/src/index.d.ts.map +1 -0
  31. package/package.json +169 -0
  32. package/src/NativeComTappSoSdk.ts +46 -0
  33. package/src/Types.ts +149 -0
  34. package/src/events.ts +21 -0
  35. package/src/index.tsx +81 -0
@@ -0,0 +1,354 @@
1
+ package com.comtappsosdk
2
+
3
+ import android.util.Log
4
+ import com.example.tapp.Tapp
5
+ import com.example.tapp.models.Affiliate
6
+ import com.example.tapp.models.Environment
7
+ import com.example.tapp.services.affiliate.tapp.DeferredLinkDelegate
8
+ import com.example.tapp.services.network.RequestModels
9
+ import com.example.tapp.utils.Logger
10
+ import com.example.tapp.utils.TappConfiguration
11
+ import com.facebook.react.bridge.Arguments
12
+ import com.facebook.react.bridge.Promise
13
+ import com.facebook.react.bridge.ReactApplicationContext
14
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
15
+ import com.facebook.react.bridge.ReactMethod
16
+ import com.facebook.react.bridge.ReadableMap
17
+ import com.facebook.react.bridge.ReadableType
18
+ import com.facebook.react.bridge.WritableMap
19
+ import com.facebook.react.module.annotations.ReactModule
20
+ import com.facebook.react.modules.core.DeviceEventManagerModule
21
+ import com.facebook.react.turbomodule.core.interfaces.TurboModule
22
+ import kotlinx.coroutines.CoroutineScope
23
+ import kotlinx.coroutines.Dispatchers
24
+ import kotlinx.coroutines.launch
25
+ import kotlinx.coroutines.withContext
26
+
27
+ @ReactModule(name = ComTappSoSdkModule.NAME)
28
+ class ComTappSoSdkModule(reactContext: ReactApplicationContext) :
29
+ ReactContextBaseJavaModule(reactContext), TurboModule{
30
+
31
+ companion object {
32
+ const val NAME = "ComTappSoSdk"
33
+ }
34
+
35
+ private lateinit var tapp: Tapp
36
+
37
+ init {
38
+ Log.i(NAME, "IS_NEW_ARCHITECTURE_ENABLED: ${BuildConfig.IS_NEW_ARCHITECTURE_ENABLED}")
39
+ }
40
+
41
+ override fun getName(): String = NAME
42
+
43
+
44
+ @ReactMethod
45
+ fun start(authToken: String, env: String, tappToken: String) {
46
+ if (!::tapp.isInitialized) {
47
+ tapp = Tapp(reactApplicationContext)
48
+ // Set the deferred link delegate to emit events to JS.
49
+ tapp.deferredLinkDelegate = object : DeferredLinkDelegate {
50
+ override fun didFailResolvingUrl(response: RequestModels.FailResolvingUrlResponse) {
51
+ didFailResolvingURL(response)
52
+ }
53
+
54
+ override fun didReceiveDeferredLink(linkDataResponse: RequestModels.TappLinkDataResponse) {
55
+ sendDeferredLinkEvent(linkDataResponse)
56
+ }
57
+
58
+ override fun testListener(test: String) {
59
+ sendTestEvent(test)
60
+ //Logger.logInfo("Test listener triggered with test: $test");
61
+ }
62
+ }
63
+ }
64
+ // Map environment string to Environment enum
65
+ val environment = when (env.uppercase()) {
66
+ "SANDBOX" -> Environment.SANDBOX
67
+ "PRODUCTION" -> Environment.PRODUCTION
68
+ else -> throw IllegalArgumentException("Invalid environment: $env")
69
+ }
70
+ // Map affiliate string to Affiliate enum
71
+ val affiliateEnum = when ("TAPP_NATIVE") {
72
+ "TAPP_NATIVE" -> Affiliate.TAPP_NATIVE
73
+ else -> throw IllegalArgumentException("Invalid affiliate")
74
+ }
75
+ val config = TappConfiguration(authToken, environment, tappToken, affiliateEnum)
76
+ tapp.start(config)
77
+ }
78
+
79
+ @ReactMethod
80
+ fun url(influencer: String, adGroup: String?, creative: String?, data: ReadableMap?, promise: Promise?) {
81
+ if (!::tapp.isInitialized) {
82
+ tapp = Tapp(reactApplicationContext)
83
+ }
84
+ val dataMap = data?.toHashMap()?.mapValues { it.value.toString() } ?: emptyMap()
85
+ CoroutineScope(Dispatchers.IO).launch {
86
+ try {
87
+ val result = tapp.url(influencer, adGroup, creative, dataMap)
88
+ if (result.error) {
89
+ promise?.resolve(result.message)
90
+ } else {
91
+ promise?.resolve(result.influencer_url)
92
+ }
93
+ } catch (e: Exception) {
94
+ promise?.reject("URL_ERROR", e.message ?: "An unexpected error occurred")
95
+ }
96
+ }
97
+ }
98
+
99
+ @ReactMethod
100
+ fun handleTappEvent(
101
+ eventAction: Double,
102
+ customValue: String?,
103
+ metadata: ReadableMap?,
104
+ promise: Promise
105
+ ) {
106
+ if (!::tapp.isInitialized) {
107
+ tapp = Tapp(reactApplicationContext)
108
+ }
109
+
110
+ try {
111
+ val action = when (eventAction.toInt()) {
112
+ 0 -> RequestModels.EventAction.custom(customValue ?: "custom_event_autogenerated")
113
+ 1 -> RequestModels.EventAction.tapp_add_payment_info
114
+ 2 -> RequestModels.EventAction.tapp_add_to_cart
115
+ 3 -> RequestModels.EventAction.tapp_add_to_wishlist
116
+ 4 -> RequestModels.EventAction.tapp_complete_registration
117
+ 5 -> RequestModels.EventAction.tapp_contact
118
+ 6 -> RequestModels.EventAction.tapp_customize_product
119
+ 7 -> RequestModels.EventAction.tapp_donate
120
+ 8 -> RequestModels.EventAction.tapp_find_location
121
+ 9 -> RequestModels.EventAction.tapp_initiate_checkout
122
+ 10 -> RequestModels.EventAction.tapp_generate_lead
123
+ 11 -> RequestModels.EventAction.tapp_purchase
124
+ 12 -> RequestModels.EventAction.tapp_schedule
125
+ 13 -> RequestModels.EventAction.tapp_search
126
+ 14 -> RequestModels.EventAction.tapp_start_trial
127
+ 15 -> RequestModels.EventAction.tapp_submit_application
128
+ 16 -> RequestModels.EventAction.tapp_subscribe
129
+ 17 -> RequestModels.EventAction.tapp_view_content
130
+ 18 -> RequestModels.EventAction.tapp_click_button
131
+ 19 -> RequestModels.EventAction.tapp_download_file
132
+ 20 -> RequestModels.EventAction.tapp_join_group
133
+ 21 -> RequestModels.EventAction.tapp_achieve_level
134
+ 22 -> RequestModels.EventAction.tapp_create_group
135
+ 23 -> RequestModels.EventAction.tapp_create_role
136
+ 24 -> RequestModels.EventAction.tapp_link_click
137
+ 25 -> RequestModels.EventAction.tapp_link_impression
138
+ 26 -> RequestModels.EventAction.tapp_apply_for_loan
139
+ 27 -> RequestModels.EventAction.tapp_loan_approval
140
+ 28 -> RequestModels.EventAction.tapp_loan_disbursal
141
+ 29 -> RequestModels.EventAction.tapp_login
142
+ 30 -> RequestModels.EventAction.tapp_rate
143
+ 31 -> RequestModels.EventAction.tapp_spend_credits
144
+ 32 -> RequestModels.EventAction.tapp_unlock_achievement
145
+ 33 -> RequestModels.EventAction.tapp_add_shipping_info
146
+ 34 -> RequestModels.EventAction.tapp_earn_virtual_currency
147
+ 35 -> RequestModels.EventAction.tapp_start_level
148
+ 36 -> RequestModels.EventAction.tapp_complete_level
149
+ 37 -> RequestModels.EventAction.tapp_post_score
150
+ 38 -> RequestModels.EventAction.tapp_select_content
151
+ 39 -> RequestModels.EventAction.tapp_begin_tutorial
152
+ 40 -> RequestModels.EventAction.tapp_complete_tutorial
153
+ else -> null
154
+ }
155
+
156
+ if (action == null) {
157
+ promise.reject("INVALID_ACTION", "Invalid or unsupported event action: $eventAction")
158
+ return
159
+ }
160
+
161
+ val metaMap: Map<String, Any>? = metadata?.toSafeMetadataMap { msg ->
162
+ Log.w("TappRN", msg)
163
+ }
164
+
165
+ val tappEvent = RequestModels.TappEvent(
166
+ eventName = action,
167
+ metadata = metaMap
168
+ )
169
+
170
+ tapp.handleTappEvent(tappEvent)
171
+ promise.resolve("Tapp event handled successfully: $eventAction")
172
+
173
+ } catch (e: Exception) {
174
+ promise.reject("HANDLE_EVENT_ERROR", e.message ?: "An unexpected error occurred", e)
175
+ }
176
+ }
177
+
178
+ private fun ReadableMap.toSafeMetadataMap(
179
+ onDrop: (String) -> Unit
180
+ ): Map<String, Any> {
181
+ val out = LinkedHashMap<String, Any>()
182
+ val iterator = keySetIterator()
183
+
184
+ while (iterator.hasNextKey()) {
185
+ val key = iterator.nextKey()
186
+ when (getType(key)) {
187
+ ReadableType.String -> out[key] = getString(key).orEmpty()
188
+ ReadableType.Boolean -> out[key] = getBoolean(key)
189
+ ReadableType.Number -> {
190
+ // RN exposes numbers as Double; keep as Double (still a Number)
191
+ val n = getDouble(key)
192
+ // JSON safety: reject NaN/Infinity
193
+ if (n.isFinite()) out[key] = n
194
+ else onDrop("Dropping metadata '$key' because value is not finite: $n")
195
+ }
196
+ ReadableType.Null -> {
197
+ // Skip nulls (or decide to keep nulls if your serializer supports it)
198
+ onDrop("Dropping metadata '$key' because value is null")
199
+ }
200
+ ReadableType.Map, ReadableType.Array -> {
201
+ // Not allowed by your native contract
202
+ onDrop("Dropping metadata '$key' because nested ${getType(key)} is not supported")
203
+ }
204
+ }
205
+ }
206
+
207
+ return out
208
+ }
209
+
210
+
211
+ @ReactMethod
212
+ fun fetchLinkData(deepLink: String?, promise: Promise?) {
213
+ if (!::tapp.isInitialized) {
214
+ tapp = Tapp(reactApplicationContext)
215
+ }
216
+ CoroutineScope(Dispatchers.IO).launch {
217
+ try {
218
+ val response = tapp.fetchLinkData(deepLink ?: "")
219
+ if (response != null) {
220
+ val map: WritableMap = Arguments.createMap().apply {
221
+ putBoolean("error", response.error)
222
+ putString("message", response.message)
223
+ putString("tappUrl", response.tappUrl)
224
+ putString("attrTappUrl", response.attrTappUrl)
225
+ putString("influencer", response.influencer)
226
+ putBoolean("isFirstSession", response.isFirstSession ?: false)
227
+ putString("deepLink", response.deepLink)
228
+ val dataMap: WritableMap = Arguments.createMap()
229
+ response.data?.forEach { (key, value) ->
230
+ dataMap.putString(key, value)
231
+ }
232
+ putMap("data", dataMap)
233
+ }
234
+ withContext(Dispatchers.Main) {
235
+ promise?.resolve(map)
236
+ }
237
+ } else {
238
+ withContext(Dispatchers.Main) {
239
+ promise?.reject("NO_RESPONSE", "No link data response")
240
+ }
241
+ }
242
+ } catch (e: Exception) {
243
+ withContext(Dispatchers.Main) {
244
+ promise?.reject("FETCH_LINK_DATA_ERROR", e.message ?: "An unexpected error occurred")
245
+ }
246
+ }
247
+ }
248
+ }
249
+
250
+ @ReactMethod
251
+ fun shouldProcess(deepLink: String?, promise: Promise?) {
252
+ if (!::tapp.isInitialized) {
253
+ tapp = Tapp(reactApplicationContext)
254
+ }
255
+ try {
256
+ val process = tapp.shouldProcess(deepLink)
257
+ Logger.logInfo("shouldProcess run for deepLink: $deepLink with process $process")
258
+ promise?.resolve(process)
259
+ } catch (e: Exception) {
260
+ Logger.logError("SHOULD_PROCESS_ERROR ${e.message ?: "An unexpected error occurred"}")
261
+ promise?.resolve(false)
262
+ }
263
+ }
264
+
265
+ @ReactMethod
266
+ fun fetchOriginLinkData(promise: Promise?) {
267
+ if (!::tapp.isInitialized) {
268
+ tapp = Tapp(reactApplicationContext)
269
+ }
270
+ CoroutineScope(Dispatchers.IO).launch {
271
+ try {
272
+ val response = tapp.fetchOriginalLinkData()
273
+ if (response != null) {
274
+ val resultMap = convertResponseToMap(response)
275
+ withContext(Dispatchers.Main) {
276
+ promise?.resolve(resultMap)
277
+ }
278
+ } else {
279
+ withContext(Dispatchers.Main) {
280
+ promise?.reject("NO_DATA", "No link data returned")
281
+ }
282
+ }
283
+ } catch (e: Exception) {
284
+ withContext(Dispatchers.Main) {
285
+ promise?.reject("FETCH_ORIGINAL_LINK_DATA_ERROR", e.message ?: "An unexpected error occurred", e)
286
+ }
287
+ }
288
+ }
289
+ }
290
+
291
+ @ReactMethod
292
+ fun simulateTestEvent() {
293
+ tapp.simulateTestEvent()
294
+ }
295
+
296
+ private fun didFailResolvingURL(response: RequestModels.FailResolvingUrlResponse) {
297
+ val map: WritableMap = Arguments.createMap().apply {
298
+ putString("url", response.url)
299
+ putString("error", response.error.ifEmpty { "" })
300
+ }
301
+ reactApplicationContext
302
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
303
+ .emit("onDidFailResolvingURL", map)
304
+ }
305
+
306
+
307
+
308
+ // --- helper methods for emitting deferred link events ---
309
+ //TODO::ADD LINK DATA ON RETURN
310
+ private fun sendDeferredLinkEvent(response: RequestModels.TappLinkDataResponse) {
311
+ val map: WritableMap = Arguments.createMap().apply {
312
+ putBoolean("error", response.error)
313
+ putString("message", response.message)
314
+ putString("tappUrl", response.tappUrl)
315
+ putString("attrTappUrl", response.attrTappUrl)
316
+ putString("influencer", response.influencer)
317
+ putBoolean("isFirstSession", response.isFirstSession ?: false)
318
+ putString("deepLink", response.deepLink ?: "")
319
+ val dataMap: WritableMap = Arguments.createMap()
320
+ response.data?.forEach { (key, value) ->
321
+ dataMap.putString(key, value)
322
+ }
323
+ putMap("data", dataMap)
324
+ }
325
+ reactApplicationContext
326
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
327
+ .emit("onDeferredLinkReceived", map)
328
+ }
329
+
330
+ private fun sendTestEvent(test: String) {
331
+ val map: WritableMap = Arguments.createMap().apply {
332
+ putString("test", test)
333
+ }
334
+ reactApplicationContext
335
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
336
+ .emit("onTestListener", map)
337
+ }
338
+
339
+ private fun convertResponseToMap(response: RequestModels.TappLinkDataResponse): WritableMap {
340
+ val map = Arguments.createMap()
341
+ map.putBoolean("error", response.error)
342
+ map.putString("message", response.message)
343
+ map.putString("tappUrl", response.tappUrl)
344
+ map.putString("attrTappUrl", response.attrTappUrl)
345
+ map.putString("influencer", response.influencer)
346
+ map.putBoolean("isFirstSession", response.isFirstSession ?: false)
347
+ val dataMap = Arguments.createMap()
348
+ response.data?.forEach { (key, value) ->
349
+ dataMap.putString(key, value)
350
+ }
351
+ map.putMap("data", dataMap)
352
+ return map
353
+ }
354
+ }
@@ -0,0 +1,45 @@
1
+ package com.comtappsosdk
2
+
3
+ import com.facebook.react.BaseReactPackage
4
+ import com.facebook.react.ReactPackage
5
+ import com.facebook.react.bridge.NativeModule
6
+ import com.facebook.react.bridge.ReactApplicationContext
7
+ import com.facebook.react.module.model.ReactModuleInfo
8
+ import com.facebook.react.module.model.ReactModuleInfoProvider
9
+ import java.util.HashMap
10
+ import com.facebook.react.uimanager.ViewManager
11
+
12
+ class ComTappSoSdkPackage : ReactPackage {
13
+
14
+ private val isTurbo: Boolean
15
+ get() = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
16
+
17
+ // Legacy: create a list of native modules
18
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
19
+ return listOf(ComTappSoSdkModule(reactContext))
20
+ }
21
+
22
+ // If you don’t have any custom view managers, return an empty list
23
+ override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
24
+ return emptyList()
25
+ }
26
+
27
+
28
+ // (Optional) For TurboModules support in newer RN versions
29
+ fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
30
+ return ReactModuleInfoProvider {
31
+ val moduleInfos = HashMap<String, ReactModuleInfo>()
32
+ moduleInfos[ComTappSoSdkModule.NAME] = ReactModuleInfo(
33
+ ComTappSoSdkModule.NAME,
34
+ ComTappSoSdkModule.NAME,
35
+ false,
36
+ false,
37
+ true,
38
+ false,
39
+ isTurbo // This flag can control TurboModules
40
+ )
41
+ moduleInfos
42
+ }
43
+ }
44
+ }
45
+
@@ -0,0 +1,62 @@
1
+ //#import <ComTappSoSdkSpec/ComTappSoSdkSpec.h>
2
+ #import <React/RCTBridgeModule.h>
3
+ #import <React/RCTEventEmitter.h>
4
+
5
+ #import <Tapp/Tapp-Swift.h>
6
+ #import <Tapp/Tapp-umbrella.h>
7
+
8
+ #import <TappNetworking/TappNetworking-Swift.h>
9
+ #import <TappNetworking/Tapp-Networking-umbrella.h>
10
+
11
+ //
12
+ // Legacy definitions for RN versions before the new architecture.
13
+ // These legacy types are only needed when RCT_NEW_ARCH_ENABLED is NOT defined.
14
+ // We also guard the declarations so they’re only declared once.
15
+ //
16
+ #if !defined(RCT_NEW_ARCH_ENABLED)
17
+ #ifndef COMTAPPSOSDK_LEGACY_TYPES
18
+ #define COMTAPPSOSDK_LEGACY_TYPES
19
+
20
+ #import <functional>
21
+ namespace facebook {
22
+ namespace react {
23
+ // Define the callback type that accepts an event name as std::string and a payload.
24
+ using EventEmitterCallback = std::function<void(const std::string&, NSDictionary*)>;
25
+ }
26
+ }
27
+
28
+ // Rename the wrapper class to avoid conflicts:
29
+ @interface CTEventEmitterCallbackWrapper : NSObject
30
+ @property (nonatomic, copy) void (^eventEmitterCallback)(NSString *eventName, NSDictionary *payload);
31
+ @end
32
+
33
+ // Inline helper function to convert an Objective-C block to a std::function.
34
+ static inline facebook::react::EventEmitterCallback CTConvertBlockToStdFunction(void (^block)(NSString *, NSDictionary *)) {
35
+ if (!block) return nullptr;
36
+ // Copy the block so it lives long enough.
37
+ void (^copiedBlock)(NSString *, NSDictionary *) = [block copy];
38
+ return [=](const std::string &eventName, NSDictionary *payload) {
39
+ NSString *nsEventName = [NSString stringWithUTF8String:eventName.c_str()];
40
+ copiedBlock(nsEventName, payload);
41
+ };
42
+ }
43
+
44
+ #endif // COMTAPPSO_LEGACY_TYPES
45
+ #endif // !RCT_NEW_ARCH_ENABLED
46
+
47
+ //
48
+ // For new architecture builds, import the generated Codegen header:
49
+ #if defined(RCT_NEW_ARCH_ENABLED)
50
+ #import "generated/ComTappSoSdkSpec/ComTappSoSdkSpec.h"
51
+ #endif
52
+
53
+ //
54
+ // Declare the main interface. For new architecture, adopt <NativeComTappSoSdkSpec, TappDelegate, RCTTurboModule>,
55
+ // and for legacy, adopt <RCTBridgeModule, TappDelegate>.
56
+ #if defined(RCT_NEW_ARCH_ENABLED)
57
+ @interface ComTappSoSdk : RCTEventEmitter <NativeComTappSoSdkSpec, TappDelegate, RCTTurboModule>
58
+ #else
59
+ @interface ComTappSoSdk : RCTEventEmitter <RCTBridgeModule, TappDelegate>
60
+ #endif
61
+
62
+ @end