Conversation
|
Warning Rate limit exceeded@coodos has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 1 minutes and 22 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (1)
WalkthroughAdds a toast notification subsystem; replaces IdentityCard eName share flow with a copy-to-clipboard button; updates Drawer lifecycle/styling (hide-on-close, pane CSS); removes QR-sharing UI from main; modifies navigation/back behaviors across several routes; adds verify pages, stores, and enhanced verify/selfie/passport flows; bumps package/tauri versions and onboarding name validation. Changes
Sequence Diagram(s)sequenceDiagram
participant UI as Component
participant ToastStore as toastStore
participant ToastComp as Toast.svelte
participant Timer as Auto-remove Timer
UI->>ToastStore: toast.success("message", 3000)
ToastStore->>ToastStore: add toast {id, type, message, duration}
ToastStore->>ToastComp: notify subscribers (new toasts list)
activate ToastComp
ToastComp->>ToastComp: render toast (animate-in)
ToastComp->>Timer: schedule remove(id) after duration
Note over Timer: countdown
Timer->>ToastStore: remove(id)
ToastStore->>ToastComp: notify subscribers (updated list)
ToastComp->>ToastComp: fade out / remove toast
deactivate ToastComp
sequenceDiagram
participant Browser as Browser
participant Layout as +layout.svelte
participant Guard as AuthGuardLogic
participant Router as Router/goto
Browser->>Layout: popstate / navigate (back)
Layout->>Guard: isVaultPresent? / isTargetAuthRoute?
alt vault exists AND target is auth route
Guard->>Router: goto("/main") (replace history)
Router->>Browser: update history / prevent back to auth
else
Guard->>Browser: allow navigation/back
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (10)
infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte (1)
48-52: Verify that navigating to "/main" on cancel is intentional.The async/await pattern for
gotois good and aligns with the PR's navigation improvements. However, navigating to "/main" when the user cancels the delete operation seems unexpected—users typically expect to remain on the settings page when canceling an action.Consider whether this navigation is intentional for back-navigation flow, or if the drawers should simply close and leave the user on the settings page.
Optionally, consider adding error handling around the navigation:
async function cancelDelete() { isDeleteConfirmationOpen = false; isFinalConfirmationOpen = false; - await goto("/main"); + try { + await goto("/main"); + } catch (error) { + console.error("Navigation failed:", error); + } }infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte (4)
236-244: Simplify validation logic.The validation contains redundant checks:
- If
trimmed.length === 0, the regex test on line 242 will never execute (the function returns false on line 240)- The regex
[\s\n\r]is redundant since\salready includes newlines and carriage returns- An empty string won't match
/^[\s\n\r]+$/anywayConsider simplifying:
-const isValidName = (name: string): boolean => { - if (!name) return false; - const trimmed = name.trim(); - if (trimmed.length === 0) return false; - // Check if name is not just whitespace, newlines, or enter characters - if (/^[\s\n\r]+$/.test(trimmed)) return false; - return true; -}; +const isValidName = (name: string): boolean => { + return name?.trim().length > 0; +};
263-263: Unnecessary fallback after validation.Since line 250 validates that
trimmedNameis not empty (viaisValidName), the fallback tocapitalize(...)will never execute. The validation ensurestrimmedNameis always valid when reaching this line.Consider simplifying:
-name: trimmedName || capitalize(`${falso.randFirstName()} ${falso.randLastName()}`), +name: trimmedName,
371-373: Remove redundant input handler.The
oninputhandler is unnecessary becausebind:value={demoName}on line 370 already handles two-way data binding automatically. The explicit handler duplicates this functionality.Remove the redundant oninput handler:
<input type="text" bind:value={demoName} - oninput={(e) => { - demoName = (e.target as HTMLInputElement).value; - }} class="border-1 border-gray-200 w-full rounded-md font-medium my-2 p-2" placeholder="Enter your demo name for ePassport" />
382-383: Reuse validation function for consistency.The inline validation logic duplicates the checks in
isValidName. This violates the DRY principle and makes the code harder to maintain—if validation requirements change, both locations need updates.Use the existing validation function:
<ButtonAction - variant={!demoName || !demoName.trim() || /^[\s\n\r]+$/.test(demoName.trim()) ? "soft" : "solid"} - disabled={!demoName || !demoName.trim() || /^[\s\n\r]+$/.test(demoName.trim())} + variant={!isValidName(demoName) ? "soft" : "solid"} + disabled={!isValidName(demoName)} class="w-full" callback={handleFinalSubmit}>Continue</ButtonAction >infrastructure/eid-wallet/src/routes/+page.svelte (1)
2-40:clearPinis now only defined, never invoked — confirm this behavior changeThe new onMount logic initializes
clearPin(with good error handling) but never calls it, so any prior behavior that cleared the PIN automatically on landing is effectively removed. If the intent is to stop auto-clearing and only clear the PIN from some other UI/action, this is fine; otherwise you likely want to either invokeclearPinin the appropriate branch or drop it here to avoid confusion.infrastructure/eid-wallet/src/routes/+layout.svelte (2)
488-521:popstateguard works, butpreventDefault+pushStatemay yield odd historyThe popstate handler correctly checks for an existing vault before forcing users away from
/login//register//onboarding, but a couple of details are worth tightening:
event.preventDefault()has no effect forpopstateevents; control comes from your subsequentgoto("/main")andhistory.pushState.- Calling
goto("/main")(which pushes a new entry) and thenhistory.pushStateagain can create duplicated/mainentries, making the back button feel “sticky”.You might want to instead do something like
await goto("/main", { replaceState: true })or usehistory.replaceStateso the restricted entry is replaced rather than duplicated, and you can droppreventDefault()entirely.
3-4: Toast integration is fine; consider simplifying the render syntaxImporting
Toastand rendering it at the layout root is good for global notifications. SinceToastis a concrete component (not dynamic), you don’t need<svelte:component>here;<Toast />would be simpler and avoids relying on the now-deprecated<svelte:component>form in Svelte 5.(reddit.com)Also applies to: 540-541
infrastructure/eid-wallet/src/lib/ui/Toast/Toast.svelte (2)
7-14: Prefer idiomatic Svelte 5 store subscription.In Svelte 5, you can subscribe to stores more concisely using the
$prefix directly. The manual$effectsubscription is verbose and unnecessary.Apply this diff to use the idiomatic pattern:
-let toasts = $state<Array<{ id: string; message: string; type?: "success" | "error" | "info"; duration?: number }>>([]); - -$effect(() => { - const unsubscribe = toastStore.subscribe((value) => { - toasts = value; - }); - return unsubscribe; -}); +let toasts = $derived($toastStore);Note: You'll also need to update the
removeToastfunction to directly reference$toastStoreinstead of the localtoastsvariable, or keep the current approach if you need the local reactive variable for other reasons.
36-58: Consider adding a maximum toast limit.The toast list can grow unbounded if many toasts are triggered rapidly, potentially overwhelming the UI. Consider limiting the number of visible toasts (e.g., max 3-5) and queuing or auto-dismissing older ones.
This can be implemented in the store's
addmethod intoast.tsby checking the array length before adding a new toast.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (14)
infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.svelte(2 hunks)infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.svelte(3 hunks)infrastructure/eid-wallet/src/lib/ui/Toast/Toast.svelte(1 hunks)infrastructure/eid-wallet/src/lib/ui/Toast/toast.ts(1 hunks)infrastructure/eid-wallet/src/routes/(app)/main/+page.svelte(0 hunks)infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte(3 hunks)infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte(1 hunks)infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte(2 hunks)infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte(3 hunks)infrastructure/eid-wallet/src/routes/(auth)/verify/steps/document-type.svelte(1 hunks)infrastructure/eid-wallet/src/routes/(auth)/verify/steps/passport.svelte(3 hunks)infrastructure/eid-wallet/src/routes/(auth)/verify/steps/selfie.svelte(2 hunks)infrastructure/eid-wallet/src/routes/+layout.svelte(4 hunks)infrastructure/eid-wallet/src/routes/+page.svelte(1 hunks)
💤 Files with no reviewable changes (1)
- infrastructure/eid-wallet/src/routes/(app)/main/+page.svelte
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-14T17:54:47.719Z
Learnt from: coodos
Repo: MetaState-Prototype-Project/prototype PR: 437
File: infrastructure/eid-wallet/src/lib/global/controllers/security.ts:138-157
Timestamp: 2025-11-14T17:54:47.719Z
Learning: The `setOnboardingPin` method in `infrastructure/eid-wallet/src/lib/global/controllers/security.ts` is intentionally designed to allow overwriting existing PINs without checking if a PIN already exists. This is a workaround for Android keeping stale files around during app reinstallation or onboarding, which causes errors on some devices. Do not suggest adding guards to prevent PIN overwrites in this method.
Applied to files:
infrastructure/eid-wallet/src/routes/+page.svelte
🔇 Additional comments (9)
infrastructure/eid-wallet/src/routes/(auth)/onboarding/+page.svelte (2)
247-256: Good validation implementation.The validation flow correctly trims the input, validates it, provides user feedback, and prevents submission of invalid names.
377-379: Good user feedback implementation.The conditional error display provides clear inline feedback for name validation errors while keeping other error messages in their appropriate locations.
infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte (1)
101-105: Unified cancel/decline handlers now always redirect to/mainThe three handlers now just close their respective drawers and navigate to the main dashboard, regardless of prior error state or request type. This is technically sound, but it’s a behavior change from the previous conditional flows, so please confirm this redirect-on-cancel UX is intended for auth, signing, and reveal flows.
Also applies to: 122-126, 139-142
infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte (1)
15-16: Context wiring and fullscreen Drawer behavior look consistentProviding
showVeriffModalviasetContextand binding it into<Drawer bind:isPaneOpen={showVeriffModal} fullScreen={$verifStep === 1 || $verifStep === 2}>aligns with howdocument-type.svelteconsumes the context and with the intended fullscreen experience for steps 1 and 2. The timing also works out since the modal is only opened after onMount has run, so children will see the context when they mount.Also applies to: 252-265, 404-405
infrastructure/eid-wallet/src/routes/(auth)/verify/steps/selfie.svelte (1)
4-5: Selfie step back-navigation is wired correctlyThe new header with
Button.Iconandonclick={() => verifStep.set(1)}cleanly returns the user to the document-capture step without touching the capture logic; combined with Passport’s cleanup on step change, this should behave correctly.Also applies to: 76-90
infrastructure/eid-wallet/src/routes/(auth)/verify/steps/passport.svelte (1)
4-5: Passport step back-navigation aligns with the verify flowThe back icon now drives
verifStep.set(0), returning to the document-type selection while letting the existing onMount cleanup stop the camera on unmount. This brings the passport step in line with the other verify steps’ back UX.Also applies to: 231-256
infrastructure/eid-wallet/src/routes/(auth)/verify/steps/document-type.svelte (1)
3-22: Back behavior viashowVeriffModalcontext is robustUsing
getContext("showVeriffModal")with a{ value: boolean }contract and falling back towindow.history.back()if the context is missing gives a clean “close drawer or go back” behavior. The updated header withButton.Iconhooked togoBacklooks good.Also applies to: 26-37
infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.svelte (1)
67-82: Verify clipboard API availability in your deployment environment.The
navigator.clipboard.writeTextAPI requires a secure context (HTTPS or localhost). If this application is deployed over plain HTTP, the clipboard operation will fail.Please confirm that the application is served over HTTPS in production. Additionally, consider adding a fallback mechanism for browsers that don't support the Clipboard API:
onclick={async () => { if (userId) { try { if (navigator.clipboard && navigator.clipboard.writeText) { await navigator.clipboard.writeText(userId); toast.success("eName copied to clipboard"); } else { // Fallback for older browsers or insecure contexts const textArea = document.createElement("textarea"); textArea.value = userId; textArea.style.position = "fixed"; textArea.style.opacity = "0"; document.body.appendChild(textArea); textArea.select(); const success = document.execCommand('copy'); document.body.removeChild(textArea); if (success) { toast.success("eName copied to clipboard"); } else { toast.error("Failed to copy eName"); } } } catch (error) { console.error("Failed to copy:", error); toast.error("Failed to copy eName"); } } }}infrastructure/eid-wallet/src/lib/ui/Toast/toast.ts (1)
15-32: [rewritten comment]
[classification tag]
| <style> | ||
| @keyframes slide-in-from-top-2 { | ||
| from { | ||
| transform: translateY(-100%); | ||
| opacity: 0; | ||
| } | ||
| to { | ||
| transform: translateY(0); | ||
| opacity: 1; | ||
| } | ||
| } | ||
|
|
||
| @keyframes fade-in { | ||
| from { | ||
| opacity: 0; | ||
| } | ||
| to { | ||
| opacity: 1; | ||
| } | ||
| } | ||
|
|
||
| .animate-in { | ||
| animation: slide-in-from-top-2 0.3s ease-out, fade-in 0.3s ease-out; | ||
| } | ||
| </style> |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Add exit animations for smoother UX.
The component defines entrance animations (slide-in-from-top-2, fade-in) but no exit animations. When a toast is removed, it disappears abruptly, which creates a jarring user experience.
Consider using Svelte's transition directives for smoother enter/exit animations:
+import { fly, fade } from 'svelte/transition';
+
{#each toasts as toast (toast.id)}
<div
+ transition:fly={{ y: -20, duration: 300 }}
class={cn(
- "pointer-events-auto flex items-center justify-between gap-3 rounded-lg border p-4 shadow-lg animate-in slide-in-from-top-2 fade-in",
+ "pointer-events-auto flex items-center justify-between gap-3 rounded-lg border p-4 shadow-lg",
getToastClasses(toast.type),
)}
role="alert"
>Then you can remove the custom CSS animations and use Svelte's built-in transition system.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
in infrastructure/eid-wallet/src/lib/ui/Toast/Toast.svelte around lines 61-85,
the component only defines entrance CSS keyframe animations and lacks exit
animations, causing abrupt removals; replace or augment this with Svelte
transitions by importing a built-in transition (e.g., fly or slide/fade) and
apply a transition directive to the toast element (e.g., use:transitionName or
transition:fade with configured params) so the component animates both in and
out, and remove the custom CSS animations if the Svelte transitions cover both
directions to keep styles consistent.
| onNavigate(async (navigation) => { | ||
| if (!document.startViewTransition) return; | ||
|
|
||
| const from = navigation.from?.url.pathname; | ||
| const to = navigation.to?.url.pathname; | ||
|
|
||
| if (!from || !to || from === to) return; | ||
|
|
||
| // Prevent navigation to login/register/onboarding when logged in | ||
| if (to === "/login" || to === "/register" || to === "/onboarding") { | ||
| try { | ||
| const gs = getContext<() => GlobalState>("globalState"); | ||
| if (gs) { | ||
| const state = gs(); | ||
| const vault = await state.vaultController.vault; | ||
| if (vault) { | ||
| // User is logged in, prevent navigation and redirect to main | ||
| await goto("/main"); | ||
| return; | ||
| } | ||
| } | ||
| } catch (error) { | ||
| // If we can't check, allow navigation | ||
| console.error("Error checking auth state:", error); | ||
| } | ||
| } |
There was a problem hiding this comment.
Guard in onNavigate should handle globalState being undefined
In the auth guard you do:
const gs = getContext<() => GlobalState>("globalState");
if (gs) {
const state = gs();
const vault = await state.vaultController.vault;
...
}Because the context function exists from startup even before GlobalState.create() resolves, gs() can return undefined, leading to state.vaultController throwing if a navigation to /login//register//onboarding happens very early. Consider defensively handling that:
const gs = getContext<() => GlobalState | undefined>("globalState");
const state = gs?.();
if (state) {
const vault = await state.vaultController.vault;
if (vault) {
await goto("/main");
return;
}
}This keeps the guard robust while preserving the intended redirect behavior.
🤖 Prompt for AI Agents
in infrastructure/eid-wallet/src/routes/+layout.svelte around lines 396-421, the
auth guard assumes gs() returns a GlobalState but gs() can be undefined early
causing state.vaultController to throw; update the guard to allow gs to be a
function returning GlobalState | undefined, call gs?.() and assign to a local
state variable, then only access state.vaultController.vault if state is truthy
(preserving the existing try/catch and redirect to /main when a vault exists) so
navigation checks are defensive against an unresolved global state.
| $effect(() => { | ||
| if (mainWrapper) { | ||
| swipedetect(mainWrapper, (dir: string) => { | ||
| if (dir === "right") window.history.back(); | ||
| if (dir === "right") { | ||
| // Prevent going back to login/register/onboarding screens when logged in | ||
| const currentPath = window.location.pathname; | ||
| if (currentPath === "/login" || currentPath === "/register" || currentPath === "/onboarding") { | ||
| // If we're on login/register/onboarding, allow back navigation | ||
| window.history.back(); | ||
| } else { | ||
| // Check if we're logged in by checking if we can access globalState | ||
| try { | ||
| const gs = getContext<() => GlobalState>("globalState"); | ||
| if (gs) { | ||
| // User is logged in, prevent going back to login/register/onboarding | ||
| const historyLength = window.history.length; | ||
| // Only allow back if we're not going to login/register/onboarding | ||
| const previousPath = document.referrer; | ||
| if (previousPath && | ||
| !previousPath.includes("/login") && | ||
| !previousPath.includes("/register") && | ||
| !previousPath.includes("/onboarding")) { | ||
| window.history.back(); | ||
| } else { | ||
| // Redirect to main instead | ||
| goto("/main"); | ||
| } | ||
| } else { | ||
| window.history.back(); | ||
| } | ||
| } catch { | ||
| window.history.back(); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n infrastructure/eid-wallet/src/routes/+layout.svelte | sed -n '420,520p'Repository: MetaState-Prototype-Project/prototype
Length of output: 4850
🏁 Script executed:
rg "vault" infrastructure/eid-wallet/src/routes/+layout.svelte -B 2 -A 2Repository: MetaState-Prototype-Project/prototype
Length of output: 3079
🏁 Script executed:
rg "getContext" infrastructure/eid-wallet/src/routes/+layout.svelte -B 3 -A 3Repository: MetaState-Prototype-Project/prototype
Length of output: 2898
🏁 Script executed:
wc -l infrastructure/eid-wallet/src/routes/+layout.svelte && cat infrastructure/eid-wallet/src/routes/+layout.svelte | sed -n '1,50p'Repository: MetaState-Prototype-Project/prototype
Length of output: 1709
🏁 Script executed:
cat infrastructure/eid-wallet/src/routes/+layout.svelte | sed -n '1,100p'Repository: MetaState-Prototype-Project/prototype
Length of output: 3641
🏁 Script executed:
rg "onNavigate" infrastructure/eid-wallet/src/routes/+layout.svelte -B 5 -A 20Repository: MetaState-Prototype-Project/prototype
Length of output: 1905
Mirror the vault check from onNavigate/popstate in the swipe handler
The swipe handler at lines 462-478 treats context existence as authentication, but the onNavigate and popstate handlers both check const vault = await state.vaultController.vault; before confirming the user is logged in. Apply the same vault verification in the swipe handler to prevent logged-out users from being treated as authenticated.
🤖 Prompt for AI Agents
in infrastructure/eid-wallet/src/routes/+layout.svelte around lines 449-483, the
swipe handler currently assumes presence of the globalState context implies the
user is logged in; instead mirror the onNavigate/popstate logic by retrieving
the state/vault and awaiting state.vaultController.vault to confirm an unlocked
vault before treating the user as authenticated. Modify the try block to get the
state/context (e.g., const state = getContext("globalState") or similar), await
const vault = await state.vaultController.vault, and only use the "prevent back
/ redirect to /main" logic when vault is truthy; otherwise fall back to
window.history.back(); ensure errors are caught and handled the same way as
other handlers.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.svelte (2)
84-97: Extract duplicated class-toggling logic into a helper function.The same logic for adding/removing
drawer-fullscreen/drawer-normalclasses appears three times. Extract it to reduce duplication and improve maintainability:+function updatePaneClass(isFullScreen: boolean) { + const paneElement = document.querySelector(".pane") as HTMLElement; + if (paneElement) { + paneElement.classList.toggle("drawer-fullscreen", isFullScreen); + paneElement.classList.toggle("drawer-normal", !isFullScreen); + } +} + // Initialize pane only once when element is available $effect(() => { if (!drawerElem) return; // ... pane initialization ... - // Add class to pane element based on fullScreen prop - // Use setTimeout to ensure pane element is created - setTimeout(() => { - const paneElement = document.querySelector(".pane") as HTMLElement; - if (paneElement) { - if (fullScreen) { - paneElement.classList.add("drawer-fullscreen"); - paneElement.classList.remove("drawer-normal"); - } else { - paneElement.classList.add("drawer-normal"); - paneElement.classList.remove("drawer-fullscreen"); - } - } - }, 0); + setTimeout(() => updatePaneClass(fullScreen), 0); return () => pane?.destroy(); });Apply the same pattern in the other two locations (lines 106-116 and 120-132).
Also applies to: 106-116, 120-132
56-57: Consider handling window resize forfullScreenHeight.The height is calculated once on initialization. If the device rotates or window resizes, the fullscreen height won't update. This may cause layout issues on devices that support orientation changes.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.svelte(2 hunks)infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.svelte(6 hunks)infrastructure/eid-wallet/src/lib/ui/Toast/Toast.svelte(1 hunks)infrastructure/eid-wallet/src/routes/(app)/main/+page.svelte(0 hunks)infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte(3 hunks)infrastructure/eid-wallet/src/routes/(auth)/verify/steps/passport.svelte(3 hunks)infrastructure/eid-wallet/src/routes/(auth)/verify/steps/selfie.svelte(2 hunks)
💤 Files with no reviewable changes (1)
- infrastructure/eid-wallet/src/routes/(app)/main/+page.svelte
🚧 Files skipped from review as they are similar to previous changes (1)
- infrastructure/eid-wallet/src/lib/ui/Toast/Toast.svelte
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (5)
infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.svelte (1)
4-4: LGTM! Imports align with the new copy functionality.The toast and Copy01Icon imports are correctly added to support the new copy-to-clipboard feature.
Also applies to: 7-7
infrastructure/eid-wallet/src/routes/(auth)/verify/steps/selfie.svelte (1)
76-91: LGTM! Back navigation implementation is clean.The back button integration follows the consistent pattern used across verification steps, and the layout restructuring maintains readability while adding the navigation control.
infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte (1)
404-405: Good use of conditionalfullScreenfor camera-based verification steps.Enabling fullscreen mode for passport (step 1) and selfie (step 2) capture provides better UX for camera views.
infrastructure/eid-wallet/src/routes/(auth)/verify/steps/passport.svelte (1)
228-256: LGTM! Consistent back navigation implementation.The back button correctly navigates to step 0 (document type selection), and the layout restructuring aligns with the pattern in
selfie.svelte.infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.svelte (1)
150-183: Good approach using static CSS with class-based toggling.The styling correctly addresses the previous feedback about dynamic expressions in style blocks. The fallback selector
:not(.drawer-fullscreen)provides reasonable safety for the default case.
| <Button.Icon | ||
| icon={Copy01Icon} | ||
| iconColor={"white"} | ||
| strokeWidth={2} | ||
| onclick={async () => { | ||
| if (userId) { | ||
| try { | ||
| await navigator.clipboard.writeText(userId); | ||
| toast.success("eName copied to clipboard"); | ||
| } catch (error) { | ||
| console.error("Failed to copy:", error); | ||
| toast.error("Failed to copy eName"); | ||
| } | ||
| } | ||
| }} | ||
| /> |
There was a problem hiding this comment.
Add an accessible label or tooltip for the copy button.
The copy-to-clipboard functionality works correctly with proper error handling and toast notifications. However, the button lacks an accessible label or tooltip, which may leave users uncertain about its purpose.
Consider adding an aria-label or title attribute to improve accessibility:
<Button.Icon
icon={Copy01Icon}
iconColor={"white"}
strokeWidth={2}
+ title="Copy eName to clipboard"
onclick={async () => {Optional: Consider adding brief visual feedback on successful copy.
For enhanced UX, you could temporarily change the icon or add a visual indicator when the copy succeeds, in addition to the toast notification.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <Button.Icon | |
| icon={Copy01Icon} | |
| iconColor={"white"} | |
| strokeWidth={2} | |
| onclick={async () => { | |
| if (userId) { | |
| try { | |
| await navigator.clipboard.writeText(userId); | |
| toast.success("eName copied to clipboard"); | |
| } catch (error) { | |
| console.error("Failed to copy:", error); | |
| toast.error("Failed to copy eName"); | |
| } | |
| } | |
| }} | |
| /> | |
| <Button.Icon | |
| icon={Copy01Icon} | |
| iconColor={"white"} | |
| strokeWidth={2} | |
| title="Copy eName to clipboard" | |
| onclick={async () => { | |
| if (userId) { | |
| try { | |
| await navigator.clipboard.writeText(userId); | |
| toast.success("eName copied to clipboard"); | |
| } catch (error) { | |
| console.error("Failed to copy:", error); | |
| toast.error("Failed to copy eName"); | |
| } | |
| } | |
| }} | |
| /> |
🤖 Prompt for AI Agents
in infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.svelte
around lines 70 to 85, the Copy button is missing an accessible label/tooltip;
add an aria-label (e.g., aria-label="Copy eName to clipboard") or a title prop
on the Button.Icon component so screen readers and hover users know its purpose,
and optionally add a short local state flash (e.g., set a transient "copied"
boolean for 1–2s to swap the icon or add a visual indicator) triggered on
successful copy while keeping the existing toast and error handling.
| const paneElement = document.querySelector(".pane") as HTMLElement; | ||
| if (paneElement) { |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
CupertinoPane cssClass option element reference API documentation
💡 Result:
The cssClass option (type: string) adds one or more class names to the pane wrapper so you can style the pane instance. It is documented as "Additional classes to apply for wrapper" (i.e., the .cupertino-pane wrapper element). Use it in the settings passed to the constructor:
Example:
new CupertinoPane('.cupertino-pane', { cssClass: 'my-pane-theme another-class' });
Sources: panejs Settings documentation [1], changelog / notes about cssClass applying to .cupertino-pane-wrapper [2].
References
[1] panejs Settings — cssClass option.
[2] Release notes / changelog mentioning cssClass applied to .cupertino-pane-wrapper.
🏁 Script executed:
cd infrastructure/eid-wallet && cat -n src/lib/ui/Drawer/Drawer.svelte | head -150Repository: MetaState-Prototype-Project/prototype
Length of output: 5500
🌐 Web query:
CupertinoPane API element reference pane.el pane.element how to get pane container
💡 Result:
Use the instance's paneEl property. The constructor accepts a selector or HTMLElement, and the created CupertinoPane exposes the DOM container as myPane.paneEl.
Example:
- Create: const pane = new CupertinoPane('.cupertino-pane');
- Get container element: const container = pane.paneEl;
Sources: Cupertino Pane docs (constructor accepts selector/element). [1] Internal source shows event handlers attach to instance.paneEl. [2]
References
[1] Getting Started — Cupertino Panes documentation.
[2] cupertino-pane source (events.ts) on UNPKG (uses instance.paneEl).
🏁 Script executed:
# Search for any usage of the pane object to see how it's used in the codebase
cd infrastructure/eid-wallet && rg "pane\." src/lib/ui/Drawer/Drawer.svelte -A 2 -B 2Repository: MetaState-Prototype-Project/prototype
Length of output: 532
Replace global DOM queries with CupertinoPane's element reference.
Using document.querySelector(".pane") selects the first .pane in the document, breaking when multiple Drawer instances render simultaneously. CupertinoPane exposes the container element via the paneEl property—use pane.paneEl instead of the global query.
Update lines 87-88, 107-108, and 122-123 to replace document.querySelector(".pane") with pane.paneEl.
🤖 Prompt for AI Agents
in infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.svelte around lines 87-88,
107-108 and 122-123, the code uses document.querySelector(".pane") which can
return the wrong element when multiple Drawer instances exist; replace those
global DOM queries with the CupertinoPane instance's paneEl property
(pane.paneEl) and cast/guard it as an HTMLElement where needed so each Drawer
uses its own container element instead of the first .pane in the document.
| // Provide showVeriffModal context to child components | ||
| setContext("showVeriffModal", { | ||
| get value() { | ||
| return showVeriffModal; | ||
| }, | ||
| set value(v: boolean) { | ||
| showVeriffModal = v; | ||
| }, | ||
| }); |
There was a problem hiding this comment.
setContext cannot be called inside onMount — this will throw a runtime error.
In Svelte 5, setContext must be called synchronously during component initialization, not inside lifecycle callbacks. Move this call outside of onMount:
+// Provide showVeriffModal context to child components
+setContext("showVeriffModal", {
+ get value() {
+ return showVeriffModal;
+ },
+ set value(v: boolean) {
+ showVeriffModal = v;
+ },
+});
+
onMount(async () => {
globalState = getContext<() => GlobalState>("globalState")();
// handle verification logic + sec user data in the store
- // Provide showVeriffModal context to child components
- setContext("showVeriffModal", {
- get value() {
- return showVeriffModal;
- },
- set value(v: boolean) {
- showVeriffModal = v;
- },
- });
-
// Check hardware key support first
await checkHardwareKeySupport();🤖 Prompt for AI Agents
in infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte around lines
256-264, the setContext call is currently inside onMount which throws at runtime
in Svelte 5; move the entire setContext invocation out of onMount so it runs
synchronously during component initialization, keeping the same context object
shape (getter/setter or reactive store) and accessors, and if any values depend
on mount-time data, initialize placeholders synchronously and update them inside
onMount instead.
There was a problem hiding this comment.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.svelte (1)
51-80: Replacepane?.destroy()withpane?.hide()in the backdrop handler to keep the pane instance reusableThe separate open/close effect (lines 82–94) uses
present()to show the pane andhide()to close it, expecting the pane instance to survive multiple opens and closes. However,onBackdropTapcallspane?.destroy(), which removes the instance from the DOM. WhenisPaneOpenis next toggled totrue, callingpresent()on a destroyed instance will fail or silently do nothing.Use
hide()instead to close the UI while keeping the instance alive for reuse:events: { onBackdropTap: () => { - pane?.destroy(); + pane?.hide(); isPaneOpen = false; }, },This preserves the "hide on close, destroy on unmount" semantics, with the cleanup function (lines 75–79) handling final disposal on component unmount.
♻️ Duplicate comments (2)
infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.svelte (1)
70-85: Add an accessible label to the copy button.As previously noted, the button lacks an
aria-labelortitleattribute to indicate its purpose to users and screen readers.<Button.Icon icon={Copy01Icon} iconColor={"white"} strokeWidth={2} + aria-label="Copy eName to clipboard" onclick={async () => {infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte (1)
263-284:setContextcannot be called insideonMount—this will throw at runtime in Svelte 5.As previously flagged,
setContextmust be called synchronously during component initialization. Move bothsetContextcalls outside ofonMount:let handleContinue: () => Promise<void> = $state(async () => {}); +// Provide showVeriffModal context to child components +setContext("showVeriffModal", { + get value() { + return showVeriffModal; + }, + set value(v: boolean) { + showVeriffModal = v; + }, +}); + +// Provide verification data context for selfie page +setContext("verifyData", { + get person() { + return person; + }, + get document() { + return document; + }, + get websocketData() { + return websocketData; + }, +}); + onMount(async () => { globalState = getContext<() => GlobalState>("globalState")(); // handle verification logic + sec user data in the store - // Provide showVeriffModal context to child components - setContext("showVeriffModal", { - ... - }); - - // Provide verification data context for selfie page - setContext("verifyData", { - ... - }); - // Check hardware key support first await checkHardwareKeySupport();
🧹 Nitpick comments (7)
infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte (1)
144-144: Consider externalizing the version string.The version is hardcoded in the template. To maintain consistency and reduce manual updates, consider importing it from
package.jsonor a configuration file.For example, in the script section:
import packageJson from '../../../../package.json'; const version = packageJson.version;Then in the template:
- Version v0.5.0.0 + Version v{version}infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte (1)
101-105: Consider keeping cancel handlers sync or widening drawer callback typesAll three cancel handlers are marked
asyncsolely toawait goto("/main"), and their callbacks are passed into drawers as props. If those props are still typed as() => void, returningPromise<void>can cause TypeScript friction and the awaited result is not otherwise used. You could either:
- keep the handlers synchronous and call
void goto("/main"), or- update the drawer prop types to accept
() => void | Promise<void>if you explicitly want async handlers.This is non‑blocking but would make the signatures and typing clearer; a quick TS/Svelte typecheck of the drawer components should confirm everything is consistent.
Also applies to: 122-126, 139-142
infrastructure/eid-wallet/src/routes/(auth)/verify/passport/+page.svelte (1)
5-7: Consider using Tailwind classes instead of inline styles.For consistency with the project's styling approach, consider replacing inline
stylewith Tailwind utility classes:-<div class="fixed inset-0 bg-white z-50 overflow-y-auto" style="padding-top: 70px; padding-left: 24px; padding-right: 24px;"> +<div class="fixed inset-0 bg-white z-50 overflow-y-auto pt-[70px] px-6">infrastructure/eid-wallet/src/routes/(auth)/verify/selfie/+page.svelte (1)
5-7: Consider using Tailwind classes instead of inline styles.Same suggestion as for
passport/+page.svelte—use Tailwind utilities for consistency:-<div class="fixed inset-0 bg-white z-50 overflow-y-auto" style="padding-top: 70px; padding-left: 24px; padding-right: 24px;"> +<div class="fixed inset-0 bg-white z-50 overflow-y-auto pt-[70px] px-6">infrastructure/eid-wallet/src/routes/(auth)/verify/store.ts (1)
14-16: Avoidanytypes—define or reuse proper interfaces.The
PersonandDocumenttypes are already defined in+page.svelte. Extract them to a shared types file and use them here for type safety:-export const verificationPerson = writable<any>(null); -export const verificationDocument = writable<any>(null); +export const verificationPerson = writable<Person | null>(null); +export const verificationDocument = writable<Document | null>(null);This prevents silent type errors and improves IDE support across components consuming these stores.
infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte (1)
167-173: Consider a single source of truth for verification data.The SSE data is stored in both local variables (
person,document,websocketData) and writable stores (verificationPerson, etc.). While currently synchronized, this dual storage could lead to drift if updated independently elsewhere. Consider using only the stores and deriving local reads from them:$: person = $verificationPerson; $: document = $verificationDocument;Or remove local variables entirely and use stores throughout.
infrastructure/eid-wallet/src/routes/(auth)/verify/steps/selfie.svelte (1)
195-215: ReusestopCamera()and use idiomatic Svelte event binding for the back buttonThe “go back” button currently:
- Duplicates the track‑stopping loop already encapsulated in
stopCamera().- Uses
onclick={…}rather than Svelte’son:click={…}pattern, which is more idiomatic and consistent with the rest of the codebase.You can simplify and centralize the camera shutdown logic like this:
- <button - onclick={() => { - if (stream) { - for (const track of stream.getTracks()) { - track.stop(); - } - } - goto("/verify/passport"); - }} + <button + on:click={() => { + stopCamera(); + goto("/verify/passport"); + }} class="cursor-pointer self-start flex items-center gap-2 text-sm text-gray-600 hover:text-gray-800" >This keeps camera cleanup logic in one place and aligns with standard Svelte event syntax.
Please ensure
stopCamera()is safe to call whenstreamis undefined or already stopped (current implementation guards onstream, so this refactor should be behavior‑preserving).
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
infrastructure/eid-wallet/package.json(2 hunks)infrastructure/eid-wallet/src-tauri/tauri.conf.json(3 hunks)infrastructure/eid-wallet/src/lib/fragments/IdentityCard/IdentityCard.svelte(2 hunks)infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.svelte(3 hunks)infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte(3 hunks)infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte(2 hunks)infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte(5 hunks)infrastructure/eid-wallet/src/routes/(auth)/verify/passport/+page.svelte(1 hunks)infrastructure/eid-wallet/src/routes/(auth)/verify/selfie/+page.svelte(1 hunks)infrastructure/eid-wallet/src/routes/(auth)/verify/steps/document-type.svelte(1 hunks)infrastructure/eid-wallet/src/routes/(auth)/verify/steps/passport.svelte(6 hunks)infrastructure/eid-wallet/src/routes/(auth)/verify/steps/selfie.svelte(4 hunks)infrastructure/eid-wallet/src/routes/(auth)/verify/store.ts(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- infrastructure/eid-wallet/src/routes/(auth)/verify/steps/document-type.svelte
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (10)
infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte (4)
1-20: LGTM!The imports are well-organized and the context setup follows standard Svelte patterns.
22-30: LGTM!State declarations are well-structured for both the delete confirmation flow and the hidden tap functionality.
55-57: LGTM!Proper cleanup of the timeout on component unmount prevents potential memory leaks.
110-112: LGTM!The effect hook properly sets the page title for the settings view.
infrastructure/eid-wallet/src/routes/(app)/scan-qr/+page.svelte (2)
101-105: Cancel on Auth/Signing drawers now always redirects to/mainClosing the drawer and hard‑navigating to
/mainmakes the cancel path uniform and guarantees the page’sonDestroycleanup runs. Please double‑check with product/UX that this is desired even when the scan flow was initiated from a route other than the main dashboard, as it changes any previous “go back to where I came from” behavior.Also applies to: 122-126
139-142: Reveal cancel now goes to/maininstead of usinghistory.back()Switching from
window.history.back()togoto("/main")simplifies navigation and matches the new dashboard‑centric cancel behavior. Confirm this doesn’t regress flows where reveal might be deep‑linked from non‑main routes (e.g. you truly always want to land on the dashboard after cancelling reveal).infrastructure/eid-wallet/package.json (1)
3-3: Version bump looks correct.The version increment from 0.4.0 to 0.5.0 aligns with the corresponding update in
tauri.conf.json, maintaining consistency across the project configuration.infrastructure/eid-wallet/src-tauri/tauri.conf.json (1)
4-4: Version updates are consistent.The version bump to 0.5.0 matches
package.json. The AndroidversionCodeincrement from 12 to 20 is a notable jump—ensure this is intentional and won't cause issues with staged rollouts or version tracking in the Play Store.Also applies to: 31-31
infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.svelte (1)
108-116: Static global.panestyling looks coherent with the new lifecycleThe consolidated positioning/background/overflow rules on
:global(.pane)align with the present()/hide() lifecycle and should give a stable, centered bottom‑sheet across uses. No functional issues from this change.infrastructure/eid-wallet/src/routes/(auth)/verify/steps/selfie.svelte (1)
22-24: Selfie capture → loading → results state flow looks consistentThe combination of:
$statevariables (showResults,loading),- SSE‑driven
$status/$verifStepwatcher to flipshowResults,- camera lifecycle (
requestCameraPermissionon mount andstopCamerain both capture and unmount), and- the
{#if !load && !showResults} / {:else if load && !showResults} / {:else if showResults}branchesforms a clear state machine: capture → verification spinner → results + continue/claim/retry button. Transitions appear internally coherent and should avoid camera leaks or stuck spinners under normal flows.
Also applies to: 48-53, 55-62, 64-97, 256-317
| async function cancelDelete() { | ||
| isDeleteConfirmationOpen = false; | ||
| isFinalConfirmationOpen = false; | ||
| await goto("/main"); | ||
| } |
There was a problem hiding this comment.
Unexpected navigation on cancel.
When a user clicks "Cancel" on a delete confirmation dialog, they expect to remain on the settings page, not be navigated away to /main. This creates a confusing user experience.
Consider removing the navigation:
async function cancelDelete() {
isDeleteConfirmationOpen = false;
isFinalConfirmationOpen = false;
- await goto("/main");
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async function cancelDelete() { | |
| isDeleteConfirmationOpen = false; | |
| isFinalConfirmationOpen = false; | |
| await goto("/main"); | |
| } | |
| async function cancelDelete() { | |
| isDeleteConfirmationOpen = false; | |
| isFinalConfirmationOpen = false; | |
| } |
🤖 Prompt for AI Agents
in infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte around lines
48-52, the cancelDelete function currently closes the dialogs then navigates to
"/main", which causes unexpected navigation; remove the navigation call (delete
the await goto("/main") line) so cancelDelete only sets isDeleteConfirmationOpen
and isFinalConfirmationOpen to false, and ensure any focus or state reset needed
on the settings page is performed after closing dialogs rather than navigating
away.
| async function handleVersionTap() { | ||
| const now = Date.now(); | ||
|
|
||
| // Reset if more than 3s between taps | ||
| if (now - lastTapTime > 3000) { | ||
| tapCount = 0; | ||
| } | ||
|
|
||
| tapCount++; | ||
| lastTapTime = now; | ||
|
|
||
| // Show feedback after 5 taps | ||
| if (tapCount >= 5) { | ||
| retryMessage = `Taps: ${tapCount}/10`; | ||
| } | ||
|
|
||
| // Trigger hidden action at 10 taps | ||
| if (tapCount === 10) { | ||
| isRetrying = true; | ||
| retryMessage = "Retrying eVault profile setup..."; | ||
|
|
||
| try { | ||
| await globalState.vaultController.retryProfileCreation(); | ||
| retryMessage = | ||
| "✅ eVault profile setup completed successfully!"; | ||
|
|
||
| // Clear previous timeout if exists | ||
| if (timeoutId) clearTimeout(timeoutId); | ||
|
|
||
| timeoutId = setTimeout(() => { | ||
| tapCount = 0; | ||
| retryMessage = ""; | ||
| isRetrying = false; | ||
| }, 3000); | ||
| } catch (error) { | ||
| console.error("Failed to retry eVault profile setup:", error); | ||
| retryMessage = | ||
| "❌ Failed to setup eVault profile. Check console for details."; | ||
|
|
||
| // Clear previous timeout if exists | ||
| if (timeoutId) clearTimeout(timeoutId); | ||
|
|
||
| timeoutId = setTimeout(() => { | ||
| tapCount = 0; | ||
| retryMessage = ""; | ||
| isRetrying = false; | ||
| }, 5000); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Add null check for vaultController.
Before calling retryProfileCreation(), there's no validation that vaultController exists on the globalState object. This could result in a runtime error if the controller is not initialized.
Add a guard to validate the controller exists:
// Trigger hidden action at 10 taps
if (tapCount === 10) {
+ if (!globalState.vaultController) {
+ retryMessage = "❌ Vault controller not available.";
+ setTimeout(() => {
+ tapCount = 0;
+ retryMessage = "";
+ }, 3000);
+ return;
+ }
+
isRetrying = true;
retryMessage = "Retrying eVault profile setup...";📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| async function handleVersionTap() { | |
| const now = Date.now(); | |
| // Reset if more than 3s between taps | |
| if (now - lastTapTime > 3000) { | |
| tapCount = 0; | |
| } | |
| tapCount++; | |
| lastTapTime = now; | |
| // Show feedback after 5 taps | |
| if (tapCount >= 5) { | |
| retryMessage = `Taps: ${tapCount}/10`; | |
| } | |
| // Trigger hidden action at 10 taps | |
| if (tapCount === 10) { | |
| isRetrying = true; | |
| retryMessage = "Retrying eVault profile setup..."; | |
| try { | |
| await globalState.vaultController.retryProfileCreation(); | |
| retryMessage = | |
| "✅ eVault profile setup completed successfully!"; | |
| // Clear previous timeout if exists | |
| if (timeoutId) clearTimeout(timeoutId); | |
| timeoutId = setTimeout(() => { | |
| tapCount = 0; | |
| retryMessage = ""; | |
| isRetrying = false; | |
| }, 3000); | |
| } catch (error) { | |
| console.error("Failed to retry eVault profile setup:", error); | |
| retryMessage = | |
| "❌ Failed to setup eVault profile. Check console for details."; | |
| // Clear previous timeout if exists | |
| if (timeoutId) clearTimeout(timeoutId); | |
| timeoutId = setTimeout(() => { | |
| tapCount = 0; | |
| retryMessage = ""; | |
| isRetrying = false; | |
| }, 5000); | |
| } | |
| } | |
| } | |
| async function handleVersionTap() { | |
| const now = Date.now(); | |
| // Reset if more than 3s between taps | |
| if (now - lastTapTime > 3000) { | |
| tapCount = 0; | |
| } | |
| tapCount++; | |
| lastTapTime = now; | |
| // Show feedback after 5 taps | |
| if (tapCount >= 5) { | |
| retryMessage = `Taps: ${tapCount}/10`; | |
| } | |
| // Trigger hidden action at 10 taps | |
| if (tapCount === 10) { | |
| if (!globalState.vaultController) { | |
| retryMessage = "❌ Vault controller not available."; | |
| setTimeout(() => { | |
| tapCount = 0; | |
| retryMessage = ""; | |
| }, 3000); | |
| return; | |
| } | |
| isRetrying = true; | |
| retryMessage = "Retrying eVault profile setup..."; | |
| try { | |
| await globalState.vaultController.retryProfileCreation(); | |
| retryMessage = | |
| "✅ eVault profile setup completed successfully!"; | |
| // Clear previous timeout if exists | |
| if (timeoutId) clearTimeout(timeoutId); | |
| timeoutId = setTimeout(() => { | |
| tapCount = 0; | |
| retryMessage = ""; | |
| isRetrying = false; | |
| }, 3000); | |
| } catch (error) { | |
| console.error("Failed to retry eVault profile setup:", error); | |
| retryMessage = | |
| "❌ Failed to setup eVault profile. Check console for details."; | |
| // Clear previous timeout if exists | |
| if (timeoutId) clearTimeout(timeoutId); | |
| timeoutId = setTimeout(() => { | |
| tapCount = 0; | |
| retryMessage = ""; | |
| isRetrying = false; | |
| }, 5000); | |
| } | |
| } | |
| } |
🤖 Prompt for AI Agents
In infrastructure/eid-wallet/src/routes/(app)/settings/+page.svelte around lines
59-108, add a null/undefined guard for globalState.vaultController before
calling retryProfileCreation to avoid runtime errors; if the controller is
missing, set an appropriate retryMessage, reset isRetrying and tapCount,
optionally console.error the missing controller, clear any existing timeout, and
return early; otherwise proceed to await retryProfileCreation as existing code
does.
| <button | ||
| onclick={() => { | ||
| stopCamera(); | ||
| goto("/verify"); | ||
| }} | ||
| class="cursor-pointer self-start flex items-center gap-2 text-sm text-gray-600 hover:text-gray-800" | ||
| > | ||
| <Button.Icon | ||
| icon={ArrowLeft01Icon} | ||
| iconColor="currentColor" | ||
| strokeWidth={2} | ||
| class="w-4 h-4" | ||
| /> | ||
| <span>go back</span> | ||
| </button> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# Find Button component definition
fd -t f -i "button" --extension svelte | head -20Repository: MetaState-Prototype-Project/prototype
Length of output: 1108
🏁 Script executed:
# Search for Button.Icon component in the codebase
rg -t svelte "Button\.Icon|export.*Icon" --max-count=20Repository: MetaState-Prototype-Project/prototype
Length of output: 111
🏁 Script executed:
# Look for Button component in common locations
git ls-files | grep -i button | grep -E "\.(svelte|ts|js)$"Repository: MetaState-Prototype-Project/prototype
Length of output: 1777
🏁 Script executed:
cat infrastructure/eid-wallet/src/lib/ui/Button/ButtonIcon.svelteRepository: MetaState-Prototype-Project/prototype
Length of output: 5727
🏁 Script executed:
# Verify how the back button is actually used in passport.svelte and see if there's text content
cat -n infrastructure/eid-wallet/src/routes/\(auth\)/verify/steps/passport.svelte | sed -n '233,247p'Repository: MetaState-Prototype-Project/prototype
Length of output: 845
Remove nested button elements.
Button.Icon renders as a <button> element, creating nested interactive elements when wrapped in another <button>. This is invalid HTML and causes accessibility issues.
The outer <button> wrapper should be removed and the onclick handler and styling applied directly to Button.Icon. However, since Button.Icon doesn't render text children, you'll need to either:
- Use
Button.Iconalone without the "go back" text:
-<button
- onclick={() => {
- stopCamera();
- goto("/verify");
- }}
- class="cursor-pointer self-start flex items-center gap-2 text-sm text-gray-600 hover:text-gray-800"
->
- <Button.Icon
- icon={ArrowLeft01Icon}
- iconColor="currentColor"
- strokeWidth={2}
- class="w-4 h-4"
- />
- <span>go back</span>
-</button>
+<Button.Icon
+ icon={ArrowLeft01Icon}
+ iconColor="currentColor"
+ strokeWidth={2}
+ class="w-4 h-4 cursor-pointer self-start"
+ onclick={() => {
+ stopCamera();
+ goto("/verify");
+ }}
+/>- Or restructure using a
<div>or anchor element that groups the icon and text without creating nested buttons.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
in infrastructure/eid-wallet/src/routes/(auth)/verify/steps/passport.svelte
around lines 233-247, the current markup nests Button.Icon (which renders a
<button>) inside another <button>; remove the outer <button> to avoid nested
interactive elements and instead wrap the icon and text in a non-button
container (e.g., <div> or <a>) or move the click handler onto Button.Icon and
keep the "go back" text as a sibling element outside the inner button; ensure
the click handler still calls stopCamera() and navigates to "/verify", preserve
the existing classes on the new wrapper or apply them to Button.Icon, and make
the wrapper accessible by adding role="button", tabindex="0" and keyboard
handlers for Enter/Space if you choose a non-anchor element.
| async function getApplicationPublicKey(): Promise<string> { | ||
| if (!globalState) throw new Error("Global state is not defined"); | ||
| const keyManager = globalState.keyService.getKeyManagerForContext("verification"); | ||
| const publicKey = await keyManager.getPublicKey("default"); | ||
| return publicKey; | ||
| } | ||
|
|
||
| async function handleContinue() { | ||
| if ($status !== "approved" && $status !== "duplicate") { | ||
| return verifStep.set(0); | ||
| } | ||
| if (!globalState) throw new Error("Global state is not defined"); | ||
| if (!$verificationPerson || !$verificationDocument) { | ||
| console.error("Missing verification data"); | ||
| return; | ||
| } | ||
|
|
||
| loading = true; | ||
|
|
||
| try { | ||
| globalState.userController.user = { | ||
| name: capitalize( | ||
| `${$verificationPerson.firstName.value} ${$verificationPerson.lastName.value ?? ""}`, | ||
| ), | ||
| "Date of Birth": new Date($verificationPerson.dateOfBirth.value).toDateString(), | ||
| "ID submitted": | ||
| $verificationDocument.type.value === "passport" | ||
| ? `Passport - ${$verificationDocument.country.value}` | ||
| : $verificationDocument.type.value === "drivers_license" | ||
| ? `Driving License - ${$verificationDocument.country.value}` | ||
| : `ID Card - ${$verificationDocument.country.value}`, | ||
| "Document Number": $verificationDocument.number.value, | ||
| }; | ||
| globalState.userController.document = { | ||
| "Valid From": new Date($verificationDocument.validFrom.value).toDateString(), | ||
| "Valid Until": new Date($verificationDocument.validUntil.value).toDateString(), | ||
| "Verified On": new Date().toDateString(), | ||
| }; | ||
| globalState.userController.isFake = false; | ||
|
|
||
| if ($status === "duplicate") { | ||
| // For duplicate case, skip provision and resolve the existing eVault URI | ||
| const existingW3id = $verificationWebsocketData?.w3id; | ||
| if (!existingW3id) { | ||
| throw new Error("No w3id provided for duplicate eVault"); | ||
| } | ||
|
|
||
| // Resolve the eVault URI from the registry | ||
| const response = await axios.get( | ||
| new URL( | ||
| `resolve?w3id=${existingW3id}`, | ||
| PUBLIC_REGISTRY_URL, | ||
| ).toString(), | ||
| ); | ||
| // Skip profile creation for duplicates by setting status directly | ||
| globalState.vaultController.profileCreationStatus = "success"; | ||
| // For duplicates, just set the vault without triggering profile creation | ||
| globalState.vaultController.vault = { | ||
| uri: response.data.uri, | ||
| ename: existingW3id, | ||
| }; | ||
| } else { | ||
| // Normal flow for approved status | ||
| const { | ||
| data: { token: registryEntropy }, | ||
| } = await axios.get( | ||
| new URL("/entropy", PUBLIC_REGISTRY_URL).toString(), | ||
| ); | ||
| const { data } = await axios.post( | ||
| new URL("/provision", PUBLIC_PROVISIONER_URL).toString(), | ||
| { | ||
| registryEntropy, | ||
| namespace: uuidv4(), | ||
| verificationId: $verificaitonId, | ||
| publicKey: await getApplicationPublicKey(), | ||
| }, | ||
| ); | ||
| if (data.success === true) { | ||
| // Set vault in controller - this will trigger profile creation with retry logic | ||
| globalState.vaultController.vault = { | ||
| uri: data.uri, | ||
| ename: data.w3id, | ||
| }; | ||
| } | ||
| } | ||
|
|
||
| // Go straight to register WITHOUT the 10-second delay | ||
| goto("/register"); | ||
| } catch (error) { | ||
| console.error("Failed to provision:", error); | ||
| loading = false; | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find the file and understand the current implementation
cd infrastructure/eid-wallet && find . -name "selfie.svelte" -type fRepository: MetaState-Prototype-Project/prototype
Length of output: 127
🏁 Script executed:
# Search for API endpoint definitions and contracts
fd -e ts -e js -e svelte . | xargs grep -l "entropy\|provision\|resolve" | head -20Repository: MetaState-Prototype-Project/prototype
Length of output: 1415
🏁 Script executed:
# Look for backend code or services that handle these endpoints
fd -type d -name "*api*" -o -name "*services*" -o -name "*backend*" | head -10Repository: MetaState-Prototype-Project/prototype
Length of output: 251
🏁 Script executed:
# Check for tests that might reveal the API contracts
fd -e test.ts -e test.js -e spec.ts -o -name "*.test.*" -o -name "*.spec.*" | xargs grep -l "provision\|entropy\|resolve" 2>/dev/null | head -10Repository: MetaState-Prototype-Project/prototype
Length of output: 311
🏁 Script executed:
# Search for PUBLIC_REGISTRY_URL and PUBLIC_PROVISIONER_URL definitions
grep -r "PUBLIC_REGISTRY_URL\|PUBLIC_PROVISIONER_URL" infrastructure/eid-wallet/src --include="*.ts" --include="*.js" --include="*.svelte"Repository: MetaState-Prototype-Project/prototype
Length of output: 3474
🏁 Script executed:
# Look for provision endpoint implementation
grep -r "provision" infrastructure --include="*.ts" --include="*.js" -l | grep -E "(routes|api|server)" | head -10Repository: MetaState-Prototype-Project/prototype
Length of output: 199
🏁 Script executed:
# Look for entropy endpoint implementation
grep -r "entropy" infrastructure --include="*.ts" --include="*.js" -l | grep -E "(routes|api|server)" | head -10Repository: MetaState-Prototype-Project/prototype
Length of output: 63
🏁 Script executed:
# Look for resolve endpoint in registry
grep -r "resolve" infrastructure --include="*.ts" --include="*.js" -l | grep -E "(routes|api|server|registry)" | head -10Repository: MetaState-Prototype-Project/prototype
Length of output: 531
🏁 Script executed:
# Examine the evault-core server for endpoint implementations
cat -n infrastructure/evault-core/src/core/http/server.ts | head -100Repository: MetaState-Prototype-Project/prototype
Length of output: 4034
🏁 Script executed:
# Check mock registry server for response contracts
cat -n infrastructure/evault-core/src/test-utils/mock-registry-server.tsRepository: MetaState-Prototype-Project/prototype
Length of output: 3805
🏁 Script executed:
# Look for /provision endpoint specifically
grep -r "provision" infrastructure/evault-core/src --include="*.ts" -A 5 -B 2 | head -60Repository: MetaState-Prototype-Project/prototype
Length of output: 5219
🏁 Script executed:
# Check the evault controller for how it handles responses
grep -r "success" infrastructure/eid-wallet/src/lib/global/controllers/evault.ts -B 3 -A 3Repository: MetaState-Prototype-Project/prototype
Length of output: 3348
🏁 Script executed:
# Look at the full /provision endpoint implementation in server.ts
grep -n "provision" infrastructure/evault-core/src/core/http/server.ts | head -20Repository: MetaState-Prototype-Project/prototype
Length of output: 435
🏁 Script executed:
# Find and view the /provision endpoint full implementation
sed -n '200,400p' infrastructure/evault-core/src/core/http/server.tsRepository: MetaState-Prototype-Project/prototype
Length of output: 6283
🏁 Script executed:
# Search for /entropy endpoint
grep -n "entropy" infrastructure --include="*.ts" --include="*.js" -rRepository: MetaState-Prototype-Project/prototype
Length of output: 5371
🏁 Script executed:
# Check ProvisioningService to understand response structure
find infrastructure -name "ProvisioningService*" -type fRepository: MetaState-Prototype-Project/prototype
Length of output: 292
🏁 Script executed:
# Find the /entropy endpoint implementation
grep -n "/entropy" infrastructure/evault-core/src/core/http/server.ts -A 30 | head -50Repository: MetaState-Prototype-Project/prototype
Length of output: 63
🏁 Script executed:
# Look at the full /provision endpoint response
sed -n '512,650p' infrastructure/evault-core/src/core/http/server.tsRepository: MetaState-Prototype-Project/prototype
Length of output: 5046
🏁 Script executed:
# Check ProvisioningService implementation for response structure
cat -n infrastructure/evault-core/src/services/ProvisioningService.ts | head -200Repository: MetaState-Prototype-Project/prototype
Length of output: 8881
🏁 Script executed:
# Look for where registry entropy endpoint is defined
find infrastructure -type f \( -name "*.ts" -o -name "*.js" \) -exec grep -l "\/entropy" {} \; | grep -E "(registry|server|routes)" | head -5Repository: MetaState-Prototype-Project/prototype
Length of output: 63
🏁 Script executed:
# Check the rest of ProvisioningService error handling
sed -n '200,250p' infrastructure/evault-core/src/services/ProvisioningService.tsRepository: MetaState-Prototype-Project/prototype
Length of output: 409
🏁 Script executed:
# Look for /entropy endpoint in registry or other services
find infrastructure -type f \( -name "*.ts" -o -name "*.js" \) -exec grep -l "get.*entropy\|\/entropy" {} \; | head -10Repository: MetaState-Prototype-Project/prototype
Length of output: 121
🏁 Script executed:
# Verify the exact response structure by checking how selfie.svelte uses response.data
grep -n "response.data" infrastructure/eid-wallet/src/routes/\(auth\)/verify/steps/selfie.svelte -B 2 -A 2Repository: MetaState-Prototype-Project/prototype
Length of output: 328
🏁 Script executed:
# Get the complete handleContinue function to see the full flow
sed -n '99,191p' infrastructure/eid-wallet/src/routes/\(auth\)/verify/steps/selfie.svelteRepository: MetaState-Prototype-Project/prototype
Length of output: 3957
Only navigate to /register once vault resolution/provision has actually succeeded
In handleContinue, goto("/register") is executed unconditionally at the end of the try block:
- For the "approved" path, if
/entropyor/provisionfails, or ifdata.success !== true, the vault is never set but navigation still occurs, leaving the downstream registration flow in a broken state. - For the "duplicate" path, if the registry
resolvecall returns an unexpected response (e.g., missingurifield), vault setup fails silently but navigation proceeds.
This makes failures difficult to detect in the UI and breaks downstream flows that assume a populated vault.
Consider gating navigation on confirmed success and clearing loading on failure, for example:
async function handleContinue() {
- loading = true;
+ loading = true;
try {
+ let hasVault = false;
+
if ($status === "duplicate") {
const response = await axios.get(
new URL(
`resolve?w3id=${existingW3id}`,
PUBLIC_REGISTRY_URL,
).toString(),
);
globalState.vaultController.profileCreationStatus = "success";
globalState.vaultController.vault = {
uri: response.data.uri,
ename: existingW3id,
};
+ hasVault = true;
} else {
const {
data: { token: registryEntropy },
} = await axios.get(
new URL("/entropy", PUBLIC_REGISTRY_URL).toString(),
);
const { data } = await axios.post(
new URL("/provision", PUBLIC_PROVISIONER_URL).toString(),
{
registryEntropy,
namespace: uuidv4(),
verificationId: $verificaitonId,
publicKey: await getApplicationPublicKey(),
},
);
if (data.success === true) {
globalState.vaultController.vault = {
uri: data.uri,
ename: data.w3id,
};
+ hasVault = true;
}
}
- // Go straight to register WITHOUT the 10-second delay
- goto("/register");
+ // Only navigate when vault is confirmed
+ if (hasVault) {
+ goto("/register");
+ } else {
+ loading = false;
+ }
} catch (error) {
console.error("Failed to provision:", error);
loading = false;
}
}This keeps the user on the results screen when provisioning or resolution fails instead of navigating into a broken registration state.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
in infrastructure/eid-wallet/src/routes/(auth)/verify/steps/selfie.svelte around
lines 99 to 191, the code unconditionally calls goto("/register") even if vault
resolution/provision failed; change the control flow so navigation only happens
after confirming the vault was successfully set: validate the registry resolve
response contains response.data.uri before assigning vault for the duplicate
branch and abort/throw (or return) with loading = false on missing data; for the
approved branch confirm the POST /provision returned data.success === true and
data.uri/data.w3id exist before assigning vault and only then call
goto("/register"); ensure any failure paths set loading = false and do not
navigate so the user remains on the result screen for retry or error display.
Description of change
Issue Number
Type of change
How the change has been tested
Change checklist
Summary by CodeRabbit
New Features
Bug Fixes
UI/UX
Chores
✏️ Tip: You can customize this high-level summary in your review settings.