diff --git a/README.md b/README.md index 0304e3ec0..eae5f7b9b 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,8 @@ This is Nouns Builder front-end mono-repo. You can find Nouns Builder deployed on: -- [Mainnet](nous.build) -- [Goerli testnet](testnet.nouns.build) +- [Mainnet](//nouns.build) +- [Goerli testnet](//testnet.nouns.build) For an introduction to Nouns Builder and its concept, you can find further [documentation here](https://docs.zora.co/docs/smart-contracts/nouns-builder/intro). You can also find [Nouns Protocol here](https://github.com/ourzora/nouns-protocol). diff --git a/apps/web/src/components/Home/Marquee.css.ts b/apps/web/src/components/Home/Marquee.css.ts new file mode 100644 index 000000000..eae488230 --- /dev/null +++ b/apps/web/src/components/Home/Marquee.css.ts @@ -0,0 +1,30 @@ +import { style } from '@vanilla-extract/css' + +const baseStyles = { + height: '110px', + width: 'auto', +} + +const getMobileStyle = (width: string) => { + return { + '@media': { + 'screen and (max-width: 768px)': { + height: 'auto', + width: width, + }, + }, + } +} + +export const Unlock = style([baseStyles, getMobileStyle('130px')]) +export const PurpleGalaxy = style([baseStyles, getMobileStyle('34px')]) +export const The = style([baseStyles, getMobileStyle('62px')]) +export const BlueWheel = style([baseStyles, getMobileStyle('36px')]) +export const Possibilities = style([baseStyles, getMobileStyle('225px')]) +export const Of = style([baseStyles, getMobileStyle('38px')]) +export const GreenClover = style([baseStyles, getMobileStyle('36px')]) +export const Collective = style([baseStyles, getMobileStyle('180px')]) +export const PurpleStar = style([baseStyles, getMobileStyle('36px')]) +export const BlueSun = style([baseStyles, getMobileStyle('36px')]) +export const NounsGlasses = style([baseStyles, getMobileStyle('62px')]) +export const Creation = style([baseStyles, getMobileStyle('157px')]) diff --git a/apps/web/src/components/Home/Marquee.tsx b/apps/web/src/components/Home/Marquee.tsx index 5207e0eb2..2ff20906b 100644 --- a/apps/web/src/components/Home/Marquee.tsx +++ b/apps/web/src/components/Home/Marquee.tsx @@ -1,33 +1,45 @@ import { Flex, atoms } from '@zoralabs/zord' import { motion } from 'framer-motion' +import Image from 'next/image' import React from 'react' import { useLayoutStore } from 'src/stores' +import { + BlueSun, + BlueWheel, + Collective, + Creation, + GreenClover, + NounsGlasses, + Of, + Possibilities, + PurpleGalaxy, + PurpleStar, + The, + Unlock, +} from './Marquee.css' + const Marquee = () => { const { isMobile } = useLayoutStore() return ( - {'unlock'} + {'unlock'} - {'purple - {'the'} + {'the'} { repeat: Infinity, }} > - {'blue + {'blue - {'possibilities'} - {'of'} + {'of'} { repeat: Infinity, }} > - {'green - {'collective'} + {'collective'} { }} transition={{ duration: 2, repeat: Infinity }} > - {'purple { }} transition={{ duration: 2, repeat: Infinity, delay: 1 }} > - {'blue + {'blue - {'small - {'creation'} + {'creation'} ) diff --git a/apps/web/src/modules/create-dao/components/AllocationForm/AllocationForm.schema.ts b/apps/web/src/modules/create-dao/components/AllocationForm/AllocationForm.schema.ts index 886818cae..e6e4cd679 100644 --- a/apps/web/src/modules/create-dao/components/AllocationForm/AllocationForm.schema.ts +++ b/apps/web/src/modules/create-dao/components/AllocationForm/AllocationForm.schema.ts @@ -14,7 +14,10 @@ const allocationSchema = Yup.object().shape({ allocationPercentage: Yup.number() .transform((value) => (isNaN(value) ? undefined : value)) .required('*') - .min(1, '> 0') // (condition, errorMessage) - allocation represented as % must be greater than or equal to 0 + .when('admin', (admin, schema) => { + if (!admin) return schema.min(1, '> 0') // (condition, errorMessage) - allocation represented as % must be greater than or equal to 0 + return schema + }) .max(100, '< 100') .integer('Must be whole number'), endDate: Yup.string() @@ -25,6 +28,7 @@ const allocationSchema = Yup.object().shape({ const now = new Date() return date > now }), + admin: Yup.boolean(), }) export const validationSchemaContributions = Yup.object().shape({ diff --git a/apps/web/src/modules/create-dao/components/AllocationForm/AllocationForm.tsx b/apps/web/src/modules/create-dao/components/AllocationForm/AllocationForm.tsx index 98cd15c57..330203b01 100644 --- a/apps/web/src/modules/create-dao/components/AllocationForm/AllocationForm.tsx +++ b/apps/web/src/modules/create-dao/components/AllocationForm/AllocationForm.tsx @@ -25,6 +25,7 @@ export interface TokenAllocation { allocationPercentage: number | string founderAddress: string endDate: string + admin?: boolean } export interface FounderAllocationFormValues { @@ -42,6 +43,7 @@ export const AllocationForm: React.FC = ({ title }) => { activeSection, setFulfilledSections, vetoPower, + vetoerAddress, auctionSettings: { auctionDuration }, } = useFormStore( (state) => ({ @@ -52,6 +54,7 @@ export const AllocationForm: React.FC = ({ title }) => { activeSection: state.activeSection, setFulfilledSections: state.setFulfilledSections, vetoPower: state.vetoPower, + vetoerAddress: state.vetoerAddress, auctionSettings: state.auctionSettings, }), shallow @@ -73,6 +76,7 @@ export const AllocationForm: React.FC = ({ title }) => { founderAddress: signerAddress || '', allocationPercentage: '', endDate: '', + admin: true, }, ] : [ @@ -80,6 +84,7 @@ export const AllocationForm: React.FC = ({ title }) => { founderAddress: signerAddress || '', allocationPercentage: founderAllocation[0].allocationPercentage, endDate: founderAllocation[0].endDate, + admin: true, }, ...founderAllocation.slice(1), ] @@ -108,6 +113,7 @@ export const AllocationForm: React.FC = ({ title }) => { setFounderAllocation( founderAllocation.map((allocation, idx) => ({ ...allocation, + admin: undefined, founderAddress: founderAllocationAddresses[idx], })) ) @@ -138,6 +144,7 @@ export const AllocationForm: React.FC = ({ title }) => { formik={formik} auctionDuration={auctionDuration!} vetoPower={vetoPower} + vetoerAddress={vetoerAddress} touched={formik.touched} values={formik.values} errors={formik.errors} diff --git a/apps/web/src/modules/create-dao/components/AllocationForm/FounderAllocationFields.tsx b/apps/web/src/modules/create-dao/components/AllocationForm/FounderAllocationFields.tsx index e715677c2..89b720757 100644 --- a/apps/web/src/modules/create-dao/components/AllocationForm/FounderAllocationFields.tsx +++ b/apps/web/src/modules/create-dao/components/AllocationForm/FounderAllocationFields.tsx @@ -17,6 +17,7 @@ interface FounderAllocationFieldsProps { values: FounderAllocationFormValues auctionDuration: Duration vetoPower?: boolean + vetoerAddress: string errors?: FormikErrors touched: FormikTouched formik: FormikProps @@ -27,6 +28,7 @@ interface FounderAllocationFieldsProps { export const FounderAllocationFields = ({ values, vetoPower, + vetoerAddress, auctionDuration, errors, touched, @@ -45,6 +47,7 @@ export const FounderAllocationFields = ({ {values.founderAllocation.map((founder, index) => { const isFounder = index === 0 + const isVetoer = vetoPower && vetoerAddress === founder.founderAddress const error = typeof errors?.founderAllocation === 'object' @@ -129,8 +132,12 @@ export const FounderAllocationFields = ({ )} - {isFounder && vetoPower === true && ( - + {isVetoer && ( + This address has proposal veto power diff --git a/apps/web/src/modules/create-dao/components/Artwork/ArtworkUpload.tsx b/apps/web/src/modules/create-dao/components/Artwork/ArtworkUpload.tsx index 2f3bac6ee..6125cbc40 100644 --- a/apps/web/src/modules/create-dao/components/Artwork/ArtworkUpload.tsx +++ b/apps/web/src/modules/create-dao/components/Artwork/ArtworkUpload.tsx @@ -124,7 +124,9 @@ export const ArtworkUpload: React.FC = ({ uri: upload?.ipfs?.uri || '', url: encodeURI( getFetchableUrl(upload?.ipfs?.uri) + - `/${upload.webkitRelativePath.split('/').slice(1).join('/')}` || '' + `/${sanitizeFileName( + upload.webkitRelativePath.split('/').slice(1).join('/') + )}` || '' ), path: upload.webkitRelativePath, content: upload?.content, diff --git a/apps/web/src/modules/create-dao/components/ReviewAndDeploy/ReviewAndDeploy.tsx b/apps/web/src/modules/create-dao/components/ReviewAndDeploy/ReviewAndDeploy.tsx index 0f957d46f..ed17b66a3 100644 --- a/apps/web/src/modules/create-dao/components/ReviewAndDeploy/ReviewAndDeploy.tsx +++ b/apps/web/src/modules/create-dao/components/ReviewAndDeploy/ReviewAndDeploy.tsx @@ -70,6 +70,7 @@ export const ReviewAndDeploy: React.FC = ({ title }) => { ipfsUpload, setFulfilledSections, vetoPower, + vetoerAddress, } = useFormStore() const handlePrev = () => { @@ -125,7 +126,7 @@ export const ReviewAndDeploy: React.FC = ({ title }) => { : BigNumber.from('0'), vetoer: vetoPower === true - ? ethers.utils.getAddress(founderParams?.[0].wallet as AddressType) + ? ethers.utils.getAddress(vetoerAddress as AddressType) : ethers.utils.getAddress(NULL_ADDRESS), } diff --git a/apps/web/src/modules/create-dao/components/VetoForm.tsx b/apps/web/src/modules/create-dao/components/VetoForm.tsx index 17b9f7646..e70bafa68 100644 --- a/apps/web/src/modules/create-dao/components/VetoForm.tsx +++ b/apps/web/src/modules/create-dao/components/VetoForm.tsx @@ -1,15 +1,19 @@ -import { Button, Flex } from '@zoralabs/zord' +import { Button, Flex, atoms } from '@zoralabs/zord' import { Form, Formik } from 'formik' -import React from 'react' +import { motion } from 'framer-motion' +import React, { BaseSyntheticEvent } from 'react' import * as Yup from 'yup' import Radio from 'src/components/Fields/Radio' +import SmartInput from 'src/components/Fields/SmartInput' import { defaultBackButton, defaultFormButtonWithPrev, } from 'src/components/Fields/styles.css' import { Icon } from 'src/components/Icon' +import { getEnsAddress, isValidAddress } from 'src/utils/ens' import { isEmpty } from 'src/utils/helpers' +import { getProvider } from 'src/utils/provider' import { useFormStore } from '../stores' @@ -19,28 +23,53 @@ interface VetoFormProps { interface VetoFromValues { vetoPower?: boolean + vetoerAddress: string +} + +const animation = { + init: { + height: 0, + }, + open: { + height: 'auto', + }, } export const vetoValidationSchema = Yup.object().shape({ vetoPower: Yup.boolean().required(), + vetoerAddress: Yup.string().when('vetoPower', { + is: true, + then: Yup.string() + .required('*') + .test( + 'isValidAddress', + 'invalid address', + (value: string | undefined) => !!value && isValidAddress(value, getProvider()) + ), + }), }) export const VetoForm: React.FC = ({ title }) => { const { vetoPower, setVetoPower, + vetoerAddress, + setVetoerAddress, setFulfilledSections, activeSection, setActiveSection, } = useFormStore() const initialValues: VetoFromValues = { vetoPower: vetoPower, + vetoerAddress: vetoerAddress, } - const handleSubmit = (values: VetoFromValues) => { + const handleSubmit = async (values: VetoFromValues) => { const vetoPower = values.vetoPower + const vetoerAddress = await getEnsAddress(values.vetoerAddress) if (vetoPower !== undefined) { setVetoPower(vetoPower) + setVetoerAddress(vetoerAddress) setFulfilledSections(title) setActiveSection(activeSection + 1) } @@ -71,6 +100,34 @@ export const VetoForm: React.FC = ({ title }) => { { value: false, label: 'No' }, ]} /> + + { + formik.setFieldValue('vetoerAddress', target.value) + }} + id="vetoerAddress" + type={'text'} + placeholder={'0x... or .eth'} + onBlur={formik.handleBlur} + autoSubmit={false} + isAddress={true} + errorMessage={ + formik.touched['vetoerAddress'] && formik.errors['vetoerAddress'] + ? formik.errors['vetoerAddress'] + : undefined + } + /> + +