Skip to content

Commit 0349635

Browse files
authored
Merge pull request #283 from MeshJS/preprod
Release: preprod → main (stuck-loading auth fixes)
2 parents 6db3dc0 + b248e7c commit 0349635

3 files changed

Lines changed: 60 additions & 4 deletions

File tree

src/components/common/cardano-objects/connect-wallet.tsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Wallet, Loader2, CheckCircle2, AlertCircle } from "lucide-react";
1+
import { Wallet, Loader2, CheckCircle2, AlertCircle, ShieldCheck } from "lucide-react";
22
import { Button } from "@/components/ui/button";
33
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
44
import {
@@ -98,6 +98,7 @@ function ConnectWalletContent({
9898
const { user, isLoading: isUserLoading } = useUser();
9999
const userAddress = useUserStore((state) => state.userAddress);
100100
const setUserAddress = useUserStore((state) => state.setUserAddress);
101+
const requestReauth = useUserStore((state) => state.requestReauth);
101102
const { toast } = useToast();
102103

103104
// Use WalletContext for regular wallet connection
@@ -654,6 +655,17 @@ function ConnectWalletContent({
654655
</>
655656
);
656657
}
658+
// Connected but the user query resolved with no user → no wallet session
659+
// was established (authorization failed/cancelled). Don't spin forever:
660+
// show an actionable "Authorize" label; the dropdown offers re-authorize.
661+
if (isConnected && !user && !isUserLoading && !isConnecting) {
662+
return (
663+
<>
664+
<ShieldCheck className="mr-2 h-4 w-4 transition-all duration-300" />
665+
<span className="font-medium transition-opacity duration-300">Authorize</span>
666+
</>
667+
);
668+
}
657669
if (isConnected && isLoading) {
658670
return (
659671
<>
@@ -745,6 +757,22 @@ function ConnectWalletContent({
745757

746758
{isConnected && (
747759
<>
760+
{!user && !isUserLoading && (
761+
<DropdownMenuItem
762+
onClick={() => requestReauth()}
763+
className={cn(
764+
"px-3 py-2.5 rounded-md",
765+
"text-zinc-900 dark:text-zinc-50",
766+
"hover:bg-zinc-100 dark:hover:bg-zinc-800",
767+
"focus:bg-zinc-100 dark:focus:bg-zinc-800",
768+
"transition-colors duration-150",
769+
"cursor-pointer"
770+
)}
771+
>
772+
<ShieldCheck className="mr-2.5 h-4 w-4" />
773+
<span className="font-medium">Authorize wallet</span>
774+
</DropdownMenuItem>
775+
)}
748776
<DropdownMenuItem
749777
onClick={handleDisconnect}
750778
className={cn(

src/components/common/overall-layout/layout.tsx

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { publicRoutes } from "@/data/public-routes";
66
import { api } from "@/utils/api";
77
import useUser from "@/hooks/useUser";
88
import { useUserStore } from "@/lib/zustand/user";
9+
import { normalizeAddressToBech32 } from "@/utils/addressCompatibility";
910
import useAppWallet from "@/hooks/useAppWallet";
1011
import useUTXOS from "@/hooks/useUTXOS";
1112
import useMeshWallet from "@/hooks/useMeshWallet";
@@ -102,7 +103,11 @@ export default function RootLayout({
102103
// 1.9 IWallet bridge — used for getDRep(), which the react 2.0 wallet lacks.
103104
const { wallet: meshWallet } = useMeshWallet();
104105
const { state: walletState, connectedWalletInstance } = useWalletContext();
105-
const address = useAddress();
106+
// react-2.0's useAddress can return hex-encoded address bytes; user records
107+
// and sessions are keyed by bech32. Normalize once at the source so every
108+
// consumer below (store sync, session check) uses the bech32 form.
109+
const rawAddress = useAddress();
110+
const address = rawAddress ? normalizeAddressToBech32(rawAddress) : rawAddress;
106111
const { user, isLoading: isLoadingUser } = useUser();
107112
const router = useRouter();
108113
const { appWallet } = useAppWallet();
@@ -111,6 +116,7 @@ export default function RootLayout({
111116

112117
const userAddress = useUserStore((state) => state.userAddress);
113118
const setUserAddress = useUserStore((state) => state.setUserAddress);
119+
const reauthNonce = useUserStore((state) => state.reauthNonce);
114120
const ctx = api.useUtils();
115121

116122
// State for wallet authorization modal
@@ -257,15 +263,15 @@ export default function RootLayout({
257263
activeWallet.getUsedAddresses()
258264
.then((addresses) => {
259265
if (addresses && addresses.length > 0) {
260-
setUserAddress(addresses[0]!);
266+
setUserAddress(normalizeAddressToBech32(addresses[0]!));
261267
fetchingAddressRef.current = false;
262268
} else {
263269
return activeWallet.getUnusedAddresses();
264270
}
265271
})
266272
.then((addresses) => {
267273
if (addresses && addresses.length > 0 && !userAddress) {
268-
setUserAddress(addresses[0]!);
274+
setUserAddress(normalizeAddressToBech32(addresses[0]!));
269275
}
270276
fetchingAddressRef.current = false;
271277
})
@@ -398,6 +404,21 @@ export default function RootLayout({
398404
// Don't refetch here - let the natural query refetch handle it if needed
399405
}, []);
400406

407+
// Manual re-authorization: when the user is connected but never got a
408+
// session (e.g. the auto-authorize failed/was cancelled), hasCheckedSession
409+
// stays true and the modal never reopens — leaving the connect button stuck
410+
// on "Loading…". Bumping reauthNonce (from the connect dropdown) clears that
411+
// latch and refetches the session so the session-check effect reopens the
412+
// auth modal.
413+
useEffect(() => {
414+
if (reauthNonce > 0) {
415+
setHasCheckedSession(false);
416+
setCheckingSession(false);
417+
setShowAuthModal(false);
418+
void refetchWalletSession();
419+
}
420+
}, [reauthNonce, refetchWalletSession]);
421+
401422
const handleAuthModalAuthorized = useCallback(async () => {
402423
setShowAuthModal(false);
403424
setCheckingSession(false);

src/lib/zustand/user.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ interface UserState {
2525
setPastWallet: (pastWallet: string | undefined) => void;
2626
pastUtxosEnabled: boolean;
2727
setPastUtxosEnabled: (enabled: boolean | ((prev: boolean) => boolean)) => void;
28+
// Bumped when the user explicitly asks to re-run wallet authorization
29+
// (e.g. after a failed/cancelled auto-authorize left them connected but
30+
// unauthorized). The layout watches this to reopen the auth modal.
31+
reauthNonce: number;
32+
requestReauth: () => void;
2833
}
2934

3035
export const useUserStore = create<UserState>()(
@@ -36,6 +41,8 @@ export const useUserStore = create<UserState>()(
3641
setUser: (user) => set({ user }),
3742
pastWallet: undefined,
3843
setPastWallet: (wallet) => set({ pastWallet: wallet }),
44+
reauthNonce: 0,
45+
requestReauth: () => set((state) => ({ reauthNonce: state.reauthNonce + 1 })),
3946
pastUtxosEnabled: false,
4047
setPastUtxosEnabled: (enabled) => {
4148
const newValue = typeof enabled === "function" ? enabled(get().pastUtxosEnabled) : enabled;

0 commit comments

Comments
 (0)