Skip to content

Commit 54500ff

Browse files
committed
feat: create demo wallet connector with custom components
1 parent 1a2bca9 commit 54500ff

File tree

12 files changed

+1014
-39
lines changed

12 files changed

+1014
-39
lines changed

examples/cosmos-kit-nextjs-app-router-example/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
},
1111
"dependencies": {
1212
"@cosmos-kit/keplr": "^2.15.0",
13+
"@cosmos-kit/keplr-mobile": "^2.15.0",
1314
"@cosmos-kit/react": "^2.22.0",
1415
"@interchain-ui/react": "latest",
1516
"@radix-ui/react-slot": "^1.1.2",
@@ -20,6 +21,7 @@
2021
"next": "15.2.3",
2122
"react": "^19.0.0",
2223
"react-dom": "^19.0.0",
24+
"react-qr-code": "^2.0.15",
2325
"tailwind-merge": "^3.0.2",
2426
"tw-animate-css": "^1.2.4"
2527
},
@@ -36,6 +38,7 @@
3638
},
3739
"resolutions": {
3840
"@cosmos-kit/keplr": "file:../../wallets/keplr",
41+
"@cosmos-kit/keplr-mobile": "file:../../wallets/keplr-mobile",
3942
"@cosmos-kit/react": "file:../../packages/react"
4043
}
4144
}

examples/cosmos-kit-nextjs-app-router-example/src/components/cosmos-kit-provider.tsx

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,45 @@
11
"use client";
22

3-
import "@interchain-ui/react/styles";
4-
import { ChainProvider } from "@cosmos-kit/react";
3+
import { ChainProvider } from "@cosmos-kit/react-lite";
54
import { chains, assets } from "chain-registry";
6-
import { wallets } from "@cosmos-kit/keplr";
5+
import { wallets as keplrWallets } from "@cosmos-kit/keplr";
6+
import { wallets as keplrMobileWallets } from "@cosmos-kit/keplr-mobile";
7+
import { CustomWalletModal } from "./custom-wallet-connect/CustomWalletModal";
8+
9+
const combinedWallets = [...keplrWallets, ...keplrMobileWallets];
10+
const uniqueWalletsMap = new Map();
11+
combinedWallets.forEach((wallet) => {
12+
if (!uniqueWalletsMap.has(wallet.walletInfo.name)) {
13+
uniqueWalletsMap.set(wallet.walletInfo.name, wallet);
14+
}
15+
});
16+
const uniqueWallets = Array.from(uniqueWalletsMap.values());
717

