@gluwa/connect-kit 0.1.0-next.2 → 0.1.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.
@@ -9,10 +9,16 @@ import {
9
9
  type FC,
10
10
  type ReactNode,
11
11
  } from 'react';
12
- import { useAccount, useDisconnect } from 'wagmi';
12
+ import { WagmiProvider, useAccount, useDisconnect } from 'wagmi';
13
+ import type { Config } from 'wagmi';
14
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
13
15
  import { ConnectModal } from './ConnectModal';
14
16
  import type { ConnectErrorContext, ConnectResult, ConnectorId, Connectors } from './types';
15
17
 
18
+ // wagmiConfig prop으로 기존 프로젝트 통합 시 내부에서 사용하는 QueryClient.
19
+ // 모듈 레벨에 한 번만 생성 — 연결 플로우용으로만 쓰이므로 공유해도 무방.
20
+ const internalQueryClient = new QueryClient();
21
+
16
22
  export interface ConnectKitProviderProps {
17
23
  children: ReactNode;
18
24
  connectors: Connectors;
@@ -33,6 +39,16 @@ export interface ConnectKitProviderProps {
33
39
  * 미제공 시 기본 동작은 selector로 복귀.
34
40
  */
35
41
  renderConnectError?: (ctx: ConnectErrorContext) => ReactNode;
42
+ /**
43
+ * 기존 프로젝트 통합용. wagmi config를 넘기면 ConnectKitProvider 내부에서
44
+ * WagmiProvider를 직접 세운다.
45
+ *
46
+ * - 기존 프로젝트: 앱에 WagmiProvider가 없으므로 wagmiConfig를 넘긴다.
47
+ * ConnectKitProvider가 같은 config 인스턴스로 WagmiProvider를 셀프 마운트하므로
48
+ * 기존 wagmi 상태 store와 단일 config를 공유한다.
49
+ * - 신규 프로젝트: 앱이 이미 WagmiProvider를 감싸므로 이 prop은 불필요하다.
50
+ */
51
+ wagmiConfig?: Config;
36
52
  }
37
53
 
38
54
  export interface ConnectKitContextValue {
@@ -57,7 +73,20 @@ const resolveConnectorId = (wagmiConnectorId: string | undefined): ConnectorId |
57
73
  return null;
58
74
  };
59
75
 
60
- export const ConnectKitProvider: FC<ConnectKitProviderProps> = ({
76
+ export const ConnectKitProvider: FC<ConnectKitProviderProps> = (props) => {
77
+ if (props.wagmiConfig) {
78
+ return (
79
+ <WagmiProvider config={props.wagmiConfig}>
80
+ <QueryClientProvider client={internalQueryClient}>
81
+ <ConnectKitProviderInner {...props} />
82
+ </QueryClientProvider>
83
+ </WagmiProvider>
84
+ );
85
+ }
86
+ return <ConnectKitProviderInner {...props} />;
87
+ };
88
+
89
+ const ConnectKitProviderInner: FC<ConnectKitProviderProps> = ({
61
90
  children,
62
91
  connectors,
63
92
  wcProjectId,
@@ -520,11 +520,11 @@
520
520
  }
521
521
  }
522
522
 
523
- // 스크롤 wrapper — 520px 고정, 세로 스크롤만 담당
523
+ // 스크롤 wrapper — 세로 스크롤만 담당
524
524
  .ck-wallet-grid-scroll {
525
525
  flex: 1;
526
526
  min-height: 0;
527
- width: 520px;
527
+ width: 100%;
528
528
  overflow-x: hidden;
529
529
  overflow-y: auto;
530
530
  }
@@ -1,4 +1,4 @@
1
- import { useState, useEffect, useCallback, useRef, useMemo, type FC, type ReactNode } from 'react';
1
+ import { useState, useEffect, useCallback, useMemo, type FC, type ReactNode } from 'react';
2
2
  import './ConnectModal.scss';
3
3
  import { useAccount, useConfig, useDisconnect } from 'wagmi';
4
4
  import {
@@ -303,8 +303,7 @@ const ConnectModalInner: FC<ConnectModalInnerProps> = ({
303
303
  renderConnectError,
304
304
  }) => {
305
305
  const [selectedConnector, setSelectedConnector] = useState<ConnectorId | null>(null);
306
- const [qrUriMap, setQrUriMap] = useState<Partial<Record<ConnectorId, string>>>({});
307
- const pendingQrConnectorRef = useRef<ConnectorId | null>(null);
306
+ const [qrUri, setQrUri] = useState<string | null>(null);
308
307
  const [recentConnector, setRecentConnector] = useState<ConnectorId | null>(null);
309
308
  const [hasMetaMaskExtension, setHasMetaMaskExtension] = useState<boolean>(false);
310
309
  const [errorState, setErrorState] = useState<{ connectorId: ConnectorId; error: Error } | null>(
@@ -329,12 +328,9 @@ const ConnectModalInner: FC<ConnectModalInnerProps> = ({
329
328
  return wagmiConfig.chains.find((chain) => chain.id === requiredChainId)?.name;
330
329
  }, [wagmiConfig.chains, requiredChainId]);
331
330
 
332
- // QR URI 수신 — 커넥터별 캐시에 저장 (null은 무시하여 캐시 유지)
331
+ // QR URI 수신
333
332
  const handleQrUri = useCallback((uri: string | null) => {
334
- if (uri) {
335
- const id = pendingQrConnectorRef.current;
336
- if (id) setQrUriMap((prev) => ({ ...prev, [id]: uri }));
337
- }
333
+ setQrUri(uri);
338
334
  }, []);
339
335
 
340
336
  // 연결 완료
@@ -349,7 +345,7 @@ const ConnectModalInner: FC<ConnectModalInnerProps> = ({
349
345
  [onConnect, onClose],
350
346
  );
351
347
 
352
- // 연결 실패 — 실패한 커넥터의 QR 캐시만 삭제
348
+ // 연결 실패
353
349
  const handleError = useCallback(
354
350
  (error: unknown, connectorId: ConnectorId): void => {
355
351
  const normalizedError = error instanceof Error ? error : new Error(String(error));
@@ -357,11 +353,7 @@ const ConnectModalInner: FC<ConnectModalInnerProps> = ({
357
353
  `[connect-kit] connection failed (${connectorId}): ${normalizedError.message}`,
358
354
  normalizedError,
359
355
  );
360
- setQrUriMap((prev) => {
361
- const next = { ...prev };
362
- delete next[connectorId];
363
- return next;
364
- });
356
+ setQrUri(null);
365
357
  wc.resetView();
366
358
  if (renderConnectError) {
367
359
  setErrorState({ connectorId, error: normalizedError });
@@ -401,24 +393,23 @@ const ConnectModalInner: FC<ConnectModalInnerProps> = ({
401
393
  setHasMetaMaskExtension(detectMetaMaskExtension());
402
394
  }, []);
403
395
 
404
- // 모달 닫힐 때 상태 + QR 캐시 전체 초기화
396
+ // 모달 닫힐 때 상태 초기화
405
397
  useEffect(() => {
406
398
  if (!open) {
407
399
  setSelectedConnector(null);
408
- setQrUriMap({});
409
- pendingQrConnectorRef.current = null;
400
+ setQrUri(null);
410
401
  setErrorState(null);
411
402
  wc.reset();
412
403
  cancelConnect();
413
404
  }
414
405
  }, [open, cancelConnect, wc.reset]);
415
406
 
416
- // 커넥터 선택
407
+ // 커넥터 선택 — 선택할 때마다 QR을 초기화해 새 세션 URI가 표시되도록 함
417
408
  const handleSelectConnector = useCallback(
418
409
  (id: ConnectorId): void => {
419
410
  if (id === selectedConnector && !errorState) return;
420
- pendingQrConnectorRef.current = id;
421
411
  setSelectedConnector(id);
412
+ setQrUri(null);
422
413
  setErrorState(null);
423
414
  wc.resetView();
424
415
  writeRecentConnector(id);
@@ -442,7 +433,7 @@ const ConnectModalInner: FC<ConnectModalInnerProps> = ({
442
433
  ],
443
434
  );
444
435
 
445
- // 뒤로가기 — QR 캐시는 유지, wagmi만 reset
436
+ // 뒤로가기
446
437
  const handleBack = useCallback((): void => {
447
438
  if (errorState) {
448
439
  setErrorState(null);
@@ -450,8 +441,8 @@ const ConnectModalInner: FC<ConnectModalInnerProps> = ({
450
441
  return;
451
442
  }
452
443
  if (selectedConnector === 'WALLET_CONNECT' && wc.handleBack()) return;
453
- pendingQrConnectorRef.current = null;
454
444
  setSelectedConnector(null);
445
+ setQrUri(null);
455
446
  cancelConnect();
456
447
  }, [selectedConnector, errorState, wc.handleBack, cancelConnect]);
457
448
 
@@ -487,7 +478,7 @@ const ConnectModalInner: FC<ConnectModalInnerProps> = ({
487
478
  retry: () => {
488
479
  const id = errorState.connectorId;
489
480
  setErrorState(null);
490
- pendingQrConnectorRef.current = id;
481
+ setQrUri(null);
491
482
  cancelConnect();
492
483
  triggerConnect(id);
493
484
  },
@@ -554,7 +545,7 @@ const ConnectModalInner: FC<ConnectModalInnerProps> = ({
554
545
  />
555
546
  <DetailPane
556
547
  selectedConnector={selectedConnector}
557
- qrUri={selectedConnector ? (qrUriMap[selectedConnector] ?? null) : null}
548
+ qrUri={qrUri}
558
549
  hasMetaMaskExtension={hasMetaMaskExtension}
559
550
  connectorLogoMap={connectorLogoMap}
560
551
  wc={wc}
@@ -0,0 +1,2 @@
1
+ export { creditConnectConnector } from './creditConnectConnector';
2
+ export type { CreditConnectConnectorOptions } from './creditConnectConnector';
@@ -285,6 +285,7 @@ export const creditConnectConnector = (
285
285
  } finally {
286
286
  isLocalDisconnect = false;
287
287
  }
288
+ provider = null;
288
289
  resetState();
289
290
  config.emitter.emit('disconnect');
290
291
  },
package/src/index.ts CHANGED
@@ -1,8 +1,6 @@
1
1
  export { ConnectModal } from './ConnectModal';
2
2
  export { ConnectKitProvider, useConnectKit } from './ConnectKitProvider';
3
3
  export type { ConnectKitContextValue, ConnectKitProviderProps } from './ConnectKitProvider';
4
- export { creditConnectConnector } from './creditConnectConnector';
5
- export type { CreditConnectConnectorOptions } from './creditConnectConnector';
6
4
  export type {
7
5
  ConnectErrorContext,
8
6
  ConnectErrorReason,
@@ -1,4 +1,4 @@
1
- import { type FC, useEffect, useMemo } from 'react';
1
+ import { type FC, useEffect, useMemo, useRef } from 'react';
2
2
  import { isMobileDevice, tryOpenDeepLink } from '../utils/platform';
3
3
  import { CONNECTOR_META } from '../connector-meta';
4
4
  import { type ConnectorId } from '../types';
@@ -12,9 +12,12 @@ interface CreditWalletViewProps {
12
12
  export const CreditWalletView: FC<CreditWalletViewProps> = ({ connectorId, qrUri }) => {
13
13
  const { downloadUrl, logoUrl, deepLinkBase } = CONNECTOR_META[connectorId];
14
14
  const isMobile = useMemo(() => isMobileDevice(), []);
15
+ // WC 세션 만료로 qrUri가 재발급되더라도 딥링크는 최초 1회만 실행
16
+ const hasRedirectedRef = useRef(false);
15
17
 
16
18
  useEffect(() => {
17
- if (isMobile && qrUri && deepLinkBase) {
19
+ if (isMobile && qrUri && deepLinkBase && !hasRedirectedRef.current) {
20
+ hasRedirectedRef.current = true;
18
21
  tryOpenDeepLink(qrUri, deepLinkBase);
19
22
  }
20
23
  }, [isMobile, qrUri, deepLinkBase]);
package/tsup.config.ts CHANGED
@@ -2,7 +2,10 @@ import { defineConfig } from 'tsup';
2
2
  import { sassPlugin } from 'esbuild-sass-plugin';
3
3
 
4
4
  export default defineConfig({
5
- entry: { index: 'src/index.ts' },
5
+ entry: {
6
+ index: 'src/index.ts',
7
+ 'credit-connect': 'src/credit-connect.ts',
8
+ },
6
9
  format: ['esm'],
7
10
  dts: true,
8
11
  clean: true,
@@ -13,6 +16,7 @@ export default defineConfig({
13
16
  'wagmi',
14
17
  '@wagmi/core',
15
18
  'viem',
19
+ '@tanstack/react-query',
16
20
  '@gluwa/credit-connect-sdk',
17
21
  '@gluwa/credit-connect-sdk/dapp',
18
22
  '@gluwa/credit-connect-sdk/storage',