@gluwa/connect-kit 0.2.0-next.1 → 0.2.0-next.3
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/CHANGELOG.md +11 -0
- package/assets/creditwallet.png +0 -0
- package/assets/fonts/Inter-Medium.ttf +0 -0
- package/assets/fonts/Inter-Medium.woff2 +0 -0
- package/assets/fonts/Inter-Regular.ttf +0 -0
- package/assets/fonts/Inter-Regular.woff2 +0 -0
- package/assets/fonts/Inter-SemiBold.ttf +0 -0
- package/assets/fonts/Inter-SemiBold.woff2 +0 -0
- package/assets/graphic.png +0 -0
- package/assets/metamask.png +0 -0
- package/assets/wc-logo.svg +3 -0
- package/assets/wc-search.png +0 -0
- package/dist/chunk-EDNRW47A.js +401 -0
- package/dist/credit-connect.js +2 -409
- package/dist/index.css +1903 -390
- package/dist/index.d.ts +16 -10
- package/dist/index.js +1110 -870
- package/dist/package.json +7 -6
- package/package.json +8 -8
- package/src/ConnectKitProvider.tsx +90 -65
- package/src/assets.d.ts +5 -0
- package/src/components/actionItem/index.tsx +50 -0
- package/src/components/actionItem/style.scss +47 -0
- package/src/components/copyLink/index.tsx +16 -0
- package/src/components/copyLink/style.scss +16 -0
- package/src/components/qrArea/index.tsx +70 -0
- package/src/components/qrArea/style.scss +130 -0
- package/src/components/searchBar/asset/Search.tsx +20 -0
- package/src/components/searchBar/asset/XMarkCircle.tsx +24 -0
- package/src/components/searchBar/index.tsx +44 -0
- package/src/components/searchBar/style.scss +58 -0
- package/src/components/snackbar/index.tsx +25 -0
- package/src/components/snackbar/style.scss +51 -0
- package/src/components/toggle/index.tsx +27 -0
- package/src/components/toggle/style.scss +53 -0
- package/src/components/walletGrid/index.tsx +64 -0
- package/src/components/walletGrid/style.scss +108 -0
- package/src/components/walletItem/index.tsx +49 -0
- package/src/components/walletItem/style.scss +70 -0
- package/src/connector-meta.ts +12 -7
- package/src/core/config.ts +20 -1
- package/src/creditConnectConnector.ts +17 -25
- package/src/hooks/useWCState.ts +22 -5
- package/src/hooks/useWagmiConnect.ts +0 -1
- package/src/index.ts +2 -2
- package/src/layout/allWallets/index.tsx +52 -0
- package/src/layout/allWallets/style.scss +62 -0
- package/src/layout/connectDialog/asset/backIcon.tsx +24 -0
- package/src/layout/connectDialog/asset/checkIcon.tsx +28 -0
- package/src/layout/connectDialog/asset/closeIcon.tsx +21 -0
- package/src/layout/connectDialog/asset/copyIcon.tsx +28 -0
- package/src/layout/connectDialog/asset/linkIcon.tsx +24 -0
- package/src/layout/connectDialog/asset/spinner.tsx +21 -0
- package/src/layout/connectDialog/idle/index.tsx +32 -0
- package/src/layout/connectDialog/idle/style.scss +61 -0
- package/src/layout/connectDialog/index.tsx +286 -0
- package/src/layout/connectDialog/loading/index.tsx +13 -0
- package/src/layout/connectDialog/loading/style.scss +19 -0
- package/src/layout/connectDialog/metaMask/index.tsx +61 -0
- package/src/layout/connectDialog/metaMask/style.scss +65 -0
- package/src/layout/connectDialog/qr/index.tsx +33 -0
- package/src/layout/connectDialog/style.scss +95 -0
- package/src/layout/connectDialog/timeout/index.tsx +31 -0
- package/src/layout/connectDialog/timeout/style.scss +64 -0
- package/src/layout/connectDialog/walletConnect/index.tsx +45 -0
- package/src/layout/connectDialog/walletConnect/style.scss +48 -0
- package/src/layout/connectDialog/walletQR/index.tsx +37 -0
- package/src/layout/detailPanel/index.tsx +147 -0
- package/src/layout/detailPanel/style.scss +64 -0
- package/src/layout/selectorPanel/index.tsx +100 -0
- package/src/layout/selectorPanel/style.scss +113 -0
- package/src/style/color.scss +417 -0
- package/src/style/font.scss +28 -0
- package/src/style/index.scss +5 -0
- package/src/style/mixin.scss +217 -0
- package/src/style/reset.scss +110 -0
- package/src/style/variables.scss +13 -0
- package/src/types.ts +11 -8
- package/src/utils/platform.ts +6 -0
- package/tsup.config.ts +32 -2
- package/dist/chunk-DDA6FP6U.js +0 -7
- package/src/ConnectModal.scss +0 -665
- package/src/ConnectModal.tsx +0 -573
- package/src/components/QRFrame.tsx +0 -43
- package/src/views/CreditWalletView.tsx +0 -58
- package/src/views/IdleView.tsx +0 -10
- package/src/views/MetaMaskView.tsx +0 -66
- package/src/views/SwitchChainView.tsx +0 -72
- package/src/views/WalletConnectView.tsx +0 -206
package/src/ConnectModal.tsx
DELETED
|
@@ -1,573 +0,0 @@
|
|
|
1
|
-
import { useState, useEffect, useCallback, useMemo, type FC, type ReactNode } from 'react';
|
|
2
|
-
import './ConnectModal.scss';
|
|
3
|
-
import { useAccount, useConfig, useDisconnect } from 'wagmi';
|
|
4
|
-
import {
|
|
5
|
-
type ConnectModalProps,
|
|
6
|
-
type ConnectErrorContext,
|
|
7
|
-
type ConnectErrorReason,
|
|
8
|
-
type Connectors,
|
|
9
|
-
type ConnectorId,
|
|
10
|
-
type WCWallet,
|
|
11
|
-
type WCSubView,
|
|
12
|
-
} from './types';
|
|
13
|
-
import { CONNECTOR_META } from './connector-meta';
|
|
14
|
-
import { detectMetaMaskExtension } from './utils/platform';
|
|
15
|
-
import { useWagmiConnect, resolveWagmiConnector } from './hooks/useWagmiConnect';
|
|
16
|
-
import { useWCState, type WCState } from './hooks/useWCState';
|
|
17
|
-
import { IdleView } from './views/IdleView';
|
|
18
|
-
import { CreditWalletView } from './views/CreditWalletView';
|
|
19
|
-
import { MetaMaskView } from './views/MetaMaskView';
|
|
20
|
-
import { WalletConnectView } from './views/WalletConnectView';
|
|
21
|
-
import { SwitchChainView } from './views/SwitchChainView';
|
|
22
|
-
|
|
23
|
-
const RECENT_KEY = 'connect-kit:recent';
|
|
24
|
-
const VALID_CONNECTOR_IDS: ReadonlySet<ConnectorId> = new Set<ConnectorId>([
|
|
25
|
-
'CREDIT_CONNECT',
|
|
26
|
-
'CREDIT_WALLET',
|
|
27
|
-
'METAMASK',
|
|
28
|
-
'WALLET_CONNECT',
|
|
29
|
-
]);
|
|
30
|
-
|
|
31
|
-
const readRecentConnector = (): ConnectorId | null => {
|
|
32
|
-
try {
|
|
33
|
-
const raw = localStorage.getItem(RECENT_KEY);
|
|
34
|
-
if (raw == null) return null;
|
|
35
|
-
return VALID_CONNECTOR_IDS.has(raw as ConnectorId) ? (raw as ConnectorId) : null;
|
|
36
|
-
} catch {
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const writeRecentConnector = (
|
|
42
|
-
id: ConnectorId,
|
|
43
|
-
onLog?: (message: string, error?: Error) => void,
|
|
44
|
-
): void => {
|
|
45
|
-
try {
|
|
46
|
-
localStorage.setItem(RECENT_KEY, id);
|
|
47
|
-
} catch (err) {
|
|
48
|
-
const normalized = err instanceof Error ? err : new Error(String(err));
|
|
49
|
-
onLog?.(`[connect-kit] writeRecentConnector failed: ${normalized.message}`, normalized);
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
const REJECTED_ERROR_CODES = new Set<unknown>([
|
|
54
|
-
4001, // EIP-1193 user rejected
|
|
55
|
-
'ACTION_REJECTED', // ethers v6
|
|
56
|
-
]);
|
|
57
|
-
|
|
58
|
-
const classifyError = (error: Error): ConnectErrorReason => {
|
|
59
|
-
const code =
|
|
60
|
-
typeof error === 'object' && error !== null && 'code' in error
|
|
61
|
-
? (error as unknown as { code: unknown }).code
|
|
62
|
-
: undefined;
|
|
63
|
-
if (code != null && REJECTED_ERROR_CODES.has(code)) return 'rejected';
|
|
64
|
-
const message = error.message?.toLowerCase() ?? '';
|
|
65
|
-
if (
|
|
66
|
-
message.includes('user rejected') ||
|
|
67
|
-
message.includes('user denied') ||
|
|
68
|
-
message.includes('user cancel') ||
|
|
69
|
-
message.includes('rejected by user') ||
|
|
70
|
-
message.includes('action_rejected') ||
|
|
71
|
-
message.includes('사용자가 거부') ||
|
|
72
|
-
message.includes('사용자가 취소') ||
|
|
73
|
-
message.includes('ユーザーが拒否') ||
|
|
74
|
-
message.includes('ユーザーがキャンセル')
|
|
75
|
-
) {
|
|
76
|
-
return 'rejected';
|
|
77
|
-
}
|
|
78
|
-
if (message.includes('unsupported') || message.includes('not installed')) return 'unsupported';
|
|
79
|
-
return 'unknown';
|
|
80
|
-
};
|
|
81
|
-
|
|
82
|
-
interface SelectorPaneProps {
|
|
83
|
-
connectors: ConnectorId[];
|
|
84
|
-
selected: ConnectorId | null;
|
|
85
|
-
recentConnector: ConnectorId | null;
|
|
86
|
-
onSelect: (id: ConnectorId) => void;
|
|
87
|
-
onClose: () => void;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const SelectorPane: FC<SelectorPaneProps> = ({
|
|
91
|
-
connectors,
|
|
92
|
-
selected,
|
|
93
|
-
recentConnector,
|
|
94
|
-
onSelect,
|
|
95
|
-
onClose,
|
|
96
|
-
}) => {
|
|
97
|
-
const hasRecent = recentConnector !== null && connectors.includes(recentConnector);
|
|
98
|
-
const recentConnectors = hasRecent ? [recentConnector] : [];
|
|
99
|
-
const otherConnectors = connectors.filter((id) => id !== recentConnector);
|
|
100
|
-
|
|
101
|
-
const downloadUrl = connectors
|
|
102
|
-
.map((id) => CONNECTOR_META[id].downloadUrl)
|
|
103
|
-
.find((url) => url != null);
|
|
104
|
-
|
|
105
|
-
return (
|
|
106
|
-
<div className="ck-pane ck-pane--selector">
|
|
107
|
-
<div className="ck-pane__header">
|
|
108
|
-
<h3 className="ck-pane__title">Connect Wallet</h3>
|
|
109
|
-
<button
|
|
110
|
-
type="button"
|
|
111
|
-
className="ck-btn-close ck-mobile-only"
|
|
112
|
-
onClick={onClose}
|
|
113
|
-
aria-label="Close"
|
|
114
|
-
/>
|
|
115
|
-
</div>
|
|
116
|
-
|
|
117
|
-
<div className="ck-connector-list">
|
|
118
|
-
{hasRecent && (
|
|
119
|
-
<>
|
|
120
|
-
{recentConnectors.map((id) => (
|
|
121
|
-
<ConnectorItem
|
|
122
|
-
key={id}
|
|
123
|
-
id={id}
|
|
124
|
-
isSelected={selected === id}
|
|
125
|
-
isRecent
|
|
126
|
-
onSelect={onSelect}
|
|
127
|
-
/>
|
|
128
|
-
))}
|
|
129
|
-
{otherConnectors.length > 0 && (
|
|
130
|
-
<div className="ck-divider">
|
|
131
|
-
<span>OR</span>
|
|
132
|
-
</div>
|
|
133
|
-
)}
|
|
134
|
-
</>
|
|
135
|
-
)}
|
|
136
|
-
|
|
137
|
-
{otherConnectors.map((id) => (
|
|
138
|
-
<ConnectorItem
|
|
139
|
-
key={id}
|
|
140
|
-
id={id}
|
|
141
|
-
isSelected={selected === id}
|
|
142
|
-
isRecent={false}
|
|
143
|
-
onSelect={onSelect}
|
|
144
|
-
/>
|
|
145
|
-
))}
|
|
146
|
-
</div>
|
|
147
|
-
|
|
148
|
-
{downloadUrl && (
|
|
149
|
-
<div className="ck-pane__footer">
|
|
150
|
-
<a
|
|
151
|
-
href={downloadUrl}
|
|
152
|
-
target="_blank"
|
|
153
|
-
rel="nofollow noopener noreferrer"
|
|
154
|
-
className="ck-link-no-wallet"
|
|
155
|
-
>
|
|
156
|
-
I don't have a wallet >
|
|
157
|
-
</a>
|
|
158
|
-
</div>
|
|
159
|
-
)}
|
|
160
|
-
</div>
|
|
161
|
-
);
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
interface ConnectorItemProps {
|
|
165
|
-
id: ConnectorId;
|
|
166
|
-
isSelected: boolean;
|
|
167
|
-
isRecent: boolean;
|
|
168
|
-
onSelect: (id: ConnectorId) => void;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const ConnectorItem: FC<ConnectorItemProps> = ({ id, isSelected, isRecent, onSelect }) => {
|
|
172
|
-
const meta = CONNECTOR_META[id];
|
|
173
|
-
|
|
174
|
-
return (
|
|
175
|
-
<button
|
|
176
|
-
type="button"
|
|
177
|
-
className={`ck-connector-item ck-connector-item--${id.toLowerCase().replaceAll('_', '-')} ${isSelected ? 'is-active' : ''}`}
|
|
178
|
-
onClick={() => {
|
|
179
|
-
onSelect(id);
|
|
180
|
-
}}
|
|
181
|
-
>
|
|
182
|
-
{meta.logoUrl ? (
|
|
183
|
-
<img className="ck-connector-item__icon" src={meta.logoUrl} alt="" aria-hidden="true" />
|
|
184
|
-
) : (
|
|
185
|
-
<span className="ck-connector-item__icon" aria-hidden="true" />
|
|
186
|
-
)}
|
|
187
|
-
<span className="ck-connector-item__label">{meta.label}</span>
|
|
188
|
-
{isRecent && <span className="ck-connector-item__badge">Recent</span>}
|
|
189
|
-
</button>
|
|
190
|
-
);
|
|
191
|
-
};
|
|
192
|
-
|
|
193
|
-
interface DetailPaneProps {
|
|
194
|
-
selectedConnector: ConnectorId | null;
|
|
195
|
-
qrUri: string | null;
|
|
196
|
-
hasMetaMaskExtension: boolean;
|
|
197
|
-
connectorLogoMap: Partial<Record<ConnectorId, string>>;
|
|
198
|
-
wc: WCState;
|
|
199
|
-
errorOverride: ReactNode | null;
|
|
200
|
-
onBack: () => void;
|
|
201
|
-
onClose: () => void;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
const DetailPane: FC<DetailPaneProps> = ({
|
|
205
|
-
selectedConnector,
|
|
206
|
-
qrUri,
|
|
207
|
-
hasMetaMaskExtension,
|
|
208
|
-
connectorLogoMap,
|
|
209
|
-
wc,
|
|
210
|
-
errorOverride,
|
|
211
|
-
onBack,
|
|
212
|
-
onClose,
|
|
213
|
-
}) => {
|
|
214
|
-
const title = getDetailTitle(
|
|
215
|
-
selectedConnector,
|
|
216
|
-
hasMetaMaskExtension,
|
|
217
|
-
wc.subView,
|
|
218
|
-
wc.selectedWallet,
|
|
219
|
-
);
|
|
220
|
-
const showBack = selectedConnector !== null;
|
|
221
|
-
|
|
222
|
-
return (
|
|
223
|
-
<div className="ck-pane ck-pane--detail">
|
|
224
|
-
<div className="ck-pane__header">
|
|
225
|
-
{showBack && (
|
|
226
|
-
<button type="button" className="ck-btn-back" onClick={onBack} aria-label="Back" />
|
|
227
|
-
)}
|
|
228
|
-
<h3 className="ck-pane__title">{title}</h3>
|
|
229
|
-
<button type="button" className="ck-btn-close" onClick={onClose} aria-label="Close" />
|
|
230
|
-
</div>
|
|
231
|
-
|
|
232
|
-
<div className="ck-pane__body">
|
|
233
|
-
{errorOverride || (
|
|
234
|
-
<>
|
|
235
|
-
{!selectedConnector && <IdleView />}
|
|
236
|
-
|
|
237
|
-
{(selectedConnector === 'CREDIT_CONNECT' || selectedConnector === 'CREDIT_WALLET') && (
|
|
238
|
-
<CreditWalletView connectorId={selectedConnector} qrUri={qrUri} />
|
|
239
|
-
)}
|
|
240
|
-
|
|
241
|
-
{selectedConnector === 'METAMASK' && (
|
|
242
|
-
<MetaMaskView
|
|
243
|
-
qrUri={qrUri}
|
|
244
|
-
hasExtension={hasMetaMaskExtension}
|
|
245
|
-
logoUrl={connectorLogoMap.METAMASK}
|
|
246
|
-
/>
|
|
247
|
-
)}
|
|
248
|
-
|
|
249
|
-
{selectedConnector === 'WALLET_CONNECT' && (
|
|
250
|
-
<WalletConnectView
|
|
251
|
-
subView={wc.subView}
|
|
252
|
-
qrUri={qrUri}
|
|
253
|
-
logoUrl={connectorLogoMap.WALLET_CONNECT}
|
|
254
|
-
walletList={wc.walletList}
|
|
255
|
-
walletListLoading={wc.walletListLoading}
|
|
256
|
-
walletListSearch={wc.walletListSearch}
|
|
257
|
-
walletListFilterActive={wc.filterActive}
|
|
258
|
-
selectedWallet={wc.selectedWallet}
|
|
259
|
-
onShowList={wc.onShowList}
|
|
260
|
-
onSelectWallet={wc.onSelectWallet}
|
|
261
|
-
onSearchChange={wc.onSearchChange}
|
|
262
|
-
onFilterToggle={wc.onFilterToggle}
|
|
263
|
-
/>
|
|
264
|
-
)}
|
|
265
|
-
</>
|
|
266
|
-
)}
|
|
267
|
-
</div>
|
|
268
|
-
</div>
|
|
269
|
-
);
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
const getDetailTitle = (
|
|
273
|
-
connector: ConnectorId | null,
|
|
274
|
-
hasMetaMaskExtension: boolean,
|
|
275
|
-
wcSubView: WCSubView,
|
|
276
|
-
wcSelectedWallet: WCWallet | null,
|
|
277
|
-
): string => {
|
|
278
|
-
if (!connector) return '';
|
|
279
|
-
|
|
280
|
-
if (connector === 'WALLET_CONNECT') {
|
|
281
|
-
if (wcSubView === 'list') return 'All Wallets';
|
|
282
|
-
if (wcSubView === 'wallet' && wcSelectedWallet) return `Scan with ${wcSelectedWallet.name}`;
|
|
283
|
-
return 'Scan with WalletConnect';
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
if (connector === 'METAMASK' && hasMetaMaskExtension) return 'MetaMask';
|
|
287
|
-
|
|
288
|
-
return CONNECTOR_META[connector].detailTitle;
|
|
289
|
-
};
|
|
290
|
-
|
|
291
|
-
const resolveConnectorsFromConfig = (connectorsConfig?: Connectors): ConnectorId[] => {
|
|
292
|
-
if (!connectorsConfig) return [];
|
|
293
|
-
|
|
294
|
-
const connectors: ConnectorId[] = [];
|
|
295
|
-
|
|
296
|
-
if (connectorsConfig.creditWallet === 'walletConnect') connectors.push('CREDIT_WALLET');
|
|
297
|
-
if (connectorsConfig.creditWallet === 'creditConnect') connectors.push('CREDIT_CONNECT');
|
|
298
|
-
if (connectorsConfig.metamask) connectors.push('METAMASK');
|
|
299
|
-
if (connectorsConfig.walletConnect) connectors.push('WALLET_CONNECT');
|
|
300
|
-
|
|
301
|
-
return connectors;
|
|
302
|
-
};
|
|
303
|
-
|
|
304
|
-
export const ConnectModal: FC<ConnectModalProps> = (props) => {
|
|
305
|
-
if (!props.open) return null;
|
|
306
|
-
|
|
307
|
-
const resolvedConnectors = resolveConnectorsFromConfig(props.connectors);
|
|
308
|
-
|
|
309
|
-
if (
|
|
310
|
-
resolvedConnectors.includes('CREDIT_WALLET') &&
|
|
311
|
-
resolvedConnectors.includes('CREDIT_CONNECT')
|
|
312
|
-
) {
|
|
313
|
-
throw new Error('CREDIT_WALLET and CREDIT_CONNECT cannot be used together');
|
|
314
|
-
}
|
|
315
|
-
if (resolvedConnectors.length === 0) {
|
|
316
|
-
throw new Error('At least one connector must be configured');
|
|
317
|
-
}
|
|
318
|
-
return <ConnectModalInner {...props} connectors={resolvedConnectors} />;
|
|
319
|
-
};
|
|
320
|
-
|
|
321
|
-
type ConnectModalInnerProps = Omit<ConnectModalProps, 'connectors'> & {
|
|
322
|
-
connectors: ConnectorId[];
|
|
323
|
-
};
|
|
324
|
-
|
|
325
|
-
const ConnectModalInner: FC<ConnectModalInnerProps> = ({
|
|
326
|
-
open,
|
|
327
|
-
connectors,
|
|
328
|
-
onConnect,
|
|
329
|
-
onClose,
|
|
330
|
-
onLog,
|
|
331
|
-
wcProjectId,
|
|
332
|
-
requiredChainId,
|
|
333
|
-
renderConnectError,
|
|
334
|
-
}) => {
|
|
335
|
-
const [selectedConnector, setSelectedConnector] = useState<ConnectorId | null>(null);
|
|
336
|
-
const [qrUri, setQrUri] = useState<string | null>(null);
|
|
337
|
-
const [recentConnector, setRecentConnector] = useState<ConnectorId | null>(null);
|
|
338
|
-
const [hasMetaMaskExtension, setHasMetaMaskExtension] = useState<boolean>(false);
|
|
339
|
-
const [errorState, setErrorState] = useState<{ connectorId: ConnectorId; error: Error } | null>(
|
|
340
|
-
null,
|
|
341
|
-
);
|
|
342
|
-
|
|
343
|
-
const wc = useWCState();
|
|
344
|
-
|
|
345
|
-
const account = useAccount();
|
|
346
|
-
const { disconnectAsync } = useDisconnect();
|
|
347
|
-
const wagmiConfig = useConfig();
|
|
348
|
-
|
|
349
|
-
const chainMismatch =
|
|
350
|
-
requiredChainId != null &&
|
|
351
|
-
account.status === 'connected' &&
|
|
352
|
-
account.chainId != null &&
|
|
353
|
-
account.chainId !== requiredChainId;
|
|
354
|
-
|
|
355
|
-
const requiredChainName = useMemo<string | undefined>(() => {
|
|
356
|
-
if (requiredChainId == null) return undefined;
|
|
357
|
-
return wagmiConfig.chains.find((chain) => chain.id === requiredChainId)?.name;
|
|
358
|
-
}, [wagmiConfig.chains, requiredChainId]);
|
|
359
|
-
|
|
360
|
-
const handleQrUri = useCallback((uri: string | null) => {
|
|
361
|
-
setQrUri(uri);
|
|
362
|
-
}, []);
|
|
363
|
-
|
|
364
|
-
const handleConnect = useCallback(
|
|
365
|
-
(result: Parameters<typeof onConnect>[0]) => {
|
|
366
|
-
setErrorState(null);
|
|
367
|
-
onConnect(result);
|
|
368
|
-
onClose();
|
|
369
|
-
},
|
|
370
|
-
[onConnect, onClose],
|
|
371
|
-
);
|
|
372
|
-
|
|
373
|
-
const handleError = useCallback(
|
|
374
|
-
(error: unknown, connectorId: ConnectorId): void => {
|
|
375
|
-
const normalizedError = error instanceof Error ? error : new Error(String(error));
|
|
376
|
-
onLog?.(
|
|
377
|
-
`[connect-kit] connection failed (${connectorId}): ${normalizedError.message}`,
|
|
378
|
-
normalizedError,
|
|
379
|
-
);
|
|
380
|
-
setQrUri(null);
|
|
381
|
-
wc.resetView();
|
|
382
|
-
if (renderConnectError) {
|
|
383
|
-
setErrorState({ connectorId, error: normalizedError });
|
|
384
|
-
} else {
|
|
385
|
-
setSelectedConnector(null);
|
|
386
|
-
}
|
|
387
|
-
},
|
|
388
|
-
[onLog, wc.resetView, renderConnectError],
|
|
389
|
-
);
|
|
390
|
-
|
|
391
|
-
const { triggerConnect, cancelConnect } = useWagmiConnect({
|
|
392
|
-
onConnect: handleConnect,
|
|
393
|
-
onError: handleError,
|
|
394
|
-
onQrUri: handleQrUri,
|
|
395
|
-
});
|
|
396
|
-
|
|
397
|
-
const connectorLogoMap = useMemo(() => {
|
|
398
|
-
const map: Partial<Record<ConnectorId, string>> = {};
|
|
399
|
-
const connectorIds: ConnectorId[] = [
|
|
400
|
-
'CREDIT_CONNECT',
|
|
401
|
-
'CREDIT_WALLET',
|
|
402
|
-
'METAMASK',
|
|
403
|
-
'WALLET_CONNECT',
|
|
404
|
-
];
|
|
405
|
-
for (const connectorId of connectorIds) {
|
|
406
|
-
const wagmiConnector = resolveWagmiConnector(wagmiConfig.connectors, connectorId);
|
|
407
|
-
if (wagmiConnector?.icon) map[connectorId] = wagmiConnector.icon;
|
|
408
|
-
}
|
|
409
|
-
return map;
|
|
410
|
-
}, [wagmiConfig.connectors]);
|
|
411
|
-
|
|
412
|
-
useEffect(() => {
|
|
413
|
-
setRecentConnector(readRecentConnector());
|
|
414
|
-
setHasMetaMaskExtension(detectMetaMaskExtension());
|
|
415
|
-
}, []);
|
|
416
|
-
|
|
417
|
-
useEffect(() => {
|
|
418
|
-
if (!open) {
|
|
419
|
-
setSelectedConnector(null);
|
|
420
|
-
setQrUri(null);
|
|
421
|
-
setErrorState(null);
|
|
422
|
-
wc.reset();
|
|
423
|
-
cancelConnect();
|
|
424
|
-
}
|
|
425
|
-
}, [open, cancelConnect, wc.reset]);
|
|
426
|
-
|
|
427
|
-
const handleSelectConnector = useCallback(
|
|
428
|
-
(id: ConnectorId): void => {
|
|
429
|
-
if (id === selectedConnector && !errorState) return;
|
|
430
|
-
setSelectedConnector(id);
|
|
431
|
-
setQrUri(null);
|
|
432
|
-
setErrorState(null);
|
|
433
|
-
wc.resetView();
|
|
434
|
-
writeRecentConnector(id, onLog);
|
|
435
|
-
setRecentConnector(id);
|
|
436
|
-
|
|
437
|
-
if (id === 'WALLET_CONNECT' && wcProjectId) {
|
|
438
|
-
wc.loadWalletList(wcProjectId);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
cancelConnect();
|
|
442
|
-
triggerConnect(id);
|
|
443
|
-
},
|
|
444
|
-
[
|
|
445
|
-
selectedConnector,
|
|
446
|
-
errorState,
|
|
447
|
-
triggerConnect,
|
|
448
|
-
cancelConnect,
|
|
449
|
-
wcProjectId,
|
|
450
|
-
wc.resetView,
|
|
451
|
-
wc.loadWalletList,
|
|
452
|
-
onLog,
|
|
453
|
-
],
|
|
454
|
-
);
|
|
455
|
-
|
|
456
|
-
const handleBack = useCallback((): void => {
|
|
457
|
-
if (errorState) {
|
|
458
|
-
setErrorState(null);
|
|
459
|
-
setSelectedConnector(null);
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
if (selectedConnector === 'WALLET_CONNECT' && wc.handleBack()) return;
|
|
463
|
-
setSelectedConnector(null);
|
|
464
|
-
setQrUri(null);
|
|
465
|
-
cancelConnect();
|
|
466
|
-
}, [selectedConnector, errorState, wc.handleBack, cancelConnect]);
|
|
467
|
-
|
|
468
|
-
const handleSwitchChainDisconnect = useCallback((): void => {
|
|
469
|
-
disconnectAsync().catch((err: unknown) => {
|
|
470
|
-
const normalized = err instanceof Error ? err : new Error(String(err));
|
|
471
|
-
onLog?.(`[connect-kit] disconnect failed: ${normalized.message}`, normalized);
|
|
472
|
-
});
|
|
473
|
-
}, [disconnectAsync, onLog]);
|
|
474
|
-
|
|
475
|
-
const handleOverlayClick = useCallback((): void => {
|
|
476
|
-
if (chainMismatch) return;
|
|
477
|
-
onClose();
|
|
478
|
-
}, [chainMismatch, onClose]);
|
|
479
|
-
|
|
480
|
-
const handleCloseClick = useCallback((): void => {
|
|
481
|
-
if (chainMismatch) {
|
|
482
|
-
handleSwitchChainDisconnect();
|
|
483
|
-
return;
|
|
484
|
-
}
|
|
485
|
-
onClose();
|
|
486
|
-
}, [chainMismatch, handleSwitchChainDisconnect, onClose]);
|
|
487
|
-
|
|
488
|
-
const renderedError: ReactNode = useMemo(() => {
|
|
489
|
-
if (!errorState || !renderConnectError) return null;
|
|
490
|
-
const ctx: ConnectErrorContext = {
|
|
491
|
-
connectorId: errorState.connectorId,
|
|
492
|
-
reason: classifyError(errorState.error),
|
|
493
|
-
error: errorState.error,
|
|
494
|
-
retry: () => {
|
|
495
|
-
const id = errorState.connectorId;
|
|
496
|
-
setErrorState(null);
|
|
497
|
-
setQrUri(null);
|
|
498
|
-
cancelConnect();
|
|
499
|
-
triggerConnect(id);
|
|
500
|
-
},
|
|
501
|
-
dismiss: () => {
|
|
502
|
-
setErrorState(null);
|
|
503
|
-
setSelectedConnector(null);
|
|
504
|
-
},
|
|
505
|
-
};
|
|
506
|
-
return renderConnectError(ctx);
|
|
507
|
-
}, [errorState, renderConnectError, triggerConnect, cancelConnect]);
|
|
508
|
-
|
|
509
|
-
if (chainMismatch && requiredChainId != null) {
|
|
510
|
-
return (
|
|
511
|
-
<div className="ck-root">
|
|
512
|
-
<div className="ck-overlay" onClick={handleOverlayClick} />
|
|
513
|
-
<div
|
|
514
|
-
className="ck-modal ck-modal--switch-chain"
|
|
515
|
-
role="dialog"
|
|
516
|
-
aria-modal="true"
|
|
517
|
-
aria-label="Switch network"
|
|
518
|
-
>
|
|
519
|
-
<div className="ck-pane">
|
|
520
|
-
<div className="ck-pane__header">
|
|
521
|
-
<h3 className="ck-pane__title">Switch network</h3>
|
|
522
|
-
<button
|
|
523
|
-
type="button"
|
|
524
|
-
className="ck-btn-close"
|
|
525
|
-
onClick={handleCloseClick}
|
|
526
|
-
aria-label="Cancel and disconnect"
|
|
527
|
-
/>
|
|
528
|
-
</div>
|
|
529
|
-
<div className="ck-pane__body">
|
|
530
|
-
<SwitchChainView
|
|
531
|
-
currentChainId={account.chainId}
|
|
532
|
-
requiredChainId={requiredChainId}
|
|
533
|
-
requiredChainName={requiredChainName}
|
|
534
|
-
onDisconnect={handleSwitchChainDisconnect}
|
|
535
|
-
onLog={onLog}
|
|
536
|
-
/>
|
|
537
|
-
</div>
|
|
538
|
-
</div>
|
|
539
|
-
</div>
|
|
540
|
-
</div>
|
|
541
|
-
);
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
return (
|
|
545
|
-
<div className="ck-root">
|
|
546
|
-
<div className="ck-overlay" onClick={handleOverlayClick} />
|
|
547
|
-
<div
|
|
548
|
-
className={`ck-modal${(selectedConnector ?? errorState) ? ' ck-modal--has-detail' : ''}`}
|
|
549
|
-
role="dialog"
|
|
550
|
-
aria-modal="true"
|
|
551
|
-
aria-label="Connect Wallet"
|
|
552
|
-
>
|
|
553
|
-
<SelectorPane
|
|
554
|
-
connectors={connectors}
|
|
555
|
-
selected={selectedConnector}
|
|
556
|
-
recentConnector={recentConnector}
|
|
557
|
-
onSelect={handleSelectConnector}
|
|
558
|
-
onClose={handleCloseClick}
|
|
559
|
-
/>
|
|
560
|
-
<DetailPane
|
|
561
|
-
selectedConnector={selectedConnector}
|
|
562
|
-
qrUri={qrUri}
|
|
563
|
-
hasMetaMaskExtension={hasMetaMaskExtension}
|
|
564
|
-
connectorLogoMap={connectorLogoMap}
|
|
565
|
-
wc={wc}
|
|
566
|
-
errorOverride={renderedError}
|
|
567
|
-
onBack={handleBack}
|
|
568
|
-
onClose={handleCloseClick}
|
|
569
|
-
/>
|
|
570
|
-
</div>
|
|
571
|
-
</div>
|
|
572
|
-
);
|
|
573
|
-
};
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { type FC } from 'react';
|
|
2
|
-
import { QRCodeSVG } from 'qrcode.react';
|
|
3
|
-
|
|
4
|
-
interface QRFrameProps {
|
|
5
|
-
qrUri: string | null;
|
|
6
|
-
logoUrl?: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export const QRFrame: FC<QRFrameProps> = ({ qrUri, logoUrl }) => (
|
|
10
|
-
<div className={`ck-qr-frame ${!qrUri ? 'ck-qr-frame--loading' : ''}`}>
|
|
11
|
-
{qrUri ? (
|
|
12
|
-
<QRCodeSVG
|
|
13
|
-
value={qrUri}
|
|
14
|
-
size={196}
|
|
15
|
-
level="H"
|
|
16
|
-
imageSettings={
|
|
17
|
-
logoUrl ? { src: logoUrl, width: 50, height: 50, excavate: true } : undefined
|
|
18
|
-
}
|
|
19
|
-
/>
|
|
20
|
-
) : (
|
|
21
|
-
<div className="ck-qr-spinner" aria-label="Loading QR code" />
|
|
22
|
-
)}
|
|
23
|
-
</div>
|
|
24
|
-
);
|
|
25
|
-
|
|
26
|
-
interface CopyLinkButtonProps {
|
|
27
|
-
qrUri: string | null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export const CopyLinkButton: FC<CopyLinkButtonProps> = ({ qrUri }) => {
|
|
31
|
-
if (!qrUri) return null;
|
|
32
|
-
return (
|
|
33
|
-
<button
|
|
34
|
-
type="button"
|
|
35
|
-
className="ck-copy-link"
|
|
36
|
-
onClick={() => {
|
|
37
|
-
navigator.clipboard?.writeText(qrUri).catch(() => {});
|
|
38
|
-
}}
|
|
39
|
-
>
|
|
40
|
-
Copy link
|
|
41
|
-
</button>
|
|
42
|
-
);
|
|
43
|
-
};
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { type FC, useEffect, useMemo, useRef } from 'react';
|
|
2
|
-
import { isMobileDevice, tryOpenDeepLink } from '../utils/platform';
|
|
3
|
-
import { CONNECTOR_META } from '../connector-meta';
|
|
4
|
-
import { type ConnectorId } from '../types';
|
|
5
|
-
import { QRFrame } from '../components/QRFrame';
|
|
6
|
-
|
|
7
|
-
interface CreditWalletViewProps {
|
|
8
|
-
connectorId: Extract<ConnectorId, 'CREDIT_CONNECT' | 'CREDIT_WALLET'>;
|
|
9
|
-
qrUri: string | null;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export const CreditWalletView: FC<CreditWalletViewProps> = ({ connectorId, qrUri }) => {
|
|
13
|
-
const { downloadUrl, logoUrl, deepLinkBase } = CONNECTOR_META[connectorId];
|
|
14
|
-
const isMobile = useMemo(() => isMobileDevice(), []);
|
|
15
|
-
const hasRedirectedRef = useRef(false);
|
|
16
|
-
|
|
17
|
-
useEffect(() => {
|
|
18
|
-
if (isMobile && qrUri && deepLinkBase && !hasRedirectedRef.current) {
|
|
19
|
-
hasRedirectedRef.current = true;
|
|
20
|
-
tryOpenDeepLink(qrUri, deepLinkBase);
|
|
21
|
-
}
|
|
22
|
-
}, [isMobile, qrUri, deepLinkBase]);
|
|
23
|
-
|
|
24
|
-
if (isMobile && deepLinkBase) {
|
|
25
|
-
return (
|
|
26
|
-
<div className="ck-view ck-view--mobile-redirect">
|
|
27
|
-
<p>Opening Credit Wallet...</p>
|
|
28
|
-
</div>
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return (
|
|
33
|
-
<div className="ck-view ck-view--qr">
|
|
34
|
-
<QRFrame qrUri={qrUri} logoUrl={logoUrl} />
|
|
35
|
-
|
|
36
|
-
<p className="ck-view__caption">Open the Credit Wallet app and scan the QR</p>
|
|
37
|
-
|
|
38
|
-
{downloadUrl && (
|
|
39
|
-
<div className="ck-download-card">
|
|
40
|
-
<div className="ck-download-card__text">
|
|
41
|
-
<p className="ck-download-card__title">Get Credit Wallet app</p>
|
|
42
|
-
<p className="ck-download-card__sub">
|
|
43
|
-
Once connected, you can log in with it next time
|
|
44
|
-
</p>
|
|
45
|
-
</div>
|
|
46
|
-
<a
|
|
47
|
-
href={downloadUrl}
|
|
48
|
-
target="_blank"
|
|
49
|
-
rel="nofollow noopener noreferrer"
|
|
50
|
-
className="ck-btn-primary"
|
|
51
|
-
>
|
|
52
|
-
Download
|
|
53
|
-
</a>
|
|
54
|
-
</div>
|
|
55
|
-
)}
|
|
56
|
-
</div>
|
|
57
|
-
);
|
|
58
|
-
};
|
package/src/views/IdleView.tsx
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { type FC } from 'react';
|
|
2
|
-
import graphic from '../../assets/graphic.png';
|
|
3
|
-
|
|
4
|
-
export const IdleView: FC = () => (
|
|
5
|
-
<div className="ck-view ck-view--idle">
|
|
6
|
-
<img className="ck-view__illustration" src={graphic} alt="" aria-hidden="true" />
|
|
7
|
-
<h4 className="ck-view__title">Connect anytime, anywhere</h4>
|
|
8
|
-
<p className="ck-view__description">Choose a wallet on the left to start connecting.</p>
|
|
9
|
-
</div>
|
|
10
|
-
);
|