diff --git a/components/CharacterConflictModal/index.scss b/components/CharacterConflictModal/index.scss new file mode 100644 index 00000000..48176c81 --- /dev/null +++ b/components/CharacterConflictModal/index.scss @@ -0,0 +1,63 @@ +.Conflict.Dialog { + & > p { + line-height: 1.2; + max-width: 400px; + } + img { + border-radius: 1rem; + } + + .arrow { + color: $grey-50; + font-size: 4rem; + text-align: center; + } + + .character { + display: flex; + flex-direction: column; + gap: $unit; + text-align: center; + width: 12rem; + font-weight: $medium; + } + + .diagram { + display: grid; + grid-template-columns: 1fr 1fr 1fr; + align-items: center; + + ul { + display: flex; + flex-direction: column; + gap: $unit * 2; + } + } + + footer { + display: flex; + flex-direction: row; + gap: $unit; + + .Button { + font-size: $font-regular; + padding: ($unit * 1.5) ($unit * 2); + width: 100%; + + &.btn-disabled { + background: $grey-90; + color: $grey-70; + cursor: not-allowed; + } + + &:not(.btn-disabled) { + background: $grey-90; + color: $grey-40; + + &:hover { + background: $grey-80; + } + } + } + } +} diff --git a/components/CharacterConflictModal/index.tsx b/components/CharacterConflictModal/index.tsx new file mode 100644 index 00000000..5d393d28 --- /dev/null +++ b/components/CharacterConflictModal/index.tsx @@ -0,0 +1,114 @@ +import React, { useEffect, 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 api from "~utils/api" +import { appState } from "~utils/appState" +import { accountState } from "~utils/accountState" + +import Button from "~components/Button" + +import "./index.scss" + +interface Props { + open: boolean + incomingCharacter?: Character + conflictingCharacters?: GridCharacter[] + desiredPosition: number + resolveConflict: () => void + resetConflict: () => void +} + +const CharacterConflictModal = (props: Props) => { + const { t } = useTranslation("common") + + // States + const [open, setOpen] = useState(false) + + useEffect(() => { + setOpen(props.open) + }, [setOpen, props.open]) + + function imageUrl(character?: Character, uncap: number = 0) { + // Change the image based on the uncap level + let suffix = "01" + if (uncap == 6) suffix = "04" + else if (uncap == 5) suffix = "03" + else if (uncap > 2) suffix = "02" + + console.log(appState.grid.weapons.mainWeapon) + // Special casing for Lyria (and Young Cat eventually) + if (character?.granblue_id === "3030182000") { + let element = 1 + if ( + appState.grid.weapons.mainWeapon && + appState.grid.weapons.mainWeapon.element + ) { + element = appState.grid.weapons.mainWeapon.element + } else if (appState.party.element != 0) { + element = appState.party.element + } + + suffix = `${suffix}_0${element}` + } + + return `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/chara-square/${character?.granblue_id}_${suffix}.jpg` + } + + function openChange(open: boolean) { + setOpen(open) + } + + function close() { + setOpen(false) + props.resetConflict() + } + + return ( + + + event.preventDefault()} + > + + Only one version of a character can be included in each party. Do + you want to change your party members? + + + + {props.conflictingCharacters?.map((character) => ( + + + {character.object.name.en} + + ))} + + → + + + {props.incomingCharacter?.name.en} + + + + + + + + ) +} + +export default CharacterConflictModal diff --git a/components/CharacterGrid/index.tsx b/components/CharacterGrid/index.tsx index ebfb9115..18329974 100644 --- a/components/CharacterGrid/index.tsx +++ b/components/CharacterGrid/index.tsx @@ -13,6 +13,8 @@ import api from "~utils/api" import { appState } from "~utils/appState" import "./index.scss" +import CharacterConflictModal from "~components/CharacterConflictModal" +import { resolve } from "path" // Props interface Props { @@ -38,6 +40,12 @@ const CharacterGrid = (props: Props) => { // Set up state for view management const { party, grid } = useSnapshot(appState) const [slug, setSlug] = useState() + const [modalOpen, setModalOpen] = useState(false) + + // Set up state for conflict management + const [incoming, setIncoming] = useState() + const [conflicts, setConflicts] = useState([]) + const [position, setPosition] = useState(0) // Create a temporary state to store previous character uncap values const [previousUncapValues, setPreviousUncapValues] = useState<{ @@ -85,11 +93,22 @@ const CharacterGrid = (props: Props) => { } else { if (party.editable) saveCharacter(party.id, character, position) - .then((response) => storeGridCharacter(response.data.grid_character)) + .then((response) => handleCharacterResponse(response.data)) .catch((error) => console.error(error)) } } + async function handleCharacterResponse(data: any) { + if (data.hasOwnProperty("conflicts")) { + setIncoming(data.incoming) + setConflicts(data.conflicts) + setPosition(data.position) + setModalOpen(true) + } else { + storeGridCharacter(data.grid_character) + } + } + async function saveCharacter( partyId: string, character: Character, @@ -112,6 +131,39 @@ const CharacterGrid = (props: Props) => { appState.grid.characters[gridCharacter.position] = gridCharacter } + async function resolveConflict() { + if (incoming && conflicts.length > 0) { + await api + .resolveCharacterConflict({ + incoming: incoming.id, + conflicting: conflicts.map((c) => c.id), + position: position, + params: headers, + }) + .then((response) => { + // Store new character in state + storeGridCharacter(response.data.grid_character) + + // Remove conflicting characters from state + conflicts.forEach( + (c) => (appState.grid.characters[c.position] = undefined) + ) + + // Reset conflict + resetConflict() + + // Close modal + setModalOpen(false) + }) + } + } + + function resetConflict() { + setPosition(-1) + setConflicts([]) + setIncoming(undefined) + } + // Methods: Helpers function characterUncapLevel(character: Character) { let uncapLevel @@ -197,6 +249,14 @@ const CharacterGrid = (props: Props) => { + {Array.from(Array(numCharacters)).map((x, i) => { return (
+ Only one version of a character can be included in each party. Do + you want to change your party members? +