package com.blaze.rtnblazesdk import android.util.Log import com.blaze.blazesdk.analytics.models.BlazeAnalyticsEvent import com.blaze.blazesdk.delegates.BlazePlayerEntryPointDelegate import com.blaze.blazesdk.delegates.BlazeSDKDelegate import com.blaze.blazesdk.delegates.models.BlazeCTAActionType import com.blaze.blazesdk.delegates.models.BlazePlayerEvent import com.blaze.blazesdk.delegates.models.BlazePlayerType import com.blaze.blazesdk.external_modules.BlazeExternalModulesBinder import com.blaze.blazesdk.features.shared.models.ui_shared.BlazeLinkActionHandleType import com.blaze.blazesdk.prefetch.models.BlazeCachingLevel import com.blaze.blazesdk.shared.BlazeSDK import com.blaze.blazesdk.shared.results.BlazeResult import com.blaze.blazesdk.style.players.moments.BlazeMomentsPlayerStyle import com.blaze.blazesdk.style.players.stories.BlazeStoryPlayerStyle import com.blaze.rtnblazesdk.events.SdkModuleJsEvent import com.blaze.rtnblazesdk.utils.extractCachingLevel import com.blaze.rtnblazesdk.utils.extractDataSource import com.blaze.rtnblazesdk.utils.extractMomentsPlayerStyle import com.blaze.rtnblazesdk.utils.extractStoriesPlayerStyle import com.blaze.rtnblazesdk.utils.handleResult import com.blaze.rtnblazesdk.utils.rejectWith import com.facebook.react.bridge.NoSuchKeyException import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReadableMap class BlazeSdkModule(private val context: ReactApplicationContext): NativeBlazeSdkSpec(context) { private val reactNativeSDKHelper = BlazeReactSDKHelper() private var appOverridesCTAHandling: Boolean = false private val globalDelegate = object : BlazeSDKDelegate { override fun onErrorThrown(error: BlazeResult.Error) { val event = SdkModuleJsEvent.ErrorThrown(error) sendJsEvent(event) } override fun onEventTriggered(eventData: BlazeAnalyticsEvent) { val event = SdkModuleJsEvent.AnalyticEventTriggered(eventData) sendJsEvent(event) } } private val playerEntryPointDelegate = object : BlazePlayerEntryPointDelegate { override fun onDataLoadComplete( playerType: BlazePlayerType, sourceId: String?, itemsCount: Int, result: BlazeResult ) { val event = SdkModuleJsEvent.DataLoadCompleted(playerType, sourceId, itemsCount, result) sendJsEvent(event) } override fun onDataLoadStarted(playerType: BlazePlayerType, sourceId: String?) { val event = SdkModuleJsEvent.DataLoadStarted(playerType, sourceId) sendJsEvent(event) } override fun onPlayerDidAppear(playerType: BlazePlayerType, sourceId: String?) { val event = SdkModuleJsEvent.PlayerDidAppear(playerType, sourceId) sendJsEvent(event) } override fun onPlayerDidDismiss(playerType: BlazePlayerType, sourceId: String?) { val event = SdkModuleJsEvent.PlayerDidDismiss(playerType, sourceId) sendJsEvent(event) } override fun onTriggerCTA( playerType: BlazePlayerType, sourceId: String?, actionType: BlazeCTAActionType, actionParam: String ): Boolean { val event = SdkModuleJsEvent.TriggerCTA(playerType, sourceId, actionType, actionParam) sendJsEvent(event) return appOverridesCTAHandling } override fun onTriggerPlayerBodyTextLink( playerType: BlazePlayerType, sourceId: String?, actionParam: String ): BlazeLinkActionHandleType { val event = SdkModuleJsEvent.TriggerPlayerBodyTextLink(playerType, sourceId, actionParam) sendJsEvent(event) return BlazeLinkActionHandleType.DEEPLINK } override fun onPlayerEventTriggered( playerType: BlazePlayerType, sourceId: String?, event: BlazePlayerEvent ) { try { val event = SdkModuleJsEvent.TriggerPlayerEventTriggered( playerType = playerType, sourceId = sourceId, event = event ) sendJsEvent(event) } catch (e: Throwable) { Log.e(TAG, "onPlayerEventTriggered: Exception - $e") } } } init { BlazeExternalModulesBinder.reactNativeSDKHelper = reactNativeSDKHelper } override fun init(options: ReadableMap, promise: Promise) { try { setAppOverrideCtaHandling(options) val cachingLevel = options.extractCachingLevel() ?: BlazeCachingLevel.DEFAULT BlazeSdkModuleRepo.cachingLevel = cachingLevel BlazeSDK.init( apiKey = options.getString(KEY_INIT_API_KEY)!!, externalUserId = options.getString(KEY_INIT_EXTERNAL_USER_ID), geoLocation = options.getString(KEY_INIT_GEO_LOCATION), cachingSize = options.getInt(KEY_INIT_CACHING_SIZE), cachingLevel = cachingLevel, completionBlock = { promise.resolve(BlazeResult.Success(true).toString()) }, errorBlock = { e: BlazeResult.Error -> promise.handleResult(e) }, sdkDelegate = globalDelegate, playerEntryPointDelegate = playerEntryPointDelegate, ) val reactStoryPlayerStyle = options.getMap(KEY_INIT_DEFAULT_STORY_PLAYER_STYLE) val reactMomentPlayerStyle = options.getMap(KEY_INIT_DEFAULT_MOMENT_PLAYER_STYLE) reactStoryPlayerStyle.extractStoriesPlayerStyle(context).also { BlazeSDK.setDefaultStoryPlayerStyle(it) } reactMomentPlayerStyle.extractMomentsPlayerStyle(context).also { BlazeSDK.setDefaultMomentsPlayerStyle(it) } } catch (e: Exception) { promise.reject("BlazeSDK", e.toString()) Log.e(TAG, "init: Exception - $e") } } private fun ReadableMap.extractCachingLevel(): BlazeCachingLevel? { return getString(KEY_INIT_CACHING_LEVEL)?.extractCachingLevel() } private fun setAppOverrideCtaHandling(options: ReadableMap) { // getBoolean can throw exception while getString will return null if no such key try { appOverridesCTAHandling = options.getBoolean(KEY_INIT_APP_OVERRIDES_CTA_HANDLING) } catch(e: NoSuchKeyException) { Log.e(TAG, "setAppOverrideCtaHandling: NoSuchKeyException - ${e.message}") } } private fun sendJsEvent(event: SdkModuleJsEvent) { context.emitDeviceEvent(event.eventName, event.params) } override fun playStory(options: ReadableMap, promise: Promise) { try { val storyId = options.getString(KEY_STORY_OPT_STORY_ID) ?: run { promise.rejectWith("no story id") return } val pageId = options.getString(KEY_STORY_OPT_PAGE_ID) val playerStyle = options.getMap(KEY_ENTRY_POINT_PLAYER_THEME) .extractStoriesPlayerStyle(context) BlazeSDK.playStory( storyId = storyId, pageId = pageId, storyPlayerStyle = playerStyle ) { result -> promise.handleResult(result) } } catch (e: Exception) { promise.rejectWith(e.toString()) } } override fun playMoment(options: ReadableMap, promise: Promise) { try { val momentId = options.getString(KEY_STORY_OPT_MOMENT_ID) ?: run { promise.rejectWith("no moment id") return } val playerStyle = options.getMap(KEY_ENTRY_POINT_PLAYER_THEME) .extractMomentsPlayerStyle(context) BlazeSDK.playMoment( momentId = momentId, momentsPlayerStyle = playerStyle) { result -> promise.handleResult(result) } } catch (e: Exception) { promise.rejectWith(e.toString()) } } override fun prepareStories(options: ReadableMap, promise: Promise) { try { val dataSource = options.getMap(KEY_ENTRY_POINT_DATA_SOURCE)?.extractDataSource() ?: run { promise.rejectWith("Error parsing data source for prepareStories") return } BlazeSDK.prepareStories(dataSource) { result -> promise.handleResult(result) } } catch (e: Exception) { promise.rejectWith(e.toString()) } } override fun prepareMoments(options: ReadableMap, promise: Promise) { try { val dataSource = options.getMap(KEY_ENTRY_POINT_DATA_SOURCE)?.extractDataSource() ?: run { promise.rejectWith("Error parsing data source for prepareMoments") return } BlazeSDK.prepareMoments(dataSource) { result -> promise.handleResult(result) } } catch (e: Exception) { promise.rejectWith(e.toString()) } } override fun dismissPlayer(promise: Promise) { BlazeSDK.dismissCurrentPlayer() promise.resolve(true) } override fun isInitialized(): Boolean { return BlazeSDK.isInitialized() } override fun setExternalUserId(externalUserId: String?, promise: Promise) { BlazeSDK.setExternalUserId(externalUserId) { result -> promise.handleResult(result) } } override fun setDoNotTrack(doNotTrack: Boolean, promise: Promise) { BlazeSDK.setDoNotTrack(doNotTrack) promise.resolve(doNotTrack) } override fun playStories(options: ReadableMap, promise: Promise) { try { val dataSource = options.getMap(KEY_ENTRY_POINT_DATA_SOURCE)?.extractDataSource() ?: run { promise.rejectWith("Error parsing data source for playStories") return } val playerStyle = options.getMap(KEY_ENTRY_POINT_PLAYER_THEME) .extractStoriesPlayerStyle(context) BlazeSDK.playStories( dataSource = dataSource, storyPlayerStyle = playerStyle) { result -> promise.handleResult(result) } } catch (e: Exception) { promise.rejectWith(e.toString()) } } override fun playMoments(options: ReadableMap, promise: Promise) { try { val dataSource = options.getMap(KEY_ENTRY_POINT_DATA_SOURCE)?.extractDataSource() ?: run { promise.rejectWith("Error parsing data source for playMoments") return } val playerStyle = options.getMap(KEY_ENTRY_POINT_PLAYER_THEME) .extractMomentsPlayerStyle(context) BlazeSDK.playMoments( dataSource = dataSource, momentsPlayerStyle = playerStyle) { result -> promise.handleResult(result) } } catch (e: Exception) { promise.rejectWith(e.toString()) } } override fun handleUniversalLink(link: String, promise: Promise) { try { BlazeSDK.handleUniversalLink(link){ result -> promise.handleResult(result) } } catch (e: Exception) { promise.rejectWith(e.toString()) } } override fun canHandleUniversalLink(link: String, promise: Promise) { try { val canHandle = BlazeSDK.canHandleUniversalLink(link) promise.resolve(canHandle) } catch (e: Exception) { promise.rejectWith(e.toString()) } } override fun updateGeoRestriction(geoLocation: String?, promise: Promise) { val updatedGeoLocation = if (geoLocation.isNullOrEmpty()) null else geoLocation try { BlazeSDK.updateGeoRestriction(updatedGeoLocation){ result -> promise.handleResult(result) } } catch (e: Exception) { promise.rejectWith(e.toString()) } } override fun canHandlePushNotification(payload: ReadableMap, promise: Promise) { try { // This is needed for now since the Android SDK requires . Remove if it changes. val hashMap = payload.toHashMap() as HashMap val canHandle = BlazeSDK.canHandlePushNotification(hashMap) promise.resolve(canHandle) } catch (e: Exception) { promise.rejectWith("Error parsing the payload: $e") } } override fun handleNotificationPayload(payload: ReadableMap, promise: Promise) { try { // This is needed for now since the Android SDK requires . Remove if it changes. val hashMap = payload.toHashMap() as HashMap BlazeSDK.handlePushNotificationPayload(hashMap){ result -> promise.handleResult(result) } } catch (e: Exception) { promise.rejectWith(e.toString()) } } companion object { val TAG: String = BlazeSdkModule::class.java.simpleName const val KEY_INIT_API_KEY = "apiKey" const val KEY_INIT_EXTERNAL_USER_ID = "externalUserId" const val KEY_INIT_GEO_LOCATION = "geoLocation" const val KEY_INIT_CACHING_SIZE = "cachingSize" const val KEY_INIT_CACHING_LEVEL = "cachingLevel" const val KEY_INIT_APP_OVERRIDES_CTA_HANDLING = "appOverridesCTAHandling" const val KEY_INIT_DEFAULT_STORY_PLAYER_STYLE = "defaultStoryPlayerStyle" const val KEY_INIT_DEFAULT_MOMENT_PLAYER_STYLE = "defaultMomentsPlayerStyle" const val KEY_STORY_OPT_STORY_ID = "storyId" const val KEY_STORY_OPT_PAGE_ID = "pageId" const val KEY_STORY_OPT_MOMENT_ID = "momentId" const val KEY_ENTRY_POINT_DATA_SOURCE = "dataSource" const val KEY_ENTRY_POINT_PLAYER_THEME = "playerStyle" } }