Skip to content

Commit 4e06cc1

Browse files
committed
fix typo and refactor UI
1 parent 7282c92 commit 4e06cc1

File tree

19 files changed

+1006
-468
lines changed

19 files changed

+1006
-468
lines changed

frontend/middleware.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { NextRequest, NextResponse } from 'next/server';
2+
3+
const allowedUserRoutes = new Set(['alice', 'bob', 'connected-wallet']);
4+
5+
export function middleware(request: NextRequest) {
6+
const { pathname } = request.nextUrl;
7+
8+
if (
9+
pathname.startsWith('/_next') ||
10+
pathname.startsWith('/api') ||
11+
pathname.startsWith('/assets') ||
12+
pathname === '/favicon.ico'
13+
) {
14+
return NextResponse.next();
15+
}
16+
17+
if (pathname === '/') {
18+
return NextResponse.redirect(new URL('/connected-wallet', request.url));
19+
}
20+
21+
if (pathname === '/mint-authority') {
22+
return NextResponse.next();
23+
}
24+
25+
const segments = pathname.split('/').filter(Boolean);
26+
if (segments.length > 0 && allowedUserRoutes.has(segments[0])) {
27+
return NextResponse.next();
28+
}
29+
30+
return NextResponse.redirect(new URL('/connected-wallet', request.url));
31+
}
32+
33+
export const config = {
34+
matcher: ['/((?!_next/static|_next/image|favicon.ico|api).*)'],
35+
};

frontend/next.config.js

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -59,15 +59,15 @@ module.exports = (phase, {defaultConfig}) => {
5959
},
6060
];
6161
},
62-
async redirects() {
63-
return [
64-
{
65-
source: '/',
66-
destination: '/mint-authority',
67-
permanent: true, // Use true for a 301 redirect, false for 302
68-
},
69-
];
70-
},
62+
async redirects() {
63+
return [
64+
{
65+
source: '/',
66+
destination: '/connected-wallet',
67+
permanent: true, // Use true for a 301 redirect, false for 302
68+
},
69+
];
70+
},
7171
experimental: {
7272
esmExternals: true, // Ensure modern module support
7373
},
@@ -87,4 +87,4 @@ module.exports = (phase, {defaultConfig}) => {
8787
"@lucid-evolution/lucid"
8888
]
8989
}
90-
}
90+
}

frontend/package-lock.json

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

frontend/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"react": "^18",
2424
"react-dom": "^18",
2525
"regenerator-runtime": "^0.14.1",
26+
"use-sync-external-store": "^1.6.0",
2627
"zustand": "^5.0.2"
2728
},
2829
"devDependencies": {
@@ -42,4 +43,4 @@
4243
"tsconfig-paths-webpack-plugin": "^4.2.0",
4344
"typescript": "^5"
4445
}
45-
}
46+
}

frontend/src/app/[username]/index.tsx

Lines changed: 77 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ export default function Profile() {
4444
const [programmableBalanceError, setProgrammableBalanceError] = useState<string | null>(null);
4545
const [programmableBalanceRefreshKey, setProgrammableBalanceRefreshKey] = useState(0);
4646
const [selectedTokenHex, setSelectedTokenHex] = useState('');
47+
const [isSendingTokens, setIsSendingTokens] = useState(false);
48+
const [isUserMinting, setIsUserMinting] = useState(false);
49+
const [isUserFreezing, setIsUserFreezing] = useState(false);
50+
const [isUserUnfreezing, setIsUserUnfreezing] = useState(false);
51+
const [isUserSeizing, setIsUserSeizing] = useState(false);
4752

4853
const demoEnv = useContext(DemoEnvironmentContext);
4954

@@ -276,6 +281,9 @@ export default function Profile() {
276281
}, [accounts.walletUser.regular_address, currentUser, programmableBalanceRefreshKey]);
277282

278283
const onSend = async () => {
284+
if (isSendingTokens) {
285+
return;
286+
}
279287
if (getUserAccountDetails()?.status === 'Frozen' && !overrideTx) {
280288
changeAlertInfo({
281289
severity: 'error',
@@ -346,6 +354,7 @@ export default function Profile() {
346354
});
347355
return;
348356
}
357+
setIsSendingTokens(true);
349358
const requestData = {
350359
asset_name: selectedTokenHex,
351360
issuer: issuerForPolicy,
@@ -370,9 +379,17 @@ export default function Profile() {
370379
await updateAccountBalance(sendRecipientAddress);
371380
await updateAccountBalance(accountInfo.regular_address);
372381
setProgrammableBalanceRefreshKey((key) => key + 1);
373-
changeAlertInfo({severity: 'success', message: 'Transaction sent successfully!', open: true, link: `${demoEnv.explorer_url}/${txId}`});
382+
changeAlertInfo({
383+
severity: 'success',
384+
message: 'Transaction sent successfully!',
385+
open: true,
386+
link: `${demoEnv.explorer_url}/${txId}`,
387+
actionText: 'View on Explorer'
388+
});
374389
} catch (error) {
375390
console.error('Send failed:', error);
391+
} finally {
392+
setIsSendingTokens(false);
376393
}
377394
};
378395

@@ -422,7 +439,8 @@ export default function Profile() {
422439
severity: 'success',
423440
message: msg,
424441
open: true,
425-
link: txId ? `${demoEnv.explorer_url}/${txId}` : ''
442+
link: txId ? `${demoEnv.explorer_url}/${txId}` : '',
443+
actionText: txId ? 'View on Explorer' : undefined
426444
});
427445
};
428446

