Component cookie fixes
This commit is contained in:
parent
94c885513d
commit
61a762b29b
8 changed files with 1435 additions and 1290 deletions
|
|
@ -1,211 +1,255 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import React, { useEffect, useState } from "react"
|
||||
import { getCookie } from "cookies-next"
|
||||
import { useRouter } from "next/router"
|
||||
import { useSnapshot } from "valtio"
|
||||
import { useTranslation } from "next-i18next"
|
||||
|
||||
import * as Dialog from '@radix-ui/react-dialog'
|
||||
import * as Switch from '@radix-ui/react-switch'
|
||||
import * as Dialog from "@radix-ui/react-dialog"
|
||||
import * as Switch from "@radix-ui/react-switch"
|
||||
|
||||
import api from '~utils/api'
|
||||
import { accountState } from '~utils/accountState'
|
||||
import { pictureData } from '~utils/pictureData'
|
||||
import api from "~utils/api"
|
||||
import { accountState } from "~utils/accountState"
|
||||
import { pictureData } from "~utils/pictureData"
|
||||
|
||||
import Button from '~components/Button'
|
||||
import Button from "~components/Button"
|
||||
|
||||
import CrossIcon from '~public/icons/Cross.svg'
|
||||
import './index.scss'
|
||||
import CrossIcon from "~public/icons/Cross.svg"
|
||||
import "./index.scss"
|
||||
|
||||
const AccountModal = () => {
|
||||
const { account } = useSnapshot(accountState)
|
||||
const { account } = useSnapshot(accountState)
|
||||
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation("common")
|
||||
const locale =
|
||||
router.locale && ["en", "ja"].includes(router.locale) ? router.locale : "en"
|
||||
|
||||
// Cookies
|
||||
const [cookies, setCookies] = useCookies()
|
||||
// Cookies
|
||||
const cookie = getCookie("account")
|
||||
|
||||
const headers = (cookies.account != null) ? {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${cookies.account.access_token}`
|
||||
}
|
||||
} : {}
|
||||
|
||||
// State
|
||||
const [open, setOpen] = useState(false)
|
||||
const [picture, setPicture] = useState('')
|
||||
const [language, setLanguage] = useState('')
|
||||
const [gender, setGender] = useState(0)
|
||||
const [privateProfile, setPrivateProfile] = useState(false)
|
||||
const headers = {}
|
||||
// cookies.account != null
|
||||
// ? {
|
||||
// headers: {
|
||||
// Authorization: `Bearer ${cookies.account.access_token}`,
|
||||
// },
|
||||
// }
|
||||
// : {}
|
||||
|
||||
// Refs
|
||||
const pictureSelect = React.createRef<HTMLSelectElement>()
|
||||
const languageSelect = React.createRef<HTMLSelectElement>()
|
||||
const genderSelect = React.createRef<HTMLSelectElement>()
|
||||
const privateSelect = React.createRef<HTMLInputElement>()
|
||||
// State
|
||||
const [open, setOpen] = useState(false)
|
||||
const [picture, setPicture] = useState("")
|
||||
const [language, setLanguage] = useState("")
|
||||
const [gender, setGender] = useState(0)
|
||||
const [privateProfile, setPrivateProfile] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
if (cookies.user) setPicture(cookies.user.picture)
|
||||
if (cookies.user) setLanguage(cookies.user.language)
|
||||
if (cookies.user) setGender(cookies.user.gender)
|
||||
}, [cookies])
|
||||
// Refs
|
||||
const pictureSelect = React.createRef<HTMLSelectElement>()
|
||||
const languageSelect = React.createRef<HTMLSelectElement>()
|
||||
const genderSelect = React.createRef<HTMLSelectElement>()
|
||||
const privateSelect = React.createRef<HTMLInputElement>()
|
||||
|
||||
const pictureOptions = (
|
||||
pictureData.sort((a, b) => (a.name.en > b.name.en) ? 1 : -1).map((item, i) => {
|
||||
return (
|
||||
<option key={`picture-${i}`} value={item.filename}>{item.name[locale]}</option>
|
||||
)
|
||||
})
|
||||
)
|
||||
// useEffect(() => {
|
||||
// if (cookies.user) setPicture(cookies.user.picture)
|
||||
// if (cookies.user) setLanguage(cookies.user.language)
|
||||
// if (cookies.user) setGender(cookies.user.gender)
|
||||
// }, [cookies])
|
||||
|
||||
function handlePictureChange(event: React.ChangeEvent<HTMLSelectElement>) {
|
||||
if (pictureSelect.current)
|
||||
setPicture(pictureSelect.current.value)
|
||||
const pictureOptions = pictureData
|
||||
.sort((a, b) => (a.name.en > b.name.en ? 1 : -1))
|
||||
.map((item, i) => {
|
||||
return (
|
||||
<option key={`picture-${i}`} value={item.filename}>
|
||||
{item.name[locale]}
|
||||
</option>
|
||||
)
|
||||
})
|
||||
|
||||
function handlePictureChange(event: React.ChangeEvent<HTMLSelectElement>) {
|
||||
if (pictureSelect.current) setPicture(pictureSelect.current.value)
|
||||
}
|
||||
|
||||
function handleLanguageChange(event: React.ChangeEvent<HTMLSelectElement>) {
|
||||
if (languageSelect.current) setLanguage(languageSelect.current.value)
|
||||
}
|
||||
|
||||
function handleGenderChange(event: React.ChangeEvent<HTMLSelectElement>) {
|
||||
if (genderSelect.current) setGender(parseInt(genderSelect.current.value))
|
||||
}
|
||||
|
||||
function handlePrivateChange(checked: boolean) {
|
||||
setPrivateProfile(checked)
|
||||
}
|
||||
|
||||
function update(event: React.FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault()
|
||||
|
||||
const object = {
|
||||
user: {
|
||||
picture: picture,
|
||||
element: pictureData.find((i) => i.filename === picture)?.element,
|
||||
language: language,
|
||||
gender: gender,
|
||||
private: privateProfile,
|
||||
},
|
||||
}
|
||||
|
||||
function handleLanguageChange(event: React.ChangeEvent<HTMLSelectElement>) {
|
||||
if (languageSelect.current)
|
||||
setLanguage(languageSelect.current.value)
|
||||
}
|
||||
// api.endpoints.users
|
||||
// .update(cookies.account.user_id, object, headers)
|
||||
// .then((response) => {
|
||||
// const user = response.data.user
|
||||
|
||||
function handleGenderChange(event: React.ChangeEvent<HTMLSelectElement>) {
|
||||
if (genderSelect.current)
|
||||
setGender(parseInt(genderSelect.current.value))
|
||||
}
|
||||
// const cookieObj = {
|
||||
// picture: user.picture.picture,
|
||||
// element: user.picture.element,
|
||||
// gender: user.gender,
|
||||
// language: user.language,
|
||||
// }
|
||||
|
||||
function handlePrivateChange(checked: boolean) {
|
||||
setPrivateProfile(checked)
|
||||
}
|
||||
// setCookies("user", cookieObj, { path: "/" })
|
||||
|
||||
function update(event: React.FormEvent<HTMLFormElement>) {
|
||||
event.preventDefault()
|
||||
// accountState.account.user = {
|
||||
// id: user.id,
|
||||
// username: user.username,
|
||||
// picture: user.picture.picture,
|
||||
// element: user.picture.element,
|
||||
// gender: user.gender,
|
||||
// }
|
||||
|
||||
const object = {
|
||||
user: {
|
||||
picture: picture,
|
||||
element: pictureData.find(i => i.filename === picture)?.element,
|
||||
language: language,
|
||||
gender: gender,
|
||||
private: privateProfile
|
||||
}
|
||||
}
|
||||
// setOpen(false)
|
||||
// changeLanguage(user.language)
|
||||
// })
|
||||
}
|
||||
|
||||
api.endpoints.users.update(cookies.account.user_id, object, headers)
|
||||
.then(response => {
|
||||
const user = response.data.user
|
||||
function changeLanguage(newLanguage: string) {
|
||||
// if (newLanguage !== router.locale) {
|
||||
// setCookies("NEXT_LOCALE", newLanguage, { path: "/" })
|
||||
// router.push(router.asPath, undefined, { locale: newLanguage })
|
||||
// }
|
||||
}
|
||||
|
||||
const cookieObj = {
|
||||
picture: user.picture.picture,
|
||||
element: user.picture.element,
|
||||
gender: user.gender,
|
||||
language: user.language
|
||||
}
|
||||
|
||||
setCookies('user', cookieObj, { path: '/'})
|
||||
function openChange(open: boolean) {
|
||||
setOpen(open)
|
||||
}
|
||||
|
||||
accountState.account.user = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
picture: user.picture.picture,
|
||||
element: user.picture.element,
|
||||
gender: user.gender
|
||||
}
|
||||
return (
|
||||
<Dialog.Root open={open} onOpenChange={openChange}>
|
||||
<Dialog.Trigger asChild>
|
||||
<li className="MenuItem">
|
||||
<span>{t("menu.settings")}</span>
|
||||
</li>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Content
|
||||
className="Account Dialog"
|
||||
onOpenAutoFocus={(event) => event.preventDefault()}
|
||||
>
|
||||
<div className="DialogHeader">
|
||||
<div className="DialogTop">
|
||||
<Dialog.Title className="SubTitle">
|
||||
{t("modals.settings.title")}
|
||||
</Dialog.Title>
|
||||
<Dialog.Title className="DialogTitle">
|
||||
@{account.user?.username}
|
||||
</Dialog.Title>
|
||||
</div>
|
||||
<Dialog.Close className="DialogClose" asChild>
|
||||
<span>
|
||||
<CrossIcon />
|
||||
</span>
|
||||
</Dialog.Close>
|
||||
</div>
|
||||
|
||||
setOpen(false)
|
||||
changeLanguage(user.language)
|
||||
})
|
||||
}
|
||||
<form onSubmit={update}>
|
||||
<div className="field">
|
||||
<div className="left">
|
||||
<label>{t("modals.settings.labels.picture")}</label>
|
||||
</div>
|
||||
|
||||
function changeLanguage(newLanguage: string) {
|
||||
if (newLanguage !== router.locale) {
|
||||
setCookies('NEXT_LOCALE', newLanguage, { path: '/'})
|
||||
router.push(router.asPath, undefined, { locale: newLanguage })
|
||||
}
|
||||
}
|
||||
|
||||
function openChange(open: boolean) {
|
||||
setOpen(open)
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog.Root open={open} onOpenChange={openChange}>
|
||||
<Dialog.Trigger asChild>
|
||||
<li className="MenuItem">
|
||||
<span>{t('menu.settings')}</span>
|
||||
</li>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Content className="Account Dialog" onOpenAutoFocus={ (event) => event.preventDefault() }>
|
||||
<div className="DialogHeader">
|
||||
<div className="DialogTop">
|
||||
<Dialog.Title className="SubTitle">{t('modals.settings.title')}</Dialog.Title>
|
||||
<Dialog.Title className="DialogTitle">@{account.user?.username}</Dialog.Title>
|
||||
</div>
|
||||
<Dialog.Close className="DialogClose" asChild>
|
||||
<span>
|
||||
<CrossIcon />
|
||||
</span>
|
||||
</Dialog.Close>
|
||||
</div>
|
||||
|
||||
<form onSubmit={update}>
|
||||
<div className="field">
|
||||
<div className="left">
|
||||
<label>{t('modals.settings.labels.picture')}</label>
|
||||
</div>
|
||||
|
||||
<div className={`preview ${pictureData.find(i => i.filename === picture)?.element}`}>
|
||||
<img
|
||||
alt="Profile preview"
|
||||
srcSet={`/profile/${picture}.png,
|
||||
<div
|
||||
className={`preview ${
|
||||
pictureData.find((i) => i.filename === picture)?.element
|
||||
}`}
|
||||
>
|
||||
<img
|
||||
alt="Profile preview"
|
||||
srcSet={`/profile/${picture}.png,
|
||||
/profile/${picture}@2x.png 2x`}
|
||||
src={`/profile/${picture}.png`}
|
||||
/>
|
||||
</div>
|
||||
src={`/profile/${picture}.png`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<select name="picture" onChange={handlePictureChange} value={picture} ref={pictureSelect}>
|
||||
{pictureOptions}
|
||||
</select>
|
||||
</div>
|
||||
<div className="field">
|
||||
<div className="left">
|
||||
<label>{t('modals.settings.labels.gender')}</label>
|
||||
</div>
|
||||
<select
|
||||
name="picture"
|
||||
onChange={handlePictureChange}
|
||||
value={picture}
|
||||
ref={pictureSelect}
|
||||
>
|
||||
{pictureOptions}
|
||||
</select>
|
||||
</div>
|
||||
<div className="field">
|
||||
<div className="left">
|
||||
<label>{t("modals.settings.labels.gender")}</label>
|
||||
</div>
|
||||
|
||||
<select name="gender" onChange={handleGenderChange} value={gender} ref={genderSelect}>
|
||||
<option key="gran" value="0">{t('modals.settings.gender.gran')}</option>
|
||||
<option key="djeeta" value="1">{t('modals.settings.gender.djeeta')}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="field">
|
||||
<div className="left">
|
||||
<label>{t('modals.settings.labels.language')}</label>
|
||||
</div>
|
||||
<select
|
||||
name="gender"
|
||||
onChange={handleGenderChange}
|
||||
value={gender}
|
||||
ref={genderSelect}
|
||||
>
|
||||
<option key="gran" value="0">
|
||||
{t("modals.settings.gender.gran")}
|
||||
</option>
|
||||
<option key="djeeta" value="1">
|
||||
{t("modals.settings.gender.djeeta")}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="field">
|
||||
<div className="left">
|
||||
<label>{t("modals.settings.labels.language")}</label>
|
||||
</div>
|
||||
|
||||
<select name="language" onChange={handleLanguageChange} value={language} ref={languageSelect}>
|
||||
<option key="en" value="en">{t('modals.settings.language.english')}</option>
|
||||
<option key="jp" value="ja">{t('modals.settings.language.japanese')}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="field">
|
||||
<div className="left">
|
||||
<label>{t('modals.settings.labels.private')}</label>
|
||||
<p className={locale}>{t('modals.settings.descriptions.private')}</p>
|
||||
</div>
|
||||
<select
|
||||
name="language"
|
||||
onChange={handleLanguageChange}
|
||||
value={language}
|
||||
ref={languageSelect}
|
||||
>
|
||||
<option key="en" value="en">
|
||||
{t("modals.settings.language.english")}
|
||||
</option>
|
||||
<option key="jp" value="ja">
|
||||
{t("modals.settings.language.japanese")}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div className="field">
|
||||
<div className="left">
|
||||
<label>{t("modals.settings.labels.private")}</label>
|
||||
<p className={locale}>
|
||||
{t("modals.settings.descriptions.private")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Switch.Root className="Switch" onCheckedChange={handlePrivateChange} checked={privateProfile}>
|
||||
<Switch.Thumb className="Thumb" />
|
||||
</Switch.Root>
|
||||
</div>
|
||||
<Switch.Root
|
||||
className="Switch"
|
||||
onCheckedChange={handlePrivateChange}
|
||||
checked={privateProfile}
|
||||
>
|
||||
<Switch.Thumb className="Thumb" />
|
||||
</Switch.Root>
|
||||
</div>
|
||||
|
||||
<Button>{t('modals.settings.buttons.confirm')}</Button>
|
||||
</form>
|
||||
</Dialog.Content>
|
||||
<Dialog.Overlay className="Overlay" />
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
)
|
||||
<Button>{t("modals.settings.buttons.confirm")}</Button>
|
||||
</form>
|
||||
</Dialog.Content>
|
||||
<Dialog.Overlay className="Overlay" />
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export default AccountModal
|
||||
|
|
|
|||
|
|
@ -1,187 +1,201 @@
|
|||
import React, { useEffect, useState } from "react"
|
||||
import { useRouter } from "next/router"
|
||||
import { useSnapshot } from "valtio"
|
||||
import { useTranslation } from "next-i18next"
|
||||
import classNames from "classnames"
|
||||
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import classNames from 'classnames'
|
||||
import { accountState } from "~utils/accountState"
|
||||
import { formatTimeAgo } from "~utils/timeAgo"
|
||||
|
||||
import { accountState } from '~utils/accountState'
|
||||
import { formatTimeAgo } from '~utils/timeAgo'
|
||||
import Button from "~components/Button"
|
||||
import { ButtonType } from "~utils/enums"
|
||||
|
||||
import Button from '~components/Button'
|
||||
import { ButtonType } from '~utils/enums'
|
||||
|
||||
import './index.scss'
|
||||
import "./index.scss"
|
||||
|
||||
interface Props {
|
||||
shortcode: string
|
||||
id: string
|
||||
name: string
|
||||
raid: Raid
|
||||
grid: GridWeapon[]
|
||||
user?: User
|
||||
favorited: boolean
|
||||
createdAt: Date
|
||||
displayUser?: boolean | false
|
||||
onClick: (shortcode: string) => void
|
||||
onSave?: (partyId: string, favorited: boolean) => void
|
||||
shortcode: string
|
||||
id: string
|
||||
name: string
|
||||
raid: Raid
|
||||
grid: GridWeapon[]
|
||||
user?: User
|
||||
favorited: boolean
|
||||
createdAt: Date
|
||||
displayUser?: boolean | false
|
||||
onClick: (shortcode: string) => void
|
||||
onSave?: (partyId: string, favorited: boolean) => void
|
||||
}
|
||||
|
||||
const GridRep = (props: Props) => {
|
||||
const numWeapons: number = 9
|
||||
const numWeapons: number = 9
|
||||
|
||||
const { account } = useSnapshot(accountState)
|
||||
const { account } = useSnapshot(accountState)
|
||||
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation("common")
|
||||
const locale =
|
||||
router.locale && ["en", "ja"].includes(router.locale) ? router.locale : "en"
|
||||
|
||||
const [mainhand, setMainhand] = useState<Weapon>()
|
||||
const [weapons, setWeapons] = useState<GridArray<Weapon>>({})
|
||||
const [mainhand, setMainhand] = useState<Weapon>()
|
||||
const [weapons, setWeapons] = useState<GridArray<Weapon>>({})
|
||||
|
||||
const titleClass = classNames({
|
||||
'empty': !props.name
|
||||
})
|
||||
const titleClass = classNames({
|
||||
empty: !props.name,
|
||||
})
|
||||
|
||||
const raidClass = classNames({
|
||||
'raid': true,
|
||||
'empty': !props.raid
|
||||
})
|
||||
const raidClass = classNames({
|
||||
raid: true,
|
||||
empty: !props.raid,
|
||||
})
|
||||
|
||||
const userClass = classNames({
|
||||
'user': true,
|
||||
'empty': !props.user
|
||||
})
|
||||
const userClass = classNames({
|
||||
user: true,
|
||||
empty: !props.user,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const newWeapons = Array(numWeapons)
|
||||
useEffect(() => {
|
||||
const newWeapons = Array(numWeapons)
|
||||
|
||||
for (const [key, value] of Object.entries(props.grid)) {
|
||||
if (value.position == -1)
|
||||
setMainhand(value.object)
|
||||
else if (!value.mainhand && value.position != null)
|
||||
newWeapons[value.position] = value.object
|
||||
}
|
||||
|
||||
setWeapons(newWeapons)
|
||||
}, [props.grid])
|
||||
|
||||
function navigate() {
|
||||
props.onClick(props.shortcode)
|
||||
for (const [key, value] of Object.entries(props.grid)) {
|
||||
if (value.position == -1) setMainhand(value.object)
|
||||
else if (!value.mainhand && value.position != null)
|
||||
newWeapons[value.position] = value.object
|
||||
}
|
||||
|
||||
function generateMainhandImage() {
|
||||
let url = ''
|
||||
setWeapons(newWeapons)
|
||||
}, [props.grid])
|
||||
|
||||
if (mainhand) {
|
||||
if (mainhand.element == 0 && props.grid[0].element) {
|
||||
url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${mainhand.granblue_id}_${props.grid[0].element}.jpg`
|
||||
} else {
|
||||
url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${mainhand.granblue_id}.jpg`
|
||||
}
|
||||
}
|
||||
function navigate() {
|
||||
props.onClick(props.shortcode)
|
||||
}
|
||||
|
||||
return (mainhand) ?
|
||||
<img alt={mainhand.name[locale]} src={url} /> : ''
|
||||
function generateMainhandImage() {
|
||||
let url = ""
|
||||
|
||||
if (mainhand) {
|
||||
if (mainhand.element == 0 && props.grid[0].element) {
|
||||
url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${mainhand.granblue_id}_${props.grid[0].element}.jpg`
|
||||
} else {
|
||||
url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${mainhand.granblue_id}.jpg`
|
||||
}
|
||||
}
|
||||
|
||||
function generateGridImage(position: number) {
|
||||
let url = ''
|
||||
return mainhand && props.grid[0] ? (
|
||||
<img alt={mainhand.name[locale]} src={url} />
|
||||
) : (
|
||||
""
|
||||
)
|
||||
}
|
||||
|
||||
if (weapons[position]) {
|
||||
if (weapons[position].element == 0 && props.grid[position].element) {
|
||||
url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapons[position]?.granblue_id}_${props.grid[position].element}.jpg`
|
||||
} else {
|
||||
url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapons[position]?.granblue_id}.jpg`
|
||||
}
|
||||
}
|
||||
function generateGridImage(position: number) {
|
||||
let url = ""
|
||||
|
||||
return (weapons[position]) ?
|
||||
<img alt={weapons[position].name[locale]} src={url} /> : ''
|
||||
if (weapons[position]) {
|
||||
if (weapons[position].element == 0 && props.grid[position].element) {
|
||||
url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapons[position]?.granblue_id}_${props.grid[position].element}.jpg`
|
||||
} else {
|
||||
url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapons[position]?.granblue_id}.jpg`
|
||||
}
|
||||
}
|
||||
|
||||
function sendSaveData() {
|
||||
if (props.onSave)
|
||||
props.onSave(props.id, props.favorited)
|
||||
}
|
||||
return weapons[position] ? (
|
||||
<img alt={weapons[position].name[locale]} src={url} />
|
||||
) : (
|
||||
""
|
||||
)
|
||||
}
|
||||
|
||||
const userImage = () => {
|
||||
if (props.user)
|
||||
return (
|
||||
<img
|
||||
alt={props.user.picture.picture}
|
||||
className={`profile ${props.user.picture.element}`}
|
||||
srcSet={`/profile/${props.user.picture.picture}.png,
|
||||
function sendSaveData() {
|
||||
if (props.onSave) props.onSave(props.id, props.favorited)
|
||||
}
|
||||
|
||||
const userImage = () => {
|
||||
if (props.user)
|
||||
return (
|
||||
<img
|
||||
alt={props.user.picture.picture}
|
||||
className={`profile ${props.user.picture.element}`}
|
||||
srcSet={`/profile/${props.user.picture.picture}.png,
|
||||
/profile/${props.user.picture.picture}@2x.png 2x`}
|
||||
src={`/profile/${props.user.picture.picture}.png`}
|
||||
/>
|
||||
src={`/profile/${props.user.picture.picture}.png`}
|
||||
/>
|
||||
)
|
||||
else return <div className="no-user" />
|
||||
}
|
||||
|
||||
const details = (
|
||||
<div className="Details">
|
||||
<h2 className={titleClass} onClick={navigate}>
|
||||
{props.name ? props.name : t("no_title")}
|
||||
</h2>
|
||||
<div className="bottom">
|
||||
<div className={raidClass}>
|
||||
{props.raid ? props.raid.name[locale] : t("no_raid")}
|
||||
</div>
|
||||
<time className="last-updated" dateTime={props.createdAt.toISOString()}>
|
||||
{formatTimeAgo(props.createdAt, locale)}
|
||||
</time>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const detailsWithUsername = (
|
||||
<div className="Details">
|
||||
<div className="top">
|
||||
<div className="info">
|
||||
<h2 className={titleClass} onClick={navigate}>
|
||||
{props.name ? props.name : t("no_title")}
|
||||
</h2>
|
||||
<div className={raidClass}>
|
||||
{props.raid ? props.raid.name[locale] : t("no_raid")}
|
||||
</div>
|
||||
</div>
|
||||
{account.authorized &&
|
||||
((props.user && account.user && account.user.id !== props.user.id) ||
|
||||
!props.user) ? (
|
||||
<Button
|
||||
active={props.favorited}
|
||||
icon="save"
|
||||
type={ButtonType.IconOnly}
|
||||
onClick={sendSaveData}
|
||||
/>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
<div className="bottom">
|
||||
<div className={userClass}>
|
||||
{userImage()}
|
||||
{props.user ? props.user.username : t("no_user")}
|
||||
</div>
|
||||
<time className="last-updated" dateTime={props.createdAt.toISOString()}>
|
||||
{formatTimeAgo(props.createdAt, locale)}
|
||||
</time>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="GridRep">
|
||||
{props.displayUser ? detailsWithUsername : details}
|
||||
<div className="Grid" onClick={navigate}>
|
||||
<div className="weapon grid_mainhand">{generateMainhandImage()}</div>
|
||||
|
||||
<ul className="grid_weapons">
|
||||
{Array.from(Array(numWeapons)).map((x, i) => {
|
||||
return (
|
||||
<li
|
||||
key={`${props.shortcode}-${i}`}
|
||||
className="weapon grid_weapon"
|
||||
>
|
||||
{generateGridImage(i)}
|
||||
</li>
|
||||
)
|
||||
else
|
||||
return (<div className="no-user" />)
|
||||
}
|
||||
|
||||
const details = (
|
||||
<div className="Details">
|
||||
<h2 className={titleClass} onClick={navigate}>{ (props.name) ? props.name : t('no_title') }</h2>
|
||||
<div className="bottom">
|
||||
<div className={raidClass}>{ (props.raid) ? props.raid.name[locale] : t('no_raid') }</div>
|
||||
<time className="last-updated" dateTime={props.createdAt.toISOString()}>{formatTimeAgo(props.createdAt, locale)}</time>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
const detailsWithUsername = (
|
||||
<div className="Details">
|
||||
<div className="top">
|
||||
<div className="info">
|
||||
<h2 className={titleClass} onClick={navigate}>{ (props.name) ? props.name : t('no_title') }</h2>
|
||||
<div className={raidClass}>{ (props.raid) ? props.raid.name[locale] : t('no_raid') }</div>
|
||||
</div>
|
||||
{
|
||||
(account.authorized && (
|
||||
(props.user && account.user && account.user.id !== props.user.id)
|
||||
|| (!props.user)
|
||||
)) ?
|
||||
<Button
|
||||
active={props.favorited}
|
||||
icon="save"
|
||||
type={ButtonType.IconOnly}
|
||||
onClick={sendSaveData} />
|
||||
: ''
|
||||
}
|
||||
</div>
|
||||
<div className="bottom">
|
||||
<div className={userClass}>
|
||||
{ userImage() }
|
||||
{ (props.user) ? props.user.username : t('no_user') }
|
||||
</div>
|
||||
<time className="last-updated" dateTime={props.createdAt.toISOString()}>{formatTimeAgo(props.createdAt, locale)}</time>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="GridRep">
|
||||
{ (props.displayUser) ? detailsWithUsername : details}
|
||||
<div className="Grid" onClick={navigate}>
|
||||
<div className="weapon grid_mainhand">
|
||||
{generateMainhandImage()}
|
||||
</div>
|
||||
|
||||
<ul className="grid_weapons">
|
||||
{
|
||||
Array.from(Array(numWeapons)).map((x, i) => {
|
||||
return (
|
||||
<li key={`${props.shortcode}-${i}`} className="weapon grid_weapon">
|
||||
{generateGridImage(i)}
|
||||
</li>
|
||||
)
|
||||
})
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default GridRep
|
||||
|
|
|
|||
|
|
@ -1,15 +1,10 @@
|
|||
.GridRepCollection {
|
||||
display: grid;
|
||||
grid-template-columns: auto auto auto;
|
||||
margin: 0 auto;
|
||||
opacity: 0;
|
||||
padding: 0;
|
||||
width: fit-content;
|
||||
transition: opacity 0.14s ease-in-out;
|
||||
// width: fit-content;
|
||||
max-width: 996px;
|
||||
|
||||
&.visible {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
display: grid;
|
||||
grid-template-columns: auto auto auto;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
width: fit-content;
|
||||
transition: opacity 0.14s ease-in-out;
|
||||
// width: fit-content;
|
||||
max-width: 996px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,24 +1,18 @@
|
|||
import classNames from 'classnames'
|
||||
import React from 'react'
|
||||
import classNames from "classnames"
|
||||
import React from "react"
|
||||
|
||||
import './index.scss'
|
||||
import "./index.scss"
|
||||
|
||||
interface Props {
|
||||
loading: boolean
|
||||
children: React.ReactNode
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const GridRepCollection = (props: Props) => {
|
||||
const classes = classNames({
|
||||
'GridRepCollection': true,
|
||||
'visible': !props.loading
|
||||
})
|
||||
|
||||
return (
|
||||
<div className={classes}>
|
||||
{props.children}
|
||||
</div>
|
||||
)
|
||||
const classes = classNames({
|
||||
GridRepCollection: true,
|
||||
})
|
||||
|
||||
return <div className={classes}>{props.children}</div>
|
||||
}
|
||||
|
||||
export default GridRepCollection
|
||||
|
|
|
|||
|
|
@ -1,213 +1,216 @@
|
|||
import React, { useState } from 'react'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import Router, { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { AxiosResponse } from 'axios'
|
||||
import React, { useState } from "react"
|
||||
import { setCookie } from "cookies-next"
|
||||
import Router, { useRouter } from "next/router"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { AxiosResponse } from "axios"
|
||||
|
||||
import * as Dialog from '@radix-ui/react-dialog'
|
||||
import * as Dialog from "@radix-ui/react-dialog"
|
||||
|
||||
import api from '~utils/api'
|
||||
import { accountState } from '~utils/accountState'
|
||||
import api from "~utils/api"
|
||||
import { accountState } from "~utils/accountState"
|
||||
|
||||
import Button from '~components/Button'
|
||||
import Fieldset from '~components/Fieldset'
|
||||
import Button from "~components/Button"
|
||||
import Fieldset from "~components/Fieldset"
|
||||
|
||||
import CrossIcon from '~public/icons/Cross.svg'
|
||||
import './index.scss'
|
||||
import CrossIcon from "~public/icons/Cross.svg"
|
||||
import "./index.scss"
|
||||
|
||||
interface Props {}
|
||||
|
||||
interface ErrorMap {
|
||||
[index: string]: string
|
||||
email: string
|
||||
password: string
|
||||
[index: string]: string
|
||||
email: string
|
||||
password: string
|
||||
}
|
||||
|
||||
const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||
const emailRegex =
|
||||
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||
|
||||
const LoginModal = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation("common")
|
||||
|
||||
// Set up form states and error handling
|
||||
const [formValid, setFormValid] = useState(false)
|
||||
const [errors, setErrors] = useState<ErrorMap>({
|
||||
email: '',
|
||||
password: ''
|
||||
})
|
||||
// Set up form states and error handling
|
||||
const [formValid, setFormValid] = useState(false)
|
||||
const [errors, setErrors] = useState<ErrorMap>({
|
||||
email: "",
|
||||
password: "",
|
||||
})
|
||||
|
||||
// Cookies
|
||||
const [cookies, setCookies] = useCookies()
|
||||
// States
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
// States
|
||||
const [open, setOpen] = useState(false)
|
||||
// Set up form refs
|
||||
const emailInput: React.RefObject<HTMLInputElement> = React.createRef()
|
||||
const passwordInput: React.RefObject<HTMLInputElement> = React.createRef()
|
||||
const form: React.RefObject<HTMLInputElement>[] = [emailInput, passwordInput]
|
||||
|
||||
// Set up form refs
|
||||
const emailInput: React.RefObject<HTMLInputElement> = React.createRef()
|
||||
const passwordInput: React.RefObject<HTMLInputElement> = React.createRef()
|
||||
const form: React.RefObject<HTMLInputElement>[] = [emailInput, passwordInput]
|
||||
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
const { name, value } = event.target
|
||||
let newErrors = { ...errors }
|
||||
|
||||
function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
const { name, value } = event.target
|
||||
let newErrors = {...errors}
|
||||
switch (name) {
|
||||
case "email":
|
||||
if (value.length == 0)
|
||||
newErrors.email = t("modals.login.errors.empty_email")
|
||||
else if (!emailRegex.test(value))
|
||||
newErrors.email = t("modals.login.errors.invalid_email")
|
||||
else newErrors.email = ""
|
||||
break
|
||||
|
||||
switch(name) {
|
||||
case 'email':
|
||||
if (value.length == 0)
|
||||
newErrors.email = t('modals.login.errors.empty_email')
|
||||
else if (!emailRegex.test(value))
|
||||
newErrors.email = t('modals.login.errors.invalid_email')
|
||||
else
|
||||
newErrors.email = ''
|
||||
break
|
||||
case "password":
|
||||
newErrors.password =
|
||||
value.length == 0 ? t("modals.login.errors.empty_password") : ""
|
||||
break
|
||||
|
||||
case 'password':
|
||||
newErrors.password = value.length == 0
|
||||
? t('modals.login.errors.empty_password')
|
||||
: ''
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
setErrors(newErrors)
|
||||
setFormValid(validateForm(newErrors))
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
function validateForm(errors: ErrorMap) {
|
||||
let valid = true
|
||||
setErrors(newErrors)
|
||||
setFormValid(validateForm(newErrors))
|
||||
}
|
||||
|
||||
Object.values(form).forEach(
|
||||
(input) => input.current?.value.length == 0 && (valid = false)
|
||||
)
|
||||
function validateForm(errors: ErrorMap) {
|
||||
let valid = true
|
||||
|
||||
Object.values(errors).forEach(
|
||||
(error) => error.length > 0 && (valid = false)
|
||||
)
|
||||
|
||||
return valid
|
||||
}
|
||||
|
||||
function login(event: React.FormEvent) {
|
||||
event.preventDefault()
|
||||
|
||||
const body = {
|
||||
email: emailInput.current?.value,
|
||||
password: passwordInput.current?.value,
|
||||
grant_type: 'password'
|
||||
}
|
||||
|
||||
if (formValid) {
|
||||
api.login(body)
|
||||
.then(response => {
|
||||
storeCookieInfo(response)
|
||||
return response.data.user.id
|
||||
})
|
||||
.then(id => fetchUserInfo(id))
|
||||
.then(infoResponse => storeUserInfo(infoResponse))
|
||||
}
|
||||
}
|
||||
|
||||
function fetchUserInfo(id: string) {
|
||||
return api.userInfo(id)
|
||||
}
|
||||
|
||||
function storeCookieInfo(response: AxiosResponse) {
|
||||
const user = response.data.user
|
||||
|
||||
const cookieObj = {
|
||||
user_id: user.id,
|
||||
username: user.username,
|
||||
access_token: response.data.access_token
|
||||
}
|
||||
|
||||
setCookies('account', cookieObj, { path: '/' })
|
||||
}
|
||||
|
||||
function storeUserInfo(response: AxiosResponse) {
|
||||
const user = response.data.user
|
||||
|
||||
const cookieObj = {
|
||||
picture: user.picture.picture,
|
||||
element: user.picture.element,
|
||||
language: user.language,
|
||||
gender: user.gender
|
||||
}
|
||||
|
||||
setCookies('user', cookieObj, { path: '/' })
|
||||
|
||||
accountState.account.user = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
picture: user.picture.picture,
|
||||
element: user.picture.element,
|
||||
gender: user.gender
|
||||
}
|
||||
|
||||
accountState.account.authorized = true
|
||||
|
||||
setOpen(false)
|
||||
changeLanguage(user.language)
|
||||
}
|
||||
|
||||
function changeLanguage(newLanguage: string) {
|
||||
if (newLanguage !== router.locale) {
|
||||
setCookies('NEXT_LOCALE', newLanguage, { path: '/'})
|
||||
router.push(router.asPath, undefined, { locale: newLanguage })
|
||||
}
|
||||
}
|
||||
|
||||
function openChange(open: boolean) {
|
||||
setOpen(open)
|
||||
setErrors({
|
||||
email: '',
|
||||
password: ''
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog.Root open={open} onOpenChange={openChange}>
|
||||
<Dialog.Trigger asChild>
|
||||
<li className="MenuItem">
|
||||
<span>{t('menu.login')}</span>
|
||||
</li>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Content className="Login Dialog" onOpenAutoFocus={ (event) => event.preventDefault() }>
|
||||
<div className="DialogHeader">
|
||||
<Dialog.Title className="DialogTitle">{t('modals.login.title')}</Dialog.Title>
|
||||
<Dialog.Close className="DialogClose" asChild>
|
||||
<span>
|
||||
<CrossIcon />
|
||||
</span>
|
||||
</Dialog.Close>
|
||||
</div>
|
||||
|
||||
<form className="form" onSubmit={login}>
|
||||
<Fieldset
|
||||
fieldName="email"
|
||||
placeholder={t('modals.login.placeholders.email')}
|
||||
onChange={handleChange}
|
||||
error={errors.email}
|
||||
ref={emailInput}
|
||||
/>
|
||||
|
||||
<Fieldset
|
||||
fieldName="password"
|
||||
placeholder={t('modals.login.placeholders.password')}
|
||||
onChange={handleChange}
|
||||
error={errors.password}
|
||||
ref={passwordInput}
|
||||
/>
|
||||
|
||||
<Button>{t('modals.login.buttons.confirm')}</Button>
|
||||
</form>
|
||||
</Dialog.Content>
|
||||
<Dialog.Overlay className="Overlay" />
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
Object.values(form).forEach(
|
||||
(input) => input.current?.value.length == 0 && (valid = false)
|
||||
)
|
||||
|
||||
Object.values(errors).forEach(
|
||||
(error) => error.length > 0 && (valid = false)
|
||||
)
|
||||
|
||||
return valid
|
||||
}
|
||||
|
||||
function login(event: React.FormEvent) {
|
||||
event.preventDefault()
|
||||
|
||||
const body = {
|
||||
email: emailInput.current?.value,
|
||||
password: passwordInput.current?.value,
|
||||
grant_type: "password",
|
||||
}
|
||||
|
||||
if (formValid) {
|
||||
api
|
||||
.login(body)
|
||||
.then((response) => {
|
||||
storeCookieInfo(response)
|
||||
return response.data.user.id
|
||||
})
|
||||
.then((id) => fetchUserInfo(id))
|
||||
.then((infoResponse) => storeUserInfo(infoResponse))
|
||||
}
|
||||
}
|
||||
|
||||
function fetchUserInfo(id: string) {
|
||||
return api.userInfo(id)
|
||||
}
|
||||
|
||||
function storeCookieInfo(response: AxiosResponse) {
|
||||
const user = response.data.user
|
||||
|
||||
const cookieObj: AccountCookie = {
|
||||
userId: user.id,
|
||||
username: user.username,
|
||||
token: response.data.access_token,
|
||||
}
|
||||
|
||||
setCookie("account", cookieObj, { path: "/" })
|
||||
}
|
||||
|
||||
function storeUserInfo(response: AxiosResponse) {
|
||||
const user = response.data.user
|
||||
|
||||
const cookieObj: UserCookie = {
|
||||
picture: user.picture.picture,
|
||||
element: user.picture.element,
|
||||
language: user.language,
|
||||
gender: user.gender,
|
||||
}
|
||||
|
||||
setCookie("user", cookieObj, { path: "/" })
|
||||
|
||||
accountState.account.user = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
picture: user.picture.picture,
|
||||
element: user.picture.element,
|
||||
gender: user.gender,
|
||||
}
|
||||
|
||||
console.log("Authorizing account...")
|
||||
accountState.account.authorized = true
|
||||
|
||||
setOpen(false)
|
||||
changeLanguage(user.language)
|
||||
}
|
||||
|
||||
function changeLanguage(newLanguage: string) {
|
||||
if (newLanguage !== router.locale) {
|
||||
setCookie("NEXT_LOCALE", newLanguage, { path: "/" })
|
||||
router.push(router.asPath, undefined, { locale: newLanguage })
|
||||
}
|
||||
}
|
||||
|
||||
function openChange(open: boolean) {
|
||||
setOpen(open)
|
||||
setErrors({
|
||||
email: "",
|
||||
password: "",
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog.Root open={open} onOpenChange={openChange}>
|
||||
<Dialog.Trigger asChild>
|
||||
<li className="MenuItem">
|
||||
<span>{t("menu.login")}</span>
|
||||
</li>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Content
|
||||
className="Login Dialog"
|
||||
onOpenAutoFocus={(event) => event.preventDefault()}
|
||||
>
|
||||
<div className="DialogHeader">
|
||||
<Dialog.Title className="DialogTitle">
|
||||
{t("modals.login.title")}
|
||||
</Dialog.Title>
|
||||
<Dialog.Close className="DialogClose" asChild>
|
||||
<span>
|
||||
<CrossIcon />
|
||||
</span>
|
||||
</Dialog.Close>
|
||||
</div>
|
||||
|
||||
<form className="form" onSubmit={login}>
|
||||
<Fieldset
|
||||
fieldName="email"
|
||||
placeholder={t("modals.login.placeholders.email")}
|
||||
onChange={handleChange}
|
||||
error={errors.email}
|
||||
ref={emailInput}
|
||||
/>
|
||||
|
||||
<Fieldset
|
||||
fieldName="password"
|
||||
placeholder={t("modals.login.placeholders.password")}
|
||||
onChange={handleChange}
|
||||
error={errors.password}
|
||||
ref={passwordInput}
|
||||
/>
|
||||
|
||||
<Button>{t("modals.login.buttons.confirm")}</Button>
|
||||
</form>
|
||||
</Dialog.Content>
|
||||
<Dialog.Overlay className="Overlay" />
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoginModal
|
||||
export default LoginModal
|
||||
|
|
|
|||
|
|
@ -1,311 +1,348 @@
|
|||
import React, { useEffect, useRef, useState } from 'react'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import InfiniteScroll from 'react-infinite-scroll-component'
|
||||
import React, { useEffect, useRef, useState } from "react"
|
||||
import { getCookie, setCookie } from "cookies-next"
|
||||
import { useRouter } from "next/router"
|
||||
import { useSnapshot } from "valtio"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import InfiniteScroll from "react-infinite-scroll-component"
|
||||
|
||||
import { appState } from '~utils/appState'
|
||||
import api from '~utils/api'
|
||||
import { appState } from "~utils/appState"
|
||||
import api from "~utils/api"
|
||||
|
||||
import * as Dialog from '@radix-ui/react-dialog'
|
||||
import * as Dialog from "@radix-ui/react-dialog"
|
||||
|
||||
import CharacterSearchFilterBar from '~components/CharacterSearchFilterBar'
|
||||
import WeaponSearchFilterBar from '~components/WeaponSearchFilterBar'
|
||||
import SummonSearchFilterBar from '~components/SummonSearchFilterBar'
|
||||
import CharacterSearchFilterBar from "~components/CharacterSearchFilterBar"
|
||||
import WeaponSearchFilterBar from "~components/WeaponSearchFilterBar"
|
||||
import SummonSearchFilterBar from "~components/SummonSearchFilterBar"
|
||||
|
||||
import CharacterResult from '~components/CharacterResult'
|
||||
import WeaponResult from '~components/WeaponResult'
|
||||
import SummonResult from '~components/SummonResult'
|
||||
import CharacterResult from "~components/CharacterResult"
|
||||
import WeaponResult from "~components/WeaponResult"
|
||||
import SummonResult from "~components/SummonResult"
|
||||
|
||||
import './index.scss'
|
||||
import CrossIcon from '~public/icons/Cross.svg'
|
||||
import cloneDeep from 'lodash.clonedeep'
|
||||
import "./index.scss"
|
||||
import CrossIcon from "~public/icons/Cross.svg"
|
||||
import cloneDeep from "lodash.clonedeep"
|
||||
|
||||
interface Props {
|
||||
send: (object: Character | Weapon | Summon, position: number) => any
|
||||
placeholderText: string
|
||||
fromPosition: number
|
||||
object: 'weapons' | 'characters' | 'summons',
|
||||
children: React.ReactNode
|
||||
send: (object: Character | Weapon | Summon, position: number) => any
|
||||
placeholderText: string
|
||||
fromPosition: number
|
||||
object: "weapons" | "characters" | "summons"
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const SearchModal = (props: Props) => {
|
||||
// Set up snapshot of app state
|
||||
let { grid, search } = useSnapshot(appState)
|
||||
// Set up snapshot of app state
|
||||
let { grid, search } = useSnapshot(appState)
|
||||
|
||||
// Set up router
|
||||
const router = useRouter()
|
||||
const locale = router.locale
|
||||
// Set up router
|
||||
const router = useRouter()
|
||||
const locale = router.locale
|
||||
|
||||
// Set up translation
|
||||
const { t } = useTranslation('common')
|
||||
// Set up translation
|
||||
const { t } = useTranslation("common")
|
||||
|
||||
// Set up cookies
|
||||
const [cookies, setCookies] = useCookies()
|
||||
let searchInput = React.createRef<HTMLInputElement>()
|
||||
let scrollContainer = React.createRef<HTMLDivElement>()
|
||||
|
||||
let searchInput = React.createRef<HTMLInputElement>()
|
||||
let scrollContainer = React.createRef<HTMLDivElement>()
|
||||
const [firstLoad, setFirstLoad] = useState(true)
|
||||
const [objects, setObjects] = useState<{
|
||||
[id: number]: GridCharacter | GridWeapon | GridSummon
|
||||
}>()
|
||||
const [filters, setFilters] = useState<{ [key: string]: number[] }>()
|
||||
const [open, setOpen] = useState(false)
|
||||
const [query, setQuery] = useState("")
|
||||
const [results, setResults] = useState<(Weapon | Summon | Character)[]>([])
|
||||
|
||||
const [firstLoad, setFirstLoad] = useState(true)
|
||||
const [objects, setObjects] = useState<{[id: number]: GridCharacter | GridWeapon | GridSummon}>()
|
||||
const [filters, setFilters] = useState<{ [key: string]: number[] }>()
|
||||
const [open, setOpen] = useState(false)
|
||||
const [query, setQuery] = useState('')
|
||||
const [results, setResults] = useState<(Weapon | Summon | Character)[]>([])
|
||||
// Pagination states
|
||||
const [recordCount, setRecordCount] = useState(0)
|
||||
const [currentPage, setCurrentPage] = useState(1)
|
||||
const [totalPages, setTotalPages] = useState(1)
|
||||
|
||||
// Pagination states
|
||||
const [recordCount, setRecordCount] = useState(0)
|
||||
const [currentPage, setCurrentPage] = useState(1)
|
||||
const [totalPages, setTotalPages] = useState(1)
|
||||
useEffect(() => {
|
||||
setObjects(grid[props.object])
|
||||
}, [grid, props.object])
|
||||
|
||||
useEffect(() => {
|
||||
setObjects(grid[props.object])
|
||||
}, [grid, props.object])
|
||||
useEffect(() => {
|
||||
if (searchInput.current) searchInput.current.focus()
|
||||
}, [searchInput])
|
||||
|
||||
useEffect(() => {
|
||||
if (searchInput.current)
|
||||
searchInput.current.focus()
|
||||
}, [searchInput])
|
||||
function inputChanged(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
const text = event.target.value
|
||||
if (text.length) {
|
||||
setQuery(text)
|
||||
} else {
|
||||
setQuery("")
|
||||
}
|
||||
}
|
||||
|
||||
function inputChanged(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
const text = event.target.value
|
||||
if (text.length) {
|
||||
setQuery(text)
|
||||
function fetchResults({ replace = false }: { replace?: boolean }) {
|
||||
api
|
||||
.search({
|
||||
object: props.object,
|
||||
query: query,
|
||||
filters: filters,
|
||||
locale: locale,
|
||||
page: currentPage,
|
||||
})
|
||||
.then((response) => {
|
||||
setTotalPages(response.data.total_pages)
|
||||
setRecordCount(response.data.count)
|
||||
|
||||
if (replace) {
|
||||
replaceResults(response.data.count, response.data.results)
|
||||
} else {
|
||||
setQuery('')
|
||||
appendResults(response.data.results)
|
||||
}
|
||||
}
|
||||
|
||||
function fetchResults({ replace = false }: { replace?: boolean }) {
|
||||
api.search({
|
||||
object: props.object,
|
||||
query: query,
|
||||
filters: filters,
|
||||
locale: locale,
|
||||
page: currentPage
|
||||
}).then(response => {
|
||||
setTotalPages(response.data.total_pages)
|
||||
setRecordCount(response.data.count)
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
})
|
||||
}
|
||||
|
||||
if (replace) {
|
||||
replaceResults(response.data.count, response.data.results)
|
||||
} else {
|
||||
appendResults(response.data.results)
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error(error)
|
||||
})
|
||||
function replaceResults(
|
||||
count: number,
|
||||
list: Weapon[] | Summon[] | Character[]
|
||||
) {
|
||||
if (count > 0) {
|
||||
setResults(list)
|
||||
} else {
|
||||
setResults([])
|
||||
}
|
||||
}
|
||||
|
||||
function appendResults(list: Weapon[] | Summon[] | Character[]) {
|
||||
setResults([...results, ...list])
|
||||
}
|
||||
|
||||
function storeRecentResult(result: Character | Weapon | Summon) {
|
||||
const key = `recent_${props.object}`
|
||||
const cookie = getCookie(key)
|
||||
const cookieObj: Character[] | Weapon[] | Summon[] = cookie
|
||||
? JSON.parse(cookie as string)
|
||||
: []
|
||||
let recents: Character[] | Weapon[] | Summon[] = []
|
||||
|
||||
if (props.object === "weapons") {
|
||||
recents = cloneDeep(cookieObj as Weapon[]) || []
|
||||
if (!recents.find((item) => item.granblue_id === result.granblue_id)) {
|
||||
recents.unshift(result as Weapon)
|
||||
}
|
||||
} else if (props.object === "summons") {
|
||||
recents = cloneDeep(cookieObj as Summon[]) || []
|
||||
if (!recents.find((item) => item.granblue_id === result.granblue_id)) {
|
||||
recents.unshift(result as Summon)
|
||||
}
|
||||
}
|
||||
|
||||
function replaceResults(count: number, list: Weapon[] | Summon[] | Character[]) {
|
||||
if (count > 0) {
|
||||
setResults(list)
|
||||
} else {
|
||||
setResults([])
|
||||
}
|
||||
if (recents && recents.length > 5) recents.pop()
|
||||
setCookie(`recent_${props.object}`, recents, { path: "/" })
|
||||
sendData(result)
|
||||
}
|
||||
|
||||
function sendData(result: Character | Weapon | Summon) {
|
||||
props.send(result, props.fromPosition)
|
||||
openChange()
|
||||
}
|
||||
|
||||
function receiveFilters(filters: { [key: string]: number[] }) {
|
||||
setCurrentPage(1)
|
||||
setResults([])
|
||||
setFilters(filters)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// Current page changed
|
||||
if (open && currentPage > 1) {
|
||||
fetchResults({ replace: false })
|
||||
} else if (open && currentPage == 1) {
|
||||
fetchResults({ replace: true })
|
||||
}
|
||||
}, [currentPage])
|
||||
|
||||
function appendResults(list: Weapon[] | Summon[] | Character[]) {
|
||||
setResults([...results, ...list])
|
||||
}
|
||||
useEffect(() => {
|
||||
// Filters changed
|
||||
const key = `recent_${props.object}`
|
||||
const cookie = getCookie(key)
|
||||
const cookieObj: Weapon[] | Summon[] | Character[] = cookie
|
||||
? JSON.parse(cookie as string)
|
||||
: []
|
||||
|
||||
function storeRecentResult(result: Character | Weapon | Summon) {
|
||||
const key = `recent_${props.object}`
|
||||
let recents: Character[] | Weapon[] | Summon[] = []
|
||||
|
||||
if (props.object === "weapons") {
|
||||
recents = cloneDeep(cookies[key] as Weapon[]) || []
|
||||
if (!recents.find(item => item.granblue_id === result.granblue_id)) {
|
||||
recents.unshift(result as Weapon)
|
||||
}
|
||||
} else if (props.object === "summons") {
|
||||
recents = cloneDeep(cookies[key] as Summon[]) || []
|
||||
if (!recents.find(item => item.granblue_id === result.granblue_id)) {
|
||||
recents.unshift(result as Summon)
|
||||
}
|
||||
}
|
||||
|
||||
if (recents && recents.length > 5) recents.pop()
|
||||
setCookies(`recent_${props.object}`, recents, { path: '/' })
|
||||
sendData(result)
|
||||
}
|
||||
|
||||
function sendData(result: Character | Weapon | Summon) {
|
||||
props.send(result, props.fromPosition)
|
||||
openChange()
|
||||
}
|
||||
|
||||
function receiveFilters(filters: { [key: string]: number[] }) {
|
||||
if (open) {
|
||||
if (firstLoad && cookieObj && cookieObj.length > 0) {
|
||||
setResults(cookieObj)
|
||||
setRecordCount(cookieObj.length)
|
||||
setFirstLoad(false)
|
||||
} else {
|
||||
setCurrentPage(1)
|
||||
setResults([])
|
||||
setFilters(filters)
|
||||
fetchResults({ replace: true })
|
||||
}
|
||||
}
|
||||
}, [filters])
|
||||
|
||||
useEffect(() => {
|
||||
// Current page changed
|
||||
if (open && currentPage > 1) {
|
||||
fetchResults({ replace: false })
|
||||
} else if (open && currentPage == 1) {
|
||||
fetchResults({ replace: true })
|
||||
}
|
||||
}, [currentPage])
|
||||
|
||||
useEffect(() => {
|
||||
// Filters changed
|
||||
const key = `recent_${props.object}`
|
||||
|
||||
if (open) {
|
||||
if (firstLoad && cookies[key] && cookies[key].length > 0) {
|
||||
setResults(cookies[key])
|
||||
setRecordCount(cookies[key].length)
|
||||
setFirstLoad(false)
|
||||
} else {
|
||||
setCurrentPage(1)
|
||||
fetchResults({ replace: true })
|
||||
}
|
||||
}
|
||||
}, [filters])
|
||||
|
||||
useEffect(() => {
|
||||
// Query changed
|
||||
if (open && query.length != 1) {
|
||||
setCurrentPage(1)
|
||||
fetchResults({ replace: true })
|
||||
}
|
||||
}, [query])
|
||||
|
||||
function renderResults() {
|
||||
let jsx
|
||||
|
||||
switch(props.object) {
|
||||
case 'weapons':
|
||||
jsx = renderWeaponSearchResults()
|
||||
break
|
||||
case 'summons':
|
||||
jsx = renderSummonSearchResults(results)
|
||||
break
|
||||
case 'characters':
|
||||
jsx = renderCharacterSearchResults(results)
|
||||
break
|
||||
}
|
||||
|
||||
return (
|
||||
<InfiniteScroll
|
||||
dataLength={ (results && results.length > 0) ? results.length : 0}
|
||||
next={ () => setCurrentPage(currentPage + 1) }
|
||||
hasMore={totalPages > currentPage}
|
||||
scrollableTarget="Results"
|
||||
loader={<div className="footer">Loading...</div>}>
|
||||
{jsx}
|
||||
</InfiniteScroll>
|
||||
)
|
||||
useEffect(() => {
|
||||
// Query changed
|
||||
if (open && query.length != 1) {
|
||||
setCurrentPage(1)
|
||||
fetchResults({ replace: true })
|
||||
}
|
||||
}, [query])
|
||||
|
||||
function renderWeaponSearchResults() {
|
||||
let jsx: React.ReactNode
|
||||
|
||||
const castResults: Weapon[] = results as Weapon[]
|
||||
if (castResults && Object.keys(castResults).length > 0) {
|
||||
jsx = castResults.map((result: Weapon) => {
|
||||
return <WeaponResult
|
||||
key={result.id}
|
||||
data={result}
|
||||
onClick={() => { storeRecentResult(result) }}
|
||||
/>
|
||||
})
|
||||
}
|
||||
function renderResults() {
|
||||
let jsx
|
||||
|
||||
return jsx
|
||||
}
|
||||
|
||||
function renderSummonSearchResults(results: { [key: string]: any }) {
|
||||
let jsx: React.ReactNode
|
||||
|
||||
const castResults: Summon[] = results as Summon[]
|
||||
if (castResults && Object.keys(castResults).length > 0) {
|
||||
jsx = castResults.map((result: Summon) => {
|
||||
return <SummonResult
|
||||
key={result.id}
|
||||
data={result}
|
||||
onClick={() => { storeRecentResult(result) }}
|
||||
/>
|
||||
})
|
||||
}
|
||||
|
||||
return jsx
|
||||
}
|
||||
|
||||
function renderCharacterSearchResults(results: { [key: string]: any }) {
|
||||
let jsx: React.ReactNode
|
||||
|
||||
const castResults: Character[] = results as Character[]
|
||||
if (castResults && Object.keys(castResults).length > 0) {
|
||||
jsx = castResults.map((result: Character) => {
|
||||
return <CharacterResult
|
||||
key={result.id}
|
||||
data={result}
|
||||
onClick={() => { storeRecentResult(result) }}
|
||||
/>
|
||||
})
|
||||
}
|
||||
|
||||
return jsx
|
||||
}
|
||||
|
||||
function openChange() {
|
||||
if (open) {
|
||||
setQuery('')
|
||||
setFirstLoad(true)
|
||||
setResults([])
|
||||
setRecordCount(0)
|
||||
setCurrentPage(1)
|
||||
setOpen(false)
|
||||
} else {
|
||||
setOpen(true)
|
||||
}
|
||||
switch (props.object) {
|
||||
case "weapons":
|
||||
jsx = renderWeaponSearchResults()
|
||||
break
|
||||
case "summons":
|
||||
jsx = renderSummonSearchResults(results)
|
||||
break
|
||||
case "characters":
|
||||
jsx = renderCharacterSearchResults(results)
|
||||
break
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog.Root open={open} onOpenChange={openChange}>
|
||||
<Dialog.Trigger asChild>
|
||||
{props.children}
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Content className="Search Dialog">
|
||||
<div id="Header">
|
||||
<div id="Bar">
|
||||
<label className="search_label" htmlFor="search_input">
|
||||
<input
|
||||
autoComplete="off"
|
||||
type="text"
|
||||
name="query"
|
||||
className="Input"
|
||||
id="search_input"
|
||||
ref={searchInput}
|
||||
value={query}
|
||||
placeholder={props.placeholderText}
|
||||
onChange={inputChanged}
|
||||
/>
|
||||
</label>
|
||||
<Dialog.Close className="DialogClose" onClick={openChange}>
|
||||
<CrossIcon />
|
||||
</Dialog.Close>
|
||||
</div>
|
||||
{ (props.object === 'characters') ? <CharacterSearchFilterBar sendFilters={receiveFilters} /> : '' }
|
||||
{ (props.object === 'weapons') ? <WeaponSearchFilterBar sendFilters={receiveFilters} /> : '' }
|
||||
{ (props.object === 'summons') ? <SummonSearchFilterBar sendFilters={receiveFilters} /> : '' }
|
||||
</div>
|
||||
|
||||
<div id="Results" ref={scrollContainer}>
|
||||
<h5 className="total">{t('search.result_count', { "record_count": recordCount })}</h5>
|
||||
{ (open) ? renderResults() : ''}
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
<Dialog.Overlay className="Overlay" />
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
<InfiniteScroll
|
||||
dataLength={results && results.length > 0 ? results.length : 0}
|
||||
next={() => setCurrentPage(currentPage + 1)}
|
||||
hasMore={totalPages > currentPage}
|
||||
scrollableTarget="Results"
|
||||
loader={<div className="footer">Loading...</div>}
|
||||
>
|
||||
{jsx}
|
||||
</InfiniteScroll>
|
||||
)
|
||||
}
|
||||
|
||||
function renderWeaponSearchResults() {
|
||||
let jsx: React.ReactNode
|
||||
|
||||
const castResults: Weapon[] = results as Weapon[]
|
||||
if (castResults && Object.keys(castResults).length > 0) {
|
||||
jsx = castResults.map((result: Weapon) => {
|
||||
return (
|
||||
<WeaponResult
|
||||
key={result.id}
|
||||
data={result}
|
||||
onClick={() => {
|
||||
storeRecentResult(result)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return jsx
|
||||
}
|
||||
|
||||
function renderSummonSearchResults(results: { [key: string]: any }) {
|
||||
let jsx: React.ReactNode
|
||||
|
||||
const castResults: Summon[] = results as Summon[]
|
||||
if (castResults && Object.keys(castResults).length > 0) {
|
||||
jsx = castResults.map((result: Summon) => {
|
||||
return (
|
||||
<SummonResult
|
||||
key={result.id}
|
||||
data={result}
|
||||
onClick={() => {
|
||||
storeRecentResult(result)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return jsx
|
||||
}
|
||||
|
||||
function renderCharacterSearchResults(results: { [key: string]: any }) {
|
||||
let jsx: React.ReactNode
|
||||
|
||||
const castResults: Character[] = results as Character[]
|
||||
if (castResults && Object.keys(castResults).length > 0) {
|
||||
jsx = castResults.map((result: Character) => {
|
||||
return (
|
||||
<CharacterResult
|
||||
key={result.id}
|
||||
data={result}
|
||||
onClick={() => {
|
||||
storeRecentResult(result)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return jsx
|
||||
}
|
||||
|
||||
function openChange() {
|
||||
if (open) {
|
||||
setQuery("")
|
||||
setFirstLoad(true)
|
||||
setResults([])
|
||||
setRecordCount(0)
|
||||
setCurrentPage(1)
|
||||
setOpen(false)
|
||||
} else {
|
||||
setOpen(true)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog.Root open={open} onOpenChange={openChange}>
|
||||
<Dialog.Trigger asChild>{props.children}</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Content className="Search Dialog">
|
||||
<div id="Header">
|
||||
<div id="Bar">
|
||||
<label className="search_label" htmlFor="search_input">
|
||||
<input
|
||||
autoComplete="off"
|
||||
type="text"
|
||||
name="query"
|
||||
className="Input"
|
||||
id="search_input"
|
||||
ref={searchInput}
|
||||
value={query}
|
||||
placeholder={props.placeholderText}
|
||||
onChange={inputChanged}
|
||||
/>
|
||||
</label>
|
||||
<Dialog.Close className="DialogClose" onClick={openChange}>
|
||||
<CrossIcon />
|
||||
</Dialog.Close>
|
||||
</div>
|
||||
{props.object === "characters" ? (
|
||||
<CharacterSearchFilterBar sendFilters={receiveFilters} />
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
{props.object === "weapons" ? (
|
||||
<WeaponSearchFilterBar sendFilters={receiveFilters} />
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
{props.object === "summons" ? (
|
||||
<SummonSearchFilterBar sendFilters={receiveFilters} />
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div id="Results" ref={scrollContainer}>
|
||||
<h5 className="total">
|
||||
{t("search.result_count", { record_count: recordCount })}
|
||||
</h5>
|
||||
{open ? renderResults() : ""}
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
<Dialog.Overlay className="Overlay" />
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export default SearchModal
|
||||
export default SearchModal
|
||||
|
|
|
|||
|
|
@ -1,306 +1,324 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import { useRouter } from 'next/router'
|
||||
import { Trans, useTranslation } from 'next-i18next'
|
||||
import { AxiosResponse } from 'axios'
|
||||
import React, { useEffect, useState } from "react"
|
||||
import Link from "next/link"
|
||||
import { setCookie } from "cookies-next"
|
||||
import { useRouter } from "next/router"
|
||||
import { Trans, useTranslation } from "next-i18next"
|
||||
import { AxiosResponse } from "axios"
|
||||
|
||||
import * as Dialog from '@radix-ui/react-dialog'
|
||||
import * as Dialog from "@radix-ui/react-dialog"
|
||||
|
||||
import api from '~utils/api'
|
||||
import { accountState } from '~utils/accountState'
|
||||
import api from "~utils/api"
|
||||
import { accountState } from "~utils/accountState"
|
||||
|
||||
import Button from '~components/Button'
|
||||
import Fieldset from '~components/Fieldset'
|
||||
import Button from "~components/Button"
|
||||
import Fieldset from "~components/Fieldset"
|
||||
|
||||
import CrossIcon from '~public/icons/Cross.svg'
|
||||
import './index.scss'
|
||||
import CrossIcon from "~public/icons/Cross.svg"
|
||||
import "./index.scss"
|
||||
|
||||
interface Props {}
|
||||
|
||||
interface ErrorMap {
|
||||
[index: string]: string
|
||||
username: string
|
||||
email: string
|
||||
password: string
|
||||
passwordConfirmation: string
|
||||
[index: string]: string
|
||||
username: string
|
||||
email: string
|
||||
password: string
|
||||
passwordConfirmation: string
|
||||
}
|
||||
|
||||
const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||
const emailRegex =
|
||||
/^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/
|
||||
|
||||
const SignupModal = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
// Set up form states and error handling
|
||||
const [formValid, setFormValid] = useState(false)
|
||||
const [errors, setErrors] = useState<ErrorMap>({
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
passwordConfirmation: ''
|
||||
})
|
||||
const router = useRouter()
|
||||
const { t } = useTranslation("common")
|
||||
|
||||
// Cookies
|
||||
const [cookies, setCookies] = useCookies()
|
||||
// Set up form states and error handling
|
||||
const [formValid, setFormValid] = useState(false)
|
||||
const [errors, setErrors] = useState<ErrorMap>({
|
||||
username: "",
|
||||
email: "",
|
||||
password: "",
|
||||
passwordConfirmation: "",
|
||||
})
|
||||
|
||||
// States
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
// Set up form refs
|
||||
const usernameInput = React.createRef<HTMLInputElement>()
|
||||
const emailInput = React.createRef<HTMLInputElement>()
|
||||
const passwordInput = React.createRef<HTMLInputElement>()
|
||||
const passwordConfirmationInput = React.createRef<HTMLInputElement>()
|
||||
const form = [usernameInput, emailInput, passwordInput, passwordConfirmationInput]
|
||||
// States
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
function register(event: React.FormEvent) {
|
||||
event.preventDefault()
|
||||
// Set up form refs
|
||||
const usernameInput = React.createRef<HTMLInputElement>()
|
||||
const emailInput = React.createRef<HTMLInputElement>()
|
||||
const passwordInput = React.createRef<HTMLInputElement>()
|
||||
const passwordConfirmationInput = React.createRef<HTMLInputElement>()
|
||||
const form = [
|
||||
usernameInput,
|
||||
emailInput,
|
||||
passwordInput,
|
||||
passwordConfirmationInput,
|
||||
]
|
||||
|
||||
const body = {
|
||||
user: {
|
||||
username: usernameInput.current?.value,
|
||||
email: emailInput.current?.value,
|
||||
password: passwordInput.current?.value,
|
||||
password_confirmation: passwordConfirmationInput.current?.value,
|
||||
language: router.locale
|
||||
}
|
||||
}
|
||||
function register(event: React.FormEvent) {
|
||||
event.preventDefault()
|
||||
|
||||
if (formValid)
|
||||
api.endpoints.users.create(body)
|
||||
.then(response => {
|
||||
storeCookieInfo(response)
|
||||
return response.data.user.user_id
|
||||
})
|
||||
.then(id => fetchUserInfo(id))
|
||||
.then(infoResponse => storeUserInfo(infoResponse))
|
||||
const body = {
|
||||
user: {
|
||||
username: usernameInput.current?.value,
|
||||
email: emailInput.current?.value,
|
||||
password: passwordInput.current?.value,
|
||||
password_confirmation: passwordConfirmationInput.current?.value,
|
||||
language: router.locale,
|
||||
},
|
||||
}
|
||||
|
||||
function storeCookieInfo(response: AxiosResponse) {
|
||||
const user = response.data.user
|
||||
|
||||
const cookieObj = {
|
||||
user_id: user.user_id,
|
||||
username: user.username,
|
||||
access_token: user.token
|
||||
}
|
||||
|
||||
setCookies('account', cookieObj, { path: '/'})
|
||||
}
|
||||
|
||||
function fetchUserInfo(id: string) {
|
||||
return api.userInfo(id)
|
||||
}
|
||||
|
||||
function storeUserInfo(response: AxiosResponse) {
|
||||
const user = response.data.user
|
||||
|
||||
const cookieObj = {
|
||||
picture: user.picture.picture,
|
||||
element: user.picture.element,
|
||||
language: user.language,
|
||||
gender: user.gender
|
||||
}
|
||||
|
||||
// TODO: Set language
|
||||
setCookies('user', cookieObj, { path: '/'})
|
||||
|
||||
accountState.account.user = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
picture: user.picture.picture,
|
||||
element: user.picture.element,
|
||||
gender: user.gender
|
||||
}
|
||||
|
||||
accountState.account.authorized = true
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
function handleNameChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
event.preventDefault()
|
||||
|
||||
const fieldName = event.target.name
|
||||
const value = event.target.value
|
||||
|
||||
if (value.length >= 3) {
|
||||
api.check(fieldName, value)
|
||||
.then((response) => {
|
||||
processNameCheck(fieldName, value, response.data.available)
|
||||
}, (error) => {
|
||||
console.error(error)
|
||||
})
|
||||
} else {
|
||||
validateName(fieldName, value)
|
||||
}
|
||||
}
|
||||
|
||||
function processNameCheck(fieldName: string, value: string, available: boolean) {
|
||||
const newErrors = {...errors}
|
||||
|
||||
if (available) {
|
||||
// Continue checking for errors
|
||||
newErrors[fieldName] = ''
|
||||
setErrors(newErrors)
|
||||
setFormValid(true)
|
||||
|
||||
validateName(fieldName, value)
|
||||
} else {
|
||||
newErrors[fieldName] = t('modals.signup.errors.field_in_use', { field: fieldName})
|
||||
setErrors(newErrors)
|
||||
setFormValid(false)
|
||||
}
|
||||
}
|
||||
|
||||
function validateName(fieldName: string, value: string) {
|
||||
let newErrors = {...errors}
|
||||
|
||||
switch(fieldName) {
|
||||
case 'username':
|
||||
if (value.length < 3)
|
||||
newErrors.username = t('modals.signup.errors.username_too_short')
|
||||
else if (value.length > 20)
|
||||
newErrors.username = t('modals.signup.errors.username_too_long')
|
||||
else
|
||||
newErrors.username = ''
|
||||
|
||||
break
|
||||
|
||||
case 'email':
|
||||
newErrors.email = emailRegex.test(value)
|
||||
? ''
|
||||
: t('modals.signup.errors.invalid_email')
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
setFormValid(validateForm(newErrors))
|
||||
}
|
||||
|
||||
function handlePasswordChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
event.preventDefault()
|
||||
|
||||
const { name, value } = event.target
|
||||
let newErrors = {...errors}
|
||||
|
||||
switch(name) {
|
||||
case 'password':
|
||||
newErrors.password = passwordInput.current?.value.includes(usernameInput.current?.value!)
|
||||
? t('modals.signup.errors.password_contains_username')
|
||||
: ''
|
||||
break
|
||||
|
||||
case 'password':
|
||||
newErrors.password = value.length < 8
|
||||
? t('modals.signup.errors.password_too_short')
|
||||
: ''
|
||||
break
|
||||
|
||||
case 'confirm_password':
|
||||
newErrors.passwordConfirmation = passwordInput.current?.value === passwordConfirmationInput.current?.value
|
||||
? ''
|
||||
: t('modals.signup.errors.passwords_dont_match')
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
setFormValid(validateForm(newErrors))
|
||||
}
|
||||
|
||||
function validateForm(errors: ErrorMap) {
|
||||
let valid = true
|
||||
|
||||
Object.values(form).forEach(
|
||||
(input) => input.current?.value.length == 0 && (valid = false)
|
||||
)
|
||||
|
||||
Object.values(errors).forEach(
|
||||
(error) => error.length > 0 && (valid = false)
|
||||
)
|
||||
|
||||
return valid
|
||||
}
|
||||
|
||||
function openChange(open: boolean) {
|
||||
setOpen(open)
|
||||
setErrors({
|
||||
username: '',
|
||||
email: '',
|
||||
password: '',
|
||||
passwordConfirmation: ''
|
||||
if (formValid)
|
||||
api.endpoints.users
|
||||
.create(body)
|
||||
.then((response) => {
|
||||
storeCookieInfo(response)
|
||||
return response.data.user.user_id
|
||||
})
|
||||
.then((id) => fetchUserInfo(id))
|
||||
.then((infoResponse) => storeUserInfo(infoResponse))
|
||||
}
|
||||
|
||||
function storeCookieInfo(response: AxiosResponse) {
|
||||
const user = response.data.user
|
||||
|
||||
const cookieObj: AccountCookie = {
|
||||
userId: user.user_id,
|
||||
username: user.username,
|
||||
token: user.token,
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog.Root open={open} onOpenChange={openChange}>
|
||||
<Dialog.Trigger asChild>
|
||||
<li className="MenuItem">
|
||||
<span>{t('menu.signup')}</span>
|
||||
</li>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Content className="Signup Dialog" onOpenAutoFocus={ (event) => event.preventDefault() }>
|
||||
<div className="DialogHeader">
|
||||
<Dialog.Title className="DialogTitle">{t('modals.signup.title')}</Dialog.Title>
|
||||
<Dialog.Close className="DialogClose" asChild>
|
||||
<span>
|
||||
<CrossIcon />
|
||||
</span>
|
||||
</Dialog.Close>
|
||||
</div>
|
||||
setCookie("account", cookieObj, { path: "/" })
|
||||
}
|
||||
|
||||
<form className="form" onSubmit={register}>
|
||||
<Fieldset
|
||||
fieldName="username"
|
||||
placeholder={t('modals.signup.placeholders.username')}
|
||||
onChange={handleNameChange}
|
||||
error={errors.username}
|
||||
ref={usernameInput}
|
||||
/>
|
||||
function fetchUserInfo(id: string) {
|
||||
return api.userInfo(id)
|
||||
}
|
||||
|
||||
<Fieldset
|
||||
fieldName="email"
|
||||
placeholder={t('modals.signup.placeholders.email')}
|
||||
onChange={handleNameChange}
|
||||
error={errors.email}
|
||||
ref={emailInput}
|
||||
/>
|
||||
function storeUserInfo(response: AxiosResponse) {
|
||||
const user = response.data.user
|
||||
|
||||
<Fieldset
|
||||
fieldName="password"
|
||||
placeholder={t('modals.signup.placeholders.password')}
|
||||
onChange={handlePasswordChange}
|
||||
error={errors.password}
|
||||
ref={passwordInput}
|
||||
/>
|
||||
const cookieObj: UserCookie = {
|
||||
picture: user.picture.picture,
|
||||
element: user.picture.element,
|
||||
language: user.language,
|
||||
gender: user.gender,
|
||||
}
|
||||
|
||||
<Fieldset
|
||||
fieldName="confirm_password"
|
||||
placeholder={t('modals.signup.placeholders.password_confirm')}
|
||||
onChange={handlePasswordChange}
|
||||
error={errors.passwordConfirmation}
|
||||
ref={passwordConfirmationInput}
|
||||
/>
|
||||
// TODO: Set language
|
||||
setCookie("user", cookieObj, { path: "/" })
|
||||
|
||||
<Button>{t('modals.signup.buttons.confirm')}</Button>
|
||||
accountState.account.user = {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
picture: user.picture.picture,
|
||||
element: user.picture.element,
|
||||
gender: user.gender,
|
||||
}
|
||||
|
||||
<Dialog.Description className="terms">
|
||||
{/* <Trans i18nKey="modals.signup.agreement">
|
||||
accountState.account.authorized = true
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
function handleNameChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
event.preventDefault()
|
||||
|
||||
const fieldName = event.target.name
|
||||
const value = event.target.value
|
||||
|
||||
if (value.length >= 3) {
|
||||
api.check(fieldName, value).then(
|
||||
(response) => {
|
||||
processNameCheck(fieldName, value, response.data.available)
|
||||
},
|
||||
(error) => {
|
||||
console.error(error)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
validateName(fieldName, value)
|
||||
}
|
||||
}
|
||||
|
||||
function processNameCheck(
|
||||
fieldName: string,
|
||||
value: string,
|
||||
available: boolean
|
||||
) {
|
||||
const newErrors = { ...errors }
|
||||
|
||||
if (available) {
|
||||
// Continue checking for errors
|
||||
newErrors[fieldName] = ""
|
||||
setErrors(newErrors)
|
||||
setFormValid(true)
|
||||
|
||||
validateName(fieldName, value)
|
||||
} else {
|
||||
newErrors[fieldName] = t("modals.signup.errors.field_in_use", {
|
||||
field: fieldName,
|
||||
})
|
||||
setErrors(newErrors)
|
||||
setFormValid(false)
|
||||
}
|
||||
}
|
||||
|
||||
function validateName(fieldName: string, value: string) {
|
||||
let newErrors = { ...errors }
|
||||
|
||||
switch (fieldName) {
|
||||
case "username":
|
||||
if (value.length < 3)
|
||||
newErrors.username = t("modals.signup.errors.username_too_short")
|
||||
else if (value.length > 20)
|
||||
newErrors.username = t("modals.signup.errors.username_too_long")
|
||||
else newErrors.username = ""
|
||||
|
||||
break
|
||||
|
||||
case "email":
|
||||
newErrors.email = emailRegex.test(value)
|
||||
? ""
|
||||
: t("modals.signup.errors.invalid_email")
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
setFormValid(validateForm(newErrors))
|
||||
}
|
||||
|
||||
function handlePasswordChange(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
event.preventDefault()
|
||||
|
||||
const { name, value } = event.target
|
||||
let newErrors = { ...errors }
|
||||
|
||||
switch (name) {
|
||||
case "password":
|
||||
newErrors.password = passwordInput.current?.value.includes(
|
||||
usernameInput.current?.value!
|
||||
)
|
||||
? t("modals.signup.errors.password_contains_username")
|
||||
: ""
|
||||
break
|
||||
|
||||
case "password":
|
||||
newErrors.password =
|
||||
value.length < 8 ? t("modals.signup.errors.password_too_short") : ""
|
||||
break
|
||||
|
||||
case "confirm_password":
|
||||
newErrors.passwordConfirmation =
|
||||
passwordInput.current?.value ===
|
||||
passwordConfirmationInput.current?.value
|
||||
? ""
|
||||
: t("modals.signup.errors.passwords_dont_match")
|
||||
break
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
setFormValid(validateForm(newErrors))
|
||||
}
|
||||
|
||||
function validateForm(errors: ErrorMap) {
|
||||
let valid = true
|
||||
|
||||
Object.values(form).forEach(
|
||||
(input) => input.current?.value.length == 0 && (valid = false)
|
||||
)
|
||||
|
||||
Object.values(errors).forEach(
|
||||
(error) => error.length > 0 && (valid = false)
|
||||
)
|
||||
|
||||
return valid
|
||||
}
|
||||
|
||||
function openChange(open: boolean) {
|
||||
setOpen(open)
|
||||
setErrors({
|
||||
username: "",
|
||||
email: "",
|
||||
password: "",
|
||||
passwordConfirmation: "",
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog.Root open={open} onOpenChange={openChange}>
|
||||
<Dialog.Trigger asChild>
|
||||
<li className="MenuItem">
|
||||
<span>{t("menu.signup")}</span>
|
||||
</li>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Content
|
||||
className="Signup Dialog"
|
||||
onOpenAutoFocus={(event) => event.preventDefault()}
|
||||
>
|
||||
<div className="DialogHeader">
|
||||
<Dialog.Title className="DialogTitle">
|
||||
{t("modals.signup.title")}
|
||||
</Dialog.Title>
|
||||
<Dialog.Close className="DialogClose" asChild>
|
||||
<span>
|
||||
<CrossIcon />
|
||||
</span>
|
||||
</Dialog.Close>
|
||||
</div>
|
||||
|
||||
<form className="form" onSubmit={register}>
|
||||
<Fieldset
|
||||
fieldName="username"
|
||||
placeholder={t("modals.signup.placeholders.username")}
|
||||
onChange={handleNameChange}
|
||||
error={errors.username}
|
||||
ref={usernameInput}
|
||||
/>
|
||||
|
||||
<Fieldset
|
||||
fieldName="email"
|
||||
placeholder={t("modals.signup.placeholders.email")}
|
||||
onChange={handleNameChange}
|
||||
error={errors.email}
|
||||
ref={emailInput}
|
||||
/>
|
||||
|
||||
<Fieldset
|
||||
fieldName="password"
|
||||
placeholder={t("modals.signup.placeholders.password")}
|
||||
onChange={handlePasswordChange}
|
||||
error={errors.password}
|
||||
ref={passwordInput}
|
||||
/>
|
||||
|
||||
<Fieldset
|
||||
fieldName="confirm_password"
|
||||
placeholder={t("modals.signup.placeholders.password_confirm")}
|
||||
onChange={handlePasswordChange}
|
||||
error={errors.passwordConfirmation}
|
||||
ref={passwordConfirmationInput}
|
||||
/>
|
||||
|
||||
<Button>{t("modals.signup.buttons.confirm")}</Button>
|
||||
|
||||
<Dialog.Description className="terms">
|
||||
{/* <Trans i18nKey="modals.signup.agreement">
|
||||
By signing up, I agree to the <Link href="/privacy"><span>Privacy Policy</span></Link><Link href="/usage"><span>Usage Guidelines</span></Link>.
|
||||
</Trans> */}
|
||||
</Dialog.Description>
|
||||
</form>
|
||||
</Dialog.Content>
|
||||
<Dialog.Overlay className="Overlay" />
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
)
|
||||
</Dialog.Description>
|
||||
</form>
|
||||
</Dialog.Content>
|
||||
<Dialog.Overlay className="Overlay" />
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
export default SignupModal
|
||||
export default SignupModal
|
||||
|
|
|
|||
|
|
@ -1,223 +1,263 @@
|
|||
import React, { useState } from 'react'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useTranslation } from 'next-i18next'
|
||||
import { AxiosResponse } from 'axios'
|
||||
import React, { useState } from "react"
|
||||
// import { useCookies } from 'react-cookie'
|
||||
import { useRouter } from "next/router"
|
||||
import { useTranslation } from "next-i18next"
|
||||
import { AxiosResponse } from "axios"
|
||||
|
||||
import * as Dialog from '@radix-ui/react-dialog'
|
||||
import * as Dialog from "@radix-ui/react-dialog"
|
||||
|
||||
import AXSelect from '~components/AxSelect'
|
||||
import ElementToggle from '~components/ElementToggle'
|
||||
import WeaponKeyDropdown from '~components/WeaponKeyDropdown'
|
||||
import Button from '~components/Button'
|
||||
import AXSelect from "~components/AxSelect"
|
||||
import ElementToggle from "~components/ElementToggle"
|
||||
import WeaponKeyDropdown from "~components/WeaponKeyDropdown"
|
||||
import Button from "~components/Button"
|
||||
|
||||
import api from '~utils/api'
|
||||
import { appState } from '~utils/appState'
|
||||
import api from "~utils/api"
|
||||
import { appState } from "~utils/appState"
|
||||
|
||||
import CrossIcon from '~public/icons/Cross.svg'
|
||||
import './index.scss'
|
||||
import CrossIcon from "~public/icons/Cross.svg"
|
||||
import "./index.scss"
|
||||
|
||||
interface GridWeaponObject {
|
||||
weapon: {
|
||||
element?: number
|
||||
weapon_key1_id?: string
|
||||
weapon_key2_id?: string
|
||||
weapon_key3_id?: string
|
||||
ax_modifier1?: number
|
||||
ax_modifier2?: number
|
||||
ax_strength1?: number
|
||||
ax_strength2?: number
|
||||
}
|
||||
weapon: {
|
||||
element?: number
|
||||
weapon_key1_id?: string
|
||||
weapon_key2_id?: string
|
||||
weapon_key3_id?: string
|
||||
ax_modifier1?: number
|
||||
ax_modifier2?: number
|
||||
ax_strength1?: number
|
||||
ax_strength2?: number
|
||||
}
|
||||
}
|
||||
|
||||
interface Props {
|
||||
gridWeapon: GridWeapon
|
||||
children: React.ReactNode
|
||||
gridWeapon: GridWeapon
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const WeaponModal = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const locale = (router.locale && ['en', 'ja'].includes(router.locale)) ? router.locale : 'en'
|
||||
const { t } = useTranslation('common')
|
||||
|
||||
// Cookies
|
||||
const [cookies] = useCookies(['account'])
|
||||
const headers = (cookies.account != null) ? {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${cookies.account.access_token}`
|
||||
const router = useRouter()
|
||||
const locale =
|
||||
router.locale && ["en", "ja"].includes(router.locale) ? router.locale : "en"
|
||||
const { t } = useTranslation("common")
|
||||
|
||||
// Cookies
|
||||
const [cookies] = useCookies(["account"])
|
||||
const headers =
|
||||
cookies.account != null
|
||||
? {
|
||||
headers: {
|
||||
Authorization: `Bearer ${cookies.account.access_token}`,
|
||||
},
|
||||
}
|
||||
} : {}
|
||||
|
||||
// Refs
|
||||
const weaponKey1Select = React.createRef<HTMLSelectElement>()
|
||||
const weaponKey2Select = React.createRef<HTMLSelectElement>()
|
||||
const weaponKey3Select = React.createRef<HTMLSelectElement>()
|
||||
: {}
|
||||
|
||||
// State
|
||||
const [open, setOpen] = useState(false)
|
||||
const [formValid, setFormValid] = useState(false)
|
||||
// Refs
|
||||
const weaponKey1Select = React.createRef<HTMLSelectElement>()
|
||||
const weaponKey2Select = React.createRef<HTMLSelectElement>()
|
||||
const weaponKey3Select = React.createRef<HTMLSelectElement>()
|
||||
|
||||
const [element, setElement] = useState(-1)
|
||||
const [primaryAxModifier, setPrimaryAxModifier] = useState(-1)
|
||||
const [secondaryAxModifier, setSecondaryAxModifier] = useState(-1)
|
||||
const [primaryAxValue, setPrimaryAxValue] = useState(0.0)
|
||||
const [secondaryAxValue, setSecondaryAxValue] = useState(0.0)
|
||||
|
||||
function receiveAxValues(primaryAxModifier: number, primaryAxValue: number, secondaryAxModifier: number, secondaryAxValue: number) {
|
||||
setPrimaryAxModifier(primaryAxModifier)
|
||||
setSecondaryAxModifier(secondaryAxModifier)
|
||||
// State
|
||||
const [open, setOpen] = useState(false)
|
||||
const [formValid, setFormValid] = useState(false)
|
||||
|
||||
setPrimaryAxValue(primaryAxValue)
|
||||
setSecondaryAxValue(secondaryAxValue)
|
||||
const [element, setElement] = useState(-1)
|
||||
const [primaryAxModifier, setPrimaryAxModifier] = useState(-1)
|
||||
const [secondaryAxModifier, setSecondaryAxModifier] = useState(-1)
|
||||
const [primaryAxValue, setPrimaryAxValue] = useState(0.0)
|
||||
const [secondaryAxValue, setSecondaryAxValue] = useState(0.0)
|
||||
|
||||
function receiveAxValues(
|
||||
primaryAxModifier: number,
|
||||
primaryAxValue: number,
|
||||
secondaryAxModifier: number,
|
||||
secondaryAxValue: number
|
||||
) {
|
||||
setPrimaryAxModifier(primaryAxModifier)
|
||||
setSecondaryAxModifier(secondaryAxModifier)
|
||||
|
||||
setPrimaryAxValue(primaryAxValue)
|
||||
setSecondaryAxValue(secondaryAxValue)
|
||||
}
|
||||
|
||||
function receiveAxValidity(isValid: boolean) {
|
||||
setFormValid(isValid)
|
||||
}
|
||||
|
||||
function receiveElementValue(element: string) {
|
||||
setElement(parseInt(element))
|
||||
}
|
||||
|
||||
function prepareObject() {
|
||||
let object: GridWeaponObject = { weapon: {} }
|
||||
|
||||
if (props.gridWeapon.object.element == 0) object.weapon.element = element
|
||||
|
||||
if ([2, 3, 17, 24].includes(props.gridWeapon.object.series))
|
||||
object.weapon.weapon_key1_id = weaponKey1Select.current?.value
|
||||
|
||||
if ([2, 3, 17].includes(props.gridWeapon.object.series))
|
||||
object.weapon.weapon_key2_id = weaponKey2Select.current?.value
|
||||
|
||||
if (props.gridWeapon.object.series == 17)
|
||||
object.weapon.weapon_key3_id = weaponKey3Select.current?.value
|
||||
|
||||
if (props.gridWeapon.object.ax > 0) {
|
||||
object.weapon.ax_modifier1 = primaryAxModifier
|
||||
object.weapon.ax_modifier2 = secondaryAxModifier
|
||||
object.weapon.ax_strength1 = primaryAxValue
|
||||
object.weapon.ax_strength2 = secondaryAxValue
|
||||
}
|
||||
|
||||
function receiveAxValidity(isValid: boolean) {
|
||||
setFormValid(isValid)
|
||||
}
|
||||
return object
|
||||
}
|
||||
|
||||
function receiveElementValue(element: string) {
|
||||
setElement(parseInt(element))
|
||||
}
|
||||
async function updateWeapon() {
|
||||
const updateObject = prepareObject()
|
||||
return await api.endpoints.grid_weapons
|
||||
.update(props.gridWeapon.id, updateObject, headers)
|
||||
.then((response) => processResult(response))
|
||||
.catch((error) => processError(error))
|
||||
}
|
||||
|
||||
function prepareObject() {
|
||||
let object: GridWeaponObject = { weapon: {} }
|
||||
function processResult(response: AxiosResponse) {
|
||||
const gridWeapon: GridWeapon = response.data.grid_weapon
|
||||
|
||||
if (props.gridWeapon.object.element == 0)
|
||||
object.weapon.element = element
|
||||
if (gridWeapon.mainhand) appState.grid.weapons.mainWeapon = gridWeapon
|
||||
else appState.grid.weapons.allWeapons[gridWeapon.position] = gridWeapon
|
||||
|
||||
if ([2, 3, 17, 24].includes(props.gridWeapon.object.series))
|
||||
object.weapon.weapon_key1_id = weaponKey1Select.current?.value
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
if ([2, 3, 17].includes(props.gridWeapon.object.series))
|
||||
object.weapon.weapon_key2_id = weaponKey2Select.current?.value
|
||||
function processError(error: any) {
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
if (props.gridWeapon.object.series == 17)
|
||||
object.weapon.weapon_key3_id = weaponKey3Select.current?.value
|
||||
|
||||
if (props.gridWeapon.object.ax > 0) {
|
||||
object.weapon.ax_modifier1 = primaryAxModifier
|
||||
object.weapon.ax_modifier2 = secondaryAxModifier
|
||||
object.weapon.ax_strength1 = primaryAxValue
|
||||
object.weapon.ax_strength2 = secondaryAxValue
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
|
||||
async function updateWeapon() {
|
||||
const updateObject = prepareObject()
|
||||
return await api.endpoints.grid_weapons.update(props.gridWeapon.id, updateObject, headers)
|
||||
.then(response => processResult(response))
|
||||
.catch(error => processError(error))
|
||||
}
|
||||
|
||||
function processResult(response: AxiosResponse) {
|
||||
const gridWeapon: GridWeapon = response.data.grid_weapon
|
||||
|
||||
if (gridWeapon.mainhand)
|
||||
appState.grid.weapons.mainWeapon = gridWeapon
|
||||
else
|
||||
appState.grid.weapons.allWeapons[gridWeapon.position] = gridWeapon
|
||||
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
function processError(error: any) {
|
||||
console.error(error)
|
||||
}
|
||||
|
||||
const elementSelect = () => {
|
||||
return (
|
||||
<section>
|
||||
<h3>{t('modals.weapon.subtitles.element')}</h3>
|
||||
<ElementToggle
|
||||
currentElement={props.gridWeapon.element}
|
||||
sendValue={receiveElementValue}
|
||||
/>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
const keySelect = () => {
|
||||
return (
|
||||
<section>
|
||||
<h3>{t('modals.weapon.subtitles.weapon_keys')}</h3>
|
||||
{ ([2, 3, 17, 22].includes(props.gridWeapon.object.series)) ?
|
||||
<WeaponKeyDropdown
|
||||
currentValue={ (props.gridWeapon.weapon_keys) ? props.gridWeapon.weapon_keys[0] : undefined }
|
||||
series={props.gridWeapon.object.series}
|
||||
slot={0}
|
||||
ref={weaponKey1Select} />
|
||||
: ''}
|
||||
|
||||
{ ([2, 3, 17].includes(props.gridWeapon.object.series)) ?
|
||||
<WeaponKeyDropdown
|
||||
currentValue={ (props.gridWeapon.weapon_keys) ? props.gridWeapon.weapon_keys[1] : undefined }
|
||||
series={props.gridWeapon.object.series}
|
||||
slot={1}
|
||||
ref={weaponKey2Select} />
|
||||
: ''}
|
||||
|
||||
{ (props.gridWeapon.object.series == 17) ?
|
||||
<WeaponKeyDropdown
|
||||
currentValue={ (props.gridWeapon.weapon_keys) ? props.gridWeapon.weapon_keys[2] : undefined }
|
||||
series={props.gridWeapon.object.series}
|
||||
slot={2}
|
||||
ref={weaponKey3Select} />
|
||||
: ''}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
const axSelect = () => {
|
||||
return (
|
||||
<section>
|
||||
<h3>{t('modals.weapon.subtitles.ax_skills')}</h3>
|
||||
<AXSelect
|
||||
axType={props.gridWeapon.object.ax}
|
||||
currentSkills={props.gridWeapon.ax}
|
||||
sendValidity={receiveAxValidity}
|
||||
sendValues={receiveAxValues}
|
||||
/>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function openChange(open: boolean) {
|
||||
setFormValid(false)
|
||||
setOpen(open)
|
||||
}
|
||||
|
||||
const elementSelect = () => {
|
||||
return (
|
||||
<Dialog.Root open={open} onOpenChange={openChange}>
|
||||
<Dialog.Trigger asChild>
|
||||
{ props.children }
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Content className="Weapon Dialog" onOpenAutoFocus={ (event) => event.preventDefault() }>
|
||||
<div className="DialogHeader">
|
||||
<div className="DialogTop">
|
||||
<Dialog.Title className="SubTitle">{t('modals.weapon.title')}</Dialog.Title>
|
||||
<Dialog.Title className="DialogTitle">{props.gridWeapon.object.name[locale]}</Dialog.Title>
|
||||
</div>
|
||||
<Dialog.Close className="DialogClose" asChild>
|
||||
<span>
|
||||
<CrossIcon />
|
||||
</span>
|
||||
</Dialog.Close>
|
||||
</div>
|
||||
|
||||
<div className="mods">
|
||||
{ (props.gridWeapon.object.element == 0) ? elementSelect() : '' }
|
||||
{ ([2, 3, 17, 24].includes(props.gridWeapon.object.series)) ? keySelect() : '' }
|
||||
{ (props.gridWeapon.object.ax > 0) ? axSelect() : '' }
|
||||
<Button onClick={updateWeapon} disabled={props.gridWeapon.object.ax > 0 && !formValid}>{t('modals.weapon.buttons.confirm')}</Button>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
<Dialog.Overlay className="Overlay" />
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
<section>
|
||||
<h3>{t("modals.weapon.subtitles.element")}</h3>
|
||||
<ElementToggle
|
||||
currentElement={props.gridWeapon.element}
|
||||
sendValue={receiveElementValue}
|
||||
/>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
const keySelect = () => {
|
||||
return (
|
||||
<section>
|
||||
<h3>{t("modals.weapon.subtitles.weapon_keys")}</h3>
|
||||
{[2, 3, 17, 22].includes(props.gridWeapon.object.series) ? (
|
||||
<WeaponKeyDropdown
|
||||
currentValue={
|
||||
props.gridWeapon.weapon_keys
|
||||
? props.gridWeapon.weapon_keys[0]
|
||||
: undefined
|
||||
}
|
||||
series={props.gridWeapon.object.series}
|
||||
slot={0}
|
||||
ref={weaponKey1Select}
|
||||
/>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
|
||||
{[2, 3, 17].includes(props.gridWeapon.object.series) ? (
|
||||
<WeaponKeyDropdown
|
||||
currentValue={
|
||||
props.gridWeapon.weapon_keys
|
||||
? props.gridWeapon.weapon_keys[1]
|
||||
: undefined
|
||||
}
|
||||
series={props.gridWeapon.object.series}
|
||||
slot={1}
|
||||
ref={weaponKey2Select}
|
||||
/>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
|
||||
{props.gridWeapon.object.series == 17 ? (
|
||||
<WeaponKeyDropdown
|
||||
currentValue={
|
||||
props.gridWeapon.weapon_keys
|
||||
? props.gridWeapon.weapon_keys[2]
|
||||
: undefined
|
||||
}
|
||||
series={props.gridWeapon.object.series}
|
||||
slot={2}
|
||||
ref={weaponKey3Select}
|
||||
/>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
const axSelect = () => {
|
||||
return (
|
||||
<section>
|
||||
<h3>{t("modals.weapon.subtitles.ax_skills")}</h3>
|
||||
<AXSelect
|
||||
axType={props.gridWeapon.object.ax}
|
||||
currentSkills={props.gridWeapon.ax}
|
||||
sendValidity={receiveAxValidity}
|
||||
sendValues={receiveAxValues}
|
||||
/>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
function openChange(open: boolean) {
|
||||
setFormValid(false)
|
||||
setOpen(open)
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog.Root open={open} onOpenChange={openChange}>
|
||||
<Dialog.Trigger asChild>{props.children}</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<Dialog.Content
|
||||
className="Weapon Dialog"
|
||||
onOpenAutoFocus={(event) => event.preventDefault()}
|
||||
>
|
||||
<div className="DialogHeader">
|
||||
<div className="DialogTop">
|
||||
<Dialog.Title className="SubTitle">
|
||||
{t("modals.weapon.title")}
|
||||
</Dialog.Title>
|
||||
<Dialog.Title className="DialogTitle">
|
||||
{props.gridWeapon.object.name[locale]}
|
||||
</Dialog.Title>
|
||||
</div>
|
||||
<Dialog.Close className="DialogClose" asChild>
|
||||
<span>
|
||||
<CrossIcon />
|
||||
</span>
|
||||
</Dialog.Close>
|
||||
</div>
|
||||
|
||||
<div className="mods">
|
||||
{props.gridWeapon.object.element == 0 ? elementSelect() : ""}
|
||||
{[2, 3, 17, 24].includes(props.gridWeapon.object.series)
|
||||
? keySelect()
|
||||
: ""}
|
||||
{props.gridWeapon.object.ax > 0 ? axSelect() : ""}
|
||||
<Button
|
||||
onClick={updateWeapon}
|
||||
disabled={props.gridWeapon.object.ax > 0 && !formValid}
|
||||
>
|
||||
{t("modals.weapon.buttons.confirm")}
|
||||
</Button>
|
||||
</div>
|
||||
</Dialog.Content>
|
||||
<Dialog.Overlay className="Overlay" />
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export default WeaponModal
|
||||
export default WeaponModal
|
||||
|
|
|
|||
Loading…
Reference in a new issue