diff --git a/example/src/components/TicketFieldsToReveal.tsx b/example/src/components/DeveloperPanel.tsx similarity index 55% rename from example/src/components/TicketFieldsToReveal.tsx rename to example/src/components/DeveloperPanel.tsx index 0433a20..746d121 100644 --- a/example/src/components/TicketFieldsToReveal.tsx +++ b/example/src/components/DeveloperPanel.tsx @@ -2,15 +2,16 @@ import React from 'react'; import { EdDSATicketFieldsToReveal } from "@pcd/zk-eddsa-event-ticket-pcd"; import Toggle from '@/components/Toggle'; -interface TicketFieldsToRevealGridProps { - ticketFieldsToReveal: EdDSATicketFieldsToReveal; +interface DeveloperPanelProps { + fieldsToReveal: EdDSATicketFieldsToReveal; onToggleField: (fieldName: keyof EdDSATicketFieldsToReveal) => void; - disabled?: boolean + disabled?: boolean; } -// Renders a grid of toggles for revealing specific ticket fields only. -const TicketFieldsToRevealGrid: React.FC = ({ ticketFieldsToReveal, onToggleField, disabled }) => { - const toggleKeys = Object.keys(ticketFieldsToReveal) as Array; +// Display a set of toggles associated with ticket fields. When a toggle is activated, +// the ticket proof will reveal the corresponding ticket field. +const DeveloperPanel: React.FC = ({ fieldsToReveal, onToggleField, disabled = false }) => { + const toggleKeys = Object.keys(fieldsToReveal) as Array; return (
@@ -18,7 +19,7 @@ const TicketFieldsToRevealGrid: React.FC = ({ tic

{fieldName}

onToggleField(fieldName)} disabled={disabled} /> @@ -28,4 +29,4 @@ const TicketFieldsToRevealGrid: React.FC = ({ tic ); } -export default TicketFieldsToRevealGrid; +export default DeveloperPanel; diff --git a/example/src/components/TicketRevealedFieldsInfo.tsx b/example/src/components/DisplayRevealedFields.tsx similarity index 60% rename from example/src/components/TicketRevealedFieldsInfo.tsx rename to example/src/components/DisplayRevealedFields.tsx index 624f911..f8f7b97 100644 --- a/example/src/components/TicketRevealedFieldsInfo.tsx +++ b/example/src/components/DisplayRevealedFields.tsx @@ -1,17 +1,22 @@ import React from 'react'; import { EdDSATicketFieldsToReveal } from "@pcd/zk-eddsa-event-ticket-pcd"; -interface RevealedFieldsInfoProps { +interface DisplayRevealedFieldsProps { user: { [key: string]: boolean | string | number; }; revealedFields: EdDSATicketFieldsToReveal; } -const RevealedFieldsInfo: React.FC = ({ user, revealedFields }) => { + +// Display the field name and corresponent value for each one that were revealed. +const DisplayRevealedFields: React.FC = ({ user, revealedFields }) => { const renderedFields = Object.entries(revealedFields).map(([fieldName, shouldReveal]) => { if (shouldReveal) { + // Remove the 'reveal' substring and lower the subsequent capitalized letter. + // eg., from 'revealTicketId' to 'ticketId'. const replaced = fieldName.replace('reveal', '').charAt(0).toLowerCase() + fieldName.slice(7) + const fieldValue = user[replaced]; return (
@@ -23,12 +28,10 @@ const RevealedFieldsInfo: React.FC = ({ user, revealedF return null; }); - const allNull = renderedFields.filter(field => field !== null).length === 0; - return (
- {allNull ? ( -
You're logged in without revealing anything :)
+ {renderedFields.filter(field => field !== null).length === 0 ? ( +
You're in, anon! No ticket info has been revealed 🕶
) : ( renderedFields )} @@ -36,4 +39,4 @@ const RevealedFieldsInfo: React.FC = ({ user, revealedF ); }; -export default RevealedFieldsInfo; +export default DisplayRevealedFields; diff --git a/example/src/components/Toggle.tsx b/example/src/components/Toggle.tsx index 2b6fa00..052ce72 100644 --- a/example/src/components/Toggle.tsx +++ b/example/src/components/Toggle.tsx @@ -7,9 +7,7 @@ interface ToggleProps { disabled?: boolean; } -const Toggle: React.FC = ({ checked, onToggle, disabled }) => { - const isChecked = checked !== undefined ? checked : false; - +const Toggle: React.FC = ({ checked = false, onToggle, disabled }) => { const handleClick = () => { if (!disabled) { onToggle(); @@ -18,13 +16,13 @@ const Toggle: React.FC = ({ checked, onToggle, disabled }) => { return ( ); } -export default Toggle; \ No newline at end of file +export default Toggle; diff --git a/example/src/pages/index.tsx b/example/src/pages/index.tsx index b820408..add6260 100644 --- a/example/src/pages/index.tsx +++ b/example/src/pages/index.tsx @@ -1,90 +1,60 @@ import axios from "axios" import Head from "next/head" import Image from "next/image" -import { useCallback, useEffect, useState } from "react" +import { useEffect, useState } from "react" import { useZuAuth } from "zuauth" import { EdDSATicketFieldsToReveal } from "@pcd/zk-eddsa-event-ticket-pcd" -import TicketFieldsToRevealGrid from "@/components/TicketFieldsToReveal" import Toggle from "@/components/Toggle" -import RevealedFieldsInfo from "@/components/TicketRevealedFieldsInfo" +import DisplayRevealedFields from "@/components/DisplayRevealedFields" +import DeveloperPanel from "@/components/DeveloperPanel" + +const defaultSetOfTicketFieldsToReveal: EdDSATicketFieldsToReveal = { + revealTicketId: false, + revealEventId: true, + revealProductId: true, + revealTimestampConsumed: false, + revealTimestampSigned: false, + revealAttendeeSemaphoreId: false, + revealIsConsumed: false, + revealIsRevoked: false, + revealTicketCategory: false, + revealAttendeeEmail: true, + revealAttendeeName: false +} export default function Home() { const { authenticate, pcd } = useZuAuth() const [user, setUser] = useState() const [developerMode, setDeveloperMode] = useState(false); - const [ticketFieldsToReveal, setTicketFieldsToReveal] = useState({ - revealTicketId: false, - revealEventId: true, - revealProductId: true, - revealTimestampConsumed: false, - revealTimestampSigned: false, - revealAttendeeSemaphoreId: false, - revealIsConsumed: false, - revealIsRevoked: false, - revealTicketCategory: false, - revealAttendeeEmail: true, - revealAttendeeName: false - }); - - const handleToggleField = (fieldName: keyof EdDSATicketFieldsToReveal) => { - setTicketFieldsToReveal((prevState: EdDSATicketFieldsToReveal) => { - const revealedFields = { - ...prevState, - [fieldName]: !prevState[fieldName] - }; - - localStorage.setItem("ticketFieldsToReveal", JSON.stringify(revealedFields)); - - return revealedFields; - }); - }; - - const handleSetDeveloperMode = () => { - setDeveloperMode((value: boolean) => { - localStorage.setItem("developerMode", JSON.stringify(!value)) - - if (!value === true) { - localStorage.setItem("ticketFieldsToReveal", JSON.stringify(ticketFieldsToReveal)); - } else { - setTicketFieldsToReveal({ - revealTicketId: false, - revealEventId: true, - revealProductId: true, - revealTimestampConsumed: false, - revealTimestampSigned: false, - revealAttendeeSemaphoreId: false, - revealIsConsumed: false, - revealIsRevoked: false, - revealTicketCategory: false, - revealAttendeeEmail: true, - revealAttendeeName: false - }) - - localStorage.removeItem("ticketFieldsToReveal") - } - return !value - }) - } + const [ticketFieldsToReveal, setTicketFieldsToReveal] = useState(defaultSetOfTicketFieldsToReveal); // Every time the page loads, an API call is made to check if the - // user is logged in and, if they are, to retrieve the current session's user data. + // user is logged in and, if they are, to retrieve the current session's user data + // and local storage data (to guarantee consistency across refreshes). useEffect(() => { ; (async function () { const { data } = await axios.get("/api/user") setUser(data.user) - const savedFields = localStorage.getItem("ticketFieldsToReveal"); - if (savedFields) { - setTicketFieldsToReveal(JSON.parse(savedFields)); - } + const fields = localStorage.getItem("ticketFieldsToReveal"); + const mode = localStorage.getItem("developerMode") - const developerMode = localStorage.getItem("developerMode") - if (developerMode) { - setDeveloperMode(JSON.parse(developerMode)) - } + if (fields) setTicketFieldsToReveal(JSON.parse(fields)); + if (mode) setDeveloperMode(JSON.parse(mode)) })() }, []) + // When the popup is closed and the user successfully + // generates the PCD, they can login. + useEffect(() => { + ; (async function () { + if (pcd) { + const { data } = await axios.post("/api/login", { pcd }) + setUser(data.user) + } + })() + }, [pcd]) + // Before logging in, the PCD is generated with the nonce from the // session created on the server. // Note that the nonce is used as a watermark for the PCD. Therefore, @@ -94,51 +64,50 @@ export default function Home() { const { data } = await axios.post("/api/nonce") authenticate( - developerMode ? { ...ticketFieldsToReveal } : { - revealAttendeeEmail: true, - revealEventId: true, - revealProductId: true - }, + developerMode ? { ...ticketFieldsToReveal } : { ...defaultSetOfTicketFieldsToReveal }, data.nonce ) } - // When the popup is closed and the user successfully - // generates the PCD, they can login. - useEffect(() => { - ; (async function () { - if (pcd) { - const { data } = await axios.post("/api/login", { pcd }) - - setUser(data.user) - } - })() - }, [pcd]) - - // Logging out simply clears the active session. + // Logging out simply clears the active session, local storage and state. const logout = async () => { - await axios.post("/api/logout") + setUser(false) localStorage.removeItem("ticketFieldsToReveal") localStorage.removeItem("developerMode") - setTicketFieldsToReveal({ - revealTicketId: false, - revealEventId: true, - revealProductId: true, - revealTimestampConsumed: false, - revealTimestampSigned: false, - revealAttendeeSemaphoreId: false, - revealIsConsumed: false, - revealIsRevoked: false, - revealTicketCategory: false, - revealAttendeeEmail: true, - revealAttendeeName: false - }) + setTicketFieldsToReveal(defaultSetOfTicketFieldsToReveal) setDeveloperMode(false) + } - setUser(false) + const handleToggleField = (fieldToReveal: keyof EdDSATicketFieldsToReveal) => { + setTicketFieldsToReveal(prevState => { + const fieldsToReveal = { + ...prevState, + [fieldToReveal]: !prevState[fieldToReveal] + }; + + localStorage.setItem("ticketFieldsToReveal", JSON.stringify(fieldsToReveal)); + return fieldsToReveal; + }); + }; + + const handleSetDeveloperMode = () => { + setDeveloperMode(value => { + const newValue = !value + + if (newValue) { + localStorage.setItem("ticketFieldsToReveal", JSON.stringify(ticketFieldsToReveal)); + } else { + setTicketFieldsToReveal(defaultSetOfTicketFieldsToReveal) + localStorage.removeItem("ticketFieldsToReveal") + } + + localStorage.setItem("developerMode", JSON.stringify(newValue)) + + return newValue + }) } return ( @@ -156,10 +125,6 @@ export default function Home() { ZuAuth Example -
- -
-

This demo illustrates how the{" "} IronSession - . Check the{" "} + . You can choose which ticket fields to reveal during the authentication process by enabling the Developer Mode. + We kindly invite you to check the{" "} {developerMode && ( - @@ -224,8 +190,8 @@ export default function Home() { } {user &&

} +
}
) -} +} \ No newline at end of file