From d32293995be073d92f00833ceadc071062dbfcea Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 31 Jan 2023 00:18:54 -0800 Subject: [PATCH 01/10] Refactor setUserToken into two methods This refactors `setUserToken` into `accountCookie`, which just returns a cookie if it exists, and `setHeaders`, which sets the Axios header defaults. Then, we update the calls in all the required files. --- components/LoginModal/index.tsx | 4 ++-- components/SignupModal/index.tsx | 4 ++-- pages/[username].tsx | 4 ++-- pages/_app.tsx | 4 ++-- pages/about.tsx | 4 ++-- pages/new/index.tsx | 6 +++--- pages/p/[party].tsx | 4 ++-- pages/saved.tsx | 4 ++-- pages/teams.tsx | 4 ++-- utils/{setUserToken.tsx => userToken.tsx} | 17 ++++++++++++----- 10 files changed, 31 insertions(+), 24 deletions(-) rename utils/{setUserToken.tsx => userToken.tsx} (51%) diff --git a/components/LoginModal/index.tsx b/components/LoginModal/index.tsx index 66fd34cf..5386f114 100644 --- a/components/LoginModal/index.tsx +++ b/components/LoginModal/index.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next' import axios, { AxiosError, AxiosResponse } from 'axios' import api from '~utils/api' -import setUserToken from '~utils/setUserToken' +import { setHeaders } from '~utils/userToken' import { accountState } from '~utils/accountState' import Button from '~components/Button' @@ -147,7 +147,7 @@ const LoginModal = (props: Props) => { setCookie('account', cookieObj, { path: '/', expires: expiresAt }) // Set Axios default headers - setUserToken() + setHeaders() } function storeUserInfo(response: AxiosResponse) { diff --git a/components/SignupModal/index.tsx b/components/SignupModal/index.tsx index f8cea3f0..eff5f9a3 100644 --- a/components/SignupModal/index.tsx +++ b/components/SignupModal/index.tsx @@ -5,7 +5,7 @@ import { useTranslation } from 'next-i18next' import { AxiosResponse } from 'axios' import api from '~utils/api' -import setUserToken from '~utils/setUserToken' +import { setHeaders } from '~utils/userToken' import { accountState } from '~utils/accountState' import Button from '~components/Button' @@ -103,7 +103,7 @@ const SignupModal = (props: Props) => { setCookie('account', cookieObj, { path: '/', expires: expiresAt }) // Set Axios default headers - setUserToken() + setHeaders() } function fetchUserInfo(id: string) { diff --git a/pages/[username].tsx b/pages/[username].tsx index 0b13f655..cf60511a 100644 --- a/pages/[username].tsx +++ b/pages/[username].tsx @@ -9,7 +9,7 @@ import api from '~utils/api' import extractFilters from '~utils/extractFilters' import fetchLatestVersion from '~utils/fetchLatestVersion' import organizeRaids from '~utils/organizeRaids' -import setUserToken from '~utils/setUserToken' +import { setHeaders } from '~utils/userToken' import useDidMountEffect from '~utils/useDidMountEffect' import { appState } from '~utils/appState' import { elements, allElement } from '~data/elements' @@ -329,7 +329,7 @@ export const getServerSidePaths = async () => { // prettier-ignore export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => { // Set headers for server-side requests - setUserToken(req, res) + setHeaders(req, res) // Fetch latest version const version = await fetchLatestVersion() diff --git a/pages/_app.tsx b/pages/_app.tsx index cfc1806f..200d7665 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -7,7 +7,7 @@ import type { AppProps } from 'next/app' import Layout from '~components/Layout' import { accountState } from '~utils/accountState' -import setUserToken from '~utils/setUserToken' +import { setHeaders } from '~utils/userToken' import '../styles/globals.scss' import { ToastProvider, Viewport } from '@radix-ui/react-toast' @@ -23,9 +23,9 @@ function MyApp({ Component, pageProps }: AppProps) { } useEffect(() => { - setUserToken() if (accountCookie) { + setHeaders() console.log(`Logged in as user "${cookieData.account.username}"`) accountState.account.authorized = true diff --git a/pages/about.tsx b/pages/about.tsx index 179e1d53..06258b18 100644 --- a/pages/about.tsx +++ b/pages/about.tsx @@ -6,7 +6,7 @@ import { useTranslation } from 'next-i18next' import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import { AboutTabs } from '~utils/enums' -import setUserToken from '~utils/setUserToken' +import { setHeaders } from '~utils/userToken' import AboutPage from '~components/AboutPage' import UpdatesPage from '~components/UpdatesPage' @@ -160,7 +160,7 @@ export const getServerSidePaths = async () => { // prettier-ignore export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => { // Set headers for server-side requests - setUserToken(req, res) + setHeaders(req, res) // Fetch and organize raids return { diff --git a/pages/new/index.tsx b/pages/new/index.tsx index e03a3cfa..f78b6225 100644 --- a/pages/new/index.tsx +++ b/pages/new/index.tsx @@ -10,7 +10,7 @@ import NewHead from '~components/NewHead' import api from '~utils/api' import fetchLatestVersion from '~utils/fetchLatestVersion' import organizeRaids from '~utils/organizeRaids' -import setUserToken from '~utils/setUserToken' +import { accountCookie, setHeaders } from '~utils/userToken' import { appState, initialAppState } from '~utils/appState' import { groupWeaponKeys } from '~utils/groupWeaponKeys' @@ -119,8 +119,8 @@ export const getServerSidePaths = async () => { // prettier-ignore export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => { - // Set headers for server-side requests - setUserToken(req, res) + // Set headers for API calls + setHeaders(req, res) // Fetch latest version const version = await fetchLatestVersion() diff --git a/pages/p/[party].tsx b/pages/p/[party].tsx index 3a85a1fc..1b22b046 100644 --- a/pages/p/[party].tsx +++ b/pages/p/[party].tsx @@ -10,7 +10,7 @@ import api from '~utils/api' import elementEmoji from '~utils/elementEmoji' import fetchLatestVersion from '~utils/fetchLatestVersion' import organizeRaids from '~utils/organizeRaids' -import setUserToken from '~utils/setUserToken' +import { setHeaders } from '~utils/userToken' import { appState } from '~utils/appState' import { groupWeaponKeys } from '~utils/groupWeaponKeys' @@ -108,7 +108,7 @@ export const getServerSidePaths = async () => { // prettier-ignore export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => { // Set headers for server-side requests - setUserToken(req, res) + setHeaders(req, res) // Fetch latest version const version = await fetchLatestVersion() diff --git a/pages/saved.tsx b/pages/saved.tsx index 0328fb19..db375c10 100644 --- a/pages/saved.tsx +++ b/pages/saved.tsx @@ -7,7 +7,7 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import clonedeep from 'lodash.clonedeep' import api from '~utils/api' -import setUserToken from '~utils/setUserToken' +import { setHeaders } from '~utils/userToken' import extractFilters from '~utils/extractFilters' import fetchLatestVersion from '~utils/fetchLatestVersion' import organizeRaids from '~utils/organizeRaids' @@ -363,7 +363,7 @@ export const getServerSidePaths = async () => { // prettier-ignore export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => { // Set headers for server-side requests - setUserToken(req, res) + setHeaders(req, res) // Fetch latest version const version = await fetchLatestVersion() diff --git a/pages/teams.tsx b/pages/teams.tsx index 199cb1a3..6b875587 100644 --- a/pages/teams.tsx +++ b/pages/teams.tsx @@ -7,7 +7,7 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import clonedeep from 'lodash.clonedeep' import api from '~utils/api' -import setUserToken from '~utils/setUserToken' +import { setHeaders } from '~utils/userToken' import extractFilters from '~utils/extractFilters' import fetchLatestVersion from '~utils/fetchLatestVersion' import organizeRaids from '~utils/organizeRaids' @@ -363,7 +363,7 @@ export const getServerSidePaths = async () => { // prettier-ignore export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => { // Set headers for server-side requests - setUserToken(req, res) + setHeaders(req, res) // Fetch latest version const version = await fetchLatestVersion() diff --git a/utils/setUserToken.tsx b/utils/userToken.tsx similarity index 51% rename from utils/setUserToken.tsx rename to utils/userToken.tsx index 004a2bc6..9e989028 100644 --- a/utils/setUserToken.tsx +++ b/utils/userToken.tsx @@ -2,17 +2,24 @@ import axios from 'axios' import { getCookie } from 'cookies-next' import type { NextApiRequest, NextApiResponse } from 'next' -export default ( +export const accountCookie = ( req: NextApiRequest | undefined = undefined, res: NextApiResponse | undefined = undefined ) => { - // Set up cookies const options = req && res ? { req, res } : {} const cookie = getCookie('account', options) + return cookie ? cookie : undefined +} + +export const setHeaders = ( + req: NextApiRequest | undefined = undefined, + res: NextApiResponse | undefined = undefined +) => { + const cookie = accountCookie(req, res) if (cookie) { - axios.defaults.headers.common['Authorization'] = `Bearer ${ - JSON.parse(cookie as string).token - }` + const parsed = JSON.parse(cookie as string) + if (parsed.token) + axios.defaults.headers.common['Authorization'] = `Bearer ${parsed.token}` } else { delete axios.defaults.headers.common['Authorization'] } From 01b9787fb87959146f58f6f09ef46c2f62392412 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 31 Jan 2023 00:19:50 -0800 Subject: [PATCH 02/10] Create a UUID for unauth users when they visit /new This snippet adds a dependency on the `uuid` module, then uses it to create a UUID for unauth users that gets stored in an otherwise empty `account` cookie. We update _app to account for this change. --- package-lock.json | 27 +++++++++++++++++++++++++++ package.json | 2 ++ pages/_app.tsx | 3 +-- pages/new/index.tsx | 18 ++++++++++++++++++ 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 754af5dc..19dcccba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45,6 +45,7 @@ "sanitize-html": "^2.8.1", "sass": "^1.49.0", "usehooks-ts": "^2.9.1", + "uuid": "^9.0.0", "valtio": "^1.3.0", "youtube-api-v3-wrapper": "^2.3.0" }, @@ -59,6 +60,7 @@ "@types/react-linkify": "^1.0.1", "@types/react-scroll": "^1.8.3", "@types/sanitize-html": "^2.8.0", + "@types/uuid": "^9.0.0", "eslint": "8.7.0", "eslint-config-next": "12.0.8", "eslint-plugin-valtio": "^0.4.1", @@ -3154,6 +3156,12 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, + "node_modules/@types/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==", + "dev": true + }, "node_modules/@typescript-eslint/parser": { "version": "5.47.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.47.1.tgz", @@ -7309,6 +7317,14 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -9576,6 +9592,12 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, + "@types/uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-kr90f+ERiQtKWMz5rP32ltJ/BtULDI5RVO0uavn1HQUOwjx0R1h0rnDYNL0CepF1zL5bSY6FISAfd9tOdDhU5Q==", + "dev": true + }, "@typescript-eslint/parser": { "version": "5.47.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.47.1.tgz", @@ -12507,6 +12529,11 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "uuid": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz", + "integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==" + }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", diff --git a/package.json b/package.json index ac4d8332..d3911bc4 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "sanitize-html": "^2.8.1", "sass": "^1.49.0", "usehooks-ts": "^2.9.1", + "uuid": "^9.0.0", "valtio": "^1.3.0", "youtube-api-v3-wrapper": "^2.3.0" }, @@ -64,6 +65,7 @@ "@types/react-linkify": "^1.0.1", "@types/react-scroll": "^1.8.3", "@types/sanitize-html": "^2.8.0", + "@types/uuid": "^9.0.0", "eslint": "8.7.0", "eslint-config-next": "12.0.8", "eslint-plugin-valtio": "^0.4.1", diff --git a/pages/_app.tsx b/pages/_app.tsx index 200d7665..d3455586 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -23,9 +23,8 @@ function MyApp({ Component, pageProps }: AppProps) { } useEffect(() => { - - if (accountCookie) { setHeaders() + if (cookieData.account && cookieData.account.token) { console.log(`Logged in as user "${cookieData.account.username}"`) accountState.account.authorized = true diff --git a/pages/new/index.tsx b/pages/new/index.tsx index f78b6225..5f3bd215 100644 --- a/pages/new/index.tsx +++ b/pages/new/index.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useState } from 'react' import { useRouter } from 'next/router' import { serverSideTranslations } from 'next-i18next/serverSideTranslations' +import { v4 as uuidv4 } from 'uuid' import clonedeep from 'lodash.clonedeep' import ErrorSection from '~components/ErrorSection' @@ -18,6 +19,7 @@ import type { AxiosError } from 'axios' import type { NextApiRequest, NextApiResponse } from 'next' import type { PageContextObj, ResponseStatus } from '~types' import { GridType } from '~utils/enums' +import { setCookie } from 'cookies-next' interface Props { context?: PageContextObj @@ -122,6 +124,22 @@ export const getServerSideProps = async ({ req, res, locale, query }: { req: Nex // Set headers for API calls setHeaders(req, res) + // If there is no account entry in cookies, create a UUID and store it + if (!accountCookie(req, res)) { + const uuid = uuidv4() + const expiresAt = new Date() + expiresAt.setDate(expiresAt.getDate() + 60) + + const cookieObj = { + userId: uuid, + username: undefined, + token: undefined, + } + + const options = req && res ? { req, res } : {} + setCookie('account', cookieObj, { path: '/', expires: expiresAt, ...options }) + } + // Fetch latest version const version = await fetchLatestVersion() From 20cf1789dd27364be0789ec861454a0a09836856 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 31 Jan 2023 01:23:20 -0800 Subject: [PATCH 03/10] Add local_id to Party definition --- types/Party.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/types/Party.d.ts b/types/Party.d.ts index eebea134..3a41e705 100644 --- a/types/Party.d.ts +++ b/types/Party.d.ts @@ -29,6 +29,7 @@ interface Party { weapons: Array summons: Array user: User + local_id?: string remix: boolean remixes: Party[] created_at: string From 6b83f81eb6a8c89f791c0a241cd3217e530b7748 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 31 Jan 2023 01:24:16 -0800 Subject: [PATCH 04/10] Send generated local_id with create payload --- components/Party/index.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/components/Party/index.tsx b/components/Party/index.tsx index e935947c..aba5eb09 100644 --- a/components/Party/index.tsx +++ b/components/Party/index.tsx @@ -59,7 +59,7 @@ const Party = (props: Props) => { if (details) payload = formatDetailsObject(details) return await api.endpoints.parties - .create(payload) + .create({ ...payload, ...localId() }) .then((response) => storeParty(response.data.party)) } @@ -69,6 +69,14 @@ const Party = (props: Props) => { else updateParty(details) } + function localId() { + const cookie = accountCookie() + const parsed = JSON.parse(cookie as string) + if (parsed && !parsed.token) { + return { local_id: parsed.userId } + } else return {} + } + function formatDetailsObject(details: DetailsObject) { const payload: { [key: string]: any } = {} From c0df78758b7eb223ecb12ad7c8d4106fba856f02 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 31 Jan 2023 01:24:42 -0800 Subject: [PATCH 05/10] Store edit key in localStorage on create response --- components/Party/index.tsx | 9 ++++++++- package-lock.json | 11 +++++++++++ package.json | 1 + 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/components/Party/index.tsx b/components/Party/index.tsx index aba5eb09..23c75e78 100644 --- a/components/Party/index.tsx +++ b/components/Party/index.tsx @@ -147,7 +147,7 @@ const Party = (props: Props) => { } // Methods: Storing party data - const storeParty = function (team: Party) { + const storeParty = function (team: any) { // Store the important party and state-keeping values in global state appState.party.name = team.name appState.party.description = team.description @@ -170,6 +170,9 @@ const Party = (props: Props) => { appState.party.detailsVisible = false + // Store the edit key in local storage + if (team.edit_key) storeEditKey(team.id, team.edit_key) + // Populate state storeCharacters(team.characters) storeWeapons(team.weapons) @@ -191,6 +194,10 @@ const Party = (props: Props) => { return team } + const storeEditKey = (id: string, key: string) => { + ls(id, key) + } + const storeCharacters = (list: Array) => { list.forEach((object: GridCharacter) => { if (object.position != null) diff --git a/package-lock.json b/package-lock.json index 19dcccba..c5ab4514 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "i18next": "^21.6.13", "i18next-browser-languagedetector": "^6.1.3", "i18next-http-backend": "^1.3.2", + "local-storage": "^2.0.0", "lodash.clonedeep": "^4.5.0", "lodash.debounce": "^4.0.8", "meyer-reset-scss": "^2.0.4", @@ -5655,6 +5656,11 @@ "json5": "lib/cli.js" } }, + "node_modules/local-storage": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/local-storage/-/local-storage-2.0.0.tgz", + "integrity": "sha512-/0sRoeijw7yr/igbVVygDuq6dlYCmtsuTmmpnweVlVtl/s10pf5BCq8LWBxW/AMyFJ3MhMUuggMZiYlx6qr9tw==" + }, "node_modules/lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", @@ -11398,6 +11404,11 @@ } } }, + "local-storage": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/local-storage/-/local-storage-2.0.0.tgz", + "integrity": "sha512-/0sRoeijw7yr/igbVVygDuq6dlYCmtsuTmmpnweVlVtl/s10pf5BCq8LWBxW/AMyFJ3MhMUuggMZiYlx6qr9tw==" + }, "lodash.clonedeep": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", diff --git a/package.json b/package.json index d3911bc4..f0347c8b 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,7 @@ "i18next": "^21.6.13", "i18next-browser-languagedetector": "^6.1.3", "i18next-http-backend": "^1.3.2", + "local-storage": "^2.0.0", "lodash.clonedeep": "^4.5.0", "lodash.debounce": "^4.0.8", "meyer-reset-scss": "^2.0.4", From 608b744a71d68cd77f73b69b109221b1f1dbe504 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 31 Jan 2023 01:25:02 -0800 Subject: [PATCH 06/10] Determine editable at Party level, pass down to grids --- components/CharacterGrid/index.tsx | 20 ++++------------ components/Party/index.tsx | 37 ++++++++++++++++++++++++++++++ components/SummonGrid/index.tsx | 22 +++++------------- components/WeaponGrid/index.tsx | 20 ++++------------ 4 files changed, 53 insertions(+), 46 deletions(-) diff --git a/components/CharacterGrid/index.tsx b/components/CharacterGrid/index.tsx index b97218da..bed247ca 100644 --- a/components/CharacterGrid/index.tsx +++ b/components/CharacterGrid/index.tsx @@ -23,6 +23,7 @@ import './index.scss' // Props interface Props { new: boolean + editable: boolean characters?: GridCharacter[] createParty: (details?: DetailsObject) => Promise pushHistory?: (path: string) => void @@ -75,17 +76,6 @@ const CharacterGrid = (props: Props) => { [key: number]: number | undefined }>({}) - // Set the editable flag only on first load - useEffect(() => { - // If user is logged in and matches - if ( - (accountData && party.user && accountData.userId === party.user.id) || - props.new - ) - appState.party.editable = true - else appState.party.editable = false - }, [props.new, accountData, party]) - useEffect(() => { setJob(appState.party.job) setJobSkills(appState.party.jobSkills) @@ -115,7 +105,7 @@ const CharacterGrid = (props: Props) => { .catch((error) => console.error(error)) }) } else { - if (party.editable) + if (props.editable) saveCharacter(party.id, character, position) .then((response) => handleCharacterResponse(response.data)) .catch((error) => { @@ -232,7 +222,7 @@ const CharacterGrid = (props: Props) => { } function saveJobSkill(skill: JobSkill, position: number) { - if (party.id && appState.party.editable) { + if (party.id && props.editable) { const positionedKey = `skill${position}_id` let skillObject: { @@ -522,7 +512,7 @@ const CharacterGrid = (props: Props) => { job={job} jobSkills={jobSkills} jobAccessory={jobAccessory} - editable={party.editable} + editable={props.editable} saveJob={saveJob} saveSkill={saveJobSkill} saveAccessory={saveAccessory} @@ -541,7 +531,7 @@ const CharacterGrid = (props: Props) => {
  • { // Set up states const { party } = useSnapshot(appState) + const [editable, setEditable] = useState(false) const [currentTab, setCurrentTab] = useState(GridType.Weapon) // Retrieve cookies @@ -48,6 +52,36 @@ const Party = (props: Props) => { if (props.team) storeParty(props.team) }, []) + // Set editable on first load + useEffect(() => { + // Get cookie + const cookie = getCookie('account') + const accountData: AccountCookie = cookie + ? JSON.parse(cookie as string) + : null + + let editable = false + + if (props.new) editable = true + + if (accountData && props.team && !props.new) { + if (accountData.token) { + // Authenticated + if (props.team.user && accountData.userId === props.team.user.id) { + editable = true + } + } else { + // Not authenticated + if (!props.team.user && accountData.userId === props.team.local_id) { + editable = true + } + } + } + + appState.party.editable = editable + setEditable(editable) + }) + // Set selected tab from props useEffect(() => { setCurrentTab(props.selectedTab) @@ -267,6 +301,7 @@ const Party = (props: Props) => { const weaponGrid = ( { const summonGrid = ( { const characterGrid = ( Promise pushHistory?: (path: string) => void @@ -55,17 +56,6 @@ const SummonGrid = (props: Props) => { [key: number]: number }>({}) - // Set the editable flag only on first load - useEffect(() => { - // If user is logged in and matches - if ( - (accountData && party.user && accountData.userId === party.user.id) || - props.new - ) - appState.party.editable = true - else appState.party.editable = false - }, [props.new, accountData, party]) - // Initialize an array of current uncap values for each summon useEffect(() => { let initialPreviousUncapValues: { [key: number]: number } = {} @@ -100,7 +90,7 @@ const SummonGrid = (props: Props) => { ) }) } else { - if (party.editable) + if (props.editable) saveSummon(party.id, summon, position) .then((response) => handleSummonResponse(response.data)) .catch((error) => { @@ -401,7 +391,7 @@ const SummonGrid = (props: Props) => {
    {t('summons.main')}
    {
    {t('summons.friend')}
    {
  • { const subAuraSummonElement = ( Promise pushHistory?: (path: string) => void @@ -60,17 +61,6 @@ const WeaponGrid = (props: Props) => { [key: number]: number }>({}) - // Set the editable flag only on first load - useEffect(() => { - // If user is logged in and matches - if ( - (accountData && party.user && accountData.userId === party.user.id) || - props.new - ) - appState.party.editable = true - else appState.party.editable = false - }, [props.new, accountData, party]) - // Initialize an array of current uncap values for each weapon useEffect(() => { let initialPreviousUncapValues: { [key: number]: number } = {} @@ -99,7 +89,7 @@ const WeaponGrid = (props: Props) => { }) }) } else { - if (party.editable) + if (props.editable) saveWeapon(party.id, weapon, position) .then((response) => { if (response) handleWeaponResponse(response.data) @@ -337,7 +327,7 @@ const WeaponGrid = (props: Props) => { const mainhandElement = ( {
  • { const extraGridElement = ( Date: Tue, 31 Jan 2023 02:53:29 -0800 Subject: [PATCH 07/10] Add setEditKey This method lets us set the edit key in a default header much like we do with user tokens --- utils/userToken.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/utils/userToken.tsx b/utils/userToken.tsx index 9e989028..e75429ff 100644 --- a/utils/userToken.tsx +++ b/utils/userToken.tsx @@ -1,4 +1,5 @@ import axios from 'axios' +import ls, { get, set } from 'local-storage' import { getCookie } from 'cookies-next' import type { NextApiRequest, NextApiResponse } from 'next' @@ -24,3 +25,17 @@ export const setHeaders = ( delete axios.defaults.headers.common['Authorization'] } } + +export const setEditKey = (id: string, user?: User) => { + if (!user) { + const edit_key = get(id) + console.log('Setting header...', edit_key) + axios.defaults.headers.common['X-Edit-Key'] = edit_key + } else { + unsetEditKey() + } +} + +export const unsetEditKey = () => { + delete axios.defaults.headers.common['X-Edit-Key'] +} From ce3d0d73a2831a8b1d3c4c2a5ffe47bf2931b110 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 31 Jan 2023 02:54:50 -0800 Subject: [PATCH 08/10] Store and set edit keys This stores and sets the edit keys on page load and after party create --- components/Party/index.tsx | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/components/Party/index.tsx b/components/Party/index.tsx index 6e2cd912..16977190 100644 --- a/components/Party/index.tsx +++ b/components/Party/index.tsx @@ -73,7 +73,11 @@ const Party = (props: Props) => { } else { // Not authenticated if (!props.team.user && accountData.userId === props.team.local_id) { + // Set editable editable = true + + // Also set edit key header + setEditKey(props.team.id, props.team.user) } } } @@ -103,14 +107,6 @@ const Party = (props: Props) => { else updateParty(details) } - function localId() { - const cookie = accountCookie() - const parsed = JSON.parse(cookie as string) - if (parsed && !parsed.token) { - return { local_id: parsed.userId } - } else return {} - } - function formatDetailsObject(details: DetailsObject) { const payload: { [key: string]: any } = {} @@ -205,7 +201,10 @@ const Party = (props: Props) => { appState.party.detailsVisible = false // Store the edit key in local storage - if (team.edit_key) storeEditKey(team.id, team.edit_key) + if (team.edit_key) { + storeEditKey(team.id, team.edit_key) + setEditKey(team.id, team.user) + } // Populate state storeCharacters(team.characters) @@ -289,6 +288,15 @@ const Party = (props: Props) => { } } + // Methods: Unauth validation + function localId() { + const cookie = accountCookie() + const parsed = JSON.parse(cookie as string) + if (parsed && !parsed.token) { + return { local_id: parsed.userId } + } else return {} + } + // Render: JSX components const navigation = ( Date: Tue, 31 Jan 2023 02:55:14 -0800 Subject: [PATCH 09/10] Use props instead of appState --- components/Party/index.tsx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/components/Party/index.tsx b/components/Party/index.tsx index 16977190..1daf7c9b 100644 --- a/components/Party/index.tsx +++ b/components/Party/index.tsx @@ -14,11 +14,11 @@ import api from '~utils/api' import { appState, initialAppState } from '~utils/appState' import { GridType } from '~utils/enums' import { retrieveCookies } from '~utils/retrieveCookies' +import { accountCookie, setEditKey, unsetEditKey } from '~utils/userToken' + import type { DetailsObject } from '~types' import './index.scss' -import { accountCookie } from '~utils/userToken' -import { getCookie } from 'cookies-next' // Props interface Props { @@ -103,7 +103,7 @@ const Party = (props: Props) => { // Methods: Updating the party's details async function updateDetails(details: DetailsObject) { - if (!appState.party.id) return await createParty(details) + if (!props.team) return await createParty(details) else updateParty(details) } @@ -130,9 +130,9 @@ const Party = (props: Props) => { async function updateParty(details: DetailsObject) { const payload = formatDetailsObject(details) - if (appState.party.id) { + if (props.team && props.team.id) { return await api.endpoints.parties - .update(appState.party.id, payload) + .update(props.team.id, payload) .then((response) => storeParty(response.data.party)) } } @@ -141,8 +141,8 @@ const Party = (props: Props) => { appState.party.extra = event.target.checked // Only save if this is a saved party - if (appState.party.id) { - api.endpoints.parties.update(appState.party.id, { + if (props.team && props.team.id) { + api.endpoints.parties.update(props.team.id, { party: { extra: event.target.checked }, }) } @@ -150,9 +150,9 @@ const Party = (props: Props) => { // Deleting the party function deleteTeam() { - if (appState.party.editable && appState.party.id) { + if (props.team && editable) { api.endpoints.parties - .destroy({ id: appState.party.id }) + .destroy({ id: props.team.id }) .then(() => { // Push to route if (cookies && cookies.account.username) { From ade64446dc4a2f00dd4a873d8cb2b9279b60e73c Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Tue, 31 Jan 2023 02:56:42 -0800 Subject: [PATCH 10/10] Unset edit key on page load Just in case, we're unsetting the edit key on page load, so that we can re-set it after clearing our logic checks --- components/Party/index.tsx | 2 ++ utils/userToken.tsx | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/components/Party/index.tsx b/components/Party/index.tsx index 1daf7c9b..347bc49e 100644 --- a/components/Party/index.tsx +++ b/components/Party/index.tsx @@ -1,4 +1,5 @@ import React, { useEffect, useState } from 'react' +import { getCookie } from 'cookies-next' import { useRouter } from 'next/router' import { useSnapshot } from 'valtio' import clonedeep from 'lodash.clonedeep' @@ -61,6 +62,7 @@ const Party = (props: Props) => { : null let editable = false + unsetEditKey() if (props.new) editable = true diff --git a/utils/userToken.tsx b/utils/userToken.tsx index e75429ff..08563bda 100644 --- a/utils/userToken.tsx +++ b/utils/userToken.tsx @@ -29,7 +29,6 @@ export const setHeaders = ( export const setEditKey = (id: string, user?: User) => { if (!user) { const edit_key = get(id) - console.log('Setting header...', edit_key) axios.defaults.headers.common['X-Edit-Key'] = edit_key } else { unsetEditKey()