11import styles from './AddToInventoryDialog.module.scss' ;
22import {
3- Box ,
4- Button , Dialog , DialogActions , DialogContent , DialogTitle ,
5- Stack ,
6- ToggleButton ,
7- ToggleButtonGroup ,
8- useMediaQuery , useTheme ,
3+ Box , Button , Dialog , DialogActions , DialogContent , DialogTitle ,
4+ Stack , ToggleButton , ToggleButtonGroup , useMediaQuery , useTheme ,
95} from '@mui/material' ;
10- import React , { useEffect , useMemo , useReducer , useState } from 'react' ;
6+ import React , { useContext , useEffect , useMemo , useState } from 'react' ;
117import { useTranslation } from 'next-i18next' ;
128import { useForm } from 'react-hook-form' ;
139import {
@@ -21,7 +17,46 @@ import {
2117 Comparators , SortingOrder ,
2218 buildArrayIndexComparator , buildComparator ,
2319} from 'common/sortUtils' ;
24- import { EquipmentCategories } from 'model/Equipment' ;
20+ import { EquipmentCategories , EquipmentCompositionType } from 'model/Equipment' ;
21+
22+ const AddToInventoryDialogContext = React . createContext ( {
23+ open : ( drops : DropPieceIdWithProbAndCount [ ] ) => { } ,
24+ close : ( ) => { } ,
25+ } ) ;
26+
27+ export const useAddToInventoryDialogContext = ( ) => {
28+ const { open, close} = useContext ( AddToInventoryDialogContext ) ;
29+ return [ open , close ] as const ;
30+ } ;
31+
32+ export const AddToInventoryDialogContextProvider = ( {
33+ equipById,
34+ piecesState,
35+ children,
36+ } : {
37+ equipById : EquipmentsById ,
38+ piecesState : Map < string , PieceState > ,
39+ children ?: React . ReactNode ,
40+ } ) => {
41+ const [ drops , setDrops ] = useState < DropPieceIdWithProbAndCount [ ] > ( [ ] ) ;
42+ const [ open , setOpen ] = useState ( false ) ;
43+ const contextValue = useMemo ( ( ) => ( {
44+ open : ( drops : DropPieceIdWithProbAndCount [ ] ) => {
45+ setDrops ( drops ) ;
46+ setOpen ( true ) ;
47+ } ,
48+ close : ( ) => {
49+ setOpen ( false ) ;
50+ } ,
51+ } ) , [ setDrops ] ) ;
52+
53+ return < AddToInventoryDialogContext . Provider value = { contextValue } >
54+ < AddToInventoryDialog open = { open }
55+ onUpdate = { contextValue . close } onCancel = { contextValue . close }
56+ equipById = { equipById } piecesState = { piecesState } drops = { drops } />
57+ { children }
58+ </ AddToInventoryDialogContext . Provider > ;
59+ } ;
2560
2661const AddToInventoryDialog = ( {
2762 open,
@@ -42,20 +77,16 @@ const AddToInventoryDialog = ({
4277 const { t} = useTranslation ( 'home' ) ;
4378 const theme = useTheme ( ) ;
4479
45- // hack to lazy rendering
46- const [ onceOpen , notifyOpened ] = useReducer ( ( x ) => true , false ) ;
47- useEffect ( ( ) => {
48- open && notifyOpened ( ) ;
49- } , [ open ] ) ;
50-
5180 const [ mode , setMode ] = useState < 'all' | 'lack' | 'required' > ( 'lack' ) ;
5281
5382 const pieces = useMemo ( ( ) => {
5483 // 20-3: Necklace, Watch, Bag
5584 // 20-4: Watch, Charm, Badge
5685 // 13-1: Shoes, Gloves, Hat
5786 // descending tier -> descending category order?
58- return drops . map ( ( { id} ) => ( piecesState . get ( id ) ?? {
87+ return drops . filter ( ( { id} ) => (
88+ equipById . get ( id ) ?. equipmentCompositionType === EquipmentCompositionType . Piece
89+ ) ) . map ( ( { id} ) => ( piecesState . get ( id ) ?? {
5990 pieceId : id ,
6091 needCount : 0 ,
6192 inStockCount : 0 ,
@@ -72,45 +103,55 @@ const AddToInventoryDialog = ({
72103 ) ) ,
73104 ) ) ;
74105 } , [ drops , piecesState , mode , equipById ] ) ;
75- const defaultValues = useMemo ( ( ) => {
76- return pieces . reduce < InventoryForm > ( ( acc , piece ) => {
77- acc [ piece . pieceId ] = '' ;
78- return acc ;
79- } , { } ) ;
80- } , [ pieces ] ) ;
81106
82107 const {
83- control,
84- formState : { isValid : isCountValid , errors : allErrors } ,
85- getValues ,
86- reset ,
108+ control, formState ,
109+ getValues , reset ,
110+ getFieldState , setFocus ,
111+ handleSubmit ,
87112 } = useForm < InventoryForm > ( {
88113 mode : 'onChange' ,
89- defaultValues,
114+ defaultValues : Object . fromEntries ( drops . map ( ( { id } ) => [ id , '' ] ) ) ,
90115 } ) ;
91116
92- const handleCancelDialog = ( ) => {
117+ useEffect ( ( ) => {
118+ if ( ! open ) return ;
119+ reset ( Object . fromEntries ( drops . map ( ( { id} ) => [ id , '' ] ) ) ) ;
120+ } , [ drops , open , reset ] ) ;
121+
122+ const handleCancel = ( ) => {
93123 onCancel ( ) ;
94- reset ( ) ;
95124 } ;
96125
97- const handleUpdateInventory = ( ) => {
98- onUpdate ( getValues ( ) ) ;
99- const inventory = Object . entries ( getValues ( ) ) . reduce < InventoryForm > ( ( acc , [ id , value ] ) => {
100- const count = parseInt ( value ) ?? 0 ;
101- const stock = piecesState . get ( id ) ?. inStockCount ?? 0 ;
102- acc [ id ] = `${ count + stock } ` ;
103- return acc ;
104- } , { } ) ;
126+ const handleUpdate = handleSubmit ( ( value ) => {
127+ onUpdate ( value ) ;
128+ const inventory = Object . fromEntries ( Object . entries ( value ) . map ( ( [ id , value ] ) => {
129+ const count = parseInt ( value ) || 0 ;
130+ const stock = piecesState . get ( id ) ?. inStockCount || 0 ;
131+ return [ id , `${ count + stock } ` ] ;
132+ } ) ) ;
105133 store . equipmentsRequirementStore . updateInventory ( inventory ) ;
106- reset ( ) ;
107- } ;
134+ } , ( errors ) => {
135+ const field = Object . entries ( errors ) . find ( ( [ , it ] ) => it && 'ref' in it ) ?. [ 0 ] ;
136+ console . log ( errors ) ;
137+ field && setFocus ( field ) ;
138+ } ) ;
108139
109140 const isFullScreen = useMediaQuery ( theme . breakpoints . down ( 'md' ) ) ;
110141 const isXsOrSmallerScreen = useMediaQuery ( theme . breakpoints . down ( 'sm' ) ) ;
111142 const hasManyPieces = ( ) => pieces . length > 3 ;
112143
113- return ! onceOpen ? null : < Dialog open = { open } keepMounted fullWidth
144+ useEffect ( ( ) => {
145+ const id = setTimeout ( ( ) => {
146+ const field = pieces . find ( ( { pieceId} ) => (
147+ getValues ( pieceId ) === '' || getFieldState ( pieceId ) . invalid
148+ ) ) ?? pieces [ 0 ] ;
149+ field && setFocus ( field . pieceId , { shouldSelect : true } ) ;
150+ } , 100 ) ;
151+ return ( ) => clearTimeout ( id ) ;
152+ } , [ equipById , getFieldState , getValues , open , pieces , setFocus ] ) ;
153+
154+ return < Dialog open = { open } fullWidth
114155 fullScreen = { hasManyPieces ( ) && isFullScreen }
115156 maxWidth = { hasManyPieces ( ) && 'xl' } >
116157 < Stack component = { DialogTitle } direction = 'row' alignItems = 'center' >
@@ -127,21 +168,20 @@ const AddToInventoryDialog = ({
127168 < DialogContent className = { styles . dialogContentContainer } >
128169 < div className = { styles . filler } > </ div >
129170 < div className = { `${ styles . container } ${ isXsOrSmallerScreen && styles . xs } ` } >
130- { pieces . map ( ( piece , index ) => {
131- return < ObtainedPieceBox key = { piece . pieceId } allErrors = { allErrors }
132- control = { control }
133- equipmentsById = { equipById }
134- piece = { piece }
135- focused = { index === 0 } /> ;
171+ { pieces . map ( ( piece ) => {
172+ return < ObtainedPieceBox key = { piece . pieceId }
173+ allErrors = { formState . errors } control = { control }
174+ equipmentsById = { equipById } piece = { piece }
175+ required = { false } /> ;
136176 } ) }
137177 </ div >
138178 { pieces . length === 0 && < div > { t ( 'filterResultEmpty' ) } </ div > }
139179 < div className = { styles . filler } > </ div >
140180 </ DialogContent >
141181
142182 < DialogActions >
143- < Button onClick = { handleCancelDialog } > { t ( 'cancelButton' ) } </ Button >
144- < Button onClick = { handleUpdateInventory } disabled = { ! isCountValid } > { t ( 'addButton' ) } </ Button >
183+ < Button onClick = { handleCancel } > { t ( 'cancelButton' ) } </ Button >
184+ < Button onClick = { handleUpdate } disabled = { ! formState . isValid } > { t ( 'addButton' ) } </ Button >
145185 </ DialogActions >
146186 </ Dialog > ;
147187} ;
0 commit comments