react-native-purchases-ui 7.15.0-rc.1 → 7.15.0
Sign up to get free protection for your applications and to get access to all the features.
- 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":";;;;;;;;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAWA,IAAAC,4BAAA,GAAAD,OAAA;AACA,IAAAE,MAAA,GAAAC,uBAAA,CAAAH,OAAA;AAAmE,SAAAI,yBAAAC,CAAA,6BAAAC,OAAA,mBAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAF,wBAAA,YAAAA,CAAAC,CAAA,WAAAA,CAAA,GAAAG,CAAA,GAAAD,CAAA,KAAAF,CAAA;AAAA,SAAAF,wBAAAE,CAAA,EAAAE,CAAA,SAAAA,CAAA,IAAAF,CAAA,IAAAA,CAAA,CAAAI,UAAA,SAAAJ,CAAA,eAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,WAAAK,OAAA,EAAAL,CAAA,QAAAG,CAAA,GAAAJ,wBAAA,CAAAG,CAAA,OAAAC,CAAA,IAAAA,CAAA,CAAAG,GAAA,CAAAN,CAAA,UAAAG,CAAA,CAAAI,GAAA,CAAAP,CAAA,OAAAQ,CAAA,KAAAC,SAAA,UAAAC,CAAA,GAAAC,MAAA,CAAAC,cAAA,IAAAD,MAAA,CAAAE,wBAAA,WAAAC,CAAA,IAAAd,CAAA,oBAAAc,CAAA,IAAAH,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAjB,CAAA,EAAAc,CAAA,SAAAI,CAAA,GAAAR,CAAA,GAAAC,MAAA,CAAAE,wBAAA,CAAAb,CAAA,EAAAc,CAAA,UAAAI,CAAA,KAAAA,CAAA,CAAAX,GAAA,IAAAW,CAAA,CAAAC,GAAA,IAAAR,MAAA,CAAAC,cAAA,CAAAJ,CAAA,EAAAM,CAAA,EAAAI,CAAA,IAAAV,CAAA,CAAAM,CAAA,IAAAd,CAAA,CAAAc,CAAA,YAAAN,CAAA,CAAAH,OAAA,GAAAL,CAAA,EAAAG,CAAA,IAAAA,CAAA,CAAAgB,GAAA,CAAAnB,CAAA,EAAAQ,CAAA,GAAAA,CAAA;AAAA,SAAAY,SAAA,IAAAA,QAAA,GAAAT,MAAA,CAAAU,MAAA,GAAAV,MAAA,CAAAU,MAAA,CAAAC,IAAA,eAAAC,MAAA,aAAAL,CAAA,MAAAA,CAAA,GAAAM,SAAA,CAAAC,MAAA,EAAAP,CAAA,UAAAQ,MAAA,GAAAF,SAAA,CAAAN,CAAA,YAAAS,GAAA,IAAAD,MAAA,QAAAf,MAAA,CAAAI,SAAA,CAAAC,cAAA,CAAAC,IAAA,CAAAS,MAAA,EAAAC,GAAA,KAAAJ,MAAA,CAAAI,GAAA,IAAAD,MAAA,CAAAC,GAAA,gBAAAJ,MAAA,YAAAH,QAAA,CAAAQ,KAAA,OAAAJ,SAAA;AAInE,MAAMK,aAAa,GAChB,sFAAqF,GACtFC,qBAAQ,CAACC,MAAM,CAAC;EAACC,GAAG,EAAE,gCAAgC;EAAE3B,OAAO,EAAE;AAAE,CAAC,CAAC,GACrE,sDAAsD,GACtD,+BAA+B;AAEjC,MAAM4B,UAAU,GAAGC,0BAAa,CAACD,UAAU;AAE3C,MAAME,YAAY,GAAG,IAAIC,+BAAkB,CAACH,UAAU,CAAC;AAOvD,MAAMI,eAAe,GACnBC,sBAAS,CAACC,oBAAoB,CAAC,SAAS,CAAC,IAAI,IAAI,GAC7C,IAAAC,mCAAsB,EAAmB,SAAS,CAAC,GACnD,MAAM;EACN,MAAM,IAAIC,KAAK,CAACZ,aAAa,CAAC;AAChC,CAAC;AAEL,MAAMa,yBAAyB,GAAGJ,sBAAS,CAACC,oBAAoB,CAAC,SAAS,CAAC,IAAI,IAAI,GAC/E,IAAAC,mCAAsB,EAAmB,qBAAqB,CAAC,GAC/D,MAAM;EACN,MAAM,IAAIC,KAAK,CAACZ,aAAa,CAAC;AAChC,CAAC;AAYY,MAAMc,YAAY,CAAC;EAEhC;AACF;AACA;AACA;AACA;EACE,OAAcC,cAAc,GAAGA,2CAAc;EAE7C,OAAcC,cAAcA,CAAC,CAAuB,CAAC,GAAG,CAAC,CAAC,EAA2B;IACnF,OAAOZ,UAAU,CAACY,cAAc,CAAC,CAAC;EACpC;EAEA,OAAcC,sBAAsBA,CAAC;IAACC;EAA2D,CAAC,EAA2B;IAC3H,OAAOd,UAAU,CAACa,sBAAsB,CAACC,6BAA6B,CAAC;EACzE;EAEA,OAAcC,OAAO,GAAgCC,KAAK,iBACxDpD,MAAA,CAAAQ,OAAA,CAAA6C,aAAA,CAACb,eAAe,EAAAjB,QAAA,KAAK6B,KAAK;IAAEE,KAAK,EAAE,CAAC;MAACC,IAAI,EAAE;IAAC,CAAC,EAAEH,KAAK,CAACE,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,GAAG,IAAAC,eAAQ,EAAC,EAAE,CAAC;IAEtD,IAAAC,gBAAS,EAAC,MAAM;MAKd,MAAMC,0BAA0B,GAAGA,CAAC;QAAEC;MAAyC,CAAC,KAAK;QACnFJ,gBAAgB,CAAC,EAAE,GAAGI,MAAM,CAAC;MAC/B,CAAC;MAED,MAAMC,YAAY,GAAG1B,YAAY,CAAC2B,WAAW,CAC3C,yBAAyB,EACzBH,0BACF,CAAC;MAED,OAAO,MAAM;QACXE,YAAY,CAACE,MAAM,CAAC,CAAC;MACvB,CAAC;IACH,CAAC,EAAE,EAAE,CAAC;IAEN,oBACElE,MAAA,CAAAQ,OAAA,CAAA6C,aAAA,CAACxD,YAAA,CAAAsE,IAAI;MAACb,KAAK,EAAE,CAAC;QAACC,IAAI,EAAE;MAAC,CAAC,EAAED,KAAK;IAAE,gBAC9BtD,MAAA,CAAAQ,OAAA,CAAA6C,aAAA,CAACxD,YAAA,CAAAuE,UAAU;MAACC,qBAAqB,EAAE;QAACC,QAAQ,EAAE,CAAC;QAAEZ;MAAa;IAAE,GAC7DD,QACS,CAAC,eAEbzD,MAAA,CAAAQ,OAAA,CAAA6C,aAAA,CAACR,yBAAyB;MAACS,KAAK,EAAE;QAACiB,SAAS,EAAE,CAAC;MAAE;IAAE,CAAC,CAChD,CAAC;EAEX,CAAC;AACH;AAACC,OAAA,CAAAhE,OAAA,GAAAsC,YAAA"}
|
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
|
-
}
|