Skip to content

Commit

Permalink
feat: add jumper requirements and remove deprecated ones
Browse files Browse the repository at this point in the history
  • Loading branch information
BrickheadJohnny committed Jan 24, 2025
1 parent 9c741b0 commit 4eb8dd7
Show file tree
Hide file tree
Showing 13 changed files with 317 additions and 35 deletions.
12 changes: 12 additions & 0 deletions public/requirementLogos/jumper.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
96 changes: 96 additions & 0 deletions src/requirements/Jumper/JumperForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { FormControl, FormField, FormItem, FormLabel } from "@/components/ui/Form"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/Select"
import { Separator } from "@/components/ui/Separator"
import { ComponentType } from "react"
import { useFormContext, useWatch } from "react-hook-form"
import { RequirementFormProps } from "requirements/types"
import { JumperLevelForm } from "./components/JumperLevelForm"
import { JumperTraitForm } from "./components/JumperTraitForm"
import { JumperTypeForm } from "./components/JumperTypeForm"
import { JumperRequirementType } from "./types"

const jumperRequirementTypes = [
{
label: "Have at least level x",
value: "JUMPER_LEVEL",
JumperRequirement: JumperLevelForm,
},
{
label: "Have a reward of a certain type",
value: "JUMPER_TYPE",
JumperRequirement: JumperTypeForm,
},
{
label: "Have a trait",
value: "JUMPER_TRAITS",
JumperRequirement: JumperTraitForm,
},
] satisfies {
label: string
value: JumperRequirementType
JumperRequirement: ComponentType<RequirementFormProps>
}[]

const JumperForm = ({ baseFieldPath, field }: RequirementFormProps) => {
const { control, resetField } = useFormContext()

const type = useWatch({ name: `${baseFieldPath}.type` })
const selected = jumperRequirementTypes.find((reqType) => reqType.value === type)

const resetForm = () => {
resetField(`${baseFieldPath}.data.minAmount`, { defaultValue: undefined })
resetField(`${baseFieldPath}.data.rewardType`, { defaultValue: "" })
resetField(`${baseFieldPath}.data.category`, { defaultValue: "" })
resetField(`${baseFieldPath}.data.name`, { defaultValue: "" })
}

return (
<div className="flex flex-col items-start gap-4">
<FormField
control={control}
name={`${baseFieldPath}.type`}
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>Type</FormLabel>
<Select
onValueChange={(e) => {
resetForm()
field.onChange(e)
}}
defaultValue={field.value}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select one..." />
</SelectTrigger>
</FormControl>
<SelectContent>
{jumperRequirementTypes.map(({ value, label }) => (
<SelectItem key={value} value={value}>
{label}
</SelectItem>
))}
</SelectContent>
</Select>
</FormItem>
)}
/>

{selected?.JumperRequirement && (
<>
<Separator />
<selected.JumperRequirement baseFieldPath={baseFieldPath} field={field} />
</>
)}
</div>
)
}

export default JumperForm
50 changes: 50 additions & 0 deletions src/requirements/Jumper/JumperRequirement.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import {
Requirement,
RequirementProps,
} from "components/[guild]/Requirements/components/Requirement"
import { useRequirementContext } from "components/[guild]/Requirements/components/RequirementContext"
import { ComponentType } from "react"
import REQUIREMENTS from "requirements"
import { JumperRequirementType } from "./types"

const JumperLevelDisplay = () => {
const { data } = useRequirementContext<"JUMPER_LEVEL">()

return `Wallet level ${data.minAmount} or above`
}

const JumperTypeDisplay = () => {
const { data } = useRequirementContext<"JUMPER_TYPE">()

return `Get a reward with type ${data.rewardType}`
}

const JumperTraitsDisplay = () => {
const { data } = useRequirementContext<"JUMPER_TRAITS">()

if ("category" in data && "name" in data)
return `Get a trait in the ${data.category} category with title ${data.name}`

if ("category" in data) return `Get a trait in the ${data.category} category`

return `Get a trait with title ${data.name}`
}

