@pinwheel/react-native-pinwheel 2.4.0 → 2.5.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/CHANGELOG.md ADDED
@@ -0,0 +1,85 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## 2.5.x Releases
6
+
7
+ [2.5.0](#250)
8
+
9
+ ---
10
+
11
+ ### 2.5.0
12
+
13
+ #### Notes
14
+
15
+ We're thrilled to announce the latest version of our SDK! While you'll find that our familiar API contract remains unchanged, there's a host of improvements that make this upgrade indispensable:
16
+
17
+ - **Enhanced Redundancy**: 🛡️ We've fortified our systems, ensuring smoother recovery from integration failures for a significant percentage of our traffic.
18
+ - **Superior Uptime**: 🦾 Reliability is a top priority. This upgrade brings even more robust uptime for DDS integrations.
19
+ - **Increased Conversion**: ↗️ We are leveraging system level features to increase conversion.
20
+ - **Easy Upgrade**: 🥧 No changes were made to the API contract. Easy as pie.
21
+
22
+ ## 2.4.x Releases
23
+
24
+ [2.4.0](#240)
25
+
26
+ ---
27
+
28
+ ### 2.4.0
29
+
30
+ - Removing `overrides` from main package.
31
+ - Updating example app to use newest ReactNative versions.
32
+ - Changing devDependencies to use newest React Native Webview (for typing).
33
+
34
+ ## 2.3.x Releases
35
+
36
+ [2.3.4](#234) | [2.3.5](#235) | [2.3.6](#236) | [2.3.10](#2310) | [2.3.12](#2312) | [2.3.13](#2313) | [2.3.14](#2314) | [2.3.17](#2317)
37
+
38
+ ---
39
+
40
+ ### 2.3.17
41
+
42
+ Export `ScreenTransition` event payload type for the `screen_transition` event.
43
+
44
+
45
+ ### 2.3.14
46
+
47
+ Remove `hermes-engine` and `shell-quote` sub-dependencies from package-lock files.
48
+
49
+ ### 2.3.13
50
+
51
+ Bump `hermes-engine` and `shell-quote` sub-dependency package.
52
+
53
+ ### 2.3.12
54
+
55
+ Add CircleCI scripting and local scripts.
56
+ - Add `npm run dev` script.
57
+ - Remove need for hardcoding api secret in code to run locally.
58
+
59
+ ### 2.3.10
60
+
61
+ Use node 16.7.0 instead of 12.16.1 to install dependencies.
62
+
63
+ ### [2.3.6](https://github.com/underdog-tech/react-native-pinwheel/releases/tag/2.3.6)
64
+
65
+ N/A
66
+
67
+ ### [2.3.5](https://github.com/underdog-tech/react-native-pinwheel/releases/tag/2.3.5)
68
+
69
+ N/A
70
+
71
+ ### [2.3.4](https://github.com/underdog-tech/react-native-pinwheel/releases/tag/2.3.4)
72
+
73
+ #### Added
74
+
75
+ - Export `EventPayload` type.
76
+ - Export `PinwheelError` type.
77
+ - Export `PinwheelErrorType` type.
78
+ - Export `EmptyPayloadObject` type as `Record<string, never>`.
79
+
80
+ ##### Updated
81
+
82
+ - `EventPayload` is no longer a union containing `{}`. It now contains `Record<string, never>` instead due to [this behavior](https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-all-types-assignable-to-empty-interfaces).
83
+ - Mark exported `Error` type as **@deprecated** because it collides with the built-in javascript `Error` object. Replaced with `PinwheelError`.
84
+ - Mark exported `ErrorType` type as **@deprecated** because the new naming convention is prefixing with "`PinwheelError`". Replaced with `PinwheelErrorType`.
85
+ - Update `onExit` type to be `(error: PinwheelError | Record<string, never>)` to be accurate with current functionality.
package/README.md CHANGED
@@ -8,7 +8,7 @@ You may want to run the example app locally to get started.
8
8
 
9
9
  #### Dependencies
10
10
 
11
- - Node 16.7.0 (check with `node -v` and upgrade versions using `nvm`)
11
+ - Node 16.7.0 (check with `node -v` and upgrade versions using `nvm` if needed)
12
12
  - iPhone 14 simulator (open your Simulator app and check the available versions)
13
13
  - iOS 16 running on the simulator (open your Simulator app and check the available versions)
14
14
  - `pod` version 1.11.3 (check with `pod --version`)
@@ -35,14 +35,7 @@ For this or other errors related to command line developer tools:
35
35
 
36
36
  ## Installation
37
37
 
38
- 1. Install [`react-native-webview`](https://www.npmjs.com/package/react-native-webview) peer dependency.
39
-
40
- ```bash
41
- $ npm install --save react-native-webview
42
- $ cd ios && pod install
43
- ```
44
-
45
- 2. Install Pinwheel React Native SDK
38
+ 1. Install Pinwheel React Native SDK
46
39
 
47
40
  ```bash
48
41
  $ npm install --save @pinwheel/react-native-pinwheel
@@ -54,7 +47,7 @@ $ npm install --save @pinwheel/react-native-pinwheel
54
47
 
55
48
  To initialize Link Modal, a short-lived link token will need to be generated first. Your server can generate the link token by sending a POST request to the /v1/link_tokens endpoint. DO NOT ever send this request from the client side and publicly expose your api_secret.
56
49
 
57
- The link token returned is valid for 15 minutes, after which it expires and can no longer be used to initialize Link. The expiration time is returned as a unix timestamp.
50
+ The link token returned is valid for one hour, after which it expires and can no longer be used to initialize Link. The expiration time is returned as a unix timestamp.
58
51
 
59
52
  ### Pinwheel Component
60
53
 
@@ -0,0 +1,18 @@
1
+ Pod::Spec.new do |s|
2
+ s.name = "RNPinwheelSDK"
3
+ s.version = "2.5.0"
4
+ s.summary = "React Native plugin for Pinwheel's SDK"
5
+ s.description = <<-DESC
6
+ An open source React Native plugin for calling Pinwheel's native SDKs to manage payroll data.
7
+ DESC
8
+ s.homepage = "https://github.com/underdog-tech/react-native-pinwheel"
9
+ s.license = { :file => 'LICENSE' }
10
+ s.author = { 'Pinwheel' => 'info@pinwheelapi.com' }
11
+ s.platform = :ios, "12.0"
12
+ s.source = { :path => 'ios' }
13
+ s.source_files = "ios/**/*.{h,m}"
14
+ s.public_header_files = 'ios/**/*.h'
15
+ s.requires_arc = true
16
+ s.dependency "React"
17
+ s.dependency 'PinwheelSDK', '2.4.4'
18
+ end
@@ -0,0 +1,50 @@
1
+ buildscript {
2
+ def kotlin_version = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : '1.7.22'
3
+
4
+ repositories {
5
+ google()
6
+ mavenCentral()
7
+ }
8
+
9
+ dependencies {
10
+ classpath 'com.android.tools.build:gradle:7.2.2'
11
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
12
+ }
13
+ }
14
+
15
+ apply plugin: 'com.android.library'
16
+ apply plugin: 'kotlin-android'
17
+
18
+ android {
19
+ compileSdkVersion 33
20
+
21
+ defaultConfig {
22
+ minSdkVersion 24
23
+ targetSdkVersion 33
24
+ versionCode 1
25
+ versionName "1.0"
26
+ }
27
+ compileOptions {
28
+ sourceCompatibility = 1.8
29
+ targetCompatibility = 1.8
30
+ }
31
+
32
+ sourceSets {
33
+ main.java.srcDirs += 'src/main/java'
34
+ }
35
+ }
36
+
37
+ rootProject.allprojects {
38
+ repositories {
39
+ google()
40
+ mavenCentral()
41
+ }
42
+ }
43
+
44
+ def PW_SDK_VERSION = '2.4.3'
45
+
46
+ dependencies {
47
+ def pwVersion = rootProject.hasProperty('pwVersion') ? rootProject.pwVersion : PW_SDK_VERSION
48
+ implementation 'com.facebook.react:react-native:+'
49
+ implementation "com.getpinwheel:pinwheel-android:$pwVersion"
50
+ }
@@ -0,0 +1,6 @@
1
+
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3
+ package="com.underdog_tech.react">
4
+
5
+ </manifest>
6
+
@@ -0,0 +1,16 @@
1
+ package com.underdog_tech.react
2
+
3
+ import com.facebook.react.ReactPackage
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import com.facebook.react.bridge.NativeModule
6
+ import com.facebook.react.uimanager.ViewManager
7
+
8
+ class PinwheelPackage : ReactPackage {
9
+ override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
10
+ return listOf(RNTPinwheelEvents(reactContext)).toMutableList()
11
+ }
12
+
13
+ override fun createViewManagers(
14
+ reactContext: ReactApplicationContext
15
+ ) = listOf(PinwheelViewManager(reactContext))
16
+ }
@@ -0,0 +1,213 @@
1
+ package com.underdog_tech.react
2
+
3
+ import android.view.Choreographer
4
+ import android.view.View
5
+ import android.view.ViewGroup
6
+ import android.widget.FrameLayout
7
+ import androidx.fragment.app.FragmentActivity
8
+ import com.facebook.react.bridge.Arguments
9
+ import com.facebook.react.bridge.ReactApplicationContext
10
+ import com.facebook.react.bridge.ReadableArray
11
+ import com.facebook.react.bridge.WritableMap
12
+ import com.facebook.react.bridge.WritableNativeMap
13
+ import com.facebook.react.modules.core.DeviceEventManagerModule
14
+ import com.facebook.react.uimanager.ThemedReactContext
15
+ import com.facebook.react.uimanager.ViewGroupManager
16
+ import com.facebook.react.uimanager.annotations.ReactPropGroup
17
+ import com.facebook.react.uimanager.annotations.ReactProp
18
+ import com.underdog_tech.pinwheel_android.PinwheelFragment
19
+ import com.underdog_tech.pinwheel_android.PinwheelEventListener
20
+ import com.underdog_tech.pinwheel_android.model.PinwheelEventType
21
+ import com.underdog_tech.pinwheel_android.model.PinwheelEventPayload
22
+ import com.underdog_tech.pinwheel_android.model.PinwheelAmount
23
+ import com.underdog_tech.pinwheel_android.model.PinwheelTarget
24
+ import com.underdog_tech.pinwheel_android.model.PinwheelAllocation
25
+ import com.underdog_tech.pinwheel_android.model.PinwheelParams
26
+ import com.underdog_tech.pinwheel_android.model.PinwheelResult
27
+ import com.underdog_tech.pinwheel_android.model.PinwheelError
28
+ import com.underdog_tech.pinwheel_android.model.PinwheelSelectedEmployerPayload
29
+ import com.underdog_tech.pinwheel_android.model.PinwheelSelectedPlatformPayload
30
+ import com.underdog_tech.pinwheel_android.model.PinwheelLoginPayload
31
+ import com.underdog_tech.pinwheel_android.model.PinwheelLoginAttemptPayload
32
+ import com.underdog_tech.pinwheel_android.model.PinwheelDDFormCreatePayload
33
+ import com.underdog_tech.pinwheel_android.model.PinwheelScreenTransitionPayload
34
+ import com.underdog_tech.pinwheel_android.model.PinwheelInputAmountPayload
35
+
36
+ fun PinwheelTarget.toWritableMap(): WritableMap {
37
+ return Arguments.createMap().apply {
38
+ putString("accountType", this@toWritableMap.accountType)
39
+ putString("accountName", this@toWritableMap.accountName)
40
+ }
41
+ }
42
+
43
+ fun PinwheelAllocation.toWritableMap(): WritableMap {
44
+ return Arguments.createMap().apply {
45
+ putString("type", this@toWritableMap.type)
46
+ this@toWritableMap.value?.let { putDouble("value", it.toDouble()) }
47
+ putMap("target", this@toWritableMap.target?.toWritableMap())
48
+ }
49
+ }
50
+
51
+ fun PinwheelParams.toWritableMap(): WritableMap {
52
+ return Arguments.createMap().apply {
53
+ putMap("amount", this@toWritableMap.amount?.toWritableMap())
54
+ }
55
+ }
56
+
57
+ fun PinwheelEventPayload.toWritableMap(): WritableMap = when (this) {
58
+ is PinwheelAmount -> Arguments.createMap().apply {
59
+ putDouble("value", this@toWritableMap.value.toDouble())
60
+ putString("unit", this@toWritableMap.unit)
61
+ }
62
+
63
+ is PinwheelInputAmountPayload -> Arguments.createMap().apply {
64
+ putString("action", this@toWritableMap.action)
65
+ putMap("allocation", this@toWritableMap.allocation?.toWritableMap())
66
+ }
67
+
68
+ is PinwheelResult -> Arguments.createMap().apply {
69
+ putString("accountId", this@toWritableMap.accountId)
70
+ putString("platformId", this@toWritableMap.platformId)
71
+ putString("job", this@toWritableMap.job)
72
+ putMap("params", this@toWritableMap.params?.toWritableMap())
73
+ }
74
+ is PinwheelError -> Arguments.createMap().apply {
75
+ putString("type", this@toWritableMap.type)
76
+ putString("code", this@toWritableMap.code)
77
+ putString("message", this@toWritableMap.message)
78
+ putBoolean("pendingRetry", this@toWritableMap.pendingRetry)
79
+ }
80
+ is PinwheelSelectedEmployerPayload -> Arguments.createMap().apply {
81
+ putString("selectedEmployerId", this@toWritableMap.selectedEmployerId)
82
+ putString("selectedEmployerName", this@toWritableMap.selectedEmployerName)
83
+ }
84
+ is PinwheelSelectedPlatformPayload -> Arguments.createMap().apply {
85
+ putString("selectedPlatformId", this@toWritableMap.selectedPlatformId)
86
+ putString("selectedPlatformName", this@toWritableMap.selectedPlatformName)
87
+ }
88
+ is PinwheelLoginPayload -> Arguments.createMap().apply {
89
+ putString("accountId", this@toWritableMap.accountId)
90
+ putString("platformId", this@toWritableMap.platformId)
91
+ }
92
+ is PinwheelLoginAttemptPayload -> Arguments.createMap().apply {
93
+ putString("platformId", this@toWritableMap.platformId)
94
+ }
95
+ is PinwheelDDFormCreatePayload -> Arguments.createMap().apply {
96
+ putString("url", this@toWritableMap.url)
97
+ }
98
+ is PinwheelScreenTransitionPayload -> Arguments.createMap().apply {
99
+ putString("screenName", this@toWritableMap.screenName)
100
+ putString("selectedEmployerId", this@toWritableMap.selectedEmployerId)
101
+ putString("selectedEmployerName", this@toWritableMap.selectedEmployerName)
102
+ putString("selectedPlatformId", this@toWritableMap.selectedPlatformId)
103
+ putString("selectedPlatformName", this@toWritableMap.selectedPlatformName)
104
+ }
105
+ else -> throw IllegalArgumentException("Unsupported PinwheelEventPayload type")
106
+ }
107
+
108
+ class PinwheelViewManager(
109
+ private val reactContext: ReactApplicationContext
110
+ ) : ViewGroupManager<FrameLayout>(), PinwheelEventListener {
111
+ private var propWidth: Int? = reactContext.resources.displayMetrics.widthPixels
112
+ private var propHeight: Int? = reactContext.resources.displayMetrics.heightPixels
113
+ private var token: String? = null
114
+
115
+ override fun getName() = REACT_CLASS
116
+
117
+ /**
118
+ * Return a FrameLayout which will later hold the Fragment
119
+ */
120
+ override fun createViewInstance(reactContext: ThemedReactContext): FrameLayout {
121
+ return FrameLayout(reactContext)
122
+ }
123
+
124
+ /**
125
+ * Map the "create" command to an integer
126
+ */
127
+ override fun getCommandsMap() = mapOf("create" to COMMAND_CREATE)
128
+
129
+ /**
130
+ * Handle "create" command (called from JS) and call createFragment method
131
+ */
132
+ override fun receiveCommand(
133
+ root: FrameLayout,
134
+ commandId: String,
135
+ args: ReadableArray?
136
+ ) {
137
+ super.receiveCommand(root, commandId, args)
138
+ val reactNativeViewId = requireNotNull(args).getInt(0)
139
+
140
+ when (commandId.toInt()) {
141
+ COMMAND_CREATE -> createFragment(root, reactNativeViewId)
142
+ }
143
+ }
144
+
145
+ @ReactPropGroup(names = ["width", "height"], customType = "Style")
146
+ fun setStyle(view: FrameLayout, index: Int, value: Int) {
147
+ if (index == 0) propWidth = value
148
+ if (index == 1) propHeight = value
149
+ }
150
+
151
+ @ReactProp(name="token")
152
+ fun setToken(view: FrameLayout, token: String) {
153
+ this.token = token
154
+ }
155
+
156
+ /**
157
+ * Replace your React Native view with a custom fragment
158
+ */
159
+ fun createFragment(root: FrameLayout, reactNativeViewId: Int) {
160
+ val parentView = root.findViewById<ViewGroup>(reactNativeViewId)
161
+ setupLayout(parentView)
162
+
163
+
164
+ this.token?.let {
165
+ val pinwheelFragment = PinwheelFragment.newInstance(it, "react native", "2.5.0")
166
+ pinwheelFragment.pinwheelEventListener = this
167
+ val activity = reactContext.currentActivity as FragmentActivity
168
+ activity.supportFragmentManager
169
+ .beginTransaction()
170
+ .replace(reactNativeViewId, pinwheelFragment, reactNativeViewId.toString())
171
+ .commit()
172
+ }
173
+ }
174
+
175
+ fun setupLayout(view: View) {
176
+ Choreographer.getInstance().postFrameCallback(object: Choreographer.FrameCallback {
177
+ override fun doFrame(frameTimeNanos: Long) {
178
+ manuallyLayoutChildren(view)
179
+ view.viewTreeObserver.dispatchOnGlobalLayout()
180
+ Choreographer.getInstance().postFrameCallback(this)
181
+ }
182
+ })
183
+ }
184
+
185
+ override fun onEvent(eventName: PinwheelEventType, payload: PinwheelEventPayload?) {
186
+ val params = WritableNativeMap()
187
+ params.putString("name", eventName.toString())
188
+ params.putMap("payload", payload?.toWritableMap())
189
+ reactContext
190
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
191
+ .emit("PINWHEEL_EVENT", params)
192
+ }
193
+
194
+ /**
195
+ * Layout all children properly
196
+ */
197
+ private fun manuallyLayoutChildren(view: View) {
198
+ // propWidth and propHeight coming from react-native props
199
+ val width = requireNotNull(propWidth)
200
+ val height = requireNotNull(propHeight)
201
+
202
+ view.measure(
203
+ View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
204
+ View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY))
205
+
206
+ view.layout(0, 0, width, height)
207
+ }
208
+
209
+ companion object {
210
+ private const val REACT_CLASS = "RNTPinwheel"
211
+ private const val COMMAND_CREATE = 1
212
+ }
213
+ }
@@ -0,0 +1,51 @@
1
+ package com.underdog_tech.react
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+ import com.facebook.react.bridge.ReactContextBaseJavaModule
6
+ import com.facebook.react.bridge.ReactMethod
7
+ import com.facebook.react.bridge.WritableMap
8
+ import com.facebook.react.modules.core.DeviceEventManagerModule
9
+
10
+ class RNTPinwheelEvents(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
11
+ companion object {
12
+ private const val EVENT_KEY = "PINWHEEL_EVENT"
13
+ }
14
+
15
+ override fun getName(): String {
16
+ return "RNTPinwheelEvents"
17
+ }
18
+
19
+ @ReactMethod
20
+ fun anExposedMethod() {
21
+ val currentContext = reactApplicationContext
22
+ val eventName = "PINWHEEL_EVENT"
23
+ val params: WritableMap = Arguments.createMap().apply {
24
+ putString("type", "myEventType")
25
+ }
26
+ sendEvent(currentContext, eventName, params)
27
+ }
28
+
29
+ fun sendEvent(reactContext: ReactApplicationContext, eventName: String, params: WritableMap?) {
30
+ reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java).emit(eventName, params)
31
+ }
32
+
33
+ private var listenerCount = 0
34
+
35
+ @ReactMethod
36
+ fun addListener(eventName: String) {
37
+ if (listenerCount == 0) {
38
+ // Set up any upstream listeners or background tasks as necessary
39
+ }
40
+
41
+ listenerCount += 1
42
+ }
43
+
44
+ @ReactMethod
45
+ fun removeListeners(count: Int) {
46
+ listenerCount -= count
47
+ if (listenerCount == 0) {
48
+ // Remove upstream listeners, stop unnecessary background tasks
49
+ }
50
+ }
51
+ }
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Importing package.json here causes a problem with the folder structure when we npm pack and publish.
3
3
  */
4
- export declare const VERSION = "2.3.18";
4
+ export declare const VERSION = "2.5.0";
5
5
  export declare const LINK_PAGE_URL = "https://cdn.getpinwheel.com/link-v2.3.0.html";
6
6
  export declare const PINWHEEL_DOMAIN = "getpinwheel.com";
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Importing package.json here causes a problem with the folder structure when we npm pack and publish.
3
3
  */
4
- export const VERSION = '2.3.18';
4
+ export const VERSION = '2.5.0';
5
5
  export const LINK_PAGE_URL = 'https://cdn.getpinwheel.com/link-v2.3.0.html';
6
6
  export const PINWHEEL_DOMAIN = 'getpinwheel.com';
package/index.js ADDED
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ // @ts-ignore-next-line
3
+ import Pinwheel from './pinwheel-wrapper';
4
+ import { SafeAreaView, StyleSheet } from 'react-native';
5
+ const styles = StyleSheet.create({
6
+ container: {
7
+ flex: 1,
8
+ },
9
+ });
10
+ export const PINWHEEL_MESSAGE_TYPES = {
11
+ PINWHEEL_EXIT: 'PINWHEEL_EXIT',
12
+ PINWHEEL_MODAL_CLOSE: 'PINWHEEL_MODAL_CLOSE',
13
+ PINWHEEL_LOAD_COMPLETE: 'PINWHEEL_LOAD_COMPLETE',
14
+ PINWHEEL_SUCCESS: 'PINWHEEL_SUCCESS',
15
+ PINWHEEL_EVENT: 'PINWHEEL_EVENT',
16
+ };
17
+ export default ({ linkToken, onLogin, onLoginAttempt, onSuccess, onError, onExit, onEvent }) => {
18
+ const handleEvent = (event) => {
19
+ if (!event) {
20
+ // first event is always an empty string
21
+ return;
22
+ }
23
+ const { name, payload } = event;
24
+ onEvent && onEvent(name, payload);
25
+ switch (name.toLowerCase()) {
26
+ case 'exit':
27
+ // console.log(`case: exit, onExit: ${onExit}`);
28
+ onExit && onExit(payload);
29
+ break;
30
+ case 'success':
31
+ // console.log(`case: success, onSuccess: ${onSuccess}`);
32
+ onSuccess && onSuccess(payload);
33
+ break;
34
+ case 'login':
35
+ // console.log(`case: login, onLogin: ${onLogin}`);
36
+ onLogin && onLogin(payload);
37
+ break;
38
+ case 'login_attempt':
39
+ // console.log(`case: login_attempt, onLoginAttempt: ${onLoginAttempt}`);
40
+ onLoginAttempt && onLoginAttempt(payload);
41
+ break;
42
+ case 'error':
43
+ // console.log(`case: error, onError: ${onError}`);
44
+ onError && onError(payload);
45
+ break;
46
+ default:
47
+ }
48
+ };
49
+ return (<SafeAreaView style={styles.container}>
50
+ {linkToken && <Pinwheel token={linkToken} style={{ flex: 1 }} onEvent={handleEvent}/>}
51
+ </SafeAreaView>);
52
+ };
@@ -0,0 +1,9 @@
1
+ #import <React/RCTBridgeModule.h>
2
+ #import <React/RCTEventEmitter.h>
3
+
4
+ @interface RNTPinwheelEvents : RCTEventEmitter <RCTBridgeModule>
5
+
6
+ + (instancetype)sharedInstance;
7
+ - (void)handlePinwheelEvent:(NSDictionary<NSString *, id> *)payload;
8
+
9
+ @end
@@ -0,0 +1,62 @@
1
+ #import "RNTPinwheelEvents.h"
2
+
3
+ @implementation RNTPinwheelEvents
4
+ {
5
+ bool hasListeners; // This is an instance variable declaration.
6
+ }
7
+
8
+ RCT_EXPORT_MODULE();
9
+ + (BOOL)requiresMainQueueSetup {
10
+ return NO; // change this to NO if you don't need the main thread
11
+ }
12
+
13
+ + (instancetype)sharedInstance {
14
+ static RNTPinwheelEvents *sharedInstance = nil;
15
+ static dispatch_once_t onceToken;
16
+ dispatch_once(&onceToken, ^{
17
+ sharedInstance = [[super allocWithZone:NULL] init];
18
+ });
19
+ return sharedInstance;
20
+ }
21
+
22
+ + (id)allocWithZone:(struct _NSZone *)zone {
23
+ return [self sharedInstance];
24
+ }
25
+
26
+ - (id)copyWithZone:(NSZone *)zone {
27
+ return self;
28
+ }
29
+ // Overriding the default init to make sure it's not used directly
30
+ - (instancetype)init {
31
+ if (self = [super init]) {
32
+ // Custom initialization if needed
33
+ }
34
+ return self;
35
+ }
36
+
37
+ // Will be called when this module's first listener is added.
38
+ -(void)startObserving {
39
+ NSLog(@"startObserving called");
40
+ hasListeners = YES;
41
+ // Set up any upstream listeners or background tasks as necessary
42
+ }
43
+
44
+ // Will be called when this module's last listener is removed, or on dealloc.
45
+ -(void)stopObserving {
46
+ NSLog(@"stopObserving called");
47
+ hasListeners = NO;
48
+ // Remove upstream listeners, stop unnecessary background tasks
49
+ }
50
+
51
+ - (void)handlePinwheelEvent:(NSDictionary<NSString *, id> *)payload
52
+ {
53
+ if (hasListeners) {// Only send events if anyone is listening
54
+ [self sendEventWithName:@"PINWHEEL_EVENT" body:payload];
55
+ }
56
+ }
57
+
58
+ - (NSArray<NSString *> *)supportedEvents {
59
+ return @[@"PINWHEEL_EVENT"]; // Add more event names here.
60
+ }
61
+
62
+ @end
@@ -0,0 +1,21 @@
1
+ #import <React/RCTViewManager.h>
2
+ #import "RNTPinwheelView.h"
3
+
4
+ @interface RNTPinwheelManager : RCTViewManager
5
+
6
+ @end
7
+
8
+ @implementation RNTPinwheelManager : RCTViewManager
9
+
10
+ RCT_EXPORT_MODULE(RNTPinwheelManager);
11
+
12
+ - (UIView *)view
13
+ {
14
+ RNTPinwheelView *pv = [[RNTPinwheelView alloc] init];
15
+ return pv;
16
+ }
17
+
18
+ RCT_EXPORT_VIEW_PROPERTY(token, NSString);
19
+
20
+ @end
21
+
@@ -0,0 +1,12 @@
1
+ #import <UIKit/UIKit.h>
2
+ #import <PinwheelSDK/PinwheelSDK-Swift.h>
3
+ #import "RNTPinwheelEvents.h"
4
+
5
+ @interface RNTPinwheelView : UIView <PinwheelWrapperDelegate>
6
+
7
+ @property (nonatomic, strong) PinwheelWrapperVC *pinwheelWrapperVC;
8
+ @property (nonatomic, assign) NSString *token;
9
+
10
+ - (instancetype)initWithFrame:(CGRect)frame token:(NSString *)token;
11
+
12
+ @end
@@ -0,0 +1,65 @@
1
+ #import "RNTPinwheelView.h"
2
+
3
+ @implementation RNTPinwheelView
4
+
5
+ - (instancetype)initWithFrame:(CGRect)frame {
6
+ if ((self = [super initWithFrame:frame])) {
7
+ [self initPinwheelWrapperVC];
8
+ }
9
+ return self;
10
+ }
11
+
12
+ - (instancetype)initWithFrame:(CGRect)frame token:(NSString *)token {
13
+ if ((self = [super initWithFrame:frame])) {
14
+ _token = token;
15
+ }
16
+ return self;
17
+ }
18
+
19
+ - (void)initPinwheelWrapperVC {
20
+ if (self.token != nil && self.pinwheelWrapperVC == nil) {
21
+ self.pinwheelWrapperVC = [[PinwheelWrapperVC alloc] initWithToken:self.token delegate:self sdk:@"react native" version: @"2.5.0"];
22
+ [self addSubview:self.pinwheelWrapperVC.view];
23
+ }
24
+ }
25
+
26
+ - (void)setToken:(NSString *)newToken {
27
+ if (![_token isEqualToString:newToken]) {
28
+ _token = newToken;
29
+ [self initPinwheelWrapperVC];
30
+ }
31
+ }
32
+
33
+ - (void)layoutSubviews
34
+ {
35
+ [super layoutSubviews];
36
+ self.pinwheelWrapperVC.view.frame = self.bounds;
37
+ }
38
+
39
+ - (void)onEventWithName:(NSString *)name event:(NSDictionary<NSString *, id> *)event {
40
+ NSLog(@"%@", name);
41
+ NSDictionary *dataToSend = @{@"name": name, @"payload": event};
42
+ [RNTPinwheelEvents.sharedInstance handlePinwheelEvent:dataToSend];
43
+ }
44
+
45
+ - (void)onExit:(NSDictionary<NSString *, id> *)error {
46
+ NSLog(@"%@", error);
47
+ }
48
+
49
+ - (void)onSuccess:(NSDictionary<NSString *, id> *)result {
50
+ NSLog(@"%@", result);
51
+ }
52
+
53
+ - (void)onLogin:(NSDictionary<NSString *, id> *)result {
54
+ NSLog(@"%@", result);
55
+ }
56
+
57
+ - (void)onLoginAttempt:(NSDictionary<NSString *, id> *)result {
58
+ NSLog(@"%@", result);
59
+ }
60
+
61
+ - (void)onError:(NSDictionary<NSString *, id> *)error {
62
+ NSLog(@"%@", error);
63
+ }
64
+
65
+ @end
package/package.json CHANGED
@@ -1,13 +1,10 @@
1
1
  {
2
2
  "name": "@pinwheel/react-native-pinwheel",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "type": "module",
5
5
  "description": "Pinwheel React Native SDK",
6
- "main": "lib/index.js",
7
- "types": "lib/index.d.ts",
8
- "files": [
9
- "lib/**/*"
10
- ],
6
+ "main": "index.js",
7
+ "types": "index.d.ts",
11
8
  "scripts": {
12
9
  "build": "tsc",
13
10
  "bump-pkg-version": "./scripts/bump-pkg-version.sh",
@@ -17,12 +14,10 @@
17
14
  "license": "MIT",
18
15
  "peerDependencies": {
19
16
  "react": "^16.13.1 || ^17 || ^18",
20
- "react-native": "*",
21
- "react-native-webview": "*"
17
+ "react-native": "*"
22
18
  },
23
19
  "devDependencies": {
24
20
  "@types/react-native": "^0.72.2",
25
- "react-native-webview": "^13.3.1",
26
21
  "typescript": "^4.0.3"
27
22
  }
28
23
  }
@@ -0,0 +1,6 @@
1
+ export interface RNTPinwheelProps {
2
+ token: string;
3
+ style: any;
4
+ onEvent: (event: any) => void;
5
+ }
6
+ export declare const RNTPinwheel: import("react-native").HostComponent<RNTPinwheelProps>;
@@ -0,0 +1,2 @@
1
+ import { requireNativeComponent } from 'react-native';
2
+ export const RNTPinwheel = requireNativeComponent('RNTPinwheel');
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ import { RNTPinwheelProps } from './pinwheel-view';
3
+ declare const RNTPinwheelView: (props: RNTPinwheelProps) => React.JSX.Element;
4
+ export default RNTPinwheelView;
@@ -0,0 +1,28 @@
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { UIManager, findNodeHandle, NativeEventEmitter, NativeModules } from 'react-native';
3
+ import { RNTPinwheel } from './pinwheel-view';
4
+ const createFragment = (viewId) => {
5
+ UIManager.dispatchViewManagerCommand(viewId,
6
+ // we are calling the 'create' command
7
+ // @ts-ignore
8
+ UIManager.RNTPinwheel.Commands.create.toString(), [viewId]);
9
+ };
10
+ const RNTPinwheelView = (props) => {
11
+ const ref = useRef(null);
12
+ useEffect(() => {
13
+ setTimeout(() => {
14
+ const viewId = findNodeHandle(ref.current);
15
+ if (viewId) {
16
+ createFragment(viewId);
17
+ }
18
+ // events
19
+ const { RNTPinwheelEvents } = NativeModules;
20
+ const eventEmitter = new NativeEventEmitter(RNTPinwheelEvents);
21
+ const eventListener = eventEmitter.addListener('PINWHEEL_EVENT', (event) => {
22
+ props.onEvent(event);
23
+ });
24
+ }, 10);
25
+ }, []);
26
+ return <RNTPinwheel {...props} onEvent={(event) => { console.log `rawbee: ${JSON.stringify(event)}`; }} ref={ref}/>;
27
+ };
28
+ export default RNTPinwheelView;
@@ -0,0 +1,4 @@
1
+ import React from 'react';
2
+ import { RNTPinwheelProps } from './pinwheel-view';
3
+ declare const RNTPinwheelView: (props: RNTPinwheelProps) => React.JSX.Element;
4
+ export default RNTPinwheelView;
@@ -0,0 +1,17 @@
1
+ import React, { useEffect } from 'react';
2
+ import { NativeEventEmitter, NativeModules } from 'react-native';
3
+ import { RNTPinwheel } from './pinwheel-view';
4
+ const RNTPinwheelView = (props) => {
5
+ useEffect(() => {
6
+ const { RNTPinwheelEvents } = NativeModules;
7
+ const eventEmitter = new NativeEventEmitter(RNTPinwheelEvents);
8
+ const eventListener = eventEmitter.addListener('PINWHEEL_EVENT', (event) => {
9
+ props.onEvent(event);
10
+ });
11
+ return () => {
12
+ eventListener.remove();
13
+ };
14
+ }, []);
15
+ return <RNTPinwheel {...props}/>;
16
+ };
17
+ export default RNTPinwheelView;
package/lib/index.js DELETED
@@ -1,122 +0,0 @@
1
- import React from 'react';
2
- import { WebView } from 'react-native-webview';
3
- import { Linking, Platform, SafeAreaView, StyleSheet } from 'react-native';
4
- import { LINK_PAGE_URL, PINWHEEL_DOMAIN, VERSION } from './constants';
5
- const styles = StyleSheet.create({
6
- container: {
7
- flex: 1,
8
- },
9
- });
10
- export const PINWHEEL_MESSAGE_TYPES = {
11
- PINWHEEL_EXIT: 'PINWHEEL_EXIT',
12
- PINWHEEL_MODAL_CLOSE: 'PINWHEEL_MODAL_CLOSE',
13
- PINWHEEL_LOAD_COMPLETE: 'PINWHEEL_LOAD_COMPLETE',
14
- PINWHEEL_SUCCESS: 'PINWHEEL_SUCCESS',
15
- PINWHEEL_EVENT: 'PINWHEEL_EVENT',
16
- };
17
- export default ({ linkToken, onLogin, onLoginAttempt, onSuccess, onError, onExit, onEvent }) => {
18
- const handleEvent = (event) => {
19
- if (!event) {
20
- // first event is always an empty string
21
- return;
22
- }
23
- let eventData;
24
- try {
25
- eventData = JSON.parse(event.nativeEvent.data);
26
- }
27
- catch (_error) {
28
- let error = _error;
29
- console.error(error);
30
- onExit && onExit(error);
31
- onError && onError(error);
32
- onEvent && onEvent('error', error);
33
- return;
34
- }
35
- const { type, eventName, payload } = eventData;
36
- if (type === 'PINWHEEL_EVENT') {
37
- onEvent && onEvent(eventName, payload);
38
- switch (eventName) {
39
- case 'exit':
40
- onExit && onExit(payload);
41
- break;
42
- case 'success':
43
- onSuccess && onSuccess(payload);
44
- break;
45
- case 'login':
46
- onLogin && onLogin(payload);
47
- break;
48
- case 'login_attempt':
49
- onLoginAttempt && onLoginAttempt(payload);
50
- break;
51
- case 'error':
52
- onError && onError(payload);
53
- break;
54
- default:
55
- }
56
- }
57
- };
58
- const now = Date.now();
59
- const [major, minor, patch] = VERSION.split('.').map(x => Number(x));
60
- const runFirst = `
61
- const uuidKey = 'pinwheel-uuid';
62
- const localStorage = window.localStorage;
63
- let uuid = localStorage.getItem(uuidKey);
64
- if(!uuid) {
65
- uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
66
- var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
67
- return v.toString(16);
68
- });
69
- localStorage.setItem(uuidKey, uuid);
70
- }
71
- try {
72
- window.addEventListener('message', event => {
73
- window.ReactNativeWebView.postMessage(JSON.stringify(event.data))
74
- });
75
- window.postMessage(
76
- {
77
- type: 'PINWHEEL_INIT',
78
- payload: {
79
- platform: "${Platform.OS}",
80
- sdk: 'react native',
81
- version: {
82
- major: ${major},
83
- minor: ${minor},
84
- patch: ${patch}
85
- },
86
- initializationOptions: {
87
- hasOnSuccess: ${!!onSuccess},
88
- hasOnEvent: ${!!onEvent},
89
- hasOnExit: ${!!onExit},
90
- hasOnError: ${!!onError},
91
- hasOnLogin: ${!!onLogin},
92
- },
93
- linkToken: '${linkToken}',
94
- uniqueUserId: uuid,
95
- initializationTimestamp: ${now}
96
- }
97
- }
98
- );
99
- } catch (err) {
100
- console.error(err);
101
- }
102
- true;
103
- `;
104
- return (<SafeAreaView style={styles.container}>
105
- <WebView source={{ uri: LINK_PAGE_URL }} onMessage={handleEvent} injectedJavaScript={runFirst} onShouldStartLoadWithRequest={(request) => {
106
- const targetURL = request.url;
107
- const isLinkPage = targetURL.includes(PINWHEEL_DOMAIN);
108
- if (!isLinkPage) {
109
- Linking.canOpenURL(targetURL).then(supported => {
110
- if (supported) {
111
- Linking.openURL(targetURL).then(() => { });
112
- }
113
- else {
114
- console.warn('Don\'t know how to open URL: ' + targetURL);
115
- }
116
- return false;
117
- }).catch(err => console.error('An error occurred ', err));
118
- }
119
- return isLinkPage;
120
- }}/>
121
- </SafeAreaView>);
122
- };
File without changes