react-native-purchases-ui 7.15.0-rc.1 → 7.15.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 +2 -3
- package/android/src/main/java/com/revenuecat/purchases/react/ui/PaywallFooterViewManager.kt +64 -0
- package/android/src/main/java/com/revenuecat/purchases/react/ui/PaywallViewManager.kt +25 -0
- package/android/src/main/java/com/revenuecat/purchases/react/ui/PaywallViewShadowNode.kt +29 -0
- package/android/src/main/java/com/revenuecat/purchases/react/ui/RNPaywallsModule.kt +22 -10
- package/android/src/main/java/com/revenuecat/purchases/react/ui/RNPaywallsPackage.kt +1 -1
- package/ios/{RNPaywallManager.h → PaywallViewManager.h} +1 -1
- package/ios/{RNPaywallManager.m → PaywallViewManager.m} +3 -3
- package/ios/RCPaywallFooterViewManager.h +17 -0
- package/ios/RCPaywallFooterViewManager.m +147 -0
- package/ios/RNPaywalls.h +2 -0
- package/ios/RNPaywalls.m +24 -8
- package/ios/UIView+Extensions.h +15 -0
- package/ios/UIView+Extensions.m +23 -0
- package/lib/commonjs/index.js +79 -9
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +71 -8
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/index.d.ts +28 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/package.json +8 -5
- package/src/index.tsx +106 -7
- package/android/src/main/java/com/revenuecat/purchases/react/ui/Paywall.kt +0 -33
- package/android/src/main/java/com/revenuecat/purchases/react/ui/RNPaywallManager.kt +0 -18
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 "PurchasesHybridCommon", '8.10.
|
20
|
+
spec.dependency "PurchasesHybridCommon", '8.10.1'
|
21
21
|
spec.swift_version = '5.7'
|
22
22
|
end
|
package/android/build.gradle
CHANGED
@@ -59,7 +59,7 @@ android {
|
|
59
59
|
minSdkVersion getExtOrIntegerDefault("minSdkVersion")
|
60
60
|
targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
|
61
61
|
versionCode 1
|
62
|
-
versionName '7.15.0
|
62
|
+
versionName '7.15.0'
|
63
63
|
}
|
64
64
|
|
65
65
|
buildTypes {
|
@@ -91,8 +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:8.10.
|
94
|
+
implementation 'com.revenuecat.purchases:purchases-hybrid-common-ui:8.10.1'
|
95
95
|
implementation 'androidx.compose.ui:ui-android:1.5.4'
|
96
96
|
implementation "androidx.appcompat:appcompat:1.6.1"
|
97
97
|
}
|
98
|
-
|
@@ -0,0 +1,64 @@
|
|
1
|
+
package com.revenuecat.purchases.react.ui
|
2
|
+
|
3
|
+
import android.annotation.SuppressLint
|
4
|
+
import androidx.core.view.children
|
5
|
+
import com.facebook.react.uimanager.SimpleViewManager
|
6
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
7
|
+
import com.facebook.react.uimanager.UIManagerModule
|
8
|
+
import com.revenuecat.purchases.ui.revenuecatui.ExperimentalPreviewRevenueCatUIPurchasesAPI
|
9
|
+
import com.revenuecat.purchases.ui.revenuecatui.views.PaywallFooterView
|
10
|
+
|
11
|
+
@OptIn(ExperimentalPreviewRevenueCatUIPurchasesAPI::class)
|
12
|
+
internal class PaywallFooterViewManager : SimpleViewManager<PaywallFooterView>() {
|
13
|
+
override fun getName(): String {
|
14
|
+
return "RCPaywallFooterView"
|
15
|
+
}
|
16
|
+
|
17
|
+
@SuppressLint("UnsafeOptInUsageError")
|
18
|
+
override fun createViewInstance(themedReactContext: ThemedReactContext): PaywallFooterView {
|
19
|
+
val paywallFooterView: PaywallFooterView = object : PaywallFooterView(themedReactContext) {
|
20
|
+
|
21
|
+
// This is required so the change from Loading to Loaded resizes the view
|
22
|
+
// https://github.com/facebook/react-native/issues/17968#issuecomment-1672111483
|
23
|
+
override fun requestLayout() {
|
24
|
+
super.requestLayout()
|
25
|
+
post(measureAndLayout)
|
26
|
+
}
|
27
|
+
|
28
|
+
private val measureAndLayout = Runnable {
|
29
|
+
measure(
|
30
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
31
|
+
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
32
|
+
)
|
33
|
+
layout(left, top, right, bottom)
|
34
|
+
}
|
35
|
+
|
36
|
+
// This is needed so it measures correctly the size of the children and react native can
|
37
|
+
// size the Javascript view correctly. Not doing this will render the view with height 0
|
38
|
+
// and will require the devs to set a fixed height to the view, which is not ideal
|
39
|
+
// https://medium.com/traveloka-engineering/react-native-at-traveloka-native-ui-components-c6b66f789f35
|
40
|
+
public override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
41
|
+
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
42
|
+
var maxWidth = 0
|
43
|
+
var maxHeight = 0
|
44
|
+
children.forEach {
|
45
|
+
it.measure(widthMeasureSpec, MeasureSpec.UNSPECIFIED)
|
46
|
+
maxWidth = maxWidth.coerceAtLeast(it.measuredWidth)
|
47
|
+
maxHeight = maxHeight.coerceAtLeast(it.measuredHeight)
|
48
|
+
}
|
49
|
+
val finalWidth = maxWidth.coerceAtLeast(suggestedMinimumWidth)
|
50
|
+
val finalHeight = maxHeight.coerceAtLeast(suggestedMinimumHeight)
|
51
|
+
setMeasuredDimension(finalWidth, finalHeight)
|
52
|
+
(context as? ThemedReactContext)?.let { themedReactContext ->
|
53
|
+
themedReactContext.runOnNativeModulesQueueThread {
|
54
|
+
themedReactContext.getNativeModule(UIManagerModule::class.java)
|
55
|
+
?.updateNodeSize(id, finalWidth, finalHeight)
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
return paywallFooterView
|
62
|
+
}
|
63
|
+
|
64
|
+
}
|
@@ -0,0 +1,25 @@
|
|
1
|
+
package com.revenuecat.purchases.react.ui
|
2
|
+
|
3
|
+
import com.facebook.react.uimanager.SimpleViewManager
|
4
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
5
|
+
import com.revenuecat.purchases.ui.revenuecatui.ExperimentalPreviewRevenueCatUIPurchasesAPI
|
6
|
+
import com.revenuecat.purchases.ui.revenuecatui.views.PaywallView
|
7
|
+
|
8
|
+
@OptIn(ExperimentalPreviewRevenueCatUIPurchasesAPI::class)
|
9
|
+
internal class PaywallViewManager : SimpleViewManager<PaywallView>() {
|
10
|
+
companion object {
|
11
|
+
const val REACT_CLASS = "Paywall"
|
12
|
+
}
|
13
|
+
|
14
|
+
override fun getName(): String {
|
15
|
+
return REACT_CLASS
|
16
|
+
}
|
17
|
+
|
18
|
+
override fun createViewInstance(themedReactContext: ThemedReactContext): PaywallView {
|
19
|
+
return PaywallView(themedReactContext)
|
20
|
+
}
|
21
|
+
|
22
|
+
override fun createShadowNodeInstance(): PaywallViewShadowNode {
|
23
|
+
return PaywallViewShadowNode()
|
24
|
+
}
|
25
|
+
}
|
@@ -0,0 +1,29 @@
|
|
1
|
+
package com.revenuecat.purchases.react.ui
|
2
|
+
|
3
|
+
import android.view.View
|
4
|
+
import com.facebook.react.uimanager.LayoutShadowNode
|
5
|
+
import com.facebook.yoga.YogaMeasureFunction
|
6
|
+
import com.facebook.yoga.YogaMeasureMode
|
7
|
+
import com.facebook.yoga.YogaMeasureOutput
|
8
|
+
import com.facebook.yoga.YogaNode
|
9
|
+
|
10
|
+
internal class PaywallViewShadowNode: LayoutShadowNode(), YogaMeasureFunction {
|
11
|
+
init {
|
12
|
+
setMeasureFunction(this)
|
13
|
+
}
|
14
|
+
|
15
|
+
override fun measure(
|
16
|
+
yogaNode: YogaNode,
|
17
|
+
width: Float,
|
18
|
+
yogaMeasureMode: YogaMeasureMode,
|
19
|
+
height: Float,
|
20
|
+
yogaMeasureMode1: YogaMeasureMode
|
21
|
+
): Long {
|
22
|
+
// This is needed so the footer doesn't require a fixed height
|
23
|
+
// https://nicholasmarais1158.github.io/2017/07/React-Native-Custom-Measuring
|
24
|
+
val measuredWidth = View.MeasureSpec.getSize(width.toInt()).toFloat()
|
25
|
+
val measuredHeight = View.MeasureSpec.getSize(height.toInt()).toFloat()
|
26
|
+
|
27
|
+
return YogaMeasureOutput.make(measuredWidth, measuredHeight)
|
28
|
+
}
|
29
|
+
}
|
@@ -1,9 +1,12 @@
|
|
1
1
|
package com.revenuecat.purchases.react.ui
|
2
2
|
|
3
|
+
import android.util.Log
|
3
4
|
import androidx.fragment.app.FragmentActivity
|
5
|
+
import com.facebook.react.bridge.Promise
|
4
6
|
import com.facebook.react.bridge.ReactApplicationContext
|
5
7
|
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
6
8
|
import com.facebook.react.bridge.ReactMethod
|
9
|
+
import com.revenuecat.purchases.hybridcommon.ui.PaywallResultListener
|
7
10
|
import com.revenuecat.purchases.hybridcommon.ui.presentPaywallFromFragment
|
8
11
|
|
9
12
|
internal class RNPaywallsModule(reactContext: ReactApplicationContext) :
|
@@ -16,7 +19,10 @@ internal class RNPaywallsModule(reactContext: ReactApplicationContext) :
|
|
16
19
|
get() {
|
17
20
|
return when (val currentActivity = currentActivity) {
|
18
21
|
is FragmentActivity -> currentActivity
|
19
|
-
else ->
|
22
|
+
else -> {
|
23
|
+
Log.e(NAME, "RevenueCat paywalls require application to use a FragmentActivity")
|
24
|
+
null
|
25
|
+
}
|
20
26
|
}
|
21
27
|
}
|
22
28
|
|
@@ -25,20 +31,26 @@ internal class RNPaywallsModule(reactContext: ReactApplicationContext) :
|
|
25
31
|
}
|
26
32
|
|
27
33
|
@ReactMethod
|
28
|
-
fun presentPaywall() {
|
29
|
-
presentPaywall(null)
|
34
|
+
fun presentPaywall(promise: Promise) {
|
35
|
+
presentPaywall(null, promise)
|
30
36
|
}
|
31
37
|
|
32
38
|
@ReactMethod
|
33
|
-
fun presentPaywallIfNeeded(requiredEntitlementIdentifier: String
|
34
|
-
presentPaywall(requiredEntitlementIdentifier)
|
39
|
+
fun presentPaywallIfNeeded(requiredEntitlementIdentifier: String?, promise: Promise) {
|
40
|
+
presentPaywall(requiredEntitlementIdentifier, promise)
|
35
41
|
}
|
36
42
|
|
37
|
-
private fun presentPaywall(requiredEntitlementIdentifier: String
|
38
|
-
val fragment = currentActivityFragment
|
39
|
-
?: // TODO: log
|
40
|
-
return
|
43
|
+
private fun presentPaywall(requiredEntitlementIdentifier: String?, promise: Promise) {
|
44
|
+
val fragment = currentActivityFragment ?: return
|
41
45
|
|
42
|
-
presentPaywallFromFragment(
|
46
|
+
presentPaywallFromFragment(
|
47
|
+
fragment,
|
48
|
+
requiredEntitlementIdentifier,
|
49
|
+
paywallResultListener = object : PaywallResultListener {
|
50
|
+
override fun onPaywallResult(paywallResult: String) {
|
51
|
+
promise.resolve(paywallResult)
|
52
|
+
}
|
53
|
+
}
|
54
|
+
)
|
43
55
|
}
|
44
56
|
}
|
@@ -12,6 +12,6 @@ class RNPaywallsPackage : ReactPackage {
|
|
12
12
|
}
|
13
13
|
|
14
14
|
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
|
15
|
-
return listOf(
|
15
|
+
return listOf(PaywallViewManager(), PaywallFooterViewManager())
|
16
16
|
}
|
17
17
|
}
|
@@ -6,12 +6,12 @@
|
|
6
6
|
// Copyright © 2023 Facebook. All rights reserved.
|
7
7
|
//
|
8
8
|
|
9
|
-
#import "
|
9
|
+
#import "PaywallViewManager.h"
|
10
10
|
@import PurchasesHybridCommon;
|
11
11
|
|
12
|
-
@implementation
|
12
|
+
@implementation PaywallViewManager
|
13
13
|
|
14
|
-
RCT_EXPORT_MODULE(
|
14
|
+
RCT_EXPORT_MODULE(Paywall)
|
15
15
|
|
16
16
|
- (UIView *)view
|
17
17
|
{
|
@@ -0,0 +1,17 @@
|
|
1
|
+
//
|
2
|
+
// RCPaywallFooterViewManager.h
|
3
|
+
// Pods
|
4
|
+
//
|
5
|
+
// Created by Cesar de la Vega on 29/12/23.
|
6
|
+
// Copyright © 2023 Facebook. All rights reserved.
|
7
|
+
//
|
8
|
+
|
9
|
+
#import <React/RCTViewManager.h>
|
10
|
+
|
11
|
+
NS_ASSUME_NONNULL_BEGIN
|
12
|
+
|
13
|
+
@interface RCPaywallFooterViewManager : RCTViewManager
|
14
|
+
|
15
|
+
@end
|
16
|
+
|
17
|
+
NS_ASSUME_NONNULL_END
|
@@ -0,0 +1,147 @@
|
|
1
|
+
//
|
2
|
+
// RCPaywallFooterViewManager.m
|
3
|
+
// RNPaywalls
|
4
|
+
//
|
5
|
+
// Created by Cesar de la Vega on 29/12/23.
|
6
|
+
//
|
7
|
+
|
8
|
+
#import "RNPaywalls.h"
|
9
|
+
#import "RCPaywallFooterViewManager.h"
|
10
|
+
|
11
|
+
@import RevenueCatUI;
|
12
|
+
@import PurchasesHybridCommon;
|
13
|
+
|
14
|
+
#import "UIView+Extensions.h"
|
15
|
+
|
16
|
+
#import <React/RCTShadowView.h>
|
17
|
+
#import <React/RCTUIManager.h>
|
18
|
+
#import <React/RCTBridge.h>
|
19
|
+
#import <React/RCTRootViewDelegate.h>
|
20
|
+
#import <React/RCTEventEmitter.h>
|
21
|
+
|
22
|
+
|
23
|
+
NS_ASSUME_NONNULL_BEGIN
|
24
|
+
|
25
|
+
@interface FooterViewWrapper : UIView
|
26
|
+
|
27
|
+
- (instancetype)initWithFooterViewController:(UIViewController *)footerViewController
|
28
|
+
bridge:(RCTBridge *)bridge;
|
29
|
+
|
30
|
+
@end
|
31
|
+
|
32
|
+
NS_ASSUME_NONNULL_END
|
33
|
+
|
34
|
+
@interface FooterViewWrapper () <RCPaywallViewControllerDelegate>
|
35
|
+
|
36
|
+
@property (strong, nonatomic) UIViewController *footerViewController;
|
37
|
+
@property (strong, nonatomic) RCTBridge *bridge;
|
38
|
+
|
39
|
+
@property BOOL addedToHierarchy;
|
40
|
+
|
41
|
+
@end
|
42
|
+
|
43
|
+
@implementation FooterViewWrapper
|
44
|
+
|
45
|
+
- (instancetype)initWithFooterViewController:(UIViewController *)footerViewController bridge:(RCTBridge *)bridge {
|
46
|
+
if ((self = [super initWithFrame:footerViewController.view.bounds])) {
|
47
|
+
_bridge = bridge;
|
48
|
+
_footerViewController = footerViewController;
|
49
|
+
}
|
50
|
+
|
51
|
+
return self;
|
52
|
+
}
|
53
|
+
|
54
|
+
- (void)safeAreaInsetsDidChange {
|
55
|
+
[super safeAreaInsetsDidChange];
|
56
|
+
|
57
|
+
// Get the safe area insets, for example
|
58
|
+
UIEdgeInsets safeAreaInsets = self.safeAreaInsets;
|
59
|
+
|
60
|
+
// TODO: figure out a better way of sending event, since this is deprecated
|
61
|
+
// It's probably better to create a singleton from the module that this view manager can call and use to send events
|
62
|
+
#pragma clang diagnostic push
|
63
|
+
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
64
|
+
[self.bridge.eventDispatcher sendAppEventWithName:safeAreaInsetsDidChangeEvent
|
65
|
+
body:@{@"top": @(safeAreaInsets.top),
|
66
|
+
@"left": @(safeAreaInsets.left),
|
67
|
+
@"bottom": @(safeAreaInsets.bottom),
|
68
|
+
@"right": @(safeAreaInsets.right)}];
|
69
|
+
#pragma clang diagnostic pop
|
70
|
+
}
|
71
|
+
|
72
|
+
- (void)paywallViewController:(RCPaywallViewController *)controller didChangeSizeTo:(CGSize)size API_AVAILABLE(ios(15.0)){
|
73
|
+
[_bridge.uiManager setSize:size forView:self];
|
74
|
+
}
|
75
|
+
|
76
|
+
- (void)layoutSubviews {
|
77
|
+
[super layoutSubviews];
|
78
|
+
|
79
|
+
// Need to wait for this view to be in the hierarchy to look for the parent UIVC.
|
80
|
+
// This is required to add a SwiftUI `UIHostingController` to the hierarchy in a way that allows
|
81
|
+
// UIKit to read properties from the environment, like traits and safe area.
|
82
|
+
if (!self.addedToHierarchy) {
|
83
|
+
UIViewController *parentController = self.parentViewController;
|
84
|
+
if (parentController) {
|
85
|
+
self.footerViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
|
86
|
+
[parentController addChildViewController:self.footerViewController];
|
87
|
+
[self addSubview:self.footerViewController.view];
|
88
|
+
[self.footerViewController didMoveToParentViewController:parentController];
|
89
|
+
|
90
|
+
[NSLayoutConstraint activateConstraints:@[
|
91
|
+
[self.footerViewController.view.topAnchor constraintEqualToAnchor:self.topAnchor],
|
92
|
+
[self.footerViewController.view.bottomAnchor constraintEqualToAnchor:self.bottomAnchor],
|
93
|
+
[self.footerViewController.view.leftAnchor constraintEqualToAnchor:self.leftAnchor],
|
94
|
+
[self.footerViewController.view.rightAnchor constraintEqualToAnchor:self.rightAnchor]
|
95
|
+
]];
|
96
|
+
|
97
|
+
self.addedToHierarchy = YES;
|
98
|
+
}
|
99
|
+
}
|
100
|
+
}
|
101
|
+
|
102
|
+
@end
|
103
|
+
|
104
|
+
@interface RCPaywallFooterViewManager ()
|
105
|
+
|
106
|
+
@property (nonatomic, strong) id proxyIfAvailable;
|
107
|
+
|
108
|
+
@end
|
109
|
+
|
110
|
+
@implementation RCPaywallFooterViewManager
|
111
|
+
|
112
|
+
RCT_EXPORT_MODULE(RCPaywallFooterView)
|
113
|
+
|
114
|
+
- (instancetype)init {
|
115
|
+
if ((self = [super init])) {
|
116
|
+
if (@available(iOS 15.0, *)) {
|
117
|
+
_proxyIfAvailable = [[PaywallProxy alloc] init];
|
118
|
+
}
|
119
|
+
}
|
120
|
+
|
121
|
+
return self;
|
122
|
+
}
|
123
|
+
|
124
|
+
- (PaywallProxy *)proxy API_AVAILABLE(ios(15.0)){
|
125
|
+
return (PaywallProxy *)self.proxyIfAvailable;
|
126
|
+
}
|
127
|
+
|
128
|
+
- (UIView *)view
|
129
|
+
{
|
130
|
+
if (@available(iOS 15.0, *)) {
|
131
|
+
UIViewController *footerViewController = [self.proxy createFooterPaywallView];
|
132
|
+
FooterViewWrapper *wrapper = [[FooterViewWrapper alloc] initWithFooterViewController:footerViewController
|
133
|
+
bridge:self.bridge];
|
134
|
+
self.proxy.delegate = wrapper;
|
135
|
+
|
136
|
+
return wrapper;
|
137
|
+
} else {
|
138
|
+
NSLog(@"Error: attempted to present paywalls on unsupported iOS version.");
|
139
|
+
return nil;
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
+ (BOOL)requiresMainQueueSetup {
|
144
|
+
return YES;
|
145
|
+
}
|
146
|
+
|
147
|
+
@end
|
package/ios/RNPaywalls.h
CHANGED
package/ios/RNPaywalls.m
CHANGED
@@ -45,7 +45,7 @@ RCT_EXPORT_MODULE();
|
|
45
45
|
// MARK: -
|
46
46
|
|
47
47
|
- (NSArray<NSString *> *)supportedEvents {
|
48
|
-
return @[];
|
48
|
+
return @[safeAreaInsetsDidChangeEvent];
|
49
49
|
}
|
50
50
|
|
51
51
|
- (dispatch_queue_t)methodQueue {
|
@@ -58,24 +58,40 @@ RCT_EXPORT_MODULE();
|
|
58
58
|
|
59
59
|
// MARK: -
|
60
60
|
|
61
|
-
|
61
|
+
RCT_REMAP_METHOD(presentPaywall,
|
62
|
+
presentPaywallWithResolve:(RCTPromiseResolveBlock)resolve
|
63
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
62
64
|
if (@available(iOS 15.0, *)) {
|
63
|
-
[self.paywalls
|
65
|
+
[self.paywalls presentPaywallWithPaywallResultHandler:^(NSString *result) {
|
66
|
+
resolve(result);
|
67
|
+
}];
|
64
68
|
} else {
|
65
|
-
[self
|
69
|
+
[self rejectPaywallsUnsupportedError:reject];
|
66
70
|
}
|
67
71
|
}
|
68
72
|
|
69
|
-
|
73
|
+
RCT_REMAP_METHOD(presentPaywallIfNeeded,
|
74
|
+
presentPaywallIfNeeded:(NSString *)requiredEntitlementIdentifier
|
75
|
+
withResolve:(RCTPromiseResolveBlock)resolve
|
76
|
+
reject:(RCTPromiseRejectBlock)reject) {
|
70
77
|
if (@available(iOS 15.0, *)) {
|
71
|
-
[self.paywalls presentPaywallIfNeededWithRequiredEntitlementIdentifier:requiredEntitlementIdentifier
|
78
|
+
[self.paywalls presentPaywallIfNeededWithRequiredEntitlementIdentifier:requiredEntitlementIdentifier
|
79
|
+
paywallResultHandler:^(NSString *result) {
|
80
|
+
resolve(result);
|
81
|
+
}];
|
72
82
|
} else {
|
73
|
-
[self
|
83
|
+
[self rejectPaywallsUnsupportedError:reject];
|
74
84
|
}
|
75
85
|
}
|
76
86
|
|
77
|
-
- (void)
|
87
|
+
- (void)rejectPaywallsUnsupportedError:(RCTPromiseRejectBlock)reject {
|
78
88
|
NSLog(@"Error: attempted to present paywalls on unsupported iOS version.");
|
89
|
+
reject(@"PaywallsUnsupportedCode", @"Paywalls are not supported prior to iOS 15.", nil);
|
90
|
+
}
|
91
|
+
|
92
|
+
+ (BOOL)requiresMainQueueSetup
|
93
|
+
{
|
94
|
+
return YES;
|
79
95
|
}
|
80
96
|
|
81
97
|
@end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
//
|
2
|
+
// UIView+Extensions.h
|
3
|
+
// Pods
|
4
|
+
//
|
5
|
+
// Created by NachoSoto on 29/12/23.
|
6
|
+
// Copyright © 2023 Facebook. All rights reserved.
|
7
|
+
//
|
8
|
+
|
9
|
+
@import UIKit;
|
10
|
+
|
11
|
+
@interface UIView (ParentViewController)
|
12
|
+
|
13
|
+
- (UIViewController *)parentViewController;
|
14
|
+
|
15
|
+
@end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
//
|
2
|
+
// UIView+Extensions.m
|
3
|
+
// RNPaywalls
|
4
|
+
//
|
5
|
+
// Created by Nacho Soto on 29/12/23.
|
6
|
+
//
|
7
|
+
|
8
|
+
#import "UIView+Extensions.h"
|
9
|
+
|
10
|
+
@implementation UIView (ParentViewController)
|
11
|
+
|
12
|
+
- (UIViewController *)parentViewController {
|
13
|
+
UIResponder *responder = self;
|
14
|
+
while (responder) {
|
15
|
+
responder = responder.nextResponder;
|
16
|
+
if ([responder isKindOfClass:[UIViewController class]]) {
|
17
|
+
return (UIViewController *)responder;
|
18
|
+
}
|
19
|
+
}
|
20
|
+
return nil;
|
21
|
+
}
|
22
|
+
|
23
|
+
@end
|
package/lib/commonjs/index.js
CHANGED
@@ -3,16 +3,86 @@
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
4
4
|
value: true
|
5
5
|
});
|
6
|
-
exports
|
7
|
-
|
6
|
+
Object.defineProperty(exports, "PAYWALL_RESULT", {
|
7
|
+
enumerable: true,
|
8
|
+
get: function () {
|
9
|
+
return _purchasesTypescriptInternal.PAYWALL_RESULT;
|
10
|
+
}
|
11
|
+
});
|
12
|
+
exports.default = void 0;
|
8
13
|
var _reactNative = require("react-native");
|
14
|
+
var _purchasesTypescriptInternal = require("@revenuecat/purchases-typescript-internal");
|
15
|
+
var _react = _interopRequireWildcard(require("react"));
|
16
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
17
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
18
|
+
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
19
|
+
const LINKING_ERROR = `The package 'react-native-purchases-view' doesn't seem to be linked. Make sure: \n\n` + _reactNative.Platform.select({
|
20
|
+
ios: "- You have run 'pod install'\n",
|
21
|
+
default: ''
|
22
|
+
}) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
|
9
23
|
const RNPaywalls = _reactNative.NativeModules.RNPaywalls;
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
}
|
14
|
-
|
15
|
-
|
16
|
-
|
24
|
+
const eventEmitter = new _reactNative.NativeEventEmitter(RNPaywalls);
|
25
|
+
const InternalPaywall = _reactNative.UIManager.getViewManagerConfig('Paywall') != null ? (0, _reactNative.requireNativeComponent)('Paywall') : () => {
|
26
|
+
throw new Error(LINKING_ERROR);
|
27
|
+
};
|
28
|
+
const InternalPaywallFooterView = _reactNative.UIManager.getViewManagerConfig('Paywall') != null ? (0, _reactNative.requireNativeComponent)('RCPaywallFooterView') : () => {
|
29
|
+
throw new Error(LINKING_ERROR);
|
30
|
+
};
|
31
|
+
class RevenueCatUI {
|
32
|
+
/**
|
33
|
+
* The result of presenting a paywall. This will be the last situation the user experienced before the paywall closed.
|
34
|
+
* @readonly
|
35
|
+
* @enum {string}
|
36
|
+
*/
|
37
|
+
static PAYWALL_RESULT = _purchasesTypescriptInternal.PAYWALL_RESULT;
|
38
|
+
static presentPaywall({} = {}) {
|
39
|
+
return RNPaywalls.presentPaywall();
|
40
|
+
}
|
41
|
+
static presentPaywallIfNeeded({
|
42
|
+
requiredEntitlementIdentifier
|
43
|
+
}) {
|
44
|
+
return RNPaywalls.presentPaywallIfNeeded(requiredEntitlementIdentifier);
|
45
|
+
}
|
46
|
+
static Paywall = props => /*#__PURE__*/_react.default.createElement(InternalPaywall, _extends({}, props, {
|
47
|
+
style: [{
|
48
|
+
flex: 1
|
49
|
+
}, props.style]
|
50
|
+
}));
|
51
|
+
static PaywallFooterContainerView = ({
|
52
|
+
style,
|
53
|
+
children
|
54
|
+
}) => {
|
55
|
+
// We use 20 as the default paddingBottom because that's the corner radius in the Android native SDK.
|
56
|
+
// We also listen to safeAreaInsetsDidChange which is only sent from iOS and which is triggered when the
|
57
|
+
// safe area insets change. Not adding this extra padding on iOS will cause the content of the scrollview
|
58
|
+
// to be hidden behind the rounded corners of the paywall.
|
59
|
+
const [paddingBottom, setPaddingBottom] = (0, _react.useState)(20);
|
60
|
+
(0, _react.useEffect)(() => {
|
61
|
+
const handleSafeAreaInsetsChange = ({
|
62
|
+
bottom
|
63
|
+
}) => {
|
64
|
+
setPaddingBottom(20 + bottom);
|
65
|
+
};
|
66
|
+
const subscription = eventEmitter.addListener('safeAreaInsetsDidChange', handleSafeAreaInsetsChange);
|
67
|
+
return () => {
|
68
|
+
subscription.remove();
|
69
|
+
};
|
70
|
+
}, []);
|
71
|
+
return /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
72
|
+
style: [{
|
73
|
+
flex: 1
|
74
|
+
}, style]
|
75
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.ScrollView, {
|
76
|
+
contentContainerStyle: {
|
77
|
+
flexGrow: 1,
|
78
|
+
paddingBottom
|
79
|
+
}
|
80
|
+
}, children), /*#__PURE__*/_react.default.createElement(InternalPaywallFooterView, {
|
81
|
+
style: {
|
82
|
+
marginTop: -20
|
83
|
+
}
|
84
|
+
}));
|
85
|
+
};
|
17
86
|
}
|
87
|
+
exports.default = RevenueCatUI;
|
18
88
|
//# sourceMappingURL=index.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"names":["_reactNative","require","RNPaywalls","NativeModules","presentPaywall","presentPaywallIfNeeded","requiredEntitlementIdentifier"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"
|
1
|
+
{"version":3,"names":["_reactNative","require","_purchasesTypescriptInternal","_react","_interopRequireWildcard","_getRequireWildcardCache","e","WeakMap","r","t","__esModule","default","has","get","n","__proto__","a","Object","defineProperty","getOwnPropertyDescriptor","u","prototype","hasOwnProperty","call","i","set","_extends","assign","bind","target","arguments","length","source","key","apply","LINKING_ERROR","Platform","select","ios","RNPaywalls","NativeModules","eventEmitter","NativeEventEmitter","InternalPaywall","UIManager","getViewManagerConfig","requireNativeComponent","Error","InternalPaywallFooterView","RevenueCatUI","PAYWALL_RESULT","presentPaywall","presentPaywallIfNeeded","requiredEntitlementIdentifier","Paywall","props","createElement","style","flex","PaywallFooterContainerView","children","paddingBottom","setPaddingBottom","useState","useEffect","handleSafeAreaInsetsChange","bottom","subscription","addListener","remove","View","ScrollView","contentContainerStyle","flexGrow","marginTop","exports"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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"}
|
package/lib/module/index.js
CHANGED
@@ -1,11 +1,74 @@
|
|
1
|
-
|
1
|
+
function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
2
|
+
import { NativeEventEmitter, NativeModules, Platform, requireNativeComponent, ScrollView, UIManager, View } from "react-native";
|
3
|
+
import { PAYWALL_RESULT } from "@revenuecat/purchases-typescript-internal";
|
4
|
+
import React, { useEffect, useState } from "react";
|
5
|
+
export { PAYWALL_RESULT } from "@revenuecat/purchases-typescript-internal";
|
6
|
+
const LINKING_ERROR = `The package 'react-native-purchases-view' doesn't seem to be linked. Make sure: \n\n` + Platform.select({
|
7
|
+
ios: "- You have run 'pod install'\n",
|
8
|
+
default: ''
|
9
|
+
}) + '- You rebuilt the app after installing the package\n' + '- You are not using Expo Go\n';
|
2
10
|
const RNPaywalls = NativeModules.RNPaywalls;
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
}
|
7
|
-
|
8
|
-
|
9
|
-
|
11
|
+
const eventEmitter = new NativeEventEmitter(RNPaywalls);
|
12
|
+
const InternalPaywall = UIManager.getViewManagerConfig('Paywall') != null ? requireNativeComponent('Paywall') : () => {
|
13
|
+
throw new Error(LINKING_ERROR);
|
14
|
+
};
|
15
|
+
const InternalPaywallFooterView = UIManager.getViewManagerConfig('Paywall') != null ? requireNativeComponent('RCPaywallFooterView') : () => {
|
16
|
+
throw new Error(LINKING_ERROR);
|
17
|
+
};
|
18
|
+
export default class RevenueCatUI {
|
19
|
+
/**
|
20
|
+
* The result of presenting a paywall. This will be the last situation the user experienced before the paywall closed.
|
21
|
+
* @readonly
|
22
|
+
* @enum {string}
|
23
|
+
*/
|
24
|
+
static PAYWALL_RESULT = PAYWALL_RESULT;
|
25
|
+
static presentPaywall({} = {}) {
|
26
|
+
return RNPaywalls.presentPaywall();
|
27
|
+
}
|
28
|
+
static presentPaywallIfNeeded({
|
29
|
+
requiredEntitlementIdentifier
|
30
|
+
}) {
|
31
|
+
return RNPaywalls.presentPaywallIfNeeded(requiredEntitlementIdentifier);
|
32
|
+
}
|
33
|
+
static Paywall = props => /*#__PURE__*/React.createElement(InternalPaywall, _extends({}, props, {
|
34
|
+
style: [{
|
35
|
+
flex: 1
|
36
|
+
}, props.style]
|
37
|
+
}));
|
38
|
+
static PaywallFooterContainerView = ({
|
39
|
+
style,
|
40
|
+
children
|
41
|
+
}) => {
|
42
|
+
// We use 20 as the default paddingBottom because that's the corner radius in the Android native SDK.
|
43
|
+
// We also listen to safeAreaInsetsDidChange which is only sent from iOS and which is triggered when the
|
44
|
+
// safe area insets change. Not adding this extra padding on iOS will cause the content of the scrollview
|
45
|
+
// to be hidden behind the rounded corners of the paywall.
|
46
|
+
const [paddingBottom, setPaddingBottom] = useState(20);
|
47
|
+
useEffect(() => {
|
48
|
+
const handleSafeAreaInsetsChange = ({
|
49
|
+
bottom
|
50
|
+
}) => {
|
51
|
+
setPaddingBottom(20 + bottom);
|
52
|
+
};
|
53
|
+
const subscription = eventEmitter.addListener('safeAreaInsetsDidChange', handleSafeAreaInsetsChange);
|
54
|
+
return () => {
|
55
|
+
subscription.remove();
|
56
|
+
};
|
57
|
+
}, []);
|
58
|
+
return /*#__PURE__*/React.createElement(View, {
|
59
|
+
style: [{
|
60
|
+
flex: 1
|
61
|
+
}, style]
|
62
|
+
}, /*#__PURE__*/React.createElement(ScrollView, {
|
63
|
+
contentContainerStyle: {
|
64
|
+
flexGrow: 1,
|
65
|
+
paddingBottom
|
66
|
+
}
|
67
|
+
}, children), /*#__PURE__*/React.createElement(InternalPaywallFooterView, {
|
68
|
+
style: {
|
69
|
+
marginTop: -20
|
70
|
+
}
|
71
|
+
}));
|
72
|
+
};
|
10
73
|
}
|
11
74
|
//# sourceMappingURL=index.js.map
|
package/lib/module/index.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"names":["NativeModules","RNPaywalls","presentPaywall","presentPaywallIfNeeded","requiredEntitlementIdentifier"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"AAAA,
|
1
|
+
{"version":3,"names":["NativeEventEmitter","NativeModules","Platform","requireNativeComponent","ScrollView","UIManager","View","PAYWALL_RESULT","React","useEffect","useState","LINKING_ERROR","select","ios","default","RNPaywalls","eventEmitter","InternalPaywall","getViewManagerConfig","Error","InternalPaywallFooterView","RevenueCatUI","presentPaywall","presentPaywallIfNeeded","requiredEntitlementIdentifier","Paywall","props","createElement","_extends","style","flex","PaywallFooterContainerView","children","paddingBottom","setPaddingBottom","handleSafeAreaInsetsChange","bottom","subscription","addListener","remove","contentContainerStyle","flexGrow","marginTop"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";AAAA,SACEA,kBAAkB,EAClBC,aAAa,EACbC,QAAQ,EACRC,sBAAsB,EACtBC,UAAU,EAEVC,SAAS,EACTC,IAAI,QAEC,cAAc;AACrB,SAASC,cAAc,QAAQ,2CAA2C;AAC1E,OAAOC,KAAK,IAAoBC,SAAS,EAAEC,QAAQ,QAAQ,OAAO;AAElE,SAASH,cAAc,QAAQ,2CAA2C;AAE1E,MAAMI,aAAa,GAChB,sFAAqF,GACtFT,QAAQ,CAACU,MAAM,CAAC;EAACC,GAAG,EAAE,gCAAgC;EAAEC,OAAO,EAAE;AAAE,CAAC,CAAC,GACrE,sDAAsD,GACtD,+BAA+B;AAEjC,MAAMC,UAAU,GAAGd,aAAa,CAACc,UAAU;AAE3C,MAAMC,YAAY,GAAG,IAAIhB,kBAAkB,CAACe,UAAU,CAAC;AAOvD,MAAME,eAAe,GACnBZ,SAAS,CAACa,oBAAoB,CAAC,SAAS,CAAC,IAAI,IAAI,GAC7Cf,sBAAsB,CAAmB,SAAS,CAAC,GACnD,MAAM;EACN,MAAM,IAAIgB,KAAK,CAACR,aAAa,CAAC;AAChC,CAAC;AAEL,MAAMS,yBAAyB,GAAGf,SAAS,CAACa,oBAAoB,CAAC,SAAS,CAAC,IAAI,IAAI,GAC/Ef,sBAAsB,CAAmB,qBAAqB,CAAC,GAC/D,MAAM;EACN,MAAM,IAAIgB,KAAK,CAACR,aAAa,CAAC;AAChC,CAAC;AAYH,eAAe,MAAMU,YAAY,CAAC;EAEhC;AACF;AACA;AACA;AACA;EACE,OAAcd,cAAc,GAAGA,cAAc;EAE7C,OAAce,cAAcA,CAAC,CAAuB,CAAC,GAAG,CAAC,CAAC,EAA2B;IACnF,OAAOP,UAAU,CAACO,cAAc,CAAC,CAAC;EACpC;EAEA,OAAcC,sBAAsBA,CAAC;IAACC;EAA2D,CAAC,EAA2B;IAC3H,OAAOT,UAAU,CAACQ,sBAAsB,CAACC,6BAA6B,CAAC;EACzE;EAEA,OAAcC,OAAO,GAAgCC,KAAK,iBACxDlB,KAAA,CAAAmB,aAAA,CAACV,eAAe,EAAAW,QAAA,KAAKF,KAAK;IAAEG,KAAK,EAAE,CAAC;MAACC,IAAI,EAAE;IAAC,CAAC,EAAEJ,KAAK,CAACG,KAAK;EAAE,EAAC,CAC9D;EAED,OAAcE,0BAA0B,GAA+BA,CAAC;IAACF,KAAK;IAAEG;EAAQ,CAAC,KAAK;IAC5F;IACA;IACA;IACA;IACA,MAAM,CAACC,aAAa,EAAEC,gBAAgB,CAAC,GAAGxB,QAAQ,CAAC,EAAE,CAAC;IAEtDD,SAAS,CAAC,MAAM;MAKd,MAAM0B,0BAA0B,GAAGA,CAAC;QAAEC;MAAyC,CAAC,KAAK;QACnFF,gBAAgB,CAAC,EAAE,GAAGE,MAAM,CAAC;MAC/B,CAAC;MAED,MAAMC,YAAY,GAAGrB,YAAY,CAACsB,WAAW,CAC3C,yBAAyB,EACzBH,0BACF,CAAC;MAED,OAAO,MAAM;QACXE,YAAY,CAACE,MAAM,CAAC,CAAC;MACvB,CAAC;IACH,CAAC,EAAE,EAAE,CAAC;IAEN,oBACE/B,KAAA,CAAAmB,aAAA,CAACrB,IAAI;MAACuB,KAAK,EAAE,CAAC;QAACC,IAAI,EAAE;MAAC,CAAC,EAAED,KAAK;IAAE,gBAC9BrB,KAAA,CAAAmB,aAAA,CAACvB,UAAU;MAACoC,qBAAqB,EAAE;QAACC,QAAQ,EAAE,CAAC;QAAER;MAAa;IAAE,GAC7DD,QACS,CAAC,eAEbxB,KAAA,CAAAmB,aAAA,CAACP,yBAAyB;MAACS,KAAK,EAAE;QAACa,SAAS,EAAE,CAAC;MAAE;IAAE,CAAC,CAChD,CAAC;EAEX,CAAC;AACH"}
|
@@ -1,3 +1,29 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
import { type StyleProp, type ViewStyle } from "react-native";
|
2
|
+
import { PAYWALL_RESULT } from "@revenuecat/purchases-typescript-internal";
|
3
|
+
import React, { type ReactNode } from "react";
|
4
|
+
export { PAYWALL_RESULT } from "@revenuecat/purchases-typescript-internal";
|
5
|
+
type PaywallViewProps = {
|
6
|
+
style?: StyleProp<ViewStyle>;
|
7
|
+
children?: ReactNode;
|
8
|
+
};
|
9
|
+
export interface PresentPaywallParams {
|
10
|
+
}
|
11
|
+
export type PresentPaywallIfNeededParams = PresentPaywallParams & {
|
12
|
+
/**
|
13
|
+
* The paywall will only be presented if this entitlement is not active.
|
14
|
+
*/
|
15
|
+
requiredEntitlementIdentifier: string;
|
16
|
+
};
|
17
|
+
export default class RevenueCatUI {
|
18
|
+
/**
|
19
|
+
* The result of presenting a paywall. This will be the last situation the user experienced before the paywall closed.
|
20
|
+
* @readonly
|
21
|
+
* @enum {string}
|
22
|
+
*/
|
23
|
+
static PAYWALL_RESULT: typeof PAYWALL_RESULT;
|
24
|
+
static presentPaywall({}?: PresentPaywallParams): Promise<PAYWALL_RESULT>;
|
25
|
+
static presentPaywallIfNeeded({ requiredEntitlementIdentifier }: PresentPaywallIfNeededParams): Promise<PAYWALL_RESULT>;
|
26
|
+
static Paywall: React.FC<PaywallViewProps>;
|
27
|
+
static PaywallFooterContainerView: React.FC<PaywallViewProps>;
|
28
|
+
}
|
3
29
|
//# sourceMappingURL=index.d.ts.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAML,KAAK,SAAS,EAGd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAC;AAC3E,OAAO,KAAK,EAAE,EAAE,KAAK,SAAS,EAAuB,MAAM,OAAO,CAAC;AAEnE,OAAO,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAC;AAY3E,KAAK,gBAAgB,GAAG;IACtB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB,CAAC;AAeF,MAAM,WAAW,oBAAoB;CACpC;AAED,MAAM,MAAM,4BAA4B,GAAG,oBAAoB,GAAG;IAChE;;OAEG;IACH,6BAA6B,EAAE,MAAM,CAAC;CACvC,CAAA;AAED,MAAM,CAAC,OAAO,OAAO,YAAY;IAE/B;;;;OAIG;IACH,OAAc,cAAc,wBAAkB;WAEhC,cAAc,CAAC,EAAE,GAAE,oBAAyB,GAAG,OAAO,CAAC,cAAc,CAAC;WAItE,sBAAsB,CAAC,EAAC,6BAA6B,EAAC,EAAE,4BAA4B,GAAG,OAAO,CAAC,cAAc,CAAC;IAI5H,OAAc,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAE/C;IAEF,OAAc,0BAA0B,EAAE,KAAK,CAAC,EAAE,CAAC,gBAAgB,CAAC,CAmClE;CACH"}
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "react-native-purchases-ui",
|
3
3
|
"title": "React Native Purchases UI",
|
4
|
-
"version": "7.15.0
|
4
|
+
"version": "7.15.0",
|
5
5
|
"description": "React Native in-app purchases and subscriptions made easy. Supports iOS and Android.",
|
6
6
|
"main": "lib/commonjs/index",
|
7
7
|
"module": "lib/module/index",
|
@@ -51,8 +51,8 @@
|
|
51
51
|
"readmeFilename": "README.md",
|
52
52
|
"devDependencies": {
|
53
53
|
"@types/jest": "^28.1.2",
|
54
|
-
"@types/react": "~
|
55
|
-
"@types/react-
|
54
|
+
"@types/react": "~18.2.0",
|
55
|
+
"@types/react-dom": "~18.2.0",
|
56
56
|
"jest": "^28.1.1",
|
57
57
|
"jest-react-native": "^18.0.0",
|
58
58
|
"pod-install": "^0.1.0",
|
@@ -67,7 +67,9 @@
|
|
67
67
|
"typescript": "^5.0.2"
|
68
68
|
},
|
69
69
|
"resolutions": {
|
70
|
-
"@types/react": "
|
70
|
+
"@types/react": "18.2.0",
|
71
|
+
"@types/react-native": "0.73.1",
|
72
|
+
"@types/react-dom": "18.2.0"
|
71
73
|
},
|
72
74
|
"peerDependencies": {
|
73
75
|
"react": "*",
|
@@ -109,6 +111,7 @@
|
|
109
111
|
]
|
110
112
|
},
|
111
113
|
"dependencies": {
|
112
|
-
"
|
114
|
+
"@revenuecat/purchases-typescript-internal": "8.10.1",
|
115
|
+
"react-native-purchases": "7.15.0"
|
113
116
|
}
|
114
117
|
}
|
package/src/index.tsx
CHANGED
@@ -1,13 +1,112 @@
|
|
1
|
-
import {
|
1
|
+
import {
|
2
|
+
NativeEventEmitter,
|
3
|
+
NativeModules,
|
4
|
+
Platform,
|
5
|
+
requireNativeComponent,
|
6
|
+
ScrollView,
|
7
|
+
type StyleProp,
|
8
|
+
UIManager,
|
9
|
+
View,
|
10
|
+
type ViewStyle,
|
11
|
+
} from "react-native";
|
12
|
+
import { PAYWALL_RESULT } from "@revenuecat/purchases-typescript-internal";
|
13
|
+
import React, { type ReactNode, useEffect, useState } from "react";
|
14
|
+
|
15
|
+
export { PAYWALL_RESULT } from "@revenuecat/purchases-typescript-internal";
|
16
|
+
|
17
|
+
const LINKING_ERROR =
|
18
|
+
`The package 'react-native-purchases-view' doesn't seem to be linked. Make sure: \n\n` +
|
19
|
+
Platform.select({ios: "- You have run 'pod install'\n", default: ''}) +
|
20
|
+
'- You rebuilt the app after installing the package\n' +
|
21
|
+
'- You are not using Expo Go\n';
|
2
22
|
|
3
23
|
const RNPaywalls = NativeModules.RNPaywalls;
|
4
24
|
|
5
|
-
|
6
|
-
|
7
|
-
|
25
|
+
const eventEmitter = new NativeEventEmitter(RNPaywalls);
|
26
|
+
|
27
|
+
type PaywallViewProps = {
|
28
|
+
style?: StyleProp<ViewStyle>;
|
29
|
+
children?: ReactNode;
|
30
|
+
};
|
31
|
+
|
32
|
+
const InternalPaywall =
|
33
|
+
UIManager.getViewManagerConfig('Paywall') != null
|
34
|
+
? requireNativeComponent<PaywallViewProps>('Paywall')
|
35
|
+
: () => {
|
36
|
+
throw new Error(LINKING_ERROR);
|
37
|
+
};
|
38
|
+
|
39
|
+
const InternalPaywallFooterView = UIManager.getViewManagerConfig('Paywall') != null
|
40
|
+
? requireNativeComponent<PaywallViewProps>('RCPaywallFooterView')
|
41
|
+
: () => {
|
42
|
+
throw new Error(LINKING_ERROR);
|
43
|
+
};
|
44
|
+
|
45
|
+
export interface PresentPaywallParams {
|
46
|
+
}
|
47
|
+
|
48
|
+
export type PresentPaywallIfNeededParams = PresentPaywallParams & {
|
49
|
+
/**
|
50
|
+
* The paywall will only be presented if this entitlement is not active.
|
51
|
+
*/
|
52
|
+
requiredEntitlementIdentifier: string;
|
8
53
|
}
|
9
54
|
|
10
|
-
export
|
11
|
-
|
12
|
-
|
55
|
+
export default class RevenueCatUI {
|
56
|
+
|
57
|
+
/**
|
58
|
+
* The result of presenting a paywall. This will be the last situation the user experienced before the paywall closed.
|
59
|
+
* @readonly
|
60
|
+
* @enum {string}
|
61
|
+
*/
|
62
|
+
public static PAYWALL_RESULT = PAYWALL_RESULT;
|
63
|
+
|
64
|
+
public static presentPaywall({}: PresentPaywallParams = {}): Promise<PAYWALL_RESULT> {
|
65
|
+
return RNPaywalls.presentPaywall();
|
66
|
+
}
|
67
|
+
|
68
|
+
public static presentPaywallIfNeeded({requiredEntitlementIdentifier}: PresentPaywallIfNeededParams): Promise<PAYWALL_RESULT> {
|
69
|
+
return RNPaywalls.presentPaywallIfNeeded(requiredEntitlementIdentifier);
|
70
|
+
}
|
71
|
+
|
72
|
+
public static Paywall: React.FC<PaywallViewProps> = (props) => (
|
73
|
+
<InternalPaywall {...props} style={[{flex: 1}, props.style]}/>
|
74
|
+
);
|
75
|
+
|
76
|
+
public static PaywallFooterContainerView: React.FC<PaywallViewProps> = ({style, children}) => {
|
77
|
+
// We use 20 as the default paddingBottom because that's the corner radius in the Android native SDK.
|
78
|
+
// We also listen to safeAreaInsetsDidChange which is only sent from iOS and which is triggered when the
|
79
|
+
// safe area insets change. Not adding this extra padding on iOS will cause the content of the scrollview
|
80
|
+
// to be hidden behind the rounded corners of the paywall.
|
81
|
+
const [paddingBottom, setPaddingBottom] = useState(20);
|
82
|
+
|
83
|
+
useEffect(() => {
|
84
|
+
interface HandleSafeAreaInsetsChangeParams {
|
85
|
+
bottom: number;
|
86
|
+
}
|
87
|
+
|
88
|
+
const handleSafeAreaInsetsChange = ({ bottom }: HandleSafeAreaInsetsChangeParams) => {
|
89
|
+
setPaddingBottom(20 + bottom);
|
90
|
+
};
|
91
|
+
|
92
|
+
const subscription = eventEmitter.addListener(
|
93
|
+
'safeAreaInsetsDidChange',
|
94
|
+
handleSafeAreaInsetsChange
|
95
|
+
);
|
96
|
+
|
97
|
+
return () => {
|
98
|
+
subscription.remove();
|
99
|
+
};
|
100
|
+
}, []);
|
101
|
+
|
102
|
+
return (
|
103
|
+
<View style={[{flex: 1}, style]}>
|
104
|
+
<ScrollView contentContainerStyle={{flexGrow: 1, paddingBottom}}>
|
105
|
+
{children}
|
106
|
+
</ScrollView>
|
107
|
+
{/*Adding negative margin to the footer view to make it overlap with the extra padding of the scroll*/}
|
108
|
+
<InternalPaywallFooterView style={{marginTop: -20}}/>
|
109
|
+
</View>
|
110
|
+
);
|
111
|
+
};
|
13
112
|
}
|
@@ -1,33 +0,0 @@
|
|
1
|
-
package com.revenuecat.purchases.react.ui
|
2
|
-
|
3
|
-
import android.app.PendingIntent.getActivity
|
4
|
-
import android.content.Context
|
5
|
-
import android.util.AttributeSet
|
6
|
-
import android.widget.ImageView
|
7
|
-
import androidx.compose.runtime.Composable
|
8
|
-
import androidx.compose.runtime.getValue
|
9
|
-
import androidx.compose.runtime.mutableStateOf
|
10
|
-
import androidx.compose.runtime.setValue
|
11
|
-
import androidx.compose.ui.platform.AbstractComposeView
|
12
|
-
import com.facebook.react.ReactActivity
|
13
|
-
import com.facebook.react.ReactActivityDelegate
|
14
|
-
import com.facebook.react.uimanager.SimpleViewManager
|
15
|
-
import com.revenuecat.purchases.ui.revenuecatui.ExperimentalPreviewRevenueCatUIPurchasesAPI
|
16
|
-
import com.revenuecat.purchases.ui.revenuecatui.Paywall
|
17
|
-
import com.revenuecat.purchases.ui.revenuecatui.PaywallOptions
|
18
|
-
|
19
|
-
internal class Paywall @JvmOverloads constructor(
|
20
|
-
context: Context,
|
21
|
-
attrs: AttributeSet? = null,
|
22
|
-
) : AbstractComposeView(context, attrs) {
|
23
|
-
@OptIn(ExperimentalPreviewRevenueCatUIPurchasesAPI::class)
|
24
|
-
@Composable
|
25
|
-
override fun Content() {
|
26
|
-
Paywall(
|
27
|
-
PaywallOptions.Builder(
|
28
|
-
dismissRequest = {}
|
29
|
-
)
|
30
|
-
.build()
|
31
|
-
)
|
32
|
-
}
|
33
|
-
}
|
@@ -1,18 +0,0 @@
|
|
1
|
-
package com.revenuecat.purchases.react.ui
|
2
|
-
|
3
|
-
import com.facebook.react.uimanager.SimpleViewManager
|
4
|
-
import com.facebook.react.uimanager.ThemedReactContext
|
5
|
-
|
6
|
-
internal class RNPaywallManager : SimpleViewManager<Paywall>() {
|
7
|
-
companion object {
|
8
|
-
const val REACT_CLASS = "RNPaywall"
|
9
|
-
}
|
10
|
-
|
11
|
-
override fun getName(): String {
|
12
|
-
return REACT_CLASS
|
13
|
-
}
|
14
|
-
|
15
|
-
override fun createViewInstance(themedReactContext: ThemedReactContext): Paywall {
|
16
|
-
return Paywall(themedReactContext)
|
17
|
-
}
|
18
|
-
}
|