const DisplayComponents = {
JUMPER_LEVEL: JumperLevelDisplay,
JUMPER_TYPE: JumperTypeDisplay,
JUMPER_TRAITS: JumperTraitsDisplay,
} satisfies Record<JumperRequirementType, ComponentType>

const JumperRequirement = (props: RequirementProps) => {
const { type } = useRequirementContext<JumperRequirementType>()
const DisplayComponent = DisplayComponents[type]

return (
<Requirement image={REQUIREMENTS.JUMPER_LEVEL.icon as string}>
<DisplayComponent />
</Requirement>
)
}

export default JumperRequirement
48 changes: 48 additions & 0 deletions src/requirements/Jumper/components/JumperLevelForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import {
FormErrorMessage,
FormField,
FormItem,
FormLabel,
} from "@/components/ui/Form"
import { Input } from "@/components/ui/Input"
import { useFormContext } from "react-hook-form"
import { RequirementFormProps } from "requirements/types"

export const JumperLevelForm = ({ baseFieldPath }: RequirementFormProps) => {
const { control } = useFormContext()

return (
<FormField
control={control}
name={`${baseFieldPath}.data.minAmount`}
rules={{
required: true,
min: 1,
}}
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>Level</FormLabel>
<Input
type="number"
{...field}
onChange={(e) => {
const newValue = e.target.value

// We need this to allow typing in a decimal point
if (/^[0-9]*\.[0-9]*0*$/i.test(newValue)) {
field.onChange?.(newValue, Number(newValue))
return field.onChange(newValue)
}

const parsedValue = parseInt(newValue)
const returnedValue = isNaN(parsedValue) ? "" : parsedValue

return field.onChange(returnedValue)
}}
/>
<FormErrorMessage />
</FormItem>
)}
/>
)
}
40 changes: 40 additions & 0 deletions src/requirements/Jumper/components/JumperTraitForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import {
FormErrorMessage,
FormField,
FormItem,
FormLabel,
} from "@/components/ui/Form"
import { Input } from "@/components/ui/Input"
import { useFormContext } from "react-hook-form"
import { RequirementFormProps } from "requirements/types"

export const JumperTraitForm = ({ baseFieldPath }: RequirementFormProps) => {
const { control } = useFormContext()

return (
<>
<FormField
control={control}
name={`${baseFieldPath}.data.category`}
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>Category</FormLabel>
<Input {...field} />
<FormErrorMessage />
</FormItem>
)}
/>
<FormField
control={control}
name={`${baseFieldPath}.data.name`}
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>Name</FormLabel>
<Input {...field} />
<FormErrorMessage />
</FormItem>
)}
/>
</>
)
}
31 changes: 31 additions & 0 deletions src/requirements/Jumper/components/JumperTypeForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
FormErrorMessage,
FormField,
FormItem,
FormLabel,
} from "@/components/ui/Form"
import { Input } from "@/components/ui/Input"
import { useFormContext } from "react-hook-form"
import { RequirementFormProps } from "requirements/types"

export const JumperTypeForm = ({ baseFieldPath }: RequirementFormProps) => {
const { control } = useFormContext()

return (
<FormField
control={control}
name={`${baseFieldPath}.data.rewardType`}
rules={{
required: true,
minLength: 1,
}}
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>Reward type</FormLabel>
<Input {...field} />
<FormErrorMessage />
</FormItem>
)}
/>
)
}
3 changes: 3 additions & 0 deletions src/requirements/Jumper/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { RequirementType } from "requirements/types"

export type JumperRequirementType = Extract<RequirementType, `JUMPER_${string}`>
8 changes: 5 additions & 3 deletions src/requirements/Twitter/TwitterRequirement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ const requirementIntentAction: Partial<
export const TWITTER_HANDLE_REGEX = /^[a-z0-9_]+$/i

