react-native-sdk-pianoio 0.3.2 → 0.3.4

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 (37) hide show
  1. package/README.md +176 -186
  2. package/android/build.gradle +29 -42
  3. package/android/gradle.properties +33 -14
  4. package/android/src/main/java/com/sdkpianoio/ComposerPianoImpl.kt +366 -0
  5. package/android/src/main/java/com/sdkpianoio/SdkPianoioModule.kt +281 -507
  6. package/android/src/main/java/com/sdkpianoio/TokenService.kt +139 -0
  7. package/android/test.sh +494 -0
  8. package/ios/ComposerPianoImpl.swift +128 -225
  9. package/ios/MyComposerDelegate.swift +142 -109
  10. package/ios/SdkPianoio.swift +69 -143
  11. package/ios/SdkPianoioBridge.m +18 -46
  12. package/ios/TokenService.swift +219 -0
  13. package/lib/commonjs/NativeSdkPianoio.ts +34 -10
  14. package/lib/commonjs/PianoComposer.js +69 -51
  15. package/lib/commonjs/PianoComposer.js.map +1 -1
  16. package/lib/commonjs/index.js.map +1 -1
  17. package/lib/module/NativeSdkPianoio.ts +34 -10
  18. package/lib/module/PianoComposer.js +70 -51
  19. package/lib/module/PianoComposer.js.map +1 -1
  20. package/lib/module/index.js +0 -14
  21. package/lib/module/index.js.map +1 -1
  22. package/lib/typescript/commonjs/src/NativeSdkPianoio.d.ts +27 -9
  23. package/lib/typescript/commonjs/src/NativeSdkPianoio.d.ts.map +1 -1
  24. package/lib/typescript/commonjs/src/PianoComposer.d.ts +45 -18
  25. package/lib/typescript/commonjs/src/PianoComposer.d.ts.map +1 -1
  26. package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
  27. package/lib/typescript/module/src/NativeSdkPianoio.d.ts +27 -9
  28. package/lib/typescript/module/src/NativeSdkPianoio.d.ts.map +1 -1
  29. package/lib/typescript/module/src/PianoComposer.d.ts +45 -18
  30. package/lib/typescript/module/src/PianoComposer.d.ts.map +1 -1
  31. package/lib/typescript/module/src/index.d.ts.map +1 -1
  32. package/package.json +4 -1
  33. package/src/NativeSdkPianoio.ts +34 -10
  34. package/src/PianoComposer.tsx +76 -59
  35. package/src/index.tsx +0 -14
  36. package/android/src/main/AndroidManifestNew.xml +0 -2
  37. package/ios/services/TokenService.swift +0 -70
@@ -1,559 +1,333 @@
1
1
  package com.sdkpianoio
2
2
 
3
- import androidx.core.util.Consumer
3
+ import androidx.activity.ComponentActivity
4
+ import androidx.activity.result.ActivityResultLauncher
5
+ import com.facebook.react.bridge.Arguments
6
+ import com.facebook.react.bridge.Promise
4
7
  import com.facebook.react.bridge.ReactApplicationContext
5
8
  import com.facebook.react.bridge.ReactContextBaseJavaModule
6
9
  import com.facebook.react.bridge.ReactMethod
7
- import com.facebook.react.bridge.Promise
8
10
  import com.facebook.react.bridge.ReadableArray
11
+ import com.facebook.react.bridge.ReadableMap
12
+ import com.facebook.react.bridge.WritableMap
9
13
  import com.facebook.react.module.annotations.ReactModule
10
-
11
- import io.piano.android.composer.Composer
12
- import io.piano.android.composer.Composer.Endpoint
13
- import io.piano.android.composer.ExperienceInterceptor
14
- import io.piano.android.composer.listeners.ExecuteExperienceListener
15
- import io.piano.android.composer.listeners.ShowLoginListener
16
- import io.piano.android.composer.model.ExperienceRequest
17
- import io.piano.android.consents.PianoConsents
18
- import java.util.function.Consumer
19
- import com.facebook.react.bridge.Arguments
20
-
21
- // importa altri listener se ti servono
22
-
14
+ import io.piano.android.id.PianoIdAuthResultContract
15
+ import io.piano.android.id.PianoIdClient
16
+ import io.piano.android.id.models.PianoIdAuthFailureResult
17
+ import io.piano.android.id.models.PianoIdAuthSuccessResult
18
+
19
+ /**
20
+ * SdkPianoioModule - React Native module for the Piano.io SDK
21
+ *
22
+ * This module serves as the bridge between JavaScript and the native Android implementation.
23
+ */
23
24
  @ReactModule(name = SdkPianoioModule.NAME)