@@ -494,7 +512,8 @@ export default function Profile() {
494512
severity: 'success',
495513
message: 'Blacklist initialised successfully!',
496514
open: true,
497-
link: `${demoEnv.explorer_url}/${txId}`
515+
link: `${demoEnv.explorer_url}/${txId}`,
516+
actionText: 'View on Explorer'
498517
});
499518
} catch (error) {
500519
console.error('Blacklist init failed:', error);
@@ -550,6 +569,9 @@ export default function Profile() {
550569
};
551570

552571
const onUserMint = async () => {
572+
if (isUserMinting) {
573+
return;
574+
}
553575
const issuerAddress = ensureWalletReady();
554576
if (!issuerAddress) return;
555577
if (!userAddressCleared) {
@@ -570,6 +592,7 @@ export default function Profile() {
570592
quantity: userMintAmount,
571593
recipient
572594
};
595+
setIsUserMinting(true);
573596
try {
574597
const response = await axios.post(
575598
'/api/v1/tx/programmable-token/issue',
@@ -584,7 +607,8 @@ export default function Profile() {
584607
severity: 'success',
585608
message: 'Mint successful!',
586609
open: true,
587-
link: `${demoEnv.explorer_url}/${txId}`
610+
link: `${demoEnv.explorer_url}/${txId}`,
611+
actionText: 'View on Explorer'
588612
});
589613
await updateAccountBalance(recipient);
590614
await updateAccountBalance(issuerAddress);
@@ -597,10 +621,15 @@ export default function Profile() {
597621
open: true,
598622
link: ''
599623
});
624+
} finally {
625+
setIsUserMinting(false);
600626
}
601627
};
602628

603629
const onUserFreeze = async () => {
630+
if (isUserFreezing) {
631+
return;
632+
}
604633
const issuerAddress = ensureWalletReady();
605634
if (!issuerAddress || !userFreezeAddress) return;
606635
changeAlertInfo({severity: 'info', message: 'Processing freeze request…', open: true, link: ''});
@@ -609,6 +638,7 @@ export default function Profile() {
609638
blacklist_address: userFreezeAddress,
610639
reason: userFreezeReason
611640
};
641+
setIsUserFreezing(true);
612642
try {
613643
const response = await axios.post(
614644
'/api/v1/tx/programmable-token/blacklist',
@@ -621,7 +651,8 @@ export default function Profile() {
621651
severity: 'success',
622652
message: 'Address frozen.',
623653
open: true,
624-
link: `${demoEnv.explorer_url}/${txId}`
654+
link: `${demoEnv.explorer_url}/${txId}`,
655+
actionText: 'View on Explorer'
625656
});
626657
const frozenWalletKey = (Object.keys(accounts) as (keyof Accounts)[]).find(
627658
(key) => accounts[key].regular_address === userFreezeAddress
@@ -643,10 +674,15 @@ export default function Profile() {
643674
} else {
644675
console.error('Connected wallet freeze failed:', error);
645676
}
677+
} finally {
678+
setIsUserFreezing(false);
646679
}
647680
};
648681

649682
const onUserUnfreeze = async () => {
683+
if (isUserUnfreezing) {
684+
return;
685+
}
650686
const issuerAddress = ensureWalletReady();
651687
if (!issuerAddress || !userUnfreezeAddress) return;
652688
changeAlertInfo({severity: 'info', message: 'Processing unfreeze request…', open: true, link: ''});
@@ -655,6 +691,7 @@ export default function Profile() {
655691
blacklist_address: userUnfreezeAddress,
656692
reason: '(unfreeze)'
657693
};
694+
setIsUserUnfreezing(true);
658695
try {
659696
const response = await axios.post(
660697
'/api/v1/tx/programmable-token/unblacklist',
@@ -667,7 +704,8 @@ export default function Profile() {
667704
severity: 'success',
668705
message: 'Address unfrozen.',
669706
open: true,
670-
link: `${demoEnv.explorer_url}/${txId}`
707+
link: `${demoEnv.explorer_url}/${txId}`,
708+
actionText: 'View on Explorer'
671709
});
672710
const unfrozenWalletKey = (Object.keys(accounts) as (keyof Accounts)[]).find(
673711
(key) => accounts[key].regular_address === userUnfreezeAddress
@@ -689,10 +727,15 @@ export default function Profile() {
689727
} else {
690728
console.error('Connected wallet unfreeze failed:', error);
691729
}
730+
} finally {
731+
setIsUserUnfreezing(false);
692732
}
693733
};
694734

695735
const onUserSeize = async () => {
736+
if (isUserSeizing) {
737+
return;
738+
}
696739
const issuerAddress = ensureWalletReady();
697740
if (!issuerAddress || !userSeizeAddress) return;
698741
changeAlertInfo({severity: 'info', message: 'Processing seizure request…', open: true, link: ''});
@@ -701,6 +744,7 @@ export default function Profile() {
701744
target: userSeizeAddress,
702745
reason: userSeizeReason
703746
};
747+
setIsUserSeizing(true);
704748
try {
705749
const response = await axios.post(
706750
'/api/v1/tx/programmable-token/seize',
@@ -714,7 +758,8 @@ export default function Profile() {
714758
severity: 'success',
715759
message: 'Funds seized.',
716760
open: true,
717-
link: `${demoEnv.explorer_url}/${txId}`
761+
link: `${demoEnv.explorer_url}/${txId}`,
762+
actionText: 'View on Explorer'
718763
});
719764
} catch (error) {
720765
console.error('Connected wallet seize failed:', error);
@@ -724,6 +769,8 @@ export default function Profile() {
724769
open: true,
725770
link: ''
726771
});
772+
} finally {
773+
setIsUserSeizing(false);
727774
}
728775
};
729776

@@ -956,9 +1003,10 @@ export default function Profile() {
9561003
{
9571004
label: 'Send',
9581005
content: sendContent,
959-
buttonLabel: 'Send',
1006+
buttonLabel: isSendingTokens ? 'Sending…' : 'Send',
9601007
onAction: onSend,
961-
buttonDisabled: !sendAmountValid
1008+
buttonDisabled: !sendAmountValid || isSendingTokens,
1009+
buttonLoading: isSendingTokens
9621010
},
9631011
{
9641012
label: 'Receive',
@@ -1011,26 +1059,34 @@ export default function Profile() {
10111059
{
10121060
label: 'Mint',
10131061
content: userMintContent,
1014-
buttonLabel: 'Mint',
1015-
onAction: onUserMint
1062+
buttonLabel: isUserMinting ? 'Minting…' : 'Mint',
1063+
onAction: onUserMint,
1064+
buttonDisabled: isUserMinting,
1065+
buttonLoading: isUserMinting
10161066
},
10171067
{
10181068
label: 'Freeze',
10191069
content: userFreezeContent,
1020-
buttonLabel: 'Freeze',
1021-
onAction: onUserFreeze
1070+
buttonLabel: isUserFreezing ? 'Freezing…' : 'Freeze',
1071+
onAction: onUserFreeze,
1072+
buttonDisabled: isUserFreezing,
1073+
buttonLoading: isUserFreezing
10221074
},
10231075
{
10241076
label: 'Unfreeze',
10251077
content: userUnfreezeContent,
1026-
buttonLabel: 'Unfreeze',
1027-
onAction: onUserUnfreeze
1078+
buttonLabel: isUserUnfreezing ? 'Unfreezing…' : 'Unfreeze',
1079+
onAction: onUserUnfreeze,
1080+
buttonDisabled: isUserUnfreezing,
1081+
buttonLoading: isUserUnfreezing
10281082
},
10291083
{
10301084
label: 'Seize',
10311085
content: userSeizeContent,
1032-
buttonLabel: 'Seize',
1033-
onAction: onUserSeize
1086+
buttonLabel: isUserSeizing ? 'Seizing…' : 'Seize',
1087+
onAction: onUserSeize,
1088+
buttonDisabled: isUserSeizing,
1089+
buttonLoading: isUserSeizing
10341090
}
10351091
]}/>
10361092
</div>
@@ -1113,14 +1169,16 @@ export default function Profile() {
11131169
content: registerContent,
11141170
buttonLabel: isRegistering ? 'Registering…' : 'Register Asset',
11151171
onAction: onRegisterAsset,
1116-
buttonDisabled: isRegistering
1172+
buttonDisabled: isRegistering,
1173+
buttonLoading: isRegistering
11171174
},
11181175
{
11191176
label: 'Blacklist Init',
11201177
content: blacklistInitContent,
11211178
buttonLabel: isInitializingBlacklist ? 'Initializing…' : 'Initialize Blacklist',
11221179
onAction: onBlacklistInit,
1123-
buttonDisabled: isInitializingBlacklist
1180+
buttonDisabled: isInitializingBlacklist,
1181+
buttonLoading: isInitializingBlacklist
11241182
}
11251183
]}/>
11261184
</div>

0 commit comments

Comments
 (0)