chore: updated connect-wallet 9aa2740e
Steve · 2025-09-18 22:15 1 file(s) · +210 −27
components/connect-wallet.js +210 −27
4 4
		this.attachShadow({ mode: "open" });
5 5
		this.connected = false;
6 6
		this.address = "";
7 +
		this.ensData = null;
8 +
		this.loading = false;
7 9
	}
8 10
9 11
	connectedCallback() {
12 14
13 15
	render() {
14 16
		this.shadowRoot.innerHTML = `
15 -
  		<style>
16 -
        :host {
17 -
          --bg-color: ${this.connected ? "#232323" : "#5F8787"};
18 -
          --bg-hover-color: ${this.connected ? "#262626" : "#6F9797"};
19 -
        }
17 +
			<style>
18 +
				:host {
19 +
					--bg-color: ${this.connected ? "#232323" : "#5F8787"};
20 +
					--bg-hover-color: ${this.connected ? "#262626" : "#6F9797"};
21 +
					display: inline-block;
22 +
				}
23 +
24 +
				button {
25 +
					padding: 10px 20px;
26 +
					background: var(--bg-color);
27 +
					color: white;
28 +
					border: none;
29 +
					border-radius: 4px;
30 +
					cursor: pointer;
31 +
					font-size: 16px;
32 +
					transition: background-color 0.3s ease;
33 +
				}
34 +
35 +
				button:hover {
36 +
					background: var(--bg-hover-color);
37 +
				}
38 +
39 +
				button:disabled {
40 +
					opacity: 0.7;
41 +
					cursor: not-allowed;
42 +
				}
43 +
44 +
				.profile {
45 +
					display: flex;
46 +
					align-items: center;
47 +
					gap: 12px;
48 +
					padding: 12px;
49 +
					background: var(--bg-color);
50 +
					border-radius: 8px;
51 +
					color: white;
52 +
					min-width: 220px;
53 +
					transition: background-color 0.3s ease;
54 +
				}
55 +
56 +
				.profile:hover {
57 +
					background: var(--bg-hover-color);
58 +
				}
59 +
60 +
				.avatar {
61 +
					width: 40px;
62 +
					height: 40px;
63 +
					border-radius: 50%;
64 +
					object-fit: cover;
65 +
					border: 2px solid rgba(255, 255, 255, 0.2);
66 +
				}
67 +
68 +
				.avatar-placeholder {
69 +
					width: 40px;
70 +
					height: 40px;
71 +
					border-radius: 50%;
72 +
					background: linear-gradient(45deg, #667eea, #764ba2);
73 +
					display: flex;
74 +
					align-items: center;
75 +
					justify-content: center;
76 +
					color: white;
77 +
					font-weight: bold;
78 +
					font-size: 16px;
79 +
				}
80 +
81 +
				.profile-info {
82 +
					flex: 1;
83 +
					min-width: 0;
84 +
				}
85 +
86 +
				.profile-info h3 {
87 +
					margin: 0 0 4px 0;
88 +
					font-size: 14px;
89 +
					font-weight: 600;
90 +
					white-space: nowrap;
91 +
					overflow: hidden;
92 +
					text-overflow: ellipsis;
93 +
				}
94 +
95 +
				.profile-info p {
96 +
					margin: 0;
97 +
					font-size: 12px;
98 +
					opacity: 0.8;
99 +
					white-space: nowrap;
100 +
					overflow: hidden;
101 +
					text-overflow: ellipsis;
102 +
				}
103 +
104 +
				.loading {
105 +
					display: flex;
106 +
					align-items: center;
107 +
					gap: 8px;
108 +
				}
109 +
110 +
				.spinner {
111 +
					width: 16px;
112 +
					height: 16px;
113 +
					border: 2px solid rgba(255, 255, 255, 0.3);
114 +
					border-top: 2px solid white;
115 +
					border-radius: 50%;
116 +
					animation: spin 1s linear infinite;
117 +
				}
118 +
119 +
				@keyframes spin {
120 +
					from { transform: rotate(0deg); }
121 +
					to { transform: rotate(360deg); }
122 +
				}
123 +
			</style>
124 +
		`;
125 +
126 +
		if (this.loading) {
127 +
			this.renderLoading();
128 +
		} else if (this.connected) {
129 +
			this.renderProfile();
130 +
		} else {
131 +
			this.renderConnectButton();
132 +
		}
133 +
	}
20 134
21 -
        button {
22 -
          padding: 10px 20px;
23 -
          background: var(--bg-color);
24 -
          color: white;
25 -
          border: none;
26 -
          border-radius: 4px;
27 -
          cursor: pointer;
28 -
          font-size: 16px;
29 -
          transition: background-color 0.3s ease;
30 -
        }
31 -
        button:hover {
32 -
          background: var(--bg-hover-color);
33 -
        }
34 -
      </style>
35 -
    `;
135 +
	renderLoading() {
136 +
		const loadingDiv = document.createElement("div");
137 +
		loadingDiv.className = "profile loading";
138 +
		loadingDiv.innerHTML = `
139 +
			<div class="spinner"></div>
140 +
			<span>Connecting...</span>
141 +
		`;
142 +
		this.shadowRoot.appendChild(loadingDiv);
143 +
	}
36 144
145 +
	renderConnectButton() {
37 146
		const button = document.createElement("button");
38 -
		if (this.connected) {
39 -
			button.textContent = this.truncateAddress(this.address);
40 -
			button.addEventListener("click", () => this.disconnect());
147 +
		button.textContent = "Connect Wallet";
148 +
		button.addEventListener("click", () => this.connect());
149 +
		this.shadowRoot.appendChild(button);
150 +
	}
151 +
152 +
	renderProfile() {
153 +
		const profileDiv = document.createElement("div");
154 +
		profileDiv.className = "profile";
155 +
156 +
		const avatar = this.ensData?.avatar_small;
157 +
		const displayName = this.getDisplayName();
158 +
		const hasEns = this.ensData?.ens_primary;
159 +
		const addressToShow = this.truncateAddress(this.address);
160 +
161 +
		// Create avatar element
162 +
		let avatarElement = "";
163 +
		if (avatar) {
164 +
			avatarElement = `<img src="${avatar}" alt="Avatar" class="avatar" onerror="this.style.display='none'">`;
41 165
		} else {
42 -
			button.textContent = "Connect Wallet";
43 -
			button.addEventListener("click", () => this.connect());
166 +
			avatarElement = `<div class="avatar-placeholder"></div>`;
44 167
		}
45 168
46 -
		this.shadowRoot.appendChild(button);
169 +
		profileDiv.innerHTML = `
170 +
			${avatarElement}
171 +
			<div class="profile-info">
172 +
				<h3>${displayName}</h3>
173 +
				${hasEns ? `<p>${addressToShow}</p>` : ""}
174 +
			</div>
175 +
		`;
176 +
177 +
		profileDiv.addEventListener("click", () => {
178 +
			this.disconnect();
179 +
		});
180 +
181 +
		this.shadowRoot.appendChild(profileDiv);
47 182
	}
48 183
49 184
	async connect() {
50 185
		if (window.ethereum) {
51 186
			try {
187 +
				this.loading = true;
188 +
				this.render();
189 +
52 190
				const accounts = await window.ethereum.request({
53 191
					method: "eth_requestAccounts",
54 192
				});
193 +
55 194
				this.address = accounts[0];
56 195
				this.connected = true;
196 +
197 +
				// Fetch ENS data
198 +
				await this.fetchEnsData();
199 +
200 +
				this.loading = false;
57 201
				this.render();
202 +
203 +
				// Dispatch custom event
204 +
				this.dispatchEvent(
205 +
					new CustomEvent("wallet-connected", {
206 +
						detail: {
207 +
							address: this.address,
208 +
							ensData: this.ensData,
209 +
						},
210 +
					}),
211 +
				);
58 212
			} catch (error) {
59 213
				console.error("Connection failed", error);
214 +
				this.loading = false;
215 +
				this.render();
60 216
			}
61 217
		} else {
62 -
			alert("Please install a wallet extension");
218 +
			alert("Please install a wallet extension like MetaMask");
219 +
		}
220 +
	}
221 +
222 +
	async fetchEnsData() {
223 +
		try {
224 +
			const response = await fetch(`https://api.ensdata.net/${this.address}`);
225 +
			if (response.ok) {
226 +
				this.ensData = await response.json();
227 +
				console.log("ENS data loaded:", this.ensData);
228 +
			} else {
229 +
				console.log("No ENS data found for this address");
230 +
				this.ensData = null;
231 +
			}
232 +
		} catch (error) {
233 +
			console.error("Failed to fetch ENS data", error);
234 +
			this.ensData = null;
63 235
		}
64 236
	}
65 237
66 238
	disconnect() {
67 239
		this.connected = false;
68 240
		this.address = "";
241 +
		this.ensData = null;
69 242
		this.render();
243 +
244 +
		// Dispatch custom event
245 +
		this.dispatchEvent(new CustomEvent("wallet-disconnected"));
246 +
	}
247 +
248 +
	getDisplayName() {
249 +
		if (this.ensData?.ens_primary) {
250 +
			return this.ensData.ens_primary;
251 +
		}
252 +
		return this.truncateAddress(this.address);
70 253
	}
71 254
72 255
	truncateAddress(addr) {