sendou.ink/frontend-react/src/components/user/ProfileModal.tsx
2020-06-27 14:28:33 +03:00

203 lines
6.4 KiB
TypeScript

import React, { useState } from "react"
import Modal from "../elements/Modal"
import Label from "../elements/Label"
import Input from "../elements/Input"
import { useMutation } from "@apollo/react-hooks"
import { UPDATE_USER } from "../../graphql/mutations/updateUser"
import { Weapon } from "../../types"
import { useToast, Box } from "@chakra-ui/core"
import Select from "../elements/Select"
import { countries } from "../../utils/lists"
import SensInput from "./SensInput"
import WeaponSelector from "../common/WeaponSelector"
import Button from "../elements/Button"
import { useContext } from "react"
import MyThemeContext from "../../themeContext"
import { useEffect } from "react"
import MarkdownInput from "./MarkdownInput"
import { useTranslation } from "react-i18next"
interface ProfileModalProps {
closeModal: () => void
existingProfile: UpdateUserVars
}
interface UpdateUserVars {
country?: string
motion_sens?: number
stick_sens?: number
weapons?: Weapon[]
custom_url?: string
bio?: string
}
const ProfileModal: React.FC<ProfileModalProps> = ({
closeModal,
existingProfile,
}) => {
const { grayWithShade } = useContext(MyThemeContext)
const { t } = useTranslation()
const [profile, setProfile] = useState<UpdateUserVars>(existingProfile)
const [error, setError] = useState<string | null>(null)
const toast = useToast()
useEffect(() => {
if (
profile.motion_sens !== null &&
!profile.stick_sens &&
profile.stick_sens !== 0
) {
setError(t("users;Motion sensitivity entered without stick sensitivity"))
} else if (profile.custom_url && profile.custom_url.length < 2) {
setError(t("users;Custom URL has to be over 2 characters long"))
} else if (profile.custom_url && profile.custom_url.length > 32) {
setError(t("users;Custom URL can be at most 32 characters long"))
} else if (profile.custom_url && !isNaN(profile.custom_url as any)) {
setError(t("users;Custom URL has to contain at least one letter"))
} else if (
profile.custom_url &&
!/^[a-z0-9]+$/i.test(profile.custom_url as any)
) {
setError(t("users;Custom URL can only contain letters and numbers"))
} else if (profile.weapons && profile.weapons.length > 5) {
setError(t("users;Weapon pool's max size is 5"))
} else if (profile.bio && profile.bio.length > 10000) {
setError(t("users;Bio's max length is 10000"))
} else {
setError(null)
}
}, [profile, t])
const handleChange = (newValueObject: UpdateUserVars) => {
setProfile({ ...profile, ...newValueObject })
}
const [updateUser, { loading }] = useMutation<boolean, UpdateUserVars>(
UPDATE_USER,
{
variables: profile,
onCompleted: () => {
closeModal()
toast({
description: t("users;Profile updated"),
position: "top-right",
status: "success",
duration: 10000,
})
},
onError: (error) => {
toast({
title: t("An error occurred"),
description: error.message,
position: "top-right",
status: "error",
duration: 10000,
})
},
refetchQueries: ["searchForUser"],
}
)
return (
<Modal title={t("users;Editing profile")} closeModal={closeModal}>
<Box display="flex" flexDirection="column">
<Box>
<Label>{t("users;Profile picture")}</Label>
{t("users;profilePicExplanation")}
</Box>
<Box mt="1em">
<Input
label={t("users;Custom URL")}
textLeft="https://sendou.ink/u/"
value={profile.custom_url ?? ""}
disabled={!!existingProfile.custom_url}
setValue={(value: string) => handleChange({ custom_url: value })}
/>
<Box color={grayWithShade} mt="0.5em" fontSize="15px">
{t(
"users;Please note Custom URL can't be updated after you have set it."
)}
</Box>
</Box>
<Box mt="1em">
<Select
label={t("users;Country")}
isSearchable
value={
profile.country
? {
value: profile.country,
label: t(`countries;${profile.country.toUpperCase()}`),
}
: null
}
setValue={(country: string) => handleChange({ country: country })}
options={countries.map((countryObj) => ({
label: t(`countries;${countryObj.toUpperCase()}`),
value: countryObj,
}))}
/>
</Box>
<Box display="flex" mt="1em" flexWrap="wrap">
<Box mr="1em">
<SensInput
label={t("users;Stick sensitivity")}
value={profile.stick_sens}
onChange={(value) => handleChange({ stick_sens: value })}
/>
</Box>
<SensInput
label={t("users;Motion sensitivity")}
value={profile.motion_sens}
onChange={(value) => handleChange({ motion_sens: value })}
/>
</Box>
<Box mt="1em">
<WeaponSelector
label={t("users;Weapon pool")}
isMulti
setValue={(value: Weapon[]) => handleChange({ weapons: value })}
value={profile.weapons}
showAlts
/>
</Box>
<Box mt="1em">
<MarkdownInput
label={t("users;Bio")}
value={profile.bio ?? ""}
setValue={(value: string) => handleChange({ bio: value })}
limit={10000}
/>
<Box color={grayWithShade} mt="0.5em" fontSize="15px">
{t("users;markdownPrompt")}{" "}
<a href="/markdown" target="_blank" rel="noreferrer noopener">
https://sendou.ink/markdown
</a>
</Box>
</Box>
<Box mt="1em">
<Button
onClick={() => updateUser()}
disabled={!!error}
loading={loading}
>
{t("users;Submit")}
</Button>
<Box as="span" ml="0.5em">
<Button outlined onClick={() => closeModal()}>
{t("users;Cancel")}
</Button>
</Box>
</Box>
{error && (
<Box as="span" mt="0.5em" color="red.500">
{error}
</Box>
)}
</Box>
</Modal>
)
}
export default ProfileModal