@openfort/react-native 0.0.1
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/README.md +68 -0
- package/dist/Iframe.js +35 -0
- package/dist/index.js +1 -0
- package/dist/types/Iframe.d.ts +4 -0
- package/dist/types/index.d.ts +1 -0
- package/package.json +42 -0
- package/polyfills/index.ts +78 -0
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Openfort React native SDK
|
|
2
|
+
|
|
3
|
+
## Install required packages:
|
|
4
|
+
|
|
5
|
+
Using the package manager of your preference, install the openfort-js react native library, e.g. with yarn: `yarn add @openfort/react-native`.
|
|
6
|
+
|
|
7
|
+
Since react native requires installing native dependencies directly, you also have to install these required dependencies
|
|
8
|
+
```
|
|
9
|
+
yarn add buffer react-native-crypto react-native-get-random-values react-native-randombytes stream-browserify react-native-mmkv
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Setup your metro config
|
|
13
|
+
|
|
14
|
+
If you do not already have a `metro.config.js`, create one with those required extra node modules:
|
|
15
|
+
```
|
|
16
|
+
// sample metro.config.js
|
|
17
|
+
const { getDefaultConfig } = require('expo/metro-config');
|
|
18
|
+
|
|
19
|
+
module.exports = (() => {
|
|
20
|
+
const config = getDefaultConfig(__dirname);
|
|
21
|
+
|
|
22
|
+
// Add shims for Node.js modules like crypto and stream
|
|
23
|
+
config.resolver.extraNodeModules = {
|
|
24
|
+
crypto: require.resolve('react-native-crypto'),
|
|
25
|
+
stream: require.resolve('stream-browserify'),
|
|
26
|
+
buffer: require.resolve('buffer'),
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return config;
|
|
30
|
+
})();
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
## Import `Openfort` at the top of your app
|
|
34
|
+
The first file loaded should be Openfort-js polyfills:
|
|
35
|
+
```
|
|
36
|
+
import "@openfort/react-native/polyfills";
|
|
37
|
+
```
|
|
38
|
+
This will ensure the correct modules are imported and will ensure `openfort-js` works properly.
|
|
39
|
+
|
|
40
|
+
## Render secure WebView
|
|
41
|
+
|
|
42
|
+
Openfort uses a `WebView` (from `react-native-webview`) to operate as a secure environment, managing the private key and executing wallet operations. [Learn more](https://www.openfort.xyz/docs/security#embedded-self-custodial-signer).
|
|
43
|
+
|
|
44
|
+
This WebView needs to always be displayed, it is recommended to put it on top of your app. It is wrapped inside a view with `flex: 0`
|
|
45
|
+
|
|
46
|
+
Simply import it from `@openfort/react-native`
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
// Sample app/_layout.tsx using expo router
|
|
50
|
+
import { OpenfortCommunicationWebView } from '@openfort/react-native';
|
|
51
|
+
|
|
52
|
+
export default function RootLayout() {
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<>
|
|
56
|
+
<OpenfortCommunicationWebView />
|
|
57
|
+
<Slot />
|
|
58
|
+
</>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
## Notes
|
|
63
|
+
|
|
64
|
+
Because we are using `mmkv` storage, expo-go will not work. To run your app use `expo run:ios` or `expo run:android`.
|
|
65
|
+
|
|
66
|
+
# Sample
|
|
67
|
+
|
|
68
|
+
You can check out the [React native auth sample](https://github.com/openfort-xyz/react-native-auth-sample) to get your app running.
|
package/dist/Iframe.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
|
+
import { View } from "react-native";
|
|
4
|
+
import WebView from "react-native-webview";
|
|
5
|
+
const injectedCode = `
|
|
6
|
+
window.parent = {};
|
|
7
|
+
window.parent.postMessage = (msg) => window.ReactNativeWebView.postMessage(JSON.stringify(msg))
|
|
8
|
+
`;
|
|
9
|
+
export default function Iframe({ customUri }) {
|
|
10
|
+
const webViewRef = useRef(null);
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
12
|
+
const fnCallbackRef = useRef(null); // Ref to store the callback
|
|
13
|
+
const [loaded, setLoaded] = useState(false);
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
global.openfortListener = (fn) => {
|
|
16
|
+
fnCallbackRef.current = fn; // Store the callback in the ref
|
|
17
|
+
};
|
|
18
|
+
global.openfortPostMessage = (message) => {
|
|
19
|
+
webViewRef?.current?.postMessage(JSON.stringify(message));
|
|
20
|
+
setLoaded(true);
|
|
21
|
+
};
|
|
22
|
+
}, [webViewRef?.current]);
|
|
23
|
+
const handleMessage = (event) => {
|
|
24
|
+
// Trigger the stored callback, if any
|
|
25
|
+
if (fnCallbackRef.current) {
|
|
26
|
+
const origin = event.nativeEvent.url.endsWith('/') ? event.nativeEvent.url.slice(0, -1) : event.nativeEvent.url;
|
|
27
|
+
fnCallbackRef.current({ origin, data: event.nativeEvent.data });
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
if (!loaded)
|
|
31
|
+
return null;
|
|
32
|
+
const uri = customUri ? customUri : "https://embedded.openfort.xyz";
|
|
33
|
+
return (React.createElement(View, { style: { flex: 0 } },
|
|
34
|
+
React.createElement(WebView, { ref: webViewRef, source: { uri: uri }, onMessage: handleMessage, injectedJavaScript: injectedCode })));
|
|
35
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Iframe } from './Iframe';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Iframe } from './Iframe';
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@openfort/react-native",
|
|
3
|
+
"main": "dist/index.js",
|
|
4
|
+
"version": "0.0.1",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"build": "rimraf dist && tsc",
|
|
7
|
+
"lint": "eslint . --ignore-pattern dist"
|
|
8
|
+
},
|
|
9
|
+
"types": "dist/types/index.d.ts",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"polyfills"
|
|
13
|
+
],
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"events": "^3.3.0",
|
|
16
|
+
"react-native-get-random-values": "^1.11.0",
|
|
17
|
+
"react-native-mmkv": "^3.2.0",
|
|
18
|
+
"react-native-randombytes": "^3.6.1",
|
|
19
|
+
"react-native-webview": "13.12.5",
|
|
20
|
+
"text-encoding": "^0.7.0"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"@openfort/openfort-js": "^0.8.15",
|
|
24
|
+
"react": "*",
|
|
25
|
+
"react-native": "*"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@openfort/openfort-js": "^0.8.15",
|
|
29
|
+
"react": "18.3.1",
|
|
30
|
+
"react-native": "0.76.5",
|
|
31
|
+
"@eslint/js": "^9.17.0",
|
|
32
|
+
"@types/react": "~18.3.12",
|
|
33
|
+
"@types/react-test-renderer": "^18.3.0",
|
|
34
|
+
"eslint": "^9.17.0",
|
|
35
|
+
"eslint-plugin-react": "^7.37.3",
|
|
36
|
+
"globals": "^15.14.0",
|
|
37
|
+
"typescript": "^5.3.3",
|
|
38
|
+
"typescript-eslint": "^8.19.0"
|
|
39
|
+
},
|
|
40
|
+
"private": false,
|
|
41
|
+
"packageManager": "yarn@1.22.17"
|
|
42
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { polyfillGlobal } from "react-native/Libraries/Utilities/PolyfillFunctions"
|
|
2
|
+
|
|
3
|
+
// This is a workaround to fix the issue with process.version
|
|
4
|
+
if (process.version === undefined) {
|
|
5
|
+
Object.defineProperty(process, 'version', {
|
|
6
|
+
value: "", // Set the desired version (you can use any string)
|
|
7
|
+
writable: false,
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Crypto module needs getRandomValues
|
|
12
|
+
import 'react-native-get-random-values';
|
|
13
|
+
|
|
14
|
+
const MMKVStorage = require("react-native-mmkv").MMKV;
|
|
15
|
+
|
|
16
|
+
// Initialize MMKVStorage
|
|
17
|
+
const MMKV = new MMKVStorage();
|
|
18
|
+
|
|
19
|
+
const localStorage: Storage = {
|
|
20
|
+
getItem: (key: string): string | null => {
|
|
21
|
+
try {
|
|
22
|
+
const value = MMKV.getString(key);
|
|
23
|
+
// console.log("getItem", key, value);
|
|
24
|
+
return value !== undefined ? value : null; // Ensure it returns string or null
|
|
25
|
+
} catch (e) {
|
|
26
|
+
console.error('Error reading value from localStorage polyfill', e);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
setItem: (key: string, value: string) => {
|
|
31
|
+
try {
|
|
32
|
+
// console.log("setItem", key, value);
|
|
33
|
+
MMKV.set(key, value); // Sets value synchronously
|
|
34
|
+
} catch (e) {
|
|
35
|
+
console.error('Error saving value to localStorage polyfill', e);
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
removeItem: (key: string) => {
|
|
39
|
+
try {
|
|
40
|
+
// console.log("removeItem", key);
|
|
41
|
+
MMKV.delete(key); // Removes item synchronously
|
|
42
|
+
} catch (e) {
|
|
43
|
+
console.error('Error removing value from localStorage polyfill', e);
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
clear: () => {
|
|
47
|
+
try {
|
|
48
|
+
// console.log("clear");
|
|
49
|
+
MMKV.clearAll(); // Clears store synchronously
|
|
50
|
+
} catch (e) {
|
|
51
|
+
console.error('Error clearing localStorage polyfill', e);
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
key: (index: number): string | null => {
|
|
55
|
+
return MMKV.getAllKeys()[index] || null;
|
|
56
|
+
},
|
|
57
|
+
get length(): number {
|
|
58
|
+
try {
|
|
59
|
+
return MMKV.getAllKeys().length;
|
|
60
|
+
} catch {
|
|
61
|
+
console.error('Error: localStorage.length is not supported');
|
|
62
|
+
return -1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
global.localStorage = localStorage;
|
|
68
|
+
global.sessionStorage = localStorage;
|
|
69
|
+
|
|
70
|
+
// for TextEncoder and TextDecoder
|
|
71
|
+
const applyGlobalPolyfills = () => {
|
|
72
|
+
const { TextEncoder, TextDecoder } = require("text-encoding")
|
|
73
|
+
|
|
74
|
+
polyfillGlobal("TextEncoder", () => TextEncoder)
|
|
75
|
+
polyfillGlobal("TextDecoder", () => TextDecoder)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
applyGlobalPolyfills()
|