Skip to content

Commit

Permalink
refactoring: complete developer mode features
Browse files Browse the repository at this point in the history
  • Loading branch information
0xjei committed Nov 8, 2023
1 parent 391edc6 commit 209ae25
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 128 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,24 @@ 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<TicketFieldsToRevealGridProps> = ({ ticketFieldsToReveal, onToggleField, disabled }) => {
const toggleKeys = Object.keys(ticketFieldsToReveal) as Array<keyof EdDSATicketFieldsToReveal>;
// 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<DeveloperPanelProps> = ({ fieldsToReveal, onToggleField, disabled = false }) => {
const toggleKeys = Object.keys(fieldsToReveal) as Array<keyof EdDSATicketFieldsToReveal>;

return (
<div className="grid grid-cols-4 gap-4">
{toggleKeys.map(fieldName => (
<div key={fieldName} className="flex flex-col items-center">
<p className="text-center">{fieldName}</p>
<Toggle
checked={ticketFieldsToReveal[fieldName]}
checked={fieldsToReveal[fieldName]}
onToggle={() => onToggleField(fieldName)}
disabled={disabled}
/>
Expand All @@ -28,4 +29,4 @@ const TicketFieldsToRevealGrid: React.FC<TicketFieldsToRevealGridProps> = ({ tic
);
}

export default TicketFieldsToRevealGrid;
export default DeveloperPanel;
Original file line number Diff line number Diff line change
@@ -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<RevealedFieldsInfoProps> = ({ user, revealedFields }) => {

// Display the field name and corresponent value for each one that were revealed.
const DisplayRevealedFields: React.FC<DisplayRevealedFieldsProps> = ({ 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 (
<div key={fieldName} className="my-2 text-center">
Expand All @@ -23,17 +28,15 @@ const RevealedFieldsInfo: React.FC<RevealedFieldsInfoProps> = ({ user, revealedF
return null;
});

const allNull = renderedFields.filter(field => field !== null).length === 0;

return (
<div>
{allNull ? (
<div className="text-center">You're logged in without revealing anything :)</div>
{renderedFields.filter(field => field !== null).length === 0 ? (
<div className="text-center">You&apos;re in, anon! No ticket info has been revealed 🕶</div>
) : (
renderedFields
)}
</div>
);
};

export default RevealedFieldsInfo;
export default DisplayRevealedFields;
10 changes: 4 additions & 6 deletions example/src/components/Toggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ interface ToggleProps {
disabled?: boolean;
}

const Toggle: React.FC<ToggleProps> = ({ checked, onToggle, disabled }) => {
const isChecked = checked !== undefined ? checked : false;

const Toggle: React.FC<ToggleProps> = ({ checked = false, onToggle, disabled }) => {
const handleClick = () => {
if (!disabled) {
onToggle();
Expand All @@ -18,13 +16,13 @@ const Toggle: React.FC<ToggleProps> = ({ checked, onToggle, disabled }) => {

return (
<button
className={`toggle-icon ${isChecked ? 'active' : ''}`}
className={`toggle-icon ${checked ? 'active' : ''}`}
onClick={handleClick}
disabled={disabled}
>
{isChecked ? <FaToggleOn size={30} className="text-blue-800"/> : <FaToggleOff size={30} />}
{checked ? <FaToggleOn size={30} className="text-blue-800"/> : <FaToggleOff size={30} />}
</button>
);
}

export default Toggle;
export default Toggle;
180 changes: 73 additions & 107 deletions example/src/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -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<any>()
const [developerMode, setDeveloperMode] = useState(false);
const [ticketFieldsToReveal, setTicketFieldsToReveal] = useState<EdDSATicketFieldsToReveal>({
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<EdDSATicketFieldsToReveal>(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,
Expand All @@ -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 (
Expand All @@ -156,10 +125,6 @@ export default function Home() {
ZuAuth Example
</h1>

<div className="text-center">

</div>

<p className="my-8 text-justify">
This demo illustrates how the{" "}
<a
Expand All @@ -181,7 +146,8 @@ export default function Home() {
>
IronSession
</a>
. 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{" "}
<a
className="text-blue-600 visited:text-purple-600"
href="https://github.com/cedoor/zuauth#readme"
Expand Down Expand Up @@ -213,8 +179,8 @@ export default function Home() {

<div style={{ height: "300px" }}>
{developerMode && (
<TicketFieldsToRevealGrid
ticketFieldsToReveal={ticketFieldsToReveal}
<DeveloperPanel
fieldsToReveal={ticketFieldsToReveal}
onToggleField={handleToggleField}
disabled={!!user}
/>
Expand All @@ -224,8 +190,8 @@ export default function Home() {
}

{user && <div className="my-8 text-center">
<RevealedFieldsInfo user={user} revealedFields={ticketFieldsToReveal} /> </div>}
<DisplayRevealedFields user={user} revealedFields={ticketFieldsToReveal} /> </div>}
</div>
</main >
)
}
}

0 comments on commit 209ae25

Please sign in to comment.