Merge pull request #15 from jedmund/saving
Implement the ability to save your favorite grids
This commit is contained in:
commit
65d46f8f96
19 changed files with 408 additions and 30 deletions
|
|
@ -44,7 +44,7 @@ const BottomHeader = () => {
|
|||
|
||||
function deleteTeam(event: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
|
||||
if (appState.party.editable && appState.party.id) {
|
||||
api.endpoints.parties.destroy(appState.party.id, headers)
|
||||
api.endpoints.parties.destroy({ id: appState.party.id, params: headers })
|
||||
.then(() => {
|
||||
// Push to route
|
||||
router.push('/')
|
||||
|
|
|
|||
|
|
@ -34,6 +34,33 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.save:hover {
|
||||
color: #FF4D4D;
|
||||
|
||||
.icon svg {
|
||||
fill: #FF4D4D;
|
||||
stroke: #FF4D4D;
|
||||
}
|
||||
}
|
||||
|
||||
&.save.Active {
|
||||
color: #FF4D4D;
|
||||
|
||||
.icon svg {
|
||||
fill: #FF4D4D;
|
||||
stroke: #FF4D4D;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: darken(#FF4D4D, 30);
|
||||
|
||||
.icon svg {
|
||||
fill: darken(#FF4D4D, 30);
|
||||
stroke: darken(#FF4D4D, 30);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.modal:hover {
|
||||
background: $grey-90;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import CrossIcon from '~public/icons/Cross.svg'
|
|||
import EditIcon from '~public/icons/Edit.svg'
|
||||
import LinkIcon from '~public/icons/Link.svg'
|
||||
import MenuIcon from '~public/icons/Menu.svg'
|
||||
import SaveIcon from '~public/icons/Save.svg'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
|
|
@ -63,6 +64,10 @@ class Button extends React.Component<Props, State> {
|
|||
icon = <span className='icon'>
|
||||
<EditIcon />
|
||||
</span>
|
||||
} else if (this.props.icon === 'save') {
|
||||
icon = <span className='icon stroke'>
|
||||
<SaveIcon />
|
||||
</span>
|
||||
}
|
||||
|
||||
const classes = classNames({
|
||||
|
|
@ -70,12 +75,14 @@ class Button extends React.Component<Props, State> {
|
|||
'Active': this.props.active,
|
||||
'btn-pressed': this.state.isPressed,
|
||||
'btn-disabled': this.props.disabled,
|
||||
'save': this.props.icon === 'save',
|
||||
'destructive': this.props.type == ButtonType.Destructive
|
||||
})
|
||||
|
||||
return <button className={classes} disabled={this.props.disabled} onClick={this.props.click}>
|
||||
{icon}
|
||||
<span className='text'>{this.props.children}</span>
|
||||
{ (this.props.type != ButtonType.IconOnly) ?
|
||||
<span className='text'>{this.props.children}</span> : '' }
|
||||
</button>
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ const CharacterGrid = (props: Props) => {
|
|||
|
||||
// Methods: Fetching an object from the server
|
||||
async function fetchGrid(shortcode: string) {
|
||||
return api.endpoints.parties.getOneWithObject({ id: shortcode, object: 'characters' })
|
||||
return api.endpoints.parties.getOneWithObject({ id: shortcode, object: 'characters', params: headers })
|
||||
.then(response => processResult(response))
|
||||
.catch(error => processError(error))
|
||||
}
|
||||
|
|
@ -78,6 +78,8 @@ const CharacterGrid = (props: Props) => {
|
|||
|
||||
// Store the important party and state-keeping values
|
||||
appState.party.id = party.id
|
||||
appState.party.user = party.user
|
||||
appState.party.favorited = party.favorited
|
||||
|
||||
setFound(true)
|
||||
setLoading(false)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@
|
|||
|
||||
&:hover {
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
|
||||
h2, .Grid {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.Grid .weapon {
|
||||
box-shadow: inset 0 0 0 1px $grey-80;
|
||||
|
|
@ -68,6 +71,30 @@
|
|||
}
|
||||
}
|
||||
|
||||
.top {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: $unit / 2;
|
||||
align-items: center;
|
||||
|
||||
.info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
gap: $unit / 2;
|
||||
}
|
||||
|
||||
button svg {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
button.Active {
|
||||
background: $grey-90;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
|
|
|||
|
|
@ -1,25 +1,35 @@
|
|||
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useSnapshot } from 'valtio'
|
||||
import classNames from 'classnames'
|
||||
|
||||
import { accountState } from '~utils/accountState'
|
||||
import { formatTimeAgo } from '~utils/timeAgo'
|
||||
|
||||
import Button from '~components/Button'
|
||||
import { ButtonType } from '~utils/enums'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
shortcode: string
|
||||
id: string
|
||||
name: string
|
||||
raid: Raid
|
||||
grid: GridWeapon[]
|
||||
user?: User
|
||||
favorited: boolean
|
||||
createdAt: Date
|
||||
displayUser?: boolean | false
|
||||
onClick: (shortcode: string) => void
|
||||
onSave: (partyId: string, favorited: boolean) => void
|
||||
}
|
||||
|
||||
const GridRep = (props: Props) => {
|
||||
const numWeapons: number = 9
|
||||
|
||||
const { account } = useSnapshot(accountState)
|
||||
|
||||
const [mainhand, setMainhand] = useState<Weapon>()
|
||||
const [weapons, setWeapons] = useState<GridArray<Weapon>>({})
|
||||
|
||||
|
|
@ -64,6 +74,10 @@ const GridRep = (props: Props) => {
|
|||
<img alt={weapons[position]?.name.en} src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/weapon-grid/${weapons[position]?.granblue_id}.jpg`} /> : ''
|
||||
}
|
||||
|
||||
function sendSaveData() {
|
||||
props.onSave(props.id, props.favorited)
|
||||
}
|
||||
|
||||
const userImage = () => {
|
||||
if (props.user)
|
||||
return (
|
||||
|
|
@ -80,7 +94,7 @@ const GridRep = (props: Props) => {
|
|||
|
||||
const details = (
|
||||
<div className="Details">
|
||||
<h2 className={titleClass}>{ (props.name) ? props.name : 'Untitled' }</h2>
|
||||
<h2 className={titleClass} onClick={navigate}>{ (props.name) ? props.name : 'Untitled' }</h2>
|
||||
<div className="bottom">
|
||||
<div className={raidClass}>{ (props.raid) ? props.raid.name.en : 'No raid set' }</div>
|
||||
<time className="last-updated" dateTime={props.createdAt.toISOString()}>{formatTimeAgo(props.createdAt, 'en-us')}</time>
|
||||
|
|
@ -90,8 +104,19 @@ const GridRep = (props: Props) => {
|
|||
|
||||
const detailsWithUsername = (
|
||||
<div className="Details">
|
||||
<h2 className={titleClass}>{ (props.name) ? props.name : 'Untitled' }</h2>
|
||||
<div className={raidClass}>{ (props.raid) ? props.raid.name.en : 'No raid set' }</div>
|
||||
<div className="top">
|
||||
<div className="info">
|
||||
<h2 className={titleClass} onClick={navigate}>{ (props.name) ? props.name : 'Untitled' }</h2>
|
||||
<div className={raidClass}>{ (props.raid) ? props.raid.name.en : 'No raid set' }</div>
|
||||
</div>
|
||||
{ (!props.user || (account.user && account.user.id !== props.user.id)) ?
|
||||
<Button
|
||||
active={props.favorited}
|
||||
icon="save"
|
||||
type={ButtonType.IconOnly}
|
||||
click={sendSaveData}
|
||||
/> : ''}
|
||||
</div>
|
||||
<div className="bottom">
|
||||
<div className={userClass}>
|
||||
{ userImage() }
|
||||
|
|
@ -103,9 +128,9 @@ const GridRep = (props: Props) => {
|
|||
)
|
||||
|
||||
return (
|
||||
<div className="GridRep" onClick={navigate}>
|
||||
<div className="GridRep">
|
||||
{ (props.displayUser) ? detailsWithUsername : details}
|
||||
<div className="Grid">
|
||||
<div className="Grid" onClick={navigate}>
|
||||
<div className="weapon grid_mainhand">
|
||||
{generateMainhandImage()}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
bottom: $unit * 2;
|
||||
}
|
||||
|
||||
#right {
|
||||
#right > div {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ const HeaderMenu = (props: Props) => {
|
|||
<Link href={`/${props.username}` || ''}>{props.username}</Link>
|
||||
</li>
|
||||
<li className="MenuItem">
|
||||
<Link href={`/${props.username}/saved` || ''}>Saved</Link>
|
||||
<Link href={`/saved` || ''}>Saved</Link>
|
||||
</li>
|
||||
</div>
|
||||
<div className="MenuGroup">
|
||||
|
|
|
|||
|
|
@ -117,13 +117,15 @@ const Party = (props: Props) => {
|
|||
|
||||
// Methods: Fetch party details
|
||||
function fetchDetails(shortcode: string) {
|
||||
return api.endpoints.parties.getOne({ id: shortcode })
|
||||
return api.endpoints.parties.getOne({ id: shortcode, params: headers })
|
||||
.then(response => processResult(response))
|
||||
.catch(error => processError(error))
|
||||
}
|
||||
|
||||
function processResult(response: AxiosResponse) {
|
||||
appState.party.id = response.data.party.id
|
||||
appState.party.user = response.data.party.user
|
||||
appState.party.favorited = response.data.party.favorited
|
||||
|
||||
// Store the party's user-generated details
|
||||
appState.party.name = response.data.party.name
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ const SummonGrid = (props: Props) => {
|
|||
|
||||
// Methods: Fetching an object from the server
|
||||
async function fetchGrid(shortcode: string) {
|
||||
return api.endpoints.parties.getOneWithObject({ id: shortcode, object: 'summons' })
|
||||
return api.endpoints.parties.getOneWithObject({ id: shortcode, object: 'summons', params: headers })
|
||||
.then(response => processResult(response))
|
||||
.catch(error => processError(error))
|
||||
}
|
||||
|
|
@ -88,6 +88,8 @@ const SummonGrid = (props: Props) => {
|
|||
|
||||
// Store the important party and state-keeping values
|
||||
appState.party.id = party.id
|
||||
appState.party.user = party.user
|
||||
appState.party.favorited = party.favorited
|
||||
|
||||
setFound(true)
|
||||
setLoading(false)
|
||||
|
|
|
|||
|
|
@ -44,8 +44,7 @@ const SummonUnit = (props: Props) => {
|
|||
'2040094000', '2040100000', '2040080000', '2040098000',
|
||||
'2040090000', '2040084000', '2040003000', '2040056000'
|
||||
]
|
||||
|
||||
console.log(`${summon.granblue_id} ${summon.name.en} ${props.gridSummon.uncap_level} ${upgradedSummons.indexOf(summon.granblue_id.toString())}`)
|
||||
|
||||
let suffix = ''
|
||||
if (upgradedSummons.indexOf(summon.granblue_id.toString()) != -1 && props.gridSummon.uncap_level == 5)
|
||||
suffix = '_02'
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import { useRouter } from 'next/router'
|
|||
import clonedeep from 'lodash.clonedeep'
|
||||
import { useSnapshot } from 'valtio'
|
||||
|
||||
import api from '~utils/api'
|
||||
import { accountState } from '~utils/accountState'
|
||||
import { appState, initialAppState } from '~utils/appState'
|
||||
|
||||
|
|
@ -12,9 +13,14 @@ import Button from '~components/Button'
|
|||
import HeaderMenu from '~components/HeaderMenu'
|
||||
|
||||
const TopHeader = () => {
|
||||
// Cookies
|
||||
const [cookies, _, removeCookie] = useCookies(['user'])
|
||||
const headers = (cookies.user != null) ? {
|
||||
'Authorization': `Bearer ${cookies.user.access_token}`
|
||||
} : {}
|
||||
|
||||
const accountSnap = useSnapshot(accountState)
|
||||
const { account } = useSnapshot(accountState)
|
||||
const { party } = useSnapshot(appState)
|
||||
const router = useRouter()
|
||||
|
||||
function copyToClipboard() {
|
||||
|
|
@ -53,21 +59,60 @@ const TopHeader = () => {
|
|||
return false
|
||||
}
|
||||
|
||||
function toggleFavorite() {
|
||||
if (party.favorited)
|
||||
unsaveFavorite()
|
||||
else
|
||||
saveFavorite()
|
||||
}
|
||||
|
||||
function saveFavorite() {
|
||||
if (party.id)
|
||||
api.saveTeam({ id: party.id, params: headers })
|
||||
.then((response) => {
|
||||
if (response.status == 201)
|
||||
appState.party.favorited = true
|
||||
})
|
||||
else
|
||||
console.error("Failed to save team: No party ID")
|
||||
}
|
||||
|
||||
function unsaveFavorite() {
|
||||
if (party.id)
|
||||
api.unsaveTeam({ id: party.id, params: headers })
|
||||
.then((response) => {
|
||||
if (response.status == 200)
|
||||
appState.party.favorited = false
|
||||
})
|
||||
else
|
||||
console.error("Failed to unsave team: No party ID")
|
||||
}
|
||||
|
||||
const leftNav = () => {
|
||||
return (
|
||||
<div className="dropdown">
|
||||
<Button icon="menu">Menu</Button>
|
||||
{ (accountSnap.account.user) ?
|
||||
<HeaderMenu authenticated={accountSnap.account.authorized} username={accountSnap.account.user.username} logout={logout} /> :
|
||||
<HeaderMenu authenticated={accountSnap.account.authorized} />
|
||||
{ (account.user) ?
|
||||
<HeaderMenu authenticated={account.authorized} username={account.user.username} logout={logout} /> :
|
||||
<HeaderMenu authenticated={account.authorized} />
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const saveButton = () => {
|
||||
if (party.favorited)
|
||||
return (<Button icon="save" active={true} click={toggleFavorite}>Saved</Button>)
|
||||
else
|
||||
return (<Button icon="save" click={toggleFavorite}>Save</Button>)
|
||||
}
|
||||
|
||||
const rightNav = () => {
|
||||
return (
|
||||
<div>
|
||||
{ (router.route === '/p/[party]' && account.user && (!party.user || party.user.id !== account.user.id)) ?
|
||||
saveButton() : ''
|
||||
}
|
||||
{ (router.route === '/p/[party]') ?
|
||||
<Button icon="link" click={copyToClipboard}>Copy link</Button> : ''
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ const WeaponGrid = (props: Props) => {
|
|||
|
||||
// Methods: Fetching an object from the server
|
||||
async function fetchGrid(shortcode: string) {
|
||||
return api.endpoints.parties.getOneWithObject({ id: shortcode, object: 'weapons' })
|
||||
return api.endpoints.parties.getOneWithObject({ id: shortcode, object: 'weapons', params: headers })
|
||||
.then(response => processResult(response))
|
||||
.catch(error => processError(error))
|
||||
}
|
||||
|
|
@ -85,7 +85,8 @@ const WeaponGrid = (props: Props) => {
|
|||
// Store the important party and state-keeping values
|
||||
appState.party.id = party.id
|
||||
appState.party.extra = party.extra
|
||||
|
||||
appState.party.user = party.user
|
||||
appState.party.favorited = party.favorited
|
||||
|
||||
setFound(true)
|
||||
setLoading(false)
|
||||
|
|
|
|||
161
pages/saved.tsx
Normal file
161
pages/saved.tsx
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import clonedeep from 'lodash.clonedeep'
|
||||
|
||||
import api from '~utils/api'
|
||||
|
||||
import GridRep from '~components/GridRep'
|
||||
import GridRepCollection from '~components/GridRepCollection'
|
||||
import FilterBar from '~components/FilterBar'
|
||||
|
||||
const SavedRoute: React.FC = () => {
|
||||
const router = useRouter()
|
||||
|
||||
// Cookies
|
||||
const [cookies, _] = useCookies(['user'])
|
||||
const headers = (cookies.user != null) ? {
|
||||
'Authorization': `Bearer ${cookies.user.access_token}`
|
||||
} : {}
|
||||
|
||||
const [found, setFound] = useState(false)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [scrolled, setScrolled] = useState(false)
|
||||
const [parties, setParties] = useState<Party[]>([])
|
||||
|
||||
useEffect(() => {
|
||||
console.log(`Fetching favorite teams...`)
|
||||
fetchTeams()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("scroll", handleScroll)
|
||||
return () => window.removeEventListener("scroll", handleScroll);
|
||||
}, [])
|
||||
|
||||
async function fetchTeams(element?: number, raid?: string, recency?: number) {
|
||||
const params = {
|
||||
params: {
|
||||
element: (element && element >= 0) ? element : undefined,
|
||||
raid: (raid && raid != '0') ? raid : undefined,
|
||||
recency: (recency && recency > 0) ? recency : undefined
|
||||
},
|
||||
headers: {
|
||||
'Authorization': `Bearer ${cookies.user.access_token}`
|
||||
}
|
||||
}
|
||||
|
||||
api.savedTeams(params)
|
||||
.then(response => {
|
||||
const parties: Party[] = response.data
|
||||
setParties(parties.map((p: any) => p.party).sort((a, b) => (a.created_at > b.created_at) ? -1 : 1))
|
||||
})
|
||||
.then(() => {
|
||||
setFound(true)
|
||||
setLoading(false)
|
||||
})
|
||||
.catch(error => {
|
||||
if (error.response != null) {
|
||||
if (error.response.status == 404) {
|
||||
setFound(false)
|
||||
}
|
||||
} else {
|
||||
console.error(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function toggleFavorite(teamId: string, favorited: boolean) {
|
||||
if (favorited)
|
||||
unsaveFavorite(teamId)
|
||||
else
|
||||
saveFavorite(teamId)
|
||||
}
|
||||
|
||||
function saveFavorite(teamId: string) {
|
||||
api.saveTeam({ id: teamId, params: headers })
|
||||
.then((response) => {
|
||||
if (response.status == 201) {
|
||||
const index = parties.findIndex(p => p.id === teamId)
|
||||
const party = parties[index]
|
||||
|
||||
party.favorited = true
|
||||
|
||||
let clonedParties = clonedeep(parties)
|
||||
clonedParties[index] = party
|
||||
|
||||
setParties(clonedParties)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function unsaveFavorite(teamId: string) {
|
||||
api.unsaveTeam({ id: teamId, params: headers })
|
||||
.then((response) => {
|
||||
if (response.status == 200) {
|
||||
const index = parties.findIndex(p => p.id === teamId)
|
||||
const party = parties[index]
|
||||
|
||||
party.favorited = false
|
||||
|
||||
let clonedParties = clonedeep(parties)
|
||||
clonedParties.splice(index, 1)
|
||||
|
||||
setParties(clonedParties)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function handleScroll() {
|
||||
if (window.pageYOffset > 90)
|
||||
setScrolled(true)
|
||||
else
|
||||
setScrolled(false)
|
||||
}
|
||||
|
||||
function goTo(shortcode: string) {
|
||||
router.push(`/p/${shortcode}`)
|
||||
}
|
||||
|
||||
function renderGrids() {
|
||||
return (
|
||||
<GridRepCollection>
|
||||
{
|
||||
parties.map((party, i) => {
|
||||
return <GridRep
|
||||
id={party.id}
|
||||
shortcode={party.shortcode}
|
||||
name={party.name}
|
||||
createdAt={new Date(party.created_at)}
|
||||
raid={party.raid}
|
||||
grid={party.weapons}
|
||||
user={party.user}
|
||||
favorited={party.favorited}
|
||||
key={`party-${i}`}
|
||||
displayUser={true}
|
||||
onClick={goTo}
|
||||
onSave={toggleFavorite}
|
||||
/>
|
||||
})
|
||||
}
|
||||
</GridRepCollection>
|
||||
)
|
||||
}
|
||||
|
||||
function renderNoGrids() {
|
||||
return (
|
||||
<div id="NotFound">
|
||||
<h2>You haven't saved any teams yet</h2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div id="Teams">
|
||||
<FilterBar onFilter={fetchTeams} name="Your saved teams" scrolled={scrolled} />
|
||||
{ (parties.length > 0) ? renderGrids() : renderNoGrids() }
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SavedRoute
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
import React, { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useCookies } from 'react-cookie'
|
||||
import clonedeep from 'lodash.clonedeep'
|
||||
|
||||
import api from '~utils/api'
|
||||
|
||||
|
|
@ -10,6 +12,12 @@ import FilterBar from '~components/FilterBar'
|
|||
const TeamsRoute: React.FC = () => {
|
||||
const router = useRouter()
|
||||
|
||||
// Cookies
|
||||
const [cookies, _] = useCookies(['user'])
|
||||
const headers = (cookies.user != null) ? {
|
||||
'Authorization': `Bearer ${cookies.user.access_token}`
|
||||
} : {}
|
||||
|
||||
const [found, setFound] = useState(false)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [scrolled, setScrolled] = useState(false)
|
||||
|
|
@ -31,6 +39,9 @@ const TeamsRoute: React.FC = () => {
|
|||
element: (element && element >= 0) ? element : undefined,
|
||||
raid: (raid && raid != '0') ? raid : undefined,
|
||||
recency: (recency && recency > 0) ? recency : undefined
|
||||
},
|
||||
headers: {
|
||||
'Authorization': `Bearer ${cookies.user.access_token}`
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -54,6 +65,47 @@ const TeamsRoute: React.FC = () => {
|
|||
})
|
||||
}
|
||||
|
||||
function toggleFavorite(teamId: string, favorited: boolean) {
|
||||
if (favorited)
|
||||
unsaveFavorite(teamId)
|
||||
else
|
||||
saveFavorite(teamId)
|
||||
}
|
||||
|
||||
function saveFavorite(teamId: string) {
|
||||
api.saveTeam({ id: teamId, params: headers })
|
||||
.then((response) => {
|
||||
if (response.status == 201) {
|
||||
const index = parties.findIndex(p => p.id === teamId)
|
||||
const party = parties[index]
|
||||
|
||||
party.favorited = true
|
||||
|
||||
let clonedParties = clonedeep(parties)
|
||||
clonedParties[index] = party
|
||||
|
||||
setParties(clonedParties)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function unsaveFavorite(teamId: string) {
|
||||
api.unsaveTeam({ id: teamId, params: headers })
|
||||
.then((response) => {
|
||||
if (response.status == 200) {
|
||||
const index = parties.findIndex(p => p.id === teamId)
|
||||
const party = parties[index]
|
||||
|
||||
party.favorited = false
|
||||
|
||||
let clonedParties = clonedeep(parties)
|
||||
clonedParties[index] = party
|
||||
|
||||
setParties(clonedParties)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function handleScroll() {
|
||||
if (window.pageYOffset > 90)
|
||||
setScrolled(true)
|
||||
|
|
@ -71,15 +123,18 @@ const TeamsRoute: React.FC = () => {
|
|||
{
|
||||
parties.map((party, i) => {
|
||||
return <GridRep
|
||||
id={party.id}
|
||||
shortcode={party.shortcode}
|
||||
name={party.name}
|
||||
createdAt={new Date(party.created_at)}
|
||||
raid={party.raid}
|
||||
grid={party.weapons}
|
||||
user={party.user}
|
||||
favorited={party.favorited}
|
||||
key={`party-${i}`}
|
||||
displayUser={true}
|
||||
onClick={goTo}
|
||||
onSave={toggleFavorite}
|
||||
/>
|
||||
})
|
||||
}
|
||||
|
|
@ -90,7 +145,7 @@ const TeamsRoute: React.FC = () => {
|
|||
function renderNoGrids() {
|
||||
return (
|
||||
<div id="NotFound">
|
||||
<h2>No grids found</h2>
|
||||
<h2>No teams found</h2>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
1
types/Party.d.ts
vendored
1
types/Party.d.ts
vendored
|
|
@ -4,6 +4,7 @@ interface Party {
|
|||
raid: Raid
|
||||
shortcode: string
|
||||
extra: boolean
|
||||
favorited: boolean
|
||||
characters: Array<GridCharacter>
|
||||
weapons: Array<GridWeapon>
|
||||
summons: Array<GridSummon>
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
import axios, { Axios, AxiosRequestConfig, AxiosResponse } from "axios"
|
||||
import { appState } from "./appState"
|
||||
|
||||
interface Entity {
|
||||
name: string
|
||||
}
|
||||
|
||||
type CollectionEndpoint = (params?: {}) => Promise<AxiosResponse<any>>
|
||||
type IdEndpoint = ({ id }: { id: string }) => Promise<AxiosResponse<any>>
|
||||
type IdWithObjectEndpoint = ({ id, object }: { id: string, object: string }) => Promise<AxiosResponse<any>>
|
||||
type IdEndpoint = ({ id, params }: { id: string, params?: {} }) => Promise<AxiosResponse<any>>
|
||||
type IdWithObjectEndpoint = ({ id, object, params }: { id: string, object: string, params?: {} }) => Promise<AxiosResponse<any>>
|
||||
type PostEndpoint = (object: {}, headers?: {}) => Promise<AxiosResponse<any>>
|
||||
type PutEndpoint = (id: string, object: {}, headers?: {}) => Promise<AxiosResponse<any>>
|
||||
type DestroyEndpoint = (id: string, headers?: {}) => Promise<AxiosResponse<any>>
|
||||
type DestroyEndpoint = ({ id, params }: { id: string, params?: {} }) => Promise<AxiosResponse<any>>
|
||||
|
||||
interface EndpointMap {
|
||||
getAll: CollectionEndpoint
|
||||
|
|
@ -42,11 +43,11 @@ class Api {
|
|||
|
||||
return {
|
||||
getAll: (params?: {}) => axios.get(resourceUrl, params),
|
||||
getOne: ({ id }: { id: string }) => axios.get(`${resourceUrl}/${id}/`),
|
||||
getOneWithObject: ({ id, object }: { id: string, object: string }) => axios.get(`${resourceUrl}/${id}/${object}`),
|
||||
getOne: ({ id, params }: { id: string, params?: {} }) => axios.get(`${resourceUrl}/${id}/`, params),
|
||||
getOneWithObject: ({ id, object, params }: { id: string, object: string, params?: {} }) => axios.get(`${resourceUrl}/${id}/${object}`, params),
|
||||
create: (object: {}, headers?: {}) => axios.post(resourceUrl, object, headers),
|
||||
update: (id: string, object: {}, headers?: {}) => axios.put(`${resourceUrl}/${id}`, object, headers),
|
||||
destroy: (id: string, headers?: {}) => axios.delete(`${resourceUrl}/${id}`, headers)
|
||||
destroy: ({ id, params }: { id: string, params?: {} }) => axios.delete(`${resourceUrl}/${id}`, params)
|
||||
} as EndpointMap
|
||||
}
|
||||
|
||||
|
|
@ -70,6 +71,23 @@ class Api {
|
|||
})
|
||||
}
|
||||
|
||||
savedTeams(params: {}) {
|
||||
const resourceUrl = `${this.url}/parties/favorites`
|
||||
return axios.get(resourceUrl, params)
|
||||
}
|
||||
|
||||
saveTeam({ id, params }: { id: string, params?: {} }) {
|
||||
const body = { favorite: { party_id: id } }
|
||||
const resourceUrl = `${this.url}/favorites`
|
||||
return axios.post(resourceUrl, body, { headers: params })
|
||||
}
|
||||
|
||||
unsaveTeam({ id, params }: { id: string, params?: {} }) {
|
||||
const body = { favorite: { party_id: id } }
|
||||
const resourceUrl = `${this.url}/favorites`
|
||||
return axios.delete(resourceUrl, { data: body, headers: params })
|
||||
}
|
||||
|
||||
updateUncap(resource: 'character'|'weapon'|'summon', id: string, value: number) {
|
||||
const pluralized = resource + 's'
|
||||
const resourceUrl = `${this.url}/${pluralized}/update_uncap`
|
||||
|
|
@ -89,5 +107,6 @@ api.createEntity( { name: 'characters' })
|
|||
api.createEntity( { name: 'weapons' })
|
||||
api.createEntity( { name: 'summons' })
|
||||
api.createEntity( { name: 'raids' })
|
||||
api.createEntity( { name: 'favorites' })
|
||||
|
||||
export default api
|
||||
|
|
@ -11,7 +11,9 @@ interface AppState {
|
|||
description: string | undefined,
|
||||
raid: Raid | undefined,
|
||||
element: number,
|
||||
extra: boolean
|
||||
extra: boolean,
|
||||
user: User | undefined,
|
||||
favorited: boolean
|
||||
},
|
||||
grid: {
|
||||
weapons: {
|
||||
|
|
@ -40,7 +42,9 @@ export const initialAppState: AppState = {
|
|||
description: undefined,
|
||||
raid: undefined,
|
||||
element: 0,
|
||||
extra: false
|
||||
extra: false,
|
||||
user: undefined,
|
||||
favorited: false
|
||||
},
|
||||
grid: {
|
||||
weapons: {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
export enum ButtonType {
|
||||
Base,
|
||||
IconOnly,
|
||||
Destructive
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue