Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 8 additions & 2 deletions infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
// isPaneOpen = false;
// };
// Initialize pane only once when element is available
$effect(() => {
if (!drawerElem) return;
pane = new CupertinoPane(drawerElem, {
Expand All @@ -44,13 +45,18 @@
initialBreak: "bottom",
});
return () => pane?.destroy();
});
// Handle open/close state separately
$effect(() => {
if (!pane) return;
if (isPaneOpen) {
pane.present({ animate: true });
} else {
pane.destroy({ animate: true });
}
return () => pane.destroy();
});
Comment on lines +51 to 60
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Destroying pane on close prevents reopening (pane not re-initialized)

Calling pane.destroy() when isPaneOpen=false removes the instance; later openings won’t recreate it because the init effect doesn’t rerun. Use hide/close (non-destructive) on close, and reserve destroy for unmount.

Apply:

-        if (isPaneOpen) {
-            pane.present({ animate: true });
-        } else {
-            pane.destroy({ animate: true });
-        }
+        if (isPaneOpen) {
+            pane.present({ animate: true });
+        } else {
+            // Prefer non-destructive close
+            if (typeof (pane as any).hide === "function") {
+                (pane as any).hide({ animate: true });
+            } else {
+                // As a fallback, keep the instance; avoid destroy here
+            }
+        }

Option B: if hide isn’t available, gate destroy to unmount and re-init pane when reopening.


🌐 Web query:

Does CupertinoPane expose a non-destructive method (e.g., hide/close/dismiss) to close the pane without destroying it?

💡 Result:

Yes. CupertinoPane provides a non‑destructive hide() that removes the pane from view but keeps it in the DOM (you can check isHidden() and call present() to show it again). [1][2]

Sources:

  • GitHub docs (public methods: hide, destroy, isHidden). [1]
  • npm package docs (showing hide(), destroy(), present(), isHidden()). [2]

Replace pane.destroy() with pane.hide() on close
CupertinoPane provides hide(), which non-destructively closes the pane and lets you call present() to reopen it.
File: infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.svelte lines 51–60

-        if (isPaneOpen) {
-            pane.present({ animate: true });
-        } else {
-            pane.destroy({ animate: true });
-        }
+        if (isPaneOpen) {
+            pane.present({ animate: true });
+        } else {
+            pane.hide({ animate: true });
+        }
📝 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.

Suggested change
// Handle open/close state separately
$effect(() => {
if (!pane) return;
if (isPaneOpen) {
pane.present({ animate: true });
} else {
pane.destroy({ animate: true });
}
return () => pane.destroy();
});
// Handle open/close state separately
$effect(() => {
if (!pane) return;
if (isPaneOpen) {
pane.present({ animate: true });
} else {
pane.hide({ animate: true });
}
});
🤖 Prompt for AI Agents
In infrastructure/eid-wallet/src/lib/ui/Drawer/Drawer.svelte around lines 51 to
60, the code currently calls pane.destroy({ animate: true }) when closing;
replace that with pane.hide({ animate: true }) so the pane is closed
non-destructively and can be reopened with pane.present(), keeping the existing
null check for pane; ensure the call signature matches the CupertinoPane API
(use hide with the animate option) and remove any logic that assumes the pane is
destroyed.

</script>

Expand Down
33 changes: 24 additions & 9 deletions infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
import { getContext, onMount } from "svelte";
import { Shadow } from "svelte-loading-spinners";
import { v4 as uuidv4 } from "uuid";
import DocumentType from "./steps/document-type.svelte";
import Passport from "./steps/passport.svelte";
Comment on lines +17 to 18
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Generalize UI and stored labels for non-passport docs

Hardcoded “Passport” persists in user metadata and UI, which mislabels non-passport flows. Import documentType, add a label map, and switch labels to be document‑type aware. Also generalize the hero subtitle and alt text.

Apply:

-    import {
-        DocFront,
-        DocBack,
-        Selfie as SelfiePic,
-        reason,
-        status,
-        verifStep,
-        verificaitonId,
-    } from "./store";
+    import {
+        DocFront,
+        DocBack,
+        Selfie as SelfiePic,
+        reason,
+        status,
+        verifStep,
+        verificaitonId,
+        documentType,
+    } from "./store";
-    let hardwareKeyCheckComplete = $state(false);
+    let hardwareKeyCheckComplete = $state(false);
+    const docLabel = {
+        passport: "Passport",
+        id: "National ID",
+        permit: "Residence Permit",
+        dl: "Driver's License",
+    } as const;
-                "ID submitted": `Passport - ${person.nationality.value}`,
-                "Passport Number": document.number.value,
+                "ID submitted": `${docLabel[$documentType ?? "passport"]} - ${person.nationality.value}`,
+                "Document Number": document.number.value,
-                Get your passport ready. You’ll be directed to present your
-                passport and take a quick selfie.
+                Get your document ready. You’ll be directed to present your
+                chosen ID and take a quick selfie.
-        <img class="mx-auto mt-20" src="images/Passport.svg" alt="passport" />
+        <img class="mx-auto mt-20" src="images/Passport.svg" alt="identity document" />

Also applies to: 358-362