24
- class SdkPianoioModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { // <-- EXTEND ReactContextBaseJavaModule
25
-
26
- private var composer: Composer? = null
27
-
28
- // <-- ADD THIS REQUIRED METHOD
29
- override fun getName(): String { // <--- MAKE THIS CHANGE
30
- return NAME
31
- }
32
-
33
- @ReactMethod
34
- fun initializeComposer(aid: String, promise: Promise) {
35
- try {
36
- val context = reactContext.currentActivity ?: reactContext.applicationContext;
37
-
38
- Composer.init(
39
- context,
40
- aid,
41
- Endpoint.SANDBOX, // Use Endpoint.SANDBOX instead of Composer.Endpoint.Companion
42
- null
43
- )
44
- promise.resolve(true) // Resolve the promise on successful initialization
45
- } catch (e: Exception) {
46
- promise.reject("INIT_ERROR", "Errore inizializzazione Composer", e)
25
+ class SdkPianoioModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
26
+
27
+ private var composerImpl: ComposerPianoImpl? = null
28
+ private var isInitialized: Boolean = false
29
+ private var signInPromise: Promise? = null
30
+
31
+ private lateinit var authResultLauncher: ActivityResultLauncher<PianoIdClient.SignInContext>
32
+
33
+ init {
34
+ // The ActivityResultLauncher must be registered in the init block or onCreate.
35
+ // We need to ensure we have a ComponentActivity to access the registry.
36
+ val activity = reactContext.currentActivity
37
+ if (activity is ComponentActivity) {
38
+ authResultLauncher = activity.activityResultRegistry.register(
39
+ "piano-id-login",
40
+ PianoIdAuthResultContract()
41
+ ) { result ->
42
+ val promise = signInPromise
43
+ signInPromise = null // Clean up promise immediately
44
+ when (result) {
45
+ is PianoIdAuthSuccessResult -> {
46
+ val token = result.token
47
+ composerImpl?.tokenService?.setToken(token)
48
+ val user = composerImpl?.tokenService?.getCurrentUser()
49
+ if (user != null) {
50
+ val resultMap = Arguments.createMap().apply {
51
+ putBoolean("success", true)
52
+ putString("userId", user.id)
53
+ putString("userEmail", user.email)
54
+ putString("accessToken", user.accessToken)
55
+ }
56
+ promise?.resolve(resultMap)
57
+ } else {
58
+ promise?.reject("SIGNIN_ERROR", "Failed to construct user after login.")
59
+ }
60
+ }
61
+ is PianoIdAuthFailureResult -> {
62
+ val exception = result.exception
63
+ promise?.reject("SIGNIN_ERROR", exception.message, exception)
64
+ }
65
+ null -> {
66
+ // This handles the case where the user cancels the flow
67
+ promise?.reject("CANCELLED", "User cancelled the sign-in process.")
68
+ }
69
+ }
70
+ }
71
+ } else {
72
+ // This is a fallback, though in a normal RN app, currentActivity should be a ComponentActivity.
73
+ // You might want to log a warning here.
74
+ }
47
75
  }
48
- }
49
-
50
-
51
- private val tags = mutableListOf<String>()
52
- private var zoneId: String? = null
53
- private var referrer: String? = null
54
- private val customVariables = mutableMapOf<String, String>()
55
- private var userToken: String? = null
56
- private var url: String? = null
57
-
58
- @ReactMethod
59
- fun addComposerTag(tag: String, promise: Promise) {
60
- try {
61
- if (!tags.contains(tag)) {
62
- tags.add(tag)
63
- }
64
- promise.resolve(null)
65
- } catch (e: Exception) {
66
- promise.reject("TAG_ERROR", "Errore aggiunta tag", e)
76
+
77
+ override fun getName(): String {
78
+ return NAME
67
79
  }
68
- }
69
-
70
- @ReactMethod
71
- fun addComposerTags(tagsList: ReadableArray, promise: Promise) {
72
- try {
73
- for (i in 0 until tagsList.size()) {
74
- val tag = tagsList.getString(i)
75
- if (tag != null && !tags.contains(tag)) {
76
- tags.add(tag)
80
+
81
+ @ReactMethod
82
+ fun initialize(aid: String, promise: Promise) {
83
+ try {
84
+ if (isInitialized) {
85
+ promise.resolve(true)
86
+ return
87
+ }
88
+ composerImpl = ComposerPianoImpl()
89
+ composerImpl?.initializeComposer(reactContext, aid)
90
+ isInitialized = true
91
+ promise.resolve(true)
92
+ } catch (e: Exception) {
93
+ promise.reject("INIT_ERROR", "Failed to initialize Piano SDK", e)
77
94
  }
78
- }
79
- promise.resolve(null)
80
- } catch (e: Exception) {
81
- promise.reject("TAGS_ERROR", "Errore aggiunta tags", e)
82
95
  }
83
- }
84
96
 
85
- @ReactMethod
86
- fun setComposerZoneId(zoneId: String, promise: Promise) {
87
- try {
88
- this.zoneId = zoneId
89
- promise.resolve(null)
90
- } catch (e: Exception) {
91
- promise.reject("ZONEID_ERROR", "Errore impostazione zoneId", e)
92
- }
93
- }
97
+ // MARK: - Composer Configuration Methods
94
98
 
95
- @ReactMethod
96
- fun setComposerReferrer(referrer: String, promise: Promise) {
97
- try {
98
- this.referrer = referrer
99
- promise.resolve(null)
100
- } catch (e: Exception) {
101
- promise.reject("REFERRER_ERROR", "Errore impostazione referrer", e)
99
+ @ReactMethod
100
+ fun addTag(tag: String, promise: Promise) {
101
+ try {
102
+ if (!validateInitialization(promise) || !validateParameter(tag, "tag", promise)) return
103
+ composerImpl?.addTag(tag)
104
+ promise.resolve(true)
105
+ } catch (e: Exception) {
106
+ promise.reject("ADD_TAG_ERROR", "Error adding tag", e)
107
+ }
102
108
  }
103
- }
104
109
 
105
- @ReactMethod
106
- fun setComposerCustomVariable(name: String, value: String, promise: Promise) {
107
- try {
108
- customVariables[name] = value
109
- promise.resolve(null)
110
- } catch (e: Exception) {
111
- promise.reject("CUSTOMVAR_ERROR", "Errore impostazione custom variable", e)
110
+ @ReactMethod
111
+ fun addTags(tagsArray: ReadableArray, promise: Promise) {
112
+ try {
113
+ if (!validateInitialization(promise) || !validateArray(tagsArray, "tagsArray", promise)) return
114
+ val tags = tagsArray.toArrayList().mapNotNull { it?.toString() }
115
+ composerImpl?.addTags(tags)
116
+ promise.resolve(true)
117
+ } catch (e: Exception) {
118
+ promise.reject("ADD_TAGS_ERROR", "Error adding tags", e)
119
+ }
112
120
  }
113
- }
114
121
 
115
- @ReactMethod
116
- fun setComposerUserToken(token: String, promise: Promise) {
117
- try {
118
- userToken = token
119
- promise.resolve(null)
120
- } catch (e: Exception) {
121
- promise.reject("TOKEN_ERROR", "Errore impostazione token", e)
122
+ @ReactMethod
123
+ fun setZoneId(zoneId: String, promise: Promise) {
124
+ try {
125
+ if (!validateInitialization(promise)) return
126
+ composerImpl?.setZoneId(zoneId)
127
+ promise.resolve(true)
128
+ } catch (e: Exception) {
129
+ promise.reject("SET_ZONEID_ERROR", "Error setting Zone ID", e)
130
+ }
122
131
  }
123
- }
124
132
 
125
- @ReactMethod
126
- fun setComposerUrl(url: String, promise: Promise) {
127
- try {
128
- this.url = url
129
- promise.resolve(null)
130
- } catch (e: Exception) {
131
- promise.reject("URL_ERROR", "Errore impostazione URL", e)
133
+ @ReactMethod
134
+ fun setReferrer(referrer: String, promise: Promise) {
135
+ try {
136
+ if (!validateInitialization(promise)) return
137
+ composerImpl?.setReferrer(referrer)
138
+ promise.resolve(true)
139
+ } catch (e: Exception) {
140
+ promise.reject("SET_REFERRER_ERROR", "Error setting referrer", e)
141
+ }
132
142
  }
133
- }
134
143
 
135
- @ReactMethod
136
- fun getComposer(promise: Promise) {
137
- try {
138
- val context = reactContext.currentActivity ?: reactContext.applicationContext
139
- composer = Composer.getInstance()
140
- promise.resolve(true)
141
- } catch (e: Exception) {
142
- promise.reject("GET_COMPOSER_ERROR", "Errore ottenimento Composer", e)
144
+ @ReactMethod
145
+ fun setUrl(url: String, promise: Promise) {
146
+ try {
147
+ if (!validateInitialization(promise)) return
148
+ composerImpl?.setUrl(url)
149
+ promise.resolve(true)
150
+ } catch (e: Exception) {
151
+ promise.reject("SET_URL_ERROR", "Error setting URL", e)
152
+ }
143
153
  }
144
- }
145
-
146
- // Replace your old executeExperience method with this one.
147
154
 
148
- @ReactMethod
149
- fun executeExperience(promise: Promise) {
150
- // 1. Get the Composer instance, rejecting the promise if it's not initialized.
151
- val composer = Composer.getInstance()
152
- if (composer == null) {
153
- promise.reject("NOT_INITIALIZED", "Piano Composer is not initialized.")
154
- return
155
+ @ReactMethod
156
+ fun setUserToken(token: String, promise: Promise) {
157
+ try {
158
+ if (!validateInitialization(promise)) return
159
+ composerImpl?.setUserToken(token)
160
+ promise.resolve(true)
161
+ } catch (e: Exception) {
162
+ promise.reject("SET_TOKEN_ERROR", "Error setting user token", e)
163
+ }
155
164
  }
156
165
 
157
- // 2. Build the request object from the stored properties.
158
- // This part of your original code was correct.
159
- val request = ExperienceRequest.Builder()
160
- .debug(true)
161
- .tags(tags)
162
- .zone(zoneId)
163
- .referer(referrer)
164
- .customVariables(customVariables.mapValues { listOf(it.value) })
165
- .url(url)
166
- .userToken(userToken)
167
- .build()
168
-
169
- // 3. Create a listener to handle the asynchronous response from the Piano SDK.
170
- val listener = ExecuteExperienceListener { events ->
171
- // This code runs when the SDK gets a successful response.
166
+ @ReactMethod
167
+ fun addCustomVariable(key: String, value: String, promise: Promise) {
172
168
  try {
173
- // We need to convert the list of Piano 'Event' objects
174
- // into a WritableArray of WritableMaps that React Native can understand.
175
- val eventsArray = Arguments.createArray()
176
-
177
- for (event in events) {
178
- val eventMap = Arguments.createMap()
179
- eventMap.putString("eventType", event.eventType)
180
- eventMap.putString("experienceId", event.experienceId)
181
- // Add any other event data you need to this map.
182
- // For example, for a 'showTemplate' event:
183
- // if (event.eventData is ShowTemplateEventParams) {
184
- // eventMap.putString("templateUrl", (event.eventData as ShowTemplateEventParams).templateUrl)
185
- // }
186
- eventsArray.pushMap(eventMap)
187
- }
188
-
189
- // 4. Resolve the promise with the array of events.
190
- promise.resolve(eventsArray)
191
-
169
+ if (!validateInitialization(promise)) return
170
+ composerImpl?.addCustomVariable(key, value)
171
+ promise.resolve(true)
192
172
  } catch (e: Exception) {
193
- promise.reject("DATA_CONVERSION_ERROR", "Failed to convert event data.", e)
173
+ promise.reject("ADD_CUSTOMVAR_ERROR", "Error adding custom variable", e)
194
174
  }
195
175
  }
196
-
197
- // 5. Create a listener for any errors.
198
- val errorListener = Consumer<Exception> { e ->
199
- promise.reject("EXECUTE_ERROR", "Piano SDK execution failed.", e)
176
+
177
+ @ReactMethod
178
+ fun setCustomVariables(variables: ReadableMap, promise: Promise) {
179
+ try {
180
+ if (!validateInitialization(promise) || !validateMap(variables, "variables", promise)) return
181
+ val map = mutableMapOf<String, String>()
182
+ val iterator = variables.keySetIterator()
183
+ while (iterator.hasNextKey()) {
184
+ val key = iterator.nextKey()
185
+ val value = variables.getString(key)
186
+ if (value != null && value.isNotBlank()) {
187
+ map[key] = value
188
+ }
189
+ }
190
+ if (map.isEmpty()) {
191
+ promise.reject("INVALID_PARAMETER", "No valid variables found in the map")
192
+ return
193
+ }
194
+ composerImpl?.setCustomVariables(map)
195
+ promise.resolve(true)
196
+ } catch (e: Exception) {
197
+ promise.reject("SET_CUSTOMVARS_ERROR", "Error setting custom variables", e)
198
+ }
200
199
  }
201
200
 
202
- // 6. Finally, execute the request with the composer, passing the request and listeners.
203
- composer.getExperience(request, listener, errorListener)
204
- }
205
-
206
- @ReactMethod
207
- fun executeComposer(promise: Promise) {
208
- try {
209
- val composer = Composer.getInstance()
210
- if (composer == null) {
211
- promise.reject("NOT_INITIALIZED", "Piano Composer is not initialized.")
212
- return
213
- }
214
-
215
- // Execute the composer with current state
216
- val request = ExperienceRequest.Builder()
217
- .debug(true)
218
- .tags(tags)
219
- .zone(zoneId)
220
- .referer(referrer)
221
- .customVariables(customVariables.mapValues { listOf(it.value) })
222
- .url(url)
223
- .userToken(userToken)
224
- .build()
225
-
226
- val listener = ExecuteExperienceListener { events ->
227
- promise.resolve(true)
228
- }
201
+ // MARK: - Execution Method
229
202
 
230
- val errorListener = Consumer<Exception> { e ->
231
- promise.reject("EXECUTE_ERROR", "Piano SDK execution failed.", e)
232
- }
233
-
234
- composer.getExperience(request, listener, errorListener)
235
- } catch (e: Exception) {
236
- promise.reject("EXECUTE_ERROR", "Error executing composer", e)
203
+ @ReactMethod
204
+ fun executeExperience(promise: Promise) {
205
+ try {
206
+ if (!validateInitialization(promise)) return
207
+ composerImpl?.executeComposer(promise)
208
+ } catch (e: Exception) {
209
+ promise.reject("EXECUTION_ERROR", "Error executing experience", e)
210
+ }
237
211
  }
238
- }
239
212
 
240
- @ReactMethod
241
- fun showLogin(promise: Promise) {
242
- try {
243
- val composer = Composer.getInstance()
244
- if (composer == null) {
245
- promise.reject("NOT_INITIALIZED", "Piano Composer is not initialized.")
246
- return
247
- }
248
-
249
- val listener = ShowLoginListener { loginResult ->
250
- promise.resolve(loginResult)
251
- }
252
-
253
- composer.showLogin(listener)
254
- } catch (e: Exception) {
255
- promise.reject("LOGIN_ERROR", "Error showing login", e)
213
+ // MARK: - Authentication Methods
214
+
215
+ @ReactMethod
216
+ fun signIn(promise: Promise) {
217
+ if (signInPromise != null) {
218
+ promise.reject("IN_PROGRESS", "Another sign-in operation is already in progress.")
219
+ return
220
+ }
221
+ try {
222
+ if (!validateInitialization(promise)) return
223
+ this.signInPromise = promise
224
+ composerImpl?.tokenService?.signIn(authResultLauncher)
225
+ } catch (e: Exception) {
226
+ promise.reject("SIGNIN_ERROR", "Error during sign in", e)
227
+ this.signInPromise = null
228
+ }
256
229
  }
257
- }
258
-
259
- @ReactMethod
260
- fun showTemplate(promise: Promise) {
261
- try {
262
- val composer = Composer.getInstance()
263
- if (composer == null) {
264
- promise.reject("NOT_INITIALIZED", "Piano Composer is not initialized.")
265
- return
266
- }
267
-
268
- val request = ExperienceRequest.Builder()
269
- .debug(true)
270
- .tags(tags)
271
- .zone(zoneId)
272
- .referer(referrer)
273
- .customVariables(customVariables.mapValues { listOf(it.value) })
274
- .url(url)
275
- .userToken(userToken)
276
- .build()
277
-
278
- val listener = ExecuteExperienceListener { events ->
279
- promise.resolve(true)
280
- }
281
-
282
- val errorListener = Consumer<Exception> { e ->
283
- promise.reject("TEMPLATE_ERROR", "Error showing template", e)
284
- }
285
-
286
- composer.getExperience(request, listener, errorListener)
287
- } catch (e: Exception) {
288
- promise.reject("TEMPLATE_ERROR", "Error showing template", e)
230
+
231
+ @ReactMethod
232
+ fun signOut(promise: Promise) {
233
+ try {
234
+ if (!validateInitialization(promise)) return
235
+ composerImpl?.tokenService?.signOut { success ->
236
+ if (success) {
237
+ promise.resolve(Arguments.createMap().apply { putBoolean("success", true) })
238
+ } else {
239
+ promise.reject("SIGNOUT_ERROR", "Sign out failed")
240
+ }
241
+ }
242
+ } catch (e: Exception) {
243
+ promise.reject("SIGNOUT_ERROR", "Error during sign out", e)
244
+ }
289
245
  }
290
- }
291
-
292
- @ReactMethod
293
- fun showForm(promise: Promise) {
294
- try {
295
- val composer = Composer.getInstance()
296
- if (composer == null) {
297
- promise.reject("NOT_INITIALIZED", "Piano Composer is not initialized.")
298
- return
299
- }
300
-
301
- val request = ExperienceRequest.Builder()
302
- .debug(true)
303
- .tags(tags)
304
- .zone(zoneId)
305
- .referer(referrer)
306
- .customVariables(customVariables.mapValues { listOf(it.value) })
307
- .url(url)
308
- .userToken(userToken)
309
- .build()
310
-
311
- val listener = ExecuteExperienceListener { events ->
312
- promise.resolve(true)
313
- }
314
-
315
- val errorListener = Consumer<Exception> { e ->
316
- promise.reject("FORM_ERROR", "Error showing form", e)
317
- }
318
-
319
- composer.getExperience(request, listener, errorListener)
320
- } catch (e: Exception) {
321
- promise.reject("FORM_ERROR", "Error showing form", e)
246
+
247
+ @ReactMethod
248
+ fun getCurrentUser(promise: Promise) {
249
+ try {
250
+ if (!validateInitialization(promise)) return
251
+ val user = composerImpl?.tokenService?.getCurrentUser()
252
+ if (user != null) {
253
+ val result = Arguments.createMap().apply {
254
+ putBoolean("success", true)
255
+ putString("userId", user.id)
256
+ putString("userEmail", user.email)
257
+ putString("accessToken", user.accessToken)
258
+ putBoolean("authenticated", true)
259
+ }
260
+ promise.resolve(result)
261
+ } else {
262
+ val result = Arguments.createMap().apply {
263
+ putBoolean("success", true)
264
+ putBoolean("authenticated", false)
265
+ }
266
+ promise.resolve(result)
267
+ }
268
+ } catch (e: Exception) {
269
+ promise.reject("USER_ERROR", "Error getting current user", e)
270
+ }
322
271
  }
323
- }
324
-
325
- @ReactMethod
326
- fun showRecommendations(promise: Promise) {
327
- try {
328
- val composer = Composer.getInstance()
329
- if (composer == null) {
330
- promise.reject("NOT_INITIALIZED", "Piano Composer is not initialized.")
331
- return
332
- }
333
-
334
- val request = ExperienceRequest.Builder()
335
- .debug(true)
336
- .tags(tags)
337
- .zone(zoneId)
338
- .referer(referrer)
339
- .customVariables(customVariables.mapValues { listOf(it.value) })
340
- .url(url)
341
- .userToken(userToken)
342
- .build()
343
-
344
- val listener = ExecuteExperienceListener { events ->
345
- promise.resolve(true)
346
- }
347
-
348
- val errorListener = Consumer<Exception> { e ->
349
- promise.reject("RECOMMENDATIONS_ERROR", "Error showing recommendations", e)
350
- }
351
-
352
- composer.getExperience(request, listener, errorListener)
353
- } catch (e: Exception) {
354
- promise.reject("RECOMMENDATIONS_ERROR", "Error showing recommendations", e)
272
+
273
+ @ReactMethod
274
+ fun isAuthenticated(promise: Promise) {
275
+ try {
276
+ if (!validateInitialization(promise)) return
277
+ val isAuth = composerImpl?.tokenService?.isAuthenticated() ?: false
278
+ promise.resolve(Arguments.createMap().apply { putBoolean("authenticated", isAuth) })
279
+ } catch (e: Exception) {
280
+ promise.reject("AUTH_ERROR", "Error checking authentication", e)
281
+ }
355
282
  }
356
- }
357
-
358
- @ReactMethod
359
- fun nonSite(promise: Promise) {
360
- try {
361
- val composer = Composer.getInstance()
362
- if (composer == null) {
363
- promise.reject("NOT_INITIALIZED", "Piano Composer is not initialized.")
364
- return
365
- }
366
-
367
- val request = ExperienceRequest.Builder()
368
- .debug(true)
369
- .tags(tags)
370
- .zone(zoneId)
371
- .referer(referrer)
372
- .customVariables(customVariables.mapValues { listOf(it.value) })
373
- .url(url)
374
- .userToken(userToken)
375
- .build()
376
283
 
377
- val listener = ExecuteExperienceListener { events ->
378
- promise.resolve(true)
379
- }
284
+ // MARK: - Helper and Cleanup Methods
380
285
 
381
- val errorListener = Consumer<Exception> { e ->
382
- promise.reject("NONSITE_ERROR", "Error executing nonSite", e)
383
- }
384
-
385
- composer.getExperience(request, listener, errorListener)
386
- } catch (e: Exception) {
387
- promise.reject("NONSITE_ERROR", "Error executing nonSite", e)
286
+ @ReactMethod
287
+ fun clearConfiguration(promise: Promise) {
288
+ try {
289
+ if (!validateInitialization(promise)) return
290
+ composerImpl?.clearTags()
291
+ composerImpl?.clearCustomVariables()
292
+ promise.resolve(true)
293
+ } catch (e: Exception) {
294
+ promise.reject("CLEAR_ERROR", "Error clearing configurations", e)
295
+ }
388
296
  }
389
- }
390
-
391
- @ReactMethod
392
- fun userSegmentTrue(promise: Promise) {
393
- try {
394
- val composer = Composer.getInstance()
395
- if (composer == null) {
396
- promise.reject("NOT_INITIALIZED", "Piano Composer is not initialized.")
397
- return
398
- }
399
-
400
- val request = ExperienceRequest.Builder()
401
- .debug(true)
402
- .tags(tags)
403
- .zone(zoneId)
404
- .referer(referrer)
405
- .customVariables(customVariables.mapValues { listOf(it.value) })
406
- .url(url)
407
- .userToken(userToken)
408
- .build()
409
-
410
- val listener = ExecuteExperienceListener { events ->
411
- promise.resolve(true)
412
- }
413
-
414
- val errorListener = Consumer<Exception> { e ->
415
- promise.reject("USERSEGMENT_ERROR", "Error executing userSegmentTrue", e)
416
- }
417
297
 
418
- composer.getExperience(request, listener, errorListener)
419
- } catch (e: Exception) {
420
- promise.reject("USERSEGMENT_ERROR", "Error executing userSegmentTrue", e)
298
+ private fun validateInitialization(promise: Promise): Boolean {
299
+ if (!isInitialized || composerImpl == null) {
300
+ promise.reject("NOT_INITIALIZED", "Piano SDK not initialized. Call initialize() first.")
301
+ return false
302
+ }
303
+ return true
421
304
  }
422
- }
423
-
424
- @ReactMethod
425
- fun userSegmentFalse(promise: Promise) {
426
- try {
427
- val composer = Composer.getInstance()
428
- if (composer == null) {
429
- promise.reject("NOT_INITIALIZED", "Piano Composer is not initialized.")
430
- return
431
- }
432
-
433
- val request = ExperienceRequest.Builder()
434
- .debug(true)
435
- .tags(tags)
436
- .zone(zoneId)
437
- .referer(referrer)
438
- .customVariables(customVariables.mapValues { listOf(it.value) })
439
- .url(url)
440
- .userToken(userToken)
441
- .build()
442
-
443
- val listener = ExecuteExperienceListener { events ->
444
- promise.resolve(true)
445
- }
446
305
 
447
- val errorListener = Consumer<Exception> { e ->
448
- promise.reject("USERSEGMENT_ERROR", "Error executing userSegmentFalse", e)
449
- }
450
-
451
- composer.getExperience(request, listener, errorListener)
452
- } catch (e: Exception) {
453
- promise.reject("USERSEGMENT_ERROR", "Error executing userSegmentFalse", e)
306
+ private fun validateParameter(value: String?, paramName: String, promise: Promise): Boolean {
307
+ if (value.isNullOrBlank()) {
308
+ promise.reject("INVALID_PARAMETER", "Parameter '$paramName' cannot be empty")
309
+ return false
310
+ }
311
+ return true
454
312
  }
455
- }
456
-
457
- @ReactMethod
458
- fun meterActive(promise: Promise) {
459
- try {
460
- val composer = Composer.getInstance()
461
- if (composer == null) {
462
- promise.reject("NOT_INITIALIZED", "Piano Composer is not initialized.")
463
- return
464
- }
465
313
 
466
- val request = ExperienceRequest.Builder()
467
- .debug(true)
468
- .tags(tags)
469
- .zone(zoneId)
470
- .referer(referrer)
471
- .customVariables(customVariables.mapValues { listOf(it.value) })
472
- .url(url)
473
- .userToken(userToken)
474
- .build()
475
-
476
- val listener = ExecuteExperienceListener { events ->
477
- promise.resolve(true)
478
- }
479
-
480
- val errorListener = Consumer<Exception> { e ->
481
- promise.reject("METER_ERROR", "Error executing meterActive", e)
482
- }
483
-
484
- composer.getExperience(request, listener, errorListener)
485
- } catch (e: Exception) {
486
- promise.reject("METER_ERROR", "Error executing meterActive", e)
314
+ private fun validateArray(array: ReadableArray?, paramName: String, promise: Promise): Boolean {
315
+ if (array == null || array.size() == 0) {
316
+ promise.reject("INVALID_PARAMETER", "Array '$paramName' cannot be empty")
317
+ return false
318
+ }
319
+ return true
487
320
  }
488
- }
489
-
490
- @ReactMethod
491
- fun meterExpired(promise: Promise) {
492
- try {
493
- val composer = Composer.getInstance()
494
- if (composer == null) {
495
- promise.reject("NOT_INITIALIZED", "Piano Composer is not initialized.")
496
- return
497
- }
498
-
499
- val request = ExperienceRequest.Builder()
500
- .debug(true)
501
- .tags(tags)
502
- .zone(zoneId)
503
- .referer(referrer)
504
- .customVariables(customVariables.mapValues { listOf(it.value) })
505
- .url(url)
506
- .userToken(userToken)
507
- .build()
508
-
509
- val listener = ExecuteExperienceListener { events ->
510
- promise.resolve(true)
511
- }
512
-
513
- val errorListener = Consumer<Exception> { e ->
514
- promise.reject("METER_ERROR", "Error executing meterExpired", e)
515
- }
516
321
 
517
- composer.getExperience(request, listener, errorListener)
518
- } catch (e: Exception) {
519
- promise.reject("METER_ERROR", "Error executing meterExpired", e)
322
+ private fun validateMap(map: ReadableMap?, paramName: String, promise: Promise): Boolean {
323
+ if (map == null) {
324
+ promise.reject("INVALID_PARAMETER", "Map '$paramName' cannot be empty")
325
+ return false
326
+ }
327
+ return true
520
328
  }
521
- }
522
-
523
- @ReactMethod
524
- fun composerExecutionCompleted(promise: Promise) {
525
- try {
526
- val composer = Composer.getInstance()
527
- if (composer == null) {
528
- promise.reject("NOT_INITIALIZED", "Piano Composer is not initialized.")
529
- return
530
- }
531
-
532
- val request = ExperienceRequest.Builder()
533
- .debug(true)
534
- .tags(tags)
535
- .zone(zoneId)
536
- .referer(referrer)
537
- .customVariables(customVariables.mapValues { listOf(it.value) })
538
- .url(url)
539
- .userToken(userToken)
540
- .build()
541
-
542
- val listener = ExecuteExperienceListener { events ->
543
- promise.resolve(true)
544
- }
545
-
546
- val errorListener = Consumer<Exception> { e ->
547
- promise.reject("COMPLETION_ERROR", "Error executing composerExecutionCompleted", e)
548
- }
549
-
550
- composer.getExperience(request, listener, errorListener)
551
- } catch (e: Exception) {
552
- promise.reject("COMPLETION_ERROR", "Error executing composerExecutionCompleted", e)
329
+
330
+ companion object {
331
+ const val NAME = "SdkPianoio"
553
332
  }
554
- }
555
-
556
- companion object {
557
- const val NAME = "SdkPianoio"
558
- }
559
333
  }