From 5cf6d3fff3b2baa15a695a007d1346114bfb7ff0 Mon Sep 17 00:00:00 2001 From: Justin Edmund Date: Sat, 28 Jan 2023 17:20:44 -0800 Subject: [PATCH] Refactor [party] page This is a blueprint for all the other pages --- components/PartyHead/index.tsx | 73 +++++++++++ pages/p/[party].tsx | 232 ++++++++++++++------------------- types/index.d.ts | 11 +- utils/elementEmoji.tsx | 14 ++ utils/generateTitle.tsx | 2 +- utils/getElementForParty.tsx | 8 ++ 6 files changed, 199 insertions(+), 141 deletions(-) create mode 100644 components/PartyHead/index.tsx create mode 100644 utils/elementEmoji.tsx create mode 100644 utils/getElementForParty.tsx diff --git a/components/PartyHead/index.tsx b/components/PartyHead/index.tsx new file mode 100644 index 00000000..3f8a764d --- /dev/null +++ b/components/PartyHead/index.tsx @@ -0,0 +1,73 @@ +import React from 'react' +import Head from 'next/head' +import { useRouter } from 'next/router' +import { useTranslation } from 'next-i18next' + +import generateTitle from '~utils/generateTitle' + +interface Props { + party: Party + meta: { [key: string]: string } +} + +const PartyHead = ({ party, meta }: Props) => { + // Import translations + const { t } = useTranslation('common') + + // Set up router + const router = useRouter() + const locale = + router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' + + return ( + + {/* HTML */} + + {generateTitle(meta.element, party.user?.username, party.name)} + + + + + {/* OpenGraph */} + + + + + + {/* Twitter */} + + + + + + ) +} + +export default PartyHead diff --git a/pages/p/[party].tsx b/pages/p/[party].tsx index b7e16f22..0722556f 100644 --- a/pages/p/[party].tsx +++ b/pages/p/[party].tsx @@ -1,43 +1,40 @@ import React, { useEffect, useState } from 'react' -import Head from 'next/head' import { useRouter } from 'next/router' -import { useTranslation } from 'next-i18next' import { serverSideTranslations } from 'next-i18next/serverSideTranslations' import Party from '~components/Party' +import ErrorSection from '~components/ErrorSection' +import PartyHead from '~components/PartyHead' import api from '~utils/api' -import generateTitle from '~utils/generateTitle' +import elementEmoji from '~utils/elementEmoji' +import fetchLatestVersion from '~utils/fetchLatestVersion' import organizeRaids from '~utils/organizeRaids' import setUserToken from '~utils/setUserToken' import { appState } from '~utils/appState' import { groupWeaponKeys } from '~utils/groupWeaponKeys' -import { GridType } from '~utils/enums' -import { printError } from '~utils/reportError' +import { GridType } from '~utils/enums' import type { NextApiRequest, NextApiResponse } from 'next' -import type { GroupedWeaponKeys } from '~utils/groupWeaponKeys' +import type { PageContextObj, ResponseStatus } from '~types' +import type { AxiosError } from 'axios' interface Props { - party: Party - jobs: Job[] - jobSkills: JobSkill[] - raids: Raid[] - sortedRaids: Raid[][] - weaponKeys: GroupedWeaponKeys - meta: { [key: string]: string } + context?: PageContextObj + version: AppUpdate + error: boolean + status?: ResponseStatus } -const PartyRoute: React.FC = (props: Props) => { - // Import translations - const { t } = useTranslation('common') - - // Set up router +const PartyRoute: React.FC = ({ + context, + version, + error, + status, +}: Props) => { + // Set up state to save selected tab and + // update when router changes const router = useRouter() - const locale = - router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en' - - // URL state const [selectedTab, setSelectedTab] = useState(GridType.Weapon) useEffect(() => { @@ -57,86 +54,45 @@ const PartyRoute: React.FC = (props: Props) => { } }, [router.asPath]) - // Static data + // Set the initial data from props useEffect(() => { - persistStaticData() - }, [persistStaticData]) + if (context && !error) { + appState.raids = context.raids + appState.jobs = context.jobs ? context.jobs : [] + appState.jobSkills = context.jobSkills ? context.jobSkills : [] + appState.weaponKeys = context.weaponKeys + } - function persistStaticData() { - appState.raids = props.raids - appState.jobs = props.jobs - appState.jobSkills = props.jobSkills - appState.weaponKeys = props.weaponKeys + if (status && error) { + appState.status = status + } + + appState.version = version + }, []) + + // Methods: Page component rendering + function pageHead() { + if (context && context.party && context.meta) + return } - return ( - - - - {/* HTML */} - - {generateTitle( - props.meta.element, - props.party.user?.username, - props.party.name - )} - - - + function pageError() { + if (status) return + else return
+ } - {/* OpenGraph */} - + {pageHead()} + - - - - - {/* Twitter */} - - - - - - - ) + + ) + } else return pageError() } export const getServerSidePaths = async () => { @@ -154,47 +110,28 @@ export const getServerSideProps = async ({ req, res, locale, query }: { req: Nex // Set headers for server-side requests setUserToken(req, res) - function getElement(party?: Party) { - if (party) { - const mainhand = party.weapons.find((weapon) => weapon.mainhand) - if (mainhand && mainhand.object.element === 0) { - return mainhand.element - } else { - return mainhand?.object.element - } - } else { - return 0 - } - } - - function elementEmoji(party?: Party) { - const element = getElement(party) - - if (element === 0) return '⚪' - else if (element === 1) return '🟢' - else if (element === 2) return '🔴' - else if (element === 3) return '🔵' - else if (element === 4) return '🟤' - else if (element === 5) return '🟣' - else if (element === 6) return '🟡' - else return '⚪' - } + // Fetch latest version + const version = await fetchLatestVersion() try { + // Fetch and organize raids let { raids, sortedRaids } = await api.endpoints.raids .getAll() .then((response) => organizeRaids(response.data)) - let jobs = await api.endpoints.jobs.getAll().then((response) => { - return response.data - }) + // Fetch jobs and job skills + let jobs = await api.endpoints.jobs + .getAll() + .then((response) => response.data) let jobSkills = await api.allJobSkills().then((response) => response.data) + // Fetch and organize weapon keys let weaponKeys = await api.endpoints.weapon_keys .getAll() .then((response) => groupWeaponKeys(response.data)) + // Fetch the party let party: Party | undefined = undefined if (query.party) { let response = await api.endpoints.parties.getOne({ @@ -202,26 +139,49 @@ export const getServerSideProps = async ({ req, res, locale, query }: { req: Nex }) party = response.data.party } else { - console.log('No party code') + console.error('No party code') } + // Consolidate data into context object + const context: PageContextObj = { + party: party, + jobs: jobs, + jobSkills: jobSkills, + raids: raids, + sortedRaids: sortedRaids, + weaponKeys: weaponKeys, + meta: { + element: elementEmoji(party), + }, + } + + // Pass to the page component as props return { props: { - party: party, - jobs: jobs, - jobSkills: jobSkills, - raids: raids, - sortedRaids: sortedRaids, - weaponKeys: weaponKeys, - meta: { - element: elementEmoji(party), - }, + context: context, + version: version, + error: false, ...(await serverSideTranslations(locale, ['common', 'roadmap'])), - // Will be passed to the page component as props }, } } catch (error) { - printError(error, 'axios') + // Extract the underlying Axios error + const axiosError = error as AxiosError + const response = axiosError.response + + // Pass to the page component as props + return { + props: { + context: null, + error: true, + version: version, + status: { + code: response?.status, + text: response?.statusText, + }, + ...(await serverSideTranslations(locale, ['common', 'roadmap'])), + }, + } } } diff --git a/types/index.d.ts b/types/index.d.ts index 21337840..fd93e089 100644 --- a/types/index.d.ts +++ b/types/index.d.ts @@ -72,13 +72,16 @@ interface PerpetuityObject { } interface PageContextObj { + user?: User + teams?: Party[] party?: Party - jobs: Job[] - jobSkills: JobSkill[] + jobs?: Job[] + jobSkills?: JobSkill[] raids: Raid[] sortedRaids: Raid[][] - weaponKeys: GroupedWeaponKeys - meta: { [key: string]: string } + weaponKeys?: GroupedWeaponKeys + pagination?: PaginationObject + meta?: { [key: string]: string } } interface ResponseStatus { diff --git a/utils/elementEmoji.tsx b/utils/elementEmoji.tsx new file mode 100644 index 00000000..25195603 --- /dev/null +++ b/utils/elementEmoji.tsx @@ -0,0 +1,14 @@ +import getElementForParty from './getElementForParty' + +export default function elementEmoji(party?: Party) { + const element = party ? getElementForParty(party) : 0 + + if (element === 0) return '⚪' + else if (element === 1) return '🟢' + else if (element === 2) return '🔴' + else if (element === 3) return '🔵' + else if (element === 4) return '🟤' + else if (element === 5) return '🟣' + else if (element === 6) return '🟡' + else return '⚪' +} diff --git a/utils/generateTitle.tsx b/utils/generateTitle.tsx index 70582204..ee6b9c5a 100644 --- a/utils/generateTitle.tsx +++ b/utils/generateTitle.tsx @@ -1,7 +1,7 @@ import { useTranslation } from 'next-i18next' export default function generateTitle( - element: string, + element?: string, username?: string, name?: string ) { diff --git a/utils/getElementForParty.tsx b/utils/getElementForParty.tsx new file mode 100644 index 00000000..9e2595ae --- /dev/null +++ b/utils/getElementForParty.tsx @@ -0,0 +1,8 @@ +export default function getElementForParty(party: Party) { + const mainhand = party.weapons.find((weapon) => weapon.mainhand) + if (mainhand && mainhand.object.element === 0) { + return mainhand.element + } else { + return mainhand?.object.element + } +}