Merge pull request #3 from jedmund/routing-refactor

Refactor routing to use Next.js routing instead of react-router-dom
This commit is contained in:
Justin Edmund 2022-01-31 23:41:38 -08:00 committed by GitHub
commit fd428e8af7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 294 additions and 365 deletions

View file

@ -1,29 +0,0 @@
import React from 'react'
import { Route, Routes, useNavigate } from 'react-router-dom'
import Header from '~components/Header'
import NewRoute from '~routes/NewRoute'
import PartyRoute from '~routes/PartyRoute'
import PartiesRoute from '~routes/PartiesRoute'
import ProfileRoute from '~routes/ProfileRoute'
const App = () => {
const navigate = useNavigate()
const route = (pathname: string) => navigate(pathname)
return (
<div>
<Header navigate={route} />
<Routes>
<Route path='/' element={<NewRoute />} />
<Route path='/parties/' element={<PartiesRoute />} />
<Route path='/p/:hash' element={<PartyRoute />} />
<Route path='/:username' element={<ProfileRoute />} />
</Routes>
</div>
)
}
export default App

View file

@ -1,36 +1,36 @@
.Header { #Header {
display: flex; display: flex;
height: 34px; height: 34px;
width: 100%; width: 100%;
}
.Header .right { #right {
display: flex; display: flex;
gap: 8px; gap: 8px;
} }
.Header .push { .dropdown {
margin-left: auto; display: inline-block;
} position: relative;
.dropdown { &:hover {
display: inline-block; padding-right: 50px;
position: relative; padding-bottom: 16px;
}
.dropdown:hover { .Button .icon {
padding-right: 50px; color: #2360C5;
padding-bottom: 16px; }
}
.dropdown:hover .Button .icon { .Button .text {
color: #2360C5; color: #555;
} }
.dropdown:hover .Button .text { .Menu {
color: #555; display: block;
} }
}
}
.dropdown:hover .Menu { .push {
display: block; margin-left: auto;
} }
}

View file

