@gluwa/connect-kit 0.1.0-next.0

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.
@@ -0,0 +1,10 @@
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
+ );
@@ -0,0 +1,89 @@
1
+ import { type FC, useEffect } from 'react';
2
+ import { QRCodeSVG } from 'qrcode.react';
3
+ import { isMobileDevice, tryOpenDeepLink } from '../utils/platform';
4
+ import { CONNECTOR_META } from '../connector-meta';
5
+
6
+ const { downloadUrl, deepLinkBase } = CONNECTOR_META.METAMASK;
7
+
8
+ interface MetaMaskViewProps {
9
+ qrUri: string | null;
10
+ hasExtension: boolean;
11
+ logoUrl?: string;
12
+ }
13
+
14
+ export const MetaMaskView: FC<MetaMaskViewProps> = ({ qrUri, hasExtension, logoUrl }) => {
15
+ const isMobile = isMobileDevice();
16
+
17
+ useEffect(() => {
18
+ if (isMobile && qrUri && deepLinkBase) {
19
+ tryOpenDeepLink(qrUri, deepLinkBase);
20
+ }
21
+ }, [isMobile, qrUri]);
22
+
23
+ if (isMobile && deepLinkBase) {
24
+ return (
25
+ <div className="ck-view ck-view--mobile-redirect">
26
+ <p>Opening MetaMask...</p>
27
+ </div>
28
+ );
29
+ }
30
+
31
+ if (hasExtension) {
32
+ return (
33
+ <div className="ck-view ck-view--extension">
34
+ <span className="ck-view__wallet-icon ck-view__wallet-icon--metamask" aria-hidden="true" />
35
+ <h4 className="ck-view__title">Open MetaMask</h4>
36
+ <p className="ck-view__description">Check the connection in your extension</p>
37
+ <span className="ck-spinner" aria-label="Connecting" />
38
+ </div>
39
+ );
40
+ }
41
+
42
+ return (
43
+ <div className="ck-view ck-view--qr">
44
+ <div className={`ck-qr-frame ${!qrUri ? 'ck-qr-frame--loading' : ''}`}>
45
+ {qrUri ? (
46
+ <QRCodeSVG
47
+ value={qrUri}
48
+ size={196}
49
+ level="H"
50
+ imageSettings={
51
+ logoUrl ? { src: logoUrl, width: 40, height: 40, excavate: true } : undefined
52
+ }
53
+ />
54
+ ) : (
55
+ <div className="ck-qr-spinner" aria-label="Loading QR code" />
56
+ )}
57
+ </div>
58
+
59
+ {qrUri && (
60
+ <button
61
+ type="button"
62
+ className="ck-copy-link"
63
+ onClick={() => {
64
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
65
+ navigator.clipboard.writeText(qrUri);
66
+ }}
67
+ >
68
+ Copy link
69
+ </button>
70
+ )}
71
+
72
+ {downloadUrl && (
73
+ <div className="ck-download-card">
74
+ <div className="ck-download-card__text">
75
+ <p className="ck-download-card__title">Don&apos;t have MetaMask Wallet?</p>
76
+ </div>
77
+ <a
78
+ href={downloadUrl}
79
+ target="_blank"
80
+ rel="nofollow noopener noreferrer"
81
+ className="ck-btn-primary"
82
+ >
83
+ Download
84
+ </a>
85
+ </div>
86
+ )}
87
+ </div>
88
+ );
89
+ };
@@ -0,0 +1,266 @@
1
+ import { type FC, useMemo } from 'react';
2
+ import { QRCodeSVG } from 'qrcode.react';
3
+ import { isMobileDevice, tryOpenDeepLink } from '../utils/platform';
4
+ import { type WCWallet, type WCSubView } from '../types';
5
+ import deeplink from '../../assets/deeplink.png';
6
+
7
+ interface WalletConnectViewProps {
8
+ subView: WCSubView;
9
+ qrUri: string | null;
10
+ logoUrl?: string;
11
+ walletList: WCWallet[];
12
+ walletListLoading: boolean;
13
+ walletListSearch: string;
14
+ walletListFilterActive: boolean;
15
+ selectedWallet: WCWallet | null;
16
+ onShowList: () => void;
17
+ onSelectWallet: (wallet: WCWallet) => void;
18
+ onSearchChange: (q: string) => void;
19
+ onFilterToggle: () => void;
20
+ }
21
+
22
+ export const WalletConnectView: FC<WalletConnectViewProps> = (props) => {
23
+ const { subView, selectedWallet } = props;
24
+
25
+ if (subView === 'list') return <WalletListView {...props} />;
26
+ if (subView === 'wallet' && selectedWallet)
27
+ return <WalletQRView wallet={selectedWallet} qrUri={props.qrUri} />;
28
+ return <WCQRView {...props} />;
29
+ };
30
+
31
+ const WCQRView: FC<WalletConnectViewProps> = ({ qrUri, logoUrl, walletList, onShowList }) => (
32
+ <div className="ck-view ck-view--qr">
33
+ <div className={`ck-qr-frame ${!qrUri ? 'ck-qr-frame--loading' : ''}`}>
34
+ {qrUri ? (
35
+ <QRCodeSVG
36
+ value={qrUri}
37
+ size={196}
38
+ level="H"
39
+ imageSettings={
40
+ logoUrl
41
+ ? {
42
+ src: logoUrl,
43
+ width: 40,
44
+ height: 40,
45
+ excavate: true,
46
+ }
47
+ : undefined
48
+ }
49
+ />
50
+ ) : (
51
+ <div className="ck-qr-spinner" aria-label="Loading QR code" />
52
+ )}
53
+ </div>
54
+
55
+ <p className="ck-view__caption">Scan this QR Code with your phone</p>
56
+
57
+ {qrUri && (
58
+ <button
59
+ type="button"
60
+ className="ck-copy-link"
61
+ onClick={() => {
62
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
63
+ navigator.clipboard.writeText(qrUri);
64
+ }}
65
+ >
66
+ Copy link
67
+ </button>
68
+ )}
69
+
70
+ <button type="button" className="ck-all-wallets-btn" onClick={onShowList}>
71
+ <span className="ck-all-wallets-btn__icon" aria-hidden="true" />
72
+ <span className="ck-all-wallets-btn__label">All Wallets</span>
73
+ {walletList.length > 0 && (
74
+ <span className="ck-all-wallets-btn__count">{walletList.length}+</span>
75
+ )}
76
+ </button>
77
+ </div>
78
+ );
79
+
80
+ const CREDIT_WALLET_NAME = 'Credit Wallet';
81
+
82
+ const WalletListView: FC<WalletConnectViewProps> = ({
83
+ walletList,
84
+ walletListLoading,
85
+ walletListSearch,
86
+ walletListFilterActive,
87
+ logoUrl,
88
+ qrUri,
89
+ onSelectWallet,
90
+ onSearchChange,
91
+ onFilterToggle,
92
+ }) => {
93
+ const isMobile = isMobileDevice();
94
+
95
+ const filtered = useMemo(() => {
96
+ const q = walletListSearch.trim().toLowerCase();
97
+ let list = walletList;
98
+ if (walletListFilterActive) {
99
+ list = list.filter(
100
+ (w) => w.mobileNative || w.mobileUniversal || w.desktopNative || w.desktopUniversal,
101
+ );
102
+ }
103
+ if (q) {
104
+ list = list.filter((w) => w.name.toLowerCase().includes(q));
105
+ }
106
+ return [...list].sort((a, b) => {
107
+ if (a.name === CREDIT_WALLET_NAME) return -1;
108
+ if (b.name === CREDIT_WALLET_NAME) return 1;
109
+ return 0;
110
+ });
111
+ }, [walletList, walletListSearch, walletListFilterActive]);
112
+
113
+ const handleClickWallet = (wallet: WCWallet): void => {
114
+ const uri = qrUri;
115
+ if (isMobile && uri) {
116
+ const target = wallet.mobileNative || wallet.mobileUniversal;
117
+ if (target) {
118
+ tryOpenDeepLink(uri, target);
119
+ return;
120
+ }
121
+ }
122
+ onSelectWallet(wallet);
123
+ };
124
+
125
+ return (
126
+ <div className="ck-view ck-view--wallet-list">
127
+ <div className="ck-wallet-search">
128
+ <input
129
+ type="text"
130
+ className="ck-wallet-search__input"
131
+ placeholder="Search wallet"
132
+ value={walletListSearch}
133
+ onChange={(e) => {
134
+ onSearchChange(e.target.value);
135
+ }}
136
+ />
137
+ <button
138
+ type="button"
139
+ className={`ck-wc-filter${walletListFilterActive ? ' is-active' : ''}`}
140
+ onClick={onFilterToggle}
141
+ aria-pressed={walletListFilterActive}
142
+ aria-label="Show WalletConnect wallets only"
143
+ title="WalletConnect wallets only"
144
+ >
145
+ {logoUrl && (
146
+ <img src={logoUrl} alt="" className="ck-wc-filter__icon" aria-hidden="true" />
147
+ )}
148
+ <span className="ck-wc-filter__track">
149
+ <span className="ck-wc-filter__thumb" />
150
+ </span>
151
+ </button>
152
+ </div>
153
+
154
+ {walletListLoading && walletList.length === 0 && (
155
+ <div className="ck-wallet-list-loading">
156
+ <span className="ck-spinner" aria-label="Loading wallets" />
157
+ </div>
158
+ )}
159
+
160
+ {!walletListLoading && filtered.length === 0 && walletList.length > 0 && (
161
+ <p className="ck-wallet-list-empty">No wallets found.</p>
162
+ )}
163
+
164
+ <div className="ck-wallet-grid-scroll">
165
+ <ul className="ck-wallet-grid">
166
+ {filtered.map((wallet) => {
167
+ const hasDeeplink = !!(
168
+ wallet.mobileNative ||
169
+ wallet.mobileUniversal ||
170
+ wallet.desktopNative ||
171
+ wallet.desktopUniversal
172
+ );
173
+ return (
174
+ <li key={wallet.id}>
175
+ <button
176
+ type="button"
177
+ className="ck-wallet-grid__item"
178
+ onClick={() => {
179
+ handleClickWallet(wallet);
180
+ }}
181
+ >
182
+ {wallet.imageUrl ? (
183
+ <img className="ck-wallet-grid__icon" src={wallet.imageUrl} alt="" />
184
+ ) : (
185
+ <span className="ck-wallet-grid__icon-placeholder" aria-hidden="true" />
186
+ )}
187
+ <span className="ck-wallet-grid__name">
188
+ <span className="ck-wallet-grid__name-text">{wallet.name}</span>
189
+ {hasDeeplink && (
190
+ <img
191
+ src={deeplink}
192
+ alt=""
193
+ className="ck-wallet-grid__wc-badge"
194
+ aria-hidden="true"
195
+ />
196
+ )}
197
+ </span>
198
+ </button>
199
+ </li>
200
+ );
201
+ })}
202
+ </ul>
203
+ </div>
204
+ </div>
205
+ );
206
+ };
207
+
208
+ const WalletQRView: FC<{ wallet: WCWallet; qrUri: string | null }> = ({ wallet, qrUri }) => {
209
+ const downloadUrl = wallet.appIos || wallet.appAndroid || wallet.homepage;
210
+ const logoSrc = wallet.imageUrl;
211
+
212
+ return (
213
+ <div className="ck-view ck-view--qr">
214
+ <div className={`ck-qr-frame ${!qrUri ? 'ck-qr-frame--loading' : ''}`}>
215
+ {qrUri ? (
216
+ <QRCodeSVG
217
+ value={qrUri}
218
+ size={196}
219
+ level="H"
220
+ {...(logoSrc && {
221
+ imageSettings: {
222
+ src: logoSrc,
223
+ width: 40,
224
+ height: 40,
225
+ excavate: true,
226
+ },
227
+ })}
228
+ />
229
+ ) : (
230
+ <div className="ck-qr-spinner" aria-label="Loading QR code" />
231
+ )}
232
+ </div>
233
+
234
+ <p className="ck-view__caption">Scan this QR Code with your phone</p>
235
+
236
+ {qrUri && (
237
+ <button
238
+ type="button"
239
+ className="ck-copy-link"
240
+ onClick={() => {
241
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
242
+ navigator.clipboard.writeText(qrUri);
243
+ }}
244
+ >
245
+ Copy link
246
+ </button>
247
+ )}
248
+
249
+ {downloadUrl && (
250
+ <div className="ck-download-card">
251
+ <div className="ck-download-card__text">
252
+ <p className="ck-download-card__title">Don&apos;t have {wallet.name}?</p>
253
+ </div>
254
+ <a
255
+ href={downloadUrl}
256
+ target="_blank"
257
+ rel="nofollow noopener noreferrer"
258
+ className="ck-btn-primary"
259
+ >
260
+ Download
261
+ </a>
262
+ </div>
263
+ )}
264
+ </div>
265
+ );
266
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "noEmit": true,
5
+ "baseUrl": "./"
6
+ },
7
+ "include": ["src/**/*"]
8
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,18 @@
1
+ import { defineConfig } from 'tsup';
2
+ import { sassPlugin } from 'esbuild-sass-plugin';
3
+
4
+ export default defineConfig({
5
+ entry: { index: 'src/index.ts' },
6
+ format: ['esm'],
7
+ dts: true,
8
+ clean: true,
9
+ target: 'es2019',
10
+ external: ['react', 'react-native'],
11
+ banner: {
12
+ js: 'import React from "react";',
13
+ },
14
+ esbuildPlugins: [sassPlugin({ type: 'style' })],
15
+ esbuildOptions(options) {
16
+ options.loader = { ...options.loader, '.png': 'dataurl' };
17
+ },
18
+ });