@@ -4,26 +4,14 @@ import { Contract } from 'web3-eth-contract'
44import { AbiFragment } from 'web3'
55
66/**
7- * Interface for managing CryptoKitties transfer and auction form state
7+ * Interface for managing form input and loading state
88 * @interface FormDetails
99 * @property {string } kittyId - ID of the CryptoKitty or comma-separated IDs
10- * @property {boolean } transferrable - Whether the kitty can be transferred
11- * @property {boolean } forSale - Whether the kitty is in a sale auction
12- * @property {boolean } forSire - Whether the kitty is in a sire auction
1310 * @property {boolean } loading - Loading state during operations
14- * @property {boolean } auctionCancelled - Whether auction was successfully cancelled
15- * @property {boolean } transferSuccess - Whether transfer was successful
16- * @property {string } [error] - Error message if operation failed
1711 */
1812export interface FormDetails {
1913 kittyId : string ,
20- transferrable : boolean ,
21- forSale : boolean ,
22- forSire : boolean ,
2314 loading : boolean ,
24- auctionCancelled : boolean ,
25- transferSuccess : boolean ,
26- error ?: string ,
2715}
2816
2917/**
@@ -75,44 +63,39 @@ const CryptoKitties: React.FC<{
7563
7664 const initFormState : FormDetails = {
7765 kittyId : '' ,
78- transferrable : false ,
79- forSire : false ,
80- forSale : false ,
8166 loading : false ,
82- auctionCancelled : false ,
83- transferSuccess : false ,
84- error : undefined ,
8567 }
8668
8769 // Component state
8870 const [ kittyStatuses , setKittyStatuses ] = useState < KittyStatus [ ] > ( [ ] ) // Status for multiple kitties
89- const [ total , setTotal ] = useState < number > ( 0 ) // Total CryptoKitties supply
9071 const [ balance , setBalance ] = useState < number > ( 0 ) // User's CryptoKitties balance
72+ const [ totalSupply , setTotalSupply ] = useState < number > ( 0 ) // Total number of CryptoKitties
9173 const [ formDetails , setFormDetails ] = useState < FormDetails > ( initFormState ) // Form state
9274
9375 useEffect ( ( ) => {
94- const getCryptoKittiesBalanceAndTotal = async ( ) => {
76+ const init = async ( ) => {
9577 const _balance = await core . methods . balanceOf ( dapperWalletAddress ) . call ( )
96- const _total = await core . methods . totalSupply ( ) . call ( )
97- if ( _balance !== undefined && _balance !== null && _total !== undefined && _total !== null ) {
78+ const _totalSupply = await core . methods . totalSupply ( ) . call ( )
79+ if ( _balance !== undefined && _balance !== null ) {
9880 setBalance ( parseInt ( _balance . toString ( ) ) )
99- setTotal ( parseInt ( _total . toString ( ) ) )
81+ }
82+ if ( _totalSupply !== undefined && _totalSupply !== null ) {
83+ setTotalSupply ( parseInt ( _totalSupply . toString ( ) ) )
10084 }
10185 }
102- getCryptoKittiesBalanceAndTotal ( )
103- } , [ ] )
86+ init ( )
87+ } , [ core , dapperWalletAddress ] )
10488
105- useEffect ( ( ) => {
106- if ( formDetails . transferrable || formDetails . forSire || formDetails . forSale || formDetails . error ) {
107- setFormDetails ( prevState => ( {
108- ...prevState ,
109- transferrable : false ,
110- forSale : false ,
111- forSire : false ,
112- error : undefined
113- } ) )
114- }
115- } , [ formDetails . kittyId ] )
89+ /**
90+ * Validates a kitty ID format and range
91+ * @param {string } id - ID to validate
92+ * @returns {boolean } Whether ID is valid
93+ */
94+ const isValidKittyId = ( id : string ) => {
95+ if ( ! ( / ^ \d + $ / . test ( id . trim ( ) ) ) ) return false ;
96+ const numId = parseInt ( id . trim ( ) ) ;
97+ return numId <= totalSupply ;
98+ }
11699
117100 /**
118101 * Checks if a kitty is in an active auction
@@ -142,13 +125,14 @@ const CryptoKitties: React.FC<{
142125 const methodCall = contract . methods . cancelAuction ( tokenId )
143126 try {
144127 await invokeTx ( address , methodCall , '0' )
145- setFormDetails ( prevState => ( { ...prevState , forSale : false , forSire : false , auctionCancelled : true } ) )
146128 setKittyStatuses ( prev => prev . map ( s =>
147129 s . id === tokenId ? { ...s , auctionCancelled : true } : s
148130 ) ) ;
149131 } catch ( e ) {
132+ const errorMessage = 'Failed to cancel auction. Please try again.' ;
133+ alert ( errorMessage ) ;
150134 setKittyStatuses ( prev => prev . map ( s =>
151- s . id === tokenId ? { ...s , error : 'Failed to cancel auction. Please try again.' } : s
135+ s . id === tokenId ? { ...s , error : errorMessage } : s
152136 ) ) ;
153137 } finally {
154138 setFormDetails ( prevState => ( { ...prevState , loading : false } ) )
@@ -168,15 +152,15 @@ const CryptoKitties: React.FC<{
168152 const methodCall = core . methods . transfer ( walletAddress , kittyId )
169153 try {
170154 await invokeTx ( address , methodCall , '0' )
171- if ( kittyStatuses . length > 0 ) {
172- setKittyStatuses ( prev => prev . map ( s =>
173- s . id === kittyId ? { ...s , transferSuccess : true } : s
174- ) )
175- } else {
176- setFormDetails ( prevState => ( { ...prevState , transferrable : false , transferSuccess : true } ) )
177- }
155+ setKittyStatuses ( prev => prev . map ( s =>
156+ s . id === kittyId ? { ...s , transferSuccess : true } : s
157+ ) )
178158 } catch ( e ) {
179- setFormDetails ( prev => ( { ...prev , error : 'Failed to transfer. Please try again.' } ) ) ;
159+ const errorMessage = 'Failed to transfer. Please try again.' ;
160+ alert ( errorMessage ) ;
161+ setKittyStatuses ( prev => prev . map ( s =>
162+ s . id === kittyId ? { ...s , error : errorMessage } : s
163+ ) ) ;
180164 } finally {
181165 setFormDetails ( prevState => ( { ...prevState , loading : false } ) )
182166 }
@@ -196,24 +180,19 @@ const CryptoKitties: React.FC<{
196180 // Clear existing statuses when input changes
197181 setKittyStatuses ( [ ] )
198182
199- // If input contains commas, treat as multiple IDs
200- const ids = value . split ( ',' ) . map ( id => id . trim ( ) ) . filter ( id => id !== '' ) ;
201- if ( ids . length > 0 ) {
202- // Validate all IDs
203- const validIds = ids . filter ( id => / ^ \d + $ / . test ( id ) && total && parseInt ( id , 10 ) <= total ) ;
204- if ( validIds . length !== ids . length ) {
205- alert ( 'Some kitty IDs were invalid and will be ignored' ) ;
206- }
183+ // Show all IDs immediately
184+ const ids = value . split ( ',' )
185+ . map ( id => id . trim ( ) )
186+ . filter ( id => id !== '' ) ;
207187
208- // Initialize statuses for valid IDs
209- setKittyStatuses ( validIds . map ( id => ( {
210- id,
211- transferrable : false ,
212- forSale : false ,
213- forSire : false ,
214- loading : false
215- } ) ) ) ;
216- }
188+ setKittyStatuses ( ids . map ( id => ( {
189+ id,
190+ transferrable : false ,
191+ forSale : false ,
192+ forSire : false ,
193+ loading : false ,
194+ error : ! isValidKittyId ( id ) ? 'Invalid kitty ID' : undefined
195+ } ) ) ) ;
217196 }
218197 setFormDetails ( newState )
219198 }
@@ -224,7 +203,10 @@ const CryptoKitties: React.FC<{
224203 * @async
225204 */
226205 const checkAllKitties = async ( ) => {
227- const ids = formDetails . kittyId . split ( ',' ) . map ( id => id . trim ( ) ) . filter ( id => / ^ \d + $ / . test ( id ) ) ;
206+ // Get valid IDs
207+ const ids = kittyStatuses
208+ . filter ( status => ! status . error )
209+ . map ( status => status . id ) ;
228210 for ( let i = 0 ; i < ids . length ; i ++ ) {
229211 const kittyId = ids [ i ] ;
230212 setKittyStatuses ( prev => prev . map ( status =>
@@ -259,8 +241,9 @@ const CryptoKitties: React.FC<{
259241 }
260242 }
261243 } catch ( error ) {
244+ const errorMessage = 'An error occurred while checking ownership.' ;
262245 setKittyStatuses ( prev => prev . map ( status =>
263- status . id === kittyId ? { ...status , loading : false , error : 'An error occurred while checking ownership.' } : status
246+ status . id === kittyId ? { ...status , loading : false , error : errorMessage } : status
264247 ) ) ;
265248 }
266249 }
@@ -274,11 +257,6 @@ const CryptoKitties: React.FC<{
274257 */
275258 const formatBalance = ( balance : number ) => balance === 1 ? '1 CryptoKitty' : `${ balance } CryptoKitties`
276259
277- /**
278- * Resets the form state to initial values
279- */
280- // const resetForm = () => setFormDetails(initFormState)
281-
282260 return (
283261 < >
284262 < h2 > { `CryptoKitties` } </ h2 >
@@ -299,7 +277,10 @@ const CryptoKitties: React.FC<{
299277 placeholder = "Example: 123 or 123,456,789"
300278 />
301279 </ label >
302- < button onClick = { checkAllKitties } disabled = { formDetails . loading || kittyStatuses . length === 0 } >
280+ < button
281+ onClick = { checkAllKitties }
282+ disabled = { formDetails . loading }
283+ >
303284 { 'Check Kitties' }
304285 </ button >
305286
@@ -329,15 +310,7 @@ const CryptoKitties: React.FC<{
329310 ) }
330311 { ( status . forSale || status . forSire ) && (
331312 < button
332- onClick = { async ( ) => {
333- setFormDetails ( prev => ( {
334- ...prev ,
335- kittyId : status . id ,
336- forSale : status . forSale ,
337- forSire : status . forSire ,
338- } ) ) ;
339- await handleCancelAuction ( status . forSale , status . id ) ;
340- } }
313+ onClick = { async ( ) => await handleCancelAuction ( status . forSale , status . id ) }
341314 disabled = { formDetails . loading }
342315 >
343316 { `Cancel ${ status . forSale ? 'Sale' : 'Sire' } Auction` }
@@ -348,36 +321,7 @@ const CryptoKitties: React.FC<{
348321 </ div >
349322 ) ) }
350323 </ div >
351- ) : formDetails . auctionCancelled || formDetails . transferSuccess || formDetails . error ? (
352- < div >
353- { formDetails . error ? (
354- < p className = "error" > { formDetails . error } </ p >
355- ) : formDetails . auctionCancelled ? (
356- < p > < span className = { 'success' } > ✓</ span > { `Cancel auction method invoked for Kitty ID: #${ formDetails . kittyId } ` } </ p >
357- ) : (
358- < p > < span className = { 'success' } > ✓</ span > { `Transfer method invoked for Kitty ID: #${ formDetails . kittyId } ` } </ p >
359- ) }
360- </ div >
361- ) : (
362- < div style = { { marginTop : '10px' } } >
363- { formDetails . transferrable && (
364- < button
365- onClick = { async ( ) => await handleTransfer ( formDetails . kittyId ) }
366- disabled = { formDetails . loading }
367- >
368- { `transfer kitty #${ formDetails . kittyId } ` }
369- </ button >
370- ) }
371- { ( formDetails . forSale || formDetails . forSire ) && (
372- < button
373- onClick = { async ( ) => await handleCancelAuction ( formDetails . forSale , formDetails . kittyId ) }
374- disabled = { formDetails . loading }
375- >
376- { `cancel ${ formDetails . forSale ? 'sale' : 'sire' } auction` }
377- </ button >
378- ) }
379- </ div >
380- ) }
324+ ) : null }
381325 </ >
382326 )
383327}
0 commit comments