Skip to content

Commit

Permalink
feat(ceremony): contributors (#2980)
Browse files Browse the repository at this point in the history
  • Loading branch information
cor authored Sep 19, 2024
2 parents 89d4e78 + 506ccbb commit 99bbf7b
Show file tree
Hide file tree
Showing 26 changed files with 590 additions and 128 deletions.
15 changes: 11 additions & 4 deletions ceremony/src/lib/client/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import { get, post } from "$lib/client/http.ts"
import { user } from "$lib/stores/user.svelte.ts"
import { getQueuePayloadId } from "$lib/supabase/queries.ts"
import type { ClientState, ContributeBody } from "$lib/client/types.ts"
import { supabase } from "$lib/supabase/client.ts"

export const start = async (): Promise<ClientState | undefined> => {
const userId = user?.session?.user.id
const email = user?.session?.user?.email
const { data: session, error: sessionError } = await supabase.auth.refreshSession()

if (sessionError) {
console.error("Error refreshing session:", sessionError)
return
}

const userId = session.session?.user.id
const email = session.session?.user?.email

if (!userId) {
console.log("User not logged in")
Expand All @@ -27,7 +34,7 @@ export const start = async (): Promise<ClientState | undefined> => {
const contributeBody: Partial<ContributeBody> = {
payloadId: data.payload_id,
contributorId: userId,
jwt: user?.session?.access_token,
jwt: session.session?.access_token,
supabaseProject: import.meta.env.VITE_SUPABASE_URL,
apiKey: import.meta.env.VITE_SUPABASE_ANON_KEY,
bucket: import.meta.env.VITE_BUCKET_ID,
Expand Down
2 changes: 1 addition & 1 deletion ceremony/src/lib/components/Blink.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ function startRandomBlinking() {
$effect(() => {
if (sleep) {
eye = "-"
eye = "×"
clearInterval(blinkInterval)
} else {
eye = "0"
Expand Down
55 changes: 32 additions & 23 deletions ceremony/src/lib/components/Ceremony.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import H3 from "$lib/components/typography/H3.svelte"
import H2 from "$lib/components/typography/H2.svelte"
import Install from "$lib/components/Install.svelte"
import { start } from "$lib/client"
import H4 from "$lib/components/typography/H4.svelte"
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"
type Props = {
contributor: ContributorState
Expand All @@ -31,52 +32,60 @@ let addressValidState: ValidState = $state("PENDING")
<div class="p-8 w-full flex items-center justify-center flex-col">

{#if contributor.state === 'inQueue'}
<H1>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={100} current={50}/>
<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>

{#if contributor.clientState === 'offline'}
<Install/>
{/if}

<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>


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

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

{:else if contributor.state === 'verifying'}
<Status {contributor} />
<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>
<H2>Get your nft</H2>
<AddressForm class="" onValidation={result => (addressValidState = result)}/>
<Tweet tweetText="0____0"/>
<Tweet/>
</div>

<!--Your turn but no client-->
{:else if contributor.state === 'noClient'}

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

{:else}
<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">
<H4>
<Blink
loading={contributor.state === 'contributing'}
sleep={contributor.clientState === 'offline'}
/>
</H4>
<H4>{contributor.clientState}</H4>
</div>
68 changes: 68 additions & 0 deletions ceremony/src/lib/components/Code.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<script lang="ts">
import { callJoinQueue } from "$lib/supabase"
import { toast } from "svelte-sonner"
import type { ContributorState } from "$lib/stores/state.svelte.ts"
import Button from "$lib/components/Button.svelte"
type Props = {
contributor: ContributorState
}
let { contributor }: Props = $props()
let words: Array<string> = $state(new Array(6).fill(""))
let code = $derived(normalizeString(words))
let codeLoading = $state(false)
function handlePaste(e: ClipboardEvent): void {
e.preventDefault()
const pastedText: string = e.clipboardData?.getData("text") || ""
const pastedWords: Array<string> = pastedText.split(/\s+/).slice(0, 6)
words = [...pastedWords, ...new Array(6 - pastedWords.length).fill("")]
}
function normalizeString(words: Array<string>): string {
return words
.map(word => word.trim().toLowerCase())
.join("")
.replace(/[^a-z0-9]/gi, "")
}
async function handleCodeJoin() {
codeLoading = true
try {
console.log(code)
const codeOk = await callJoinQueue(code)
if (codeOk) {
contributor.setAllowanceState("hasRedeemed")
toast.success("Code successfully redeemed")
} else {
toast.error("The code is not valid")
}
} catch (error) {
console.error("Error redeeming code:", error)
toast.error("An error occurred while redeeming the code")
} finally {
codeLoading = false
words = new Array(6).fill("")
}
}
</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}
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;"
/>
{#if index !== words.length - 1}
<div class="text-union-accent-500"><p>-</p></div>
{/if}
{/each}
</div>
<Button loading={codeLoading} type="button" onclick={handleCodeJoin}>
USE CODE
</Button>
85 changes: 85 additions & 0 deletions ceremony/src/lib/components/Contribution.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<script lang="ts">
import { getUserContribution } from "$lib/supabase"
import Spinner from "$lib/components/Spinner.svelte"
import H2 from "$lib/components/typography/H2.svelte"
import Button from "$lib/components/Button.svelte"
import { toast } from "svelte-sonner"
import { page } from "$app/stores"
type Props = {
hash: string
}
let { hash }: Props = $props()
function hexToUint8Array(hexString: string) {
return new Uint8Array(hexString.match(/.{1,2}/g)?.map(byte => Number.parseInt(byte, 16)) || [])
}
function uint8ArrayToUtf8(bytes: Uint8Array) {
return new TextDecoder().decode(bytes)
}
function decodeHexString(hexString: string) {
return uint8ArrayToUtf8(hexToUint8Array(hexString))
}
async function copyToClipboard(text: string, label: string) {
try {
await navigator.clipboard.writeText(text)
toast.success(`Copied ${label}!`)
} catch (err) {
console.error("Failed to copy text: ", err)
toast.error(`Failed to copy ${label} to clipboard.`)
}
}
const imagePath = "/images/ceremony.png"
let imageUrl = $derived(new URL(imagePath, $page.url.origin).href)
</script>

<svelte:head>
<meta property="og:type" content="Website"/>
<meta property="og:site_name" content="Union Ceremony"/>
<meta property="og:locale" content="en"/>
<meta property="og:image:type" content="image/png"/>
<meta property="og:image:width" content="1200"/>
<meta property="og:image:height" content="675"/>
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:site" content="@union_build"/>
<meta name="twitter:creator" content="@union_build"/>
<meta property="og:image" content={imageUrl}/>
<meta property="og:image:secure_url" content={imageUrl}/>
<meta name="twitter:image" content={imageUrl}/>
</svelte:head>

{#await getUserContribution(hash)}
<Spinner class="size-5 text-union-accent-500"/>
{:then contribution}
{#if contribution}
<div class="flex flex-col items-start gap-1 px-3 py-2">
<div>
<H2>Contributor: <span class="!text-union-accent-500">{contribution.user_name}</span></H2>
</div>

<div class="flex flex-col gap-4">
<div>
<H2 class="mb-2">Public key</H2>
<pre class="text-white whitespace-pre-wrap bg-neutral-800 p-4 mb-4">{decodeHexString(contribution.public_key)}</pre>
<Button onclick={() => copyToClipboard(decodeHexString(contribution.public_key), "public key")}>Copy
Public
key
</Button>
</div>

<div>
<H2 class="mb-2">Signature</H2>
<pre class="text-white whitespace-pre-wrap bg-neutral-800 p-4 mb-4">{decodeHexString(contribution.signature)}</pre>
<Button onclick={() => copyToClipboard(decodeHexString(contribution.signature), "signature")}>Copy
Signature
</Button>
</div>
</div>
</div>
{/if}
{/await}
46 changes: 46 additions & 0 deletions ceremony/src/lib/components/Contributions.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<script lang="ts">
import Spinner from "$lib/components/Spinner.svelte"
import Text from "$lib/components/typography/Text.svelte"
import { getContributions } from "$lib/supabase"
let intervalId: NodeJS.Timeout | number
let contributions = $state()
async function loadContributions() {
contributions = await getContributions()
}
$effect(() => {
loadContributions()
intervalId = setInterval(loadContributions, 1000 * 5)
return () => {
if (intervalId) clearInterval(intervalId)
}
})
</script>
{#if contributions}

<div class="flex flex-col items-center h-svh overflow-y-auto pb-24 pt-36 w-full">
<div class="w-full h-48 bg-gradient-to-b via-black from-black to-transparent absolute top-0"></div>
<div class="flex flex-col items-center max-w-md">
<div class="rounded-full border-[2px] h-8 w-8"></div>
<div class="h-24 w-[2px] bg-white"></div>
{#each contributions as contribution, index }
<a href="/contributions?hash={contribution.public_key_hash}" class="flex items-center gap-4 w-full">

<Text>{(index + 1) * 10}M</Text>
<div class="text-white flex gap-1 items-center border-white border px-3 py-2 w-full">
<img class="size-7" src={contribution.avatar_url} alt="">
<Text class="uppercase max-w-48 truncate">{contribution.user_name}</Text>
</div>
</a>
{#if index !== contributions.length - 1}
<div class="h-12 w-[2px] bg-white"></div>
{/if}
{/each}
</div>
</div>
{:else}
<Spinner class="size-5 text-union-accent-500"/>
{/if}
Loading

0 comments on commit 99bbf7b

Please sign in to comment.