diff --git a/components/CharacterGrid/index.tsx b/components/CharacterGrid/index.tsx index df55fe71..8200144a 100644 --- a/components/CharacterGrid/index.tsx +++ b/components/CharacterGrid/index.tsx @@ -1,30 +1,22 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useCallback, useEffect, useMemo, useState } from 'react' +import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react' import { useCookies } from 'react-cookie' import { useModal as useModal } from '~utils/useModal' import { AxiosResponse } from 'axios' import debounce from 'lodash.debounce' +import AppContext from '~context/AppContext' + import CharacterUnit from '~components/CharacterUnit' import SearchModal from '~components/SearchModal' import api from '~utils/api' import './index.scss' -// GridType -export enum GridType { - Class, - Character, - Weapon, - Summon -} - // Props interface Props { - partyId?: string - characters: GridArray - editable: boolean + slug?: string createParty: () => Promise> pushHistory?: (path: string) => void } @@ -44,6 +36,12 @@ const CharacterGrid = (props: Props) => { // Set up state for party const [partyId, setPartyId] = useState('') + // Set up state for view management + const [found, setFound] = useState(false) + const [loading, setLoading] = useState(true) + const [editable, setEditable] = useState(false) + const { setEditable: setEditableContext } = useContext(AppContext) + // Set up states for Grid data const [characters, setCharacters] = useState>({}) @@ -56,17 +54,16 @@ const CharacterGrid = (props: Props) => { // Create a state dictionary to store pure objects for Search const [searchGrid, setSearchGrid] = useState>({}) - - // Set states from props + + // Fetch data from the server useEffect(() => { - setPartyId(props.partyId || '') - setCharacters(props.characters || {}) - }, [props]) + if (props.slug) fetchGrid(props.slug) + }, []) // Initialize an array of current uncap values for each characters useEffect(() => { let initialPreviousUncapValues: {[key: number]: number} = {} - Object.values(props.characters).map(o => initialPreviousUncapValues[o.position] = o.uncap_level) + Object.values(characters).map(o => initialPreviousUncapValues[o.position] = o.uncap_level) setPreviousUncapValues(initialPreviousUncapValues) }, [props]) @@ -76,6 +73,58 @@ const CharacterGrid = (props: Props) => { setSearchGrid(newSearchGrid) }, [characters]) + // Methods: Fetching an object from the server + async function fetchGrid(shortcode: string) { + return api.endpoints.parties.getOneWithObject({ id: shortcode, object: 'characters' }) + .then(response => processResult(response)) + .catch(error => processError(error)) + } + + function processResult(response: AxiosResponse) { + // Store the response + const party = response.data.party + + // Get the party user and logged in user, if possible, to compare + const partyUser = (party.user_id) ? party.user_id : undefined + const loggedInUser = (cookies.user) ? cookies.user.user_id : '' + + if (partyUser != undefined && loggedInUser != undefined && partyUser === loggedInUser) { + setEditable(true) + setEditableContext(true) + } + + // Store the important party and state-keeping values + setPartyId(party.id) + setFound(true) + setLoading(false) + + // Populate the weapons in state + populateCharacters(party.characters) + } + + function processError(error: any) { + if (error.response != null) { + if (error.response.status == 404) { + setFound(false) + setLoading(false) + } + } else { + console.error(error) + } + } + + function populateCharacters(list: [GridCharacter]) { + let characters: GridArray = {} + + list.forEach((object: GridCharacter) => { + if (object.position != null) + characters[object.position] = object + }) + + setCharacters(characters) + } + + // Methods: Adding an object from search function openSearchModal(position: number) { setItemPositionForSearch(position) @@ -200,8 +249,8 @@ const CharacterGrid = (props: Props) => { return (
  • { openSearchModal(i) }} updateUncap={initiateUncapUpdate} diff --git a/components/Party/index.tsx b/components/Party/index.tsx index f8c16ecc..6f6dfff8 100644 --- a/components/Party/index.tsx +++ b/components/Party/index.tsx @@ -23,15 +23,7 @@ enum GridType { // Props interface Props { - partyId?: string - mainWeapon?: GridWeapon - mainSummon?: GridSummon - friendSummon?: GridSummon - characters?: GridArray - weapons?: GridArray - summons?: GridArray - extra: boolean - editable: boolean + slug?: string pushHistory?: (path: string) => void } @@ -45,17 +37,13 @@ const Party = (props: Props) => { } : {} // Set up states - const [extra, setExtra] = useState(false) const [currentTab, setCurrentTab] = useState(GridType.Weapon) const [element, setElement] = useState(TeamElement.Any) - - // Set states from props - useEffect(() => { - setExtra(props.extra || false) - }, [props]) + const [editable, setEditable] = useState(false) + const [hasExtra, setHasExtra] = useState(false) // Methods: Creating a new party - async function createParty() { + async function createParty(extra: boolean = false) { let body = { party: { ...(cookies.user) && { user_id: cookies.user.user_id }, @@ -69,7 +57,7 @@ const Party = (props: Props) => { // Methods: Updating the party's extra flag // Note: This doesn't save to the server yet. function checkboxChanged(event: React.ChangeEvent) { - setExtra(event.target.checked) + setHasExtra(event.target.checked) } // Methods: Navigating with segmented control @@ -95,8 +83,6 @@ const Party = (props: Props) => { // Render: JSX components const navigation = ( { const weaponGrid = ( @@ -117,11 +99,7 @@ const Party = (props: Props) => { const summonGrid = ( @@ -129,9 +107,7 @@ const Party = (props: Props) => { const characterGrid = ( @@ -150,7 +126,7 @@ const Party = (props: Props) => { return (
    - + { navigation } { currentGrid() } diff --git a/components/PartySegmentedControl/index.tsx b/components/PartySegmentedControl/index.tsx index c9c006c4..3d30e8f4 100644 --- a/components/PartySegmentedControl/index.tsx +++ b/components/PartySegmentedControl/index.tsx @@ -16,15 +16,13 @@ export enum GridType { } interface Props { - editable: boolean - extra: boolean selectedTab: GridType onClick: (event: React.ChangeEvent) => void onCheckboxChange: (event: React.ChangeEvent) => void } const PartySegmentedControl = (props: Props) => { - const { element } = useContext(PartyContext) + const { editable, element, hasExtra } = useContext(PartyContext) function getElement() { switch(element) { @@ -42,8 +40,8 @@ const PartySegmentedControl = (props: Props) => { Extra
    @@ -82,7 +80,7 @@ const PartySegmentedControl = (props: Props) => { { (() => { - if (props.editable && props.selectedTab == GridType.Weapon) { + if (editable && props.selectedTab == GridType.Weapon) { return extraToggle } })() diff --git a/components/SummonGrid/index.tsx b/components/SummonGrid/index.tsx index baf16dd9..ab86ffa1 100644 --- a/components/SummonGrid/index.tsx +++ b/components/SummonGrid/index.tsx @@ -1,11 +1,13 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useCallback, useEffect, useMemo, useState } from 'react' +import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react' import { useCookies } from 'react-cookie' import { useModal as useModal } from '~utils/useModal' import { AxiosResponse } from 'axios' import debounce from 'lodash.debounce' +import AppContext from '~context/AppContext' + import SearchModal from '~components/SearchModal' import SummonUnit from '~components/SummonUnit' import ExtraSummons from '~components/ExtraSummons' @@ -13,21 +15,9 @@ import ExtraSummons from '~components/ExtraSummons' import api from '~utils/api' import './index.scss' -// GridType -export enum GridType { - Class, - Character, - Weapon, - Summon -} - // Props interface Props { - partyId?: string - mainSummon: GridSummon | undefined - friendSummon: GridSummon | undefined - summons: GridArray - editable: boolean + slug?: string createParty: () => Promise> pushHistory?: (path: string) => void } @@ -47,6 +37,12 @@ const SummonGrid = (props: Props) => { // Set up state for party const [partyId, setPartyId] = useState('') + // Set up state for view management + const [found, setFound] = useState(false) + const [loading, setLoading] = useState(true) + const [editable, setEditable] = useState(false) + const { setEditable: setEditableContext } = useContext(AppContext) + // Set up states for Grid data const [summons, setSummons] = useState>({}) const [mainSummon, setMainSummon] = useState() @@ -62,22 +58,20 @@ const SummonGrid = (props: Props) => { // Create a state dictionary to store pure objects for Search const [searchGrid, setSearchGrid] = useState>({}) + // Fetch data from the server + useEffect(() => { + if (props.slug) fetchGrid(props.slug) + }, []) + // Initialize an array of current uncap values for each summon useEffect(() => { let initialPreviousUncapValues: {[key: number]: number} = {} - if (props.mainSummon) initialPreviousUncapValues[-1] = props.mainSummon.uncap_level - if (props.friendSummon) initialPreviousUncapValues[6] = props.friendSummon.uncap_level - Object.values(props.summons).map(o => initialPreviousUncapValues[o.position] = o.uncap_level) + if (mainSummon) initialPreviousUncapValues[-1] = mainSummon.uncap_level + if (friendSummon) initialPreviousUncapValues[6] = friendSummon.uncap_level + Object.values(summons).map(o => initialPreviousUncapValues[o.position] = o.uncap_level) setPreviousUncapValues(initialPreviousUncapValues) }, [props]) - // Set states from props - useEffect(() => { - setSummons(props.summons || {}) - setMainSummon(props.mainSummon) - setFriendSummon(props.friendSummon) - }, [props]) - // Update search grid whenever any summon is updated useEffect(() => { let newSearchGrid = Object.values(summons).map((o) => o.summon) @@ -91,6 +85,61 @@ const SummonGrid = (props: Props) => { setSearchGrid(newSearchGrid) }, [summons, mainSummon, friendSummon]) + // Methods: Fetching an object from the server + async function fetchGrid(shortcode: string) { + return api.endpoints.parties.getOneWithObject({ id: shortcode, object: 'summons' }) + .then(response => processResult(response)) + .catch(error => processError(error)) + } + + function processResult(response: AxiosResponse) { + // Store the response + const party = response.data.party + + // Get the party user and logged in user, if possible, to compare + const partyUser = (party.user_id) ? party.user_id : undefined + const loggedInUser = (cookies.user) ? cookies.user.user_id : '' + + if (partyUser != undefined && loggedInUser != undefined && partyUser === loggedInUser) { + setEditable(true) + setEditableContext(true) + } + + // Store the important party and state-keeping values + setPartyId(party.id) + setFound(true) + setLoading(false) + + // Populate the weapons in state + populateSummons(party.summons) + } + + function processError(error: any) { + if (error.response != null) { + if (error.response.status == 404) { + setFound(false) + setLoading(false) + } + } else { + console.error(error) + } + } + + function populateSummons(list: [GridSummon]) { + let summons: GridArray = {} + + list.forEach((object: GridSummon) => { + if (object.main) + setMainSummon(object) + else if (object.friend) + setFriendSummon(object) + else if (!object.main && !object.friend && object.position != null) + summons[object.position] = object + }) + + setSummons(summons) + } + // Methods: Adding an object from search function openSearchModal(position: number) { setItemPositionForSearch(position) @@ -209,8 +258,8 @@ const SummonGrid = (props: Props) => {
    Main Summon
    {
    Friend Summon
    { {Array.from(Array(numSummons)).map((x, i) => { return (
  • { openSearchModal(i) }} @@ -255,8 +304,8 @@ const SummonGrid = (props: Props) => { ) const subAuraSummonElement = ( - extra: boolean - editable: boolean - createParty: () => Promise> + slug?: string + createParty: (extra: boolean) => Promise> pushHistory?: (path: string) => void } @@ -49,7 +38,14 @@ const WeaponGrid = (props: Props) => { // Set up state for party const [partyId, setPartyId] = useState('') + // Set up state for view management + const [found, setFound] = useState(false) + const [loading, setLoading] = useState(true) + // Set up the party context + const { setEditable: setAppEditable } = useContext(AppContext) + const { editable, setEditable } = useContext(PartyContext) + const { hasExtra, setHasExtra } = useContext(PartyContext) const { setElement } = useContext(PartyContext) // Set up states for Grid data @@ -66,21 +62,18 @@ const WeaponGrid = (props: Props) => { // Create a state dictionary to store pure objects for Search const [searchGrid, setSearchGrid] = useState>({}) - // Set states from props + // Fetch data from the server useEffect(() => { - setPartyId(props.partyId || '') - setWeapons(props.weapons || {}) - setMainWeapon(props.mainhand) - if (props.mainhand) setElement(props.mainhand.weapon.element) - }, [props]) + if (props.slug) fetchGrid(props.slug) + }, [props.slug]) // Initialize an array of current uncap values for each weapon useEffect(() => { let initialPreviousUncapValues: {[key: number]: number} = {} - if (props.mainhand) initialPreviousUncapValues[-1] = props.mainhand.uncap_level - Object.values(props.weapons).map(o => initialPreviousUncapValues[o.position] = o.uncap_level) + if (mainWeapon) initialPreviousUncapValues[-1] = mainWeapon.uncap_level + Object.values(weapons).map(o => initialPreviousUncapValues[o.position] = o.uncap_level) setPreviousUncapValues(initialPreviousUncapValues) - }, [props]) + }, [mainWeapon, weapons]) // Update search grid whenever weapons or the mainhand are updated useEffect(() => { @@ -92,6 +85,62 @@ const WeaponGrid = (props: Props) => { setSearchGrid(newSearchGrid) }, [weapons, mainWeapon]) + // Methods: Fetching an object from the server + async function fetchGrid(shortcode: string) { + return api.endpoints.parties.getOneWithObject({ id: shortcode, object: 'weapons' }) + .then(response => processResult(response)) + .catch(error => processError(error)) + } + + function processResult(response: AxiosResponse) { + // Store the response + const party = response.data.party + + // Get the party user and logged in user, if possible, to compare + const partyUser = (party.user_id) ? party.user_id : undefined + const loggedInUser = (cookies.user) ? cookies.user.user_id : '' + + if (partyUser != undefined && loggedInUser != undefined && partyUser === loggedInUser) { + setEditable(true) + setAppEditable(true) + } + + // Store the important party and state-keeping values + setPartyId(party.id) + setHasExtra(party.is_extra) + setFound(true) + setLoading(false) + + // Populate the weapons in state + populateWeapons(party.weapons) + } + + function processError(error: any) { + if (error.response != null) { + if (error.response.status == 404) { + setFound(false) + setLoading(false) + } + } else { + console.error(error) + } + } + + function populateWeapons(list: [GridWeapon]) { + let weapons: GridArray = {} + + list.forEach((object: GridWeapon) => { + if (object.mainhand) { + setMainWeapon(object) + setElement(object.weapon.element) + } else if (!object.mainhand && object.position != null) { + weapons[object.position] = object + } + }) + + setWeapons(weapons) + } + // Methods: Adding an object from search function openSearchModal(position: number) { setItemPositionForSearch(position) @@ -103,7 +152,7 @@ const WeaponGrid = (props: Props) => { setElement(weapon.element) if (!partyId) { - props.createParty() + props.createParty(hasExtra) .then(response => { const party = response.data.party setPartyId(party.id) @@ -208,7 +257,7 @@ const WeaponGrid = (props: Props) => { const mainhandElement = ( {
  • { openSearchModal(i) }} @@ -237,7 +286,7 @@ const WeaponGrid = (props: Props) => { const extraGridElement = ( {
      { weaponGridElement }
    - { (() => { return (props.extra) ? extraGridElement : '' })() } + { (() => { return (hasExtra) ? extraGridElement : '' })() } {open ? ( {} + setElement: (element: TeamElement) => {}, + editable: false, + setEditable: (editable: boolean) => {}, + hasExtra: false, + setHasExtra: (hasExtra: boolean) => {} }) export default PartyContext \ No newline at end of file diff --git a/pages/p/[party].tsx b/pages/p/[party].tsx index 1ee727cf..a317520c 100644 --- a/pages/p/[party].tsx +++ b/pages/p/[party].tsx @@ -1,159 +1,34 @@ import React, { useContext, useEffect, useState } from 'react' -import { withCookies, useCookies } from 'react-cookie' import { useRouter } from 'next/router' -import AppContext from '~context/AppContext' -import api from '~utils/api' - import Party from '~components/Party' -import Button from '~components/Button' - -interface Props { - hash: string -} const PartyRoute: React.FC = () => { const router = useRouter() const { party: slug } = router.query - const { setEditable: setEditableContext } = useContext(AppContext) + return ( +
    + +
    + ) - const [found, setFound] = useState(false) - const [loading, setLoading] = useState(true) - const [editable, setEditable] = useState(false) + // function renderNotFound() { + // return ( + //
    + //

    There's no grid here.

    + // + //
    + // ) + // } - const [characters, setCharacters] = useState>({}) - const [weapons, setWeapons] = useState>({}) - const [summons, setSummons] = useState>({}) - - const [mainWeapon, setMainWeapon] = useState() - const [mainSummon, setMainSummon] = useState() - const [friendSummon, setFriendSummon] = useState() - - const [partyId, setPartyId] = useState('') - const [extra, setExtra] = useState(false) - const [cookies, _] = useCookies(['user']) - - useEffect(() => { - async function fetchGrid(shortcode: string) { - return api.endpoints.parties.getOne({ id: shortcode }) - .then(response => { - const party = response.data.party - - const partyUser = (party.user_id) ? party.user_id : undefined - const loggedInUser = (cookies.user) ? cookies.user.user_id : '' - - if (partyUser != undefined && loggedInUser != undefined && partyUser === loggedInUser) { - setEditable(true) - setEditableContext(true) - } - - const characters = populateCharacters(party.characters) - const weapons = populateWeapons(party.weapons) - const summons = populateSummons(party.summons) - - setExtra(response.data.party.is_extra) - setFound(true) - setLoading(false) - setCharacters(characters) - setWeapons(weapons) - setSummons(summons) - setPartyId(party.id) - }) - .catch(error => { - if (error.response != null) { - if (error.response.status == 404) { - setFound(false) - setLoading(false) - } - } else { - console.error(error) - } - }) - } - - function populateCharacters(list: [GridCharacter]) { - let characters: GridArray = {} - - list.forEach((object: GridCharacter) => { - if (object.position != null) - characters[object.position] = object - }) - - return characters - } - - function populateWeapons(list: [GridWeapon]) { - let weapons: GridArray = {} - - list.forEach((object: GridWeapon) => { - if (object.mainhand) - setMainWeapon(object) - else if (!object.mainhand && object.position != null) - weapons[object.position] = object - }) - - return weapons - } - - function populateSummons(list: [GridSummon]) { - let summons: GridArray = {} - - list.forEach((object: GridSummon) => { - if (object.main) - setMainSummon(object) - else if (object.friend) - setFriendSummon(object) - else if (!object.main && !object.friend && object.position != null) - summons[object.position] = object - }) - - return summons - } - - const shortcode: string = slug as string - if (shortcode) - fetchGrid(shortcode) - - }, [slug, cookies.user, setEditableContext]) - - function render() { - return ( -
    - -
    - ) - } - - function renderNotFound() { - return ( -
    -

    There's no grid here.

    - -
    - ) - } - - if (!found && !loading) { - return renderNotFound() - } else if (found && !loading) { - return render() - } else { - return (
    ) - } + // if (!found && !loading) { + // return renderNotFound() + // } else if (found && !loading) { + // return render() + // } else { + // return (
    ) + // } } -export default - withCookies( - PartyRoute - ) \ No newline at end of file +export default PartyRoute \ No newline at end of file diff --git a/utils/api.tsx b/utils/api.tsx index f1ae1b4e..89203821 100644 --- a/utils/api.tsx +++ b/utils/api.tsx @@ -1,4 +1,4 @@ -import axios, { AxiosRequestConfig, AxiosResponse } from "axios" +import axios, { Axios, AxiosRequestConfig, AxiosResponse } from "axios" interface Entity { name: string @@ -6,11 +6,13 @@ interface Entity { type CollectionEndpoint = ({ query }: { query: AxiosRequestConfig }) => Promise> type IdEndpoint = ({ id }: { id: string }) => Promise> +type IdWithObjectEndpoint = ({ id, object }: { id: string, object: string }) => Promise> type PostEndpoint = (object: {}, headers?: {}) => Promise> interface EndpointMap { getAll: CollectionEndpoint getOne: IdEndpoint + getOneWithObject: IdWithObjectEndpoint create: PostEndpoint update: PostEndpoint destroy: IdEndpoint @@ -38,7 +40,8 @@ class Api { return { getAll: ({ query }: { query: AxiosRequestConfig }) => axios.get(resourceUrl, { params: { query }}), - getOne: ({ id }: { id: string }) => axios.get(`${resourceUrl}/${id}`), + getOne: ({ id }: { id: string }) => axios.get(`${resourceUrl}/${id}/`), + getOneWithObject: ({ id, object }: { id: string, object: string }) => axios.get(`${resourceUrl}/${id}/${object}`), create: (object: {}, headers?: {}) => axios.post(resourceUrl, object, headers), update: (object: {}, headers?: {}) => axios.put(resourceUrl, object, headers), destroy: ({ id }: { id: string }) => axios.delete(`${resourceUrl}/${id}`) diff --git a/utils/enums.tsx b/utils/enums.tsx index f55ca8d8..86e2af6d 100644 --- a/utils/enums.tsx +++ b/utils/enums.tsx @@ -1,3 +1,9 @@ +export enum GridType { + Class, + Character, + Weapon, + Summon +} export enum TeamElement { Any, Wind,