From 2c881f4e1f6312bf47a3941c01159c76546148e1 Mon Sep 17 00:00:00 2001 From: devwura Date: Tue, 29 Sep 2026 08:47:51 +0100 Subject: [PATCH] fix: resolve Stellar Wave bugs (#451-454) - Fix ConfirmDialog preventing close while loading async action (#454) - Add onOpenChange handler to prevent dialog close when loading is true - Prevents user from dismissing dialog during destructive operations - Fix useSessions AbortController cleanup is unreachable (#453) - Move AbortController creation from callback to useEffect - Properly return cleanup function to cancel in-flight requests on unmount - Prevents stale state updates after component unmount - Fix Connect page silently swallowing profile fetch errors (#452) - Move success toast after successful profile fetch - Add proper error handling and display for profile fetch failures - Prevent showing success message when full connection flow fails - Fix Settings page language preference never persisted (#451) - Load language preference from profile on mount - Include language in profile update payload - User's language preference now persists across sessions --- src/app/(auth)/connect/page.tsx | 9 +++++---- src/app/settings/page.tsx | 2 ++ src/components/shared/confirm-dialog.tsx | 9 ++++++++- src/lib/hooks/use-sessions.ts | 17 +++++++++-------- 4 files changed, 24 insertions(+), 13 deletions(-) diff --git a/src/app/(auth)/connect/page.tsx b/src/app/(auth)/connect/page.tsx index 6f95752..e9dca42 100644 --- a/src/app/(auth)/connect/page.tsx +++ b/src/app/(auth)/connect/page.tsx @@ -71,16 +71,17 @@ export default function ConnectPage() { return; } - addToast("Wallet connected successfully!", "success"); try { const { getProfile } = await import("@/lib/api/auth"); const { useAuthStore } = await import("@/store/auth-store"); const jwt = useAuthStore.getState().jwt; const profile = jwt ? await getProfile(jwt) : null; + addToast("Wallet connected successfully!", "success"); router.replace(profile?.displayName ? "/dashboard" : "/onboarding"); - } catch { - addToast("Wallet connected, but your profile could not be loaded. Continue setup to get started.", "error"); - router.replace("/onboarding"); + } catch (err) { + connectingFromPage.current = false; + const errorMessage = err instanceof Error ? err.message : "Failed to fetch profile"; + addToast(`Error: ${errorMessage}`, "error"); } }; diff --git a/src/app/settings/page.tsx b/src/app/settings/page.tsx index 7ed7be2..4cd030d 100644 --- a/src/app/settings/page.tsx +++ b/src/app/settings/page.tsx @@ -67,6 +67,7 @@ export default function SettingsPage() { setBackground(profile.background ?? ""); setLearningGoals((profile.learningGoals ?? []).join(", ")); setPace(profile.preferredPace ?? "moderate"); + setLanguage(profile.language ?? "English"); }) .catch(() => { addToast("Failed to load profile", "error"); @@ -86,6 +87,7 @@ export default function SettingsPage() { .map((g) => g.trim()) .filter(Boolean), preferredPace: pace, + language, }); addToast("Profile updated successfully", "success"); } catch { diff --git a/src/components/shared/confirm-dialog.tsx b/src/components/shared/confirm-dialog.tsx index d1b6918..c2122ee 100644 --- a/src/components/shared/confirm-dialog.tsx +++ b/src/components/shared/confirm-dialog.tsx @@ -39,8 +39,15 @@ export function ConfirmDialog({ destructive = false, icon, }: ConfirmDialogProps) { + const handleOpenChange = (newOpen: boolean) => { + if (loading && !newOpen) { + return; + } + onOpenChange(newOpen); + }; + return ( - +
(null); const [revokingId, setRevokingId] = useState(null); - const fetchSessions = useCallback(async () => { + const fetchSessions = useCallback(async (controller?: AbortController) => { if (!jwt) { setLoading(false); return; @@ -26,14 +26,14 @@ export function useSessions() { setLoading(true); setError(null); - const controller = new AbortController(); + const signal = controller?.signal; try { - const data = await getSessions(jwt, controller.signal); + const data = await getSessions(jwt, signal); setSessions(data || []); } catch (err) { if (isAbortError(err)) return; - + // Fallback to current session if endpoint is not available const fallbackSession: UserSession = { id: "current-session", @@ -44,14 +44,12 @@ export function useSessions() { createdAt: new Date().toISOString(), isCurrent: true, }; - + setSessions([fallbackSession]); setError(err instanceof Error ? err.message : "Failed to fetch sessions"); } finally { setLoading(false); } - - return () => controller.abort(); }, [jwt]); const revoke = useCallback( @@ -77,7 +75,10 @@ export function useSessions() { ); useEffect(() => { - fetchSessions(); + const controller = new AbortController(); + fetchSessions(controller); + + return () => controller.abort(); }, [fetchSessions]); return {