Skip to content

Commit ae37a0a

Browse files
committed
fix remove wallet
1 parent eb13968 commit ae37a0a

6 files changed

Lines changed: 120 additions & 24 deletions

File tree

gateway-web/app/components/DashboardPage.tsx

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"use client";
22

33
import React, { useEffect, useState } from "react";
4+
import toast, { Toaster } from "react-hot-toast";
45
import {
56
Wallet as WalletIcon,
67
PlusCircle,
@@ -27,7 +28,7 @@ type WalletUI = Wallet & {
2728

2829
export const DashboardPage: React.FC<DashboardPageProps> = ({ username }) => {
2930
const { account, isConnected } = useStarknetWallet();
30-
const { getUserWallets, addWallet } = useStarknetContract();
31+
const { getUserWallets, addWallet, removeWallet } = useStarknetContract();
3132

3233
const [wallets, setWallets] = useState<WalletUI[]>([]);
3334
const [isLoadingWallets, setIsLoadingWallets] = useState<boolean>(true);
@@ -84,6 +85,7 @@ export const DashboardPage: React.FC<DashboardPageProps> = ({ username }) => {
8485
} catch (err) {
8586
console.error("Failed to fetch wallets:", err);
8687
if (mounted) setError("Failed to fetch wallets. Please try again.");
88+
toast.error("Failed to fetch wallets.");
8789
} finally {
8890
if (mounted) setIsLoadingWallets(false);
8991
}
@@ -99,32 +101,33 @@ export const DashboardPage: React.FC<DashboardPageProps> = ({ username }) => {
99101
const copyToClipboard = (text: string) => {
100102
navigator.clipboard.writeText(text);
101103
setCopied(text);
104+
toast.success("Copied");
102105
setTimeout(() => setCopied(null), 1500);
103106
};
104107

105108
// Add wallet (UI + contract)
106109
const handleAddWallet = async () => {
107110
const { chainId, address, memo, tag, metadata } = newWalletForm;
108111
if (!chainId || !address) {
109-
alert("Please select chain and enter wallet address.");
112+
toast.error("Please select chain and enter wallet address.");
110113
return;
111114
}
112115
if (!isConnected || !account) {
113-
alert("Please connect your wallet to add linked wallets.");
116+
toast.error("Please connect your wallet to add linked wallets.");
114117
return;
115118
}
116119

117120
const selected = chainOptions.find((c) => c.id === chainId);
118121
if (!selected) {
119-
alert("Selected chain not found.");
122+
toast.error("Selected chain not found.");
123+
120124
return;
121125
}
122126

123127
setIsAddingWallet(true);
124128
setError(null);
125129

126130
try {
127-
// Call contract to add wallet
128131
await addWallet(
129132
account,
130133
selected.symbol,
@@ -134,7 +137,6 @@ export const DashboardPage: React.FC<DashboardPageProps> = ({ username }) => {
134137
metadata || undefined
135138
);
136139

137-
// Update local state after successful transaction
138140
const newEntry: WalletUI = {
139141
chainSymbol: selected.symbol,
140142
address,
@@ -153,10 +155,11 @@ export const DashboardPage: React.FC<DashboardPageProps> = ({ username }) => {
153155
tag: "",
154156
metadata: "",
155157
});
156-
alert("Wallet added successfully!");
158+
toast.success("Wallet added successfully!");
157159
} catch (err) {
158160
console.error("Add wallet failed:", err);
159161
setError("Failed to add wallet. See console for details.");
162+
toast.error("Failed to add wallet.");
160163
} finally {
161164
setIsAddingWallet(false);
162165
}
@@ -165,12 +168,13 @@ export const DashboardPage: React.FC<DashboardPageProps> = ({ username }) => {
165168
// Remove wallet (UI + contract)
166169
const handleRemoveWallet = async (addressToRemove: string) => {
167170
if (!isConnected || !account) {
168-
alert("Please connect your wallet to remove linked wallets.");
171+
toast.error("Please connect your wallet to remove linked wallets.");
169172
return;
170173
}
171174

172175
const found = wallets.find((w) => w.address === addressToRemove);
173176
if (!found) return;
177+
174178
const selectedChain = chainOptions.find(
175179
(c) => c.symbol.toLowerCase() === found.chainSymbol.toLowerCase()
176180
);
@@ -185,18 +189,17 @@ export const DashboardPage: React.FC<DashboardPageProps> = ({ username }) => {
185189
setError(null);
186190

187191
try {
188-
// TODO: Replace with actual contract remove call
189-
// const tx = await removeWalletFromUser({ owner: account, chainSymbol: found.chainSymbol, address: addressToRemove });
190-
// await waitForTx(tx);
192+
await removeWallet(account, found.chainSymbol);
191193

192-
// Update local state
193194
setWallets((prev) => prev.filter((w) => w.address !== addressToRemove));
194-
alert(
195-
"Wallet removed (UI-only). Replace placeholder with on-chain call."
196-
);
195+
196+
toast.success(`Removed from ${found.chainName}`);
197197
} catch (err) {
198198
console.error("Remove wallet failed:", err);
199-
setError("Failed to remove wallet. See console for details.");
199+
const errorMsg =
200+
err instanceof Error ? err.message : "Failed to remove wallet";
201+
setError(errorMsg);
202+
toast.error(`Failed to remove wallet: ${errorMsg}`);
200203
} finally {
201204
setRemovingAddress(null);
202205
}

gateway-web/app/components/HomePage.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
import { FeatureCard } from "./FeatureCard";
1515
import { useStarknetContract } from "../hooks/useStarknetContract";
1616
import { useStarknetWallet } from "../hooks/useStarknetWallet";
17+
import toast, { Toaster } from "react-hot-toast";
1718

1819
interface SearchResult {
1920
username: string;
@@ -35,7 +36,7 @@ export const HomePage: React.FC = () => {
3536

3637
const handleSearch = async () => {
3738
if (!searchQuery.trim()) {
38-
alert("Please enter a username");
39+
toast.error("Please enter a username");
3940
return;
4041
}
4142

@@ -48,7 +49,6 @@ export const HomePage: React.FC = () => {
4849
available: true,
4950
});
5051
} else {
51-
5252
setSearchResult({
5353
username: searchQuery,
5454
available: false,
@@ -60,37 +60,37 @@ export const HomePage: React.FC = () => {
6060
}
6161
} catch (err) {
6262
console.error("Error checking username:", err);
63-
alert("Failed to check username. Please try again.");
63+
toast.error("Failed to check username. Please try again.");
6464
}
6565
};
6666

6767
const handleRegister = async () => {
6868
if (!isConnected) {
69-
alert("Please connect your wallet first");
69+
toast.error("Please connect your wallet first");
7070
handleConnect();
7171
return;
7272
}
7373

7474
if (!account) {
75-
alert("No account found. Please connect your wallet.");
75+
toast.error("No account found. Please connect your wallet.");
7676
return;
7777
}
7878

7979
if (!searchQuery.trim()) {
80-
alert("Please enter a username");
80+
toast.error("Please enter a username");
8181
return;
8282
}
8383

8484
setIsRegistering(true);
8585
try {
8686
await registerUsername(searchQuery, account);
87-
alert(`Username @${searchQuery} registered successfully! 🎉`);
87+
toast.success(`Username @${searchQuery} registered successfully! 🎉`);
8888
setSearchResult(null);
8989
setSearchQuery("");
9090
window.location.reload();
9191
} catch (err) {
9292
console.error("Error registering username:", err);
93-
alert(
93+
toast.error(
9494
"Failed to register username OR Already Have yo register. Please try again."
9595
);
9696
} finally {

gateway-web/app/hooks/useStarknetContract.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
CairoOption,
1010
} from "starknet";
1111
import { ABI } from "./abi";
12+
import { isValidUsername, getUsernameError } from "../utils/validation";
1213

1314
const CONTRACT_ADDRESS = process.env.NEXT_PUBLIC_CONTRACT_ADDRESS;
1415
const RPC_URL = process.env.NEXT_PUBLIC_RPC_URL;
@@ -126,6 +127,10 @@ export const useStarknetContract = () => {
126127
setError(null);
127128

128129
try {
130+
const validationError = getUsernameError(username);
131+
if (validationError) {
132+
throw new Error(validationError);
133+
}
129134
const contract = getContract();
130135
const exists = await contract.check_username_exist(username);
131136

@@ -201,6 +206,11 @@ export const useStarknetContract = () => {
201206
setError(null);
202207

203208
try {
209+
const validationError = getUsernameError(username);
210+
if (validationError) {
211+
throw new Error(validationError);
212+
}
213+
204214
const contract = new Contract(ABI, CONTRACT_ADDRESS, account);
205215
const result = await contract.register_username(username);
206216
await account.waitForTransaction(result.transaction_hash);
@@ -254,6 +264,29 @@ export const useStarknetContract = () => {
254264
[]
255265
);
256266

267+
const removeWallet = useCallback(
268+
async (account: AccountInterface, chainSymbol: string) => {
269+
setLoading(true);
270+
setError(null);
271+
272+
try {
273+
const contract = new Contract(ABI, CONTRACT_ADDRESS, account);
274+
const result = await contract.remove_wallet(chainSymbol);
275+
276+
await account.waitForTransaction(result.transaction_hash);
277+
return result;
278+
} catch (err) {
279+
const errorMessage =
280+
err instanceof Error ? err.message : "Failed to remove wallet";
281+
setError(errorMessage);
282+
throw new Error(errorMessage);
283+
} finally {
284+
setLoading(false);
285+
}
286+
},
287+
[]
288+
);
289+
257290
return {
258291
loading,
259292
error,
@@ -264,5 +297,6 @@ export const useStarknetContract = () => {
264297
registerUsername,
265298
addWallet,
266299
getUsername,
300+
removeWallet,
267301
};
268302
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Username validation utility
3+
* ----------------------------
4+
* Rules:
5+
* - 4–20 characters
6+
* - Only letters (a–z, A–Z), numbers (0–9), and underscores (_)
7+
* - No spaces or special symbols
8+
*/
9+
10+
export function isValidUsername(username: string): boolean {
11+
const usernameRegex = /^[a-zA-Z0-9_]{4,20}$/;
12+
return usernameRegex.test(username);
13+
}
14+
15+
/**
16+
* Returns an error message if invalid, or null if valid.
17+
* Useful for displaying specific validation feedback.
18+
*/
19+
export function getUsernameError(username: string): string | null {
20+
if (!username || username.trim().length === 0)
21+
return "Username cannot be empty.";
22+
23+
if (username.length < 4)
24+
return "Username must be at least 4 characters long.";
25+
26+
if (username.length > 20) return "Username cannot exceed 20 characters.";
27+
28+
if (!/^[a-zA-Z0-9_]+$/.test(username))
29+
return "Only letters, numbers, and underscores are allowed.";
30+
31+
return null;
32+
}

gateway-web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"next": "15.5.4",
1717
"react": "19.1.0",
1818
"react-dom": "19.1.0",
19+
"react-hot-toast": "^2.6.0",
1920
"starknet": "^7.6.4"
2021
},
2122
"devDependencies": {

gateway-web/pnpm-lock.yaml

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)