Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ui/.env.production
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ NEXT_PUBLIC_FLOWSCAN_URL=https://flowscan.org

NEXT_PUBLIC_NFTCATALOG_ADDRESS=0x49a7cda3a1eecc29
NEXT_PUBLIC_NON_FUNGIBLE_TOKEN_ADDRESS=0x1d7e57aa55817448
NEXT_PUBLIC_METADATAVIEWS_ADDRESS=0x1d7e57aa55817448
NEXT_PUBLIC_EMERALD_BOT_VERIFIERS_ADDRESS=0x129fee333390875e

DISCORD_CLIENT_ID=907407354427998279
Expand Down
27 changes: 25 additions & 2 deletions ui/components/BasicVerifierCreator.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ import { useRecoilState } from "recoil"
import {
transactionInProgressState,
} from "../lib/atoms"
import { PlusIcon } from "@heroicons/react/outline";
import TraitFilterModal from "./TraitFilterModal";

export default function BasicVerifierCreator(props) {
const [transactionInProgress,] = useRecoilState(transactionInProgressState)
const { index, verifierInfo, updateVerifierParam, updateNFTCatalogVerifier } = props
const { index, verifierInfo, updateVerifierParam, updateNFTCatalogVerifier, updateVerifierTraits} = props
const [selectedNFT, setSelectedNFT] = useState(null)

const [traitFilterOpen, setTraitFilterOpen] = useState(false)

const [logo, setLogo] = useState(verifierInfo.logo)
const [name, setName] = useState(verifierInfo.name)
const [description, setDescription] = useState(verifierInfo.description)
Expand Down Expand Up @@ -62,7 +66,7 @@ export default function BasicVerifierCreator(props) {
<label className="cursor-pointer text-left text-sm">{description}</label>
</div>

<div className="flex flex-col gap-y-1 justify-end pb-4">
<div className="flex flex-col gap-y-1 justify-end pb-2">
<NFTSelector selectedNFT={selectedNFT} setSelectedNFT={setSelectedNFT} />
<div>
<label className="block text-base font-bold font-flow">
Expand Down Expand Up @@ -100,7 +104,26 @@ export default function BasicVerifierCreator(props) {
/>
</div>
</div>
<button className="mt-2 flex flex-col h-8 w-full items-center justify-center bg-emerald disabled:bg-emerald-light rounded-xl text-black disabled:text-gray-500"
disabled={selectedNFT ? false : true}
onClick={() => {
setTraitFilterOpen(!traitFilterOpen)
}}>
<div className="flex gap-x-1 w-full justify-center items-center">
<PlusIcon className="h-5 w-5" />
<div>{`Traits Filter (${verifierInfo.traits.length})`}</div>
</div>
</button>
</div>
<TraitFilterModal
index={index}
open={traitFilterOpen}
setOpen={setTraitFilterOpen}
name={selectedNFT && selectedNFT.name}
initTraits={verifierInfo.traits}
initTraitsLogic={verifierInfo.traitsLogic}
updateVerifierTraits={updateVerifierTraits}
/>
</div>
)
}
6 changes: 3 additions & 3 deletions ui/components/BasicVerifierView.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import BasicVerifierCreator from "./BasicVerifierCreator";
import PresetBasicVerifier from "./PresetBasicVerifier";

export default function BasicVerifierView(props) {
const { isPreset, verifierInfo, index, updateNFTCatalogVerifier, updateVerifierParam, deleteVerifier } = props
const { isPreset, verifierInfo, index, updateNFTCatalogVerifier, updateVerifierParam, deleteVerifier, updateVerifierTraits } = props

return (
<div className="
h-[424px] p-4 shrink-0
h-[434px] p-4 shrink-0
shadow-lg bg-white
ring-1 ring-black ring-opacity-5 rounded-2xl
flex flex-col gap-y-2
Expand All @@ -28,7 +28,7 @@ export default function BasicVerifierView(props) {
{
isPreset ?
<PresetBasicVerifier index={index} updateVerifierParam={updateVerifierParam} verifierInfo={verifierInfo} />
: <BasicVerifierCreator index={index} updateVerifierParam={updateVerifierParam} verifierInfo={verifierInfo} updateNFTCatalogVerifier={updateNFTCatalogVerifier} />
: <BasicVerifierCreator index={index} updateVerifierParam={updateVerifierParam} verifierInfo={verifierInfo} updateNFTCatalogVerifier={updateNFTCatalogVerifier} updateVerifierTraits={updateVerifierTraits} />
}
</div>
)
Expand Down
9 changes: 7 additions & 2 deletions ui/components/LogicSelector.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,19 @@ export const BasicVerifiersLogic = {
OR: "OR"
}

export const TraitsLogic = {
AND: "AND",
OR: "OR"
}

export default function LogicSelector(props) {
const [transactionInProgress,] = useRecoilState(transactionInProgressState)
const { basicVerifiersLogic, setBasicVerifiersLogic } = props
const { basicVerifiersLogic, setBasicVerifiersLogic, title } = props

return (
<div className="flex flex-col gap-y-2">
<label className="block text-2xl font-bold font-flow">
Verifier Logic
{title}
</label>
<div className="mt-1 flex gap-x-4 h-12">
<button
Expand Down
3 changes: 2 additions & 1 deletion ui/components/PresetBasicVerifier.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { PlusCircleIcon, PlusIcon } from "@heroicons/react/outline";
import Image from "next/image";
import { useRecoilState } from "recoil"
import {
Expand Down Expand Up @@ -62,7 +63,7 @@ export default function PresetBasicVerifier(props) {
<label className="cursor-pointer truncate text-left text-xl font-bold">{verifierInfo.name}</label>
<label className="cursor-pointer text-left text-sm">{verifierInfo.description}</label>
</div>
<div className="flex flex-col gap-y-2 p-1 pb-4">
<div className="flex flex-col gap-y-2 p-1 pb-2">
{
verifierInfo.parameters.length > 0 ?
verifierInfo.parameters.map((parameter) => {
Expand Down
29 changes: 27 additions & 2 deletions ui/components/RoleVerifierCreator.js
Original file line number Diff line number Diff line change
@@ -1,18 +1,30 @@
import BasicVerifierSelector from "./BasicVerifierSelector"
import BasicVerifierView from "./BasicVerifierView"
import { catalogTemplate } from "../flow/preset_verifiers"
import { TraitsLogic } from "./LogicSelector"
import { useState } from "react"

export default function RoleVerifierCreator(props) {
const { basicVerifiers: verifiers, setBasicVerifiers: setVerifiers } = props

const [verifierID, setVerifierID] = useState(verifiers.length)

const createNewVerifier = () => {
const verifier = Object.assign({}, catalogTemplate)
verifier.id = verifierID
setVerifierID(verifierID + 1)

verifier.isPreset = false
verifier.traits = []
verifier.traitsLogic = TraitsLogic.AND
setVerifiers(oldVerifiers => [...oldVerifiers, verifier])
}

const createPresetVerifier = (verifierInfo) => {
const verifier = Object.assign({}, verifierInfo)
verifier.id = verifierID
setVerifierID(verifierID + 1)

verifier.isPreset = true
setVerifiers(oldVerifiers => [...oldVerifiers, verifier])
}
Expand Down Expand Up @@ -56,6 +68,18 @@ export default function RoleVerifierCreator(props) {
})
}

const updateVerifierTraits = (index, traits, traitsLogic) => {
setVerifiers(oldVerifiers => {
const newVerifiers = oldVerifiers.map((verifier, idx) => {
if (idx == index) {
return { ...verifier, traits: traits, traitsLogic: traitsLogic}
}
return verifier
})
return newVerifiers
})
}

return (
<div className="flex flex-col gap-y-2">
<div className="flex gap-x-2 justify-between items-center">
Expand All @@ -70,7 +94,7 @@ export default function RoleVerifierCreator(props) {
if (verifier.isPreset) {
return (
<BasicVerifierView
key={index}
key={verifier.id}
index={index}
isPreset={true}
verifierInfo={verifier}
Expand All @@ -81,11 +105,12 @@ export default function RoleVerifierCreator(props) {
}
return (
<BasicVerifierView
key={index}
key={verifier.id}
index={index}
isPreset={false}
verifierInfo={verifier}
updateNFTCatalogVerifier={updateNFTCatalogVerifier}
updateVerifierTraits={updateVerifierTraits}
updateVerifierParam={updateVerifierParam}
deleteVerifier={deleteVerifier}
/>
Expand Down
1 change: 1 addition & 0 deletions ui/components/RoleVerifierCreatorSlideOver.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export default function RoleVerifierCreatorSlideOver(props) {
<LogicSelector
basicVerifiersLogic={basicVerifiersLogic}
setBasicVerifiersLogic={setBasicVerifiersLogic}
title={"Verifier Logic"}
/>
<RoleVerifierCreator
basicVerifiers={basicVerifiers}
Expand Down
139 changes: 139 additions & 0 deletions ui/components/TraitFilterModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { Fragment, useEffect, useState } from 'react'
import { Dialog, Transition } from '@headlessui/react'
import { XIcon } from '@heroicons/react/outline'
import RoleVerifierCreator from './RoleVerifierCreator'
import DiscordRoleSelector from './DiscordRoleSelector'
import LogicSelector, { BasicVerifiersLogic, TraitsLogic } from './LogicSelector'
import { useRecoilState } from "recoil"
import {
showBasicNotificationState,
basicNotificationContentState
} from "../lib/atoms.js"
import TraitsEditor from './TraitsEditor'

const getTraitsDefaultValue = (initTraits, traitID) => {
if (initTraits.length == 0) {
return [{ id: traitID, trait: "", value: "" }]
}
return initTraits
}

const getTraitsIDDefaultValue = (initTraits) => {
let id = 0
for (let i = 0; i < initTraits.length; i++) {
const t = initTraits[i]
if (t.id > id) {
id = t.id
}
}
return id + 1
}

export default function TraitFilterModal(props) {
const [, setShowBasicNotification] = useRecoilState(showBasicNotificationState)
const [, setBasicNotificationContent] = useRecoilState(basicNotificationContentState)

const [traits, setTraits] = useState([])

const { open, setOpen, name, index, updateVerifierTraits, initTraits, initTraitsLogic } = props

const [traitID, setTraitID] = useState(getTraitsIDDefaultValue(initTraits))
const [traitsLogic, setTraitsLogic] = useState(initTraitsLogic)

useEffect(() => {
if (open) {
const defaultTraits = getTraitsDefaultValue(initTraits, traitID)
setTraits(defaultTraits)
}
}, [open])

return (
<Transition.Root show={open} as={Fragment}>
<Dialog as="div" className="relative z-10" onClose={setOpen}>
<Transition.Child
as={Fragment}
enter="ease-in-out duration-400"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="ease-in-out duration-400"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity" />
</Transition.Child>

<div className="fixed inset-0 overflow-hidden">
<div className="absolute inset-0 overflow-hidden">
<div className="pointer-events-none fixed inset-y-0 right-0 flex max-w-full pl-10 sm:pl-16">
<Transition.Child
as={Fragment}
enter="transform transition ease-in-out duration-500 sm:duration-500"
enterFrom="translate-x-full"
enterTo="translate-x-0"
leave="transform transition ease-in-out duration-500 sm:duration-500"
leaveFrom="translate-x-0"
leaveTo="translate-x-full"
>
<Dialog.Panel className="pointer-events-auto w-screen max-w-4xl">
<div className="flex h-full flex-col divide-y divide-gray-200 bg-white shadow-xl">
<div className="flex min-h-0 flex-1 flex-col overflow-y-scroll py-6">
<div className="px-4 sm:px-6">
<div className="flex items-start justify-between">
<Dialog.Title className="text-lg font-medium text-gray-900">{name}</Dialog.Title>
<div className="ml-3 flex h-7 items-center">
<button
type="button"
className="rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-emerald"
onClick={() => setOpen(false)}
>
<span className="sr-only">Close panel</span>
<XIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
</div>
</div>
<div className="relative mt-6 flex-1 px-4 sm:px-6">
<div className="flex flex-col gap-y-8">
<LogicSelector
basicVerifiersLogic={traitsLogic}
setBasicVerifiersLogic={setTraitsLogic}
title={"Traits Logic"}
/>
<TraitsEditor traits={traits} setTraits={setTraits} traitID={traitID} setTraitID={setTraitID} />
</div>
</div>
</div>
<div className="flex flex-shrink-0 justify-end px-4 py-4">
<button
type="button"
className="h-12 w-24 rounded-xl border border-gray-300 bg-white py-2 px-4 text-sm font-bold text-black shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-emerald focus:ring-offset-2"
onClick={() => {
setOpen(false)
}}
>
Cancel
</button>
<button
type="submit"
className="h-12 w-24 items-center ml-4 inline-flex justify-center rounded-xl border border-transparent bg-emerald py-2 px-4 text-sm font-bold text-black shadow-sm hover:bg-emerald-dark focus:outline-none focus:ring-2 focus:ring-emerald focus:ring-offset-2"
onClick={() => {
const filteredTraits = traits.filter((t) => {
return t.trait.trim().length > 0 && t.value.trim().length > 0
})
updateVerifierTraits(index, filteredTraits, traitsLogic)
setOpen(false)
}}
>
{"Save"}
</button>
</div>
</div>
</Dialog.Panel>
</Transition.Child>
</div>
</div>
</div>
</Dialog>
</Transition.Root>
)
}
50 changes: 50 additions & 0 deletions ui/components/TraitInput.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { XIcon } from '@heroicons/react/outline'
import { useEffect, useState } from 'react'

export default function TraitInput(props) {
const {index, trait, value, deleteTrait, updateTrait, deleteEnabled} = props

const [traitName, setTraitName] = useState(trait)
const [traitValue, setTraitValue] = useState(value)

useEffect(() => {
setTraitName(trait)
setTraitValue(value)
}, [index])

return (
<div className="flex flex-row gap-x-3 w-full items-center">
<div className="flex flex-row gap-x-3 flex-grow">
<input className="basis-1/2 border-emerald bg-white block w-full font-flow text-lg rounded-2xl px-3 py-2 border
focus:border-emerald-dark
outline-0 focus:outline-2 focus:outline-emerald-dark
placeholder:text-gray-300"
defaultValue={traitName}
onChange={(event) => {
updateTrait(index, event.target.value, value)
}}
/>
<input className="basis-1/2 border-emerald bg-white block w-full font-flow text-lg rounded-2xl px-3 py-2 border
focus:border-emerald-dark
outline-0 focus:outline-2 focus:outline-emerald-dark
placeholder:text-gray-300"

defaultValue={traitValue}
onChange={(event) => {
updateTrait(index, trait,event.target.value)
}}
/>
</div>
<button
type="button"
className="bg-white rounded-md inline-flex text-gray-400 disabled:text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-emerald-dark"
disabled={!deleteEnabled}
onClick={() => {
deleteTrait(index)
}}
>
<XIcon className="h-6 w-6" aria-hidden="true" />
</button>
</div>
)
}
Loading