@ -1,29 +1,30 @@
import React, { useEffect, useState } from 'react' import React, { useContext, useEffect, useState } from 'react'
import { useNavigate, useLocation } from 'react-router-dom'
import { useCookies } from 'react-cookie' import { useCookies } from 'react-cookie'
import { useRouter } from 'next/router'
import AppContext from '~context/AppContext'
import Button from '~components/Button' import Button from '~components/Button'
import HeaderMenu from '~components/HeaderMenu' import HeaderMenu from '~components/HeaderMenu'
import './index.scss' import './index.scss'
interface Props { interface Props {}
navigate: (pathname: string) => void
}
const Header = (props: Props) => { const Header = (props: Props) => {
const [username, setUsername] = useState(undefined) const { editable, setEditable, setAuthenticated } = useContext(AppContext)
const [cookies, setCookie, removeCookie] = useCookies(['user'])
let navigate = useNavigate() const [username, setUsername] = useState(undefined)
let location = useLocation() const [cookies, _, removeCookie] = useCookies(['user'])
const route = (pathname: string) => props.navigate(pathname) const router = useRouter()
useEffect(() => { useEffect(() => {
if (cookies.user) { if (cookies.user) {
setUsername(cookies.user.username) setUsername(cookies.user.username)
console.log(`Logged in as user "${cookies.user.username}"`) console.log(`Logged in as user "${cookies.user.username}"`)
} else {
console.log('You are currently not logged in.')
} }
}, [cookies]) }, [cookies])
@ -39,29 +40,34 @@ const Header = (props: Props) => {
} }
function newParty() { function newParty() {
navigate('/') router.push('/')
} }
function logout() { function logout() {
removeCookie('user') removeCookie('user')
window.history.replaceState(null, `Grid Tool`, `/`)
navigate(0) setAuthenticated(false)
if (editable) setEditable(false)
// How can we log out without navigating to root
router.push('/')
return false
} }
return ( return (
<nav className="Header"> <nav id="Header">
<div className="left"> <div id="left">
<div className="dropdown"> <div className="dropdown">
<Button type="menu">Menu</Button> <Button type="menu">Menu</Button>
<HeaderMenu username={username} logout={logout} /> <HeaderMenu username={username} logout={logout} />
</div> </div>
</div> </div>
<div className="push" /> <div className="push" />
<div className="right"> <div id="right">
{/* { (location.pathname.includes('/p/')) ? { (editable && router.route === '/p/[slug]') ?
<Button color="red" type="link" click={() => {}}>Delete</Button> : '' <Button color="red" type="link" click={() => {}}>Delete</Button> : ''
} */} }
{ (location.pathname.includes('/p/')) ? { (router.route === '/p/[slug]') ?
<Button type="link" click={copyToClipboard}>Copy link</Button> : '' <Button type="link" click={copyToClipboard}>Copy link</Button> : ''
} }
<Button type="new" click={newParty}>New</Button> <Button type="new" click={newParty}>New</Button>

View file

@ -1,24 +1,29 @@
import React from 'react'
import './index.scss' import './index.scss'
import React, { useContext } from 'react'
import Link from 'next/link'
import LoginModal from '~components/LoginModal' import LoginModal from '~components/LoginModal'
import SignupModal from '~components/SignupModal' import SignupModal from '~components/SignupModal'
import AppContext from '~context/AppContext'
import { useModal as useSignupModal } from '~utils/useModal' import { useModal as useSignupModal } from '~utils/useModal'
import { useModal as useLoginModal } from '~utils/useModal' import { useModal as useLoginModal } from '~utils/useModal'
import { useModal as useAboutModal } from '~utils/useModal' import { useModal as useAboutModal } from '~utils/useModal'
import { Link, Route } from 'react-router-dom'
import Profile from '~routes/ProfileRoute' import Profile from '~routes/ProfileRoute'
import AboutModal from '~components/AboutModal' import AboutModal from '~components/AboutModal'
interface Props { interface Props {
username?: string username: string,
logout: () => void logout: () => void
} }
const HeaderMenu = (props: Props) => { const HeaderMenu = (props: Props) => {
const { authenticated } = useContext(AppContext)
const { open: signupOpen, openModal: openSignupModal, closeModal: closeSignupModal } = useSignupModal() const { open: signupOpen, openModal: openSignupModal, closeModal: closeSignupModal } = useSignupModal()
const { open: loginOpen, openModal: openLoginModal, closeModal: closeLoginModal } = useLoginModal() const { open: loginOpen, openModal: openLoginModal, closeModal: closeLoginModal } = useLoginModal()
const { open: aboutOpen, openModal: openAboutModal, closeModal: closeAboutModal } = useAboutModal() const { open: aboutOpen, openModal: openAboutModal, closeModal: closeAboutModal } = useAboutModal()
@ -29,7 +34,7 @@ const HeaderMenu = (props: Props) => {
<ul className="Menu auth"> <ul className="Menu auth">
<div className="MenuGroup"> <div className="MenuGroup">
<li className="MenuItem"> <li className="MenuItem">
<Link to={`/${props.username}` || ''}>My Parties</Link> <Link href={`/${props.username}` || ''}>My Parties</Link>
</li> </li>
</div> </div>
<div className="MenuGroup"> <div className="MenuGroup">
@ -38,9 +43,9 @@ const HeaderMenu = (props: Props) => {
<AboutModal close={closeAboutModal} /> <AboutModal close={closeAboutModal} />
) : null} ) : null}
{/* <li className="MenuItem"> <li className="MenuItem">
<Link to='/guides'>Guides</Link> <Link href='/guides'>Guides</Link>
</li> */} </li>
</div> </div>
<div className="MenuGroup"> <div className="MenuGroup">
<li className="MenuItem" onClick={props.logout}>Logout</li> <li className="MenuItem" onClick={props.logout}>Logout</li>
@ -79,7 +84,7 @@ const HeaderMenu = (props: Props) => {
) )
} }
return (props.username !== undefined) ? authItems() : unauthItems() return (authenticated) ? authItems() : unauthItems()
} }
export default HeaderMenu export default HeaderMenu

View file

@ -0,0 +1,17 @@
import type { ReactElement } from 'react'
import Header from '~components/Header'
interface Props {
children: ReactElement
}
const Layout = ({children}: Props) => {
return (
<>
<Header />
<main>{children}</main>
</>
)
}
export default Layout

View file

@ -1,6 +1,8 @@
import React, { useState } from 'react' import React, { useContext, useState } from 'react'
import { withCookies, Cookies } from 'react-cookie' import { withCookies, Cookies } from 'react-cookie'
import { createPortal } from 'react-dom' import { createPortal } from 'react-dom'
import AppContext from '~context/AppContext'
import api from '~utils/api' import api from '~utils/api'
import Button from '~components/Button' import Button from '~components/Button'
@ -26,6 +28,8 @@ interface ErrorMap {
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 LoginModal = (props: Props) => {
const { setAuthenticated } = useContext(AppContext)
const emailInput: React.RefObject<HTMLInputElement> = React.createRef() const emailInput: React.RefObject<HTMLInputElement> = React.createRef()
const passwordInput: React.RefObject<HTMLInputElement> = React.createRef() const passwordInput: React.RefObject<HTMLInputElement> = React.createRef()
const form: React.RefObject<HTMLInputElement>[] = [emailInput, passwordInput] const form: React.RefObject<HTMLInputElement>[] = [emailInput, passwordInput]
@ -98,6 +102,8 @@ const LoginModal = (props: Props) => {
} }
cookies.set('user', cookieObj, { path: '/'}) cookies.set('user', cookieObj, { path: '/'})
setAuthenticated(true)
props.close() props.close()
}, (error) => { }, (error) => {
console.error(error) console.error(error)

View file

@ -36,7 +36,7 @@ interface Props {
} }
const Party = (props: Props) => { const Party = (props: Props) => {
const [cookies, setCookie] = useCookies(['user']) const [cookies, _] = useCookies(['user'])
const headers = (cookies.user != null) ? { const headers = (cookies.user != null) ? {
headers: { headers: {

View file

@ -67,7 +67,7 @@ const PartySegmentedControl = (props: Props) => {
{ {
(() => { (() => {
if (props.selectedTab == GridType.Weapon) { if (props.editable && props.selectedTab == GridType.Weapon) {
return extraToggle return extraToggle
} }
})() })()

10
context/AppContext.tsx Normal file
View file

@ -0,0 +1,10 @@
import { createContext } from 'react'
const AppContext = createContext({
authenticated: false,
editable: false,
setAuthenticated: (auth: boolean) => {},
setEditable: (editable: boolean) => {}
})
export default AppContext

View file

@ -1,11 +1,20 @@
/** @type {import('next').NextConfig} */ /** @type {import('next').NextConfig} */
const path = require('path') const path = require('path')
module.exports = {reactStrictMode: true, module.exports = {
reactStrictMode: true,
sassOptions: { sassOptions: {
prependData: '@import "variables";', prependData: '@import "variables";',
includePaths: [path.join(__dirname, 'styles')], includePaths: [path.join(__dirname, 'styles')],
}, },
async rewrites() {
return [
{
source: '/',
destination: '/new'
}
]
},
webpack(config) { webpack(config) {
config.module.rules.push({ config.module.rules.push({
test: /\.svg$/, test: /\.svg$/,
@ -18,4 +27,3 @@ module.exports = {reactStrictMode: true,
return config; return config;
}, },
}; };

58
package-lock.json generated
View file

@ -20,7 +20,6 @@
"react-cookie": "^4.1.1", "react-cookie": "^4.1.1",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-i18next": "^11.15.3", "react-i18next": "^11.15.3",
"react-router-dom": "^6.2.1",
"sass": "^1.49.0" "sass": "^1.49.0"
}, },
"devDependencies": { "devDependencies": {
@ -4776,14 +4775,6 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/history": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz",
"integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==",
"dependencies": {
"@babel/runtime": "^7.7.6"
}
},
"node_modules/hoist-non-react-statics": { "node_modules/hoist-non-react-statics": {
"version": "3.3.2", "version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@ -5891,30 +5882,6 @@
} }
} }
}, },
"node_modules/react-router": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz",
"integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==",
"dependencies": {
"history": "^5.2.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz",
"integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==",
"dependencies": {
"history": "^5.2.0",
"react-router": "6.2.1"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/react-style-singleton": { "node_modules/react-style-singleton": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.1.1.tgz", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.1.1.tgz",
@ -10095,14 +10062,6 @@
"has-symbols": "^1.0.2" "has-symbols": "^1.0.2"
} }
}, },
"history": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/history/-/history-5.2.0.tgz",
"integrity": "sha512-uPSF6lAJb3nSePJ43hN3eKj1dTWpN9gMod0ZssbFTIsen+WehTmEadgL+kg78xLJFdRfrrC//SavDzmRVdE+Ig==",
"requires": {
"@babel/runtime": "^7.7.6"
}
},
"hoist-non-react-statics": { "hoist-non-react-statics": {
"version": "3.3.2", "version": "3.3.2",
"resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
@ -10893,23 +10852,6 @@
"tslib": "^1.0.0" "tslib": "^1.0.0"
} }
}, },
"react-router": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.2.1.tgz",
"integrity": "sha512-2fG0udBtxou9lXtK97eJeET2ki5//UWfQSl1rlJ7quwe6jrktK9FCCc8dQb5QY6jAv3jua8bBQRhhDOM/kVRsg==",
"requires": {
"history": "^5.2.0"
}
},
"react-router-dom": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.2.1.tgz",
"integrity": "sha512-I6Zax+/TH/cZMDpj3/4Fl2eaNdcvoxxHoH1tYOREsQ22OKDYofGebrNm6CTPUcvLvZm63NL/vzCYdjf9CUhqmA==",
"requires": {
"history": "^5.2.0",
"react-router": "6.2.1"
}
},
"react-style-singleton": { "react-style-singleton": {
"version": "2.1.1", "version": "2.1.1",
"resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.1.1.tgz", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.1.1.tgz",

View file

@ -25,7 +25,6 @@
"react-cookie": "^4.1.1", "react-cookie": "^4.1.1",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-i18next": "^11.15.3", "react-i18next": "^11.15.3",
"react-router-dom": "^6.2.1",
"sass": "^1.49.0" "sass": "^1.49.0"
}, },
"devDependencies": { "devDependencies": {

View file

@ -1,15 +1,10 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { withCookies, useCookies } from 'react-cookie' import { useRouter } from 'next/router'
import { useParams, useNavigate } from 'react-router-dom'
import api from '~utils/api' import api from '~utils/api'
import GridRep from '~components/GridRep' import GridRep from '~components/GridRep'
import GridRepCollection from '~components/GridRepCollection' import GridRepCollection from '~components/GridRepCollection'
import { composeInitialProps } from 'react-i18next'
interface Props {
username: string
}
interface User { interface User {
id: string id: string
@ -24,10 +19,8 @@ interface Party {
} }
const ProfileRoute: React.FC = () => { const ProfileRoute: React.FC = () => {
const params = useParams() const router = useRouter()
const navigate = useNavigate() const { username } = router.query
const [cookies, setCookie] = useCookies(['user'])
const [found, setFound] = useState(false) const [found, setFound] = useState(false)
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
@ -38,12 +31,10 @@ const ProfileRoute: React.FC = () => {
granblueId: 0 granblueId: 0
}) })
const username = params.username || ''
useEffect(() => { useEffect(() => {
console.log(`Fetching profile for ${username}...`) console.log(`Fetching profile for ${username}...`)
fetchProfile(username) fetchProfile(username as string)
}, []) }, [username])
async function fetchProfile(username: string) { async function fetchProfile(username: string) {
api.endpoints.users.getOne({ id: username }) api.endpoints.users.getOne({ id: username })
@ -81,7 +72,7 @@ const ProfileRoute: React.FC = () => {
} }
function goTo(shortcode: string) { function goTo(shortcode: string) {
navigate(`/p/${shortcode}`) router.push(`/p/${shortcode}`)
} }
function renderGrids() { function renderGrids() {
@ -126,7 +117,4 @@ const ProfileRoute: React.FC = () => {
} }
} }
export default export default ProfileRoute
withCookies(
ProfileRoute
)

View file

@ -1,9 +1,25 @@
import '../styles/globals.scss' import '../styles/globals.scss'
import { useState } from 'react'
import { CookiesProvider } from 'react-cookie'
import Layout from '~components/Layout'
import AppContext from '~context/AppContext'
import type { AppProps } from 'next/app' import type { AppProps } from 'next/app'
function MyApp({ Component, pageProps }: AppProps) { function MyApp({ Component, pageProps }: AppProps) {
return <Component {...pageProps} /> const [authenticated, setAuthenticated] = useState(false)
const [editable, setEditable] = useState(false)
return (
<CookiesProvider>
<AppContext.Provider value={{ authenticated, setAuthenticated, editable, setEditable }}>
<Layout>
<Component {...pageProps} />
</Layout>
</AppContext.Provider>
</CookiesProvider>
)
} }
export default MyApp export default MyApp

View file

@ -1,23 +0,0 @@
import type { NextPage } from 'next'
import Head from 'next/head'
import styles from '../styles/Home.module.css'
import { CookiesProvider } from 'react-cookie'
import { BrowserRouter } from 'react-router-dom'
import App from '~/components/App'
const Home: NextPage = () => {
if (typeof window === "undefined")
return null
return (
<BrowserRouter>
<CookiesProvider>
<App />
</CookiesProvider>
</BrowserRouter>
)
}
export default Home

158
pages/p/[party].tsx Normal file
View file

@ -0,0 +1,158 @@
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)
const [found, setFound] = useState(false)
const [loading, setLoading] = useState(true)
const [editable, setEditable] = useState(false)
const [characters, setCharacters] = useState<GridArray<Character>>({})
const [weapons, setWeapons] = useState<GridArray<Weapon>>({})
const [summons, setSummons] = useState<GridArray<Summon>>({})
const [mainWeapon, setMainWeapon] = useState<Weapon>()
const [mainSummon, setMainSummon] = useState<Summon>()
const [friendSummon, setFriendSummon] = useState<Summon>()
const [partyId, setPartyId] = useState('')
const [extra, setExtra] = useState<boolean>(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<Character> = {}
list.forEach((object: GridCharacter) => {
if (object.position != null)
characters[object.position] = object.character
})
return characters
}
function populateWeapons(list: [GridWeapon]) {
let weapons: GridArray<Weapon> = {}
list.forEach((object: GridWeapon) => {
if (object.mainhand)
setMainWeapon(object.weapon)
else if (!object.mainhand && object.position != null)
weapons[object.position] = object.weapon
})
return weapons
}
function populateSummons(list: [GridSummon]) {
let summons: GridArray<Summon> = {}
list.forEach((object: GridSummon) => {
if (object.main)
setMainSummon(object.summon)
else if (object.friend)
setFriendSummon(object.summon)
else if (!object.main && !object.friend && object.position != null)
summons[object.position] = object.summon
})
return summons
}
const shortcode: string = slug as string
fetchGrid(shortcode)
}, [slug, cookies.user, setEditableContext])
function render() {
return (
<div id="Content">
<Party
partyId={partyId}
mainWeapon={mainWeapon}
mainSummon={mainSummon}
friendSummon={friendSummon}
characters={characters}
weapons={weapons}
summons={summons}
editable={editable}
exists={found}
extra={extra}
/>
</div>
)
}
function renderNotFound() {
return (
<div id="NotFound">
<h2>There&apos;s no grid here.</h2>
<Button type="new">New grid</Button>
</div>
)
}
if (!found && !loading) {
return renderNotFound()
} else if (found && !loading) {
return render()
} else {
return (<div />)
}
}
export default
withCookies(
PartyRoute
)

View file

@ -1,39 +0,0 @@
import React from 'react'
interface State {
parties: {id: string, hash: string}[]
}
class PartiesRoute extends React.Component {
state: State
constructor(props: any) {
super(props)
this.state = {
parties: []
}
}
getParties() {
fetch('http://localhost:3001/parties/')
.then(response => response.json())
.then(parties => this.setState({
parties: parties
}))
}
render() {
this.getParties()
const items = this.state.parties.map((party: {id: string, hash: string }) =>
<li key={party.id}><a href={'../' + party.hash}>{party.hash}</a></li>
)
return (
<div>
<h1>A list of parties</h1>
<ul>{items}</ul>
</div>
)
}
}
export default PartiesRoute

View file

@ -1,135 +0,0 @@
import React, { useEffect, useState } from 'react'
import { withCookies, useCookies } from 'react-cookie'
import { useParams } from 'react-router-dom'
import api from '~utils/api'
import Party from '~components/Party'
import Button from '~components/Button'
interface Props {
hash: string
}
const PartyRoute: React.FC = () => {
const params = useParams()
const [found, setFound] = useState(false)
const [loading, setLoading] = useState(true)
const [editable, setEditable] = useState(false)
const [characters, setCharacters] = useState<GridArray<Character>>({})
const [weapons, setWeapons] = useState<GridArray<Weapon>>({})
const [summons, setSummons] = useState<GridArray<Summon>>({})
const [mainWeapon, setMainWeapon] = useState<Weapon>()
const [mainSummon, setMainSummon] = useState<Summon>()
const [friendSummon, setFriendSummon] = useState<Summon>()
const [partyId, setPartyId] = useState('')
const [extra, setExtra] = useState<boolean>(false)
const [cookies, setCookie] = useCookies(['user'])
const shortcode = params.hash || ''
useEffect(() => {
fetchGrid(shortcode)
}, [])
async function fetchGrid(shortcode: string) {
return api.endpoints.parties.getOne({ id: shortcode })
.then(response => {
const party = response.data.party
const partyUser = party.user_id
const loggedInUser = (cookies.user) ? cookies.user.user_id : ''
if (partyUser != undefined && loggedInUser != undefined && partyUser === loggedInUser)
setEditable(true)
let characters: GridArray<Character> = {}
let weapons: GridArray<Weapon> = {}
let summons: GridArray<Summon> = {}
party.characters.forEach((gridCharacter: GridCharacter) => {
if (gridCharacter.position != null)
characters[gridCharacter.position] = gridCharacter.character
})
party.weapons.forEach((gridWeapon: GridWeapon) => {
if (gridWeapon.mainhand)
setMainWeapon(gridWeapon.weapon)
else if (!gridWeapon.mainhand && gridWeapon.position != null)
weapons[gridWeapon.position] = gridWeapon.weapon
})
party.summons.forEach((gridSummon: GridSummon) => {
if (gridSummon.main)
setMainSummon(gridSummon.summon)
else if (gridSummon.friend)
setFriendSummon(gridSummon.summon)
else if (!gridSummon.main && !gridSummon.friend && gridSummon.position != null)
summons[gridSummon.position] = gridSummon.summon
})
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 render() {
return (
<div id="Content">
<Party
partyId={partyId}
mainWeapon={mainWeapon}
mainSummon={mainSummon}
friendSummon={friendSummon}
characters={characters}
weapons={weapons}
summons={summons}
editable={editable}
exists={found}
extra={extra}
/>
</div>
)
}
function renderNotFound() {
return (
<div id="NotFound">
<h2>There's no grid here.</h2>
<Button type="new">New grid</Button>
</div>
)
}
if (!found && !loading) {
return renderNotFound()
} else if (found && !loading) {
return render()
} else {
return (<div />)
}
}
export default
withCookies(
PartyRoute
)