Merge branch 'staging' into remove-title-button-new

This commit is contained in:
Justin Edmund 2023-01-31 03:21:09 -08:00
commit 20dc1f72f9
18 changed files with 207 additions and 95 deletions

View file

@ -23,6 +23,7 @@ import './index.scss'
// Props // Props
interface Props { interface Props {
new: boolean new: boolean
editable: boolean
characters?: GridCharacter[] characters?: GridCharacter[]
createParty: (details?: DetailsObject) => Promise<Party> createParty: (details?: DetailsObject) => Promise<Party>
pushHistory?: (path: string) => void pushHistory?: (path: string) => void
@ -75,17 +76,6 @@ const CharacterGrid = (props: Props) => {
[key: number]: number | undefined [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(() => { useEffect(() => {
setJob(appState.party.job) setJob(appState.party.job)
setJobSkills(appState.party.jobSkills) setJobSkills(appState.party.jobSkills)
@ -115,7 +105,7 @@ const CharacterGrid = (props: Props) => {
.catch((error) => console.error(error)) .catch((error) => console.error(error))
}) })
} else { } else {
if (party.editable) if (props.editable)
saveCharacter(party.id, character, position) saveCharacter(party.id, character, position)
.then((response) => handleCharacterResponse(response.data)) .then((response) => handleCharacterResponse(response.data))
.catch((error) => { .catch((error) => {
@ -232,7 +222,7 @@ const CharacterGrid = (props: Props) => {
} }
function saveJobSkill(skill: JobSkill, position: number) { function saveJobSkill(skill: JobSkill, position: number) {
if (party.id && appState.party.editable) { if (party.id && props.editable) {
const positionedKey = `skill${position}_id` const positionedKey = `skill${position}_id`
let skillObject: { let skillObject: {
@ -522,7 +512,7 @@ const CharacterGrid = (props: Props) => {
job={job} job={job}
jobSkills={jobSkills} jobSkills={jobSkills}
jobAccessory={jobAccessory} jobAccessory={jobAccessory}
editable={party.editable} editable={props.editable}
saveJob={saveJob} saveJob={saveJob}
saveSkill={saveJobSkill} saveSkill={saveJobSkill}
saveAccessory={saveAccessory} saveAccessory={saveAccessory}
@ -541,7 +531,7 @@ const CharacterGrid = (props: Props) => {
<li key={`grid_unit_${i}`}> <li key={`grid_unit_${i}`}>
<CharacterUnit <CharacterUnit
gridCharacter={grid.characters[i]} gridCharacter={grid.characters[i]}
editable={party.editable} editable={props.editable}
position={i} position={i}
updateObject={receiveCharacterFromSearch} updateObject={receiveCharacterFromSearch}
updateUncap={initiateUncapUpdate} updateUncap={initiateUncapUpdate}

View file

@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next'
import axios, { AxiosError, AxiosResponse } from 'axios' import axios, { AxiosError, AxiosResponse } from 'axios'
import api from '~utils/api' import api from '~utils/api'
import setUserToken from '~utils/setUserToken' import { setHeaders } from '~utils/userToken'
import { accountState } from '~utils/accountState' import { accountState } from '~utils/accountState'
import Button from '~components/Button' import Button from '~components/Button'
@ -147,7 +147,7 @@ const LoginModal = (props: Props) => {
setCookie('account', cookieObj, { path: '/', expires: expiresAt }) setCookie('account', cookieObj, { path: '/', expires: expiresAt })
// Set Axios default headers // Set Axios default headers
setUserToken() setHeaders()
} }
function storeUserInfo(response: AxiosResponse) { function storeUserInfo(response: AxiosResponse) {

View file

@ -1,7 +1,9 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { getCookie } from 'cookies-next'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { useSnapshot } from 'valtio' import { useSnapshot } from 'valtio'
import clonedeep from 'lodash.clonedeep' import clonedeep from 'lodash.clonedeep'
import ls from 'local-storage'
import PartySegmentedControl from '~components/PartySegmentedControl' import PartySegmentedControl from '~components/PartySegmentedControl'
import PartyDetails from '~components/PartyDetails' import PartyDetails from '~components/PartyDetails'
@ -13,6 +15,8 @@ import api from '~utils/api'
import { appState, initialAppState } from '~utils/appState' import { appState, initialAppState } from '~utils/appState'
import { GridType } from '~utils/enums' import { GridType } from '~utils/enums'
import { retrieveCookies } from '~utils/retrieveCookies' import { retrieveCookies } from '~utils/retrieveCookies'
import { accountCookie, setEditKey, unsetEditKey } from '~utils/userToken'
import type { DetailsObject } from '~types' import type { DetailsObject } from '~types'
import './index.scss' import './index.scss'
@ -36,6 +40,7 @@ const Party = (props: Props) => {
// Set up states // Set up states
const { party } = useSnapshot(appState) const { party } = useSnapshot(appState)
const [editable, setEditable] = useState(false)
const [currentTab, setCurrentTab] = useState<GridType>(GridType.Weapon) const [currentTab, setCurrentTab] = useState<GridType>(GridType.Weapon)
// Retrieve cookies // Retrieve cookies
@ -48,6 +53,41 @@ const Party = (props: Props) => {
if (props.team) storeParty(props.team) 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
unsetEditKey()
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) {
// Set editable
editable = true
// Also set edit key header
setEditKey(props.team.id, props.team.user)
}
}
}
appState.party.editable = editable
setEditable(editable)
})
// Set selected tab from props // Set selected tab from props
useEffect(() => { useEffect(() => {
setCurrentTab(props.selectedTab) setCurrentTab(props.selectedTab)
@ -59,13 +99,13 @@ const Party = (props: Props) => {
if (details) payload = formatDetailsObject(details) if (details) payload = formatDetailsObject(details)
return await api.endpoints.parties return await api.endpoints.parties
.create(payload) .create({ ...payload, ...localId() })
.then((response) => storeParty(response.data.party)) .then((response) => storeParty(response.data.party))
} }
// Methods: Updating the party's details // Methods: Updating the party's details
async function updateDetails(details: DetailsObject) { async function updateDetails(details: DetailsObject) {
if (!appState.party.id) return await createParty(details) if (!props.team) return await createParty(details)
else updateParty(details) else updateParty(details)
} }
@ -92,9 +132,9 @@ const Party = (props: Props) => {
async function updateParty(details: DetailsObject) { async function updateParty(details: DetailsObject) {
const payload = formatDetailsObject(details) const payload = formatDetailsObject(details)
if (appState.party.id) { if (props.team && props.team.id) {
return await api.endpoints.parties return await api.endpoints.parties
.update(appState.party.id, payload) .update(props.team.id, payload)
.then((response) => storeParty(response.data.party)) .then((response) => storeParty(response.data.party))
} }
} }
@ -103,8 +143,8 @@ const Party = (props: Props) => {
appState.party.extra = event.target.checked appState.party.extra = event.target.checked
// Only save if this is a saved party // Only save if this is a saved party
if (appState.party.id) { if (props.team && props.team.id) {
api.endpoints.parties.update(appState.party.id, { api.endpoints.parties.update(props.team.id, {
party: { extra: event.target.checked }, party: { extra: event.target.checked },
}) })
} }
@ -112,9 +152,9 @@ const Party = (props: Props) => {
// Deleting the party // Deleting the party
function deleteTeam() { function deleteTeam() {
if (appState.party.editable && appState.party.id) { if (props.team && editable) {
api.endpoints.parties api.endpoints.parties
.destroy({ id: appState.party.id }) .destroy({ id: props.team.id })
.then(() => { .then(() => {
// Push to route // Push to route
if (cookies && cookies.account.username) { if (cookies && cookies.account.username) {
@ -139,7 +179,7 @@ const Party = (props: Props) => {
} }
// Methods: Storing party data // 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 // Store the important party and state-keeping values in global state
appState.party.name = team.name appState.party.name = team.name
appState.party.description = team.description appState.party.description = team.description
@ -162,6 +202,12 @@ const Party = (props: Props) => {
appState.party.detailsVisible = false appState.party.detailsVisible = false
// Store the edit key in local storage
if (team.edit_key) {
storeEditKey(team.id, team.edit_key)
setEditKey(team.id, team.user)
}
// Populate state // Populate state
storeCharacters(team.characters) storeCharacters(team.characters)
storeWeapons(team.weapons) storeWeapons(team.weapons)
@ -183,6 +229,10 @@ const Party = (props: Props) => {
return team return team
} }
const storeEditKey = (id: string, key: string) => {
ls(id, key)
}
const storeCharacters = (list: Array<GridCharacter>) => { const storeCharacters = (list: Array<GridCharacter>) => {
list.forEach((object: GridCharacter) => { list.forEach((object: GridCharacter) => {
if (object.position != null) if (object.position != null)
@ -240,6 +290,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 // Render: JSX components
const navigation = ( const navigation = (
<PartySegmentedControl <PartySegmentedControl
@ -252,6 +311,7 @@ const Party = (props: Props) => {
const weaponGrid = ( const weaponGrid = (
<WeaponGrid <WeaponGrid
new={props.new || false} new={props.new || false}
editable={editable}
weapons={props.team?.weapons} weapons={props.team?.weapons}
createParty={createParty} createParty={createParty}
pushHistory={props.pushHistory} pushHistory={props.pushHistory}
@ -261,6 +321,7 @@ const Party = (props: Props) => {
const summonGrid = ( const summonGrid = (
<SummonGrid <SummonGrid
new={props.new || false} new={props.new || false}
editable={editable}
summons={props.team?.summons} summons={props.team?.summons}
createParty={createParty} createParty={createParty}
pushHistory={props.pushHistory} pushHistory={props.pushHistory}
@ -270,6 +331,7 @@ const Party = (props: Props) => {
const characterGrid = ( const characterGrid = (
<CharacterGrid <CharacterGrid
new={props.new || false} new={props.new || false}
editable={editable}
characters={props.team?.characters} characters={props.team?.characters}
createParty={createParty} createParty={createParty}
pushHistory={props.pushHistory} pushHistory={props.pushHistory}

View file

@ -5,7 +5,7 @@ import { useTranslation } from 'next-i18next'
import { AxiosResponse } from 'axios' import { AxiosResponse } from 'axios'
import api from '~utils/api' import api from '~utils/api'
import setUserToken from '~utils/setUserToken' import { setHeaders } from '~utils/userToken'
import { accountState } from '~utils/accountState' import { accountState } from '~utils/accountState'
import Button from '~components/Button' import Button from '~components/Button'
@ -103,7 +103,7 @@ const SignupModal = (props: Props) => {
setCookie('account', cookieObj, { path: '/', expires: expiresAt }) setCookie('account', cookieObj, { path: '/', expires: expiresAt })
// Set Axios default headers // Set Axios default headers
setUserToken() setHeaders()
} }
function fetchUserInfo(id: string) { function fetchUserInfo(id: string) {

View file

@ -21,6 +21,7 @@ import './index.scss'
// Props // Props
interface Props { interface Props {
new: boolean new: boolean
editable: boolean
summons?: GridSummon[] summons?: GridSummon[]
createParty: (details?: DetailsObject) => Promise<Party> createParty: (details?: DetailsObject) => Promise<Party>
pushHistory?: (path: string) => void pushHistory?: (path: string) => void
@ -55,17 +56,6 @@ const SummonGrid = (props: Props) => {
[key: number]: number [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 // Initialize an array of current uncap values for each summon
useEffect(() => { useEffect(() => {
let initialPreviousUncapValues: { [key: number]: number } = {} let initialPreviousUncapValues: { [key: number]: number } = {}
@ -100,7 +90,7 @@ const SummonGrid = (props: Props) => {
) )
}) })
} else { } else {
if (party.editable) if (props.editable)
saveSummon(party.id, summon, position) saveSummon(party.id, summon, position)
.then((response) => handleSummonResponse(response.data)) .then((response) => handleSummonResponse(response.data))
.catch((error) => { .catch((error) => {
@ -401,7 +391,7 @@ const SummonGrid = (props: Props) => {
<div className="Label">{t('summons.main')}</div> <div className="Label">{t('summons.main')}</div>
<SummonUnit <SummonUnit
gridSummon={grid.summons.mainSummon} gridSummon={grid.summons.mainSummon}
editable={party.editable} editable={props.editable}
key="grid_main_summon" key="grid_main_summon"
position={-1} position={-1}
unitType={0} unitType={0}
@ -418,7 +408,7 @@ const SummonGrid = (props: Props) => {
<div className="Label Friend">{t('summons.friend')}</div> <div className="Label Friend">{t('summons.friend')}</div>
<SummonUnit <SummonUnit
gridSummon={grid.summons.friendSummon} gridSummon={grid.summons.friendSummon}
editable={party.editable} editable={props.editable}
key="grid_friend_summon" key="grid_friend_summon"
position={6} position={6}
unitType={2} unitType={2}
@ -439,7 +429,7 @@ const SummonGrid = (props: Props) => {
<li key={`grid_unit_${i}`}> <li key={`grid_unit_${i}`}>
<SummonUnit <SummonUnit
gridSummon={grid.summons.allSummons[i]} gridSummon={grid.summons.allSummons[i]}
editable={party.editable} editable={props.editable}
position={i} position={i}
unitType={1} unitType={1}
removeSummon={removeSummon} removeSummon={removeSummon}
@ -457,7 +447,7 @@ const SummonGrid = (props: Props) => {
const subAuraSummonElement = ( const subAuraSummonElement = (
<ExtraSummons <ExtraSummons
grid={grid.summons.allSummons} grid={grid.summons.allSummons}
editable={party.editable} editable={props.editable}
exists={false} exists={false}
offset={numSummons} offset={numSummons}
removeSummon={removeSummon} removeSummon={removeSummon}

View file

@ -23,6 +23,7 @@ import './index.scss'
// Props // Props
interface Props { interface Props {
new: boolean new: boolean
editable: boolean
weapons?: GridWeapon[] weapons?: GridWeapon[]
createParty: (details: DetailsObject) => Promise<Party> createParty: (details: DetailsObject) => Promise<Party>
pushHistory?: (path: string) => void pushHistory?: (path: string) => void
@ -60,17 +61,6 @@ const WeaponGrid = (props: Props) => {
[key: number]: number [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 // Initialize an array of current uncap values for each weapon
useEffect(() => { useEffect(() => {
let initialPreviousUncapValues: { [key: number]: number } = {} let initialPreviousUncapValues: { [key: number]: number } = {}
@ -99,7 +89,7 @@ const WeaponGrid = (props: Props) => {
}) })
}) })
} else { } else {
if (party.editable) if (props.editable)
saveWeapon(party.id, weapon, position) saveWeapon(party.id, weapon, position)
.then((response) => { .then((response) => {
if (response) handleWeaponResponse(response.data) if (response) handleWeaponResponse(response.data)
@ -337,7 +327,7 @@ const WeaponGrid = (props: Props) => {
const mainhandElement = ( const mainhandElement = (
<WeaponUnit <WeaponUnit
gridWeapon={appState.grid.weapons.mainWeapon} gridWeapon={appState.grid.weapons.mainWeapon}
editable={party.editable} editable={props.editable}
key="grid_mainhand" key="grid_mainhand"
position={-1} position={-1}
unitType={0} unitType={0}
@ -352,7 +342,7 @@ const WeaponGrid = (props: Props) => {
<li key={`grid_unit_${i}`}> <li key={`grid_unit_${i}`}>
<WeaponUnit <WeaponUnit
gridWeapon={appState.grid.weapons.allWeapons[i]} gridWeapon={appState.grid.weapons.allWeapons[i]}
editable={party.editable} editable={props.editable}
position={i} position={i}
unitType={1} unitType={1}
removeWeapon={removeWeapon} removeWeapon={removeWeapon}
@ -366,7 +356,7 @@ const WeaponGrid = (props: Props) => {
const extraGridElement = ( const extraGridElement = (
<ExtraWeapons <ExtraWeapons
grid={appState.grid.weapons.allWeapons} grid={appState.grid.weapons.allWeapons}
editable={party.editable} editable={props.editable}
offset={numWeapons} offset={numWeapons}
removeWeapon={removeWeapon} removeWeapon={removeWeapon}
updateObject={receiveWeaponFromSearch} updateObject={receiveWeaponFromSearch}

38
package-lock.json generated
View file

@ -26,6 +26,7 @@
"i18next": "^21.6.13", "i18next": "^21.6.13",
"i18next-browser-languagedetector": "^6.1.3", "i18next-browser-languagedetector": "^6.1.3",
"i18next-http-backend": "^1.3.2", "i18next-http-backend": "^1.3.2",
"local-storage": "^2.0.0",
"lodash.clonedeep": "^4.5.0", "lodash.clonedeep": "^4.5.0",
"lodash.debounce": "^4.0.8", "lodash.debounce": "^4.0.8",
"meyer-reset-scss": "^2.0.4", "meyer-reset-scss": "^2.0.4",
@ -45,6 +46,7 @@
"sanitize-html": "^2.8.1", "sanitize-html": "^2.8.1",
"sass": "^1.49.0", "sass": "^1.49.0",
"usehooks-ts": "^2.9.1", "usehooks-ts": "^2.9.1",
"uuid": "^9.0.0",
"valtio": "^1.3.0", "valtio": "^1.3.0",
"youtube-api-v3-wrapper": "^2.3.0" "youtube-api-v3-wrapper": "^2.3.0"
}, },
@ -59,6 +61,7 @@
"@types/react-linkify": "^1.0.1", "@types/react-linkify": "^1.0.1",
"@types/react-scroll": "^1.8.3", "@types/react-scroll": "^1.8.3",
"@types/sanitize-html": "^2.8.0", "@types/sanitize-html": "^2.8.0",
"@types/uuid": "^9.0.0",
"eslint": "8.7.0", "eslint": "8.7.0",
"eslint-config-next": "12.0.8", "eslint-config-next": "12.0.8",
"eslint-plugin-valtio": "^0.4.1", "eslint-plugin-valtio": "^0.4.1",
@ -3154,6 +3157,12 @@
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" "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": { "node_modules/@typescript-eslint/parser": {
"version": "5.47.1", "version": "5.47.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.47.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.47.1.tgz",
@ -5647,6 +5656,11 @@
"json5": "lib/cli.js" "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": { "node_modules/lodash.clonedeep": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
@ -7309,6 +7323,14 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" "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": { "node_modules/v8-compile-cache": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",
@ -9576,6 +9598,12 @@
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz",
"integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" "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": { "@typescript-eslint/parser": {
"version": "5.47.1", "version": "5.47.1",
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.47.1.tgz", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.47.1.tgz",
@ -11376,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": { "lodash.clonedeep": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
@ -12507,6 +12540,11 @@
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" "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": { "v8-compile-cache": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz",

View file

@ -31,6 +31,7 @@
"i18next": "^21.6.13", "i18next": "^21.6.13",
"i18next-browser-languagedetector": "^6.1.3", "i18next-browser-languagedetector": "^6.1.3",
"i18next-http-backend": "^1.3.2", "i18next-http-backend": "^1.3.2",
"local-storage": "^2.0.0",
"lodash.clonedeep": "^4.5.0", "lodash.clonedeep": "^4.5.0",
"lodash.debounce": "^4.0.8", "lodash.debounce": "^4.0.8",
"meyer-reset-scss": "^2.0.4", "meyer-reset-scss": "^2.0.4",
@ -50,6 +51,7 @@
"sanitize-html": "^2.8.1", "sanitize-html": "^2.8.1",
"sass": "^1.49.0", "sass": "^1.49.0",
"usehooks-ts": "^2.9.1", "usehooks-ts": "^2.9.1",
"uuid": "^9.0.0",
"valtio": "^1.3.0", "valtio": "^1.3.0",
"youtube-api-v3-wrapper": "^2.3.0" "youtube-api-v3-wrapper": "^2.3.0"
}, },
@ -64,6 +66,7 @@
"@types/react-linkify": "^1.0.1", "@types/react-linkify": "^1.0.1",
"@types/react-scroll": "^1.8.3", "@types/react-scroll": "^1.8.3",
"@types/sanitize-html": "^2.8.0", "@types/sanitize-html": "^2.8.0",
"@types/uuid": "^9.0.0",
"eslint": "8.7.0", "eslint": "8.7.0",
"eslint-config-next": "12.0.8", "eslint-config-next": "12.0.8",
"eslint-plugin-valtio": "^0.4.1", "eslint-plugin-valtio": "^0.4.1",

View file

@ -9,7 +9,7 @@ import api from '~utils/api'
import extractFilters from '~utils/extractFilters' import extractFilters from '~utils/extractFilters'
import fetchLatestVersion from '~utils/fetchLatestVersion' import fetchLatestVersion from '~utils/fetchLatestVersion'
import organizeRaids from '~utils/organizeRaids' import organizeRaids from '~utils/organizeRaids'
import setUserToken from '~utils/setUserToken' import { setHeaders } from '~utils/userToken'
import useDidMountEffect from '~utils/useDidMountEffect' import useDidMountEffect from '~utils/useDidMountEffect'
import { appState } from '~utils/appState' import { appState } from '~utils/appState'
import { elements, allElement } from '~data/elements' import { elements, allElement } from '~data/elements'
@ -329,7 +329,7 @@ export const getServerSidePaths = async () => {
// prettier-ignore // prettier-ignore
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => { export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
// Set headers for server-side requests // Set headers for server-side requests
setUserToken(req, res) setHeaders(req, res)
// Fetch latest version // Fetch latest version
const version = await fetchLatestVersion() const version = await fetchLatestVersion()

View file

@ -7,7 +7,7 @@ import type { AppProps } from 'next/app'
import Layout from '~components/Layout' import Layout from '~components/Layout'
import { accountState } from '~utils/accountState' import { accountState } from '~utils/accountState'
import setUserToken from '~utils/setUserToken' import { setHeaders } from '~utils/userToken'
import '../styles/globals.scss' import '../styles/globals.scss'
import { ToastProvider, Viewport } from '@radix-ui/react-toast' import { ToastProvider, Viewport } from '@radix-ui/react-toast'
@ -23,9 +23,8 @@ function MyApp({ Component, pageProps }: AppProps) {
} }
useEffect(() => { useEffect(() => {
setUserToken() setHeaders()
if (cookieData.account && cookieData.account.token) {
if (accountCookie) {
console.log(`Logged in as user "${cookieData.account.username}"`) console.log(`Logged in as user "${cookieData.account.username}"`)
accountState.account.authorized = true accountState.account.authorized = true

View file

@ -6,7 +6,7 @@ import { useTranslation } from 'next-i18next'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { AboutTabs } from '~utils/enums' import { AboutTabs } from '~utils/enums'
import setUserToken from '~utils/setUserToken' import { setHeaders } from '~utils/userToken'
import AboutPage from '~components/AboutPage' import AboutPage from '~components/AboutPage'
import UpdatesPage from '~components/UpdatesPage' import UpdatesPage from '~components/UpdatesPage'
@ -160,7 +160,7 @@ export const getServerSidePaths = async () => {
// prettier-ignore // prettier-ignore
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => { export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
// Set headers for server-side requests // Set headers for server-side requests
setUserToken(req, res) setHeaders(req, res)
// Fetch and organize raids // Fetch and organize raids
return { return {

View file

@ -1,6 +1,7 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from 'react'
import { useRouter } from 'next/router' import { useRouter } from 'next/router'
import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import { v4 as uuidv4 } from 'uuid'
import clonedeep from 'lodash.clonedeep' import clonedeep from 'lodash.clonedeep'
import ErrorSection from '~components/ErrorSection' import ErrorSection from '~components/ErrorSection'
@ -10,7 +11,7 @@ import NewHead from '~components/NewHead'
import api from '~utils/api' import api from '~utils/api'
import fetchLatestVersion from '~utils/fetchLatestVersion' import fetchLatestVersion from '~utils/fetchLatestVersion'
import organizeRaids from '~utils/organizeRaids' import organizeRaids from '~utils/organizeRaids'
import setUserToken from '~utils/setUserToken' import { accountCookie, setHeaders } from '~utils/userToken'
import { appState, initialAppState } from '~utils/appState' import { appState, initialAppState } from '~utils/appState'
import { groupWeaponKeys } from '~utils/groupWeaponKeys' import { groupWeaponKeys } from '~utils/groupWeaponKeys'
@ -18,6 +19,7 @@ import type { AxiosError } from 'axios'
import type { NextApiRequest, NextApiResponse } from 'next' import type { NextApiRequest, NextApiResponse } from 'next'
import type { PageContextObj, ResponseStatus } from '~types' import type { PageContextObj, ResponseStatus } from '~types'
import { GridType } from '~utils/enums' import { GridType } from '~utils/enums'
import { setCookie } from 'cookies-next'
interface Props { interface Props {
context?: PageContextObj context?: PageContextObj
@ -119,8 +121,24 @@ export const getServerSidePaths = async () => {
// prettier-ignore // prettier-ignore
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => { export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
// Set headers for server-side requests // Set headers for API calls
setUserToken(req, res) 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 // Fetch latest version
const version = await fetchLatestVersion() const version = await fetchLatestVersion()

View file

@ -10,7 +10,7 @@ import api from '~utils/api'
import elementEmoji from '~utils/elementEmoji' import elementEmoji from '~utils/elementEmoji'
import fetchLatestVersion from '~utils/fetchLatestVersion' import fetchLatestVersion from '~utils/fetchLatestVersion'
import organizeRaids from '~utils/organizeRaids' import organizeRaids from '~utils/organizeRaids'
import setUserToken from '~utils/setUserToken' import { setHeaders } from '~utils/userToken'
import { appState } from '~utils/appState' import { appState } from '~utils/appState'
import { groupWeaponKeys } from '~utils/groupWeaponKeys' import { groupWeaponKeys } from '~utils/groupWeaponKeys'
@ -108,7 +108,7 @@ export const getServerSidePaths = async () => {
// prettier-ignore // prettier-ignore
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => { export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
// Set headers for server-side requests // Set headers for server-side requests
setUserToken(req, res) setHeaders(req, res)
// Fetch latest version // Fetch latest version
const version = await fetchLatestVersion() const version = await fetchLatestVersion()

View file

@ -7,7 +7,7 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import clonedeep from 'lodash.clonedeep' import clonedeep from 'lodash.clonedeep'
import api from '~utils/api' import api from '~utils/api'
import setUserToken from '~utils/setUserToken' import { setHeaders } from '~utils/userToken'
import extractFilters from '~utils/extractFilters' import extractFilters from '~utils/extractFilters'
import fetchLatestVersion from '~utils/fetchLatestVersion' import fetchLatestVersion from '~utils/fetchLatestVersion'
import organizeRaids from '~utils/organizeRaids' import organizeRaids from '~utils/organizeRaids'
@ -363,7 +363,7 @@ export const getServerSidePaths = async () => {
// prettier-ignore // prettier-ignore
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => { export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
// Set headers for server-side requests // Set headers for server-side requests
setUserToken(req, res) setHeaders(req, res)
// Fetch latest version // Fetch latest version
const version = await fetchLatestVersion() const version = await fetchLatestVersion()

View file

@ -7,7 +7,7 @@ import { serverSideTranslations } from 'next-i18next/serverSideTranslations'
import clonedeep from 'lodash.clonedeep' import clonedeep from 'lodash.clonedeep'
import api from '~utils/api' import api from '~utils/api'
import setUserToken from '~utils/setUserToken' import { setHeaders } from '~utils/userToken'
import extractFilters from '~utils/extractFilters' import extractFilters from '~utils/extractFilters'
import fetchLatestVersion from '~utils/fetchLatestVersion' import fetchLatestVersion from '~utils/fetchLatestVersion'
import organizeRaids from '~utils/organizeRaids' import organizeRaids from '~utils/organizeRaids'
@ -363,7 +363,7 @@ export const getServerSidePaths = async () => {
// prettier-ignore // prettier-ignore
export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => { export const getServerSideProps = async ({ req, res, locale, query }: { req: NextApiRequest, res: NextApiResponse, locale: string, query: { [index: string]: string } }) => {
// Set headers for server-side requests // Set headers for server-side requests
setUserToken(req, res) setHeaders(req, res)
// Fetch latest version // Fetch latest version
const version = await fetchLatestVersion() const version = await fetchLatestVersion()

1
types/Party.d.ts vendored
View file

@ -29,6 +29,7 @@ interface Party {
weapons: Array<GridWeapon> weapons: Array<GridWeapon>
summons: Array<GridSummon> summons: Array<GridSummon>
user: User user: User
local_id?: string
remix: boolean remix: boolean
remixes: Party[] remixes: Party[]
created_at: string created_at: string

View file

@ -1,19 +0,0 @@
import axios from 'axios'
import { getCookie } from 'cookies-next'
import type { NextApiRequest, NextApiResponse } from 'next'
export default (
req: NextApiRequest | undefined = undefined,
res: NextApiResponse | undefined = undefined
) => {
// Set up cookies
const options = req && res ? { req, res } : {}
const cookie = getCookie('account', options)
if (cookie) {
axios.defaults.headers.common['Authorization'] = `Bearer ${
JSON.parse(cookie as string).token
}`
} else {
delete axios.defaults.headers.common['Authorization']
}
}

40
utils/userToken.tsx Normal file
View file

@ -0,0 +1,40 @@
import axios from 'axios'
import ls, { get, set } from 'local-storage'
import { getCookie } from 'cookies-next'
import type { NextApiRequest, NextApiResponse } from 'next'
export const accountCookie = (
req: NextApiRequest | undefined = undefined,
res: NextApiResponse | undefined = undefined
) => {
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) {
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']
}
}
export const setEditKey = (id: string, user?: User) => {
if (!user) {
const edit_key = get<string>(id)
axios.defaults.headers.common['X-Edit-Key'] = edit_key
} else {
unsetEditKey()
}
}
export const unsetEditKey = () => {
delete axios.defaults.headers.common['X-Edit-Key']
}