wallet-stack 1.0.0-alpha.138 → 1.0.0-alpha.140
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/metro-config.js +6 -8
- package/package.json +14 -14
- package/plugin/build/index.js +2 -0
- package/plugin/build/withAndroidUserAgent.js +1 -1
- package/plugin/build/withIosLiquidGlassCompat.d.ts +14 -0
- package/plugin/build/withIosLiquidGlassCompat.js +23 -0
- package/src/app/saga.ts +1 -2
- package/src/components/Modal.tsx +39 -20
- package/src/components/TextInput.tsx +4 -4
- package/src/components/__snapshots__/AccountNumber.test.tsx.snap +0 -1
- package/src/components/__snapshots__/CircleButton.test.tsx.snap +0 -2
- package/src/components/__snapshots__/Dialog.test.tsx.snap +179 -208
- package/src/components/__snapshots__/TextInputWithButtons.test.tsx.snap +0 -1
- package/src/dapps/saga.ts +4 -2
- package/src/public/components/Button.tsx +13 -4
- package/src/qrcode/utils.ts +1 -3
- package/src/send/EnterAmount.tsx +18 -6
- package/src/walletConnect/saga.test.ts +59 -0
- package/src/walletConnect/saga.ts +15 -17
- package/src/webview/__snapshots__/WebViewAndroidBottomSheet.test.tsx.snap +0 -1
package/metro-config.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
// Wraps Expo's getDefaultConfig to add our customizations
|
|
4
|
-
function getDefaultConfig(...args) {
|
|
5
|
-
const config = getDefaultConfigExpo(...args)
|
|
6
|
-
|
|
1
|
+
// Wraps a Metro config (Expo's or Sentry's) with wallet-stack customizations.
|
|
2
|
+
function withWalletStackConfig(config) {
|
|
7
3
|
config.transformer.getTransformOptions = async () => ({
|
|
8
4
|
transform: {
|
|
9
5
|
experimentalImportSupport: false,
|
|
@@ -15,6 +11,7 @@ function getDefaultConfig(...args) {
|
|
|
15
11
|
config.resolver.assetExts = [...config.resolver.assetExts, 'txt']
|
|
16
12
|
|
|
17
13
|
config.resolver.extraNodeModules = {
|
|
14
|
+
...config.resolver.extraNodeModules,
|
|
18
15
|
// This is the crypto module we want to use moving forward (unless something better comes up).
|
|
19
16
|
// It is implemented natively using OpenSSL.
|
|
20
17
|
crypto: require.resolve('react-native-quick-crypto'),
|
|
@@ -23,6 +20,7 @@ function getDefaultConfig(...args) {
|
|
|
23
20
|
buffer: require.resolve('@craftzdog/react-native-buffer'),
|
|
24
21
|
}
|
|
25
22
|
|
|
23
|
+
const baseResolveRequest = config.resolver.resolveRequest
|
|
26
24
|
// TODO: remove this once we stop using absolute imports
|
|
27
25
|
config.resolver.resolveRequest = (context, moduleName, platform) => {
|
|
28
26
|
if (moduleName.startsWith('src/')) {
|
|
@@ -31,10 +29,10 @@ function getDefaultConfig(...args) {
|
|
|
31
29
|
if (moduleName === 'locales') {
|
|
32
30
|
return context.resolveRequest(context, 'wallet-stack/locales', platform)
|
|
33
31
|
}
|
|
34
|
-
return context.resolveRequest(context, moduleName, platform)
|
|
32
|
+
return (baseResolveRequest ?? context.resolveRequest)(context, moduleName, platform)
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
return config
|
|
38
36
|
}
|
|
39
37
|
|
|
40
|
-
module.exports = {
|
|
38
|
+
module.exports = { withWalletStackConfig }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wallet-stack",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.140",
|
|
4
4
|
"author": "Valora Inc",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -89,12 +89,12 @@
|
|
|
89
89
|
"@valora/react-native-keychain": "^10.0.0-valora.1",
|
|
90
90
|
"@valora/react-native-webview": "^13.13.4",
|
|
91
91
|
"@walletconnect/react-native-compat": "2.21.9",
|
|
92
|
-
"expo-camera": "~
|
|
92
|
+
"expo-camera": "~17.0.10",
|
|
93
93
|
"expo-splash-screen": "~0.30.10",
|
|
94
94
|
"lottie-react-native": "^5.1.6",
|
|
95
95
|
"mixpanel-react-native": "^3.2.1",
|
|
96
|
-
"react": "19.
|
|
97
|
-
"react-native": "0.
|
|
96
|
+
"react": "19.1.0",
|
|
97
|
+
"react-native": "0.81.5",
|
|
98
98
|
"react-native-adjust": "^4.38.1",
|
|
99
99
|
"react-native-app-control": "^1.0.2",
|
|
100
100
|
"react-native-auth0": "5.0.0-beta.5",
|
|
@@ -112,10 +112,10 @@
|
|
|
112
112
|
"react-native-permissions": "^4.1.5",
|
|
113
113
|
"react-native-quick-base64": ">=2.1.0",
|
|
114
114
|
"react-native-quick-crypto": "^1.0.16",
|
|
115
|
-
"react-native-reanimated": "~3.
|
|
115
|
+
"react-native-reanimated": "~3.19.5",
|
|
116
116
|
"react-native-restart": "^0.0.27",
|
|
117
117
|
"react-native-safe-area-context": "^5.6.1",
|
|
118
|
-
"react-native-screens": "~4.
|
|
118
|
+
"react-native-screens": "~4.16.0",
|
|
119
119
|
"react-native-shake": "5.5.2",
|
|
120
120
|
"react-native-share": "^11.1.0",
|
|
121
121
|
"react-native-simple-toast": "^3.3.2",
|
|
@@ -148,8 +148,8 @@
|
|
|
148
148
|
"bignumber.js": "^9.1.2",
|
|
149
149
|
"country-data": "^0.0.31",
|
|
150
150
|
"date-fns": "^4.1.0",
|
|
151
|
-
"expo": "^
|
|
152
|
-
"expo-image": "~
|
|
151
|
+
"expo": "^54.0.33",
|
|
152
|
+
"expo-image": "~3.0.11",
|
|
153
153
|
"fast-levenshtein": "^3.0.0",
|
|
154
154
|
"fp-ts": "2.16.9",
|
|
155
155
|
"futoin-hkdf": "^1.5.3",
|
|
@@ -198,9 +198,9 @@
|
|
|
198
198
|
"@tsconfig/node-lts": "^22.0.1",
|
|
199
199
|
"@types/jest": "^29.5.3",
|
|
200
200
|
"@types/node": "^20",
|
|
201
|
-
"@types/react": "~19.
|
|
201
|
+
"@types/react": "~19.1.10",
|
|
202
202
|
"@types/react-native-video": "^5.0.15",
|
|
203
|
-
"@types/react-test-renderer": "^19.
|
|
203
|
+
"@types/react-test-renderer": "^19.1.0",
|
|
204
204
|
"@types/redux-mock-store": "^1.0.6",
|
|
205
205
|
"@walletconnect/types": "2.21.9",
|
|
206
206
|
"ajv": "^8.18.0",
|
|
@@ -211,18 +211,18 @@
|
|
|
211
211
|
"jest-junit": "^10.0.0",
|
|
212
212
|
"jest-snapshot": "^29.6.2",
|
|
213
213
|
"mockdate": "^3.0.5",
|
|
214
|
-
"react": "19.
|
|
215
|
-
"react-native": "0.
|
|
214
|
+
"react": "19.1.0",
|
|
215
|
+
"react-native": "0.81.5",
|
|
216
216
|
"react-native-contacts": "https://github.com/valora-xyz/react-native-contacts#9940121",
|
|
217
217
|
"react-native-kill-packager": "^1.0.0",
|
|
218
218
|
"react-native-svg-mock": "^2.0.0",
|
|
219
|
-
"react-test-renderer": "19.
|
|
219
|
+
"react-test-renderer": "19.1.0",
|
|
220
220
|
"redux-mock-store": "^1.5.3",
|
|
221
221
|
"redux-saga-test-plan": "^4.0.6",
|
|
222
222
|
"rimraf": "^6.0.1",
|
|
223
223
|
"ts-jest": "^29.1.1",
|
|
224
224
|
"ts-node": "^11.0.0-beta.1",
|
|
225
|
-
"typescript": "
|
|
225
|
+
"typescript": "~5.9.2",
|
|
226
226
|
"typescript-json-schema": "^0.59.0"
|
|
227
227
|
},
|
|
228
228
|
"resolutions": {
|
package/plugin/build/index.js
CHANGED
|
@@ -4,6 +4,7 @@ const config_plugins_1 = require("@expo/config-plugins");
|
|
|
4
4
|
const withAndroidUserAgent_1 = require("./withAndroidUserAgent");
|
|
5
5
|
const withAndroidWindowSoftInputModeAdjustNothing_1 = require("./withAndroidWindowSoftInputModeAdjustNothing");
|
|
6
6
|
const withIosAppDelegateResetKeychain_1 = require("./withIosAppDelegateResetKeychain");
|
|
7
|
+
const withIosLiquidGlassCompat_1 = require("./withIosLiquidGlassCompat");
|
|
7
8
|
const withIosUserAgent_1 = require("./withIosUserAgent");
|
|
8
9
|
/**
|
|
9
10
|
* A config plugin for configuring `wallet-stack`
|
|
@@ -13,6 +14,7 @@ const withMobileApp = (config, props = {}) => {
|
|
|
13
14
|
// iOS
|
|
14
15
|
withIosAppDelegateResetKeychain_1.withIosAppDelegateResetKeychain,
|
|
15
16
|
[withIosUserAgent_1.withIosUserAgent, props],
|
|
17
|
+
withIosLiquidGlassCompat_1.withIosLiquidGlassCompat,
|
|
16
18
|
// Android
|
|
17
19
|
[withAndroidUserAgent_1.withAndroidUserAgent, props],
|
|
18
20
|
withAndroidWindowSoftInputModeAdjustNothing_1.withAndroidWindowSoftInputModeAdjustNothing,
|
|
@@ -46,7 +46,7 @@ function addNeededImports(src) {
|
|
|
46
46
|
tag: 'wallet-stack/main-application-user-agent-imports',
|
|
47
47
|
src,
|
|
48
48
|
newSrc: imports.join('\n'),
|
|
49
|
-
anchor: /import
|
|
49
|
+
anchor: /import expo\.modules\.ReactNativeHostWrapper/,
|
|
50
50
|
offset: 1,
|
|
51
51
|
comment: '//',
|
|
52
52
|
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ConfigPlugin } from '@expo/config-plugins';
|
|
2
|
+
/**
|
|
3
|
+
* Opt the app out of iOS 26's Liquid Glass redesign by setting
|
|
4
|
+
* `UIDesignRequiresCompatibility` in Info.plist. The flag is ignored on
|
|
5
|
+
* iOS < 26, so older versions are unaffected.
|
|
6
|
+
*
|
|
7
|
+
* This is a temporary measure: Apple removes the flag in Xcode 27 and
|
|
8
|
+
* Liquid Glass becomes mandatory by iOS 27. Track adoption of the new
|
|
9
|
+
* design as tech debt and remove this mod once wallet-stack's nav bar
|
|
10
|
+
* styling is updated for Liquid Glass.
|
|
11
|
+
*
|
|
12
|
+
* See https://www.donnywals.com/opting-your-app-out-of-the-liquid-glass-redesign-with-xcode-26/
|
|
13
|
+
*/
|
|
14
|
+
export declare const withIosLiquidGlassCompat: ConfigPlugin;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.withIosLiquidGlassCompat = void 0;
|
|
4
|
+
const config_plugins_1 = require("@expo/config-plugins");
|
|
5
|
+
/**
|
|
6
|
+
* Opt the app out of iOS 26's Liquid Glass redesign by setting
|
|
7
|
+
* `UIDesignRequiresCompatibility` in Info.plist. The flag is ignored on
|
|
8
|
+
* iOS < 26, so older versions are unaffected.
|
|
9
|
+
*
|
|
10
|
+
* This is a temporary measure: Apple removes the flag in Xcode 27 and
|
|
11
|
+
* Liquid Glass becomes mandatory by iOS 27. Track adoption of the new
|
|
12
|
+
* design as tech debt and remove this mod once wallet-stack's nav bar
|
|
13
|
+
* styling is updated for Liquid Glass.
|
|
14
|
+
*
|
|
15
|
+
* See https://www.donnywals.com/opting-your-app-out-of-the-liquid-glass-redesign-with-xcode-26/
|
|
16
|
+
*/
|
|
17
|
+
const withIosLiquidGlassCompat = (config) => {
|
|
18
|
+
return (0, config_plugins_1.withInfoPlist)(config, (config) => {
|
|
19
|
+
config.modResults.UIDesignRequiresCompatibility = true;
|
|
20
|
+
return config;
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
exports.withIosLiquidGlassCompat = withIosLiquidGlassCompat;
|
package/src/app/saga.ts
CHANGED
|
@@ -260,9 +260,8 @@ function* watchDeepLinks() {
|
|
|
260
260
|
|
|
261
261
|
export function* handleOpenUrl(action: OpenUrlAction) {
|
|
262
262
|
const { url, openExternal, isSecureOrigin } = action
|
|
263
|
-
const walletConnectEnabled: boolean = yield* call(isWalletConnectEnabled, url)
|
|
264
263
|
Logger.debug(TAG, 'Handling url', url)
|
|
265
|
-
if (isDeepLink(url) || (
|
|
264
|
+
if (isDeepLink(url) || (isWalletConnectDeepLink(url) && (yield* call(isWalletConnectEnabled)))) {
|
|
266
265
|
// Handle celo links directly, this avoids showing the "Open with App" sheet on Android
|
|
267
266
|
yield* call(handleDeepLink, openDeepLink(url, isSecureOrigin))
|
|
268
267
|
} else if (/^https?:\/\//i.test(url) === true && !openExternal) {
|
package/src/components/Modal.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as React from 'react'
|
|
2
|
-
import { StyleProp, StyleSheet, ViewStyle } from 'react-native'
|
|
3
|
-
import
|
|
4
|
-
import { SafeAreaView, useSafeAreaFrame } from 'react-native-safe-area-context'
|
|
2
|
+
import { Modal as RNModal, Pressable, StyleProp, StyleSheet, View, ViewStyle } from 'react-native'
|
|
3
|
+
import { SafeAreaView } from 'react-native-safe-area-context'
|
|
5
4
|
import Card from 'src/components/Card'
|
|
6
5
|
import colors from 'src/styles/colors'
|
|
7
6
|
|
|
@@ -24,31 +23,51 @@ export default function Modal({
|
|
|
24
23
|
onBackgroundPress,
|
|
25
24
|
onModalHide,
|
|
26
25
|
}: Props) {
|
|
27
|
-
|
|
26
|
+
// RN's <Modal> only fires `onDismiss` on iOS; emit `onModalHide` cross-platform
|
|
27
|
+
// whenever `isVisible` transitions from true to false.
|
|
28
|
+
const wasVisible = React.useRef(isVisible)
|
|
29
|
+
React.useEffect(() => {
|
|
30
|
+
if (wasVisible.current && !isVisible) {
|
|
31
|
+
onModalHide?.()
|
|
32
|
+
}
|
|
33
|
+
wasVisible.current = isVisible
|
|
34
|
+
}, [isVisible, onModalHide])
|
|
28
35
|
|
|
29
36
|
return (
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
backdropOpacity={0.1}
|
|
35
|
-
onBackdropPress={onBackgroundPress}
|
|
36
|
-
// The default uses `Dimensions.get('window').height` but sometimes reports an incorrect height on Android
|
|
37
|
-
// `useSafeAreaFrame()` seems to work better
|
|
38
|
-
deviceHeight={height}
|
|
37
|
+
<RNModal
|
|
38
|
+
visible={isVisible}
|
|
39
|
+
transparent={true}
|
|
40
|
+
animationType="fade"
|
|
39
41
|
statusBarTranslucent={true}
|
|
40
|
-
|
|
42
|
+
onRequestClose={onBackgroundPress}
|
|
43
|
+
testID={testID}
|
|
41
44
|
>
|
|
42
|
-
<
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
<View style={styles.overlay}>
|
|
46
|
+
<Pressable style={StyleSheet.absoluteFill} onPress={onBackgroundPress} />
|
|
47
|
+
<View style={[styles.contentWrapper, modalStyle]} pointerEvents="box-none">
|
|
48
|
+
<SafeAreaView>
|
|
49
|
+
<Card style={[styles.root, style]} rounded={true}>
|
|
50
|
+
{children}
|
|
51
|
+
</Card>
|
|
52
|
+
</SafeAreaView>
|
|
53
|
+
</View>
|
|
54
|
+
</View>
|
|
55
|
+
</RNModal>
|
|
48
56
|
)
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
const styles = StyleSheet.create({
|
|
60
|
+
overlay: {
|
|
61
|
+
flex: 1,
|
|
62
|
+
backgroundColor: 'rgba(0, 0, 0, 0.1)',
|
|
63
|
+
},
|
|
64
|
+
// Mirrors react-native-modal's default outer style so the card stays inset
|
|
65
|
+
// from screen edges and centered vertically.
|
|
66
|
+
contentWrapper: {
|
|
67
|
+
flex: 1,
|
|
68
|
+
margin: 20,
|
|
69
|
+
justifyContent: 'center',
|
|
70
|
+
},
|
|
52
71
|
root: {
|
|
53
72
|
backgroundColor: colors.backgroundPrimary,
|
|
54
73
|
padding: 24,
|
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
import * as React from 'react'
|
|
6
6
|
import {
|
|
7
|
-
|
|
7
|
+
BlurEvent,
|
|
8
|
+
FocusEvent,
|
|
8
9
|
Platform,
|
|
9
10
|
TextInput as RNTextInput,
|
|
10
11
|
TextInputProps as RNTextInputProps,
|
|
11
12
|
StyleProp,
|
|
12
13
|
StyleSheet,
|
|
13
|
-
TextInputFocusEventData,
|
|
14
14
|
View,
|
|
15
15
|
ViewStyle,
|
|
16
16
|
} from 'react-native'
|
|
@@ -46,12 +46,12 @@ export class CTextInput extends React.Component<Props, State> {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
-
handleInputFocus = (e:
|
|
49
|
+
handleInputFocus = (e: FocusEvent) => {
|
|
50
50
|
this.setState({ isFocused: true })
|
|
51
51
|
this.props.onFocus?.(e)
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
handleInputBlur = (e:
|
|
54
|
+
handleInputBlur = (e: BlurEvent) => {
|
|
55
55
|
this.setState({ isFocused: false })
|
|
56
56
|
this.props.onBlur?.(e)
|
|
57
57
|
}
|
|
@@ -48,7 +48,6 @@ exports[`CircleButton renders correctly with minimum props 1`] = `
|
|
|
48
48
|
onResponderTerminate={[Function]}
|
|
49
49
|
onResponderTerminationRequest={[Function]}
|
|
50
50
|
onStartShouldSetResponder={[Function]}
|
|
51
|
-
ref={null}
|
|
52
51
|
style={
|
|
53
52
|
[
|
|
54
53
|
[
|
|
@@ -154,7 +153,6 @@ exports[`CircleButton when given optional props renders correctly 1`] = `
|
|
|
154
153
|
onResponderTerminate={[Function]}
|
|
155
154
|
onResponderTerminationRequest={[Function]}
|
|
156
155
|
onStartShouldSetResponder={[Function]}
|
|
157
|
-
ref={null}
|
|
158
156
|
style={
|
|
159
157
|
[
|
|
160
158
|
[
|
|
@@ -2,247 +2,218 @@
|
|
|
2
2
|
|
|
3
3
|
exports[`renders correctly 1`] = `
|
|
4
4
|
<Modal
|
|
5
|
-
animationType="
|
|
6
|
-
deviceHeight={1334}
|
|
7
|
-
deviceWidth={null}
|
|
8
|
-
hardwareAccelerated={false}
|
|
9
|
-
hideModalContentWhileAnimating={false}
|
|
10
|
-
onBackdropPress={[Function]}
|
|
11
|
-
onModalHide={[Function]}
|
|
12
|
-
onModalWillHide={[Function]}
|
|
13
|
-
onModalWillShow={[Function]}
|
|
14
|
-
onRequestClose={[Function]}
|
|
15
|
-
panResponderThreshold={4}
|
|
16
|
-
scrollHorizontal={false}
|
|
17
|
-
scrollOffset={0}
|
|
18
|
-
scrollOffsetMax={0}
|
|
19
|
-
scrollTo={null}
|
|
5
|
+
animationType="fade"
|
|
20
6
|
statusBarTranslucent={true}
|
|
21
|
-
supportedOrientations={
|
|
22
|
-
[
|
|
23
|
-
"portrait",
|
|
24
|
-
"landscape",
|
|
25
|
-
]
|
|
26
|
-
}
|
|
27
|
-
swipeThreshold={100}
|
|
28
7
|
transparent={true}
|
|
29
8
|
visible={true}
|
|
30
9
|
>
|
|
31
10
|
<View
|
|
32
|
-
accessibilityState={
|
|
33
|
-
{
|
|
34
|
-
"busy": undefined,
|
|
35
|
-
"checked": undefined,
|
|
36
|
-
"disabled": undefined,
|
|
37
|
-
"expanded": undefined,
|
|
38
|
-
"selected": undefined,
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
accessible={true}
|
|
42
|
-
collapsable={false}
|
|
43
|
-
focusable={true}
|
|
44
|
-
onClick={[Function]}
|
|
45
|
-
onResponderGrant={[Function]}
|
|
46
|
-
onResponderMove={[Function]}
|
|
47
|
-
onResponderRelease={[Function]}
|
|
48
|
-
onResponderTerminate={[Function]}
|
|
49
|
-
onResponderTerminationRequest={[Function]}
|
|
50
|
-
onStartShouldSetResponder={[Function]}
|
|
51
|
-
style={
|
|
52
|
-
{
|
|
53
|
-
"backgroundColor": "black",
|
|
54
|
-
"bottom": 0,
|
|
55
|
-
"height": 1334,
|
|
56
|
-
"left": 0,
|
|
57
|
-
"opacity": 0,
|
|
58
|
-
"position": "absolute",
|
|
59
|
-
"right": 0,
|
|
60
|
-
"top": 0,
|
|
61
|
-
"width": 750,
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
/>
|
|
65
|
-
<View
|
|
66
|
-
collapsable={false}
|
|
67
|
-
deviceHeight={1334}
|
|
68
|
-
deviceWidth={null}
|
|
69
|
-
hideModalContentWhileAnimating={false}
|
|
70
|
-
onBackdropPress={[Function]}
|
|
71
|
-
onModalHide={[Function]}
|
|
72
|
-
onModalWillHide={[Function]}
|
|
73
|
-
onModalWillShow={[Function]}
|
|
74
|
-
panResponderThreshold={4}
|
|
75
|
-
pointerEvents="box-none"
|
|
76
|
-
scrollHorizontal={false}
|
|
77
|
-
scrollOffset={0}
|
|
78
|
-
scrollOffsetMax={0}
|
|
79
|
-
scrollTo={null}
|
|
80
|
-
statusBarTranslucent={true}
|
|
81
11
|
style={
|
|
82
12
|
{
|
|
13
|
+
"backgroundColor": "rgba(0, 0, 0, 0.1)",
|
|
83
14
|
"flex": 1,
|
|
84
|
-
"justifyContent": "center",
|
|
85
|
-
"margin": 37.5,
|
|
86
|
-
"transform": [
|
|
87
|
-
{
|
|
88
|
-
"translateY": 1334,
|
|
89
|
-
},
|
|
90
|
-
],
|
|
91
15
|
}
|
|
92
16
|
}
|
|
93
|
-
supportedOrientations={
|
|
94
|
-
[
|
|
95
|
-
"portrait",
|
|
96
|
-
"landscape",
|
|
97
|
-
]
|
|
98
|
-
}
|
|
99
|
-
swipeThreshold={100}
|
|
100
17
|
>
|
|
101
|
-
<
|
|
102
|
-
|
|
18
|
+
<View
|
|
19
|
+
accessibilityState={
|
|
20
|
+
{
|
|
21
|
+
"busy": undefined,
|
|
22
|
+
"checked": undefined,
|
|
23
|
+
"disabled": undefined,
|
|
24
|
+
"expanded": undefined,
|
|
25
|
+
"selected": undefined,
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
accessibilityValue={
|
|
103
29
|
{
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
30
|
+
"max": undefined,
|
|
31
|
+
"min": undefined,
|
|
32
|
+
"now": undefined,
|
|
33
|
+
"text": undefined,
|
|
108
34
|
}
|
|
109
35
|
}
|
|
36
|
+
accessible={true}
|
|
37
|
+
collapsable={false}
|
|
38
|
+
focusable={true}
|
|
39
|
+
onBlur={[Function]}
|
|
40
|
+
onClick={[Function]}
|
|
41
|
+
onFocus={[Function]}
|
|
42
|
+
onResponderGrant={[Function]}
|
|
43
|
+
onResponderMove={[Function]}
|
|
44
|
+
onResponderRelease={[Function]}
|
|
45
|
+
onResponderTerminate={[Function]}
|
|
46
|
+
onResponderTerminationRequest={[Function]}
|
|
47
|
+
onStartShouldSetResponder={[Function]}
|
|
48
|
+
style={
|
|
49
|
+
{
|
|
50
|
+
"bottom": 0,
|
|
51
|
+
"left": 0,
|
|
52
|
+
"position": "absolute",
|
|
53
|
+
"right": 0,
|
|
54
|
+
"top": 0,
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/>
|
|
58
|
+
<View
|
|
59
|
+
pointerEvents="box-none"
|
|
60
|
+
style={
|
|
61
|
+
[
|
|
62
|
+
{
|
|
63
|
+
"flex": 1,
|
|
64
|
+
"justifyContent": "center",
|
|
65
|
+
"margin": 20,
|
|
66
|
+
},
|
|
67
|
+
undefined,
|
|
68
|
+
]
|
|
69
|
+
}
|
|
110
70
|
>
|
|
111
|
-
<
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
"shadowColor": "rgba(156, 164, 169, 0.4)",
|
|
124
|
-
"shadowOffset": {
|
|
125
|
-
"height": 2,
|
|
126
|
-
"width": 0,
|
|
127
|
-
},
|
|
128
|
-
"shadowOpacity": 1,
|
|
129
|
-
"shadowRadius": 12,
|
|
130
|
-
},
|
|
71
|
+
<RNCSafeAreaView
|
|
72
|
+
edges={
|
|
73
|
+
{
|
|
74
|
+
"bottom": "additive",
|
|
75
|
+
"left": "additive",
|
|
76
|
+
"right": "additive",
|
|
77
|
+
"top": "additive",
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
>
|
|
81
|
+
<View
|
|
82
|
+
style={
|
|
131
83
|
[
|
|
132
84
|
{
|
|
133
85
|
"backgroundColor": "#FFFFFF",
|
|
134
|
-
"
|
|
135
|
-
"padding": 24,
|
|
86
|
+
"padding": 16,
|
|
136
87
|
},
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
style={
|
|
152
|
-
{
|
|
153
|
-
"color": "#2E3338",
|
|
154
|
-
"fontFamily": "Inter-Bold",
|
|
155
|
-
"fontSize": 20,
|
|
156
|
-
"lineHeight": 28,
|
|
157
|
-
"marginBottom": 12,
|
|
158
|
-
"textAlign": "center",
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
>
|
|
162
|
-
Dialog
|
|
163
|
-
</Text>
|
|
164
|
-
<Text
|
|
165
|
-
style={
|
|
88
|
+
{
|
|
89
|
+
"borderRadius": 8,
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"elevation": 12,
|
|
93
|
+
"shadowColor": "rgba(156, 164, 169, 0.4)",
|
|
94
|
+
"shadowOffset": {
|
|
95
|
+
"height": 2,
|
|
96
|
+
"width": 0,
|
|
97
|
+
},
|
|
98
|
+
"shadowOpacity": 1,
|
|
99
|
+
"shadowRadius": 12,
|
|
100
|
+
},
|
|
101
|
+
[
|
|
166
102
|
{
|
|
167
|
-
"
|
|
168
|
-
"
|
|
169
|
-
"
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
}
|
|
175
|
-
>
|
|
176
|
-
"HELLO"
|
|
177
|
-
</Text>
|
|
178
|
-
</View>
|
|
179
|
-
</RCTScrollView>
|
|
180
|
-
<View
|
|
181
|
-
style={
|
|
182
|
-
{
|
|
183
|
-
"flexDirection": "row",
|
|
184
|
-
"flexWrap": "wrap",
|
|
185
|
-
"justifyContent": "space-around",
|
|
186
|
-
"maxWidth": "100%",
|
|
187
|
-
}
|
|
103
|
+
"backgroundColor": "#FFFFFF",
|
|
104
|
+
"maxHeight": "100%",
|
|
105
|
+
"padding": 24,
|
|
106
|
+
},
|
|
107
|
+
undefined,
|
|
108
|
+
],
|
|
109
|
+
]
|
|
188
110
|
}
|
|
189
111
|
>
|
|
190
|
-
<
|
|
191
|
-
|
|
192
|
-
{
|
|
193
|
-
"busy": undefined,
|
|
194
|
-
"checked": undefined,
|
|
195
|
-
"disabled": undefined,
|
|
196
|
-
"expanded": undefined,
|
|
197
|
-
"selected": undefined,
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
accessibilityValue={
|
|
201
|
-
{
|
|
202
|
-
"max": undefined,
|
|
203
|
-
"min": undefined,
|
|
204
|
-
"now": undefined,
|
|
205
|
-
"text": undefined,
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
accessible={true}
|
|
209
|
-
focusable={true}
|
|
210
|
-
nativeBackgroundAndroid={
|
|
112
|
+
<RCTScrollView
|
|
113
|
+
contentContainerStyle={
|
|
211
114
|
{
|
|
212
|
-
"
|
|
213
|
-
"rippleRadius": undefined,
|
|
214
|
-
"type": "ThemeAttrAndroid",
|
|
115
|
+
"alignItems": "center",
|
|
215
116
|
}
|
|
216
117
|
}
|
|
217
|
-
onClick={[Function]}
|
|
218
|
-
onResponderGrant={[Function]}
|
|
219
|
-
onResponderMove={[Function]}
|
|
220
|
-
onResponderRelease={[Function]}
|
|
221
|
-
onResponderTerminate={[Function]}
|
|
222
|
-
onResponderTerminationRequest={[Function]}
|
|
223
|
-
onStartShouldSetResponder={[Function]}
|
|
224
118
|
>
|
|
225
|
-
<
|
|
226
|
-
|
|
227
|
-
|
|
119
|
+
<View>
|
|
120
|
+
<Text
|
|
121
|
+
style={
|
|
228
122
|
{
|
|
229
|
-
"color": "#
|
|
230
|
-
"fontFamily": "Inter-
|
|
123
|
+
"color": "#2E3338",
|
|
124
|
+
"fontFamily": "Inter-Bold",
|
|
125
|
+
"fontSize": 20,
|
|
126
|
+
"lineHeight": 28,
|
|
127
|
+
"marginBottom": 12,
|
|
128
|
+
"textAlign": "center",
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
>
|
|
132
|
+
Dialog
|
|
133
|
+
</Text>
|
|
134
|
+
<Text
|
|
135
|
+
style={
|
|
136
|
+
{
|
|
137
|
+
"color": "#2E3338",
|
|
138
|
+
"fontFamily": "Inter-Regular",
|
|
231
139
|
"fontSize": 16,
|
|
232
140
|
"lineHeight": 24,
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
141
|
+
"marginBottom": 24,
|
|
142
|
+
"textAlign": "center",
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
>
|
|
146
|
+
"HELLO"
|
|
147
|
+
</Text>
|
|
148
|
+
</View>
|
|
149
|
+
</RCTScrollView>
|
|
150
|
+
<View
|
|
151
|
+
style={
|
|
152
|
+
{
|
|
153
|
+
"flexDirection": "row",
|
|
154
|
+
"flexWrap": "wrap",
|
|
155
|
+
"justifyContent": "space-around",
|
|
156
|
+
"maxWidth": "100%",
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
>
|
|
160
|
+
<View
|
|
161
|
+
accessibilityState={
|
|
162
|
+
{
|
|
163
|
+
"busy": undefined,
|
|
164
|
+
"checked": undefined,
|
|
165
|
+
"disabled": undefined,
|
|
166
|
+
"expanded": undefined,
|
|
167
|
+
"selected": undefined,
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
accessibilityValue={
|
|
171
|
+
{
|
|
172
|
+
"max": undefined,
|
|
173
|
+
"min": undefined,
|
|
174
|
+
"now": undefined,
|
|
175
|
+
"text": undefined,
|
|
176
|
+
}
|
|
238
177
|
}
|
|
178
|
+
accessible={true}
|
|
179
|
+
focusable={true}
|
|
180
|
+
nativeBackgroundAndroid={
|
|
181
|
+
{
|
|
182
|
+
"attribute": "selectableItemBackgroundBorderless",
|
|
183
|
+
"rippleRadius": undefined,
|
|
184
|
+
"type": "ThemeAttrAndroid",
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
onClick={[Function]}
|
|
188
|
+
onResponderGrant={[Function]}
|
|
189
|
+
onResponderMove={[Function]}
|
|
190
|
+
onResponderRelease={[Function]}
|
|
191
|
+
onResponderTerminate={[Function]}
|
|
192
|
+
onResponderTerminationRequest={[Function]}
|
|
193
|
+
onStartShouldSetResponder={[Function]}
|
|
239
194
|
>
|
|
240
|
-
|
|
241
|
-
|
|
195
|
+
<Text
|
|
196
|
+
style={
|
|
197
|
+
[
|
|
198
|
+
{
|
|
199
|
+
"color": "#1AB775",
|
|
200
|
+
"fontFamily": "Inter-SemiBold",
|
|
201
|
+
"fontSize": 16,
|
|
202
|
+
"lineHeight": 24,
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"paddingTop": 16,
|
|
206
|
+
},
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
>
|
|
210
|
+
Press Me
|
|
211
|
+
</Text>
|
|
212
|
+
</View>
|
|
242
213
|
</View>
|
|
243
214
|
</View>
|
|
244
|
-
</
|
|
245
|
-
</
|
|
215
|
+
</RNCSafeAreaView>
|
|
216
|
+
</View>
|
|
246
217
|
</View>
|
|
247
218
|
</Modal>
|
|
248
219
|
`;
|
package/src/dapps/saga.ts
CHANGED
|
@@ -43,8 +43,10 @@ export function* handleOpenDapp(action: PayloadAction<DappSelectedAction>) {
|
|
|
43
43
|
).inAppWebviewEnabled
|
|
44
44
|
|
|
45
45
|
if (dappsWebViewEnabled) {
|
|
46
|
-
|
|
47
|
-
|
|
46
|
+
if (
|
|
47
|
+
isDeepLink(dappUrl) ||
|
|
48
|
+
(isWalletConnectDeepLink(dappUrl) && (yield* call(isWalletConnectEnabled)))
|
|
49
|
+
) {
|
|
48
50
|
yield* call(handleDeepLink, openDeepLink(dappUrl, true))
|
|
49
51
|
} else {
|
|
50
52
|
navigate(Screens.WebViewScreen, { uri: dappUrl })
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
// See useWallet.ts for why we lazy-require the internal module rather than
|
|
2
|
+
// using top-level runtime imports.
|
|
1
3
|
import React from 'react'
|
|
2
|
-
import
|
|
4
|
+
import type * as InternalButton from '../../components/Button'
|
|
5
|
+
import type { ButtonProps } from '../../components/Button'
|
|
6
|
+
|
|
7
|
+
const loadInternal = (): typeof InternalButton => require('../../components/Button')
|
|
3
8
|
|
|
4
9
|
export type ButtonSize = 'small' | 'medium' | 'full'
|
|
5
10
|
export type ButtonType = 'primary' | 'secondary' | 'tertiary'
|
|
@@ -11,8 +16,9 @@ export interface CustomButtonProps extends Omit<ButtonProps, 'size' | 'type' | '
|
|
|
11
16
|
textSize?: ButtonTextSize
|
|
12
17
|
}
|
|
13
18
|
|
|
14
|
-
function toInternalBtnType(type?: ButtonType): BtnTypes | undefined {
|
|
19
|
+
function toInternalBtnType(type?: ButtonType): InternalButton.BtnTypes | undefined {
|
|
15
20
|
if (!type) return undefined
|
|
21
|
+
const { BtnTypes } = loadInternal()
|
|
16
22
|
switch (type) {
|
|
17
23
|
case 'primary':
|
|
18
24
|
return BtnTypes.PRIMARY
|
|
@@ -26,8 +32,9 @@ function toInternalBtnType(type?: ButtonType): BtnTypes | undefined {
|
|
|
26
32
|
}
|
|
27
33
|
}
|
|
28
34
|
|
|
29
|
-
function toInternalBtnSize(size?: ButtonSize): BtnSizes | undefined {
|
|
35
|
+
function toInternalBtnSize(size?: ButtonSize): InternalButton.BtnSizes | undefined {
|
|
30
36
|
if (!size) return undefined
|
|
37
|
+
const { BtnSizes } = loadInternal()
|
|
31
38
|
switch (size) {
|
|
32
39
|
case 'small':
|
|
33
40
|
return BtnSizes.SMALL
|
|
@@ -41,8 +48,9 @@ function toInternalBtnSize(size?: ButtonSize): BtnSizes | undefined {
|
|
|
41
48
|
}
|
|
42
49
|
}
|
|
43
50
|
|
|
44
|
-
function toInternalTextSize(size?: ButtonTextSize): TextSizes | undefined {
|
|
51
|
+
function toInternalTextSize(size?: ButtonTextSize): InternalButton.TextSizes | undefined {
|
|
45
52
|
if (!size) return undefined
|
|
53
|
+
const { TextSizes } = loadInternal()
|
|
46
54
|
switch (size) {
|
|
47
55
|
case 'small':
|
|
48
56
|
return TextSizes.SMALL
|
|
@@ -55,6 +63,7 @@ function toInternalTextSize(size?: ButtonTextSize): TextSizes | undefined {
|
|
|
55
63
|
}
|
|
56
64
|
|
|
57
65
|
export function Button(props: CustomButtonProps) {
|
|
66
|
+
const { default: BaseButton } = loadInternal()
|
|
58
67
|
return (
|
|
59
68
|
<BaseButton
|
|
60
69
|
{...props}
|
package/src/qrcode/utils.ts
CHANGED
|
@@ -88,11 +88,9 @@ export function* handleQRCodeDefault({
|
|
|
88
88
|
}: HandleQRCodeDetectedAction) {
|
|
89
89
|
AppAnalytics.track(QrScreenEvents.qr_scanned, qrCode)
|
|
90
90
|
|
|
91
|
-
const walletConnectEnabled: boolean = yield* call(isWalletConnectEnabled, qrCode.data)
|
|
92
|
-
|
|
93
91
|
// TODO there's some duplication with deep links handing
|
|
94
92
|
// would be nice to refactor this
|
|
95
|
-
if (qrCode.data.startsWith('wc:') &&
|
|
93
|
+
if (qrCode.data.startsWith('wc:') && (yield* call(isWalletConnectEnabled))) {
|
|
96
94
|
yield* fork(handleLoadingWithTimeout, WalletConnectPairingOrigin.Scan)
|
|
97
95
|
yield* call(initialiseWalletConnect, qrCode.data, WalletConnectPairingOrigin.Scan)
|
|
98
96
|
return
|
package/src/send/EnterAmount.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next'
|
|
|
4
4
|
import { Keyboard, TextInput as RNTextInput, StyleSheet, Text } from 'react-native'
|
|
5
5
|
import { View } from 'react-native-animatable'
|
|
6
6
|
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
7
|
+
import Svg, { Path } from 'react-native-svg'
|
|
7
8
|
import AppAnalytics from 'src/analytics/AppAnalytics'
|
|
8
9
|
import { SendEvents } from 'src/analytics/Events'
|
|
9
10
|
import BackButton from 'src/components/BackButton'
|
|
@@ -29,8 +30,8 @@ import { getLocalCurrencySymbol } from 'src/localCurrency/selectors'
|
|
|
29
30
|
import { useSelector } from 'src/redux/hooks'
|
|
30
31
|
import EnterAmountOptions from 'src/send/EnterAmountOptions'
|
|
31
32
|
import { AmountEnteredIn } from 'src/send/types'
|
|
32
|
-
import { typeScale } from 'src/styles/fonts'
|
|
33
33
|
import Colors from 'src/styles/colors'
|
|
34
|
+
import { typeScale } from 'src/styles/fonts'
|
|
34
35
|
import { Spacing } from 'src/styles/styles'
|
|
35
36
|
import { feeCurrenciesSelector } from 'src/tokens/selectors'
|
|
36
37
|
import { TokenBalance } from 'src/tokens/slice'
|
|
@@ -258,7 +259,7 @@ export default function EnterAmount({
|
|
|
258
259
|
|
|
259
260
|
{!!recipientSlot && (
|
|
260
261
|
<>
|
|
261
|
-
<
|
|
262
|
+
<ConnectorArrow />
|
|
262
263
|
{recipientSlot}
|
|
263
264
|
</>
|
|
264
265
|
)}
|
|
@@ -367,6 +368,20 @@ export default function EnterAmount({
|
|
|
367
368
|
)
|
|
368
369
|
}
|
|
369
370
|
|
|
371
|
+
function ConnectorArrow() {
|
|
372
|
+
return (
|
|
373
|
+
<Svg width={12} height={16} viewBox="0 0 12 16" fill="none" style={styles.connector}>
|
|
374
|
+
<Path
|
|
375
|
+
d="M6 0 V13 M2 9 L6 13 L10 9"
|
|
376
|
+
stroke={Colors.borderPrimary}
|
|
377
|
+
strokeWidth={2}
|
|
378
|
+
strokeLinecap="round"
|
|
379
|
+
strokeLinejoin="round"
|
|
380
|
+
/>
|
|
381
|
+
</Svg>
|
|
382
|
+
)
|
|
383
|
+
}
|
|
384
|
+
|
|
370
385
|
const styles = StyleSheet.create({
|
|
371
386
|
safeAreaContainer: {
|
|
372
387
|
flex: 1,
|
|
@@ -394,10 +409,7 @@ const styles = StyleSheet.create({
|
|
|
394
409
|
paddingHorizontal: Spacing.Regular16,
|
|
395
410
|
borderRadius: 16,
|
|
396
411
|
},
|
|
397
|
-
|
|
398
|
-
width: 2,
|
|
399
|
-
height: 12,
|
|
400
|
-
backgroundColor: Colors.borderPrimary,
|
|
412
|
+
connector: {
|
|
401
413
|
alignSelf: 'center',
|
|
402
414
|
marginVertical: Spacing.Tiny4,
|
|
403
415
|
},
|
|
@@ -36,6 +36,8 @@ import {
|
|
|
36
36
|
handlePendingState,
|
|
37
37
|
initialiseWalletConnect,
|
|
38
38
|
initialiseWalletConnectV2,
|
|
39
|
+
isWalletConnectEnabled,
|
|
40
|
+
isWalletConnectV2Uri,
|
|
39
41
|
normalizeTransactions,
|
|
40
42
|
walletConnectSaga,
|
|
41
43
|
} from 'src/walletConnect/saga'
|
|
@@ -1046,6 +1048,63 @@ describe('showActionRequest', () => {
|
|
|
1046
1048
|
const v2ConnectionString =
|
|
1047
1049
|
'wc:79a02f869d0f921e435a5e0643304548ebfa4a0430f9c66fe8b1a9254db7ef77@2?relay-protocol=irn&symKey=f661b0a9316a4ce0b6892bdce42bea0f45037f2c1bee9e118a3a4bc868a32a39'
|
|
1048
1050
|
|
|
1051
|
+
describe('isWalletConnectV2Uri', () => {
|
|
1052
|
+
it('returns true for a v2 wc: URI', () => {
|
|
1053
|
+
expect(isWalletConnectV2Uri(v2ConnectionString)).toBe(true)
|
|
1054
|
+
})
|
|
1055
|
+
|
|
1056
|
+
it('returns false for any string that is not a wc: pairing URI', () => {
|
|
1057
|
+
// parseUri is meant for wc: URIs only; passing anything else would force it
|
|
1058
|
+
// down its base64 link-mode fallback, which can throw on RN's strict native
|
|
1059
|
+
// base64 decoder. The startsWith('wc:') guard keeps non-WC strings out.
|
|
1060
|
+
expect(isWalletConnectV2Uri('https://churrito.fi')).toBe(false)
|
|
1061
|
+
expect(isWalletConnectV2Uri('testapp://wallet/wc?uri=wc:abc@2')).toBe(false)
|
|
1062
|
+
expect(isWalletConnectV2Uri('')).toBe(false)
|
|
1063
|
+
})
|
|
1064
|
+
|
|
1065
|
+
it('returns false for a v1 wc: URI', () => {
|
|
1066
|
+
expect(isWalletConnectV2Uri('wc:abc@1?bridge=https%3A%2F%2Fbridge.walletconnect.org')).toBe(
|
|
1067
|
+
false
|
|
1068
|
+
)
|
|
1069
|
+
})
|
|
1070
|
+
})
|
|
1071
|
+
|
|
1072
|
+
describe('isWalletConnectEnabled', () => {
|
|
1073
|
+
it('returns true when project id is set and v2 is not disabled', () => {
|
|
1074
|
+
jest.mocked(getAppConfig).mockReturnValue({
|
|
1075
|
+
displayName: 'Test App',
|
|
1076
|
+
deepLinkUrlScheme: 'testapp',
|
|
1077
|
+
registryName: 'test',
|
|
1078
|
+
features: { walletConnect: { projectId: '123' } },
|
|
1079
|
+
})
|
|
1080
|
+
jest.mocked(getFeatureGate).mockReturnValue(false)
|
|
1081
|
+
expect(isWalletConnectEnabled()).toBe(true)
|
|
1082
|
+
})
|
|
1083
|
+
|
|
1084
|
+
it('returns false when project id is missing', () => {
|
|
1085
|
+
jest.mocked(getAppConfig).mockReturnValue({
|
|
1086
|
+
displayName: 'Test App',
|
|
1087
|
+
deepLinkUrlScheme: 'testapp',
|
|
1088
|
+
registryName: 'test',
|
|
1089
|
+
})
|
|
1090
|
+
jest.mocked(getFeatureGate).mockReturnValue(false)
|
|
1091
|
+
expect(isWalletConnectEnabled()).toBe(false)
|
|
1092
|
+
})
|
|
1093
|
+
|
|
1094
|
+
it('returns false when v2 is feature-gated off', () => {
|
|
1095
|
+
jest.mocked(getAppConfig).mockReturnValue({
|
|
1096
|
+
displayName: 'Test App',
|
|
1097
|
+
deepLinkUrlScheme: 'testapp',
|
|
1098
|
+
registryName: 'test',
|
|
1099
|
+
features: { walletConnect: { projectId: '123' } },
|
|
1100
|
+
})
|
|
1101
|
+
jest
|
|
1102
|
+
.mocked(getFeatureGate)
|
|
1103
|
+
.mockImplementation((gate) => gate === StatsigFeatureGates.DISABLE_WALLET_CONNECT_V2)
|
|
1104
|
+
expect(isWalletConnectEnabled()).toBe(false)
|
|
1105
|
+
})
|
|
1106
|
+
})
|
|
1107
|
+
|
|
1049
1108
|
describe('initialiseWalletConnect', () => {
|
|
1050
1109
|
const origin = WalletConnectPairingOrigin.Deeplink
|
|
1051
1110
|
it('initializes v2 if enabled and there is a wallet connect project id', async () => {
|
|
@@ -1123,31 +1123,29 @@ export function* initialiseWalletConnectV2(uri: string, origin: WalletConnectPai
|
|
|
1123
1123
|
yield* put(initialisePairing(uri, origin))
|
|
1124
1124
|
}
|
|
1125
1125
|
|
|
1126
|
-
export function
|
|
1127
|
-
|
|
1126
|
+
export function isWalletConnectV2Uri(uri: string): boolean {
|
|
1127
|
+
if (!uri.startsWith('wc:')) {
|
|
1128
|
+
return false
|
|
1129
|
+
}
|
|
1130
|
+
return parseUri(uri).version === 2
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
export function isWalletConnectEnabled(): boolean {
|
|
1128
1134
|
const walletConnectV2Disabled = getFeatureGate(StatsigFeatureGates.DISABLE_WALLET_CONNECT_V2)
|
|
1129
1135
|
const walletConnectProjectId = getAppConfig().features?.walletConnect?.projectId
|
|
1130
|
-
|
|
1131
|
-
return !!walletConnectProjectId && !walletConnectV2Disabled && version === 2
|
|
1136
|
+
return !!walletConnectProjectId && !walletConnectV2Disabled
|
|
1132
1137
|
}
|
|
1133
1138
|
|
|
1134
1139
|
export function* initialiseWalletConnect(uri: string, origin: WalletConnectPairingOrigin) {
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
const { version } = parseUri(uri)
|
|
1138
|
-
if (!walletConnectEnabled) {
|
|
1139
|
-
Logger.debug('initialiseWalletConnect', `v${version} is disabled, ignoring`)
|
|
1140
|
+
if (!isWalletConnectV2Uri(uri)) {
|
|
1141
|
+
Logger.debug('initialiseWalletConnect', 'URI is not a WalletConnect v2 link, ignoring')
|
|
1140
1142
|
return
|
|
1141
1143
|
}
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
yield* call(initialiseWalletConnectV2, uri, origin)
|
|
1146
|
-
break
|
|
1147
|
-
case 1:
|
|
1148
|
-
default:
|
|
1149
|
-
throw new Error(`Unsupported WalletConnect version '${version}'`)
|
|
1144
|
+
if (!isWalletConnectEnabled()) {
|
|
1145
|
+
Logger.debug('initialiseWalletConnect', 'WalletConnect is disabled, ignoring')
|
|
1146
|
+
return
|
|
1150
1147
|
}
|
|
1148
|
+
yield* call(initialiseWalletConnectV2, uri, origin)
|
|
1151
1149
|
}
|
|
1152
1150
|
|
|
1153
1151
|
export function* showWalletConnectionSuccessMessage(dappName: string) {
|
|
@@ -7,7 +7,6 @@ exports[`WebViewAndroidBottomSheet renders correctly when visible 1`] = `
|
|
|
7
7
|
animationType="none"
|
|
8
8
|
deviceHeight={null}
|
|
9
9
|
deviceWidth={null}
|
|
10
|
-
hardwareAccelerated={false}
|
|
11
10
|
hideModalContentWhileAnimating={false}
|
|
12
11
|
onBackdropPress={[MockFunction]}
|
|
13
12
|
onModalHide={[Function]}
|