Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] Fast Link #637

Merged
merged 14 commits into from
Mar 20, 2024
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,42 @@
# RELEASES

## LinkKit V11.6.0 — 2024-03-18

### React Native

#### Requirements

This SDK now works with any supported version of React Native.

#### Changes

- Update iOS SDK and Android SDKs.
- Add ability to pre-load Link.

### Android

Android SDK [4.2.0](https://github.com/plaid/plaid-link-android/releases/tag/v4.2.0)

#### Requirements

| Name | Version |
|------|---------|
| Android Studio | 4.0+ |
| Kotlin | 1.8+ |


### iOS

iOS SDK [5.3.1](https://github.com/plaid/plaid-link-ios/releases/tag/5.3.1)

#### Requirements

| Name | Version |
|------|---------|
| Xcode | >= 15.0.1 |
| iOS | >= 14.0 |


## LinkKit V11.5.2 — 2024-02-21

### React Native
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ While these older versions are expected to continue to work without disruption,

| Plaid SDK Version | Min React Native Version | Android SDK | Android Min Version | Android Compile Version| iOS SDK | iOS Min Version | Status |
|-------------------|--------------------------|-------------|---------------------|------------------------|---------|-----------------|-------------------------------|
| 11.6.0 | * | [4.2.0+] | 21 | 33 | >=5.3.1 | 14.0 | Active, supports Xcode 15.0.1 |
| 11.5.2 | * | [4.1.1+] | 21 | 33 | >=5.2.1 | 14.0 | Active, supports Xcode 15.0.1 |
| 11.5.1 | * | [4.1.1+] | 21 | 33 | >=5.2.0 | 14.0 | Active, supports Xcode 15.0.1 |
| 11.5.0 | * | [4.1.1+] | 21 | 33 | >=5.2.0 | 14.0 | Active, supports Xcode 15.0.1 |
Expand Down
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ repositories {

dependencies {
implementation "com.facebook.react:react-native:+"
implementation "com.plaid.link:sdk-core:4.1.1"
implementation "com.plaid.link:sdk-core:4.2.0"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
implementation "com.jakewharton.rxrelay2:rxrelay:2.1.1"
}
2 changes: 1 addition & 1 deletion android/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<application>
<meta-data
android:name="com.plaid.link.react_native"
android:value="11.5.2" />
android:value="11.6.0" />
</application>

</manifest>
49 changes: 49 additions & 0 deletions android/src/main/java/com/plaid/PlaidModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.modules.core.DeviceEventManagerModule
import com.plaid.gson.PlaidJsonConverter
import com.plaid.link.Plaid
import com.plaid.link.PlaidHandler
import com.plaid.link.configuration.LinkLogLevel
import com.plaid.link.configuration.LinkTokenConfiguration
import com.plaid.link.event.LinkEvent
Expand All @@ -35,6 +36,8 @@ class PlaidModule internal constructor(reactContext: ReactApplicationContext) :
private var onSuccessCallback: Callback? = null
private var onExitCallback: Callback? = null

private var plaidHandler: PlaidHandler? = null

companion object {
private const val LINK_TOKEN_PREFIX = "link"

Expand Down Expand Up @@ -76,6 +79,52 @@ class PlaidModule internal constructor(reactContext: ReactApplicationContext) :
return builder.build()
}

@ReactMethod
fun create(
token: String,
noLoadingState: Boolean,
logLevel: String,
) {
val tokenConfiguration = getLinkTokenConfiguration(token, noLoadingState, getLogLevel(logLevel))
if (tokenConfiguration == null) {
throw LinkException("Unable to open link, please check that your configuration is valid")
}

// Set the event listener here instead of in open for Layer use cases.
try {
Plaid.setLinkEventListener { linkEvent: LinkEvent ->
var json = jsonConverter.convert(linkEvent)
val eventMap = convertJsonToMap(JSONObject(json))
reactApplicationContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit("onEvent", eventMap)
}
} catch (ex: JSONException) {
Log.e("PlaidModule", ex.toString())
throw ex
}

// Create Plaid handler.
this.plaidHandler = Plaid.create(
reactApplicationContext.getApplicationContext() as Application,
tokenConfiguration
)
}

@ReactMethod
fun open(onSuccessCallback: Callback, onExitCallback: Callback) {
val activity = currentActivity ?: throw IllegalStateException("Current activity is null")

plaidHandler?.let { handler ->
// Work with nonNullValue here
this.onSuccessCallback = onSuccessCallback
this.onExitCallback = onExitCallback
handler.open(activity)
} ?: run {
// Handler is nil.
throw LinkException("Create must be called before open.")
}
}

@ReactMethod
@Suppress("unused")
fun startLinkActivityForResult(
Expand Down
10 changes: 5 additions & 5 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ PODS:
- hermes-engine/Pre-built (0.73.6)
- libevent (2.1.12)
- OpenSSL-Universal (1.1.1100)
- Plaid (5.2.1)
- Plaid (5.3.0)
- RCT-Folly (2022.05.16.00):
- boost
- DoubleConversion
Expand Down Expand Up @@ -945,8 +945,8 @@ PODS:
- React-Mapbuffer (0.73.6):
- glog
- React-debug
- react-native-plaid-link-sdk (11.5.2):
- Plaid (~> 5.2.1)
- react-native-plaid-link-sdk (11.6.0):
- Plaid (~> 5.3.0)
- React-Core
- react-native-safe-area-context (4.9.0):
- React-Core
Expand Down Expand Up @@ -1350,7 +1350,7 @@ SPEC CHECKSUMS:
hermes-engine: 9cecf9953a681df7556b8cc9c74905de8f3293c0
libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913
OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c
Plaid: 7829e84db6d766a751c91a402702946d2977ddcb
Plaid: 88bb225d46d1e5ce4c2d09536fa1b10c496aae5d
RCT-Folly: 7169b2b1c44399c76a47b5deaaba715eeeb476c0
RCTRequired: ca1d7414aba0b27efcfa2ccd37637edb1ab77d96
RCTTypeSafety: 678e344fb976ff98343ca61dc62e151f3a042292
Expand All @@ -1372,7 +1372,7 @@ SPEC CHECKSUMS:
React-jsinspector: 85583ef014ce53d731a98c66a0e24496f7a83066
React-logger: 3eb80a977f0d9669468ef641a5e1fabbc50a09ec
React-Mapbuffer: 84ea43c6c6232049135b1550b8c60b2faac19fab
react-native-plaid-link-sdk: 0c57933e27802012c4ae670e013416ba09910456
react-native-plaid-link-sdk: 1a428de2f032ea0413b677b5f2654c868c077c9a
react-native-safe-area-context: b97eb6f9e3b7f437806c2ce5983f479f8eb5de4b
React-nativeconfig: b4d4e9901d4cabb57be63053fd2aa6086eb3c85f
React-NativeModulesApple: cd26e56d56350e123da0c1e3e4c76cb58a05e1ee
Expand Down
11 changes: 10 additions & 1 deletion example/metro.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,15 @@ const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
*
* @type {import('metro-config').MetroConfig}
*/
const config = {};
const config = {
transformer: {
getTransformOptions: async () => ({
transform: {
experimentalImportSupport: false,
inlineRequires: true,
},
}),
},
};

module.exports = mergeConfig(getDefaultConfig(__dirname), config);
93 changes: 53 additions & 40 deletions example/src/Screens/PlaidEmbeddedLinkScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,50 +1,63 @@
import * as React from 'react';
import { TextInput, Text, View } from 'react-native';
import { styles } from '../Styles';
import { EmbeddedLinkView, LinkIOSPresentationStyle, LinkEvent, LinkExit, LinkSuccess } from 'react-native-plaid-link-sdk';
import {TextInput, Text, View} from 'react-native';
import {styles} from '../Styles';
import {
EmbeddedLinkView,
LinkIOSPresentationStyle,
LinkEvent,
LinkExit,
LinkSuccess,
} from 'react-native-plaid-link-sdk';

// Simple conditional view to display Embedded Link once a token has been entered.
var EmbeddedView = ({token}: {token: string}) => {
let content
let content;

if (token) {
content = <View>
<EmbeddedLinkView
token={token}
iOSPresentationStyle={LinkIOSPresentationStyle.FULL_SCREEN}
onEvent={(event: LinkEvent) => {
console.log('onEvent', event);
}}
onSuccess={(success: LinkSuccess) => {
console.log('onSuccess', success);
}}
onExit={(exit: LinkExit) => {
console.log('onExit', exit);
}}
style={styles.embedded}
/>
</View>
} else {
content = <Text style={{ fontSize: 24, color: '#2196F3', textAlign: 'center' }}>Enter Link Token</Text>
}
if (token) {
content = (
<View>
<EmbeddedLinkView
token={token}
iOSPresentationStyle={LinkIOSPresentationStyle.MODAL}
onEvent={(event: LinkEvent) => {
console.log('onEvent', event);
}}
onSuccess={(success: LinkSuccess) => {
console.log('onSuccess', success);
}}
onExit={(exit: LinkExit) => {
console.log('onExit', exit);
}}
style={styles.embedded}
/>
</View>
);
} else {
content = (
// eslint-disable-next-line react-native/no-inline-styles
<Text style={{fontSize: 24, color: '#2196F3', textAlign: 'center'}}>
Enter Link Token
</Text>
);
}

return <View style={{ padding: 24 }}>{content}</View>
}
// eslint-disable-next-line react-native/no-inline-styles
return <View style={{padding: 24}}>{content}</View>;
};

export function PlaidEmbeddedLinkScreen() {
const [text, setText] = React.useState('');

const [text, setText] = React.useState('');

return (
<>
<TextInput
style={styles.input}
onChangeText={newText => setText(newText)}
value={text}
placeholder="link-sandbox-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
placeholderTextColor={'#D3D3D3'}
/>
<EmbeddedView token={text} />
</>
);
return (
<>
<TextInput
style={styles.input}
onChangeText={newText => setText(newText)}
value={text}
placeholder="link-sandbox-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
placeholderTextColor={'#D3D3D3'}
/>
<EmbeddedView token={text} />
</>
);
}
54 changes: 42 additions & 12 deletions example/src/Screens/PlaidLinkScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,40 @@ import {
LinkEvent,
LinkLogLevel,
LinkSuccess,
openLink,
dismissLink,
PlaidLinkProps,
LinkOpenProps,
usePlaidEmitter,
LinkIOSPresentationStyle,
LinkTokenConfiguration,
} from 'react-native-plaid-link-sdk';

// Create PlaidLinkProps from the provided token string.
function makeLinkTokenProps(token: string): PlaidLinkProps {
import {create, open} from 'react-native-plaid-link-sdk/dist/PlaidLink';

function isValidString(str: string): boolean {
if (str && str.trim() !== '') {
return true;
}
return false;
}

function createLinkTokenConfiguration(
token: string,
noLoadingState: boolean = false,
): LinkTokenConfiguration {
console.log(`token: ${token}`);
return {
token: token,
// Hides native activity indicator if true.
noLoadingState: noLoadingState,
};
}

function createLinkOpenProps(): LinkOpenProps {
return {
tokenConfig: {
token: token,
logLevel: LinkLogLevel.ERROR,
// Hides native activity indicator if true.
noLoadingState: false,
},
onSuccess: (success: LinkSuccess) => {
// User was able to successfully link their account.
console.log('Success: ', success);
success.metadata.accounts.forEach(it => console.log('accounts', it));
},
onExit: (linkExit: LinkExit) => {
// User exited Link session. There may or may not be an error depending on what occured.
Expand All @@ -34,6 +49,7 @@ function makeLinkTokenProps(token: string): PlaidLinkProps {
},
// MODAL or FULL_SCREEEN presentation on iOS. Defaults to MODAL.
iOSPresentationStyle: LinkIOSPresentationStyle.MODAL,
logLevel: LinkLogLevel.ERROR,
};
}

Expand All @@ -49,6 +65,7 @@ export function PlaidLinkScreen() {
});

const [text, onChangeText] = React.useState('');
const [disabled, setDisabled] = React.useState(true);

return (
<>
Expand All @@ -62,8 +79,21 @@ export function PlaidLinkScreen() {
<TouchableOpacity
style={styles.button}
onPress={() => {
const linkTokenProps = makeLinkTokenProps(text);
openLink(linkTokenProps);
if (isValidString(text)) {
const tokenConfiguration = createLinkTokenConfiguration(text);
create(tokenConfiguration);
setDisabled(false);
}
}}>
<Text style={styles.button}>Create Link</Text>
</TouchableOpacity>
<TouchableOpacity
disabled={disabled}
style={disabled ? styles.disabledButton : styles.button}
onPress={() => {
const openProps = createLinkOpenProps();
open(openProps);
setDisabled(true);
}}>
<Text style={styles.button}>Open Link</Text>
</TouchableOpacity>
Expand Down
Loading
Loading