From 1a39108e38a677887f34e5cb3235797d855ba753 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 23 Feb 2022 15:58:26 -0800 Subject: [PATCH 1/7] Add user info in account state --- utils/accountState.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/utils/accountState.tsx b/utils/accountState.tsx index 3fdb86f4..46a917f6 100644 --- a/utils/accountState.tsx +++ b/utils/accountState.tsx @@ -5,14 +5,19 @@ interface AccountState { account: { authorized: boolean, - language: 'en' | 'jp' + language: 'en' | 'jp', + user: { + id: string, + username: string + } | undefined } } export const initialAccountState: AccountState = { account: { authorized: false, - language: 'en' + language: 'en', + user: undefined } } From d1c602dd5928c0d6eccfe3a11c501d4a407af8ad Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 23 Feb 2022 15:58:37 -0800 Subject: [PATCH 2/7] Remove React contexts --- context/AppContext.tsx | 10 ---------- context/PartyContext.tsx | 17 ----------------- 2 files changed, 27 deletions(-) delete mode 100644 context/AppContext.tsx delete mode 100644 context/PartyContext.tsx diff --git a/context/AppContext.tsx b/context/AppContext.tsx deleted file mode 100644 index ab66c95c..00000000 --- a/context/AppContext.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import { createContext } from 'react' - -const AppContext = createContext({ - authenticated: false, - editable: false, - setAuthenticated: (auth: boolean) => {}, - setEditable: (editable: boolean) => {} -}) - -export default AppContext \ No newline at end of file diff --git a/context/PartyContext.tsx b/context/PartyContext.tsx deleted file mode 100644 index 87599c19..00000000 --- a/context/PartyContext.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { createContext } from 'react' -import { TeamElement } from '~utils/enums' - -const PartyContext = createContext({ - id: '', - setId: (id: string) => {}, - slug: '', - setSlug: (slug: string) => {}, - element: TeamElement.Any, - setElement: (element: TeamElement) => {}, - editable: false, - setEditable: (editable: boolean) => {}, - hasExtra: false, - setHasExtra: (hasExtra: boolean) => {} -}) - -export default PartyContext \ No newline at end of file From a6b222000c8c349caefe0945c0076143b24bb9ac Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 23 Feb 2022 15:59:46 -0800 Subject: [PATCH 3/7] Remove references to React context and replace with state where necessary --- components/BottomHeader/index.tsx | 24 +++++++--------------- components/HeaderMenu/index.tsx | 5 ++--- components/LoginModal/index.tsx | 14 ++++++------- components/PartySegmentedControl/index.tsx | 2 +- components/SignupModal/index.tsx | 14 +++++++------ components/SummonGrid/index.tsx | 2 +- components/WeaponGrid/index.tsx | 2 +- pages/_app.tsx | 12 +++-------- pages/p/[party].tsx | 2 +- 9 files changed, 31 insertions(+), 46 deletions(-) diff --git a/components/BottomHeader/index.tsx b/components/BottomHeader/index.tsx index 013b1f2a..133b9769 100644 --- a/components/BottomHeader/index.tsx +++ b/components/BottomHeader/index.tsx @@ -1,35 +1,25 @@ import React, { MouseEventHandler, useContext, useEffect, useState } from 'react' -import { useCookies } from 'react-cookie' import { useRouter } from 'next/router' - -import AppContext from '~context/AppContext' +import { useSnapshot } from 'valtio' import * as AlertDialog from '@radix-ui/react-alert-dialog'; import Header from '~components/Header' import Button from '~components/Button' +import { accountState } from '~utils/accountState' +import { appState } from '~utils/appState' + import { ButtonType } from '~utils/enums' import CrossIcon from '~public/icons/Cross.svg' const BottomHeader = () => { - const { editable, setEditable, authenticated, setAuthenticated } = useContext(AppContext) - - const [username, setUsername] = useState(undefined) - const [cookies, _, removeCookie] = useCookies(['user']) + const account = useSnapshot(accountState) + const app = useSnapshot(appState) const router = useRouter() - useEffect(() => { - if (cookies.user) { - setAuthenticated(true) - setUsername(cookies.user.username) - } else { - setAuthenticated(false) - } - }, [cookies, setUsername, setAuthenticated]) - function deleteTeam(event: React.MouseEvent) { // TODO: Implement deleting teams console.log("Deleting team...") @@ -42,7 +32,7 @@ const BottomHeader = () => { } const rightNav = () => { - if (editable && router.route === '/p/[party]') { + if (app.party.editable && router.route === '/p/[party]') { return ( diff --git a/components/HeaderMenu/index.tsx b/components/HeaderMenu/index.tsx index c121d83f..389edfe0 100644 --- a/components/HeaderMenu/index.tsx +++ b/components/HeaderMenu/index.tsx @@ -1,6 +1,4 @@ -import './index.scss' - -import React, { useContext } from 'react' +import React from 'react' import Link from 'next/link' import LoginModal from '~components/LoginModal' @@ -12,6 +10,7 @@ import { useModal as useAboutModal } from '~utils/useModal' import AboutModal from '~components/AboutModal' +import './index.scss' interface Props { authenticated: boolean, diff --git a/components/LoginModal/index.tsx b/components/LoginModal/index.tsx index 79d7e7ab..a3c4763b 100644 --- a/components/LoginModal/index.tsx +++ b/components/LoginModal/index.tsx @@ -1,9 +1,9 @@ -import React, { useContext, useState } from 'react' +import React, { useState } from 'react' import { withCookies, Cookies } from 'react-cookie' import { createPortal } from 'react-dom' -import AppContext from '~context/AppContext' import api from '~utils/api' +import { accountState } from '~utils/accountState' import Button from '~components/Button' import Fieldset from '~components/Fieldset' @@ -12,8 +12,6 @@ import Overlay from '~components/Overlay' import './index.scss' -// import New from '../../../assets/new' - interface Props { cookies: Cookies close: () => void @@ -28,8 +26,6 @@ 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 LoginModal = (props: Props) => { - const { setAuthenticated } = useContext(AppContext) - const emailInput: React.RefObject = React.createRef() const passwordInput: React.RefObject = React.createRef() const form: React.RefObject[] = [emailInput, passwordInput] @@ -102,7 +98,11 @@ const LoginModal = (props: Props) => { } cookies.set('user', cookieObj, { path: '/'}) - setAuthenticated(true) + accountState.account.authorized = true + accountState.account.user = { + id: cookieObj.user_id, + username: cookieObj.username + } props.close() }, (error) => { diff --git a/components/PartySegmentedControl/index.tsx b/components/PartySegmentedControl/index.tsx index 29ff5ad4..18cc6c55 100644 --- a/components/PartySegmentedControl/index.tsx +++ b/components/PartySegmentedControl/index.tsx @@ -1,4 +1,4 @@ -import React, { useContext } from 'react' +import React from 'react' import './index.scss' import { appState } from '~utils/appState' diff --git a/components/SignupModal/index.tsx b/components/SignupModal/index.tsx index 82ba9a42..137217a8 100644 --- a/components/SignupModal/index.tsx +++ b/components/SignupModal/index.tsx @@ -1,9 +1,9 @@ -import React, { useContext, useState } from 'react' +import React, { useState } from 'react' import { withCookies, Cookies } from 'react-cookie' import { createPortal } from 'react-dom' -import AppContext from '~context/AppContext' import api from '~utils/api' +import { accountState } from '~utils/accountState' import Button from '~components/Button' import Fieldset from '~components/Fieldset' @@ -28,8 +28,6 @@ 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 SignupModal = (props: Props) => { - const { setAuthenticated } = useContext(AppContext) - const [formValid, setFormValid] = useState(false) const [errors, setErrors] = useState({ username: '', @@ -81,8 +79,12 @@ const SignupModal = (props: Props) => { .then((response) => { const cookies = props.cookies cookies.set('user', response.data.user, { path: '/'}) - - setAuthenticated(true) + + accountState.account.authorized = true + accountState.account.user = { + id: response.data.user.id, + username: response.data.user.username + } props.close() }, (error) => { diff --git a/components/SummonGrid/index.tsx b/components/SummonGrid/index.tsx index 9f9231ab..5f472e31 100644 --- a/components/SummonGrid/index.tsx +++ b/components/SummonGrid/index.tsx @@ -1,5 +1,5 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import { useCookies } from 'react-cookie' import { useSnapshot } from 'valtio' diff --git a/components/WeaponGrid/index.tsx b/components/WeaponGrid/index.tsx index 79893a0c..0be9798a 100644 --- a/components/WeaponGrid/index.tsx +++ b/components/WeaponGrid/index.tsx @@ -1,5 +1,5 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import { useCookies } from 'react-cookie' import { useSnapshot } from 'valtio' diff --git a/pages/_app.tsx b/pages/_app.tsx index 0ad5f753..b3573d99 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -4,21 +4,15 @@ 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' function MyApp({ Component, pageProps }: AppProps) { - const [authenticated, setAuthenticated] = useState(false) - const [editable, setEditable] = useState(false) - return ( - - - - - + + + ) } diff --git a/pages/p/[party].tsx b/pages/p/[party].tsx index e43e2b07..1ac9a3d9 100644 --- a/pages/p/[party].tsx +++ b/pages/p/[party].tsx @@ -1,4 +1,4 @@ -import React, { useContext, useEffect, useState } from 'react' +import React from 'react' import { useRouter } from 'next/router' import Party from '~components/Party' From 1d82bc5055771fd5369bd4cfa9d4b835c1a0b162 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 23 Feb 2022 16:12:45 -0800 Subject: [PATCH 4/7] Insert account info into state when the app first loads --- pages/_app.tsx | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/pages/_app.tsx b/pages/_app.tsx index b3573d99..81c1d965 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,13 +1,30 @@ -import '../styles/globals.scss' - -import { useState } from 'react' -import { CookiesProvider } from 'react-cookie' - -import Layout from '~components/Layout' +import { useEffect } from 'react' +import { useCookies, CookiesProvider } from 'react-cookie' import type { AppProps } from 'next/app' +import Layout from '~components/Layout' + +import { accountState } from '~utils/accountState' + +import '../styles/globals.scss' function MyApp({ Component, pageProps }: AppProps) { + const [cookies] = useCookies(['user']) + + useEffect(() => { + if (cookies.user) { + console.log(`Logged in as user "${cookies.user.username}"`) + + accountState.account.authorized = true + accountState.account.user = { + id: cookies.user.user_id, + username: cookies.user.username + } + } else { + console.log(`You are not currently logged in.`) + } + }, [cookies.user]) + return ( From c88e9be02569545166f5608425b72266ecab254a Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 23 Feb 2022 16:13:28 -0800 Subject: [PATCH 5/7] Final updates removing context and replacing with state --- components/BottomHeader/index.tsx | 2 +- components/CharacterGrid/index.tsx | 2 +- components/TopHeader/index.tsx | 31 +++++++++--------------------- 3 files changed, 11 insertions(+), 24 deletions(-) diff --git a/components/BottomHeader/index.tsx b/components/BottomHeader/index.tsx index 133b9769..d823671c 100644 --- a/components/BottomHeader/index.tsx +++ b/components/BottomHeader/index.tsx @@ -1,4 +1,4 @@ -import React, { MouseEventHandler, useContext, useEffect, useState } from 'react' +import React from 'react' import { useRouter } from 'next/router' import { useSnapshot } from 'valtio' diff --git a/components/CharacterGrid/index.tsx b/components/CharacterGrid/index.tsx index e1af247f..4a1eb463 100644 --- a/components/CharacterGrid/index.tsx +++ b/components/CharacterGrid/index.tsx @@ -1,5 +1,5 @@ /* eslint-disable react-hooks/exhaustive-deps */ -import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import { useCookies } from 'react-cookie' import { useSnapshot } from 'valtio' diff --git a/components/TopHeader/index.tsx b/components/TopHeader/index.tsx index 4828f5b4..99bd9a54 100644 --- a/components/TopHeader/index.tsx +++ b/components/TopHeader/index.tsx @@ -1,35 +1,22 @@ -import React, { useContext, useEffect, useState } from 'react' +import React, { useEffect } from 'react' import { useCookies } from 'react-cookie' import { useRouter } from 'next/router' import clonedeep from 'lodash.clonedeep' +import { useSnapshot } from 'valtio' -import AppContext from '~context/AppContext' +import { accountState } from '~utils/accountState' import { appState, initialAppState } from '~utils/appState' import Header from '~components/Header' import Button from '~components/Button' import HeaderMenu from '~components/HeaderMenu' - const TopHeader = () => { - const { editable, setEditable, authenticated, setAuthenticated } = useContext(AppContext) - - const [username, setUsername] = useState(undefined) const [cookies, _, removeCookie] = useCookies(['user']) + const accountSnap = useSnapshot(accountState) const router = useRouter() - useEffect(() => { - if (cookies.user) { - setAuthenticated(true) - setUsername(cookies.user.username) - console.log(`Logged in as user "${cookies.user.username}"`) - } else { - setAuthenticated(false) - console.log('You are currently not logged in.') - } - }, [cookies, setUsername, setAuthenticated]) - function copyToClipboard() { const el = document.createElement('input') el.value = window.location.href @@ -58,8 +45,8 @@ const TopHeader = () => { function logout() { removeCookie('user') - setAuthenticated(false) - if (editable) setEditable(false) + accountState.authorized = false + appState.party.editable = false // TODO: How can we log out without navigating to root router.push('/') @@ -70,9 +57,9 @@ const TopHeader = () => { return (
- { (username) ? - : - + { (accountSnap.account.user) ? + : + }
) From 54f9d3682b35d4ac487113add2ef7fe284f2f787 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 23 Feb 2022 16:42:56 -0800 Subject: [PATCH 6/7] Update API to map destroy endpoint properly --- utils/api.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/utils/api.tsx b/utils/api.tsx index f1cc9c85..e1d02c37 100644 --- a/utils/api.tsx +++ b/utils/api.tsx @@ -9,6 +9,7 @@ type IdEndpoint = ({ id }: { id: string }) => Promise> type IdWithObjectEndpoint = ({ id, object }: { id: string, object: string }) => Promise> type PostEndpoint = (object: {}, headers?: {}) => Promise> type PutEndpoint = (id: string, object: {}, headers?: {}) => Promise> +type DestroyEndpoint = (id: string, headers?: {}) => Promise> interface EndpointMap { getAll: CollectionEndpoint @@ -16,7 +17,7 @@ interface EndpointMap { getOneWithObject: IdWithObjectEndpoint create: PostEndpoint update: PutEndpoint - destroy: IdEndpoint + destroy: DestroyEndpoint } class Api { @@ -45,7 +46,7 @@ class Api { getOneWithObject: ({ id, object }: { id: string, object: string }) => axios.get(`${resourceUrl}/${id}/${object}`), create: (object: {}, headers?: {}) => axios.post(resourceUrl, object, headers), update: (id: string, object: {}, headers?: {}) => axios.put(`${resourceUrl}/${id}`, object, headers), - destroy: ({ id }: { id: string }) => axios.delete(`${resourceUrl}/${id}`) + destroy: (id: string, headers?: {}) => axios.delete(`${resourceUrl}/${id}`, headers) } as EndpointMap } From 9a5c2835d5f2f2760739b68375feddcb2289757a Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Wed, 23 Feb 2022 16:43:08 -0800 Subject: [PATCH 7/7] Add destroy logic --- components/BottomHeader/index.tsx | 39 ++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/components/BottomHeader/index.tsx b/components/BottomHeader/index.tsx index d823671c..ef9b4272 100644 --- a/components/BottomHeader/index.tsx +++ b/components/BottomHeader/index.tsx @@ -1,28 +1,55 @@ import React from 'react' import { useRouter } from 'next/router' +import { useCookies } from 'react-cookie' import { useSnapshot } from 'valtio' +import clonedeep from 'lodash.clonedeep' -import * as AlertDialog from '@radix-ui/react-alert-dialog'; +import * as AlertDialog from '@radix-ui/react-alert-dialog' import Header from '~components/Header' import Button from '~components/Button' +import api from '~utils/api' import { accountState } from '~utils/accountState' -import { appState } from '~utils/appState' +import { appState, initialAppState } from '~utils/appState' import { ButtonType } from '~utils/enums' import CrossIcon from '~public/icons/Cross.svg' - const BottomHeader = () => { const account = useSnapshot(accountState) const app = useSnapshot(appState) const router = useRouter() + // Cookies + const [cookies] = useCookies(['user']) + const headers = (cookies.user != null) ? { + headers: { + 'Authorization': `Bearer ${cookies.user.access_token}` + } + } : {} + function deleteTeam(event: React.MouseEvent) { - // TODO: Implement deleting teams - console.log("Deleting team...") + if (appState.party.editable && appState.party.id) { + api.endpoints.parties.destroy(appState.party.id, headers) + .then(() => { + // Push to route + router.push('/') + + // Clean state + const resetState = clonedeep(initialAppState) + Object.keys(resetState).forEach((key) => { + appState[key] = resetState[key] + }) + + // Set party to be editable + appState.party.editable = true + }) + .catch((error) => { + console.error(error) + }) + } } const leftNav = () => { @@ -73,4 +100,4 @@ const BottomHeader = () => { ) } -export default BottomHeader \ No newline at end of file +export default BottomHeader