From f68d6f1a4e0d9ee151ba5a5fd3b998274a50c746 Mon Sep 17 00:00:00 2001 From: Merul Dhiman Date: Fri, 14 Nov 2025 11:50:17 +0530 Subject: [PATCH 1/2] chore: fix modal UX --- .../src/routes/(app)/scan-qr/+page.svelte | 35 +++++-- .../scan-qr/components/AuthDrawer.svelte | 69 ++++++++++++-- .../scan-qr/components/SigningDrawer.svelte | 77 ++++++++++++---- .../src/routes/(app)/scan-qr/scanLogic.ts | 92 +++++++++++++++++++ .../src/controllers/AuthController.ts | 16 ++-- 5 files changed, 242 insertions(+), 47 deletions(-) diff --git a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte index f8d40a202..4976c947e 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte @@ -37,6 +37,9 @@ const { isRevealingVote, revealSuccess, revealedVoteData, + authError, + signingError, + authLoading, } = stores; const { @@ -95,16 +98,20 @@ $effect(() => { ); }); -function handleAuthDrawerDecline() { - setCodeScannedDrawerOpen(false); - startScan(); +async function handleAuthDrawerDecline() { + // If there's an error, "Okay" button closes modal and navigates to main + if ($authError) { + setCodeScannedDrawerOpen(false); + await goto("/main"); + } else { + // Otherwise, "Decline" closes modal and restarts scanning + setCodeScannedDrawerOpen(false); + startScan(); + } } function handleAuthDrawerOpenChange(value: boolean) { setCodeScannedDrawerOpen(value); - if (!value) { - startScan(); - } } function handleLoggedInDrawerConfirm() { @@ -118,9 +125,16 @@ function handleLoggedInDrawerOpenChange(value: boolean) { setLoggedInDrawerOpen(value); } -function handleSigningDrawerDecline() { - setSigningDrawerOpen(false); - startScan(); +async function handleSigningDrawerDecline() { + // If there's an error, "Okay" button closes modal and navigates to main + if ($signingError) { + setSigningDrawerOpen(false); + await goto("/main"); + } else { + // Otherwise, "Decline" closes modal and restarts scanning + setSigningDrawerOpen(false); + startScan(); + } } function handleSigningDrawerOpenChange(value: boolean) { @@ -177,6 +191,8 @@ function handleRevealDrawerOpenChange(value: boolean) { hostname={$hostname} scannedContent={$scannedData?.content} isSigningRequest={$isSigningRequest} + authError={$authError} + authLoading={$authLoading} onConfirm={handleAuth} onDecline={handleAuthDrawerDecline} onOpenChange={handleAuthDrawerOpenChange} @@ -199,6 +215,7 @@ function handleRevealDrawerOpenChange(value: boolean) { selectedBlindVoteOption={$selectedBlindVoteOption} isSubmittingBlindVote={$isSubmittingBlindVote} loading={$loading} + signingError={$signingError} onDecline={handleSigningDrawerDecline} onSign={handleSignVote} onBlindVoteOptionChange={handleBlindVoteOptionChange} diff --git a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/components/AuthDrawer.svelte b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/components/AuthDrawer.svelte index 1cae14938..e04f97418 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/components/AuthDrawer.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/components/AuthDrawer.svelte @@ -9,6 +9,8 @@ export let platform: string | null | undefined; export let hostname: string | null | undefined; export let scannedContent: string | undefined; export let isSigningRequest: boolean; +export let authError: string | null | undefined; +export let authLoading: boolean | undefined; export let onConfirm: () => void; export let onDecline: () => void; export let onOpenChange: (value: boolean) => void; @@ -63,17 +65,64 @@ $: if (internalOpen !== lastReportedOpen) { {hostname ?? scannedContent}

+ + {#if authError} +
+
+
+ + + +
+
+

Error

+
+ {authError} +
+
+
+
+ {/if} +
- - Decline - - - Confirm - + {#if authError} + + Okay + + {:else} + + Decline + + + {#if authLoading} + Authenticating... + {:else} + Confirm + {/if} + + {/if}
{#if isSigningRequest === false} diff --git a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/components/SigningDrawer.svelte b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/components/SigningDrawer.svelte index 2033ef648..387364a07 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/components/SigningDrawer.svelte +++ b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/components/SigningDrawer.svelte @@ -14,6 +14,7 @@ export let blindVoteError: string | null; export let selectedBlindVoteOption: number | null; export let isSubmittingBlindVote: boolean; export let loading: boolean; +export let signingError: string | null | undefined; export let onDecline: () => void; export let onSign: () => void; export let onBlindVoteOptionChange: (value: number) => void; @@ -250,30 +251,66 @@ $: hasPollDetails = {signingData?.sessionId?.slice(0, 8) ?? "Unknown"}...

+ + {#if signingError} +
+
+
+ + + +
+
+

Error

+
+ {signingError} +
+
+
+
+ {/if} {/if}
{#if !isBlindVotingRequest} - - Decline - - - {#if loading} - Signing... - {:else if signingData?.pollId} - Sign Vote - {:else} - Sign Message - {/if} - + {#if signingError} + + Okay + + {:else} + + Decline + + + {#if loading} + Signing... + {:else if signingData?.pollId} + Sign Vote + {:else} + Sign Message + {/if} + + {/if} {/if}
{/if} diff --git a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts index fddf2286a..bcd962ccc 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts +++ b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts @@ -75,6 +75,9 @@ interface ScanStores { isRevealingVote: Writable; revealSuccess: Writable; revealedVoteData: Writable; + authError: Writable; + signingError: Writable; + authLoading: Writable; } interface ScanActions { @@ -136,6 +139,9 @@ export function createScanLogic({ const isRevealingVote = writable(false); const revealSuccess = writable(false); const revealedVoteData = writable(null); + const authError = writable(null); + const signingError = writable(null); + const authLoading = writable(false); let permissionsNullable: PermissionState | null = null; @@ -227,6 +233,10 @@ export function createScanLogic({ const vault = await globalState.vaultController.vault; if (!vault || !get(redirect)) return; + // Clear previous errors and set loading state + authError.set(null); + authLoading.set(true); + try { const { created } = await globalState.keyService.ensureKey( vault.ename, @@ -269,6 +279,8 @@ export function createScanLogic({ } await axios.post(redirectUrl, authPayload); + + // Close the auth drawer first codeScannedDrawerOpen.set(false); let deepLinkData = sessionStorage.getItem("deepLinkData"); @@ -293,6 +305,8 @@ export function createScanLogic({ "Invalid redirect URL:", data.redirect, ); + // Ensure auth drawer is closed before opening logged in drawer + codeScannedDrawerOpen.set(false); loggedInDrawerOpen.set(true); startScan(); return; @@ -302,6 +316,8 @@ export function createScanLogic({ new URL(data.redirect); } catch (urlError) { console.error("Invalid URL format:", urlError); + // Ensure auth drawer is closed before opening logged in drawer + codeScannedDrawerOpen.set(false); loggedInDrawerOpen.set(true); startScan(); return; @@ -346,10 +362,27 @@ export function createScanLogic({ console.log("No deep link data found after auth completion"); } + // Ensure auth drawer is closed before opening logged in drawer + codeScannedDrawerOpen.set(false); loggedInDrawerOpen.set(true); startScan(); } catch (error) { console.error("Error completing authentication:", error); + + // Set user-friendly error message + let errorMessage = "Authentication failed. Please try again."; + if (error instanceof Error) { + if (error.message.includes("network") || error.message.includes("timeout")) { + errorMessage = "Network error. Please check your connection and try again."; + } else if (error.message.includes("W3ID")) { + errorMessage = "Failed to retrieve your identity. Please try again."; + } else if (error.message.includes("redirect")) { + errorMessage = "Invalid redirect URL. Please scan the QR code again."; + } + } + authError.set(errorMessage); + } finally { + authLoading.set(false); } } @@ -361,10 +394,14 @@ export function createScanLogic({ session.set(url.searchParams.get("session")); hostname.set(redirectUrl.hostname); isSigningRequest.set(false); + authError.set(null); // Clear any previous auth errors codeScannedDrawerOpen.set(true); } function handleSigningRequest(content: string) { + // Clear any previous signing errors + signingError.set(null); + try { let parseableContent = content; if (content.startsWith("w3ds://")) { @@ -393,6 +430,7 @@ export function createScanLogic({ base64Data, redirectUri, }); + signingError.set("Invalid signing request. Please scan the QR code again."); return; } @@ -426,10 +464,12 @@ export function createScanLogic({ signingDrawerOpen.set(true); } catch (error) { console.error("Error decoding signing data:", error); + signingError.set("Failed to decode signing data. The QR code may be invalid."); return; } } catch (error) { console.error("Error parsing signing request:", error); + signingError.set("Failed to parse signing request. Please scan the QR code again."); } } @@ -506,6 +546,9 @@ export function createScanLogic({ const currentSigningSessionId = get(signingSessionId); if (!currentSigningData || !currentSigningSessionId) return; + // Clear previous errors + signingError.set(null); + try { loading.set(true); @@ -603,6 +646,21 @@ export function createScanLogic({ } } catch (error) { console.error("Error signing vote:", error); + + // Set user-friendly error message + let errorMessage = "Failed to sign. Please try again."; + if (error instanceof Error) { + if (error.message.includes("vault") || error.message.includes("W3ID")) { + errorMessage = "Failed to retrieve your identity. Please try again."; + } else if (error.message.includes("network") || error.message.includes("fetch")) { + errorMessage = "Network error. Please check your connection and try again."; + } else if (error.message.includes("redirect")) { + errorMessage = "No destination URL available. Please scan the QR code again."; + } else if (error.message.includes("submit") || error.message.includes("payload")) { + errorMessage = "Failed to submit signature. The server may be unavailable."; + } + } + signingError.set(errorMessage); } finally { loading.set(false); } @@ -986,6 +1044,10 @@ export function createScanLogic({ function setCodeScannedDrawerOpen(value: boolean) { codeScannedDrawerOpen.set(value); + // Clear auth error when drawer is closed + if (!value) { + authError.set(null); + } } function setLoggedInDrawerOpen(value: boolean) { @@ -994,6 +1056,10 @@ export function createScanLogic({ function setSigningDrawerOpen(value: boolean) { signingDrawerOpen.set(value); + // Clear signing error when drawer is closed + if (!value) { + signingError.set(null); + } } function setRevealRequestOpen(value: boolean) { @@ -1031,6 +1097,12 @@ export function createScanLogic({ if (data.type === "auth") { console.log("Handling auth deep link"); + + // Close all other modals first + signingDrawerOpen.set(false); + loggedInDrawerOpen.set(false); + isRevealRequest.set(false); + platform.set(data.platform ?? null); session.set(data.session ?? null); redirect.set(data.redirect ?? null); @@ -1043,15 +1115,24 @@ export function createScanLogic({ } isSigningRequest.set(false); + authError.set(null); // Clear any previous auth errors codeScannedDrawerOpen.set(true); } else if (data.type === "sign") { console.log("Handling signing deep link"); + + // Close all other modals first + codeScannedDrawerOpen.set(false); + loggedInDrawerOpen.set(false); + isRevealRequest.set(false); + signingSessionId.set(data.session ?? null); const base64Data = data.data; const redirectUri = data.redirect_uri; if (get(signingSessionId) && base64Data && redirectUri) { redirect.set(redirectUri); + signingError.set(null); // Clear any previous signing errors + try { const decodedString = atob(base64Data); const parsedSigningData = JSON.parse( @@ -1153,15 +1234,23 @@ export function createScanLogic({ } } else if (data.type === "reveal") { console.log("Handling reveal deep link"); + + // Close all other modals first + codeScannedDrawerOpen.set(false); + loggedInDrawerOpen.set(false); + signingDrawerOpen.set(false); + const pollId = data.pollId; if (pollId) { console.log("🔍 Reveal request for poll:", pollId); + revealError.set(null); // Clear any previous reveal errors revealPollId.set(pollId); isRevealRequest.set(true); } else { console.error("Missing pollId in reveal request"); + revealError.set("Invalid reveal request. Poll ID is missing."); } } } @@ -1271,6 +1360,9 @@ export function createScanLogic({ isRevealingVote, revealSuccess, revealedVoteData, + authError, + signingError, + authLoading, }, actions: { startScan, diff --git a/platforms/group-charter-manager-api/src/controllers/AuthController.ts b/platforms/group-charter-manager-api/src/controllers/AuthController.ts index 17520ecb8..d0b2221a1 100644 --- a/platforms/group-charter-manager-api/src/controllers/AuthController.ts +++ b/platforms/group-charter-manager-api/src/controllers/AuthController.ts @@ -75,9 +75,9 @@ export class AuthController { type: "version_mismatch" }; this.eventEmitter.emit(session, errorMessage); - return res.status(400).json({ - error: "App version too old", - message: errorMessage.message + return res.status(400).json({ + error: "App version too old", + message: errorMessage.message }); } @@ -97,19 +97,19 @@ export class AuthController { res.status(200).send(); } catch (error) { console.error("Error during login:", error); - + // Provide more specific error messages if (error instanceof Error) { if (error.message.includes("not found")) { - return res.status(404).json({ - error: "User not found", + return res.status(404).json({ + error: "User not found", message: error.message, details: "Please ensure you have the correct ename or contact support to create an account." }); } } - + res.status(500).json({ error: "Internal server error" }); } }; -} \ No newline at end of file +} From f862fc61137d678dbfbd6b218f1cb36ee07bcb26 Mon Sep 17 00:00:00 2001 From: SoSweetHam Date: Fri, 14 Nov 2025 11:57:56 +0530 Subject: [PATCH 2/2] fix: format --- .../src/routes/(app)/scan-qr/scanLogic.ts | 75 ++++++++++++------- 1 file changed, 50 insertions(+), 25 deletions(-) diff --git a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts index bcd962ccc..8f0917bbe 100644 --- a/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts +++ b/infrastructure/eid-wallet/src/routes/(app)/scan-qr/scanLogic.ts @@ -279,7 +279,7 @@ export function createScanLogic({ } await axios.post(redirectUrl, authPayload); - + // Close the auth drawer first codeScannedDrawerOpen.set(false); @@ -368,16 +368,22 @@ export function createScanLogic({ startScan(); } catch (error) { console.error("Error completing authentication:", error); - + // Set user-friendly error message let errorMessage = "Authentication failed. Please try again."; if (error instanceof Error) { - if (error.message.includes("network") || error.message.includes("timeout")) { - errorMessage = "Network error. Please check your connection and try again."; + if ( + error.message.includes("network") || + error.message.includes("timeout") + ) { + errorMessage = + "Network error. Please check your connection and try again."; } else if (error.message.includes("W3ID")) { - errorMessage = "Failed to retrieve your identity. Please try again."; + errorMessage = + "Failed to retrieve your identity. Please try again."; } else if (error.message.includes("redirect")) { - errorMessage = "Invalid redirect URL. Please scan the QR code again."; + errorMessage = + "Invalid redirect URL. Please scan the QR code again."; } } authError.set(errorMessage); @@ -401,7 +407,7 @@ export function createScanLogic({ function handleSigningRequest(content: string) { // Clear any previous signing errors signingError.set(null); - + try { let parseableContent = content; if (content.startsWith("w3ds://")) { @@ -430,7 +436,9 @@ export function createScanLogic({ base64Data, redirectUri, }); - signingError.set("Invalid signing request. Please scan the QR code again."); + signingError.set( + "Invalid signing request. Please scan the QR code again.", + ); return; } @@ -464,12 +472,16 @@ export function createScanLogic({ signingDrawerOpen.set(true); } catch (error) { console.error("Error decoding signing data:", error); - signingError.set("Failed to decode signing data. The QR code may be invalid."); + signingError.set( + "Failed to decode signing data. The QR code may be invalid.", + ); return; } } catch (error) { console.error("Error parsing signing request:", error); - signingError.set("Failed to parse signing request. Please scan the QR code again."); + signingError.set( + "Failed to parse signing request. Please scan the QR code again.", + ); } } @@ -646,18 +658,31 @@ export function createScanLogic({ } } catch (error) { console.error("Error signing vote:", error); - + // Set user-friendly error message let errorMessage = "Failed to sign. Please try again."; if (error instanceof Error) { - if (error.message.includes("vault") || error.message.includes("W3ID")) { - errorMessage = "Failed to retrieve your identity. Please try again."; - } else if (error.message.includes("network") || error.message.includes("fetch")) { - errorMessage = "Network error. Please check your connection and try again."; + if ( + error.message.includes("vault") || + error.message.includes("W3ID") + ) { + errorMessage = + "Failed to retrieve your identity. Please try again."; + } else if ( + error.message.includes("network") || + error.message.includes("fetch") + ) { + errorMessage = + "Network error. Please check your connection and try again."; } else if (error.message.includes("redirect")) { - errorMessage = "No destination URL available. Please scan the QR code again."; - } else if (error.message.includes("submit") || error.message.includes("payload")) { - errorMessage = "Failed to submit signature. The server may be unavailable."; + errorMessage = + "No destination URL available. Please scan the QR code again."; + } else if ( + error.message.includes("submit") || + error.message.includes("payload") + ) { + errorMessage = + "Failed to submit signature. The server may be unavailable."; } } signingError.set(errorMessage); @@ -1097,12 +1122,12 @@ export function createScanLogic({ if (data.type === "auth") { console.log("Handling auth deep link"); - + // Close all other modals first signingDrawerOpen.set(false); loggedInDrawerOpen.set(false); isRevealRequest.set(false); - + platform.set(data.platform ?? null); session.set(data.session ?? null); redirect.set(data.redirect ?? null); @@ -1119,12 +1144,12 @@ export function createScanLogic({ codeScannedDrawerOpen.set(true); } else if (data.type === "sign") { console.log("Handling signing deep link"); - + // Close all other modals first codeScannedDrawerOpen.set(false); loggedInDrawerOpen.set(false); isRevealRequest.set(false); - + signingSessionId.set(data.session ?? null); const base64Data = data.data; const redirectUri = data.redirect_uri; @@ -1132,7 +1157,7 @@ export function createScanLogic({ if (get(signingSessionId) && base64Data && redirectUri) { redirect.set(redirectUri); signingError.set(null); // Clear any previous signing errors - + try { const decodedString = atob(base64Data); const parsedSigningData = JSON.parse( @@ -1234,12 +1259,12 @@ export function createScanLogic({ } } else if (data.type === "reveal") { console.log("Handling reveal deep link"); - + // Close all other modals first codeScannedDrawerOpen.set(false); loggedInDrawerOpen.set(false); signingDrawerOpen.set(false); - + const pollId = data.pollId; if (pollId) {