Implement searching for/adding guidebooks to party

This commit is contained in:
Justin Edmund 2023-04-19 00:36:32 -07:00
parent 926e892b51
commit c793fdb6a9
6 changed files with 147 additions and 10 deletions

View 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;
}
}
}

View 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

View file

@ -22,9 +22,6 @@ import type { DetailsObject } from '~types'
import './index.scss' 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' import PartyHeader from '../PartyHeader'
// Props // Props
@ -139,8 +136,11 @@ const Party = (props: Props) => {
if (details.turnCount) payload.turn_count = details.turnCount if (details.turnCount) payload.turn_count = details.turnCount
if (details.extra) payload.extra = details.extra if (details.extra) payload.extra = details.extra
if (details.job) payload.job_id = details.job.id 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 {} else return {}
} }
@ -154,17 +154,31 @@ const Party = (props: Props) => {
} }
} }
function checkboxChanged(event: React.ChangeEvent<HTMLInputElement>) { function checkboxChanged(enabled: boolean) {
appState.party.extra = event.target.checked appState.party.extra = enabled
// Only save if this is a saved party // Only save if this is a saved party
if (props.team && props.team.id) { if (props.team && props.team.id) {
api.endpoints.parties.update(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 // Remixing the party
function remixTeam() { function remixTeam() {
// setOriginalName(partySnapshot.name ? partySnapshot.name : t('no_title')) // setOriginalName(partySnapshot.name ? partySnapshot.name : t('no_title'))
@ -230,6 +244,7 @@ const Party = (props: Props) => {
appState.party.id = team.id appState.party.id = team.id
appState.party.shortcode = team.shortcode appState.party.shortcode = team.shortcode
appState.party.extra = team.extra appState.party.extra = team.extra
appState.party.guidebooks = team.guidebooks
appState.party.user = team.user appState.party.user = team.user
appState.party.favorited = team.favorited appState.party.favorited = team.favorited
appState.party.remix = team.remix appState.party.remix = team.remix
@ -334,8 +349,11 @@ const Party = (props: Props) => {
new={props.new || false} new={props.new || false}
editable={editable} editable={editable}
weapons={props.team?.weapons} weapons={props.team?.weapons}
guidebooks={props.team?.guidebooks}
createParty={createParty} createParty={createParty}
pushHistory={props.pushHistory} pushHistory={props.pushHistory}
updateExtra={checkboxChanged}
updateGuidebook={updateGuidebook}
/> />
) )
@ -384,6 +402,7 @@ const Party = (props: Props) => {
{navigation} {navigation}
<section id="Party">{currentGrid()}</section> <section id="Party">{currentGrid()}</section>
<PartyDetails <PartyDetails
party={props.team} party={props.team}
new={props.new || false} new={props.new || false}

View file

@ -61,6 +61,11 @@
#Results { #Results {
margin: 0; margin: 0;
padding: 0 ($unit * 1.5); padding: 0 ($unit * 1.5);
padding-bottom: $unit * 1.5;
// Infinite scroll
overflow-y: auto;
max-height: 500px;
@include breakpoint(phone) { @include breakpoint(phone) {
max-height: inherit; max-height: inherit;

View file

@ -19,6 +19,7 @@ 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'
import JobSkillResult from '~components/job/JobSkillResult' import JobSkillResult from '~components/job/JobSkillResult'
import GuidebookResult from '~components/extra/GuidebookResult'
import type { DialogProps } from '@radix-ui/react-dialog' import type { DialogProps } from '@radix-ui/react-dialog'
import type { SearchableObject, SearchableObjectArray } from '~types' import type { SearchableObject, SearchableObjectArray } from '~types'
@ -31,7 +32,7 @@ interface Props extends DialogProps {
placeholderText: string placeholderText: string
fromPosition: number fromPosition: number
job?: Job job?: Job
object: 'weapons' | 'characters' | 'summons' | 'job_skills' object: 'weapons' | 'characters' | 'summons' | 'job_skills' | 'guidebooks'
} }
const SearchModal = (props: Props) => { const SearchModal = (props: Props) => {
@ -184,7 +185,7 @@ const SearchModal = (props: Props) => {
} else if (open && currentPage == 1) { } else if (open && currentPage == 1) {
fetchResults({ replace: true }) fetchResults({ replace: true })
} }
}, [currentPage]) }, [open, currentPage])
useEffect(() => { useEffect(() => {
// Filters changed // Filters changed
@ -219,6 +220,17 @@ const SearchModal = (props: Props) => {
} }
}, [query]) }, [query])
useEffect(() => {
if (open && props.object === 'guidebooks') {
setCurrentPage(1)
fetchResults({ replace: true })
}
}, [query, open])
function incrementPage() {
setCurrentPage(currentPage + 1)
}
function renderResults() { function renderResults() {
let jsx let jsx
@ -235,12 +247,15 @@ const SearchModal = (props: Props) => {
case 'job_skills': case 'job_skills':
jsx = renderJobSkillSearchResults(results) jsx = renderJobSkillSearchResults(results)
break break
case 'guidebooks':
jsx = renderGuidebookSearchResults(results)
break
} }
return ( return (
<InfiniteScroll <InfiniteScroll
dataLength={results && results.length > 0 ? results.length : 0} dataLength={results && results.length > 0 ? results.length : 0}
next={() => setCurrentPage(currentPage + 1)} next={incrementPage}
hasMore={totalPages > currentPage} hasMore={totalPages > currentPage}
scrollableTarget="Results" scrollableTarget="Results"
loader={<div className="footer">Loading...</div>} loader={<div className="footer">Loading...</div>}
@ -334,6 +349,27 @@ const SearchModal = (props: Props) => {
return jsx 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() { function openChange() {
if (open) { if (open) {
setQuery('') setQuery('')
@ -365,6 +401,7 @@ const SearchModal = (props: Props) => {
<DialogContent <DialogContent
className="Search" className="Search"
headerref={headerRef} headerref={headerRef}
scrollable={false}
onEscapeKeyDown={onEscapeKeyDown} onEscapeKeyDown={onEscapeKeyDown}
onOpenAutoFocus={onOpenAutoFocus} onOpenAutoFocus={onOpenAutoFocus}
> >

View file

@ -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) { async function handleWeaponResponse(data: any) {
if (data.hasOwnProperty('conflicts')) { if (data.hasOwnProperty('conflicts')) {
if (data.incoming) setIncoming(data.incoming) if (data.incoming) setIncoming(data.incoming)