@@ -13,6 +13,17 @@ export interface FormDetails {
1313    transferSuccess : boolean , 
1414} 
1515
16+ interface  KittyStatus  { 
17+     id : string ; 
18+     transferrable : boolean ; 
19+     forSale : boolean ; 
20+     forSire : boolean ; 
21+     loading : boolean ; 
22+     error ?: string ; 
23+     transferSuccess ?: boolean ; 
24+     auctionCancelled ?: boolean ; 
25+ } 
26+ 
1627const  CryptoKitties : React . FC < {  
1728    walletAddress : string , 
1829    dapperWalletAddress : string , 
@@ -32,6 +43,8 @@ const CryptoKitties: React.FC<{
3243        transferSuccess : false , 
3344    } 
3445
46+     const  [ kittyStatuses ,  setKittyStatuses ]  =  useState < KittyStatus [ ] > ( [ ] ) 
47+ 
3548    const  [ total ,  setTotal ]  =  useState < number > ( 0 ) 
3649    const  [ balance ,  setBalance ]  =  useState < number > ( 0 ) 
3750    const  [ formDetails ,  setFormDetails ]  =  useState < FormDetails > ( initFormState ) 
@@ -108,16 +121,28 @@ const CryptoKitties: React.FC<{
108121        } 
109122    } 
110123
111-     const  handleTransfer  =  async  ( )  =>  { 
124+     const  handleTransfer  =  async  ( kittyId :  string )  =>  { 
112125        setFormDetails ( prevState  =>  ( {  ...prevState ,  loading : true  } ) ) 
113126        const  address  =  Contracts [ 'Core' ] . addr 
114-         const  methodCall  =  core . methods . transfer ( walletAddress ,  formDetails . kittyId ) 
127+         const  methodCall  =  core . methods . transfer ( walletAddress ,  kittyId ) 
115128        try  { 
116129            await  invokeTx ( address ,  methodCall ,  '0' ) 
117-             setFormDetails ( prevState  =>  ( {  ...prevState ,  transferrable : false ,  transferSuccess : true  } ) ) 
130+             if  ( kittyStatuses . length  >  0 )  { 
131+                 setKittyStatuses ( prev  =>  prev . map ( s  =>  
132+                     s . id  ===  kittyId  ? {  ...s ,  transferSuccess : true  }  : s 
133+                 ) ) 
134+             }  else  { 
135+                 setFormDetails ( prevState  =>  ( {  ...prevState ,  transferrable : false ,  transferSuccess : true  } ) ) 
136+             } 
118137        }  catch  ( e )  { 
119138            console . log ( e ) 
120-             alert ( 'Failed to transfer. Please try again.' ) 
139+             if  ( kittyStatuses . length  >  0 )  { 
140+                 setKittyStatuses ( prev  =>  prev . map ( s  =>  
141+                     s . id  ===  kittyId  ? {  ...s ,  error : 'Failed to transfer. Please try again.'  }  : s 
142+                 ) ) 
143+             }  else  { 
144+                 alert ( 'Failed to transfer. Please try again.' ) 
145+             } 
121146        }  finally  { 
122147            setFormDetails ( prevState  =>  ( {  ...prevState ,  loading : false  } ) ) 
123148        } 
@@ -128,10 +153,75 @@ const CryptoKitties: React.FC<{
128153        const  newState  =  {  ...formDetails  } 
129154        if  ( changeParam  ===  'kittyId' )  { 
130155            newState . kittyId  =  value 
156+             
157+             // Clear existing statuses when input changes 
158+             setKittyStatuses ( [ ] ) 
159+             
160+             // If input contains commas, treat as multiple IDs 
161+             const  ids  =  value . split ( ',' ) . map ( id  =>  id . trim ( ) ) . filter ( id  =>  id  !==  '' ) ; 
162+             if  ( ids . length  >  0 )  { 
163+                 // Validate all IDs 
164+                 const  validIds  =  ids . filter ( id  =>  / ^ \d + $ / . test ( id )  &&  total  &&  parseInt ( id ,  10 )  <=  total ) ; 
165+                 if  ( validIds . length  !==  ids . length )  { 
166+                     alert ( 'Some kitty IDs were invalid and will be ignored' ) ; 
167+                 } 
168+                 
169+                 // Initialize statuses for valid IDs 
170+                 setKittyStatuses ( validIds . map ( id  =>  ( { 
171+                     id, 
172+                     transferrable : false , 
173+                     forSale : false , 
174+                     forSire : false , 
175+                     loading : false 
176+                 } ) ) ) ; 
177+             } 
131178        } 
132179        setFormDetails ( newState ) 
133180    } 
134181
182+     const  checkAllKitties  =  async  ( )  =>  { 
183+         const  ids  =  formDetails . kittyId . split ( ',' ) . map ( id  =>  id . trim ( ) ) . filter ( id  =>  / ^ \d + $ / . test ( id ) ) ; 
184+         for  ( let  i  =  0 ;  i  <  ids . length ;  i ++ )  { 
185+             const  kittyId  =  ids [ i ] ; 
186+             setKittyStatuses ( prev  =>  prev . map ( status  =>  
187+                 status . id  ===  kittyId  ? {  ...status ,  loading : true  }  : status 
188+             ) ) ; 
189+ 
190+             try  { 
191+                 const  owner  =  await  core . methods . ownerOf ( kittyId ) . call ( ) ; 
192+                 if  ( owner  &&  owner . toString ( ) . toLowerCase ( )  ===  dapperWalletAddress . toLowerCase ( ) )  { 
193+                     setKittyStatuses ( prev  =>  prev . map ( status  =>  
194+                         status . id  ===  kittyId  ? {  ...status ,  transferrable : true ,  loading : false  }  : status 
195+                     ) ) ; 
196+                     continue ; 
197+                 } 
198+ 
199+                 const  isInSaleAuction  =  await  checkAuction ( sale ,  kittyId ) ; 
200+                 if  ( isInSaleAuction )  { 
201+                     setKittyStatuses ( prev  =>  prev . map ( status  =>  
202+                         status . id  ===  kittyId  ? {  ...status ,  forSale : true ,  loading : false  }  : status 
203+                     ) ) ; 
204+                     continue ; 
205+                 } 
206+ 
207+                 const  isInSireAuction  =  await  checkAuction ( sire ,  kittyId ) ; 
208+                 if  ( isInSireAuction )  { 
209+                     setKittyStatuses ( prev  =>  prev . map ( status  =>  
210+                         status . id  ===  kittyId  ? {  ...status ,  forSire : true ,  loading : false  }  : status 
211+                     ) ) ; 
212+                 }  else  { 
213+                     setKittyStatuses ( prev  =>  prev . map ( status  =>  
214+                         status . id  ===  kittyId  ? {  ...status ,  loading : false ,  error : 'Not owned by this Dapper Wallet'  }  : status 
215+                     ) ) ; 
216+                 } 
217+             }  catch  ( error )  { 
218+                 setKittyStatuses ( prev  =>  prev . map ( status  =>  
219+                     status . id  ===  kittyId  ? {  ...status ,  loading : false ,  error : 'Error checking ownership'  }  : status 
220+                 ) ) ; 
221+             } 
222+         } 
223+     } 
224+ 
135225    const  formatBalance  =  ( balance : number )  =>  balance  ===  1  ? '1 CryptoKitty'  : `${ balance }   CryptoKitties` 
136226
137227    const  resetForm  =  ( )  =>  setFormDetails ( initFormState ) 
@@ -144,34 +234,98 @@ const CryptoKitties: React.FC<{
144234            < p > { `Enter a CryptoKitty id from your Dapper Wallet to check if the kitty can be transferred.` } </ p > 
145235            < p > { `If the kitty is currently for sale or sire you will be prompted to cancel the auction.` } </ p > 
146236            < p > { `If you cancel the auction (assuming you created it) you will then be able to transfer the kitty.` } </ p > 
147-             < h3 > { ( formDetails . forSale  ||  formDetails . forSire )  ? `Cancel Auction:`  : `Transfer Kitty:` } </ h3 > 
148-             { formDetails . auctionCancelled  ||  formDetails . transferSuccess  ? ( 
149-                 < > 
150-                     { formDetails . auctionCancelled  ? ( 
151-                         < p > < span  className = { 'success' } > ✓</ span > { `Cancel auction method invoked for Kitty ID: #${ formDetails . kittyId }  ` } </ p > 
237+             < div > 
238+                 < p > < b > Enter a CryptoKitty ID or multiple IDs separated by commas:</ b > </ p > 
239+                 < div > 
240+                     < input  
241+                         type = { 'text' }  
242+                         value = { formDetails . kittyId }  
243+                         onChange = { e  =>  handleChange ( e ,  'kittyId' ) }  
244+                         disabled = { formDetails . loading }  
245+                         className = { 'tokenId' }  
246+                         placeholder = "Example: 123 or 123,456,789" 
247+                     /> 
248+                     { kittyStatuses . length  >  0  ? ( 
249+                         < button  onClick = { checkAllKitties }  disabled = { formDetails . loading } > 
250+                             Check Kitties
251+                         </ button > 
152252                    )  : ( 
153-                         < p > < span  className = { 'success' } > ✓</ span > { `Transfer method invoked for Kitty ID: #${ formDetails . kittyId }  ` } </ p > 
154-                     ) } 
155-                     < button  onClick = { resetForm } > { `Reset form` } </ button > 
156-                 </ > 
157-             )  : ( 
158-                 < label > 
159-                     kitty id: 
160-                     < input  type = { 'text' }  value = { formDetails . kittyId }  onChange = { e  =>  handleChange ( e ,  'kittyId' ) }  disabled = { formDetails . loading }  className = { 'tokenId' }  /> 
161-                     { formDetails . transferrable  &&  ( 
162-                         < button  onClick = { handleTransfer }  disabled = { formDetails . loading } > { `transfer kitty #${ formDetails . kittyId }  ` } </ button > 
253+                         < button  onClick = { handleCheckOwnership }  disabled = { formDetails . loading } > 
254+                             Check Ownership
255+                         </ button > 
163256                    ) } 
164-                     { ( formDetails . forSale  ||  formDetails . forSire )  &&  ( 
165-                         < button  onClick = { handleCancelAuction }  disabled = { formDetails . loading } > { `cancel ${ formDetails . forSale  ? 'sale'  : 'sire' }   auction` } </ button > 
166-                     ) } 
167-                     { ! formDetails . transferrable  &&  ! formDetails . forSale  &&  ! formDetails . forSire  &&  ( 
168-                         < button  onClick = { handleCheckOwnership }  disabled = { formDetails . loading } > { `check ownership` } </ button > 
169-                     ) } 
170-                 </ label > 
171-             ) } 
257+                 </ div > 
258+ 
259+                 { kittyStatuses . length  >  0  ? ( 
260+                     < div  style = { {  marginTop : '20px'  } } > 
261+                         { kittyStatuses . map ( ( status )  =>  ( 
262+                             < div  key = { status . id }  style = { {  marginBottom : '10px' ,  padding : '10px' ,  border : '1px solid #ccc'  } } > 
263+                                 < h4 > Kitty #{ status . id } </ h4 > 
264+                                 { status . loading  ? ( 
265+                                     < p > Checking status...</ p > 
266+                                 )  : status . error  ? ( 
267+                                     < p  className = "error" > { status . error } </ p > 
268+                                 )  : status . transferSuccess  ? ( 
269+                                     < p > < span  className = { 'success' } > ✓</ span > { `Transfer method invoked for Kitty ID: #${ status . id }  ` } </ p > 
270+                                 )  : status . auctionCancelled  ? ( 
271+                                     < p > < span  className = { 'success' } > ✓</ span > { `Cancel auction method invoked for Kitty ID: #${ status . id }  ` } </ p > 
272+                                 )  : ( 
273+                                     < div > 
274+                                         { status . transferrable  &&  ( 
275+                                             < button  
276+                                                 onClick = { ( )  =>  handleTransfer ( status . id ) } 
277+                                                 disabled = { formDetails . loading } 
278+                                             > 
279+                                                 Transfer Kitty
280+                                             </ button > 
281+                                         ) } 
282+                                         { ( status . forSale  ||  status . forSire )  &&  ( 
283+                                             < button  
284+                                                 onClick = { ( )  =>  { 
285+                                                     setFormDetails ( prev  =>  ( {  
286+                                                         ...prev ,  
287+                                                         kittyId : status . id , 
288+                                                         forSale : status . forSale , 
289+                                                         forSire : status . forSire 
290+                                                     } ) ) ; 
291+                                                     handleCancelAuction ( ) ; 
292+                                                     setKittyStatuses ( prev  =>  prev . map ( s  =>  
293+                                                         s . id  ===  status . id  ? {  ...s ,  auctionCancelled : true  }  : s 
294+                                                     ) ) ; 
295+                                                 } } 
296+                                                 disabled = { formDetails . loading } 
297+                                             > 
298+                                                 Cancel { status . forSale  ? 'Sale'  : 'Sire' }  Auction
299+                                             </ button > 
300+                                         ) } 
301+                                     </ div > 
302+                                 ) } 
303+                             </ div > 
304+                         ) ) } 
305+                     </ div > 
306+                 )  : formDetails . auctionCancelled  ||  formDetails . transferSuccess  ? ( 
307+                     < > 
308+                         { formDetails . auctionCancelled  ? ( 
309+                             < p > < span  className = { 'success' } > ✓</ span > { `Cancel auction method invoked for Kitty ID: #${ formDetails . kittyId }  ` } </ p > 
310+                         )  : ( 
311+                             < p > < span  className = { 'success' } > ✓</ span > { `Transfer method invoked for Kitty ID: #${ formDetails . kittyId }  ` } </ p > 
312+                         ) } 
313+                         < button  onClick = { resetForm } > { `Reset form` } </ button > 
314+                     </ > 
315+                 )  : ( 
316+                     < div  style = { {  marginTop : '10px'  } } > 
317+                         { formDetails . transferrable  &&  ( 
318+                             < button  onClick = { ( )  =>  handleTransfer ( formDetails . kittyId ) }  disabled = { formDetails . loading } > { `transfer kitty #${ formDetails . kittyId }  ` } </ button > 
319+                         ) } 
320+                         { ( formDetails . forSale  ||  formDetails . forSire )  &&  ( 
321+                             < button  onClick = { handleCancelAuction }  disabled = { formDetails . loading } > { `cancel ${ formDetails . forSale  ? 'sale'  : 'sire' }   auction` } </ button > 
322+                         ) } 
323+                     </ div > 
324+                 ) } 
325+             </ div > 
172326        </ > 
173327
174328    ) 
175329} 
176330
177- export  default  CryptoKitties 
331+ export  default  CryptoKitties 
0 commit comments