818
export function CosmosKitProvider({ children }: { children: React.ReactNode }) {
919
return (
1020
<ChainProvider
1121
chains={chains}
1222
assetLists={assets}
13-
wallets={wallets}
23+
wallets={uniqueWallets}
24+
walletModal={CustomWalletModal}
1425
logLevel={"DEBUG"}
15-
throwErrors={false}
26+
throwErrors="connect_only"
27+
subscribeConnectEvents={true}
28+
defaultNameService={"stargaze"}
29+
walletConnectOptions={{
30+
signClient: {
31+
projectId: "a8510432ebb71e6948cfd6cde54b70f7",
32+
relayUrl: "wss://relay.walletconnect.org",
33+
metadata: {
34+
name: "CosmosKit Example",
35+
description: "CosmosKit test dapp",
36+
url: "https://test.cosmoskit.com/",
37+
icons: [
38+
"https://raw.githubusercontent.com/hyperweb-io/cosmos-kit/main/packages/docs/public/favicon-96x96.png",
39+
],
40+
},
41+
},
42+
}}
1643
>
1744
{children}
1845
</ChainProvider>
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import React, { useState } from 'react';
2+
import { WalletViewProps } from '@cosmos-kit/core';
3+
4+
function truncateAddress(address: string = '', start = 6, end = 4) {
5+
if (!address) return '';
6+
return `${address.substring(0, start)}...${address.substring(
7+
address.length - end
8+
)}`;
9+
}
10+
11+
export const CustomConnectedView = ({
12+
onClose,
13+
wallet,
14+
}: WalletViewProps) => {
15+
const [copied, setCopied] = useState(false);
16+
const address = typeof wallet.address === 'string' ? wallet.address : undefined;
17+
const truncatedAddress = truncateAddress(address);
18+
19+
const handleCopy = () => {
20+
if (address) {
21+
navigator.clipboard.writeText(address).then(() => {
22+
setCopied(true);
23+
setTimeout(() => setCopied(false), 1500);
24+
});
25+
}
26+
};
27+
28+
return (
29+
<>
30+
{/* Head Section */}
31+
<div className="flex items-center justify-between p-4 border-b border-border">
32+
<h3 className="text-lg font-semibold text-foreground">Wallet Connected</h3>
33+
<button
34+
onClick={onClose}
35+
className="p-1 rounded-md text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
36+
>
37+
{/* Close Icon */}
38+
<svg
39+
xmlns="http://www.w3.org/2000/svg"
40+
fill="none"
41+
viewBox="0 0 24 24"
42+
strokeWidth={1.5}
43+
stroke="currentColor"
44+
className="size-5"
45+
>
46+
<path
47+
strokeLinecap="round"
48+
strokeLinejoin="round"
49+
d="M6 18 18 6M6 6l12 12"
50+
/>
51+
</svg>
52+
</button>
53+
</div>
54+
55+
{/* Content Section */}
56+
<div className="p-6 flex flex-col items-center text-center gap-5">
57+
{/* Success Icon */}
58+
<svg
59+
xmlns="http://www.w3.org/2000/svg"
60+
fill="none"
61+
viewBox="0 0 24 24"
62+
strokeWidth={1.5}
63+
stroke="currentColor"
64+
className="size-10 text-green-500"
65+
>
66+
<path
67+
strokeLinecap="round"
68+
strokeLinejoin="round"
69+
d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"
70+
/>
71+
</svg>
72+
73+
<p className="font-medium text-foreground">Connected with {wallet.walletInfo.prettyName}</p>
74+
75+
{address && (
76+
<div className="w-full flex items-center justify-center gap-2 p-2 border border-border rounded-md bg-background">
77+
<span className="font-mono text-sm text-muted-foreground">
78+
{truncatedAddress}
79+
</span>
80+
<button
81+
title="Copy address"
82+
onClick={handleCopy}
83+
className="p-1 rounded text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring"
84+
disabled={copied}
85+
>
86+
{copied ? (
87+
<svg
88+
xmlns="http://www.w3.org/2000/svg"
89+
fill="none"
90+
viewBox="0 0 24 24"
91+
strokeWidth={1.5}
92+
stroke="currentColor"
93+
className="size-4 text-green-500"
94+
>
95+
<path
96+
strokeLinecap="round"
97+
strokeLinejoin="round"
98+
d="m4.5 12.75 6 6 9-13.5"
99+
/>
100+
</svg>
101+
) : (
102+
<svg
103+
xmlns="http://www.w3.org/2000/svg"
104+
fill="none"
105+
viewBox="0 0 24 24"
106+
strokeWidth={1.5}
107+
stroke="currentColor"
108+
className="size-4"
109+
>
110+
<path
111+
strokeLinecap="round"
112+
strokeLinejoin="round"
113+
d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375v-3.375a1.125 1.125 0 0 0-1.125-1.125h-1.5a1.125 1.125 0 0 0-1.125 1.125v3.375m-7.5-10.375h3.375a1.125 1.125 0 0 1 1.125 1.125v1.5a1.125 1.125 0 0 1-1.125 1.125h-3.375a1.125 1.125 0 0 1-1.125-1.125v-1.5a1.125 1.125 0 0 1 1.125-1.125"
114+
/>
115+
</svg>
116+
)}
117+
</button>
118+
</div>
119+
)}
120+
121+
<button
122+
onClick={() => wallet.disconnect(false)} // Disconnect without clearing pairings for WC
123+
className="mt-2 w-full px-4 py-2 border border-border bg-background text-destructive rounded-md text-sm font-medium transition-colors hover:bg-destructive hover:text-destructive-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:pointer-events-none"
124+
>
125+
Disconnect
126+
</button>
127+
</div>
128+
</>
129+
);
130+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import React from 'react';
2+
import { WalletViewProps } from '@cosmos-kit/core';
3+
4+
const Spinner = () => (
5+
<svg
6+
className="animate-spin size-6 text-primary"
7+
xmlns="http://www.w3.org/2000/svg"
8+
fill="none"
9+
viewBox="0 0 24 24"
10+
>
11+
<circle
12+
className="opacity-25"
13+
cx="12"
14+
cy="12"
15+
r="10"
16+
stroke="currentColor"
17+
strokeWidth="4"
18+
></circle>
19+
<path
20+
className="opacity-75"
21+
fill="currentColor"
22+
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
23+
></path>
24+
</svg>
25+
);
26+
27+
export const CustomConnectingView = ({
28+
onClose,
29+
onReturn,
30+
wallet,
31+
}: WalletViewProps) => {
32+
return (
33+
<>
34+
{/* Head Section */}
35+
<div className="flex items-center p-4 border-b border-border">
36+
<button
37+
onClick={onReturn}
38+
className="mr-4 p-1 rounded-md text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
39+
>
40+
{/* Back Icon */}
41+
<svg
42+
xmlns="http://www.w3.org/2000/svg"
43+
fill="none"
44+
viewBox="0 0 24 24"
45+
strokeWidth={1.5}
46+
stroke="currentColor"
47+
className="size-5"
48+
>
49+
<path
50+
strokeLinecap="round"
51+
strokeLinejoin="round"
52+
d="M15.75 19.5 8.25 12l7.5-7.5"
53+
/>
54+
</svg>
55+
</button>
56+
<h3 className="flex-1 text-lg font-semibold text-foreground">
57+
Connecting...
58+
</h3>
59+
{/* Optional: Add Close button if needed during connection */}
60+
<button
61+
onClick={onClose}
62+
className="ml-4 p-1 rounded-md text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
63+
>
64+
{/* Close Icon */}
65+
<svg
66+
xmlns="http://www.w3.org/2000/svg"
67+
fill="none"
68+
viewBox="0 0 24 24"
69+
strokeWidth={1.5}
70+
stroke="currentColor"
71+
className="size-5"
72+
>
73+
<path
74+
strokeLinecap="round"
75+
strokeLinejoin="round"
76+
d="M6 18 18 6M6 6l12 12"
77+
/>
78+
</svg>
79+
</button>
80+
</div>
81+
82+
{/* Content Section */}
83+
<div className="p-6 flex flex-col items-center text-center gap-5">
84+
<Spinner />
85+
<p className="font-medium text-foreground">
86+
Connecting to {wallet.walletInfo.prettyName}...
87+
</p>
88+
<p className="text-sm text-muted-foreground">
89+
Approve the connection request in your wallet application.
90+
</p>
91+
</div>
92+
</>
93+
);
94+
};
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import React from 'react';
2+
import { WalletViewProps } from '@cosmos-kit/core';
3+
4+
export const CustomErrorView = ({
5+
onClose,
6+
onReturn,
7+
wallet,
8+
}: WalletViewProps) => {
9+
const errorMsg = wallet.message || 'An unknown error occurred.';
10+
11+
return (
12+
<>
13+
{/* Head Section */}
14+
<div className="flex items-center p-4 border-b border-border">
15+
<button
16+
onClick={onReturn}
17+
className="mr-4 p-1 rounded-md text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
18+
>
19+
{/* Back Icon */}
20+
<svg
21+
xmlns="http://www.w3.org/2000/svg"
22+
fill="none"
23+
viewBox="0 0 24 24"
24+
strokeWidth={1.5}
25+
stroke="currentColor"
26+
className="size-5"
27+
>
28+
<path
29+
strokeLinecap="round"
30+
strokeLinejoin="round"
31+
d="M15.75 19.5 8.25 12l7.5-7.5"
32+
/>
33+
</svg>
34+
</button>
35+
<h3 className="flex-1 text-lg font-semibold text-destructive">
36+
Connection Error
37+
</h3>
38+
<button
39+
onClick={onClose}
40+
className="ml-4 p-1 rounded-md text-muted-foreground hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
41+
>
42+
{/* Close Icon */}
43+
<svg
44+
xmlns="http://www.w3.org/2000/svg"
45+
fill="none"
46+
viewBox="0 0 24 24"
47+
strokeWidth={1.5}
48+
stroke="currentColor"
49+
className="size-5"
50+
>
51+
<path
52+
strokeLinecap="round"
53+
strokeLinejoin="round"
54+
d="M6 18 18 6M6 6l12 12"
55+
/>
56+
</svg>
57+
</button>
58+
</div>
59+
60+
{/* Content Section */}
61+
<div className="p-6 flex flex-col items-center text-center gap-5">
62+
{/* Error Icon */}
63+
<svg
64+
xmlns="http://www.w3.org/2000/svg"
65+
fill="none"
66+
viewBox="0 0 24 24"
67+
strokeWidth={1.5}
68+
stroke="currentColor"
69+
className="size-10 text-destructive"
70+
>
71+
<path
72+
strokeLinecap="round"
73+
strokeLinejoin="round"
74+
d="M12 9v3.75m9-.75a9 9 0 1 1-18 0 9 9 0 0 1 18 0Zm-9 3.75h.008v.008H12v-.008Z"
75+
/>
76+
</svg>
77+
<p className="font-medium text-foreground">
78+
Failed to connect to {wallet.walletInfo.prettyName}
79+
</p>
80+
<p className="w-full text-sm text-destructive bg-destructive/10 p-3 rounded break-words border border-destructive/20">
81+
{errorMsg}
82+
</p>
83+
<button
84+
onClick={onReturn}
85+
className="mt-2 w-full px-4 py-2 border border-border bg-primary text-primary-foreground rounded-md text-sm font-medium transition-colors hover:bg-primary/90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:pointer-events-none"
86+
>
87+
Try Again
88+
</button>
89+
</div>
90+
</>
91+
);
92+
};

0 commit comments

Comments
 (0)