272 lines
7.1 KiB
TypeScript
272 lines
7.1 KiB
TypeScript
import React, { useEffect, useState } from 'react'
|
|
import Link from 'next/link'
|
|
import { useRouter } from 'next/router'
|
|
import { useSnapshot } from 'valtio'
|
|
import { useTranslation } from 'next-i18next'
|
|
import classNames from 'classnames'
|
|
import 'fix-date'
|
|
|
|
import { accountState } from '~utils/accountState'
|
|
import { formatTimeAgo } from '~utils/timeAgo'
|
|
|
|
import Button from '~components/common/Button'
|
|
|
|
import SaveIcon from '~public/icons/Save.svg'
|
|
import ShieldIcon from '~public/icons/Shield.svg'
|
|
import styles from './index.module.scss'
|
|
|
|
interface Props {
|
|
shortcode: string
|
|
id: string
|
|
name: string
|
|
raid: Raid
|
|
grid: GridWeapon[]
|
|
user?: User
|
|
fullAuto: boolean
|
|
autoGuard: boolean
|
|
favorited: boolean
|
|
createdAt: Date
|
|
onClick: (shortcode: string) => void
|
|
onSave?: (partyId: string, favorited: boolean) => void
|
|
}
|
|
|
|
const GridRep = (props: Props) => {
|
|
const numWeapons: number = 9
|
|
|
|
const { account } = useSnapshot(accountState)
|
|
|
|
const router = useRouter()
|
|
const { t } = useTranslation('common')
|
|
const locale =
|
|
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
|
|
|
const [mainhand, setMainhand] = useState<Weapon>()
|
|
const [weapons, setWeapons] = useState<GridArray<Weapon>>({})
|
|
const [grid, setGrid] = useState<GridArray<GridWeapon>>({})
|
|
|
|
const titleClass = classNames({
|
|
empty: !props.name,
|
|
})
|
|
|
|
const raidClass = classNames({
|
|
[styles.raid]: true,
|
|
[styles.empty]: !props.raid,
|
|
})
|
|
|
|
const userClass = classNames({
|
|
[styles.user]: true,
|
|
[styles.empty]: !props.user,
|
|
})
|
|
|
|
const mainhandClasses = classNames({
|
|
[styles.weapon]: true,
|
|
[styles.mainhand]: true,
|
|
})
|
|
|
|
const weaponClasses = classNames({
|
|
[styles.weapon]: true,
|
|
[styles.grid]: true,
|
|
})
|
|
|
|
useEffect(() => {
|
|
const newWeapons = Array(numWeapons)
|
|
const gridWeapons = Array(numWeapons)
|
|
|
|
let foundMainhand = false
|
|
for (const [key, value] of Object.entries(props.grid)) {
|
|
if (value.position == -1) {
|
|
setMainhand(value.object)
|
|
foundMainhand = true
|
|
} else if (!value.mainhand && value.position != null) {
|
|
newWeapons[value.position] = value.object
|
|
gridWeapons[value.position] = value
|
|
}
|
|
}
|
|
|
|
if (!foundMainhand) {
|
|
setMainhand(undefined)
|
|
}
|
|
|
|
setWeapons(newWeapons)
|
|
setGrid(gridWeapons)
|
|
}, [props.grid])
|
|
|
|
function navigate() {
|
|
props.onClick(props.shortcode)
|
|
}
|
|
|
|
function generateMainhandImage() {
|
|
let url = ''
|
|
|
|
if (mainhand) {
|
|
const weapon = Object.values(props.grid).find(
|
|
(w) => w && w.object.id === mainhand.id
|
|
)
|
|
|
|
if (mainhand.element == 0 && weapon && weapon.element) {
|
|
url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${mainhand.granblue_id}_${weapon.element}.jpg`
|
|
} else {
|
|
url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-main/${mainhand.granblue_id}.jpg`
|
|
}
|
|
}
|
|
|
|
return mainhand && props.grid[0] ? (
|
|
<img alt={mainhand.name[locale]} src={url} />
|
|
) : (
|
|
''
|
|
)
|
|
}
|
|
|
|
function generateGridImage(position: number) {
|
|
let url = ''
|
|
|
|
const weapon = weapons[position]
|
|
const gridWeapon = grid[position]
|
|
|
|
if (weapon && gridWeapon) {
|
|
if (weapon.element == 0 && gridWeapon.element) {
|
|
url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapon.granblue_id}_${gridWeapon.element}.jpg`
|
|
} else {
|
|
url = `${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapon.granblue_id}.jpg`
|
|
}
|
|
}
|
|
|
|
return weapons[position] ? (
|
|
<img alt={weapons[position]?.name[locale]} src={url} />
|
|
) : (
|
|
''
|
|
)
|
|
}
|
|
|
|
function sendSaveData() {
|
|
if (props.onSave) props.onSave(props.id, props.favorited)
|
|
}
|
|
|
|
const userImage = () => {
|
|
if (props.user && props.user.avatar) {
|
|
return (
|
|
<img
|
|
alt={props.user.avatar.picture}
|
|
className={`profile ${props.user.avatar.element}`}
|
|
srcSet={`/profile/${props.user.avatar.picture}.png,
|
|
/profile/${props.user.avatar.picture}@2x.png 2x`}
|
|
src={`/profile/${props.user.avatar.picture}.png`}
|
|
/>
|
|
)
|
|
} else
|
|
return (
|
|
<img
|
|
alt={t('no_user')}
|
|
className={`profile anonymous`}
|
|
srcSet={`/profile/npc.png,
|
|
/profile/npc@2x.png 2x`}
|
|
src={`/profile/npc.png`}
|
|
/>
|
|
)
|
|
}
|
|
|
|
const attribution = () => (
|
|
<span className={userClass}>
|
|
{userImage()}
|
|
{props.user ? props.user.username : t('no_user')}
|
|
</span>
|
|
)
|
|
|
|
function fullAutoString() {
|
|
const fullAutoElement = (
|
|
<span className={styles.fullAuto}>
|
|
{` · ${t('party.details.labels.full_auto')}`}
|
|
</span>
|
|
)
|
|
|
|
const autoGuardElement = (
|
|
<span className={styles.autoGuard}>
|
|
<ShieldIcon />
|
|
</span>
|
|
)
|
|
|
|
return (
|
|
<div className={styles.auto}>
|
|
{fullAutoElement}
|
|
{props.autoGuard ? autoGuardElement : ''}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const detailsWithUsername = (
|
|
<div className={styles.details}>
|
|
<div className={styles.top}>
|
|
<div className={styles.info}>
|
|
<h2 className={titleClass}>
|
|
{props.name ? props.name : t('no_title')}
|
|
</h2>
|
|
<div className={styles.properties}>
|
|
<span className={raidClass}>
|
|
{props.raid ? props.raid.name[locale] : t('no_raid')}
|
|
</span>
|
|
{props.fullAuto && (
|
|
<span className={styles.fullAuto}>
|
|
{` · ${t('party.details.labels.full_auto')}`}
|
|
</span>
|
|
)}
|
|
{props.raid && props.raid.group.extra && (
|
|
<span className={styles.extra}>{` · EX`}</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
{account.authorized &&
|
|
((props.user && account.user && account.user.id !== props.user.id) ||
|
|
!props.user) ? (
|
|
<Link href="#">
|
|
<Button
|
|
className={classNames({
|
|
save: true,
|
|
saved: props.favorited,
|
|
})}
|
|
leftAccessoryIcon={<SaveIcon className="stroke" />}
|
|
active={props.favorited}
|
|
bound={true}
|
|
size="small"
|
|
onClick={sendSaveData}
|
|
/>
|
|
</Link>
|
|
) : (
|
|
''
|
|
)}
|
|
</div>
|
|
<div className={styles.attributed}>
|
|
{attribution()}
|
|
|
|
<time
|
|
className={styles.lastUpdated}
|
|
dateTime={props.createdAt.toISOString()}
|
|
>
|
|
{formatTimeAgo(props.createdAt, locale)}
|
|
</time>
|
|
</div>
|
|
</div>
|
|
)
|
|
|
|
return (
|
|
<Link legacyBehavior href={`/p/${props.shortcode}`}>
|
|
<a className={styles.gridRep}>
|
|
{detailsWithUsername}
|
|
<div className={styles.weaponGrid}>
|
|
<div className={mainhandClasses}>{generateMainhandImage()}</div>
|
|
|
|
<ul className={styles.weapons}>
|
|
{Array.from(Array(numWeapons)).map((x, i) => {
|
|
return (
|
|
<li key={`${props.shortcode}-${i}`} className={weaponClasses}>
|
|
{generateGridImage(i)}
|
|
</li>
|
|
)
|
|
})}
|
|
</ul>
|
|
</div>
|
|
</a>
|
|
</Link>
|
|
)
|
|
}
|
|
|
|
export default GridRep
|