Skip to content

Commit

Permalink
Add reward refactor (#1219)
Browse files Browse the repository at this point in the history
* refactor(AddRoleRewardModal): WIP

* refactor: AddRewardButton WIP

* fix: bringing back previous functionality

* refactor(SelectRewardPanel): composition to pass SelectExistingPlatform as children

* remove duplicated ModalContent from SelectRolePanel

* refactor: create useSubmitAddReward hook

* dynamic reward modal width, move ModalContent into rendered components so it animates on every change

* cleanup(AddRoleRewardModal): remove impossible SelectRoleOrSetRequirements state

---------

Co-authored-by: valid <valid@zgen.hu>
  • Loading branch information
FBalint and dovalid authored May 21, 2024
1 parent d55bccc commit 1336585
Show file tree
Hide file tree
Showing 23 changed files with 872 additions and 769 deletions.
372 changes: 45 additions & 327 deletions src/components/[guild]/AddRewardButton/AddRewardButton.tsx

Large diffs are not rendered by default.

151 changes: 151 additions & 0 deletions src/components/[guild]/AddRewardButton/SelectRolePanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import {
HStack,
IconButton,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
Stack,
Text,
Tooltip,
useColorModeValue,
} from "@chakra-ui/react"
import Button from "components/common/Button"
import { ArrowLeft, Info } from "phosphor-react"
import SelectRoleOrSetRequirements from "platforms/components/SelectRoleOrSetRequirements"
import rewards, { CAPACITY_TIME_PLATFORMS } from "platforms/rewards"
import { useState } from "react"
import { useFormContext, useWatch } from "react-hook-form"
import { RoleTypeToAddTo, useAddRewardContext } from "../AddRewardContext"
import { defaultValues } from "./AddRewardButton"
import AvailabilitySetup from "./components/AvailabilitySetup"
import useSubmitAddReward from "./hooks/useSubmitAddReward"

const SelectRolePanel = () => {
const { modalRef, selection, activeTab, setStep, isBackButtonDisabled } =
useAddRewardContext()

const { onSubmit, isLoading } = useSubmitAddReward()

const lightModalBgColor = useColorModeValue("white", "gray.700")

const methods = useFormContext()
const rolePlatform = methods.getValues("rolePlatforms.0")

const requirements = useWatch({ name: "requirements", control: methods.control })
const roleIds = useWatch({ name: "roleIds", control: methods.control })

const [saveAsDraft, setSaveAsDraft] = useState(false)

const isRoleSelectorDisabled = selection === "ERC20"
const isAddRewardButtonDisabled =
activeTab === RoleTypeToAddTo.NEW_ROLE || isRoleSelectorDisabled
? !requirements?.length
: !roleIds?.length

const { RewardPreview } = rewards[selection] ?? {}

const goBack = () => {
if (!rewards[selection].autoRewardSetup) methods.reset(defaultValues)
setStep("HOME")
}

return (
<ModalContent>
<ModalCloseButton />
<ModalHeader bgColor={lightModalBgColor} boxShadow={"sm"} zIndex={1}>
<Stack spacing={8}>
<HStack>
<IconButton
isDisabled={isBackButtonDisabled}
rounded="full"
aria-label="Back"
size="sm"
mb="-3px"
icon={<ArrowLeft size={20} />}
variant="ghost"
onClick={goBack}
/>
<Text>{`Add ${rewards[selection].name} reward`}</Text>
</HStack>

<RewardPreview>
{CAPACITY_TIME_PLATFORMS.includes(selection) && (
<AvailabilitySetup
platformType={rolePlatform?.guildPlatform?.platformName}
rolePlatform={rolePlatform}
defaultValues={{
/**
* If the user doesn't upload mint links for a POAP, we should
* fallback to undefined, since 0 is not a valid value here
*/
capacity:
rolePlatform?.guildPlatform?.platformGuildData?.texts?.length ||
undefined,
/** POAPs have default startTime and endTime */
startTime: rolePlatform?.startTime,
endTime: rolePlatform?.endTime,
}}
onDone={({ capacity, startTime, endTime }) => {
methods.setValue(`rolePlatforms.0.capacity`, capacity)
methods.setValue(`rolePlatforms.0.startTime`, startTime)
methods.setValue(`rolePlatforms.0.endTime`, endTime)
}}
/>
)}
</RewardPreview>
</Stack>
</ModalHeader>

<ModalBody
ref={modalRef}
className="custom-scrollbar"
display="flex"
flexDir="column"
>
<SelectRoleOrSetRequirements
selectedPlatform={selection}
isRoleSelectorDisabled={isRoleSelectorDisabled}
/>
</ModalBody>

<ModalFooter pt="6" pb="8" gap={2}>
<Button
isDisabled={isAddRewardButtonDisabled}
onClick={methods.handleSubmit((data) => {
setSaveAsDraft(true)
onSubmit(data, "DRAFT")
})}
isLoading={saveAsDraft && isLoading}
rightIcon={
<Tooltip
label={
activeTab === RoleTypeToAddTo.EXISTING_ROLE
? "The reward will be added to the role you select with hidden visibility, so users won't see it yet. You can edit & activate it later"
: "The role will be created with hidden visibility, so users won't see it yet. You can edit & activate it later"
}
>
<Info />
</Tooltip>
}
>
Save as draft
</Button>
<Button
isDisabled={isAddRewardButtonDisabled}
colorScheme="green"
onClick={methods.handleSubmit((data) => {
setSaveAsDraft(false)
onSubmit(data)
})}
isLoading={!saveAsDraft && isLoading}
>
Save
</Button>
</ModalFooter>
</ModalContent>
)
}

export default SelectRolePanel
135 changes: 135 additions & 0 deletions src/components/[guild]/AddRewardButton/hooks/useSubmitAddReward.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import { useAddRewardContext } from "components/[guild]/AddRewardContext"
import useGuild from "components/[guild]/hooks/useGuild"
import { usePostHogContext } from "components/_app/PostHogProvider"
import useCreateRole from "components/create-guild/hooks/useCreateRole"
import useToast from "hooks/useToast"
import rewards from "platforms/rewards"
import { useFormContext } from "react-hook-form"
import { PlatformType, Visibility } from "types"
import getRandomInt from "utils/getRandomInt"
import { defaultValues } from "../AddRewardButton"
import useCreateReqBasedTokenReward from "../useCreateTokenReward"
import useAddReward from "./useAddReward"
import { useAddRewardDiscardAlert } from "./useAddRewardDiscardAlert"

const isERC20 = (data) =>
data.rolePlatforms[0].guildPlatform.platformId === PlatformType.ERC20

const useSubmitAddReward = () => {
const toast = useToast()
const { selection, onClose: onAddRewardModalClose } = useAddRewardContext()
const [, setIsAddRewardPanelDirty] = useAddRewardDiscardAlert()
const { roles } = useGuild()
const { captureEvent } = usePostHogContext()

const methods = useFormContext()

const onCloseAndClear = () => {
methods.reset(defaultValues)
onAddRewardModalClose()
setIsAddRewardPanelDirty(false)
}

const { onSubmit: onCreateRoleSubmit, isLoading: isCreateRoleLoading } =
useCreateRole({
onSuccess: () => {
toast({ status: "success", title: "Reward successfully added" })
onCloseAndClear()
},
})

const { submitCreate: submitCreateReqBased, isLoading: erc20Loading } =
useCreateReqBasedTokenReward({
onSuccess: () => {
toast({ status: "success", title: "Reward successfully added" })
onCloseAndClear()
},
onError: (err) => console.error(err),
})

const { onSubmit: onAddRewardSubmit, isLoading: isAddRewardLoading } =
useAddReward({
onSuccess: () => {
captureEvent("[discord setup] successfully added to existing guild")
onCloseAndClear()
},
onError: (err) => {
captureEvent("[discord setup] failed to add to existing guild", {
error: err,
})
},
})

const isLoading = isAddRewardLoading || isCreateRoleLoading || erc20Loading

const submitERC20Reward = async (
data: any,
saveAs: "DRAFT" | "PUBLIC" = "PUBLIC"
) => {
const isRequirementBased =
data.rolePlatforms[0].dynamicAmount.operation.input.type ===
"REQUIREMENT_AMOUNT"

const guildPlatformExists = !!data.rolePlatforms[0].guildPlatformId

if (isRequirementBased) {
submitCreateReqBased(data, saveAs)
return
} else {
/** TODO: Write when static reward is needed */
if (guildPlatformExists) {
data.rolePlatforms[0].guildPlatform = {
platformId: PlatformType.ERC20,
platformName: "ERC20",
platformGuildId: "",
platformGuildData: {},
}
}
return
}
}

const onSubmit = async (data: any, saveAs: "DRAFT" | "PUBLIC" = "PUBLIC") => {
if (isERC20(data)) return submitERC20Reward(data, saveAs)

if (data.requirements?.length > 0) {
const roleVisibility =
saveAs === "DRAFT" ? Visibility.HIDDEN : Visibility.PUBLIC
onCreateRoleSubmit({
...data,
name: data.name || `New ${rewards[selection].name} role`,
imageUrl: data.imageUrl || `/guildLogos/${getRandomInt(286)}.svg`,
roleVisibility,
rolePlatforms: data.rolePlatforms.map((rp) => ({
...rp,
visibility: roleVisibility,
})),
})
} else {
onAddRewardSubmit({
...data.rolePlatforms[0].guildPlatform,
rolePlatforms: data.roleIds
?.filter((roleId) => !!roleId)
.map((roleId) => ({
// We'll be able to send additional params here, like capacity & time
roleId: +roleId,
/**
* Temporary for POINTS rewards, because they can be added to
* multiple roles and this field has a unique constraint in
* the DB
*/
platformRoleId: roleId,
...data.rolePlatforms[0],
visibility:
saveAs === "DRAFT"
? Visibility.HIDDEN
: roles.find((role) => role.id === +roleId).visibility,
})),
})
}
}

return { onSubmit, isLoading }
}

export default useSubmitAddReward
10 changes: 4 additions & 6 deletions src/components/[guild]/AddRewardContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import {
} from "react"
import { PlatformName } from "types"

type AddRewardStep = "HOME" | "SELECT_ROLE"

export enum RoleTypeToAddTo {
EXISTING_ROLE,
NEW_ROLE,
Expand All @@ -27,8 +25,8 @@ const AddRewardContext = createContext<{
scrollToTop: () => void
selection: PlatformName
setSelection: (newSelection: PlatformName) => void
step: AddRewardStep
setStep: (newStep: AddRewardStep) => void
step: string
setStep: (newStep: string) => void
activeTab: RoleTypeToAddTo
setActiveTab: Dispatch<SetStateAction<RoleTypeToAddTo>>
shouldShowCloseAlert: boolean
Expand All @@ -44,9 +42,9 @@ const AddRewardProvider = ({ children }: PropsWithChildren<unknown>) => {

const [selection, setSelectionOg] = useState<PlatformName>()

const [step, setStepOg] = useState<AddRewardStep>()
const [step, setStepOg] = useState<string>()

const setStep = (newStep: AddRewardStep) => {
const setStep = (newStep: string) => {
setStepOg(newStep)
scrollToTop()
}
Expand Down
Loading

0 comments on commit 1336585

Please sign in to comment.