Implement searching for/adding guidebooks to party
This commit is contained in:
parent
926e892b51
commit
c793fdb6a9
6 changed files with 147 additions and 10 deletions
37
components/extra/GuidebookResult/index.scss
Normal file
37
components/extra/GuidebookResult/index.scss
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
.GuidebookResult {
|
||||
border-radius: 6px;
|
||||
display: flex;
|
||||
gap: $unit;
|
||||
padding: $unit * 1.5;
|
||||
|
||||
&:hover {
|
||||
background: var(--button-contained-bg);
|
||||
cursor: pointer;
|
||||
|
||||
.Info h5 {
|
||||
color: var(--text-primary);
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
background: $grey-80;
|
||||
border-radius: 6px;
|
||||
display: inline-block;
|
||||
height: auto;
|
||||
width: 90px;
|
||||
}
|
||||
|
||||
.Info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
gap: $unit-half;
|
||||
|
||||
h5 {
|
||||
color: var(--text-tertiary);
|
||||
display: inline-block;
|
||||
font-size: $font-medium;
|
||||
font-weight: $medium;
|
||||
}
|
||||
}
|
||||
}
|
||||
32
components/extra/GuidebookResult/index.tsx
Normal file
32
components/extra/GuidebookResult/index.tsx
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
import React from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
|
||||
import './index.scss'
|
||||
|
||||
interface Props {
|
||||
data: Guidebook
|
||||
onClick: () => void
|
||||
}
|
||||
|
||||
const GuidebookResult = (props: Props) => {
|
||||
const router = useRouter()
|
||||
const locale =
|
||||
router.locale && ['en', 'ja'].includes(router.locale) ? router.locale : 'en'
|
||||
|
||||
const guidebook = props.data
|
||||
|
||||
return (
|
||||
<li className="GuidebookResult" onClick={props.onClick}>
|
||||
<img
|
||||
alt={guidebook.name[locale]}
|
||||
src={`${process.env.NEXT_PUBLIC_SIERO_IMG_URL}/guidebooks/book_${guidebook.granblue_id}.png`}
|
||||
/>
|
||||
<div className="Info">
|
||||
<h5>{guidebook.name[locale]}</h5>
|
||||
<p>{guidebook.description[locale]}</p>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export default GuidebookResult
|
||||
|
|
@ -22,9 +22,6 @@ import type { DetailsObject } from '~types'
|
|||
|
||||
import './index.scss'
|
||||
|
||||
import WeaponRep from '~components/reps/WeaponRep'
|
||||
import CharacterRep from '~components/reps/CharacterRep'
|
||||
import SummonRep from '~components/reps/SummonRep'
|
||||
import PartyHeader from '../PartyHeader'
|
||||
|
||||
// Props
|
||||
|
|
@ -139,8 +136,11 @@ const Party = (props: Props) => {
|
|||
if (details.turnCount) payload.turn_count = details.turnCount
|
||||
if (details.extra) payload.extra = details.extra
|
||||
if (details.job) payload.job_id = details.job.id
|
||||
if (details.guidebook0_id) payload.guidebook0_id = details.guidebook0_id
|
||||
if (details.guidebook1_id) payload.guidebook1_id = details.guidebook1_id
|
||||
if (details.guidebook2_id) payload.guidebook2_id = details.guidebook2_id
|
||||
|
||||
if (Object.keys(payload).length > 1) return { party: payload }
|
||||
if (Object.keys(payload).length >= 1) return { party: payload }
|
||||
else return {}
|
||||
}
|
||||
|
||||
|
|
@ -154,17 +154,31 @@ const Party = (props: Props) => {
|
|||
}
|
||||
}
|
||||
|
||||
function checkboxChanged(event: React.ChangeEvent<HTMLInputElement>) {
|
||||
appState.party.extra = event.target.checked
|
||||
function checkboxChanged(enabled: boolean) {
|
||||
appState.party.extra = enabled
|
||||
|
||||
// Only save if this is a saved party
|
||||
if (props.team && props.team.id) {
|
||||
api.endpoints.parties.update(props.team.id, {
|
||||
party: { extra: event.target.checked },
|
||||
party: { extra: enabled },
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function updateGuidebook(book: Guidebook, position: number) {
|
||||
const details: DetailsObject = {
|
||||
guidebook0_id: position === 0 ? book.id : undefined,
|
||||
guidebook1_id: position === 1 ? book.id : undefined,
|
||||
guidebook2_id: position === 2 ? book.id : undefined,
|
||||
}
|
||||
|
||||
if (props.team && props.team.id) {
|
||||
updateParty(details)
|
||||
} else {
|
||||
createParty(details)
|
||||
}
|
||||
}
|
||||
|
||||
// Remixing the party
|
||||
function remixTeam() {
|
||||
// setOriginalName(partySnapshot.name ? partySnapshot.name : t('no_title'))
|
||||
|
|
@ -230,6 +244,7 @@ const Party = (props: Props) => {
|
|||
appState.party.id = team.id
|
||||
appState.party.shortcode = team.shortcode
|
||||
appState.party.extra = team.extra
|
||||
appState.party.guidebooks = team.guidebooks
|
||||
appState.party.user = team.user
|
||||
appState.party.favorited = team.favorited
|
||||
appState.party.remix = team.remix
|
||||
|
|
@ -334,8 +349,11 @@ const Party = (props: Props) => {
|
|||
new={props.new || false}
|
||||
editable={editable}
|
||||
weapons={props.team?.weapons}
|
||||
guidebooks={props.team?.guidebooks}
|
||||
createParty={createParty}
|
||||
pushHistory={props.pushHistory}
|
||||
updateExtra={checkboxChanged}
|
||||
updateGuidebook={updateGuidebook}
|
||||
/>
|
||||
)
|
||||
|
||||
|
|
@ -384,6 +402,7 @@ const Party = (props: Props) => {
|
|||
{navigation}
|
||||
|
||||
<section id="Party">{currentGrid()}</section>
|
||||
|
||||
<PartyDetails
|
||||
party={props.team}
|
||||
new={props.new || false}
|
||||
|
|
|
|||
|
|
@ -61,6 +61,11 @@
|
|||
#Results {
|
||||
margin: 0;
|
||||
padding: 0 ($unit * 1.5);
|
||||
padding-bottom: $unit * 1.5;
|
||||
|
||||
// Infinite scroll
|
||||
overflow-y: auto;
|
||||
max-height: 500px;
|
||||
|
||||
@include breakpoint(phone) {
|
||||
max-height: inherit;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import CharacterResult from '~components/character/CharacterResult'
|
|||
import WeaponResult from '~components/weapon/WeaponResult'
|
||||
import SummonResult from '~components/summon/SummonResult'
|
||||
import JobSkillResult from '~components/job/JobSkillResult'
|
||||
import GuidebookResult from '~components/extra/GuidebookResult'
|
||||
|
||||
import type { DialogProps } from '@radix-ui/react-dialog'
|
||||
import type { SearchableObject, SearchableObjectArray } from '~types'
|
||||
|
|
@ -31,7 +32,7 @@ interface Props extends DialogProps {
|
|||
placeholderText: string
|
||||
fromPosition: number
|
||||
job?: Job
|
||||
object: 'weapons' | 'characters' | 'summons' | 'job_skills'
|
||||
object: 'weapons' | 'characters' | 'summons' | 'job_skills' | 'guidebooks'
|
||||
}
|
||||
|
||||
const SearchModal = (props: Props) => {
|
||||
|
|
@ -184,7 +185,7 @@ const SearchModal = (props: Props) => {
|
|||
} else if (open && currentPage == 1) {
|
||||
fetchResults({ replace: true })
|
||||
}
|
||||
}, [currentPage])
|
||||
}, [open, currentPage])
|
||||
|
||||
useEffect(() => {
|
||||
// Filters changed
|
||||
|
|
@ -219,6 +220,17 @@ const SearchModal = (props: Props) => {
|
|||
}
|
||||
}, [query])
|
||||
|
||||
useEffect(() => {
|
||||
if (open && props.object === 'guidebooks') {
|
||||
setCurrentPage(1)
|
||||
fetchResults({ replace: true })
|
||||
}
|
||||
}, [query, open])
|
||||
|
||||
function incrementPage() {
|
||||
setCurrentPage(currentPage + 1)
|
||||
}
|
||||
|
||||
function renderResults() {
|
||||
let jsx
|
||||
|
||||
|
|
@ -235,12 +247,15 @@ const SearchModal = (props: Props) => {
|
|||
case 'job_skills':
|
||||
jsx = renderJobSkillSearchResults(results)
|
||||
break
|
||||
case 'guidebooks':
|
||||
jsx = renderGuidebookSearchResults(results)
|
||||
break
|
||||
}
|
||||
|
||||
return (
|
||||
<InfiniteScroll
|
||||
dataLength={results && results.length > 0 ? results.length : 0}
|
||||
next={() => setCurrentPage(currentPage + 1)}
|
||||
next={incrementPage}
|
||||
hasMore={totalPages > currentPage}
|
||||
scrollableTarget="Results"
|
||||
loader={<div className="footer">Loading...</div>}
|
||||
|
|
@ -334,6 +349,27 @@ const SearchModal = (props: Props) => {
|
|||
return jsx
|
||||
}
|
||||
|
||||
function renderGuidebookSearchResults(results: { [key: string]: any }) {
|
||||
let jsx: React.ReactNode
|
||||
|
||||
const castResults: Guidebook[] = results as Guidebook[]
|
||||
if (castResults && Object.keys(castResults).length > 0) {
|
||||
jsx = castResults.map((result: Guidebook) => {
|
||||
return (
|
||||
<GuidebookResult
|
||||
key={result.id}
|
||||
data={result}
|
||||
onClick={() => {
|
||||
storeRecentResult(result)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
return jsx
|
||||
}
|
||||
|
||||
function openChange() {
|
||||
if (open) {
|
||||
setQuery('')
|
||||
|
|
@ -365,6 +401,7 @@ const SearchModal = (props: Props) => {
|
|||
<DialogContent
|
||||
className="Search"
|
||||
headerref={headerRef}
|
||||
scrollable={false}
|
||||
onEscapeKeyDown={onEscapeKeyDown}
|
||||
onOpenAutoFocus={onOpenAutoFocus}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -121,6 +121,13 @@ const WeaponGrid = (props: Props) => {
|
|||
}
|
||||
}
|
||||
|
||||
function receiveGuidebookFromSearch(
|
||||
object: SearchableObject,
|
||||
position: number
|
||||
) {
|
||||
props.updateGuidebook(object as Guidebook, position)
|
||||
}
|
||||
|
||||
async function handleWeaponResponse(data: any) {
|
||||
if (data.hasOwnProperty('conflicts')) {
|
||||
if (data.incoming) setIncoming(data.incoming)
|
||||
|
|
|
|||
Loading…
Reference in a new issue