Skip to content

Commit 2cdbf19

Browse files
Merge pull request #501 from reown-com/fix/renew-uri
fix: renew uri after failure
2 parents 38611b0 + d8c49ea commit 2cdbf19

File tree

9 files changed

+144
-19
lines changed

9 files changed

+144
-19
lines changed

.changeset/cool-parks-slide.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
'@reown/appkit-react-native': patch
3+
'@reown/appkit-common-react-native': patch
4+
'@reown/appkit-bitcoin-react-native': patch
5+
'@reown/appkit-coinbase-react-native': patch
6+
'@reown/appkit-core-react-native': patch
7+
'@reown/appkit-ethers-react-native': patch
8+
'@reown/appkit-solana-react-native': patch
9+
'@reown/appkit-ui-react-native': patch
10+
'@reown/appkit-wagmi-react-native': patch
11+
---
12+
13+
fix: renew uri after failure

packages/appkit/src/partials/w3m-connecting-body/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ export interface ConnectingBodyProps {
1111
export function ConnectingBody({ title, description }: ConnectingBodyProps) {
1212
return (
1313
<FlexView padding={['3xs', '2xl', '0', '2xl']} alignItems="center" style={styles.textContainer}>
14-
<Text variant="paragraph-500">{title}</Text>
14+
<Text center numberOfLines={1} variant="paragraph-500">
15+
{title}
16+
</Text>
1517
{description ? (
1618
<Text center variant="small-400" color="fg-200" style={styles.descriptionText}>
1719
{description}

packages/appkit/src/partials/w3m-connecting-mobile/components/StoreLink.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ export function StoreLink({ visible, walletName = 'Wallet', onPress }: StoreLink
1212

1313
return (
1414
<ActionEntry style={styles.storeButton}>
15-
<Text numberOfLines={1} variant="paragraph-500" color="fg-200">
15+
<Text numberOfLines={1} style={styles.storeText} variant="paragraph-500" color="fg-200">
1616
{`Don't have ${walletName}?`}
1717
</Text>
1818
<Button
@@ -34,5 +34,8 @@ const styles = StyleSheet.create({
3434
paddingHorizontal: Spacing.l,
3535
marginHorizontal: Spacing.xl,
3636
marginTop: Spacing.l
37+
},
38+
storeText: {
39+
flexShrink: 1
3740
}
3841
});

packages/appkit/src/partials/w3m-header/index.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,12 @@ export function Header() {
100100
padding={['l', 'xl', bottomPadding, 'xl']}
101101
>
102102
{dynamicButtonTemplate()}
103-
<Text variant="paragraph-600" numberOfLines={1} testID="header-text">
103+
<Text
104+
variant="paragraph-600"
105+
numberOfLines={1}
106+
style={styles.headerText}
107+
testID="header-text"
108+
>
104109
{header}
105110
</Text>
106111
{showClose ? (

packages/appkit/src/partials/w3m-header/styles.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@ export default StyleSheet.create({
44
iconPlaceholder: {
55
height: 32,
66
width: 32
7+
},
8+
headerText: {
9+
flexShrink: 1
710
}
811
});

packages/appkit/src/views/w3m-connecting-external-view/index.tsx

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import styles from './styles';
2626
import { useInternalAppKit } from '../../AppKitContext';
2727
import { StoreLink } from '../../partials/w3m-connecting-mobile/components/StoreLink';
2828
import { WcHelpersUtil } from '../../utils/HelpersUtil';
29+
import { ErrorUtil } from '@reown/appkit-common-react-native';
2930

3031
export function ConnectingExternalView() {
3132
const { data } = useSnapshot(RouterController.state);
@@ -94,16 +95,11 @@ export function ConnectingExternalView() {
9495
}
9596
} catch (error) {
9697
LogController.sendError(error, 'ConnectingExternalView.tsx', 'onConnect');
97-
if (/(Wallet not found)/i.test((error as Error).message)) {
98-
setErrorType('not_installed');
99-
} else if (/(rejected)/i.test((error as Error).message)) {
100-
setErrorType('declined');
101-
} else {
102-
setErrorType('default');
103-
}
98+
const type = ErrorUtil.categorizeConnectionError(error);
99+
setErrorType(type);
104100
EventsController.sendEvent({
105101
type: 'track',
106-
event: 'CONNECT_ERROR',
102+
event: type === 'declined' ? 'USER_REJECTED' : 'CONNECT_ERROR',
107103
properties: { message: (error as Error)?.message ?? 'Unknown' }
108104
});
109105
}

packages/appkit/src/views/w3m-connecting-view/index.tsx

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { useSnapshot } from 'valtio';
22
import { useEffect, useLayoutEffect, useState } from 'react';
3-
import { type Platform } from '@reown/appkit-common-react-native';
3+
import { ErrorUtil, type Platform } from '@reown/appkit-common-react-native';
44
import {
55
WcController,
66
ConstantsUtil,
@@ -39,30 +39,46 @@ export function ConnectingView() {
3939
}
4040
};
4141

42-
const initializeConnection = async (retry = false) => {
42+
const initializeConnection = async (retry = false, retryTimestamp?: number) => {
4343
try {
4444
const { wcPairingExpiry } = WcController.state;
4545
const { data: routeData } = RouterController.state;
46-
if (retry || CoreHelperUtil.isPairingExpired(wcPairingExpiry)) {
46+
const isPairingExpired = CoreHelperUtil.isPairingExpired(wcPairingExpiry);
47+
if (retry || isPairingExpired) {
4748
WcController.setWcError(false);
4849

4950
const connectPromise = connect({
5051
wallet: routeData?.wallet
5152
});
5253
WcController.setWcPromise(connectPromise);
54+
await connectPromise;
5355
}
5456
} catch (error) {
5557
LogController.sendError(error, 'ConnectingView.tsx', 'initializeConnection');
5658
WcController.setWcError(true);
5759
WcController.clearUri();
58-
SnackController.showError('Declined');
59-
if (isQr && CoreHelperUtil.isAllowedRetry(lastRetry)) {
60-
setLastRetry(Date.now());
61-
initializeConnection(true);
60+
61+
const currentRetryTime = retryTimestamp ?? lastRetry;
62+
63+
if (isQr && CoreHelperUtil.isAllowedRetry(currentRetryTime)) {
64+
const newRetryTime = Date.now();
65+
setLastRetry(newRetryTime);
66+
initializeConnection(true, newRetryTime);
67+
68+
return;
6269
}
70+
71+
const isUserRejected = ErrorUtil.isUserRejectedRequestError(error);
72+
const isProposalExpired = ErrorUtil.isProposalExpiredError(error);
73+
if (!isProposalExpired) {
74+
SnackController.showError(
75+
isUserRejected ? 'User rejected the request' : 'Something went wrong'
76+
);
77+
}
78+
6379
EventsController.sendEvent({
6480
type: 'track',
65-
event: 'CONNECT_ERROR',
81+
event: isUserRejected ? 'USER_REJECTED' : 'CONNECT_ERROR',
6682
properties: {
6783
message: (error as Error)?.message ?? 'Unknown'
6884
}

packages/common/src/types/api/events.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export type EventName =
3333
| 'SELECT_WALLET'
3434
| 'CONNECT_SUCCESS'
3535
| 'CONNECT_ERROR'
36+
| 'USER_REJECTED'
3637
| 'DISCONNECT_SUCCESS'
3738
| 'DISCONNECT_ERROR'
3839
| 'CLICK_WALLET_HELP'
@@ -153,6 +154,14 @@ export type Event =
153154
message: string;
154155
};
155156
}
157+
| {
158+
type: 'track';
159+
address?: string;
160+
event: 'USER_REJECTED';
161+
properties: {
162+
message: string;
163+
};
164+
}
156165
| {
157166
type: 'track';
158167
event: 'DISCONNECT_SUCCESS';

packages/common/src/utils/ErrorUtil.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
export const ErrorUtil = {
2+
RPC_ERROR_CODE: {
3+
USER_REJECTED_REQUEST: 4001,
4+
USER_REJECTED_METHODS: 5002
5+
} as const,
26
UniversalProviderErrors: {
37
UNAUTHORIZED_DOMAIN_NOT_ALLOWED: {
48
message: 'Unauthorized: origin not allowed',
@@ -31,5 +35,79 @@ export const ErrorUtil = {
3135
shortMessage: 'Project ID Not Configured',
3236
longMessage: 'Project ID Not Configured - update configuration'
3337
}
38+
},
39+
isRpcProviderError(error: any): error is { message: string; code: number } {
40+
try {
41+
if (typeof error === 'object' && error !== null) {
42+
const objErr = error as Record<string, unknown>;
43+
44+
const hasMessage = typeof objErr['message'] === 'string';
45+
const hasCode = typeof objErr['code'] === 'number';
46+
47+
return hasMessage && hasCode;
48+
}
49+
50+
return false;
51+
} catch {
52+
return false;
53+
}
54+
},
55+
isUserRejectedMessage(message: string) {
56+
return (
57+
message.toLowerCase().includes('rejected') ||
58+
message.toLowerCase().includes('user cancelled') ||
59+
message.toLowerCase().includes('user canceled')
60+
);
61+
},
62+
isUserRejectedRequestError(error: any) {
63+
if (ErrorUtil.isRpcProviderError(error)) {
64+
const isUserRejectedCode = error.code === ErrorUtil.RPC_ERROR_CODE.USER_REJECTED_REQUEST;
65+
const isUserRejectedMethodsCode =
66+
error.code === ErrorUtil.RPC_ERROR_CODE.USER_REJECTED_METHODS;
67+
68+
return (
69+
isUserRejectedCode ||
70+
isUserRejectedMethodsCode ||
71+
ErrorUtil.isUserRejectedMessage(error.message)
72+
);
73+
}
74+
75+
if (error instanceof Error) {
76+
return ErrorUtil.isUserRejectedMessage(error.message);
77+
}
78+
79+
return false;
80+
},
81+
isProposalExpiredError(error: any) {
82+
if (ErrorUtil.isRpcProviderError(error)) {
83+
return error.message?.toLowerCase().includes('proposal expired');
84+
}
85+
86+
if (error) {
87+
return (
88+
typeof error?.message === 'string' &&
89+
error.message?.toLowerCase().includes('proposal expired')
90+
);
91+
}
92+
93+
return false;
94+
},
95+
isWalletNotFoundError(error: any) {
96+
if (error && typeof error?.message === 'string') {
97+
return /wallet not found/i.test(error.message);
98+
}
99+
100+
return false;
101+
},
102+
categorizeConnectionError(error: any): 'not_installed' | 'declined' | 'default' {
103+
if (ErrorUtil.isWalletNotFoundError(error)) {
104+
return 'not_installed';
105+
}
106+
107+
if (ErrorUtil.isUserRejectedRequestError(error)) {
108+
return 'declined';
109+
}
110+
111+
return 'default';
34112
}
35113
};

0 commit comments

Comments
 (0)