import Selfie from "./steps/selfie.svelte";
import {
DocFront,
DocBack,
Selfie as SelfiePic,
reason,
status,
Expand Down Expand Up @@ -100,7 +102,7 @@
let document: Document;
let loading = $state(false);
let keyManager: KeyManager | null = $state(null);
let websocketData: any = $state(null); // Store websocket data for duplicate case
let websocketData: { w3id?: string } | null = $state(null); // Store websocket data for duplicate case
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Type narrowing conflicts with stored data.

websocketData is typed as { w3id?: string } | null, but line 137 assigns the full websocket message (which includes status, reason, person, document, and other properties). This type mismatch defeats the purpose of type safety.

Choose one of these solutions:

Option 1 (if only w3id is needed): Store only the required property.

-        websocketData = data; // Store the full websocket data
+        websocketData = data.w3id ? { w3id: data.w3id } : null;

Option 2 (if full data is needed): Define and use the complete type.

+    type WebsocketData = {
+        status: string;
+        reason: string;
+        person: Person;
+        document: Document;
+        w3id?: string;
+    };
+
-    let websocketData: { w3id?: string } | null = $state(null);
+    let websocketData: WebsocketData | null = $state(null);
📝 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.

Suggested change
let websocketData: { w3id?: string } | null = $state(null); // Store websocket data for duplicate case
// in your WebSocket message handler (around line 137)
websocketData = data.w3id ? { w3id: data.w3id } : null;
Suggested change
let websocketData: { w3id?: string } | null = $state(null); // Store websocket data for duplicate case
<script lang="ts">
// … other imports
// Define the full shape of the incoming message
type WebsocketData = {
status: string;
reason: string;
person: Person;
document: Document;
w3id?: string;
};
-// Store only w3id (too narrow)
// Store the full payload with proper typing
let websocketData: WebsocketData | null = $state(null);
// … rest of your code
</script>
🤖 Prompt for AI Agents
in infrastructure/eid-wallet/src/routes/(auth)/verify/+page.svelte around line
105 (and referenced assignment at line 137), websocketData is declared as {
w3id?: string } | null but later assigned the entire websocket message (which
includes status, reason, person, document, etc.), causing a type mismatch;
either restrict stored value to only the needed property (Option 1) by changing
assignments to extract and store just message.w3id and keep the current type, or
switch to Option 2 by declaring a full interface/type that matches the websocket
message shape and update websocketData’s type to that interface so the full
object can be stored safely—pick the appropriate option and update both the type
declaration and all assignments/usages accordingly.

let hardwareKeySupported = $state(false);
let hardwareKeyCheckComplete = $state(false);
Expand Down Expand Up @@ -135,9 +137,10 @@
websocketData = data; // Store the full websocket data
if (data.status === "resubmission_requested") {
DocFront.set(null);
DocBack.set(null);
SelfiePic.set(null);
}
verifStep.set(2);
verifStep.set(3);
};
}
Expand Down Expand Up @@ -182,8 +185,12 @@
await initializeKeyManager();
}
if (!keyManager) {
throw new Error("Key manager not initialized");
}
try {
const res = await keyManager!.generate("default");
const res = await keyManager.generate("default");
console.log("Key generation result:", res);
return res;
} catch (e) {
Expand All @@ -197,8 +204,12 @@
await initializeKeyManager();
}
if (!keyManager) {
throw new Error("Key manager not initialized");
}
try {
const res = await keyManager!.getPublicKey("default");
const res = await keyManager.getPublicKey("default");
console.log("Public key retrieved:", res);
return res;
} catch (e) {
Expand All @@ -218,9 +229,11 @@
// Initialize key manager and check if default key pair exists
await initializeKeyManager();
const keyExists = await keyManager!.exists("default");
if (!keyExists) {
await generateApplicationKeyPair();
if (keyManager) {
const keyExists = await keyManager.exists("default");
if (!keyExists) {
await generateApplicationKeyPair();
}
}
handleContinue = async () => {
Expand Down Expand Up @@ -342,9 +355,11 @@
<Drawer bind:isPaneOpen={showVeriffModal}>
<div class="overflow-y-scroll">
{#if $verifStep === 0}
<Passport></Passport>
<DocumentType />
{:else if $verifStep === 1}
<Selfie></Selfie>
<Passport />
{:else if $verifStep === 2}
<Selfie />
{:else if loading}
<div class="my-20">
<div
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<script lang="ts">
import { ButtonAction } from "$lib/ui";
import { documentType, verifStep } from "../store";

function selectDocumentType(type: "passport" | "id" | "permit" | "dl") {
documentType.set(type);
verifStep.set(1); // Move to document capture step
}
</script>

<div class="flex flex-col gap-5">
<div>
<h3>Choose Document Type</h3>
<p>Select the type of identity document you will be presenting</p>
</div>

<div class="flex flex-col gap-3">
<ButtonAction
class="w-full"
callback={() => selectDocumentType("passport")}
>
Passport
</ButtonAction>

<ButtonAction
class="w-full"
callback={() => selectDocumentType("id")}
>
ID Card
</ButtonAction>

<ButtonAction
class="w-full"
callback={() => selectDocumentType("dl")}
>
Driving License
</ButtonAction>

<ButtonAction
class="w-full"
callback={() => selectDocumentType("permit")}
>
Residence Permit
</ButtonAction>
</div>

<div class="text-center text-xs text-white">
Accepted documents: Driver's License, Residence Permit, Passport, ID Card.
</div>
</div>

Loading
Loading