Fix typings and output with remixes

This commit is contained in:
Justin Edmund 2023-07-07 14:32:03 -07:00
parent d0535d6031
commit 973e7f34da
22 changed files with 246 additions and 207 deletions

View file

@ -20,12 +20,12 @@ interface Props {
shortcode: string shortcode: string
id: string id: string
name: string name: string
raid?: Raid raid: Raid | null
weapons: { weapons: {
mainWeapon?: GridWeapon mainWeapon: GridWeapon | null
allWeapons: GridArray<GridWeapon> allWeapons: GridArray<GridWeapon> | null
} } | null
user?: User user: User | null
fullAuto: boolean fullAuto: boolean
autoGuard: boolean autoGuard: boolean
favorited: boolean favorited: boolean

View file

@ -16,6 +16,8 @@ import type { DetailsObject, JobSkillObject, SearchableObject } from '~types'
import api from '~utils/api' import api from '~utils/api'
import { appState } from '~utils/appState' import { appState } from '~utils/appState'
import * as CharacterTransformer from '~transformers/CharacterTransformer'
import * as GridCharacterTransformer from '~transformers/GridCharacterTransformer'
import styles from './index.module.scss' import styles from './index.module.scss'
@ -121,8 +123,10 @@ const CharacterGrid = (props: Props) => {
async function handleCharacterResponse(data: any) { async function handleCharacterResponse(data: any) {
if (data.hasOwnProperty('conflicts')) { if (data.hasOwnProperty('conflicts')) {
setIncoming(data.incoming) setIncoming(CharacterTransformer.toObject(data.incoming))
setConflicts(data.conflicts) setConflicts(
data.conflicts.map((c: any) => GridCharacterTransformer.toObject(c))
)
setPosition(data.position) setPosition(data.position)
setModalOpen(true) setModalOpen(true)
} else { } else {
@ -145,7 +149,8 @@ const CharacterGrid = (props: Props) => {
}) })
} }
function storeGridCharacter(gridCharacter: GridCharacter) { function storeGridCharacter(data: any) {
const gridCharacter = GridCharacterTransformer.toObject(data)
appState.party.grid.characters[gridCharacter.position] = gridCharacter appState.party.grid.characters[gridCharacter.position] = gridCharacter
} }
@ -164,7 +169,7 @@ const CharacterGrid = (props: Props) => {
// Remove conflicting characters from state // Remove conflicting characters from state
conflicts.forEach( conflicts.forEach(
(c) => (appState.party.grid.characters[c.position] = undefined) (c) => (appState.party.grid.characters[c.position] = null)
) )
// Reset conflict // Reset conflict
@ -186,7 +191,7 @@ const CharacterGrid = (props: Props) => {
async function removeCharacter(id: string) { async function removeCharacter(id: string) {
try { try {
const response = await api.endpoints.grid_characters.destroy({ id: id }) const response = await api.endpoints.grid_characters.destroy({ id: id })
appState.party.grid.characters[response.data.position] = undefined appState.party.grid.characters[response.data.position] = null
} catch (error) { } catch (error) {
console.error(error) console.error(error)
} }

View file

@ -40,6 +40,7 @@ const CharacterResult = (props: Props) => {
flb={character.uncap.flb} flb={character.uncap.flb}
ulb={character.uncap.ulb} ulb={character.uncap.ulb}
special={character.special} special={character.special}
transcendenceStage={character.uncap.ulb ? 5 : 0}
/> />
<div className={styles.tags}> <div className={styles.tags}>
<WeaponLabelIcon labelType={character.element.slug} /> <WeaponLabelIcon labelType={character.element.slug} />

View file

@ -22,6 +22,7 @@ import UncapIndicator from '~components/uncap/UncapIndicator'
import api from '~utils/api' import api from '~utils/api'
import { appState } from '~utils/appState' import { appState } from '~utils/appState'
import { ElementMap } from '~utils/elements' import { ElementMap } from '~utils/elements'
import * as GridCharacterTransformer from '~transformers/GridCharacterTransformer'
import PlusIcon from '~public/icons/Add.svg' import PlusIcon from '~public/icons/Add.svg'
import SettingsIcon from '~public/icons/Settings.svg' import SettingsIcon from '~public/icons/Settings.svg'
@ -37,7 +38,7 @@ import type {
import styles from './index.module.scss' import styles from './index.module.scss'
interface Props { interface Props {
gridCharacter?: GridCharacter gridCharacter: GridCharacter | null
position: number position: number
editable: boolean editable: boolean
removeCharacter: (id: string) => void removeCharacter: (id: string) => void
@ -145,7 +146,9 @@ const CharacterUnit = ({
// Save the server's response to state // Save the server's response to state
function processResult(response: AxiosResponse) { function processResult(response: AxiosResponse) {
const gridCharacter: GridCharacter = response.data const gridCharacter: GridCharacter = GridCharacterTransformer.toObject(
response.data
)
let character = cloneDeep(gridCharacter) let character = cloneDeep(gridCharacter)
if (character.mastery.overMastery) { if (character.mastery.overMastery) {
@ -159,7 +162,7 @@ const CharacterUnit = ({
character.mastery.overMastery = overMastery character.mastery.overMastery = overMastery
} }
appState.grid.characters[gridCharacter.position] = character appState.party.grid.characters[gridCharacter.position] = character
} }
function processError(error: any) { function processError(error: any) {
@ -188,7 +191,11 @@ const CharacterUnit = ({
// Change the image based on the uncap level // Change the image based on the uncap level
let suffix = '01' let suffix = '01'
if (gridCharacter.transcendenceStep > 0) suffix = '04' if (
gridCharacter.transcendenceStep &&
gridCharacter.transcendenceStep > 0
)
suffix = '04'
else if (gridCharacter.uncapLevel >= 5) suffix = '03' else if (gridCharacter.uncapLevel >= 5) suffix = '03'
else if (gridCharacter.uncapLevel > 2) suffix = '02' else if (gridCharacter.uncapLevel > 2) suffix = '02'

View file

@ -18,6 +18,7 @@ import { accountState } from '~utils/accountState'
import { appState, initialAppState } from '~utils/appState' import { appState, initialAppState } from '~utils/appState'
import { getLocalId } from '~utils/localId' import { getLocalId } from '~utils/localId'
import { GridType } from '~utils/enums' import { GridType } from '~utils/enums'
import * as PartyTransformer from '~transformers/PartyTransformer'
import { retrieveCookies } from '~utils/retrieveCookies' import { retrieveCookies } from '~utils/retrieveCookies'
import { setEditKey, storeEditKey, unsetEditKey } from '~utils/userToken' import { setEditKey, storeEditKey, unsetEditKey } from '~utils/userToken'
@ -220,11 +221,11 @@ const Party = (props: Props) => {
api api
.remix({ shortcode: props.party.shortcode, body: body }) .remix({ shortcode: props.party.shortcode, body: body })
.then((response) => { .then((response) => {
const remix = response.data.party const remix = PartyTransformer.toObject(response.data.party)
// Store the edit key in local storage // Store the edit key in local storage
if (remix.edit_key) { if (remix.editKey) {
storeEditKey(remix.id, remix.edit_key) storeEditKey(remix.id, remix.editKey)
setEditKey(remix.id, remix.user) setEditKey(remix.id, remix.user)
} }
@ -275,30 +276,31 @@ const Party = (props: Props) => {
appState.party.protagonist.ultimateMastery = appState.party.protagonist.ultimateMastery =
team.protagonist.ultimate_mastery team.protagonist.ultimate_mastery
appState.party.details.chargeAttack = team.charge_attack appState.party.details.chargeAttack = team.charge_attack
appState.party.details.fullAuto = team.full_auto appState.party.details.fullAuto = team.details.full_auto
appState.party.details.autoGuard = team.auto_guard appState.party.details.autoGuard = team.details.auto_guard
appState.party.details.autoSummon = team.auto_summon appState.party.details.autoSummon = team.details.auto_summon
appState.party.details.clearTime = team.clear_time appState.party.details.clearTime = team.details.clear_time
appState.party.details.buttonCount = appState.party.details.buttonCount =
team.button_count !== null ? team.button_count : undefined team.details.button_count !== null ? team.details.button_count : undefined
appState.party.details.chainCount = appState.party.details.chainCount =
team.chain_count !== null ? team.chain_count : undefined team.details.chain_count !== null ? team.details.chain_count : undefined
appState.party.details.turnCount = appState.party.details.turnCount =
team.turn_count !== null ? team.turn_count : undefined team.details.turn_count !== null ? team.details.turn_count : undefined
appState.party.id = team.id appState.party.id = team.id
appState.party.shortcode = team.shortcode appState.party.shortcode = team.shortcode
appState.party.details.extra = team.extra appState.party.details.extra = team.details.extra
appState.party.guidebooks = team.guidebooks appState.party.guidebooks = team.guidebooks
appState.party.user = team.user appState.party.user = team.user
appState.party.social.favorited = team.favorited appState.party.social.favorited = team.social.favorited
appState.party.social.remix = team.remix appState.party.social.remix = team.social.remix
appState.party.social.remixes = team.remixes appState.party.social.remixes = team.social.remixes
appState.party.social.sourceParty = team.source_party appState.party.social.sourceParty = team.social.sourceParty
appState.party.timestamps.createdAt = team.created_at appState.party.timestamps.createdAt = team.created_at
appState.party.timestamps.updatedAt = team.updated_at appState.party.timestamps.updatedAt = team.updated_at
appState.party.grid = team.grid appState.party.grid = team.grid
console.log(team.grid)
// Store the edit key in local storage // Store the edit key in local storage
if (team.edit_key) { if (team.edit_key) {

View file

@ -16,7 +16,6 @@ import EditPartyModal from '../EditPartyModal'
import api from '~utils/api' import api from '~utils/api'
import { appState } from '~utils/appState' import { appState } from '~utils/appState'
import { youtube } from '~utils/youtube'
import type { DetailsObject } from 'types' import type { DetailsObject } from 'types'
@ -39,10 +38,7 @@ const PartyFooter = (props: Props) => {
const { t } = useTranslation('common') const { t } = useTranslation('common')
const router = useRouter() const router = useRouter()
const { party: partySnapshot } = useSnapshot(appState) const { party } = useSnapshot(appState)
const youtubeUrlRegex =
/(?:https:\/\/www\.youtube\.com\/watch\?v=|https:\/\/youtu\.be\/)([\w-]+)/g
// State: Component // State: Component
const [currentSegment, setCurrentSegment] = useState(0) const [currentSegment, setCurrentSegment] = useState(0)
@ -55,69 +51,19 @@ const PartyFooter = (props: Props) => {
const [sanitizedDescription, setSanitizedDescription] = useState('') const [sanitizedDescription, setSanitizedDescription] = useState('')
useEffect(() => { useEffect(() => {
if (partySnapshot.description) { if (party.description) {
const purified = DOMPurify.sanitize(partySnapshot.description) const purified = DOMPurify.sanitize(party.description)
setSanitizedDescription(purified) setSanitizedDescription(purified)
} else { } else {
setSanitizedDescription('') setSanitizedDescription('')
} }
}, [partySnapshot.description]) }, [party.description])
// Extract the video IDs from the description
// const videoIds = extractYoutubeVideoIds(partySnapshot.description)
// Fetch the video titles for each ID
// const fetchPromises = videoIds.map(({ id }) => fetchYoutubeData(id))
// // Wait for all the video titles to be fetched
// Promise.all(fetchPromises).then((videoTitles) => {
// // Replace the video URLs in the description with LiteYoutubeEmbed elements
// const newDescription = reactStringReplace(
// partySnapshot.description,
// youtubeUrlRegex,
// (match, i) => (
// <LiteYouTubeEmbed
// key={`${match}-${i}`}
// id={match}
// title={videoTitles[i]}
// wrapperClass={styles.youtube}
// playerClass={styles.playerButton}
// />
// )
// )
// Update the state with the new description
async function fetchYoutubeData(videoId: string) {
return await youtube
.getVideoById(videoId, { maxResults: 1 })
.then((data) => data.items[0].snippet.localized.title)
}
// Methods: Navigation // Methods: Navigation
function goTo(shortcode?: string) { function goTo(shortcode?: string) {
if (shortcode) router.push(`/p/${shortcode}`) if (shortcode) router.push(`/p/${shortcode}`)
} }
function extractYoutubeVideoIds(text: string) {
// Initialize an array to store the video IDs
const videoIds = []
// Use the regular expression to find all the Youtube URLs in the text
let match
while ((match = youtubeUrlRegex.exec(text)) !== null) {
// Extract the video ID from the URL
const videoId = match[1]
// Add the video ID to the array, along with the character position of the URL
videoIds.push({
id: videoId,
url: match[0],
position: match.index,
})
}
// Return the array of video IDs
return videoIds
}
// Methods: Favorites // Methods: Favorites
function toggleFavorite(teamId: string, favorited: boolean) { function toggleFavorite(teamId: string, favorited: boolean) {
if (favorited) unsaveFavorite(teamId) if (favorited) unsaveFavorite(teamId)
@ -202,7 +148,7 @@ const PartyFooter = (props: Props) => {
onClick={() => setCurrentSegment(1)} onClick={() => setCurrentSegment(1)}
> >
{t('footer.remixes.label', { {t('footer.remixes.label', {
count: partySnapshot?.social.remixes?.length, count: party?.social.remixes?.length ?? 0,
})} })}
</Segment> </Segment>
</SegmentedControl> </SegmentedControl>
@ -218,7 +164,7 @@ const PartyFooter = (props: Props) => {
key={props.party?.shortcode} key={props.party?.shortcode}
/> />
)} )}
{(!partySnapshot || !partySnapshot.description) && ( {(!party || !party.description) && (
<section className={styles.noDescription}> <section className={styles.noDescription}>
<h3>{t('footer.description.empty')}</h3> <h3>{t('footer.description.empty')}</h3>
{props.editable && ( {props.editable && (
@ -242,10 +188,10 @@ const PartyFooter = (props: Props) => {
const remixesSection = ( const remixesSection = (
<section className={styles.remixes}> <section className={styles.remixes}>
{partySnapshot?.social.remixes?.length > 0 && ( {party?.social.remixes?.length > 0 && (
<GridRepCollection>{renderRemixes()}</GridRepCollection> <GridRepCollection>{renderRemixes()}</GridRepCollection>
)} )}
{partySnapshot?.social.remixes?.length === 0 && ( {party?.social.remixes?.length === 0 && (
<div className={styles.noRemixes}> <div className={styles.noRemixes}>
<h3>{t('footer.remixes.empty')}</h3> <h3>{t('footer.remixes.empty')}</h3>
<Button <Button
@ -259,7 +205,7 @@ const PartyFooter = (props: Props) => {
) )
function renderRemixes() { function renderRemixes() {
return partySnapshot?.social.remixes.map((party, i) => { return party?.social.remixes.map((party, i) => {
return ( return (
<GridRep <GridRep
id={party.id} id={party.id}
@ -290,7 +236,7 @@ const PartyFooter = (props: Props) => {
<RemixTeamAlert <RemixTeamAlert
creator={props.editable} creator={props.editable}
name={partySnapshot.name ? partySnapshot.name : t('no_title')} name={party.name ? party.name : t('no_title')}
open={remixAlertOpen} open={remixAlertOpen}
onOpenChange={handleRemixTeamAlertChange} onOpenChange={handleRemixTeamAlertChange}
remixCallback={remixTeamCallback} remixCallback={remixTeamCallback}

View file

@ -15,6 +15,12 @@ import WeaponSearchFilterBar from '~components/weapon/WeaponSearchFilterBar'
import SummonSearchFilterBar from '~components/summon/SummonSearchFilterBar' import SummonSearchFilterBar from '~components/summon/SummonSearchFilterBar'
import JobSkillSearchFilterBar from '~components/job/JobSkillSearchFilterBar' import JobSkillSearchFilterBar from '~components/job/JobSkillSearchFilterBar'
import * as WeaponTransformer from '~transformers/WeaponTransformer'
import * as SummonTransformer from '~transformers/SummonTransformer'
import * as CharacterTransformer from '~transformers/CharacterTransformer'
import * as JobSkillTransformer from '~transformers/JobSkillTransformer'
import * as GuidebookTransformer from '~transformers/GuidebookTransformer'
import CharacterResult from '~components/character/CharacterResult' import CharacterResult from '~components/character/CharacterResult'
import WeaponResult from '~components/weapon/WeaponResult' import WeaponResult from '~components/weapon/WeaponResult'
import SummonResult from '~components/summon/SummonResult' import SummonResult from '~components/summon/SummonResult'
@ -270,13 +276,14 @@ const SearchModal = (props: Props) => {
const castResults: Weapon[] = results as Weapon[] const castResults: Weapon[] = results as Weapon[]
if (castResults && Object.keys(castResults).length > 0) { if (castResults && Object.keys(castResults).length > 0) {
jsx = castResults.map((result: Weapon) => { jsx = castResults.map((result: any) => {
const weapon = WeaponTransformer.toObject(result)
return ( return (
<WeaponResult <WeaponResult
key={result.id} key={weapon.id}
data={result} data={weapon}
onClick={() => { onClick={() => {
storeRecentResult(result) storeRecentResult(weapon)
}} }}
/> />
) )
@ -291,13 +298,14 @@ const SearchModal = (props: Props) => {
const castResults: Summon[] = results as Summon[] const castResults: Summon[] = results as Summon[]
if (castResults && Object.keys(castResults).length > 0) { if (castResults && Object.keys(castResults).length > 0) {
jsx = castResults.map((result: Summon) => { jsx = castResults.map((result: any) => {
const summon = SummonTransformer.toObject(result)
return ( return (
<SummonResult <SummonResult
key={result.id} key={summon.id}
data={result} data={summon}
onClick={() => { onClick={() => {
storeRecentResult(result) storeRecentResult(summon)
}} }}
/> />
) )
@ -312,13 +320,14 @@ const SearchModal = (props: Props) => {
const castResults: Character[] = results as Character[] const castResults: Character[] = results as Character[]
if (castResults && Object.keys(castResults).length > 0) { if (castResults && Object.keys(castResults).length > 0) {
jsx = castResults.map((result: Character) => { jsx = castResults.map((result: any) => {
const character = CharacterTransformer.toObject(result)
return ( return (
<CharacterResult <CharacterResult
key={result.id} key={character.id}
data={result} data={character}
onClick={() => { onClick={() => {
storeRecentResult(result) storeRecentResult(character)
}} }}
/> />
) )
@ -334,12 +343,13 @@ const SearchModal = (props: Props) => {
const castResults: JobSkill[] = results as JobSkill[] const castResults: JobSkill[] = results as JobSkill[]
if (castResults && Object.keys(castResults).length > 0) { if (castResults && Object.keys(castResults).length > 0) {
jsx = castResults.map((result: JobSkill) => { jsx = castResults.map((result: JobSkill) => {
const jobSkill = JobSkillTransformer.toObject(result)
return ( return (
<JobSkillResult <JobSkillResult
key={result.id} key={jobSkill.id}
data={result} data={jobSkill}
onClick={() => { onClick={() => {
storeRecentResult(result) storeRecentResult(jobSkill)
}} }}
/> />
) )
@ -355,12 +365,13 @@ const SearchModal = (props: Props) => {
const castResults: Guidebook[] = results as Guidebook[] const castResults: Guidebook[] = results as Guidebook[]
if (castResults && Object.keys(castResults).length > 0) { if (castResults && Object.keys(castResults).length > 0) {
jsx = castResults.map((result: Guidebook) => { jsx = castResults.map((result: Guidebook) => {
const guidebook = GuidebookTransformer.toObject(result)
return ( return (
<GuidebookResult <GuidebookResult
key={result.id} key={guidebook.id}
data={result} data={guidebook}
onClick={() => { onClick={() => {
storeRecentResult(result) storeRecentResult(guidebook)
}} }}
/> />
) )

View file

@ -14,6 +14,7 @@ import ExtraSummonsGrid from '~components/extra/ExtraSummonsGrid'
import api from '~utils/api' import api from '~utils/api'
import { appState } from '~utils/appState' import { appState } from '~utils/appState'
import * as GridSummonTransformer from '~transformers/GridSummonTransformer'
import type { DetailsObject, SearchableObject } from '~types' import type { DetailsObject, SearchableObject } from '~types'
import styles from './index.module.scss' import styles from './index.module.scss'
@ -119,11 +120,11 @@ const SummonGrid = (props: Props) => {
const position = data.meta['replaced'] const position = data.meta['replaced']
if (position == -1) { if (position == -1) {
appState.party.grid.summons.mainSummon = undefined appState.party.grid.summons.mainSummon = null
} else if (position == 6) { } else if (position == 6) {
appState.party.grid.summons.friendSummon = undefined appState.party.grid.summons.friendSummon = null
} else { } else {
appState.party.grid.summons.allSummons[position] = undefined appState.party.grid.summons.allSummons[position] = null
} }
} }
} }
@ -146,7 +147,9 @@ const SummonGrid = (props: Props) => {
}) })
} }
function storeGridSummon(gridSummon: GridSummon) { function storeGridSummon(data: GridSummon) {
const gridSummon = GridSummonTransformer.toObject(data)
if (gridSummon.position == -1) if (gridSummon.position == -1)
appState.party.grid.summons.mainSummon = gridSummon appState.party.grid.summons.mainSummon = gridSummon
else if (gridSummon.position == 6) else if (gridSummon.position == 6)
@ -360,12 +363,11 @@ const SummonGrid = (props: Props) => {
const data = response.data const data = response.data
if (data.position === -1) { if (data.position === -1) {
appState.party.grid.summons.mainSummon = undefined appState.party.grid.summons.mainSummon = null
} else if (data.position === 6) { } else if (data.position === 6) {
appState.party.grid.summons.friendSummon = undefined appState.party.grid.summons.friendSummon = null
} else { } else {
appState.party.grid.summons.allSummons[response.data.position] = appState.party.grid.summons.allSummons[response.data.position] = null
undefined
} }
} catch (error) { } catch (error) {
console.error(error) console.error(error)

View file

@ -28,8 +28,11 @@ const SummonResult = (props: Props) => {
<h5>{summon.name[locale]}</h5> <h5>{summon.name[locale]}</h5>
<UncapIndicator <UncapIndicator
type="summon" type="summon"
flb={summon.uncap.flb} ulb={summon.uncap.ulb || false}
ulb={summon.uncap.ulb} flb={summon.uncap.flb || false}
xlb={summon.uncap.xlb || false}
uncapLevel={6}
transcendenceStage={5}
special={false} special={false}
/> />
<div className={styles.tags}> <div className={styles.tags}>

View file

@ -6,6 +6,7 @@ import classNames from 'classnames'
import api from '~utils/api' import api from '~utils/api'
import { appState } from '~utils/appState' import { appState } from '~utils/appState'
import * as GridSummonTransformer from '~transformers/GridSummonTransformer'
import Alert from '~components/common/Alert' import Alert from '~components/common/Alert'
import Button from '~components/common/Button' import Button from '~components/common/Button'
@ -26,7 +27,7 @@ import SettingsIcon from '~public/icons/Settings.svg'
import styles from './index.module.scss' import styles from './index.module.scss'
interface Props { interface Props {
gridSummon: GridSummon | undefined gridSummon: GridSummon | null
unitType: 0 | 1 | 2 unitType: 0 | 1 | 2
position: number position: number
editable: boolean editable: boolean
@ -125,13 +126,23 @@ const SummonUnit = ({
// If a user sets a quick summon while one is already set, // If a user sets a quick summon while one is already set,
// the previous one will be unset. // the previous one will be unset.
const gridSummons: GridSummon[] = response.data.summons const gridSummons: GridSummon[] = response.data.summons
const mainSummon = gridSummons.find((summon) =>
GridSummonTransformer.toObject(summon)
)
const friendSummon = gridSummons.find((summon) =>
GridSummonTransformer.toObject(summon)
)
for (const gridSummon of gridSummons) { for (const gridSummon of gridSummons) {
if (gridSummon.main) { if (gridSummon.main) {
appState.grid.summons.mainSummon = gridSummon appState.grid.summons.mainSummon = mainSummon
} else if (gridSummon.friend) { } else if (gridSummon.friend) {
appState.grid.summons.friendSummon = gridSummon appState.grid.summons.friendSummon = friendSummon
} else { } else {
appState.grid.summons.allSummons[gridSummon.position] = gridSummon appState.party.grid.summons.allSummons[gridSummon.position] = gridSummon
? GridSummonTransformer.toObject(gridSummon)
: null
} }
} }
} }
@ -157,8 +168,7 @@ const SummonUnit = ({
function generateImageUrl() { function generateImageUrl() {
let imgSrc = '' let imgSrc = ''
if (gridSummon) { if (gridSummon) {
const summon = gridSummon.object! const summon = gridSummon.object
const upgradedSummons = [ const upgradedSummons = [
'2040094000', '2040094000',
'2040100000', '2040100000',
@ -179,6 +189,7 @@ const SummonUnit = ({
let suffix = '' let suffix = ''
if (gridSummon.object.uncap.xlb && gridSummon.uncapLevel == 6) { if (gridSummon.object.uncap.xlb && gridSummon.uncapLevel == 6) {
if ( if (
gridSummon.transcendenceStep &&
gridSummon.transcendenceStep >= 1 && gridSummon.transcendenceStep >= 1 &&
gridSummon.transcendenceStep < 5 gridSummon.transcendenceStep < 5
) { ) {
@ -187,7 +198,7 @@ const SummonUnit = ({
suffix = '_04' suffix = '_04'
} }
} else if ( } else if (
upgradedSummons.indexOf(summon.granblueId.toString()) != -1 && upgradedSummons.indexOf(summon.granblueId) != -1 &&
gridSummon.uncapLevel == 5 gridSummon.uncapLevel == 5
) { ) {
suffix = '_02' suffix = '_02'

View file

@ -38,7 +38,7 @@ const TranscendenceStar = ({
const starClasses = classnames({ const starClasses = classnames({
[styles.star]: true, [styles.star]: true,
[styles.immutable]: immutable, [styles.immutable]: immutable,
[styles.empty]: stage === 0, [styles.empty]: true,
[styles.stage1]: stage === 1, [styles.stage1]: stage === 1,
[styles.stage2]: stage === 2, [styles.stage2]: stage === 2,
[styles.stage3]: stage === 3, [styles.stage3]: stage === 3,

View file

@ -1,4 +1,4 @@
import React, { useState } from 'react' import React, { useEffect, useState } from 'react'
import UncapStar from '~components/uncap/UncapStar' import UncapStar from '~components/uncap/UncapStar'
import TranscendencePopover from '~components/uncap/TranscendencePopover' import TranscendencePopover from '~components/uncap/TranscendencePopover'
import TranscendenceStar from '~components/uncap/TranscendenceStar' import TranscendenceStar from '~components/uncap/TranscendenceStar'
@ -82,7 +82,11 @@ const UncapIndicator = (props: Props) => {
return props.type === 'character' || props.type === 'summon' ? ( return props.type === 'character' || props.type === 'summon' ? (
<TranscendencePopover <TranscendencePopover
open={popoverOpen} open={popoverOpen}
stage={props.transcendenceStage ? props.transcendenceStage : 0} stage={
props.transcendenceStage && props.transcendenceStage !== null
? props.transcendenceStage
: 0
}
type={props.type} type={props.type}
onOpenChange={togglePopover} onOpenChange={togglePopover}
sendValue={sendTranscendenceStage} sendValue={sendTranscendenceStage}

View file

@ -18,6 +18,7 @@ import WeaponConflictModal from '~components/weapon/WeaponConflictModal'
import api from '~utils/api' import api from '~utils/api'
import { appState } from '~utils/appState' import { appState } from '~utils/appState'
import * as GridWeaponTransformer from '~transformers/GridWeaponTransformer'
import type { DetailsObject, SearchableObject } from '~types' import type { DetailsObject, SearchableObject } from '~types'
@ -58,7 +59,7 @@ const WeaponGrid = (props: Props) => {
const [showIncompatibleAlert, setShowIncompatibleAlert] = useState(false) const [showIncompatibleAlert, setShowIncompatibleAlert] = useState(false)
// Set up state for view management // Set up state for view management
const { party, grid } = useSnapshot(appState) const { party } = useSnapshot(appState)
const [modalOpen, setModalOpen] = useState(false) const [modalOpen, setModalOpen] = useState(false)
// Set up state for conflict management // Set up state for conflict management
@ -150,10 +151,10 @@ const WeaponGrid = (props: Props) => {
const position = data.meta['replaced'] const position = data.meta['replaced']
if (position == -1) { if (position == -1) {
appState.party.grid.weapons.mainWeapon = undefined appState.party.grid.weapons.mainWeapon = null
appState.party.element = ElementMap.null appState.party.element = ElementMap.null
} else { } else {
appState.party.grid.weapons.allWeapons[position] = undefined appState.party.grid.weapons.allWeapons[position] = null
} }
} }
} }
@ -195,7 +196,8 @@ const WeaponGrid = (props: Props) => {
} }
} }
function storeGridWeapon(gridWeapon: GridWeapon) { function storeGridWeapon(data: GridWeapon) {
const gridWeapon = GridWeaponTransformer.toObject(data)
if (gridWeapon.position === -1) { if (gridWeapon.position === -1) {
appState.party.grid.weapons.mainWeapon = gridWeapon appState.party.grid.weapons.mainWeapon = gridWeapon
appState.party.element = gridWeapon.object.element appState.party.element = gridWeapon.object.element
@ -220,10 +222,10 @@ const WeaponGrid = (props: Props) => {
if ( if (
appState.party.grid.weapons.mainWeapon?.object.id === c.object.id appState.party.grid.weapons.mainWeapon?.object.id === c.object.id
) { ) {
appState.party.grid.weapons.mainWeapon = undefined appState.party.grid.weapons.mainWeapon = null
appState.party.element = ElementMap.null appState.party.element = ElementMap.null
} else { } else {
appState.party.grid.weapons.allWeapons[c.position] = undefined appState.party.grid.weapons.allWeapons[c.position] = null
} }
}) })
@ -251,10 +253,9 @@ const WeaponGrid = (props: Props) => {
const data = response.data const data = response.data
if (data.position === -1) { if (data.position === -1) {
appState.party.grid.weapons.mainWeapon = undefined appState.party.grid.weapons.mainWeapon = null
} else { } else {
appState.party.grid.weapons.allWeapons[response.data.position] = appState.party.grid.weapons.allWeapons[response.data.position] = null
undefined
} }
} catch (error) { } catch (error) {
console.error(error) console.error(error)
@ -376,7 +377,7 @@ const WeaponGrid = (props: Props) => {
return ( return (
<li className={itemClasses} key={`grid_unit_${i}`}> <li className={itemClasses} key={`grid_unit_${i}`}>
<WeaponUnit <WeaponUnit
gridWeapon={appState.party.grid.weapons.allWeapons[i]} gridWeapon={party.grid.weapons.allWeapons[i]}
editable={props.editable} editable={props.editable}
position={i} position={i}
unitType={1} unitType={1}

View file

@ -31,7 +31,7 @@ import SettingsIcon from '~public/icons/Settings.svg'
import styles from './index.module.scss' import styles from './index.module.scss'
interface Props { interface Props {
gridWeapon: GridWeapon | undefined gridWeapon: GridWeapon | null
unitType: 0 | 1 unitType: 0 | 1
position: number position: number
editable: boolean editable: boolean

View file

@ -193,7 +193,11 @@ export const getServerSideProps = async ({ req, res, locale, query }: { req: Nex
const party: Party | undefined = await api.endpoints.parties.getOne({ const party: Party | undefined = await api.endpoints.parties.getOne({
id: query.party, id: query.party,
}).then((response) => PartyTransformer.toObject(response.data.party)) }).then((response) =>
PartyTransformer.toObject(response.data.party)
)
console.log(party)
// Consolidate data into context object // Consolidate data into context object
const context: PageContextObj = { const context: PageContextObj = {

View file

@ -4,29 +4,61 @@ import * as GridCharacter from '~transformers/GridCharacterTransformer'
// Transforms API response to Party object // Transforms API response to Party object
export function toObject(data: any): Grid { export function toObject(data: any): Grid {
const mainSummon = data.summons.find((summon: any) => summon.main === true) console.log('----- GridTransformer.tsx -----')
const friendSummon = data.summons.find( console.log(data.summons, data.characters)
(summon: any) => summon.friend === true console.log('----- End GridTransformer.tsx -----')
)
const mainWeapon = data.weapons.find( const mainSummon = data.summons
(weapon: any) => weapon.mainhand === true ? data.summons.find((summon: any) => summon.main === true)
) : null
const friendSummon = data.summons
? data.summons.find((summon: any) => summon.friend === true)
: null
const allSummons = data.summons
? removeItem(data.summons, [mainSummon, friendSummon])
: null
const mainWeapon = data.weapons
? data.weapons.find((weapon: any) => weapon.mainhand === true)
: null
return { return {
characters: data.characters.map((character: any) => characters: data.characters
GridCharacter.toObject(character) ? mapToGridArray(data.characters, GridCharacter.toObject)
), : null,
summons: { summons: {
mainSummon: mainSummon ? GridSummon.toObject(mainSummon) : null, mainSummon: mainSummon ? GridSummon.toObject(mainSummon) : null,
friendSummon: friendSummon ? GridSummon.toObject(friendSummon) : null, friendSummon: friendSummon ? GridSummon.toObject(friendSummon) : null,
allSummons: data.summons.map((summon: any) => { allSummons: allSummons
if (!summon.main && !summon.friend) return GridSummon.toObject(summon) ? mapToGridArray(allSummons, GridSummon.toObject)
}), : null,
}, },
weapons: { weapons: {
mainWeapon: mainWeapon ? GridWeapon.toObject(mainWeapon) : null, mainWeapon: mainWeapon ? GridWeapon.toObject(mainWeapon) : null,
allWeapons: data.weapons.map((weapon: any) => { allWeapons: data.weapons
if (!weapon.mainhand) return GridWeapon.toObject(weapon) ? mapToGridArray(data.weapons, GridWeapon.toObject)
}), : null,
}, },
} }
} }
function removeItem<T>(arr: Array<T>, values: T[]): Array<T> {
values.forEach((value) => {
const index = arr.indexOf(value)
if (index > -1) {
arr.splice(index, 1)
}
})
return arr
}
function mapToGridArray<T>(
arr: any[],
transformer: (data: any) => T
): GridArray<T> {
return arr.reduce(
(gridArray, item) => ({ ...gridArray, [item.position]: transformer(item) }),
{} as GridArray<T>
)
}

View file

@ -12,12 +12,16 @@ export function toObject(data: any): GridWeapon {
mainhand: data.mainhand, mainhand: data.mainhand,
uncapLevel: data.uncap_level, uncapLevel: data.uncap_level,
element: Element.toObject(data.element), element: Element.toObject(data.element),
weaponKeys: data.weapon_keys.map((key: any) => WeaponKey.toObject(key)), weaponKeys: data.weapon_keys
ax: data.ax, ? data.weapon_keys.map((key: any) => WeaponKey.toObject(key))
awakening: { : null,
type: Awakening.toObject(data.awakening.type), ax: data.ax ? data.ax : null,
level: data.awakening.awakening_level, awakening: data.awakening
}, ? {
type: Awakening.toObject(data.awakening.type),
level: data.awakening.awakening_level,
}
: null,
} }
} }

View file

@ -10,11 +10,12 @@ import * as User from './UserTransformer'
export function toObject(data: any): Party { export function toObject(data: any): Party {
return { return {
id: data.id, id: data.id,
localId: data.local_id, localId: data.local_id ? data.local_id : null,
editKey: data.edit_key ? data.edit_key : null,
name: data.name, name: data.name,
description: data.description, description: data.description ? data.description : null,
shortcode: data.shortcode, shortcode: data.shortcode,
user: User.toObject(data.user), user: data.user ? User.toObject(data.user) : null,
editable: data.editable ?? false, editable: data.editable ?? false,
grid: Grid.toObject({ grid: Grid.toObject({
characters: data.characters, characters: data.characters,
@ -26,35 +27,39 @@ export function toObject(data: any): Party {
fullAuto: data.full_auto, fullAuto: data.full_auto,
autoGuard: data.auto_guard, autoGuard: data.auto_guard,
autoSummon: data.auto_summon, autoSummon: data.auto_summon,
chargeAttack: data.charge_attack, chargeAttack: data.charge_attack ? data.charge_attack : null,
clearTime: data.clear_time, clearTime: data.clear_time ? data.clear_time : null,
buttonCount: data.button_count, buttonCount: data.button_count ? data.button_count : null,
turnCount: data.turn_count, turnCount: data.turn_count ? data.turn_count : null,
chainCount: data.chain_count, chainCount: data.chain_count ? data.chain_count : null,
}, },
protagonist: { protagonist: {
job: data.job && Job.toObject(data.job), job: data.job && Job.toObject(data.job),
skills: { skills: data.job_skills
0: data.job_skills[0] && JobSkill.toObject(data.job_skills[0]), ? {
1: data.job_skills[1] && JobSkill.toObject(data.job_skills[1]), 0: data.job_skills[0] && JobSkill.toObject(data.job_skills[0]),
2: data.job_skills[2] && JobSkill.toObject(data.job_skills[2]), 1: data.job_skills[1] && JobSkill.toObject(data.job_skills[1]),
3: data.job_skills[3] && JobSkill.toObject(data.job_skills[3]), 2: data.job_skills[2] && JobSkill.toObject(data.job_skills[2]),
}, 3: data.job_skills[3] && JobSkill.toObject(data.job_skills[3]),
masterLevel: data.master_level, }
ultimateMastery: data.ultimate_mastery, : null,
accessory: data.accessory && JobAccessory.toObject(data.accessory), masterLevel: data.master_level ? data.master_level : null,
ultimateMastery: data.ultimate_mastery ? data.ultimate_mastery : null,
accessory: data.accessory ? JobAccessory.toObject(data.accessory) : null,
}, },
social: { social: {
favorited: data.favorited, favorited: data.favorited,
remix: data.remix, remix: data.remix,
remixes: data.remixes.map((remix: any) => toObject(remix)), remixes: data.remixes
sourceParty: data.source_party && toObject(data.source_party), ? data.remixes.map((remix: any) => toObject(remix))
: [],
sourceParty: data.source_party ? toObject(data.source_party) : null,
}, },
timestamps: { timestamps: {
createdAt: data.created_at, createdAt: data.created_at,
updatedAt: data.updated_at, updatedAt: data.updated_at,
}, },
raid: data.raid && Raid.toObject(data.raid), raid: data.raid ? Raid.toObject(data.raid) : null,
guidebooks: { guidebooks: {
0: data.guidebooks[1] && Guidebook.toObject(data.guidebooks[1]), 0: data.guidebooks[1] && Guidebook.toObject(data.guidebooks[1]),
1: data.guidebooks[2] && Guidebook.toObject(data.guidebooks[2]), 1: data.guidebooks[2] && Guidebook.toObject(data.guidebooks[2]),

12
types/Grid.d.ts vendored
View file

@ -1,12 +1,12 @@
interface Grid { interface Grid {
weapons: { weapons: {
mainWeapon?: GridWeapon | undefined mainWeapon: GridWeapon | null
allWeapons: GridArray<GridWeapon> allWeapons: GridArray<GridWeapon> | null
} }
summons: { summons: {
mainSummon?: GridSummon | undefined mainSummon: GridSummon | null
friendSummon?: GridSummon | undefined friendSummon: GridSummon | null
allSummons: GridArray<GridSummon> allSummons: GridArray<GridSummon> | null
} }
characters: GridArray<GridCharacter> characters: GridArray<GridCharacter> | null
} }

View file

@ -1 +1 @@
type GridArray<T> = { [key: number]: T | undefined } type GridArray<T> = { [key: number]: T | null }

View file

@ -7,8 +7,8 @@ interface GridWeapon {
element: GranblueElement element: GranblueElement
weaponKeys?: WeaponKey[] weaponKeys?: WeaponKey[]
ax?: SimpleAxSkill[] ax?: SimpleAxSkill[]
awakening?: { awakening: {
type: Awakening type: Awakening
level: number level: number
} } | null
} }

27
types/Party.d.ts vendored
View file

@ -15,11 +15,12 @@ type GuidebookList = {
interface Party { interface Party {
id: string id: string
localId?: string localId: string | null
editKey: string | null
name: string name: string
description: string description: string
shortcode: string shortcode: string
user?: User user: User | null
editable: boolean editable: boolean
element?: GranblueElement element?: GranblueElement
grid: Grid grid: Grid
@ -28,26 +29,26 @@ interface Party {
fullAuto: boolean fullAuto: boolean
autoGuard: boolean autoGuard: boolean
autoSummon: boolean autoSummon: boolean
chargeAttack: boolean chargeAttack: boolean | null
clearTime: number clearTime: number | null
buttonCount?: number buttonCount: number | null
turnCount?: number turnCount: number | null
chainCount?: number chainCount: number | null
} }
protagonist: { protagonist: {
job?: Job job?: Job
skills: JobSkillList skills: JobSkillList | null
masterLevel?: number masterLevel: number | null
ultimateMastery?: number ultimateMastery: number | null
accessory?: JobAccessory accessory: JobAccessory | null
} }
social: { social: {
favorited: boolean favorited: boolean
sourceParty?: Party sourceParty: Party | null
remix: boolean remix: boolean
remixes: Party[] remixes: Party[]
} }
raid?: Raid raid: Raid | null
guidebooks: GuidebookList guidebooks: GuidebookList
timestamps: { timestamps: {
createdAt: string createdAt: string