diff --git a/react_native/TinyQuickstartReactNative/.gitignore b/react_native/TinyQuickstartReactNative/.gitignore index 2de5345..01164b8 100644 --- a/react_native/TinyQuickstartReactNative/.gitignore +++ b/react_native/TinyQuickstartReactNative/.gitignore @@ -70,3 +70,8 @@ buck-out/ !.yarn/releases !.yarn/sdks !.yarn/versions + +# Expo +.expo +dist/ +web-build/ \ No newline at end of file diff --git a/react_native/TinyQuickstartReactNative/Gemfile.lock b/react_native/TinyQuickstartReactNative/Gemfile.lock index 2e6d047..eee0fee 100644 --- a/react_native/TinyQuickstartReactNative/Gemfile.lock +++ b/react_native/TinyQuickstartReactNative/Gemfile.lock @@ -62,7 +62,7 @@ GEM netrc (~> 0.11) cocoapods-try (1.2.0) colored2 (3.1.2) - concurrent-ruby (1.3.6) + concurrent-ruby (1.3.7) connection_pool (3.0.2) drb (2.2.3) escape (0.0.4) @@ -77,7 +77,7 @@ GEM mutex_m i18n (1.14.8) concurrent-ruby (~> 1.0) - json (2.19.5) + json (2.20.0) logger (1.7.0) minitest (5.27.0) molinillo (0.8.0) diff --git a/react_native/TinyQuickstartReactNative/__tests__/App-test.js b/react_native/TinyQuickstartReactNative/__tests__/App-test.js index 1784766..64c075a 100644 --- a/react_native/TinyQuickstartReactNative/__tests__/App-test.js +++ b/react_native/TinyQuickstartReactNative/__tests__/App-test.js @@ -9,6 +9,12 @@ import App from '../App'; // Note: test renderer must be required after react-native. import renderer from 'react-test-renderer'; +// Plaid Link is a native module with no implementation under Jest's Node +// environment, so stub it out for the render smoke test. +jest.mock('react-native-plaid-link-sdk', () => ({ + createPlaidLinkSession: jest.fn(), +})); + it('renders correctly', () => { renderer.create(); }); diff --git a/react_native/TinyQuickstartReactNative/android/app/build.gradle b/react_native/TinyQuickstartReactNative/android/app/build.gradle index 9d0eb70..6fe26ee 100644 --- a/react_native/TinyQuickstartReactNative/android/app/build.gradle +++ b/react_native/TinyQuickstartReactNative/android/app/build.gradle @@ -51,6 +51,11 @@ react { // hermesFlags = ["-O", "-output-source-map"] autolinkLibrariesWithApp() + // + // Added by install-expo-modules + entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", rootDir.getAbsoluteFile().getParentFile().getAbsolutePath(), "android", "absolute"].execute(null, rootDir).text.trim()) + cliFile = new File(["node", "--print", "require.resolve('@expo/cli')"].execute(null, rootDir).text.trim()) + bundleCommand = "export:embed" } /** diff --git a/react_native/TinyQuickstartReactNative/android/app/src/main/java/com/tinyquickstartreactnative/MainActivity.kt b/react_native/TinyQuickstartReactNative/android/app/src/main/java/com/tinyquickstartreactnative/MainActivity.kt index 378ea1b..7f6fac9 100644 --- a/react_native/TinyQuickstartReactNative/android/app/src/main/java/com/tinyquickstartreactnative/MainActivity.kt +++ b/react_native/TinyQuickstartReactNative/android/app/src/main/java/com/tinyquickstartreactnative/MainActivity.kt @@ -1,4 +1,5 @@ package com.tinyquickstartreactnative +import expo.modules.ReactActivityDelegateWrapper import com.facebook.react.ReactActivity import com.facebook.react.ReactActivityDelegate @@ -18,5 +19,5 @@ class MainActivity : ReactActivity() { * which allows you to enable New Architecture with a single boolean flags [fabricEnabled] */ override fun createReactActivityDelegate(): ReactActivityDelegate = - DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) + ReactActivityDelegateWrapper(this, BuildConfig.IS_NEW_ARCHITECTURE_ENABLED, DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled)) } diff --git a/react_native/TinyQuickstartReactNative/android/app/src/main/java/com/tinyquickstartreactnative/MainApplication.kt b/react_native/TinyQuickstartReactNative/android/app/src/main/java/com/tinyquickstartreactnative/MainApplication.kt index 2d9b297..5a28a61 100644 --- a/react_native/TinyQuickstartReactNative/android/app/src/main/java/com/tinyquickstartreactnative/MainApplication.kt +++ b/react_native/TinyQuickstartReactNative/android/app/src/main/java/com/tinyquickstartreactnative/MainApplication.kt @@ -1,4 +1,7 @@ package com.tinyquickstartreactnative +import android.content.res.Configuration +import expo.modules.ApplicationLifecycleDispatcher +import expo.modules.ExpoReactHostFactory import android.app.Application import com.facebook.react.PackageList @@ -10,7 +13,7 @@ import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost class MainApplication : Application(), ReactApplication { override val reactHost: ReactHost by lazy { - getDefaultReactHost( + ExpoReactHostFactory.getDefaultReactHost( context = applicationContext, packageList = PackageList(this).packages.apply { @@ -23,5 +26,11 @@ class MainApplication : Application(), ReactApplication { override fun onCreate() { super.onCreate() loadReactNative(this) + ApplicationLifecycleDispatcher.onApplicationCreate(this) + } + + override fun onConfigurationChanged(newConfig: Configuration) { + super.onConfigurationChanged(newConfig) + ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig) } } diff --git a/react_native/TinyQuickstartReactNative/android/build.gradle b/react_native/TinyQuickstartReactNative/android/build.gradle index 75aa060..cc038e1 100644 --- a/react_native/TinyQuickstartReactNative/android/build.gradle +++ b/react_native/TinyQuickstartReactNative/android/build.gradle @@ -3,7 +3,7 @@ buildscript { ext { buildToolsVersion = "36.0.0" - minSdkVersion = 24 + minSdkVersion = 26 compileSdkVersion = 36 targetSdkVersion = 36 ndkVersion = "30.0.14904198" @@ -48,3 +48,4 @@ allprojects { maven { url 'https://www.jitpack.io' } } } +apply plugin: "expo-root-project" diff --git a/react_native/TinyQuickstartReactNative/android/settings.gradle b/react_native/TinyQuickstartReactNative/android/settings.gradle index cefc14e..7e294f3 100644 --- a/react_native/TinyQuickstartReactNative/android/settings.gradle +++ b/react_native/TinyQuickstartReactNative/android/settings.gradle @@ -1,7 +1,20 @@ -pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") } -plugins { id("com.facebook.react.settings") } -extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() } +pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") + def expoPluginsPath = new File( + providers.exec { + workingDir(rootDir) + commandLine("node", "--print", "require.resolve('expo-modules-autolinking/package.json', { paths: [require.resolve('expo/package.json')] })") + }.standardOutput.asText.get().trim(), + "../android/expo-gradle-plugin" + ).absolutePath + includeBuild(expoPluginsPath) +} +plugins { id("com.facebook.react.settings") +id("expo-autolinking-settings") +} +extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand) } rootProject.name = 'TinyQuickstartReactNative' include ':app' -includeBuild('../node_modules/@react-native/gradle-plugin') \ No newline at end of file +expoAutolinking.useExpoModules() +expoAutolinking.useExpoVersionCatalog() +includeBuild(expoAutolinking.reactNativeGradlePlugin) diff --git a/react_native/TinyQuickstartReactNative/babel.config.js b/react_native/TinyQuickstartReactNative/babel.config.js index f7b3da3..fcb3486 100644 --- a/react_native/TinyQuickstartReactNative/babel.config.js +++ b/react_native/TinyQuickstartReactNative/babel.config.js @@ -1,3 +1,3 @@ module.exports = { - presets: ['module:@react-native/babel-preset'], + presets: ['babel-preset-expo'], }; diff --git a/react_native/TinyQuickstartReactNative/components/HomeScreen.tsx b/react_native/TinyQuickstartReactNative/components/HomeScreen.tsx index 8d633f1..50890cc 100644 --- a/react_native/TinyQuickstartReactNative/components/HomeScreen.tsx +++ b/react_native/TinyQuickstartReactNative/components/HomeScreen.tsx @@ -1,12 +1,11 @@ -import { link } from 'fs'; import React, { useState, useEffect, useCallback } from 'react'; -import { Platform, View, Text, StyleSheet, Button } from 'react-native'; -import { create, open, dismissLink, LinkSuccess, LinkExit, LinkIOSPresentationStyle, LinkLogLevel } from 'react-native-plaid-link-sdk'; +import { Platform, View, Text, Button } from 'react-native'; +import { createPlaidLinkSession, LinkSuccess, LinkExit, LinkEvent } from 'react-native-plaid-link-sdk'; var styles = require('./style'); const HomeScreen = ({ navigation }: any) => { - const [linkToken, setLinkToken] = useState(null); + const [linkToken, setLinkToken] = useState(null); const address = Platform.OS === 'ios' ? 'localhost' : '10.0.2.2'; const createLinkToken = useCallback(async () => { @@ -24,51 +23,45 @@ const HomeScreen = ({ navigation }: any) => { .catch((err) => { console.log(err); }); - }, [setLinkToken]); + }, [address]); useEffect(() => { if (linkToken == null) { createLinkToken(); - } else { - const tokenConfiguration = createLinkTokenConfiguration(linkToken); - create(tokenConfiguration); } - }, [linkToken]); + }, [linkToken, createLinkToken]); - const createLinkTokenConfiguration = (token: string, noLoadingState: boolean = false) => { - return { - token: token, - noLoadingState: noLoadingState, - }; - }; - - const createLinkOpenProps = () => { - return { - onSuccess: async (success: LinkSuccess) => { - await fetch(`http://${address}:8080/api/exchange_public_token`, { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ public_token: success.publicToken }), - }) - .catch((err) => { - console.log(err); - }); - navigation.navigate('Success', success); - }, - onExit: (linkExit: LinkExit) => { - console.log('Exit: ', linkExit); - dismissLink(); - }, - iOSPresentationStyle: LinkIOSPresentationStyle.MODAL, - logLevel: LinkLogLevel.ERROR, - }; - }; - - const handleOpenLink = () => { - const openProps = createLinkOpenProps(); - open(openProps); + const handleOpenLink = async () => { + if (linkToken == null) { + return; + } + try { + const session = await createPlaidLinkSession({ + token: linkToken, + onSuccess: async (success: LinkSuccess) => { + await fetch(`http://${address}:8080/api/exchange_public_token`, { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ public_token: success.publicToken }), + }) + .catch((err) => { + console.log(err); + }); + navigation.navigate('Success', success); + }, + onExit: (linkExit: LinkExit) => { + console.log('Exit: ', linkExit); + }, + onEvent: (linkEvent: LinkEvent) => { + console.log('Event: ', linkEvent); + }, + }); + await session.open(); + } catch (err) { + console.log('Unable to open Plaid Link: ', err); + } }; return ( @@ -80,6 +73,7 @@ const HomeScreen = ({ navigation }: any) => {