diff --git a/uplink-client/.yarn/install-state.gz b/uplink-client/.yarn/install-state.gz index d7e95b87..16a98b7d 100644 Binary files a/uplink-client/.yarn/install-state.gz and b/uplink-client/.yarn/install-state.gz differ diff --git a/uplink-client/next.config.js b/uplink-client/next.config.js index 1c26f280..a78c99c4 100644 --- a/uplink-client/next.config.js +++ b/uplink-client/next.config.js @@ -9,11 +9,6 @@ const nextConfig = { contentDispositionType: 'attachment', contentSecurityPolicy: "default-src 'self'; script-src 'none'; sandbox;", remotePatterns: [ - { - protocol: 'https', - hostname: 'res.cloudinary.com', - port: '', - }, { protocol: 'https', hostname: 'calabara.mypinata.cloud', diff --git a/uplink-client/package.json b/uplink-client/package.json index d8ff5a5c..00df4bc8 100644 --- a/uplink-client/package.json +++ b/uplink-client/package.json @@ -62,6 +62,7 @@ "next-mdx-remote": "^5.0.0", "node-gyp": "^10.0.1", "popmotion": "^11.0.5", + "probe-image-size": "^7.2.3", "react": "^18", "react-day-picker": "^8.9.1", "react-dom": "^18", @@ -103,6 +104,7 @@ "@storybook/testing-library": "^0.2.1", "@tailwindcss/typography": "^0.5.14", "@types/node": "^20", + "@types/probe-image-size": "^7", "@types/react": "^18", "@types/react-dom": "^18", "@types/react-lazyload": "^3", diff --git a/uplink-client/src/app/(home)/page.tsx b/uplink-client/src/app/(home)/page.tsx index 9fa4ec4e..e92fb54e 100644 --- a/uplink-client/src/app/(home)/page.tsx +++ b/uplink-client/src/app/(home)/page.tsx @@ -13,7 +13,7 @@ import { ChainLabel } from "@/ui/ChainLabel/ChainLabel"; import { RenderMintMedia } from "@/ui/Token/MintUtils"; import { ImageWrapper } from "@/app/(legacy)/contest/components/MediaWrapper"; import { MdAccessibility, MdDashboardCustomize, MdGroups } from "react-icons/md"; -import OptimizedImage from "@/lib/OptmizedImage"; +import OptimizedImage from "@/lib/OptimizedImage"; export const dynamic = 'force-static'; export const runtime = 'nodejs'; diff --git a/uplink-client/src/app/(legacy)/contest/[id]/page.tsx b/uplink-client/src/app/(legacy)/contest/[id]/page.tsx index e19b7c60..065077d8 100644 --- a/uplink-client/src/app/(legacy)/contest/[id]/page.tsx +++ b/uplink-client/src/app/(legacy)/contest/[id]/page.tsx @@ -4,7 +4,7 @@ import { import { Suspense } from "react"; import fetchLegacyContest from "@/lib/fetch/fetchLegacyContest"; import Link from "next/link"; -import OptimizedImage from "@/lib/OptmizedImage"; +import OptimizedImage from "@/lib/OptimizedImage"; import { ChannelStateLabel } from "@/ui/ChannelSidebar/SidebarUtils"; import ExpandableTextSection from "@/ui/ExpandableTextSection/ExpandableTextSection"; import ParseBlocks from "@/lib/blockParser"; diff --git a/uplink-client/src/app/(legacy)/contest/components/CardSubmission.tsx b/uplink-client/src/app/(legacy)/contest/components/CardSubmission.tsx index 6f3a4062..bb89e473 100644 --- a/uplink-client/src/app/(legacy)/contest/components/CardSubmission.tsx +++ b/uplink-client/src/app/(legacy)/contest/components/CardSubmission.tsx @@ -12,7 +12,7 @@ import { ParseThread } from "@/lib/threadParser"; import { RenderInteractiveVideoWithLoader } from "@/ui/VideoPlayer"; import { Decimal } from "decimal.js"; import formatDecimal from "@/lib/formatDecimal"; -import OptimizedImage from "@/lib/OptmizedImage" +import OptimizedImage from "@/lib/OptimizedImage" const ParseBlocks = dynamic(() => import("@/lib/blockParser"), { ssr: false, diff --git a/uplink-client/src/app/(legacy)/contest/components/ExpandedSubmission.tsx b/uplink-client/src/app/(legacy)/contest/components/ExpandedSubmission.tsx index 54f02a2d..25483304 100644 --- a/uplink-client/src/app/(legacy)/contest/components/ExpandedSubmission.tsx +++ b/uplink-client/src/app/(legacy)/contest/components/ExpandedSubmission.tsx @@ -1,6 +1,6 @@ // Render the submission in a large format. This is used for modals and the submission page. import type { Submission } from "@/types/submission"; -import OptimizedImage from "@/lib/OptmizedImage" +import OptimizedImage from "@/lib/OptimizedImage" const ParseBlocks = dynamic(() => import("@/lib/blockParser"), { ssr: false, loading: () => ( diff --git a/uplink-client/src/app/(space)/[name]/contest/[contractId]/@sidebar/(enabled)/layout.tsx b/uplink-client/src/app/(space)/[name]/contest/[contractId]/@sidebar/(enabled)/layout.tsx index a78fdcce..b3b07f83 100644 --- a/uplink-client/src/app/(space)/[name]/contest/[contractId]/@sidebar/(enabled)/layout.tsx +++ b/uplink-client/src/app/(space)/[name]/contest/[contractId]/@sidebar/(enabled)/layout.tsx @@ -15,7 +15,7 @@ const VoteCart = dynamic( } ) -const StateSpecificSidebar = async ({ contractId }: { contractId: ContractID }) => { +const StateSpecificSidebar = async ({ spaceName, contractId }: { spaceName: string, contractId: ContractID }) => { const channel = await fetchChannel(contractId) @@ -29,6 +29,7 @@ const StateSpecificSidebar = async ({ contractId }: { contractId: ContractID }) contractId={contractId} detailsChild={
{children}
}> - + diff --git a/uplink-client/src/app/(space)/[name]/contest/[contractId]/post/[postId]/page.tsx b/uplink-client/src/app/(space)/[name]/contest/[contractId]/post/[postId]/page.tsx index bfde35a9..3b58ae74 100644 --- a/uplink-client/src/app/(space)/[name]/contest/[contractId]/post/[postId]/page.tsx +++ b/uplink-client/src/app/(space)/[name]/contest/[contractId]/post/[postId]/page.tsx @@ -1,9 +1,8 @@ -import { calculateImageAspectRatio } from "@/lib/farcaster/utils"; import fetchChannel from "@/lib/fetch/fetchChannel"; import fetchSingleSpace from "@/lib/fetch/fetchSingleSpace"; import { fetchSingleTokenIntent, fetchSingleTokenV2 } from "@/lib/fetch/fetchTokensV2"; import { parseIpfsUrl } from "@/lib/ipfs"; -import OptimizedImage from "@/lib/OptmizedImage"; +import OptimizedImage from "@/lib/OptimizedImage"; import { ContractID } from "@/types/channel"; import { Button } from "@/ui/DesignKit/Button"; import { MintTokenSwitch } from "@/ui/Token/MintToken"; @@ -23,15 +22,10 @@ export async function generateMetadata({ const { name: spaceName, contractId, postId } = params const isIntent = searchParams?.intent ? true : false - const referral = searchParams?.referrer ?? "" - - const channel = await fetchChannel(contractId); const token = isIntent ? await fetchSingleTokenIntent(contractId, postId) : await fetchSingleTokenV2(contractId, postId) const author = token.author - const aspect = await calculateImageAspectRatio(parseIpfsUrl(token.metadata.image).gateway) - // const fcMetadata: Record = { // "fc:frame": "vNext", // "fc:frame:image": parseIpfsUrl(token.metadata.image).gateway, @@ -168,7 +162,6 @@ export default function Page({ params, searchParams }: { params: { name: string, return (
- {/* */} }> diff --git a/uplink-client/src/app/(space)/[name]/mintboard/(redirect)/page.tsx b/uplink-client/src/app/(space)/[name]/mintboard/(redirect)/page.tsx index a28ddb6a..d9caee30 100644 --- a/uplink-client/src/app/(space)/[name]/mintboard/(redirect)/page.tsx +++ b/uplink-client/src/app/(space)/[name]/mintboard/(redirect)/page.tsx @@ -11,9 +11,8 @@ import { TbLoader2 } from "react-icons/tb"; // redirect legacy mintboards to v2 mintboards const Redirect = async ({ spaceName }: { spaceName: string }) => { - const channels = await fetchSpaceChannels(spaceName); - const mintboards = channels.filter(channel => isInfiniteChannel(channel)); - const mintboard = mintboards[0]; + const channels = await fetchSpaceChannels(spaceName, 8453); + const mintboard = channels.infiniteChannels[0]; if (!mintboard) notFound(); diff --git a/uplink-client/src/app/(space)/[name]/mintboard/(redirect)/post/[postId]/page.tsx b/uplink-client/src/app/(space)/[name]/mintboard/(redirect)/post/[postId]/page.tsx index ee4bcd91..564531b2 100644 --- a/uplink-client/src/app/(space)/[name]/mintboard/(redirect)/post/[postId]/page.tsx +++ b/uplink-client/src/app/(space)/[name]/mintboard/(redirect)/post/[postId]/page.tsx @@ -8,9 +8,8 @@ import { TbLoader2 } from "react-icons/tb"; // redirect legacy mintboard posts to v2 mintboard posts const Redirect = async ({ spaceName, postId }: { spaceName: string, postId: string }) => { - const channels = await fetchSpaceChannels(spaceName); - const mintboards = channels.filter(channel => isInfiniteChannel(channel)); - const mintboard = mintboards[0]; + const channels = await fetchSpaceChannels(spaceName, 8453); + const mintboard = channels.infiniteChannels[0]; if (!mintboard) notFound(); diff --git a/uplink-client/src/app/(space)/[name]/mintboard/[contractId]/page.tsx b/uplink-client/src/app/(space)/[name]/mintboard/[contractId]/page.tsx index 917970ed..446b0a52 100644 --- a/uplink-client/src/app/(space)/[name]/mintboard/[contractId]/page.tsx +++ b/uplink-client/src/app/(space)/[name]/mintboard/[contractId]/page.tsx @@ -2,7 +2,7 @@ import fetchChannel from '@/lib/fetch/fetchChannel'; import fetchSingleSpace from '@/lib/fetch/fetchSingleSpace'; import { fetchPopularTokens, fetchTokenIntents, fetchTokensV1, fetchTokensV2 } from '@/lib/fetch/fetchTokensV2'; import { parseIpfsUrl } from '@/lib/ipfs'; -import OptimizedImage from '@/lib/OptmizedImage'; +import OptimizedImage from '@/lib/OptimizedImage'; import SwrProvider from '@/providers/SwrProvider'; import { Boundary } from '@/ui/Boundary/Boundary'; import Link from 'next/link'; diff --git a/uplink-client/src/app/(space)/[name]/mintboard/[contractId]/post/[postId]/v2/page.tsx b/uplink-client/src/app/(space)/[name]/mintboard/[contractId]/post/[postId]/v2/page.tsx index 45420c47..01fc9aff 100644 --- a/uplink-client/src/app/(space)/[name]/mintboard/[contractId]/post/[postId]/v2/page.tsx +++ b/uplink-client/src/app/(space)/[name]/mintboard/[contractId]/post/[postId]/v2/page.tsx @@ -107,7 +107,6 @@ const PageContent = async ({ spaceName, contractId, postId, searchParams }: { sp isIntent ? fetchSingleTokenIntent(contractId, postId) : fetchSingleTokenV2(contractId, postId) ]) - return (
diff --git a/uplink-client/src/app/(space)/[name]/page.tsx b/uplink-client/src/app/(space)/[name]/page.tsx index 9c551191..c3d780c6 100644 --- a/uplink-client/src/app/(space)/[name]/page.tsx +++ b/uplink-client/src/app/(space)/[name]/page.tsx @@ -4,7 +4,7 @@ import { FaTwitter } from "react-icons/fa"; import fetchSingleSpace from "@/lib/fetch/fetchSingleSpace"; import { Suspense } from "react"; import { HiSparkles } from "react-icons/hi2"; -import OptimizedImage from "@/lib/OptmizedImage" +import OptimizedImage from "@/lib/OptimizedImage" import { Boundary } from "@/ui/Boundary/Boundary"; import { AdminWrapper } from "@/lib/AdminWrapper"; import { parseIpfsUrl } from "@/lib/ipfs"; diff --git a/uplink-client/src/app/explore/client.tsx b/uplink-client/src/app/explore/client.tsx index 7444f806..fea84847 100644 --- a/uplink-client/src/app/explore/client.tsx +++ b/uplink-client/src/app/explore/client.tsx @@ -1,7 +1,7 @@ "use client";; import { Space } from "@/types/space"; import { useEffect, useRef, useState } from "react"; -import OptimizedImage from "@/lib/OptmizedImage"; +import OptimizedImage from "@/lib/OptimizedImage"; import Link from "next/link"; import { Input } from "@/ui/DesignKit/Input"; diff --git a/uplink-client/src/app/explore/page.tsx b/uplink-client/src/app/explore/page.tsx index 865b6b4e..8ffe46e5 100644 --- a/uplink-client/src/app/explore/page.tsx +++ b/uplink-client/src/app/explore/page.tsx @@ -2,7 +2,7 @@ import Link from "next/link"; import { Metadata } from "next"; import fetchSpaces from "@/lib/fetch/fetchSpaces"; import { Suspense } from "react"; -import OptimizedImage from "@/lib/OptmizedImage" +import OptimizedImage from "@/lib/OptimizedImage" import { SearchSpaces } from "./client"; import { ColorCards } from "@/ui/DesignKit/ColorCards"; import { Card, CardContent, CardFooter } from "@/ui/DesignKit/Card"; diff --git a/uplink-client/src/lib/OptmizedImage.tsx b/uplink-client/src/lib/OptimizedImage.tsx similarity index 100% rename from uplink-client/src/lib/OptmizedImage.tsx rename to uplink-client/src/lib/OptimizedImage.tsx diff --git a/uplink-client/src/lib/blockParser.tsx b/uplink-client/src/lib/blockParser.tsx index eb9da4c1..dd0e42c1 100644 --- a/uplink-client/src/lib/blockParser.tsx +++ b/uplink-client/src/lib/blockParser.tsx @@ -3,7 +3,7 @@ import { ImageWrapper } from "@/app/(legacy)/contest/components/MediaWrapper"; import type { OutputData } from "@editorjs/editorjs"; import React, { useEffect } from "react"; import Output, { LinkToolOutput, ListOutput, ParagraphOutput } from 'editorjs-react-renderer'; -import OptimizedImage from "@/lib/OptmizedImage" +import OptimizedImage from "@/lib/OptimizedImage" const createLinks = (text: string): string => { const urlRegex = /(https?:\/\/[^\s]+)/g; diff --git a/uplink-client/src/lib/farcaster/utils.ts b/uplink-client/src/lib/farcaster/utils.ts index cfdc67ca..39664b25 100644 --- a/uplink-client/src/lib/farcaster/utils.ts +++ b/uplink-client/src/lib/farcaster/utils.ts @@ -1,16 +1,24 @@ import { FrameRequest, MockFrameRequest, FrameValidationResponse, FrameMetadataHtmlResponse } from "./types"; import { neynarFrameValidation } from "./neynar"; +import probe from "probe-image-size"; -export const calculateImageAspectRatio = async (url: string) => { +export const calculateImageAspectRatio = async (url: string): Promise => { try { - const fileInfo = await fetch(`https://res.cloudinary.com/drrkx8iye/image/fetch/fl_getinfo/${url}`).then(res => res.json()) - const { output } = fileInfo; - if (output.width / output.height > 1.45) return "1.91:1"; - return "1:1" + const fileInfo = await probe(url); + const ratio = fileInfo.width / fileInfo.height; + + if (ratio > 1.45) { + // Landscape: Width is greater than height + return "1.91:1"; + } + // Square or portrait: Height is equal to or greater than width + return "1:1"; } catch (e) { - return "1:1" + console.error("Error calculating aspect ratio:", e); + return "1:1"; } -} +}; + type FrameMessageOptions = | { diff --git a/uplink-client/src/lib/threadParser.tsx b/uplink-client/src/lib/threadParser.tsx index 120e62f1..f536fb55 100644 --- a/uplink-client/src/lib/threadParser.tsx +++ b/uplink-client/src/lib/threadParser.tsx @@ -3,7 +3,7 @@ import { ImageWrapper } from "@/app/(legacy)/contest/components/MediaWrapper"; import { RenderStandardVideoWithLoader } from "@/ui/VideoPlayer"; import Image from "next/image"; import sanitizeHtml from "sanitize-html"; -import OptimizedImage from "@/lib/OptmizedImage" +import OptimizedImage from "@/lib/OptimizedImage" const createLinks = (text: string): string => { const urlRegex = /(https?:\/\/[^\s]+)/g; const twitterRegex = /([^\S]|^)@(\w+)/gi; diff --git a/uplink-client/src/lib/twitterMediaUpload.ts b/uplink-client/src/lib/twitterMediaUpload.ts deleted file mode 100644 index 71d828f7..00000000 --- a/uplink-client/src/lib/twitterMediaUpload.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { generateVideoThumbnails } from "@rajesh896/video-thumbnails-generator"; -import { useState, useTransition, useEffect } from "react"; -import FormData from 'form-data'; - -export class MediaUploadError { - code: number; - message: string; - constructor({ code, message }: { code: number, message: string }) { - this.code = code; - this.message = message; - } -} - -export const dataURLtoBlob = (dataUrl: string) => { - return fetch(dataUrl) - .then(res => res.blob()) -} - -export const IpfsUpload = async (file: File | Blob) => { - - const formData = new FormData(); - formData.append('file', file); - - try { - const response = await fetch("https://api.pinata.cloud/pinning/pinFileToIPFS", { - method: 'POST', - headers: { - 'Authorization': `Bearer ${process.env.NEXT_PUBLIC_PINATA_JWT}`, - }, - // @ts-expect-error - body: formData - }); - - if (!response.ok) { - console.error(`HTTP error! Status: ${response.status}`); - return null - } - - const responseData = await response.json(); - return `https://uplink.mypinata.cloud/ipfs/${responseData.IpfsHash}`; - } catch (err) { - console.error("Fetch error:", err); - return null; - } -}; - -const loadVideo = (file: File) => - new Promise((resolve, reject) => { - try { - let video = document.createElement('video'); - video.preload = 'metadata'; - - // Create an object URL for the file - const objectUrl = URL.createObjectURL(file); - video.src = objectUrl; - - video.onloadedmetadata = function () { - // Release the object URL after metadata is loaded - URL.revokeObjectURL(objectUrl); - resolve(this); - } - - video.onerror = function () { - reject("Invalid video. Please select a video file."); - } - } catch (e) { - reject(e); - } - }); - -const handleMediaUpload = async ( - event: any, - acceptedFormats: string[], - mimeTypeCallback: (mimeType: string) => void, - readerCallback: (data: any, mimeType: string) => void, - ipfsCallback: (uri: string, mimeType: string) => void, - videoThumbnailCallback?: (thumbnails: string[]) => void, - fileSizeCallback?: (size: number) => void, -) => { - - const acceptedMimeTypes = acceptedFormats.reduce((acc: string[], format: string) => { - if (format === 'image') { - return [...acc, 'image/png', 'image/jpeg', 'image/jpg', 'image/gif']; - } - if (format === 'video') { - return [...acc, 'video/mp4']; - } - if (format === 'svg') { - return [...acc, 'image/svg+xml']; - } - return acc; - }, []); - - const file = event.target.files[0]; - if (!file) { - throw new MediaUploadError({ code: 1, message: 'No file selected' }) - } - - const mimeType = file.type; - mimeTypeCallback(mimeType); - const fileSize = file.size; - if (fileSizeCallback) fileSizeCallback(fileSize) - - if (mimeType.includes("video")) { - const video: any = await loadVideo(file); - - if (video.duration > 140) throw new MediaUploadError({ code: 2, message: 'Videos must be less than 140 seconds' }); - - if (videoThumbnailCallback) { // if its a video and caller wants thumbnails, generate thumbnails - const thumbnails = await generateVideoThumbnails(file, 3, 'jpeg'); - videoThumbnailCallback(thumbnails); - } - } else { - if (fileSize > 5000000) throw new MediaUploadError({ code: 2, message: 'Images must be less than 5MB' }); - } - - if (!acceptedMimeTypes.includes(mimeType)) throw new MediaUploadError({ code: 3, message: 'Invalid file type.' }); - - const reader = new FileReader(); - reader.readAsDataURL(file); - reader.onload = () => { - readerCallback(reader.result, mimeType); - }; - reader.onerror = (error) => { - throw new MediaUploadError({ code: 4, message: 'Error reading file' }) - }; - - const response = await IpfsUpload(file); - if (!response) throw new MediaUploadError({ code: 5, message: 'Error uploading to IPFS' }) - - ipfsCallback(response, mimeType); -}; - -export default handleMediaUpload; diff --git a/uplink-client/src/test/ContestHandler.test.ts b/uplink-client/src/test/ContestHandler.test.ts deleted file mode 100644 index 350c82f1..00000000 --- a/uplink-client/src/test/ContestHandler.test.ts +++ /dev/null @@ -1,610 +0,0 @@ -import { describe, expect, test } from "@jest/globals"; -import { sampleERC1155Token, sampleERC20Token, sampleERC721Token, sampleETHToken } from "./sampleTokens"; -import { Prompt, SubmitterRewards, VoterRewards, VotingPolicyType, validateDeadlines, validateMetadata, validatePrompt, validateSubmitterRewards, validateVoterRewards, validateVotingPolicy } from "@/ui/ContestForm/contestHandler"; - - - -describe("Contest Handler", () => { - describe("Validate Contest Metadata", () => { - test("fail with null values", () => { - const { isError, errors, data } = validateMetadata({ type: null, category: null }) - expect(isError).toBe(true) - expect(errors).toEqual({ - type: "Please select a contest type", - category: "Please select a contest category", - }) - }) - - test("pass with valid inputs", () => { - const { isError, errors, data } = validateMetadata({ - type: "standard", - category: "art", - }) - expect(isError).toBe(false) - expect(errors).toEqual({ - type: "", - category: "", - }) - expect(data).toEqual({ - type: "standard", - category: "art", - }) - }) - }); - - describe("Validate Contest Deadlines", () => { - test("fail with empty string values", () => { - const deadlines = { - startTime: "", - voteTime: "", - endTime: "", - snapshot: "", - } - const { isError, errors, data } = validateDeadlines(deadlines, false) - - expect(isError).toBe(true) - expect(errors).toEqual({ - snapshot: "snapshot date is required", - startTime: "start date is required", - voteTime: "vote date is required", - endTime: "end date is required", - }) - expect(data).toEqual(deadlines) - }) - - test("fail with incorrect order #1", () => { - const deadlines = { - startTime: new Date(Date.now() + 5 * 864e5).toISOString(), - voteTime: new Date(Date.now() + 4 * 864e5).toISOString(), - endTime: new Date(Date.now() + 3 * 864e5).toISOString(), - snapshot: new Date(Date.now() + 6 * 864e5).toISOString(), - } - const { isError, errors, data } = validateDeadlines(deadlines, false) - - expect(isError).toBe(true) - expect(errors).toEqual({ - snapshot: "snapshot date must be less than or equal to start date", - voteTime: "vote date must be after start date", - endTime: "end date must be after start date", - startTime: "", - }) - expect(data).toEqual(deadlines) - }) - - test("fail with incorrect order #2", () => { - const deadlines = { - snapshot: new Date(Date.now() + 1 * 864e5).toISOString(), - startTime: new Date(Date.now() + 2 * 864e5).toISOString(), - voteTime: new Date(Date.now() + 4 * 864e5).toISOString(), - endTime: new Date(Date.now() + 3 * 864e5).toISOString(), - } - const { isError, errors, data } = validateDeadlines(deadlines, false) - - expect(isError).toBe(true) - expect(errors).toEqual({ - snapshot: "", - voteTime: "", - endTime: "end date must be after vote date", - startTime: "", - }) - expect(data).toEqual(deadlines) - }) - - test("pass with correct order", () => { - const deadlines = { - snapshot: new Date(Date.now() + 1 * 864e5).toISOString(), - startTime: new Date(Date.now() + 2 * 864e5).toISOString(), - voteTime: new Date(Date.now() + 3 * 864e5).toISOString(), - endTime: new Date(Date.now() + 4 * 864e5).toISOString(), - } - const { isError, errors, data } = validateDeadlines(deadlines, false) - expect(isError).toBe(false) - expect(errors).toEqual({ - snapshot: "", - startTime: "", - voteTime: "", - endTime: "", - - }) - expect(data).toEqual(deadlines) - }); - - test("pass with now for snapshot", () => { - const deadlines = { - snapshot: "now", - startTime: new Date(Date.now() + 2 * 864e5).toISOString(), - voteTime: new Date(Date.now() + 3 * 864e5).toISOString(), - endTime: new Date(Date.now() + 4 * 864e5).toISOString(), - } - const { isError, errors, data } = validateDeadlines(deadlines, false) - expect(isError).toBe(false) - expect(errors).toEqual({ - snapshot: "", - startTime: "", - voteTime: "", - endTime: "", - - }) - expect(data).toEqual(deadlines) - }) - test("pass with now for startTime", () => { - const deadlines = { - snapshot: new Date(Date.now()).toISOString(), - startTime: "now", - voteTime: new Date(Date.now() + 3 * 864e5).toISOString(), - endTime: new Date(Date.now() + 4 * 864e5).toISOString(), - } - const { isError, errors, data } = validateDeadlines(deadlines, false) - expect(isError).toBe(false) - expect(errors).toEqual({ - snapshot: "", - startTime: "", - voteTime: "", - endTime: "", - - }) - expect(data).toEqual(deadlines) - }) - - test("pass with now for startTime + snapshot", () => { - const deadlines = { - snapshot: "now", - startTime: "now", - voteTime: new Date(Date.now() + 3 * 864e5).toISOString(), - endTime: new Date(Date.now() + 4 * 864e5).toISOString(), - } - const { isError, errors, data } = validateDeadlines(deadlines, false) - expect(isError).toBe(false) - expect(errors).toEqual({ - snapshot: "", - startTime: "", - voteTime: "", - endTime: "", - - }) - expect(data).toEqual(deadlines) - }) - - test("return cleaned data", () => { - const deadlines = { - snapshot: "now", - startTime: "now", - voteTime: new Date(Date.now() + 3 * 864e5).toISOString(), - endTime: new Date(Date.now() + 4 * 864e5).toISOString(), - } - const { isError, errors, data } = validateDeadlines(deadlines, true) - expect(isError).toBe(false) - expect(errors).toEqual({ - snapshot: "", - startTime: "", - voteTime: "", - endTime: "", - - }) - expect(data.voteTime).toEqual(deadlines.voteTime) - expect(data.endTime).toEqual(deadlines.endTime) - expect(data.snapshot.length).toBeGreaterThan(3) // check that it is not "now" anymore - expect(data.startTime.length).toBeGreaterThan(3) // check that it is not "now" anymore - }) - }) - - - describe("Validate Contest Prompt", () => { - - test("fail with invalid values", () => { - const prompt: Prompt = { - title: "", - body: null, - coverUrl: "https://google.com/image.png", - coverBlob: "asdf" - } - const { isError, errors, data } = validatePrompt(prompt) - - expect(isError).toBe(true) - expect(errors).toEqual({ - title: "Please provide a title", - body: "Please provide a prompt body", - coverUrl: "Invalid cover image", - }) - expect(data).toEqual(prompt) - }) - - - test("fail with ", () => { - const prompt: Prompt = { - title: "", - body: null, - coverUrl: "", - coverBlob: "" - } - const { isError, errors, data } = validatePrompt(prompt) - - expect(isError).toBe(true) - expect(errors).toEqual({ - title: "Please provide a title", - body: "Please provide a prompt body", - coverUrl: "", - }) - expect(data).toEqual(prompt) - }) - - test("fail with empty blocks", () => { - const prompt: Prompt = { - title: " aaaaaa ", - body: { - blocks: [] - }, - coverUrl: "", - coverBlob: "" - } - const { isError, errors, data } = validatePrompt(prompt) - - expect(isError).toBe(true) - expect(errors).toEqual({ - title: "", - body: "Please provide a prompt body", - coverUrl: "", - }) - expect(data).toEqual(prompt) - }) - - test("pass with valid inputs", () => { - const prompt = { - title: " aaaaaa ", - body: { - blocks: [{ - type: "paragraph", - data: { - text: "test" - } - }] - }, - coverUrl: "https://uplink.mypinata.cloud/ipfs/QmZ1Z2Z3Z4Z5Z6Z7Z8Z9Z0", - coverBlob: "asdf" - } - const { isError, errors, data } = validatePrompt(prompt) - - expect(isError).toBe(false) - expect(errors).toEqual({ - title: "", - body: "", - coverUrl: "", - }) - expect(data).toEqual(prompt) - }) - }); - - - // remember that this function is also cleaning the data (for now) - describe("Validate Submitter Rewards", () => { - - test("fail with duplicate ranks", () => { - const rewards: SubmitterRewards = { - ETH: sampleETHToken, - payouts: [ - { - rank: 1, - ETH: { amount: '1' }, - }, - { - rank: 1, - ETH: { amount: '1' }, - }, - { - rank: 2, - ETH: { amount: '1' }, - }, - ], - }; - - const { isError, errors, data } = validateSubmitterRewards(rewards); - expect(isError).toBe(true); - expect(errors).toEqual({ - duplicateRanks: [1] - }); - expect(data).toEqual(rewards); - - }) - test("pass with empty rewards", () => { - const rewards: SubmitterRewards = {}; - - const { isError, errors, data } = validateSubmitterRewards(rewards); - expect(isError).toBe(false); - expect(errors).toEqual({ - duplicateRanks: [] - }); - expect(data).toEqual(rewards); - - }) - test("pass with valid rewards", () => { - const rewards: SubmitterRewards = { - ETH: sampleETHToken, - ERC20: sampleERC20Token, - ERC721: sampleERC721Token, - ERC1155: sampleERC1155Token, - payouts: [ - { - rank: 1, - ETH: { amount: '1' }, - ERC20: { amount: '1' }, - ERC721: { tokenId: 1 }, - ERC1155: { amount: '2' }, - }, - { - rank: 2, - ETH: { amount: '1' }, - ERC20: { amount: '1' }, - ERC721: { tokenId: 1 }, - ERC1155: { amount: '2' }, - }, - ], - }; - - const { isError, errors, data } = validateSubmitterRewards(rewards); - expect(isError).toBe(false); - expect(errors).toEqual({ - duplicateRanks: [] - }); - expect(data).toEqual(rewards); - - }) - test("clean empty rewards #1", () => { - const rewards: SubmitterRewards = { - ETH: sampleETHToken, - ERC20: sampleERC20Token, - ERC721: sampleERC721Token, - ERC1155: sampleERC1155Token, - payouts: [ - { - rank: 1, - ETH: { amount: '1' }, - ERC20: { amount: '1' }, - ERC721: { tokenId: 1 }, - }, - { - rank: 2, - ETH: { amount: '1' }, - ERC20: { amount: '1' }, - ERC721: { tokenId: 1 }, - }, - ], - }; - - const { isError, errors, data } = validateSubmitterRewards(rewards); - expect(isError).toBe(false); - expect(errors).toEqual({ - duplicateRanks: [] - }); - expect(data).toEqual({ ...rewards, ERC1155: undefined }); - }) - - test("clean empty rewards #2", () => { - const rewards: SubmitterRewards = { - ETH: sampleETHToken, - ERC20: sampleERC20Token, - ERC721: sampleERC721Token, - payouts: [ - { - rank: 1, - ETH: { amount: '1' }, - ERC20: { amount: '1' }, - ERC721: { tokenId: null }, - }, - { - rank: 2, - ETH: { amount: '1' }, - ERC20: { amount: '' }, - ERC721: { tokenId: null }, - }, - ], - }; - - const { isError, errors, data } = validateSubmitterRewards(rewards); - expect(isError).toBe(false); - expect(errors).toEqual({ - duplicateRanks: [] - }); - expect(data).toEqual({ - ETH: sampleETHToken, - ERC20: sampleERC20Token, - payouts: [ - { - rank: 1, - ETH: { amount: '1' }, - ERC20: { amount: '1' }, - }, - { - rank: 2, - ETH: { amount: '1' }, - }, - ] - }); - }) - - test("clean empty rewards #3", () => { - const rewards: SubmitterRewards = { - ETH: sampleETHToken, - ERC20: sampleERC20Token, - ERC721: sampleERC721Token, - payouts: [ - { - rank: 1, - ETH: { amount: '' }, - ERC20: { amount: '' }, - ERC721: { tokenId: null }, - }, - { - rank: 2, - ETH: { amount: '' }, - ERC20: { amount: '' }, - ERC721: { tokenId: null }, - }, - ], - }; - - const { isError, errors, data } = validateSubmitterRewards(rewards); - expect(isError).toBe(false); - expect(errors).toEqual({ - duplicateRanks: [] - }); - expect(data).toEqual({}); - }) - }) - - - - describe("Validate Voter Rewards", () => { - test("fail with duplicate ranks", () => { - const rewards: VoterRewards = { - ETH: sampleETHToken, - payouts: [ - { - rank: 1, - ETH: { amount: '1' }, - }, - { - rank: 1, - ETH: { amount: '1' }, - }, - ] - } - const { errors, isError, data } = validateVoterRewards(rewards); - expect(isError).toBe(true); - expect(errors).toEqual({ - duplicateRanks: [1] - }); - expect(data).toEqual(rewards); - }); - - test("pass with empty rewards", () => { - const rewards: VoterRewards = {}; - const { errors, isError, data } = validateVoterRewards(rewards); - expect(isError).toBe(false); - expect(errors).toEqual({ - duplicateRanks: [] - }); - expect(data).toEqual(rewards); - }); - - test("pass with valid rewards", () => { - const rewards: VoterRewards = { - ETH: sampleETHToken, - ERC20: sampleERC20Token, - payouts: [ - { - rank: 1, - ETH: { amount: '1' }, - }, - { - rank: 2, - ERC20: { amount: '30' }, - } - ] - } - - const { errors, isError, data } = validateVoterRewards(rewards); - expect(isError).toBe(false); - expect(errors).toEqual({ - duplicateRanks: [] - }); - expect(data).toEqual(rewards); - }); - - test("clean empty rewards #1", () => { - const rewards: VoterRewards = { - ETH: sampleETHToken, - ERC20: sampleERC20Token, - payouts: [ - { - rank: 1, - ETH: { amount: '1' }, - }, - { - rank: 2, - ERC20: { amount: '' }, - } - - ] - } - const { errors, isError, data } = validateVoterRewards(rewards); - expect(isError).toBe(false); - expect(errors).toEqual({ - duplicateRanks: [] - }); - expect(data).toEqual({ - ETH: sampleETHToken, - payouts: [ - { - rank: 1, - ETH: { amount: '1' }, - }, - ] - }); - }); - - test("clean empty rewards #2", () => { - const rewards: VoterRewards = { - ETH: sampleETHToken, - ERC20: sampleERC20Token, - payouts: [ - { - rank: 1, - ETH: { amount: '' }, - }, - { - rank: 2, - ERC20: { amount: '' }, - } - - ] - } - const { errors, isError, data } = validateVoterRewards(rewards); - expect(isError).toBe(false); - expect(errors).toEqual({ - duplicateRanks: [] - }); - expect(data).toEqual({}); - }); - - - - - describe("Validate Voting Policy", () => { - test("fail with empty policy", () => { - const votingPolicy: VotingPolicyType[] = []; - const { errors, isError, data } = validateVotingPolicy(votingPolicy); - expect(isError).toBe(true); - expect(errors).toEqual( - "Please add at least one voting policy", - ); - expect(data).toEqual(votingPolicy); - }); - - test("pass with valid policy", () => { - const votingPolicy: VotingPolicyType[] = [ - { - token: sampleETHToken, - strategy: { - type: "weighted", - } - - }, - { - token: sampleERC20Token, - strategy: { - type: "arcade", - votingPower: '1', - } - } - ] - - const { errors, isError, data } = validateVotingPolicy(votingPolicy); - expect(isError).toBe(false); - expect(errors).toEqual(""); - expect(data).toEqual(votingPolicy); - }); - }) - - }); - -}) \ No newline at end of file diff --git a/uplink-client/src/test/SpaceHandler.test.ts b/uplink-client/src/test/SpaceHandler.test.ts deleted file mode 100644 index 7126f1b0..00000000 --- a/uplink-client/src/test/SpaceHandler.test.ts +++ /dev/null @@ -1,197 +0,0 @@ -import { SpaceBuilderProps, validateSpaceBuilderProps, reducer, validateSpaceAdmins, validateSpaceName, validateSpaceLogo, validateSpaceWebsite, validateSpaceTwitter } from '@/app/spacebuilder/spaceHandler'; -import { describe, expect, test } from "@jest/globals"; - -const calabaraAddress = "0xa943e039B1Ce670873ccCd4024AB959082FC6Dd8" -const vitalikAddress = "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045" - -describe('Space Handler', () => { - describe('Validate Space Name', () => { - test('should fail with empty string', () => { - const { error, value } = validateSpaceName(''); - expect(error).toEqual('Name is required'); - expect(value).toEqual(''); - }) - test('should fail with string too short', () => { - const { error, value } = validateSpaceName('a'.repeat(2)); - expect(error).toEqual('Name must be at least 3 characters long'); - expect(value).toEqual('a'.repeat(2)); - }) - test('should fail with string too long', () => { - const { error, value } = validateSpaceName('a'.repeat(31)); - expect(error).toEqual('Name must be less than 30 characters long'); - expect(value).toEqual('a'.repeat(31)); - }) - test('should fail with non-alphanumeric chars', () => { - const name = 'test!'; - const { error, value } = validateSpaceName(name); - expect(error).toEqual('Name must only contain alphanumeric characters and underscores'); - expect(value).toEqual(name); - }) - }) - - describe('Validate Space Logo', () => { - test('should succeed with platform ipfs link', () => { - const logo = 'https://uplink.mypinata.cloud/ipfs/QmUxRzCcizzuNdyPRxMdn4LFQQQ5ce9cRqubnUCaR4G7Bz'; - const { error, value } = validateSpaceLogo(logo); - expect(error).toBeNull(); - expect(value).toEqual(logo); - }) - - test('should fail with invalid ipfs link', () => { - const logo = 'https://google.com'; - const { error, value } = validateSpaceLogo(logo); - expect(error).toEqual('Logo is not valid'); - expect(value).toEqual(logo); - }) - - - test('should fail with empty string', () => { - const logo = ''; - const { error, value } = validateSpaceLogo(logo); - expect(error).toEqual('Logo is required'); - expect(value).toEqual(logo); - }) - - }); - - describe('validate space website', () => { - test('should succeed with valid website #1', () => { - const website = 'https://google.com'; - const { error, value } = validateSpaceWebsite(website); - expect(error).toBeNull(); - expect(value).toEqual(website); - }) - - test('should succeed with valid website #2', () => { - const website = 'https://gnars.wtf'; - const { error, value } = validateSpaceWebsite(website); - expect(error).toBeNull(); - expect(value).toEqual(website); - }) - - test('should succeed with valid website #3', () => { - const website = 'nouns.wtf'; - const { error, value } = validateSpaceWebsite(website); - expect(error).toBeNull(); - expect(value).toEqual(website); - }) - - test('should fail with invalid website', () => { - const website = 'test'; - const { error, value } = validateSpaceWebsite(website); - expect(error).toEqual('Website is not valid'); - expect(value).toEqual(website); - }); - }) - - describe('validate space twitter', () => { - test('should succeed with valid twitter handle', () => { - const twitter = '@calabara'; - const { error, value } = validateSpaceTwitter(twitter); - expect(error).toBeNull(); - expect(value).toEqual(twitter); - }) - - test('should fail with invalid twitter handle', () => { - const twitter = 'test'; - const { error, value } = validateSpaceTwitter(twitter); - expect(error).toEqual('Twitter handle is not valid'); - expect(value).toEqual(twitter); - }) - }) - - - describe('validate space admins', () => { - - test('should succeed with valid addresses', async () => { - const admins = ['calabara.eth', 'vitalik.eth']; - const { error, value } = await validateSpaceAdmins(admins); - expect(error).toEqual([null, null]) - expect(value).toEqual([calabaraAddress, vitalikAddress]) - }) - - test('should succeed and strip empty addresses', async () => { - const admins = ['calabara.eth', '']; - const { error, value } = await validateSpaceAdmins(admins); - expect(error).toEqual([null]) - expect(value).toEqual([calabaraAddress]) - }) - - test('should strip empty addresses and return errors', async () => { - const admins = ['calabara.eth', 'test', 'vitalik.eth', '']; - const { error, value } = await validateSpaceAdmins(admins); - expect(error).toEqual([null, 'invalid address', null]) - expect(value).toEqual([calabaraAddress, 'test', vitalikAddress]) - }) - - test('should strip empty addresses, remove duplicates, and return error addresses', async () => { - const admins = ['calabara.eth', 'test', 'vitalik.eth', '', 'vitalik.eth']; - const { error, value } = await validateSpaceAdmins(admins); - expect(error).toEqual([null, 'invalid address', null]) - expect(value).toEqual([calabaraAddress, 'test', vitalikAddress]) - }) - }) - - - describe('Validate Space Builder Props', () => { - - test('should return errors', async () => { - const props: SpaceBuilderProps = { - name: "", - logoBlob: "", - logoUrl: "", - website: "", - twitter: "", - admins: [], - errors: { - admins: [], - }, - } - - const { isValid, errors, values } = await validateSpaceBuilderProps(props); - expect(isValid).toBe(false); - expect(errors).toEqual({ - name: "Name is required", - logoUrl: "Logo is required", - admins: [] - }) - expect(values).toEqual({ - name: "", - logoUrl: "", - admins: [], - }) - }) - - test('should return valid props', async () => { - const props: SpaceBuilderProps = { - name: "sample space", - logoBlob: "asdf", - logoUrl: "https://uplink.mypinata.cloud/ipfs/asdfasdf", - website: "twitter.com", - twitter: "@uplinkwtf", - admins: [calabaraAddress, vitalikAddress], - errors: { - admins: [], - }, - } - - const { isValid, errors, values } = await validateSpaceBuilderProps(props); - expect(isValid).toBe(true); - expect(errors).toEqual({ - admins: [null, null] - }) - expect(values).toEqual({ - name: "sample space", - logoUrl: "https://uplink.mypinata.cloud/ipfs/asdfasdf", - website: "twitter.com", - twitter: "@uplinkwtf", - admins: [calabaraAddress, vitalikAddress], - }) - }) - - }); - - - - -}) diff --git a/uplink-client/src/test/sampleTokens.ts b/uplink-client/src/test/sampleTokens.ts deleted file mode 100644 index 55d3b690..00000000 --- a/uplink-client/src/test/sampleTokens.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { IERCToken, INativeToken, IToken } from "@/types/token"; - -export const sampleERC1155Token: IERCToken = { - type: "ERC1155", - address: "0x7c2748C7Ec984b559EADc39C7a4944398E34911a", - symbol: "TNS", - decimals: 0, - tokenId: 2, - chainId: 1 -} - -export const sampleERC20Token: IERCToken = { - type: "ERC20", - address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", - symbol: "USDC", - decimals: 6, - tokenId: null, - chainId: 1, -} - -export const sampleERC721Token: IERCToken = { - type: "ERC721", - address: "0x9C8fF314C9Bc7F6e59A9d9225Fb22946427eDC03", - symbol: "NOUN", - decimals: 0, - tokenId: null, - chainId: 1, -} - -export const sampleETHToken: INativeToken = { - type: "ETH", - symbol: "ETH", - decimals: 18, - chainId: 1, -} \ No newline at end of file diff --git a/uplink-client/src/test/zora.test.ts b/uplink-client/src/test/zora.test.ts deleted file mode 100644 index 118254a4..00000000 --- a/uplink-client/src/test/zora.test.ts +++ /dev/null @@ -1,368 +0,0 @@ -import { describe, expect, test } from "@jest/globals"; -import { - ConfigurableZoraEditionSchema, - ConfigurableZoraEditionInput, - EditionNameSchema, - EditionSymbolSchema, - EditionSizeSchema, - EditionRoyaltyBPSSchema, - EditionPublicSalePriceSchema, - EditionSalesConfigSchema, -} from "@/hooks/useCreateZoraEdition"; -import { uint64MaxSafe } from "@/utils/uint64"; - -const unixRegex = /^\d{10}$/; - -describe("validate zora config", () => { - - describe("validate edition name", () => { }) - describe("validate edition symbol", () => { }) - describe("validate edition description", () => { }) - - describe("validate edition size", () => { - test("open edition", () => { - const size = "open"; - const result = EditionSizeSchema.safeParse(size); - expect(result.success).toBe(true); - if (result.success) { - expect(result.data).toBe("18446744073709551615"); - } - }) - test("1/1", () => { - const size = "one"; - const result = EditionSizeSchema.safeParse(size); - expect(result.success).toBe(true); - if (result.success) { - expect(result.data).toBe("1"); - } - }) - test("fixed", () => { - const size = "100"; - const result = EditionSizeSchema.safeParse(size); - expect(result.success).toBe(true); - if (result.success) { - expect(result.data).toBe("100"); - } - }) - test("fail with empty string", () => { - const size = ""; - const result: any = EditionSizeSchema.safeParse(size); - const errors = result.error.format(); - expect(result.success).toBe(false); - expect(errors._errors[0]).toBe("Edition size is required"); - }) - }) - - describe("validate edition royaltyBPS", () => { - test("0%", () => { - const bps = "zero"; - const result = EditionRoyaltyBPSSchema.safeParse(bps); - expect(result.success).toBe(true); - if (result.success) { - expect(result.data).toBe(0); - } - }) - test("5%", () => { - const bps = "five"; - const result = EditionRoyaltyBPSSchema.safeParse(bps); - expect(result.success).toBe(true); - if (result.success) { - expect(result.data).toBe(500); - } - }) - test("5.5%", () => { - const bps = "5.5" - const result = EditionRoyaltyBPSSchema.safeParse(bps); - expect(result.success).toBe(true); - if (result.success) { - expect(result.data).toBe(550); - } - }) - test("0.05%", () => { - const bps = "0.05" - const result = EditionRoyaltyBPSSchema.safeParse(bps); - expect(result.success).toBe(true); - if (result.success) { - expect(result.data).toBe(5); - } - }) - test("integer precision", () => { - const bps = "5.00005" - const result = EditionRoyaltyBPSSchema.safeParse(bps); - expect(result.success).toBe(true); - if (result.success) { - expect(result.data).toBe(500); - } - }) - test("fail with empty string", () => { - const bps = ""; - const result: any = EditionRoyaltyBPSSchema.safeParse(bps); - const errors = result.error.format(); - expect(result.success).toBe(false); - expect(errors._errors[0]).toBe("Royalty % is required"); - }) - }) - - describe("validate edition public sale price", () => { - test("free", () => { }) - test("0.001 eth", () => { - const price = "0.001"; - const result = EditionPublicSalePriceSchema.safeParse(price); - expect(result.success).toBe(true); - if (result.success) { - expect(result.data).toBe("1000000000000000"); - } - }) - - test("0.00420", () => { - const price = "0.00420"; - const result = EditionPublicSalePriceSchema.safeParse(price); - expect(result.success).toBe(true); - if (result.success) { - expect(result.data).toBe("4200000000000000"); - } - }) - - test("1.5 eth", () => { - const price = "1.5"; - const result = EditionPublicSalePriceSchema.safeParse(price); - expect(result.success).toBe(true); - if (result.success) { - expect(result.data).toBe("1500000000000000000"); - } - }) - test("fail with empty string", () => { - const price = ""; - const result: any = EditionPublicSalePriceSchema.safeParse(price); - const errors = result.error.format(); - expect(result.success).toBe(false); - expect(errors._errors[0]).toBe("Edition price is required"); - }) - }) - - - describe("validate public sale datetimes", () => { - test("startTime = now, endTime = forever", () => { - const nowUnix = Math.floor(new Date().getTime() / 1000); - const salesConfig = { - publicSalePrice: "1", - publicSaleStart: "now", - publicSaleEnd: "forever", - } - - const result = EditionSalesConfigSchema.safeParse(salesConfig); - expect(result.success).toBe(true); - if (result.success) { - expect(result.data.publicSaleStart).toMatch(unixRegex); - expect(result.data.publicSaleEnd).toBe(uint64MaxSafe.toString()); - expect(nowUnix - parseInt(result.data.publicSaleStart)).toBeLessThan(5); // 5 second tolerance - } - }) - - test("startTime = now, endTime = week", () => { - const salesConfig = { - publicSalePrice: "1", - publicSaleStart: "now", - publicSaleEnd: "week", - } - - const result = EditionSalesConfigSchema.safeParse(salesConfig); - expect(result.success).toBe(true); - if (result.success) { - expect(result.data.publicSaleStart).toMatch(unixRegex); - expect(result.data.publicSaleEnd).toMatch(unixRegex); - expect(parseInt(result.data.publicSaleEnd) - parseInt(result.data.publicSaleStart)).toBe(604800); - } - }) - - test("startTime = custom, endTime = custom", () => { - const plus1day = new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString(); - const plus2day = new Date(Date.now() + 1000 * 60 * 60 * 48).toISOString(); - const unixPlus1Day = Math.floor(new Date(plus1day).getTime() / 1000); - const unixPlus2Day = Math.floor(new Date(plus2day).getTime() / 1000); - const salesConfig = { - publicSalePrice: "1", - publicSaleStart: plus1day, - publicSaleEnd: plus2day, - } - const result = EditionSalesConfigSchema.safeParse(salesConfig); - expect(result.success).toBe(true); - - if (result.success) { - expect(result.data.publicSaleStart).toMatch(unixRegex); - expect(result.data.publicSaleEnd).toMatch(unixRegex); - expect(parseInt(result.data.publicSaleEnd) - parseInt(result.data.publicSaleStart)).toBe(86400); - expect(parseInt(result.data.publicSaleStart)).toBe(unixPlus1Day); - expect(parseInt(result.data.publicSaleEnd)).toBe(unixPlus2Day); - } - }) - - test("startTime < now", () => { - const minus1day = new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString(); - const salesConfig = { - publicSalePrice: "1", - publicSaleStart: minus1day, - publicSaleEnd: "forever", - } - const result = EditionSalesConfigSchema.safeParse(salesConfig); - expect(result.success).toBe(false); - const errrors = result.error.format(); - expect(errrors.publicSaleStart._errors[0]).toBe("Public sale start must be in the future"); - }) - - test("endTime < startTime", () => { - const plus1day = new Date(Date.now() + 1000 * 60 * 60 * 24).toISOString(); - const plus2day = new Date(Date.now() + 1000 * 60 * 60 * 48).toISOString(); - - const salesConfig = { - publicSalePrice: "1", - publicSaleStart: plus2day, - publicSaleEnd: plus1day, - } - const result = EditionSalesConfigSchema.safeParse(salesConfig); - expect(result.success).toBe(false); - const errrors = result.error.format(); - expect(errrors.publicSaleEnd._errors[0]).toBe("Public sale end must be after public sale start"); - }) - }) - - // test("should succeed with valid config", () => { - // const input: ConfigurableZoraEditionInput = { - // name: "test", - // symbol: "TST", - // editionSize: "1", - // royaltyBPS: "1000", - // description: "test", - // animationURI: "", - // imageURI: "https://test.com", - // salesConfig: { - // publicSalePrice: "1", - // publicSaleStart: "now", - // publicSaleEnd: new Date(Date.now() + 1000 * 60 * 60).toISOString(), - // } - // } - - // const result: any = ConfigurableZoraEditionSchema.safeParse(input); - // expect(result.success).toBe(true); - // const { salesConfig, ...output } = result.data; - // const { salesConfig: sc_in, ...rest } = input; - // expect(output).toStrictEqual(rest); - // expect(salesConfig.publicSaleStart).toMatch(unixRegex); - // expect(salesConfig.publicSaleEnd).toMatch(unixRegex); - // }); - - // test("should fail with invalid text inputs", () => { - // const input: ConfigurableZoraEditionInput = { - // name: "", - // symbol: "", - // editionSize: "1", - // royaltyBPS: "1000", - // description: "", - // animationURI: "", - // imageURI: "https://test.com", - // salesConfig: { - // publicSalePrice: "1", - // publicSaleStart: "now", - // publicSaleEnd: new Date(Date.now() + 1000 * 60 * 60).toISOString(), - // } - // } - - // const result: any = ConfigurableZoraEditionSchema.safeParse(input); - // const errors = result.error.format(); - // expect(result.success).toBe(false); - // expect(errors.name._errors[0]).toBe("Name is required"); - // expect(errors.symbol._errors[0]).toBe("Symbol is required"); - // expect(errors.description._errors[0]).toBe("Description is required"); - // }) - - - // test("should fail without imageURI", () => { - // const input: ConfigurableZoraEditionInput = { - // name: "test", - // symbol: "test", - // editionSize: "1", - // royaltyBPS: "1000", - // description: "test", - // animationURI: "", - // imageURI: "", - // salesConfig: { - // publicSalePrice: "1", - // publicSaleStart: "now", - // publicSaleEnd: new Date(Date.now() + 1000 * 60 * 60).toISOString(), - // } - // } - - // const result: any = ConfigurableZoraEditionSchema.safeParse(input); - // const errors = result.error.format(); - // expect(result.success).toBe(false); - // expect(errors.imageURI._errors[0]).toBe("Image must be set"); - // }) - - // test("should fail without video thumbnail", () => { - // const input: ConfigurableZoraEditionInput = { - // name: "test", - // symbol: "test", - // editionSize: "1", - // royaltyBPS: "1000", - // description: "test", - // animationURI: "https://test", - // imageURI: "", - // salesConfig: { - // publicSalePrice: "1", - // publicSaleStart: "now", - // publicSaleEnd: new Date(Date.now() + 1000 * 60 * 60).toISOString(), - // } - // } - - // const result: any = ConfigurableZoraEditionSchema.safeParse(input); - // const errors = result.error.format(); - // expect(result.success).toBe(false); - // expect(errors.animationURI._errors[0]).toBe("Video thumbnail must be set"); - // }) - - // test("should fail with sale start > sale end", () => { - // const input: ConfigurableZoraEditionInput = { - // name: "test", - // symbol: "test", - // editionSize: "1", - // royaltyBPS: "1000", - // description: "test", - // animationURI: "", - // imageURI: "https://test", - // salesConfig: { - // publicSalePrice: "1", - // publicSaleStart: new Date(Date.now() + 1000 * 60 * 60).toISOString(), - // publicSaleEnd: new Date(Date.now() + 1000 * 60 * 30).toISOString(), - // } - // } - - // const result: any = ConfigurableZoraEditionSchema.safeParse(input); - // const errors = result.error.format(); - // expect(result.success).toBe(false); - // expect(errors.salesConfig.publicSaleStart._errors[0]).toBe("Public sale start must be before public sale end"); - // }) - - // test("should fail with sale start in past", () => { - // const input: ConfigurableZoraEditionInput = { - // name: "test", - // symbol: "test", - // editionSize: "1", - // royaltyBPS: "1000", - // description: "test", - // animationURI: "", - // imageURI: "https://test", - // salesConfig: { - // publicSalePrice: "1", - // publicSaleStart: new Date(Date.now() - 1000 * 60 * 60).toISOString(), - // publicSaleEnd: new Date(Date.now() + 1000 * 60 * 30).toISOString(), - // } - // } - - // const result: any = ConfigurableZoraEditionSchema.safeParse(input); - // const errors = result.error.format(); - // expect(result.success).toBe(false); - // expect(errors.salesConfig.publicSaleStart._errors[0]).toBe("Public sale start must be in the future"); - // }) - - -}) \ No newline at end of file diff --git a/uplink-client/src/types/edition.ts b/uplink-client/src/types/edition.ts deleted file mode 100644 index 37ed643d..00000000 --- a/uplink-client/src/types/edition.ts +++ /dev/null @@ -1,27 +0,0 @@ -export type Edition = { - id: string; - chainId: number; - contractAddress: string; - name: string - symbol: string - editionSize: string - royaltyBPS: number - fundsRecipient: string - defaultAdmin: string - saleConfig: EditionSaleConfig; - description: string; - animationURI: string; - imageURI: string; - referrer: string; - -} - -export type EditionSaleConfig = { - publicSalePrice: string; - maxSalePurchasePerAddress: number; - publicSaleStart: string; - publicSaleEnd: string; - presaleStart: string; - presaleEnd: string; - presaleMerkleRoot: string; -} \ No newline at end of file diff --git a/uplink-client/src/types/mintBoard.ts b/uplink-client/src/types/mintBoard.ts deleted file mode 100644 index 346ebad3..00000000 --- a/uplink-client/src/types/mintBoard.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Edition } from "./edition"; -import { Space } from "./space"; -import { User } from "./user"; -import { z } from "zod"; -import { zeroAddress } from "viem"; - - -export type MintBoard = { - id: string; - space: Space - spaceId: string; - created: string; - chainId: number; - enabled: boolean; - threshold: number; - boardTitle: string; - boardDescription: string; - name: string; - symbol: string; - editionSize: string; - publicSalePrice: string; - publicSaleStart: string; - publicSaleEnd: string; - description: string; - referrer: string; -} - -export type PaginatedMintBoardPosts = { - posts: Array; - pageInfo: { - endCursor: string; - hasNextPage: boolean - } -} - -export type MintBoardPost = { - id: string; - created: string; - edition: Edition; - author: User; - totalMints: number; -} diff --git a/uplink-client/src/types/space.ts b/uplink-client/src/types/space.ts index 35759445..0e801663 100644 --- a/uplink-client/src/types/space.ts +++ b/uplink-client/src/types/space.ts @@ -1,4 +1,3 @@ -import { MintBoard } from "./mintBoard"; import { IToken } from "./token"; export type Admin = { diff --git a/uplink-client/src/ui/AddressDisplay/AddressDisplay.tsx b/uplink-client/src/ui/AddressDisplay/AddressDisplay.tsx index 47e0a76c..a139225c 100644 --- a/uplink-client/src/ui/AddressDisplay/AddressDisplay.tsx +++ b/uplink-client/src/ui/AddressDisplay/AddressDisplay.tsx @@ -2,7 +2,7 @@ import { User } from "@/types/user"; import Noggles from "../Noggles/Noggles"; import { Session } from "@/providers/SessionProvider"; -import OptimizedImage from "@/lib/OptmizedImage"; +import OptimizedImage from "@/lib/OptimizedImage"; import { ImageWrapper } from "@/app/(legacy)/contest/components/MediaWrapper" import { useWalletDisplayText } from "@/hooks/useWalletDisplay"; import { useEffect } from "react"; diff --git a/uplink-client/src/ui/ChannelSidebar/ContestDetailsV2.tsx b/uplink-client/src/ui/ChannelSidebar/ContestDetailsV2.tsx index b6a41cf9..ba1cd586 100644 --- a/uplink-client/src/ui/ChannelSidebar/ContestDetailsV2.tsx +++ b/uplink-client/src/ui/ChannelSidebar/ContestDetailsV2.tsx @@ -331,11 +331,13 @@ const RewardsSection = ({ // rewards (need types) const ContestDetailsV2 = ({ + spaceName, contractId, transportConfig, creatorLogic, minterLogic }: { + spaceName: string; contractId: ContractID; transportConfig: IFiniteTransportConfig; creatorLogic: ILogicConfig | null; @@ -343,7 +345,6 @@ const ContestDetailsV2 = ({ }) => { const { chainId, contractAddress } = splitContractID(contractId); - const { channelState, stateRemainingTime } = useFiniteTransportLayerState(contractId); const { settle, status, txHash, error } = useSettleFiniteChannel(); const isSettling = status === "pendingApproval" || status === "txInProgress"; const { mutateSwrChannel } = useChannel(contractId); @@ -388,29 +389,6 @@ const ContestDetailsV2 = ({
- {/* */} - - {/* - - - - - - - - */} {({ isLoading, channelState, stateRemainingTime }) => { @@ -425,7 +403,7 @@ const ContestDetailsV2 = ({ {channelState ? stateRemainingTime : }

- + @@ -449,7 +427,6 @@ const ContestDetailsV2 = ({ ) - return null; }} diff --git a/uplink-client/src/ui/ChannelSidebar/VoteCart.tsx b/uplink-client/src/ui/ChannelSidebar/VoteCart.tsx index 3be7c1cc..1140d64d 100644 --- a/uplink-client/src/ui/ChannelSidebar/VoteCart.tsx +++ b/uplink-client/src/ui/ChannelSidebar/VoteCart.tsx @@ -1,7 +1,7 @@ "use client"; import { useVoteCart } from "@/hooks/useVoteCart"; import { parseIpfsUrl } from "@/lib/ipfs"; -import OptimizedImage from "@/lib/OptmizedImage"; +import OptimizedImage from "@/lib/OptimizedImage"; import { ContractID, ChannelTokenWithUserBalance } from "@/types/channel"; import { HiSparkles, HiTrash } from "react-icons/hi2"; import { Input } from "../DesignKit/Input"; diff --git a/uplink-client/src/ui/ContestHeading/ContestHeading.tsx b/uplink-client/src/ui/ContestHeading/ContestHeading.tsx index bd8f4bf5..d8418d4c 100644 --- a/uplink-client/src/ui/ContestHeading/ContestHeading.tsx +++ b/uplink-client/src/ui/ContestHeading/ContestHeading.tsx @@ -1,4 +1,4 @@ -import OptimizedImage from "@/lib/OptmizedImage" +import OptimizedImage from "@/lib/OptimizedImage" import Link from "next/link"; import { ImageWrapper } from "../../app/(legacy)/contest/components/MediaWrapper"; diff --git a/uplink-client/src/ui/MediaUpload/AvatarUpload.tsx b/uplink-client/src/ui/MediaUpload/AvatarUpload.tsx index 9909ac99..16f74be3 100644 --- a/uplink-client/src/ui/MediaUpload/AvatarUpload.tsx +++ b/uplink-client/src/ui/MediaUpload/AvatarUpload.tsx @@ -2,7 +2,7 @@ import React, { useRef, useEffect } from 'react'; import { useMediaUpload } from '@/hooks/useMediaUpload'; import { HiPhoto } from 'react-icons/hi2'; -import OptimizedImage from "@/lib/OptmizedImage" +import OptimizedImage from "@/lib/OptimizedImage" import { Input } from '../DesignKit/Input'; import { Label } from '../DesignKit/Label'; import { useDropzone } from 'react-dropzone'; diff --git a/uplink-client/src/ui/MintboardSettings/MintboardSettings.tsx b/uplink-client/src/ui/MintboardSettings/MintboardSettings.tsx index e9c6e340..b060a1f6 100644 --- a/uplink-client/src/ui/MintboardSettings/MintboardSettings.tsx +++ b/uplink-client/src/ui/MintboardSettings/MintboardSettings.tsx @@ -313,8 +313,8 @@ export const MintboardSettings = ({ {/*<-- metadata -->*/}

Details

- setField("title", e.target.value)} error={state.errors?.title?._errors} /> -