Add perpetuity control to CharacterUnit
This involved moving the code to update a GridCharacter to the CharacterUnit from CharacterModal
This commit is contained in:
parent
942142f19a
commit
4f7d18904f
4 changed files with 145 additions and 69 deletions
|
|
@ -41,28 +41,17 @@ import CrossIcon from '~public/icons/Cross.svg'
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { CharacterOverMastery, ExtendedMastery } from '~types'
|
import {
|
||||||
|
CharacterOverMastery,
|
||||||
interface GridCharacterObject {
|
ExtendedMastery,
|
||||||
character: {
|
GridCharacterObject,
|
||||||
ring1: ExtendedMastery
|
} from '~types'
|
||||||
ring2: ExtendedMastery
|
|
||||||
ring3: ExtendedMastery
|
|
||||||
ring4: ExtendedMastery
|
|
||||||
earring: ExtendedMastery
|
|
||||||
awakening: {
|
|
||||||
type?: number
|
|
||||||
level?: number
|
|
||||||
}
|
|
||||||
transcendence_step: number
|
|
||||||
perpetuity: boolean
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
gridCharacter: GridCharacter
|
gridCharacter: GridCharacter
|
||||||
open: boolean
|
open: boolean
|
||||||
onOpenChange: (open: boolean) => void
|
onOpenChange: (open: boolean) => void
|
||||||
|
updateCharacter: (object: GridCharacterObject) => Promise<any>
|
||||||
}
|
}
|
||||||
|
|
||||||
const CharacterModal = ({
|
const CharacterModal = ({
|
||||||
|
|
@ -70,6 +59,7 @@ const CharacterModal = ({
|
||||||
children,
|
children,
|
||||||
open: modalOpen,
|
open: modalOpen,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
|
updateCharacter,
|
||||||
}: PropsWithChildren<Props>) => {
|
}: PropsWithChildren<Props>) => {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const locale =
|
const locale =
|
||||||
|
|
@ -133,42 +123,6 @@ const CharacterModal = ({
|
||||||
setPerpetuity(gridCharacter.perpetuity)
|
setPerpetuity(gridCharacter.perpetuity)
|
||||||
}, [gridCharacter])
|
}, [gridCharacter])
|
||||||
|
|
||||||
// Methods: UI state management
|
|
||||||
function handleOpenChange(open: boolean) {
|
|
||||||
setOpen(open)
|
|
||||||
onOpenChange(open)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods: Receive data from components
|
|
||||||
function receiveRingValues(overMastery: CharacterOverMastery) {
|
|
||||||
setRings(overMastery)
|
|
||||||
}
|
|
||||||
|
|
||||||
function receiveEarringValues(
|
|
||||||
earringModifier: number,
|
|
||||||
earringStrength: number
|
|
||||||
) {
|
|
||||||
setEarring({
|
|
||||||
modifier: earringModifier,
|
|
||||||
strength: earringStrength,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCheckedChange(checked: boolean) {
|
|
||||||
setPerpetuity(checked)
|
|
||||||
}
|
|
||||||
|
|
||||||
function receiveAwakeningValues(type: number, level: number) {
|
|
||||||
setAwakeningType(type)
|
|
||||||
setAwakeningLevel(level)
|
|
||||||
}
|
|
||||||
|
|
||||||
function receiveValidity(isValid: boolean) {
|
|
||||||
setFormValid(isValid)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Methods: Data syncing
|
|
||||||
|
|
||||||
// Prepare the GridWeaponObject to send to the server
|
// Prepare the GridWeaponObject to send to the server
|
||||||
function prepareObject() {
|
function prepareObject() {
|
||||||
let object: GridCharacterObject = {
|
let object: GridCharacterObject = {
|
||||||
|
|
@ -205,27 +159,45 @@ const CharacterModal = ({
|
||||||
return object
|
return object
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the GridWeaponObject to the server
|
// Methods: UI state management
|
||||||
async function updateCharacter() {
|
function handleOpenChange(open: boolean) {
|
||||||
const updateObject = prepareObject()
|
setOpen(open)
|
||||||
|
onOpenChange(open)
|
||||||
return await api.endpoints.grid_characters
|
|
||||||
.update(gridCharacter.id, updateObject)
|
|
||||||
.then((response) => processResult(response))
|
|
||||||
.catch((error) => processError(error))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save the server's response to state
|
// Methods: Receive data from components
|
||||||
function processResult(response: AxiosResponse) {
|
function receiveRingValues(overMastery: CharacterOverMastery) {
|
||||||
const gridCharacter: GridCharacter = response.data
|
setRings(overMastery)
|
||||||
appState.grid.characters[gridCharacter.position] = gridCharacter
|
}
|
||||||
|
|
||||||
|
function receiveEarringValues(
|
||||||
|
earringModifier: number,
|
||||||
|
earringStrength: number
|
||||||
|
) {
|
||||||
|
setEarring({
|
||||||
|
modifier: earringModifier,
|
||||||
|
strength: earringStrength,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCheckedChange(checked: boolean) {
|
||||||
|
setPerpetuity(checked)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleUpdateCharacter() {
|
||||||
|
await updateCharacter(prepareObject())
|
||||||
|
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
if (onOpenChange) onOpenChange(false)
|
if (onOpenChange) onOpenChange(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
function processError(error: any) {
|
function receiveAwakeningValues(type: number, level: number) {
|
||||||
console.error(error)
|
setAwakeningType(type)
|
||||||
|
setAwakeningLevel(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
function receiveValidity(isValid: boolean) {
|
||||||
|
setFormValid(isValid)
|
||||||
}
|
}
|
||||||
|
|
||||||
const ringSelect = () => {
|
const ringSelect = () => {
|
||||||
|
|
@ -322,7 +294,7 @@ const CharacterModal = ({
|
||||||
<div className="DialogFooter" ref={footerRef}>
|
<div className="DialogFooter" ref={footerRef}>
|
||||||
<Button
|
<Button
|
||||||
contained={true}
|
contained={true}
|
||||||
onClick={updateCharacter}
|
onClick={handleUpdateCharacter}
|
||||||
disabled={!formValid}
|
disabled={!formValid}
|
||||||
text={t('modals.characters.buttons.confirm')}
|
text={t('modals.characters.buttons.confirm')}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -97,4 +97,34 @@
|
||||||
font-size: $font-tiny;
|
font-size: $font-tiny;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:hover .Perpetuity.Empty {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Perpetuity {
|
||||||
|
position: absolute;
|
||||||
|
background-image: url('/icons/perpetuity/filled.svg');
|
||||||
|
background-size: $unit-4x $unit-4x;
|
||||||
|
z-index: 20;
|
||||||
|
top: $unit * -1;
|
||||||
|
right: $unit-3x;
|
||||||
|
width: $unit-4x;
|
||||||
|
height: $unit-4x;
|
||||||
|
transition: $duration-zoom opacity ease-in-out;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-image: url('/icons/perpetuity/empty.svg');
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.Empty {
|
||||||
|
background-image: url('/icons/perpetuity/empty.svg');
|
||||||
|
opacity: 0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-image: url('/icons/perpetuity/filled.svg');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import React, { MouseEvent, useEffect, useState } from 'react'
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from 'next/router'
|
||||||
import { useSnapshot } from 'valtio'
|
import { useSnapshot } from 'valtio'
|
||||||
import { Trans, useTranslation } from 'next-i18next'
|
import { Trans, useTranslation } from 'next-i18next'
|
||||||
|
import { AxiosResponse } from 'axios'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
|
||||||
import Alert from '~components/Alert'
|
import Alert from '~components/Alert'
|
||||||
|
|
@ -17,12 +18,18 @@ import ContextMenuItem from '~components/ContextMenuItem'
|
||||||
import SearchModal from '~components/SearchModal'
|
import SearchModal from '~components/SearchModal'
|
||||||
import UncapIndicator from '~components/UncapIndicator'
|
import UncapIndicator from '~components/UncapIndicator'
|
||||||
|
|
||||||
|
import api from '~utils/api'
|
||||||
import { appState } from '~utils/appState'
|
import { appState } from '~utils/appState'
|
||||||
|
|
||||||
import PlusIcon from '~public/icons/Add.svg'
|
import PlusIcon from '~public/icons/Add.svg'
|
||||||
import SettingsIcon from '~public/icons/Settings.svg'
|
import SettingsIcon from '~public/icons/Settings.svg'
|
||||||
|
|
||||||
import type { SearchableObject } from '~types'
|
// Types
|
||||||
|
import type {
|
||||||
|
GridCharacterObject,
|
||||||
|
PerpetuityObject,
|
||||||
|
SearchableObject,
|
||||||
|
} from '~types'
|
||||||
|
|
||||||
import './index.scss'
|
import './index.scss'
|
||||||
|
|
||||||
|
|
@ -99,6 +106,16 @@ const CharacterUnit = ({
|
||||||
setContextMenuOpen(!contextMenuOpen)
|
setContextMenuOpen(!contextMenuOpen)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handlePerpetuityClick() {
|
||||||
|
if (gridCharacter) {
|
||||||
|
let object: PerpetuityObject = {
|
||||||
|
character: { perpetuity: !gridCharacter.perpetuity },
|
||||||
|
}
|
||||||
|
|
||||||
|
updateCharacter(object)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Methods: Handle open change
|
// Methods: Handle open change
|
||||||
function handleCharacterModalOpenChange(open: boolean) {
|
function handleCharacterModalOpenChange(open: boolean) {
|
||||||
setDetailsModalOpen(open)
|
setDetailsModalOpen(open)
|
||||||
|
|
@ -113,6 +130,28 @@ const CharacterUnit = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Methods: Mutate data
|
// Methods: Mutate data
|
||||||
|
|
||||||
|
// Send the GridWeaponObject to the server
|
||||||
|
async function updateCharacter(
|
||||||
|
object: GridCharacterObject | PerpetuityObject
|
||||||
|
) {
|
||||||
|
if (gridCharacter)
|
||||||
|
return await api.endpoints.grid_characters
|
||||||
|
.update(gridCharacter.id, object)
|
||||||
|
.then((response) => processResult(response))
|
||||||
|
.catch((error) => processError(error))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the server's response to state
|
||||||
|
function processResult(response: AxiosResponse) {
|
||||||
|
const gridCharacter: GridCharacter = response.data
|
||||||
|
appState.grid.characters[gridCharacter.position] = gridCharacter
|
||||||
|
}
|
||||||
|
|
||||||
|
function processError(error: any) {
|
||||||
|
console.error(error)
|
||||||
|
}
|
||||||
|
|
||||||
function passUncapData(uncap: number) {
|
function passUncapData(uncap: number) {
|
||||||
if (gridCharacter) updateUncap(gridCharacter.id, position, uncap)
|
if (gridCharacter) updateUncap(gridCharacter.id, position, uncap)
|
||||||
}
|
}
|
||||||
|
|
@ -160,6 +199,7 @@ const CharacterUnit = ({
|
||||||
gridCharacter={gridCharacter}
|
gridCharacter={gridCharacter}
|
||||||
open={detailsModalOpen}
|
open={detailsModalOpen}
|
||||||
onOpenChange={handleCharacterModalOpenChange}
|
onOpenChange={handleCharacterModalOpenChange}
|
||||||
|
updateCharacter={updateCharacter}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -228,6 +268,17 @@ const CharacterUnit = ({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Methods: Core element rendering
|
// Methods: Core element rendering
|
||||||
|
const perpetuity = () => {
|
||||||
|
if (gridCharacter) {
|
||||||
|
const classes = classNames({
|
||||||
|
Perpetuity: true,
|
||||||
|
Empty: !gridCharacter.perpetuity,
|
||||||
|
})
|
||||||
|
|
||||||
|
return <i className={classes} onClick={handlePerpetuityClick} />
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const image = (
|
const image = (
|
||||||
<div className="CharacterImage" onClick={openSearchModal}>
|
<div className="CharacterImage" onClick={openSearchModal}>
|
||||||
<img
|
<img
|
||||||
|
|
@ -249,6 +300,7 @@ const CharacterUnit = ({
|
||||||
<>
|
<>
|
||||||
<div className={classes}>
|
<div className={classes}>
|
||||||
{contextMenu()}
|
{contextMenu()}
|
||||||
|
{perpetuity()}
|
||||||
{image}
|
{image}
|
||||||
{gridCharacter && character ? (
|
{gridCharacter && character ? (
|
||||||
<UncapIndicator
|
<UncapIndicator
|
||||||
|
|
|
||||||
22
types/index.d.ts
vendored
22
types/index.d.ts
vendored
|
|
@ -48,3 +48,25 @@ export type CharacterOverMastery = {
|
||||||
3: ExtendedMastery
|
3: ExtendedMastery
|
||||||
4: ExtendedMastery
|
4: ExtendedMastery
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface GridCharacterObject {
|
||||||
|
character: {
|
||||||
|
ring1: ExtendedMastery
|
||||||
|
ring2: ExtendedMastery
|
||||||
|
ring3: ExtendedMastery
|
||||||
|
ring4: ExtendedMastery
|
||||||
|
earring: ExtendedMastery
|
||||||
|
awakening: {
|
||||||
|
type?: number
|
||||||
|
level?: number
|
||||||
|
}
|
||||||
|
transcendence_step: number
|
||||||
|
perpetuity: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PerpetuityObject {
|
||||||
|
character: {
|
||||||
|
perpetuity: boolean
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue