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:
commit
fd428e8af7
19 changed files with 294 additions and 365 deletions
|
|
@ -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
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
17
components/Layout/index.tsx
Normal file
17
components/Layout/index.tsx
Normal 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
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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: {
|
||||||
|
|
|
||||||
|
|
@ -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
10
context/AppContext.tsx
Normal 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
|
||||||
|
|
@ -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
58
package-lock.json
generated
|
|
@ -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",
|
||||||
|
|
|
||||||
|
|
@ -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": {
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
)
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
158
pages/p/[party].tsx
Normal 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'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
|
||||||
|
)
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
)
|
|
||||||
Loading…
Reference in a new issue