react-native-purchases-ui 9.2.3 → 9.4.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/RNPaywalls.podspec +1 -1
- package/android/build.gradle +3 -3
- package/android/src/main/java/com/revenuecat/purchases/react/ui/BasePaywallViewManager.kt +25 -5
- package/android/src/main/java/com/revenuecat/purchases/react/ui/PaywallFooterViewManager.kt +7 -2
- package/android/src/main/java/com/revenuecat/purchases/react/ui/PaywallViewManager.kt +7 -2
- package/android/src/main/java/com/revenuecat/purchases/react/ui/RNCustomerCenterModule.kt +1 -2
- package/android/src/main/java/com/revenuecat/purchases/react/ui/RNPaywallsModule.kt +12 -3
- package/android/src/main/java/com/revenuecat/purchases/react/ui/RNPurchasesConverters.kt +27 -0
- package/android/src/main/java/com/revenuecat/purchases/react/ui/customercenter/events/CustomerCenterEventName.kt +2 -1
- package/android/src/main/java/com/revenuecat/purchases/react/ui/views/WrappedPaywallComposeView.kt +3 -2
- package/android/src/main/java/com/revenuecat/purchases/react/ui/views/WrappedPaywallFooterComposeView.kt +12 -2
- package/ios/CustomerCenterViewManager.m +6 -0
- package/ios/CustomerCenterViewWrapper.h +2 -0
- package/ios/CustomerCenterViewWrapper.m +24 -1
- package/ios/PaywallViewWrapper.m +45 -1
- package/ios/RNCustomerCenter.m +9 -0
- package/ios/RNPaywalls.m +8 -0
- package/lib/commonjs/index.js +35 -12
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/preview/previewComponents.js +35 -1
- package/lib/commonjs/preview/previewComponents.js.map +1 -1
- package/lib/module/index.js +36 -13
- package/lib/module/index.js.map +1 -1
- package/lib/module/preview/previewComponents.js +33 -0
- package/lib/module/preview/previewComponents.js.map +1 -1
- package/lib/typescript/src/index.d.ts +19 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/preview/previewComponents.d.ts +6 -0
- package/lib/typescript/src/preview/previewComponents.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/index.tsx +57 -12
- package/src/preview/previewComponents.tsx +47 -2
package/RNPaywalls.podspec
CHANGED
@@ -17,6 +17,6 @@ Pod::Spec.new do |spec|
|
|
17
17
|
spec.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
|
18
18
|
|
19
19
|
spec.dependency "React-Core"
|
20
|
-
spec.dependency "PurchasesHybridCommonUI", '17.
|
20
|
+
spec.dependency "PurchasesHybridCommonUI", '17.5.1'
|
21
21
|
spec.swift_version = '5.7'
|
22
22
|
end
|
package/android/build.gradle
CHANGED
@@ -8,7 +8,7 @@ buildscript {
|
|
8
8
|
}
|
9
9
|
|
10
10
|
dependencies {
|
11
|
-
classpath "com.android.tools.build:gradle:8.
|
11
|
+
classpath "com.android.tools.build:gradle:8.13.0"
|
12
12
|
// noinspection DifferentKotlinGradleVersion
|
13
13
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
14
14
|
}
|
@@ -59,7 +59,7 @@ android {
|
|
59
59
|
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
60
60
|
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
61
61
|
versionCode 1
|
62
|
-
versionName '9.
|
62
|
+
versionName '9.4.0'
|
63
63
|
}
|
64
64
|
|
65
65
|
buildTypes {
|
@@ -91,7 +91,7 @@ dependencies {
|
|
91
91
|
//noinspection GradleDynamicVersion
|
92
92
|
implementation "com.facebook.react:react-native:+"
|
93
93
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
94
|
-
implementation 'com.revenuecat.purchases:purchases-hybrid-common-ui:17.
|
94
|
+
implementation 'com.revenuecat.purchases:purchases-hybrid-common-ui:17.5.1'
|
95
95
|
implementation 'androidx.compose.ui:ui-android:1.5.4'
|
96
96
|
implementation "androidx.appcompat:appcompat:1.6.1"
|
97
97
|
}
|
@@ -10,6 +10,8 @@ import com.facebook.react.uimanager.ThemedReactContext
|
|
10
10
|
import com.facebook.react.uimanager.UIManagerHelper
|
11
11
|
import com.facebook.react.uimanager.annotations.ReactProp
|
12
12
|
import com.facebook.react.uimanager.events.Event
|
13
|
+
import com.revenuecat.purchases.Package
|
14
|
+
import com.revenuecat.purchases.PresentedOfferingContext
|
13
15
|
import com.revenuecat.purchases.hybridcommon.ui.PaywallListenerWrapper
|
14
16
|
import com.revenuecat.purchases.react.ui.events.OnDismissEvent
|
15
17
|
import com.revenuecat.purchases.react.ui.events.OnPurchaseCancelledEvent
|
@@ -29,9 +31,13 @@ internal abstract class BasePaywallViewManager<T : View> : SimpleViewManager<T>(
|
|
29
31
|
private const val OFFERING_IDENTIFIER = "identifier"
|
30
32
|
private const val OPTION_FONT_FAMILY = "fontFamily"
|
31
33
|
private const val OPTION_DISPLAY_CLOSE_BUTTON = "displayCloseButton"
|
34
|
+
|
35
|
+
private const val OPTION_OFFERING_AVAILABLE_PACKAGES = "availablePackages"
|
36
|
+
|
37
|
+
private const val OPTION_OFFERING_AVAILABLE_PACKAGES_PRESENTED_OFFERING_CONTEXT = "presentedOfferingContext"
|
32
38
|
}
|
33
39
|
|
34
|
-
abstract fun setOfferingId(view: T,
|
40
|
+
abstract fun setOfferingId(view: T, offeringId: String?, presentedOfferingContext: PresentedOfferingContext? = null)
|
35
41
|
|
36
42
|
abstract fun setDisplayDismissButton(view: T, display: Boolean)
|
37
43
|
|
@@ -69,12 +75,26 @@ internal abstract class BasePaywallViewManager<T : View> : SimpleViewManager<T>(
|
|
69
75
|
// getDynamic crashes if the value is null, that's why we use props?.toHashMap
|
70
76
|
return
|
71
77
|
}
|
78
|
+
|
79
|
+
val offeringMap = options.getDynamic(OPTION_OFFERING).asMap();
|
80
|
+
|
72
81
|
// this is a workaround for the fact that getDynamic doesn't work with null values
|
73
|
-
val offeringIdentifier =
|
74
|
-
|
75
|
-
|
76
|
-
setOfferingId(view, it)
|
82
|
+
val offeringIdentifier = offeringMap.getString(OFFERING_IDENTIFIER)
|
83
|
+
if (offeringIdentifier == null) {
|
84
|
+
return
|
77
85
|
}
|
86
|
+
|
87
|
+
val presentedOfferingContext = getPresentedOfferingContext(offeringIdentifier, offeringMap)
|
88
|
+
|
89
|
+
setOfferingId(view, offeringIdentifier, presentedOfferingContext)
|
90
|
+
}
|
91
|
+
|
92
|
+
private fun getPresentedOfferingContext(offeringIdentifier: String, offeringMap: ReadableMap?) : PresentedOfferingContext {
|
93
|
+
val availablePackages = offeringMap?.getArray(OPTION_OFFERING_AVAILABLE_PACKAGES)?.toArrayList()
|
94
|
+
val firstAvailablePackage = availablePackages?.firstOrNull() as? Map<*, *>;
|
95
|
+
val presentedOfferingContextMap = firstAvailablePackage?.get(OPTION_OFFERING_AVAILABLE_PACKAGES_PRESENTED_OFFERING_CONTEXT) as? Map<*,*>;
|
96
|
+
|
97
|
+
return RNPurchasesConverters.presentedOfferingContext(offeringIdentifier, presentedOfferingContextMap)
|
78
98
|
}
|
79
99
|
|
80
100
|
private fun setFontFamilyProp(view: T, options: ReadableMap?) {
|
@@ -2,6 +2,7 @@ package com.revenuecat.purchases.react.ui
|
|
2
2
|
|
3
3
|
import androidx.core.view.children
|
4
4
|
import com.facebook.react.uimanager.ThemedReactContext
|
5
|
+
import com.revenuecat.purchases.PresentedOfferingContext
|
5
6
|
import com.revenuecat.purchases.react.ui.events.OnMeasureEvent
|
6
7
|
import com.revenuecat.purchases.react.ui.views.WrappedPaywallFooterComposeView
|
7
8
|
import com.revenuecat.purchases.ui.revenuecatui.fonts.CustomFontProvider
|
@@ -72,8 +73,12 @@ internal class PaywallFooterViewManager : BasePaywallViewManager<WrappedPaywallF
|
|
72
73
|
}
|
73
74
|
}
|
74
75
|
|
75
|
-
override fun setOfferingId(
|
76
|
-
view
|
76
|
+
override fun setOfferingId(
|
77
|
+
view: WrappedPaywallFooterComposeView,
|
78
|
+
offeringId: String?,
|
79
|
+
presentedOfferingContext: PresentedOfferingContext?
|
80
|
+
) {
|
81
|
+
view.setOfferingId(offeringId, presentedOfferingContext)
|
77
82
|
}
|
78
83
|
|
79
84
|
override fun setFontFamily(view: WrappedPaywallFooterComposeView, customFontProvider: CustomFontProvider) {
|
@@ -1,6 +1,7 @@
|
|
1
1
|
package com.revenuecat.purchases.react.ui
|
2
2
|
|
3
3
|
import com.facebook.react.uimanager.ThemedReactContext
|
4
|
+
import com.revenuecat.purchases.PresentedOfferingContext
|
4
5
|
import com.revenuecat.purchases.react.ui.views.WrappedPaywallComposeView
|
5
6
|
import com.revenuecat.purchases.ui.revenuecatui.fonts.CustomFontProvider
|
6
7
|
|
@@ -26,8 +27,12 @@ internal class PaywallViewManager : BasePaywallViewManager<WrappedPaywallCompose
|
|
26
27
|
return PaywallViewShadowNode()
|
27
28
|
}
|
28
29
|
|
29
|
-
override fun setOfferingId(
|
30
|
-
view
|
30
|
+
override fun setOfferingId(
|
31
|
+
view: WrappedPaywallComposeView,
|
32
|
+
offeringId: String?,
|
33
|
+
presentedOfferingContext: PresentedOfferingContext?
|
34
|
+
) {
|
35
|
+
view.setOfferingId(offeringId, presentedOfferingContext)
|
31
36
|
}
|
32
37
|
|
33
38
|
override fun setFontFamily(view: WrappedPaywallComposeView, customFontProvider: CustomFontProvider) {
|
@@ -38,10 +38,8 @@ internal class RNCustomerCenterModule(
|
|
38
38
|
) {
|
39
39
|
if (requestCode == REQUEST_CODE_CUSTOMER_CENTER) {
|
40
40
|
if (resultCode == Activity.RESULT_OK) {
|
41
|
-
Log.d(NAME, "Customer Center closed successfully")
|
42
41
|
customerCenterPromise?.resolve(null)
|
43
42
|
} else {
|
44
|
-
Log.d(NAME, "Customer Center closed with result $resultCode")
|
45
43
|
customerCenterPromise?.reject(
|
46
44
|
"CUSTOMER_CENTER_ERROR",
|
47
45
|
"Customer Center closed with result code: $resultCode",
|
@@ -99,6 +97,7 @@ internal class RNCustomerCenterModule(
|
|
99
97
|
|
100
98
|
private fun createCustomerCenterListener(): CustomerCenterListener {
|
101
99
|
return object : CustomerCenterListenerWrapper() {
|
100
|
+
|
102
101
|
override fun onFeedbackSurveyCompletedWrapper(feedbackSurveyOptionId: String) {
|
103
102
|
val params = WritableNativeMap().apply {
|
104
103
|
putString("feedbackSurveyOptionId", feedbackSurveyOptionId)
|
@@ -6,6 +6,7 @@ import com.facebook.react.bridge.Promise
|
|
6
6
|
import com.facebook.react.bridge.ReactApplicationContext
|
7
7
|
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
8
8
|
import com.facebook.react.bridge.ReactMethod
|
9
|
+
import com.facebook.react.bridge.ReadableMap
|
9
10
|
import com.revenuecat.purchases.hybridcommon.ui.PaywallResultListener
|
10
11
|
import com.revenuecat.purchases.hybridcommon.ui.PaywallSource
|
11
12
|
import com.revenuecat.purchases.hybridcommon.ui.PresentPaywallOptions
|
@@ -38,6 +39,7 @@ internal class RNPaywallsModule(
|
|
38
39
|
@ReactMethod
|
39
40
|
fun presentPaywall(
|
40
41
|
offeringIdentifier: String?,
|
42
|
+
presentedOfferingContext: ReadableMap?,
|
41
43
|
displayCloseButton: Boolean?,
|
42
44
|
fontFamily: String?,
|
43
45
|
promise: Promise
|
@@ -45,6 +47,7 @@ internal class RNPaywallsModule(
|
|
45
47
|
presentPaywall(
|
46
48
|
null,
|
47
49
|
offeringIdentifier,
|
50
|
+
presentedOfferingContext,
|
48
51
|
displayCloseButton,
|
49
52
|
fontFamily,
|
50
53
|
promise
|
@@ -55,6 +58,7 @@ internal class RNPaywallsModule(
|
|
55
58
|
fun presentPaywallIfNeeded(
|
56
59
|
requiredEntitlementIdentifier: String,
|
57
60
|
offeringIdentifier: String?,
|
61
|
+
presentedOfferingContext: ReadableMap?,
|
58
62
|
displayCloseButton: Boolean,
|
59
63
|
fontFamily: String?,
|
60
64
|
promise: Promise
|
@@ -62,6 +66,7 @@ internal class RNPaywallsModule(
|
|
62
66
|
presentPaywall(
|
63
67
|
requiredEntitlementIdentifier,
|
64
68
|
offeringIdentifier,
|
69
|
+
presentedOfferingContext,
|
65
70
|
displayCloseButton,
|
66
71
|
fontFamily,
|
67
72
|
promise
|
@@ -81,6 +86,7 @@ internal class RNPaywallsModule(
|
|
81
86
|
private fun presentPaywall(
|
82
87
|
requiredEntitlementIdentifier: String?,
|
83
88
|
offeringIdentifier: String?,
|
89
|
+
presentedOfferingContext: ReadableMap?,
|
84
90
|
displayCloseButton: Boolean?,
|
85
91
|
fontFamilyName: String?,
|
86
92
|
promise: Promise
|
@@ -90,6 +96,11 @@ internal class RNPaywallsModule(
|
|
90
96
|
FontAssetManager.getPaywallFontFamily(fontFamilyName = it, activity.resources.assets)
|
91
97
|
}
|
92
98
|
|
99
|
+
val paywallSource: PaywallSource = offeringIdentifier?.let { offeringIdentifier ->
|
100
|
+
val presentedOfferingContextMap = RNPurchasesConverters.presentedOfferingContext(offeringIdentifier, presentedOfferingContext?.toHashMap())
|
101
|
+
PaywallSource.OfferingIdentifierWithPresentedOfferingContext(offeringIdentifier, presentedOfferingContext=presentedOfferingContextMap)
|
102
|
+
} ?: PaywallSource.DefaultOffering
|
103
|
+
|
93
104
|
// @ReactMethod is not guaranteed to run on the main thread
|
94
105
|
activity.runOnUiThread {
|
95
106
|
presentPaywallFromFragment(
|
@@ -97,9 +108,7 @@ internal class RNPaywallsModule(
|
|
97
108
|
PresentPaywallOptions(
|
98
109
|
requiredEntitlementIdentifier = requiredEntitlementIdentifier,
|
99
110
|
shouldDisplayDismissButton = displayCloseButton,
|
100
|
-
paywallSource =
|
101
|
-
PaywallSource.OfferingIdentifier(it)
|
102
|
-
} ?: PaywallSource.DefaultOffering,
|
111
|
+
paywallSource = paywallSource,
|
103
112
|
paywallResultListener = object : PaywallResultListener {
|
104
113
|
override fun onPaywallResult(paywallResult: String) {
|
105
114
|
promise.resolve(paywallResult)
|
@@ -1,9 +1,12 @@
|
|
1
1
|
package com.revenuecat.purchases.react.ui
|
2
2
|
|
3
|
+
import com.facebook.react.bridge.ReadableMap
|
3
4
|
import com.facebook.react.bridge.WritableArray
|
4
5
|
import com.facebook.react.bridge.WritableMap
|
5
6
|
import com.facebook.react.bridge.WritableNativeArray
|
6
7
|
import com.facebook.react.bridge.WritableNativeMap
|
8
|
+
import com.revenuecat.purchases.PresentedOfferingContext
|
9
|
+
import kotlin.collections.get
|
7
10
|
|
8
11
|
internal object RNPurchasesConverters {
|
9
12
|
|
@@ -45,4 +48,28 @@ internal object RNPurchasesConverters {
|
|
45
48
|
}
|
46
49
|
return writableArray
|
47
50
|
}
|
51
|
+
|
52
|
+
fun presentedOfferingContext(offeringIdentifier: String, presentedOfferingContext: Map<*,*>?) : PresentedOfferingContext {
|
53
|
+
if (presentedOfferingContext == null) {
|
54
|
+
return PresentedOfferingContext(offeringIdentifier)
|
55
|
+
}
|
56
|
+
|
57
|
+
var targetingContext: PresentedOfferingContext.TargetingContext? = null;
|
58
|
+
val targetingContextMap = presentedOfferingContext["targetingContext"] as? Map<*, *>
|
59
|
+
if (targetingContextMap != null) {
|
60
|
+
val revision = (targetingContextMap["revision"] as? Number)?.toInt()
|
61
|
+
val ruleId = targetingContextMap["ruleId"] as? String
|
62
|
+
if (revision != null && ruleId != null) {
|
63
|
+
targetingContext = PresentedOfferingContext.TargetingContext(revision, ruleId)
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
val placementIdentifier = presentedOfferingContext["placementIdentifier"] as? String
|
68
|
+
|
69
|
+
return PresentedOfferingContext(
|
70
|
+
offeringIdentifier,
|
71
|
+
placementIdentifier,
|
72
|
+
targetingContext
|
73
|
+
)
|
74
|
+
}
|
48
75
|
}
|
package/android/src/main/java/com/revenuecat/purchases/react/ui/views/WrappedPaywallComposeView.kt
CHANGED
@@ -2,6 +2,7 @@ package com.revenuecat.purchases.react.ui.views
|
|
2
2
|
|
3
3
|
import android.content.Context
|
4
4
|
import android.util.AttributeSet
|
5
|
+
import com.revenuecat.purchases.PresentedOfferingContext
|
5
6
|
import com.revenuecat.purchases.ui.revenuecatui.PaywallListener
|
6
7
|
import com.revenuecat.purchases.ui.revenuecatui.fonts.FontProvider
|
7
8
|
import com.revenuecat.purchases.ui.revenuecatui.views.PaywallView
|
@@ -20,8 +21,8 @@ class WrappedPaywallComposeView(context: Context) : ComposeViewWrapper<PaywallVi
|
|
20
21
|
wrappedView?.setDismissHandler(dismissHandler)
|
21
22
|
}
|
22
23
|
|
23
|
-
fun setOfferingId(offeringId: String?) {
|
24
|
-
wrappedView?.setOfferingId(offeringId)
|
24
|
+
fun setOfferingId(offeringId: String?, presentedOfferingContext: PresentedOfferingContext? = null) {
|
25
|
+
wrappedView?.setOfferingId(offeringId, presentedOfferingContext)
|
25
26
|
}
|
26
27
|
|
27
28
|
fun setFontProvider(fontProvider: FontProvider?) {
|
@@ -2,6 +2,8 @@ package com.revenuecat.purchases.react.ui.views
|
|
2
2
|
|
3
3
|
import android.content.Context
|
4
4
|
import android.util.AttributeSet
|
5
|
+
import com.revenuecat.purchases.InternalRevenueCatAPI
|
6
|
+
import com.revenuecat.purchases.PresentedOfferingContext
|
5
7
|
import com.revenuecat.purchases.ui.revenuecatui.PaywallListener
|
6
8
|
import com.revenuecat.purchases.ui.revenuecatui.fonts.CustomFontProvider
|
7
9
|
import com.revenuecat.purchases.ui.revenuecatui.views.OriginalTemplatePaywallFooterView
|
@@ -12,8 +14,16 @@ open class WrappedPaywallFooterComposeView(context: Context) : ComposeViewWrappe
|
|
12
14
|
return OriginalTemplatePaywallFooterView(context, attrs)
|
13
15
|
}
|
14
16
|
|
15
|
-
|
16
|
-
|
17
|
+
@OptIn(InternalRevenueCatAPI::class)
|
18
|
+
fun setOfferingId(offeringId: String?, presentedOfferingContext: PresentedOfferingContext? = null) {
|
19
|
+
if (offeringId == null) {
|
20
|
+
// We'll get rid of this deprecated API usage once https://github.com/RevenueCat/purchases-android/pull/2658 is merged
|
21
|
+
wrappedView?.setOfferingId(null)
|
22
|
+
}
|
23
|
+
else {
|
24
|
+
val presentedOfferingContext = presentedOfferingContext ?: PresentedOfferingContext(offeringId)
|
25
|
+
wrappedView?.setOfferingIdAndPresentedOfferingContext(offeringId, presentedOfferingContext)
|
26
|
+
}
|
17
27
|
}
|
18
28
|
|
19
29
|
fun setFontProvider(customFontProvider: CustomFontProvider) {
|
@@ -18,6 +18,8 @@ API_AVAILABLE(ios(15.0))
|
|
18
18
|
@implementation CustomerCenterViewManager
|
19
19
|
|
20
20
|
RCT_EXPORT_VIEW_PROPERTY(onDismiss, RCTDirectEventBlock)
|
21
|
+
RCT_EXPORT_VIEW_PROPERTY(onCustomActionSelected, RCTDirectEventBlock)
|
22
|
+
RCT_EXPORT_VIEW_PROPERTY(shouldShowCloseButton, BOOL)
|
21
23
|
RCT_EXPORT_MODULE(CustomerCenterView)
|
22
24
|
|
23
25
|
- (instancetype)init {
|
@@ -36,6 +38,10 @@ RCT_EXPORT_MODULE(CustomerCenterView)
|
|
36
38
|
|
37
39
|
// Create a placeholder block that we'll update after wrapper creation
|
38
40
|
__block CustomerCenterViewWrapper *wrapper = nil;
|
41
|
+
|
42
|
+
// Set onCloseHandler - always provide handler regardless of shouldShowCloseButton
|
43
|
+
// The close button visibility is controlled by shouldShowCloseButton property,
|
44
|
+
// but when the button IS shown, it needs this handler to function properly
|
39
45
|
viewController.onCloseHandler = ^{
|
40
46
|
if (wrapper && wrapper.onDismiss) {
|
41
47
|
wrapper.onDismiss(nil);
|
@@ -13,6 +13,8 @@
|
|
13
13
|
@interface CustomerCenterViewWrapper : UIView<RCCustomerCenterViewControllerDelegateWrapper>
|
14
14
|
|
15
15
|
@property (nonatomic, copy) RCTDirectEventBlock onDismiss;
|
16
|
+
@property (nonatomic, copy) RCTDirectEventBlock onCustomActionSelected;
|
17
|
+
@property (nonatomic, assign) BOOL shouldShowCloseButton;
|
16
18
|
|
17
19
|
- (instancetype)initWithCustomerCenterViewController:(CustomerCenterUIViewController *)viewController;
|
18
20
|
|
@@ -23,8 +23,9 @@ API_AVAILABLE(ios(15.0))
|
|
23
23
|
- (instancetype)initWithCustomerCenterViewController:(CustomerCenterUIViewController *)viewController API_AVAILABLE(ios(15.0)) {
|
24
24
|
NSParameterAssert(viewController);
|
25
25
|
|
26
|
-
if ((self = [super initWithFrame:
|
26
|
+
if ((self = [super initWithFrame:CGRectZero])) { // Don't access the .view yet (rely on autolayout in layoutSubviews)
|
27
27
|
_customerCenterVC = viewController;
|
28
|
+
_shouldShowCloseButton = YES; // Default to YES
|
28
29
|
}
|
29
30
|
|
30
31
|
return self;
|
@@ -40,6 +41,9 @@ API_AVAILABLE(ios(15.0))
|
|
40
41
|
if (!self.addedToHierarchy) {
|
41
42
|
UIViewController *parentController = self.parentViewController;
|
42
43
|
if (parentController) {
|
44
|
+
// Configure the close button BEFORE accessing .view (which triggers viewDidLoad)
|
45
|
+
self.customerCenterVC.shouldShowCloseButton = self.shouldShowCloseButton;
|
46
|
+
|
43
47
|
self.customerCenterVC.view.translatesAutoresizingMaskIntoConstraints = NO;
|
44
48
|
[parentController addChildViewController:self.customerCenterVC];
|
45
49
|
[self addSubview:self.customerCenterVC.view];
|
@@ -52,6 +56,7 @@ API_AVAILABLE(ios(15.0))
|
|
52
56
|
[self.customerCenterVC.view.rightAnchor constraintEqualToAnchor:self.rightAnchor]
|
53
57
|
]];
|
54
58
|
|
59
|
+
|
55
60
|
self.addedToHierarchy = YES;
|
56
61
|
}
|
57
62
|
}
|
@@ -63,4 +68,22 @@ API_AVAILABLE(ios(15.0))
|
|
63
68
|
}
|
64
69
|
}
|
65
70
|
|
71
|
+
- (void)customerCenterViewController:(CustomerCenterUIViewController *)controller
|
72
|
+
didSelectCustomAction:(NSString *)actionID
|
73
|
+
withPurchaseIdentifier:(NSString *)purchaseIdentifier API_AVAILABLE(ios(15.0)) {
|
74
|
+
if (self.onCustomActionSelected) {
|
75
|
+
self.onCustomActionSelected(@{@"actionId": actionID, @"purchaseIdentifier": purchaseIdentifier ?: [NSNull null]});
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
- (void)setShouldShowCloseButton:(BOOL)shouldShowCloseButton {
|
80
|
+
_shouldShowCloseButton = shouldShowCloseButton;
|
81
|
+
|
82
|
+
// Only set the view controller property if we haven't been added to hierarchy yet
|
83
|
+
// Once added to hierarchy, the property is already configured and shouldn't be changed
|
84
|
+
if (self.customerCenterVC && !self.addedToHierarchy) {
|
85
|
+
self.customerCenterVC.shouldShowCloseButton = shouldShowCloseButton;
|
86
|
+
}
|
87
|
+
}
|
88
|
+
|
66
89
|
@end
|
package/ios/PaywallViewWrapper.m
CHANGED
@@ -10,6 +10,7 @@
|
|
10
10
|
#import "UIView+React.h"
|
11
11
|
|
12
12
|
@import PurchasesHybridCommonUI;
|
13
|
+
@import RevenueCat;
|
13
14
|
@import RevenueCatUI;
|
14
15
|
|
15
16
|
static NSString *const KeyCustomerInfo = @"customerInfo";
|
@@ -81,7 +82,9 @@ API_AVAILABLE(ios(15.0))
|
|
81
82
|
if (offering && ![offering isKindOfClass:[NSNull class]]) {
|
82
83
|
NSString *identifier = offering[@"identifier"];
|
83
84
|
if (identifier) {
|
84
|
-
[self
|
85
|
+
RCPresentedOfferingContext *presentedOfferingContext = [self presentedOfferingContextFromOffering:offering];
|
86
|
+
[self.paywallViewController updateWithOfferingIdentifier:identifier
|
87
|
+
presentedOfferingContext:presentedOfferingContext];
|
85
88
|
}
|
86
89
|
}
|
87
90
|
|
@@ -100,6 +103,47 @@ API_AVAILABLE(ios(15.0))
|
|
100
103
|
}
|
101
104
|
}
|
102
105
|
|
106
|
+
- (RCPresentedOfferingContext *)presentedOfferingContextFromOffering:(NSDictionary *)offering {
|
107
|
+
NSArray *availablePackages = offering[@"availablePackages"];
|
108
|
+
if (!availablePackages || ![availablePackages isKindOfClass:[NSArray class]] || [availablePackages count] < 1) {
|
109
|
+
return nil;
|
110
|
+
}
|
111
|
+
|
112
|
+
NSDictionary *firstAvailablePackage = availablePackages[0];
|
113
|
+
if (!firstAvailablePackage || ![firstAvailablePackage isKindOfClass:[NSDictionary class]]) {
|
114
|
+
return nil;
|
115
|
+
}
|
116
|
+
|
117
|
+
NSDictionary *presentedOfferingContextOptions = firstAvailablePackage[@"presentedOfferingContext"];
|
118
|
+
if (!presentedOfferingContextOptions || [presentedOfferingContextOptions isKindOfClass:[NSNull class]]) {
|
119
|
+
return nil;
|
120
|
+
}
|
121
|
+
|
122
|
+
NSString *offeringIdentifier = presentedOfferingContextOptions[@"offeringIdentifier"];
|
123
|
+
if (!offeringIdentifier || [offeringIdentifier isKindOfClass:[NSNull class]]) {
|
124
|
+
return nil;
|
125
|
+
}
|
126
|
+
|
127
|
+
NSString *placementIdentifier = presentedOfferingContextOptions[@"placementIdentifier"];
|
128
|
+
if (![placementIdentifier isKindOfClass:[NSString class]]) {
|
129
|
+
placementIdentifier = nil;
|
130
|
+
}
|
131
|
+
|
132
|
+
RCTargetingContext *targetingContext;
|
133
|
+
NSDictionary *targetingContextOptions = presentedOfferingContextOptions[@"targetingContext"];
|
134
|
+
if (targetingContextOptions && ![targetingContextOptions isKindOfClass:[NSNull class]]) {
|
135
|
+
NSNumber *revision = targetingContextOptions[@"revision"];
|
136
|
+
NSString *ruleId = targetingContextOptions[@"ruleId"];
|
137
|
+
if (revision && [revision isKindOfClass:[NSNumber class]] && ruleId && [ruleId isKindOfClass:[NSString class]]) {
|
138
|
+
targetingContext = [[RCTargetingContext alloc] initWithRevision:[revision integerValue] ruleId:ruleId];
|
139
|
+
}
|
140
|
+
}
|
141
|
+
|
142
|
+
return [[RCPresentedOfferingContext alloc] initWithOfferingIdentifier:offeringIdentifier
|
143
|
+
placementIdentifier:placementIdentifier
|
144
|
+
targetingContext:targetingContext];
|
145
|
+
}
|
146
|
+
|
103
147
|
- (void)paywallViewController:(RCPaywallViewController *)controller
|
104
148
|
didStartPurchaseWithPackage:(NSDictionary *)packageDictionary API_AVAILABLE(ios(15.0)) {
|
105
149
|
self.onPurchaseStarted(@{
|
package/ios/RNCustomerCenter.m
CHANGED
@@ -59,6 +59,7 @@ RCT_EXPORT_MODULE();
|
|
59
59
|
@"onRefundRequestCompleted",
|
60
60
|
@"onFeedbackSurveyCompleted",
|
61
61
|
@"onManagementOptionSelected",
|
62
|
+
@"onCustomActionSelected",
|
62
63
|
@"onDismiss"
|
63
64
|
];
|
64
65
|
}
|
@@ -142,6 +143,14 @@ withURL:(NSString *)url API_AVAILABLE(ios(15.0)) {
|
|
142
143
|
[self sendEventWithName:@"onManagementOptionSelected" body:@{@"option": optionID, @"url": url ?: [NSNull null]}];
|
143
144
|
}
|
144
145
|
|
146
|
+
- (void)customerCenterViewController:(CustomerCenterUIViewController *)controller
|
147
|
+
didSelectCustomAction:(NSString *)actionID
|
148
|
+
withPurchaseIdentifier:(NSString *)purchaseIdentifier {
|
149
|
+
[self sendEventWithName:@"onCustomActionSelected"
|
150
|
+
body:@{@"actionId": actionID, @"purchaseIdentifier": purchaseIdentifier ?: [NSNull null]}
|
151
|
+
];
|
152
|
+
}
|
153
|
+
|
145
154
|
+ (BOOL)requiresMainQueueSetup
|
146
155
|
{
|
147
156
|
return YES;
|
package/ios/RNPaywalls.m
CHANGED
@@ -62,6 +62,7 @@ RCT_EXPORT_MODULE();
|
|
62
62
|
// MARK: -
|
63
63
|
|
64
64
|
RCT_EXPORT_METHOD(presentPaywall:(nullable NSString *)offeringIdentifier
|
65
|
+
presentedOfferingContext:(nullable NSDictionary *)presentedOfferingContext
|
65
66
|
shouldDisplayCloseButton:(BOOL)displayCloseButton
|
66
67
|
withFontFamily:(nullable NSString *)fontFamily
|
67
68
|
withResolve:(RCTPromiseResolveBlock)resolve
|
@@ -71,6 +72,9 @@ RCT_EXPORT_METHOD(presentPaywall:(nullable NSString *)offeringIdentifier
|
|
71
72
|
if (offeringIdentifier != nil) {
|
72
73
|
options[PaywallOptionsKeys.offeringIdentifier] = offeringIdentifier;
|
73
74
|
}
|
75
|
+
if (presentedOfferingContext != nil) {
|
76
|
+
options[PaywallOptionsKeys.presentedOfferingContext] = presentedOfferingContext;
|
77
|
+
}
|
74
78
|
options[PaywallOptionsKeys.displayCloseButton] = @(displayCloseButton);
|
75
79
|
if (fontFamily) {
|
76
80
|
options[PaywallOptionsKeys.fontName] = fontFamily;
|
@@ -87,6 +91,7 @@ RCT_EXPORT_METHOD(presentPaywall:(nullable NSString *)offeringIdentifier
|
|
87
91
|
|
88
92
|
RCT_EXPORT_METHOD(presentPaywallIfNeeded:(NSString *)requiredEntitlementIdentifier
|
89
93
|
withOfferingIdentifier:(nullable NSString *)offeringIdentifier
|
94
|
+
presentedOfferingContext:(nullable NSDictionary *)presentedOfferingContext
|
90
95
|
shouldDisplayCloseButton:(BOOL)displayCloseButton
|
91
96
|
withFontFamily:(nullable NSString *)fontFamily
|
92
97
|
withResolve:(RCTPromiseResolveBlock)resolve
|
@@ -96,6 +101,9 @@ RCT_EXPORT_METHOD(presentPaywallIfNeeded:(NSString *)requiredEntitlementIdentifi
|
|
96
101
|
if (offeringIdentifier != nil) {
|
97
102
|
options[PaywallOptionsKeys.offeringIdentifier] = offeringIdentifier;
|
98
103
|
}
|
104
|
+
if (presentedOfferingContext != nil) {
|
105
|
+
options[PaywallOptionsKeys.presentedOfferingContext] = presentedOfferingContext;
|
106
|
+
}
|
99
107
|
options[PaywallOptionsKeys.requiredEntitlementIdentifier] = requiredEntitlementIdentifier;
|
100
108
|
options[PaywallOptionsKeys.displayCloseButton] = @(displayCloseButton);
|
101
109
|
if (fontFamily) {
|
package/lib/commonjs/index.js
CHANGED
@@ -127,9 +127,7 @@ const InternalPaywallFooterView = ({
|
|
127
127
|
|
128
128
|
// Currently the same as the base type, but can be extended later if needed
|
129
129
|
|
130
|
-
const InternalCustomerCenterView = _reactNative.UIManager.getViewManagerConfig('CustomerCenterView') != null ? (0, _reactNative.requireNativeComponent)('CustomerCenterView') :
|
131
|
-
throw new Error(LINKING_ERROR);
|
132
|
-
};
|
130
|
+
const InternalCustomerCenterView = !usingPreviewAPIMode && _reactNative.UIManager.getViewManagerConfig('CustomerCenterView') != null ? (0, _reactNative.requireNativeComponent)('CustomerCenterView') : null;
|
133
131
|
|
134
132
|
// This is to prevent breaking changes when the native SDK adds new options
|
135
133
|
|
@@ -160,8 +158,9 @@ class RevenueCatUI {
|
|
160
158
|
displayCloseButton = RevenueCatUI.Defaults.PRESENT_PAYWALL_DISPLAY_CLOSE_BUTTON,
|
161
159
|
fontFamily
|
162
160
|
} = {}) {
|
161
|
+
var _offering$availablePa;
|
163
162
|
RevenueCatUI.logWarningIfPreviewAPIMode("presentPaywall");
|
164
|
-
return RNPaywalls.presentPaywall((offering === null || offering === void 0 ? void 0 : offering.identifier) ?? null, displayCloseButton, fontFamily);
|
163
|
+
return RNPaywalls.presentPaywall((offering === null || offering === void 0 ? void 0 : offering.identifier) ?? null, offering === null || offering === void 0 || (_offering$availablePa = offering.availablePackages) === null || _offering$availablePa === void 0 || (_offering$availablePa = _offering$availablePa[0]) === null || _offering$availablePa === void 0 ? void 0 : _offering$availablePa.presentedOfferingContext, displayCloseButton, fontFamily);
|
165
164
|
}
|
166
165
|
|
167
166
|
/**
|
@@ -183,8 +182,9 @@ class RevenueCatUI {
|
|
183
182
|
displayCloseButton = RevenueCatUI.Defaults.PRESENT_PAYWALL_DISPLAY_CLOSE_BUTTON,
|
184
183
|
fontFamily
|
185
184
|
}) {
|
185
|
+
var _offering$availablePa2;
|
186
186
|
RevenueCatUI.logWarningIfPreviewAPIMode("presentPaywallIfNeeded");
|
187
|
-
return RNPaywalls.presentPaywallIfNeeded(requiredEntitlementIdentifier, (offering === null || offering === void 0 ? void 0 : offering.identifier) ?? null, displayCloseButton, fontFamily);
|
187
|
+
return RNPaywalls.presentPaywallIfNeeded(requiredEntitlementIdentifier, (offering === null || offering === void 0 ? void 0 : offering.identifier) ?? null, offering === null || offering === void 0 || (_offering$availablePa2 = offering.availablePackages) === null || _offering$availablePa2 === void 0 || (_offering$availablePa2 = _offering$availablePa2[0]) === null || _offering$availablePa2 === void 0 ? void 0 : _offering$availablePa2.presentedOfferingContext, displayCloseButton, fontFamily);
|
188
188
|
}
|
189
189
|
static Paywall = ({
|
190
190
|
style,
|
@@ -310,13 +310,30 @@ class RevenueCatUI {
|
|
310
310
|
*/
|
311
311
|
static CustomerCenterView = ({
|
312
312
|
style,
|
313
|
-
onDismiss
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
313
|
+
onDismiss,
|
314
|
+
onCustomActionSelected,
|
315
|
+
shouldShowCloseButton = true
|
316
|
+
}) => {
|
317
|
+
if (usingPreviewAPIMode) {
|
318
|
+
return /*#__PURE__*/_react.default.createElement(_previewComponents.PreviewCustomerCenter, {
|
319
|
+
onDismiss: () => onDismiss && onDismiss(),
|
320
|
+
style: [{
|
321
|
+
flex: 1
|
322
|
+
}, style]
|
323
|
+
});
|
324
|
+
}
|
325
|
+
if (!InternalCustomerCenterView) {
|
326
|
+
throw new Error(LINKING_ERROR);
|
327
|
+
}
|
328
|
+
return /*#__PURE__*/_react.default.createElement(InternalCustomerCenterView, {
|
329
|
+
onDismiss: () => onDismiss && onDismiss(),
|
330
|
+
onCustomActionSelected: event => onCustomActionSelected && onCustomActionSelected(event.nativeEvent),
|
331
|
+
shouldShowCloseButton: shouldShowCloseButton,
|
332
|
+
style: [{
|
333
|
+
flex: 1
|
334
|
+
}, style]
|
335
|
+
});
|
336
|
+
};
|
320
337
|
|
321
338
|
/**
|
322
339
|
* Presents the customer center to the user.
|
@@ -376,6 +393,12 @@ class RevenueCatUI {
|
|
376
393
|
subscriptions.push(subscription);
|
377
394
|
}
|
378
395
|
}
|
396
|
+
if (callbacks.onCustomActionSelected) {
|
397
|
+
const subscription = customerCenterEventEmitter === null || customerCenterEventEmitter === void 0 ? void 0 : customerCenterEventEmitter.addListener('onCustomActionSelected', event => callbacks.onCustomActionSelected && callbacks.onCustomActionSelected(event));
|
398
|
+
if (subscription) {
|
399
|
+
subscriptions.push(subscription);
|
400
|
+
}
|
401
|
+
}
|
379
402
|
|
380
403
|
// Return a promise that resolves when the customer center is dismissed
|
381
404
|
return RNCustomerCenter.presentCustomerCenter().finally(() => {
|