Skip to content

Commit

Permalink
Ceremony update (#2987)
Browse files Browse the repository at this point in the history
  • Loading branch information
cor authored Sep 20, 2024
2 parents fa9cc02 + 67ab09e commit 2efaf0e
Show file tree
Hide file tree
Showing 26 changed files with 488 additions and 236 deletions.
2 changes: 1 addition & 1 deletion ceremony/src/lib/client/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export const get = async <T>(
export const post = async <T>(
resource: string,
params: Params,
body: Record<string, unknown>,
body: Record<string, unknown> | string,
_fetch: Fetch = fetch
): Promise<T | undefined> => {
try {
Expand Down
13 changes: 13 additions & 0 deletions ceremony/src/lib/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,16 @@ export const checkState = async (): Promise<ClientState> => {
return "offline"
}
}

export const generateSecret = async (email: string | undefined) => {
if (!email) {
console.log("No email")
return
}
try {
return await post<string>("secret_key", {}, email)
} catch (error) {
console.log("Error fetching secret:", error)
return undefined
}
}
50 changes: 41 additions & 9 deletions ceremony/src/lib/components/Button.svelte
Original file line number Diff line number Diff line change
@@ -1,14 +1,46 @@
<script lang="ts">
import Spinner from "$lib/components/Spinner.svelte"
import type { HTMLButtonAttributes, HTMLAnchorAttributes } from "svelte/elements"
let { children, class: className = "", loading = false, ...props } = $props()
interface Props extends HTMLButtonAttributes, HTMLAnchorAttributes {
children?: any
class?: string
loading?: boolean
variant?: "primary" | "secondary"
href?: string
}
let {
children,
class: className = "",
loading = false,
variant = "primary",
href,
...props
}: Props = $props()
const styles = {
primary: "bg-union-accent-500 text-black hover:text-black",
secondary: "bg-transparent text-white border-2 border-white hover:bg-neutral-800"
}
const getClass = (type: "primary" | "secondary") => styles[type] || styles.primary
let combinedClasses = $derived(
`flex items-center w-fit gap-2 px-4 py-2 font-bold uppercase justify-center ${getClass(variant)} ${className}`
)
</script>

<button
{...props}
class={`flex items-center w-fit gap-2 px-4 py-2 text-black font-bold hover:text-black bg-union-accent-500 uppercase justify-center ${className}`}>
{@render children()}
{#if loading}
<Spinner class="size-4"/>
{/if}
</button>
{#if href}
<a {href} class={combinedClasses} {...props}>
{@render children()}
{#if loading}
<Spinner class="size-4"/>
{/if}
</a>
{:else}
<button class={combinedClasses} {...props}>
{@render children()}
{#if loading}
<Spinner class="size-4"/>
{/if}
</button>
{/if}
94 changes: 33 additions & 61 deletions ceremony/src/lib/components/Ceremony.svelte
Original file line number Diff line number Diff line change
@@ -1,91 +1,63 @@
<script lang="ts">
import type { ContributorState } from "$lib/stores/state.svelte.ts"
import H1 from "$lib/components/typography/H1.svelte"
import H3 from "$lib/components/typography/H3.svelte"
import H2 from "$lib/components/typography/H2.svelte"
import { generateSecret, start } from "$lib/client"
import Reward from "$lib/components/Reward.svelte"
import Download from "$lib/components/Download.svelte"
import Queue from "$lib/components/Queue.svelte"
import Install from "$lib/components/Install.svelte"
import { start } from "$lib/client"
import { AddressForm, type ValidState } from "$lib/components/address"
import Blink from "$lib/components/Blink.svelte"
import Tweet from "$lib/components/Tweet.svelte"
import SwimLoad from "$lib/components/SwimLoad.svelte"
import { getNumberSuffix } from "$lib/utils/utils.ts"
import Text from "$lib/components/typography/Text.svelte"
import Status from "$lib/components/Status.svelte"
import Thanks from "$lib/components/Thanks.svelte"
import { user } from "$lib/stores/user.svelte.ts"
type Props = {
contributor: ContributorState
}
let { contributor }: Props = $props()
async function generate() {
const email = user.session?.user.email
await generateSecret(email)
}
$effect(() => {
if (contributor?.contributionState === "contribute" && contributor.state !== "contributing") {
console.log("Call client start")
start()
}
if (contributor.clientState !== "offline") {
generate()
}
})
let addressValidState: ValidState = $state("PENDING")
window.addEventListener("beforeunload", (e: BeforeUnloadEvent) => {
e.preventDefault()
e.returnValue = ""
})
</script>

<div class="p-8 w-full flex items-center justify-center flex-col">

{#if contributor.state === 'inQueue'}

<Status {contributor} />

<div class="border p-8 w-full max-w-4xl flex flex-col items-center">
<H1 class="mb-6">You are <span class="!text-union-accent-500">{contributor.queueState.position}<span
class="lowercase">{getNumberSuffix(contributor.queueState.position)}</span> </span> in queue</H1>

<SwimLoad max={contributor.queueState.count} current={contributor.queueState.position}/>

<div class="mb-4 text-center">
<H2>Queue length: <span class="text-union-accent-500">{contributor.queueState.count}</span></H2>
<H3>Waiting time: <span class="text-union-accent-500">{contributor.queueState.estimatedTime} minutes</span>
(est.).
</H3>
</div>

<div class="text-center">
<H2 class="mb-2">Get your nft</H2>
<AddressForm class="" onValidation={result => (addressValidState = result)}/>
</div>
</div>


{#if !contributor.userWallet}
<Reward {contributor}/>
{:else if contributor.clientState === 'offline'}
<Install {contributor}/>
{:else if !contributor.downloadedSecret}
<Download {contributor}/>
{:else if contributor.state === "inQueue"}
<Queue {contributor}/>
{:else if contributor.state === 'contribute'}
<Status {contributor} />
<H1>Starting contribution...</H1>

<H1>Starting contribution...</H1>
{:else if contributor.state === 'contributing'}
<Status {contributor} />
<H1>Contributing...</H1>

<H1>Contributing...</H1>
{:else if contributor.state === 'verifying'}
<Status {contributor} />
<H1>Verifying your contribution...</H1>

<H1>Verifying your contribution...</H1>
{:else if contributor.state === 'contributed'}

<div class="flex flex-col justify-center items-center gap-4">
<H1>Thank you! Your contribution is completed.</H1>
<Tweet/>
</div>

{:else if contributor.state === 'noClient'}

<Status {contributor} />
<H1>No client. Cannot start contribution.</H1>

<Thanks {contributor}/>
{:else}
<H1>Not able to contribute at this time</H1>

<H1>Not able to contribute at this time</H1>
{/if}

</div>


<div class="absolute bottom-10 flex flex-col px-8 text-center gap-4">
</div>
<div class="absolute bottom-10 flex flex-col px-8 text-center gap-4"></div>
42 changes: 36 additions & 6 deletions ceremony/src/lib/components/Code.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import Button from "$lib/components/Button.svelte"
type Props = {
contributor: ContributorState
secondary?: boolean
}
let { contributor }: Props = $props()
let { contributor, secondary = false }: Props = $props()
let words: Array<string> = $state(new Array(6).fill(""))
let code = $derived(normalizeString(words))
Expand Down Expand Up @@ -44,17 +45,39 @@ async function handleCodeJoin() {
toast.error("An error occurred while redeeming the code")
} finally {
codeLoading = false
words = new Array(6).fill("")
}
}
</script>
function handleKeyDown(event: KeyboardEvent, index: number) {
if (event.key === "Enter" || event.key === " ") {
event.preventDefault()
if (index < words.length - 1) {
// Move to next input
const nextInput = document.querySelector(
`input:nth-child(${2 * index + 3})`
) as HTMLInputElement
nextInput?.focus()
} else if (event.key === "Enter") {
// On last input, trigger the USE CODE button only for Enter key
handleCodeJoin()
}
} else if (event.key === "Backspace" && words[index] === "" && index > 0) {
event.preventDefault()
// Move to previous input
const prevInput = document.querySelector(
`input:nth-child(${2 * index - 1})`
) as HTMLInputElement
prevInput?.focus()
}
}
</script>

<div class="flex gap-2 max-w-4xl flex-wrap justify-center mb-8">
{#each words as word, index}
<input
bind:value={words[index]}
onpaste={handlePaste}
onkeydown={(e) => handleKeyDown(e, index)}
class="bg-transparent border-b border-white w-20 text-center text-union-accent-500 outline-none focus:ring-0 focus:border-union-accent-500"
style="--tw-ring-color: transparent;"
/>
Expand All @@ -63,6 +86,13 @@ async function handleCodeJoin() {
{/if}
{/each}
</div>
<Button loading={codeLoading} type="button" onclick={handleCodeJoin}>
USE CODE
</Button>

{#if secondary}
<Button class="bg-transparent text-white hover:text-white border-2 border-white hover:bg-neutral-800" loading={codeLoading} type="button" onclick={handleCodeJoin}>
Redeem code
</Button>
{:else}
<Button loading={codeLoading} type="button" onclick={handleCodeJoin}>
Redeem code
</Button>
{/if}
15 changes: 0 additions & 15 deletions ceremony/src/lib/components/Counter/index.svelte
Original file line number Diff line number Diff line change
@@ -1,17 +1,4 @@
<script lang="ts">
// import Zero from "./numbers/zero.svelte"
// import One from "./numbers/one.svelte"
// import Two from "./numbers/two.svelte"
// import Three from "./numbers/three.svelte"
// import Four from "./numbers/four.svelte"
// import Five from "./numbers/five.svelte"
// import Six from "./numbers/six.svelte"
// import Seven from "./numbers/seven.svelte"
// import Eight from "./numbers/eight.svelte"
// import Nine from "./numbers/nine.svelte"
// import H4 from "$lib/components/typography/H4.svelte"
// import H1 from "$lib/components/typography/H1.svelte"
// import Text from "$lib/components/typography/Text.svelte"
import H2 from "$lib/components/typography/H2.svelte"
import H3 from "$lib/components/typography/H3.svelte"
Expand All @@ -25,8 +12,6 @@ let hours = $state("00")
let minutes = $state("00")
let seconds = $state("00")
// const components = [Zero, One, Two, Three, Four, Five, Six, Seven, Eight, Nine]
let interval: ReturnType<typeof setInterval>
function updateCountdown(): void {
Expand Down
49 changes: 49 additions & 0 deletions ceremony/src/lib/components/Download.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<script lang="ts">
import Button from "$lib/components/Button.svelte"
import H1 from "$lib/components/typography/H1.svelte"
import Text from "$lib/components/typography/Text.svelte"
import { ContributorState } from "$lib/stores/state.svelte.ts"
import { onMount } from "svelte"
import { user } from "$lib/stores/user.svelte.ts"
import { generateSecret } from "$lib/client"
type Props = {
contributor: ContributorState
}
let { contributor }: Props = $props()
let download = $state(false)
function handleDownload(event: MouseEvent) {
event.preventDefault()
const newUrl = "http://localhost:4919/secret_key"
window.open(newUrl, "_blank")
download = true
}
function setDownloadedSecret() {
localStorage.setItem("downloaded-secret", "true")
contributor.downloadedSecret = true
}
</script>

{#if !download}
<H1>Generate your PGP secret</H1>
<Text class="text-center mb-4">
The MPC client automatically uses this secret to sign your contribution.<br>
Your secret is locally generated through the MPC client.
</Text>
<Button variant="primary" onclick={handleDownload}>Generate secret</Button>
{:else}
<H1>Store your PGP secret</H1>
<Text class="text-center mb-4">
Please store your secret somewhere safe, such as in your password manager.
<br> Never share your secret.
<br> This secret key is the only way to prove that you have contributed.
</Text>
<div class="flex gap-4">
<Button variant="primary" onclick={setDownloadedSecret}>I've generated and stored my secret</Button>
<Button variant="secondary" href="http://localhost:4919/secret_key" target="_blank">Generate again</Button>
</div>
{/if}
Loading

0 comments on commit 2efaf0e

Please sign in to comment.