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) {