const TwitterRequirement = (props: RequirementProps) => {
const requirement = useRequirementContext<TwitterRequirementType>()
const requirement = useRequirementContext()
const { id: userId, platformUsers } = useUser()
const isTwitterConnected = platformUsers?.find(
(pu) => pu.platformId === PlatformType.TWITTER_V1
Expand All @@ -53,7 +53,9 @@ const TwitterRequirement = (props: RequirementProps) => {
) : (
<TwitterIntent
action={
requirementIntentAction[requirement.type] as TwitterIntentAction
requirementIntentAction[
requirement.type as TwitterRequirementType
] as TwitterIntentAction
}
/>
)
Expand All @@ -68,7 +70,7 @@ const TwitterRequirement = (props: RequirementProps) => {
"minAmount" in requirement.data ? requirement.data.minAmount : 0
)

switch (requirement.type) {
switch (requirement.type as TwitterRequirementType) {
case "TWITTER_NAME":
return (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ type CovalentRequirementType =
| "COVALENT_TX_VALUE_RELATIVE"

const WalletActivityRequirement = (props: RequirementProps): JSX.Element => {
const requirement = useRequirementContext<CovalentRequirementType>()
const requirement = useRequirementContext()
const reqData = requirement.data as any // Important note: we needed a hotfix for the requirement icon, but we should find a proper solution for this.

const maxAmount = reqData?.timestamps?.maxAmount
Expand Down
24 changes: 9 additions & 15 deletions src/requirements/requirementDisplayComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,6 @@ export const REQUIREMENT_DISPLAY_COMPONENTS = {
CONTRACT: dynamic<RequirementProps>(
() => import("requirements/ContractState/ContractStateRequirement")
),
// TODO: TX_VALUE requirements are deprecated
COVALENT_TX_VALUE: dynamic<RequirementProps>(
() => import("requirements/WalletActivity/WalletActivityRequirement")
),
COVALENT_TX_VALUE_RELATIVE: dynamic<RequirementProps>(
() => import("requirements/WalletActivity/WalletActivityRequirement")
),
COVALENT_FIRST_TX: dynamic<RequirementProps>(
() => import("requirements/WalletActivity/WalletActivityRequirement")
),
Expand Down Expand Up @@ -119,10 +112,6 @@ export const REQUIREMENT_DISPLAY_COMPONENTS = {
FORM_SUBMISSION: dynamic<RequirementProps>(
() => import("requirements/Form/FormRequirement")
),
// TODO: this is a deprecated requirement
FORM_APPROVAL: dynamic<RequirementProps>(
() => import("requirements/Form/FormRequirement")
),
WORLD_ID_VERIFICATION: dynamic<RequirementProps>(
() => import("requirements/WorldID/WorldIDRequirement")
),
Expand Down Expand Up @@ -180,10 +169,6 @@ export const REQUIREMENT_DISPLAY_COMPONENTS = {
TWITTER_TWEET_COUNT: dynamic<RequirementProps>(
() => import("requirements/Twitter/TwitterRequirement")
),
// TODO: this is a deprecated requirement
TWITTER_LIST_FOLLOW: dynamic<RequirementProps>(
() => import("requirements/Twitter/TwitterRequirement")
),
GITHUB_STARRING: dynamic<RequirementProps>(
() => import("requirements/Github/GithubRequirement")
),
Expand Down Expand Up @@ -352,4 +337,13 @@ export const REQUIREMENT_DISPLAY_COMPONENTS = {
LINEA_POH: dynamic<RequirementProps>(
() => import("requirements/LineaPOH/LineaPOHRequirement")
),
JUMPER_LEVEL: dynamic<RequirementProps>(
() => import("requirements/Jumper/JumperRequirement")
),
JUMPER_TYPE: dynamic<RequirementProps>(
() => import("requirements/Jumper/JumperRequirement")
),
JUMPER_TRAITS: dynamic<RequirementProps>(
() => import("requirements/Jumper/JumperRequirement")
),
} as const satisfies Record<RequirementType, ComponentType<RequirementProps>>
Loading

0 comments on commit 4eb8dd7

Please sign in to comment.