Skip to content
This repository has been archived by the owner on Aug 1, 2024. It is now read-only.

Commit

Permalink
DEV-135 paywalls (#6)
Browse files Browse the repository at this point in the history
* DEV-135 Moved models to a dedicated file

* DEV-135 Version bump to 1.4.0, updated Glue dependency to 1.4.0

* DEV-135 Updating dependencies

* DEV-135 Updated android dependencies

* DEV-135 Updated native modules code to allow showing a native paywall

* DEV-135 Added basic kotlin paywall listener

* DEV-135 Splitted index.ts into glassfy.ts to simplify imports, added code requried to show paywalls.

* DEV-135 Can now show paywalls on iOS

* DEV-134 Added required context to show paywall, added required methods to plugin

* DEV-135 Fixed import for PaywallListener

* DEV-134 Moved to latest iOS Api

* DEV-135 Can now use preload options when showing paywalls

* DEV-135 Updated as of latest changes in android and ios sdk

* DEV-92 Updated behaviour of onClose handler

* DEV-135 fixed return type of restorePurchases

* DEV-134 Updated as of latest changes in android sdk

* Updated as of latest changes in android sdk

* DEV-92 Updated as of latest changes in Android SDK

---------

Co-authored-by: Federico Curzel <[email protected]>
  • Loading branch information
fcurzel and curzel-it authored Jun 14, 2023
1 parent 6167262 commit bdbd84e
Show file tree
Hide file tree
Showing 19 changed files with 4,976 additions and 6,203 deletions.
6 changes: 4 additions & 2 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ def kotlin_version = getExtOrDefault('kotlinVersion')

dependencies {
// noinspection GradleDynamicVersion
api 'com.facebook.react:react-native:+'
implementation("io.glassfy:androidglue:1.3.11")
api 'com.facebook.react:react-android:0.71.4'
implementation("io.glassfy:androidglue:1.4.0")
implementation("io.glassfy:androidsdk:1.4.0")
implementation("io.glassfy:paywall:1.4.0")
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
}
4 changes: 2 additions & 2 deletions android/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
GlassfyModule_kotlinVersion=1.6.10
GlassfyModule_minSdkVersion=21
GlassfyModule_kotlinVersion=1.8.10
GlassfyModule_minSdkVersion=24
GlassfyModule_compileSdkVersion=31
GlassfyModule_targetSdkVersion=28
android.useAndroidX=true
Original file line number Diff line number Diff line change
@@ -1,28 +1,38 @@
package com.reactnativeglassfymodule

import android.content.Intent
import android.net.Uri
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.DialogFragment
import com.facebook.react.bridge.*
import com.facebook.react.modules.core.DeviceEventManagerModule
import io.glassfy.glue.GlassfyGlue
import io.glassfy.paywall.GlassfyPaywall
import kotlinx.coroutines.MainScope
import org.json.JSONArray
import org.json.JSONObject

class GlassfyModuleModule(reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {

private var paywallFragment: DialogFragment? = null
private var paywallListener: ReactPaywallListener? = null

override fun getName(): String {
return "GlassfyModule"
}

fun JSONObject.toMap(): Map<String, *> = keys().asSequence().associateWith {
when (val value = this[it])
{
is JSONArray ->
{
when (val value = this[it]) {
is JSONArray -> {
val map = (0 until value.length()).associate { Pair(it.toString(), value[it]) }
JSONObject(map).toMap().values.toList()
}

is JSONObject -> value.toMap()
JSONObject.NULL -> null
else -> value
else -> value
}
}

Expand All @@ -32,14 +42,12 @@ class GlassfyModuleModule(reactContext: ReactApplicationContext) :
return
}

if (value==null) {
promise.resolve(null)
return;
}
if (value == null) {
promise.resolve(null)
return;
}

// convert json string to JSONOBJECT
val jo = value.let { JSONObject(it).toMap() }
// convert JSONOBJECT to NativeMap
val map = Arguments.makeNativeMap(jo)
promise.resolve(map)
}
Expand All @@ -52,11 +60,7 @@ class GlassfyModuleModule(reactContext: ReactApplicationContext) :
@ReactMethod
fun initialize(apiKey: String, watcherMode: Boolean, version: String, promise: Promise) {
GlassfyGlue.initialize(
this.reactApplicationContext,
apiKey,
watcherMode,
"react-native",
version
this.reactApplicationContext, apiKey, watcherMode, "react-native", version
) { value, error -> pluginCompletion(promise, value, error) }
}

Expand Down Expand Up @@ -95,9 +99,7 @@ class GlassfyModuleModule(reactContext: ReactApplicationContext) :
fun skuWithIdAndStore(identifier: String, store: Int, promise: Promise) {
GlassfyGlue.skuWithIdAndStore(identifier, store) { value, error ->
pluginCompletion(
promise,
value,
error
promise, value, error
)
}
}
Expand All @@ -106,9 +108,7 @@ class GlassfyModuleModule(reactContext: ReactApplicationContext) :
fun connectCustomSubscriber(subscriberId: String, promise: Promise) {
GlassfyGlue.connectCustomSubscriber(subscriberId) { value, error ->
pluginCompletion(
promise,
value,
error
promise, value, error
)
}
}
Expand All @@ -117,9 +117,7 @@ class GlassfyModuleModule(reactContext: ReactApplicationContext) :
fun connectPaddleLicenseKey(licenseKey: String, force: Int, promise: Promise) {
GlassfyGlue.connectPaddleLicenseKey(licenseKey, force == 1) { value, error ->
pluginCompletion(
promise,
value,
error
promise, value, error
)
}
}
Expand All @@ -128,9 +126,7 @@ class GlassfyModuleModule(reactContext: ReactApplicationContext) :
fun connectGlassfyUniversalCode(universalCode: String, force: Int, promise: Promise) {
GlassfyGlue.connectGlassfyUniversalCode(universalCode, force == 1) { value, error ->
pluginCompletion(
promise,
value,
error
promise, value, error
)
}
}
Expand All @@ -139,16 +135,14 @@ class GlassfyModuleModule(reactContext: ReactApplicationContext) :
fun setEmailUserProperty(email: String, promise: Promise) {
GlassfyGlue.setEmailUserProperty(email) { value, error ->
pluginCompletion(
promise,
value,
error
promise, value, error
)
}
}

@ReactMethod
fun setExtraUserProperty(extra: ReadableMap, promise: Promise) {
val map = HashMap<String,String>()
val map = HashMap<String, String>()

extra.entryIterator.forEach { entry ->
val value = entry.value
Expand All @@ -161,9 +155,7 @@ class GlassfyModuleModule(reactContext: ReactApplicationContext) :
}
GlassfyGlue.setExtraUserProperty(map) { value, error ->
pluginCompletion(
promise,
value,
error
promise, value, error
)
}
}
Expand All @@ -174,26 +166,24 @@ class GlassfyModuleModule(reactContext: ReactApplicationContext) :
}

@ReactMethod
fun purchaseSku(sku: ReadableMap, promise: Promise) {
val activity = this.reactApplicationContext.currentActivity
val skuId = sku.getString("skuId")
fun purchaseSku(sku: ReadableMap, promise: Promise) {
val activity = this.reactApplicationContext.currentActivity
val skuId = sku.getString("skuId")

if (activity == null) {
promise.reject("Invalid Android Activity", "Invalid Android Activity")
return
}
if (skuId == null) {
promise.reject("Invalid SKU", "Invalid SKU")
return
}
GlassfyGlue.purchaseSku(activity, skuId) { value, error ->
pluginCompletion(
promise,
value,
error
)
}
if (activity == null) {
promise.reject("Invalid Android Activity", "Invalid Android Activity")
return
}
if (skuId == null) {
promise.reject("Invalid SKU", "Invalid SKU")
return
}
GlassfyGlue.purchaseSku(activity, skuId) { value, error ->
pluginCompletion(
promise, value, error
)
}
}

@ReactMethod
fun restorePurchases(promise: Promise) {
Expand All @@ -206,22 +196,21 @@ class GlassfyModuleModule(reactContext: ReactApplicationContext) :
}

@ReactMethod
fun subscribeOnPurchaseDelegate() {}
fun subscribeOnPurchaseDelegate() {
}

@ReactMethod
fun setAttribution(type: Int, value: String, promise: Promise) {
GlassfyGlue.setAttribution(type, value) { res, error ->
pluginCompletion(
promise,
res,
error
promise, res, error
)
}
}

@ReactMethod
fun setAttributions(items: ReadableArray, promise: Promise) {
val listItems = mutableListOf<Map<String,Any?>>()
val listItems = mutableListOf<Map<String, Any?>>()
for (i in 0 until items.size()) {
val item = items.getMap(i)?.toHashMap()
if (item != null) {
Expand All @@ -234,10 +223,80 @@ class GlassfyModuleModule(reactContext: ReactApplicationContext) :

GlassfyGlue.setAttributions(listItems) { value, error ->
pluginCompletion(
promise,
value,
error
promise, value, error
)
}
}

@ReactMethod
fun _openUrl(urlString: String, promise: Promise) {
try {
val i = Intent(Intent.ACTION_VIEW).apply { data = Uri.parse(urlString) }
currentActivity?.startActivity(i)
} catch (e: Exception) {
pluginCompletion(promise, null, e.toString())
}
}

@ReactMethod
fun _closePaywall(promise: Promise) {
MainScope().run {
paywallFragment?.dismiss()
paywallFragment = null
paywallListener = null
}
pluginCompletion(promise, null, null)
}

@ReactMethod
fun _paywall(remoteConfig: String, awaitLoading: Boolean, promise: Promise) {
if (paywallFragment != null) {
promise.reject(
"Only one paywall can be shown at a time",
"Only one paywall can be shown at a time, please call `GlassfyPaywall.close()`"
)
return
}
val activity = currentActivity as? AppCompatActivity
if (activity == null) {
promise.reject("No activity", "Could not find a AppCompatActivity")
return
}

val listener = ReactPaywallListener { eventName, payload ->
sendEvent(eventName, payload)
}
paywallListener = listener
GlassfyPaywall.fragment(activity, remoteConfig, awaitLoading) { paywall, _ ->
MainScope().run {
paywall?.setCloseHandler(listener.onClose)
paywall?.setPurchaseHandler(listener.onPurchase)
paywall?.setRestoreHandler(listener.onRestore)
paywall?.setLinkHandler(listener.onLink)
paywall?.show(activity.supportFragmentManager, "paywall")
}
paywallFragment = paywall
pluginCompletion(promise, null, null)
}
}

private fun sendEvent(eventName: String, value: JSONObject) {
val params = Arguments.createMap()
params.putString("event", eventName)
params.putString("encodedData", value.toString(2))

reactApplicationContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit("paywallEvent", params)
}

@ReactMethod
fun addListener(eventName: String) {
// ...
}

@ReactMethod
fun removeListeners(count: Int) {
// ...
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.reactnativeglassfymodule

import android.net.Uri
import android.util.Log
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.Callback
import com.facebook.react.bridge.WritableMap
import io.glassfy.androidsdk.GlassfyError
import io.glassfy.androidsdk.model.Sku
import io.glassfy.androidsdk.model.Transaction
import io.glassfy.paywall.PaywallFragment
import io.glassfy.glue.encodedJson
import org.json.JSONObject

internal class ReactPaywallListener(private val handler: (String, JSONObject) -> Unit) {
val onClose: (Transaction?, GlassfyError?) -> Unit = { transaction, error ->
Log.d("ReactPaywallListener", "onClose")
val payload = JSONObject().apply {
put("transaction", transaction?.encodedJson())
put("error", error?.toString())
}
handler("onClose", payload)
}

val onLink: (Uri) -> Unit = { url ->
Log.d("ReactPaywallListener", "onLink $url")
val payload = JSONObject().apply {
put("url", url.toString())
}
handler("onLink", payload)
}

val onRestore: () -> Unit = {
Log.d("ReactPaywallListener", "onRestore")
val payload = JSONObject().apply {
// ...
}
handler("onRestore", payload)
}

val onPurchase: (Sku) -> Unit = { sku ->
Log.d("ReactPaywallListener", "onPurchase")
val payload = JSONObject().apply {
put("sku", sku.encodedJson())
}
handler("onPurchase", payload)
}
}
1 change: 1 addition & 0 deletions examples/example_app/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ buildscript {
}
}
repositories {
mavenLocal()
google()
mavenCentral()
}
Expand Down
Loading

0 comments on commit bdbd84e

Please sign in to comment.