@@ -12,6 +12,7 @@ import { fetchManifest, getPrimaryDeployment } from '../../../src/lib/manifest';
1212import { getCollection , mutableFields , type ThsCollection , type ThsField } from '../../../src/lib/ths' ;
1313import { submitWriteTx } from '../../../src/lib/tx' ;
1414import TxStatus , { type TxPhase } from '../../../src/components/TxStatus' ;
15+ import ImageFieldInput from '../../../src/components/ImageFieldInput' ;
1516
1617function inputType ( field : ThsField ) : 'text' | 'number' {
1718 if ( field . type === 'uint256' || field . type === 'int256' || field . type === 'decimal' || field . type === 'reference' ) return 'number' ;
@@ -42,7 +43,9 @@ export default function EditRecordPage(props: { params: { collection: string } }
4243 const idParam = search . get ( 'id' ) ;
4344 const rpcOverride = search . get ( 'rpc' ) ?? undefined ;
4445
45- const [ loading , setLoading ] = useState ( true ) ;
46+ const [ bootstrapping , setBootstrapping ] = useState ( true ) ;
47+ const [ recordLoading , setRecordLoading ] = useState ( false ) ;
48+ const [ initialRecordResolved , setInitialRecordResolved ] = useState ( false ) ;
4649 const [ error , setError ] = useState < string | null > ( null ) ;
4750 const [ status , setStatus ] = useState < string | null > ( null ) ;
4851 const [ txPhase , setTxPhase ] = useState < TxPhase > ( 'idle' ) ;
@@ -67,7 +70,8 @@ export default function EditRecordPage(props: { params: { collection: string } }
6770
6871 useEffect ( ( ) => {
6972 async function boot ( ) {
70- setLoading ( true ) ;
73+ setBootstrapping ( true ) ;
74+ setInitialRecordResolved ( false ) ;
7175 setError ( null ) ;
7276 try {
7377 const manifest = await fetchManifest ( ) ;
@@ -89,7 +93,7 @@ export default function EditRecordPage(props: { params: { collection: string } }
8993 } catch ( e : any ) {
9094 setError ( String ( e ?. message ?? e ) ) ;
9195 } finally {
92- setLoading ( false ) ;
96+ setBootstrapping ( false ) ;
9397 }
9498 }
9599 void boot ( ) ;
@@ -100,8 +104,11 @@ export default function EditRecordPage(props: { params: { collection: string } }
100104 const fields = collection ? mutableFields ( collection ) : [ ] ;
101105 const optimistic = Boolean ( ( collection as any ) ?. updateRules ?. optimisticConcurrency ) ;
102106
103- async function fetchRecord ( ) {
107+ async function fetchRecord ( options ?: { initial ?: boolean } ) {
104108 if ( ! publicClient || ! abi || ! appAddress || id === null ) return ;
109+ const isInitial = options ?. initial === true ;
110+ if ( isInitial ) setInitialRecordResolved ( false ) ;
111+ setRecordLoading ( true ) ;
105112 setError ( null ) ;
106113 try {
107114 assertAbiFunction ( abi , fnGet ( collectionName ) , collectionName ) ;
@@ -114,12 +121,15 @@ export default function EditRecordPage(props: { params: { collection: string } }
114121 setRecord ( r ) ;
115122 } catch ( e : any ) {
116123 setError ( String ( e ?. message ?? e ) ) ;
124+ } finally {
125+ setRecordLoading ( false ) ;
126+ if ( isInitial ) setInitialRecordResolved ( true ) ;
117127 }
118128 }
119129
120130 // Load record once ABI + id are ready.
121131 useEffect ( ( ) => {
122- void fetchRecord ( ) ;
132+ void fetchRecord ( { initial : true } ) ;
123133 // eslint-disable-next-line react-hooks/exhaustive-deps
124134 } , [ publicClient , abi , appAddress , idParam ] ) ;
125135
@@ -211,7 +221,7 @@ export default function EditRecordPage(props: { params: { collection: string } }
211221 ) ;
212222 }
213223
214- if ( loading && ! record ) {
224+ if ( ( bootstrapping || ( recordLoading && ! initialRecordResolved ) ) && ! record ) {
215225 return (
216226 < div className = "card" >
217227 < h2 > Loading…</ h2 >
@@ -264,7 +274,7 @@ export default function EditRecordPage(props: { params: { collection: string } }
264274 ) ;
265275 }
266276
267- if ( ! record ) {
277+ if ( initialRecordResolved && ! record ) {
268278 return (
269279 < div className = "card" >
270280 < h2 > Not found</ h2 >
@@ -282,31 +292,40 @@ export default function EditRecordPage(props: { params: { collection: string } }
282292 < button className = "btn" onClick = { ( ) => router . push ( `/${ collectionName } /?mode=view&id=${ String ( id ) } ` ) } > Back</ button >
283293 </ div >
284294
285- { fields . map ( ( f ) => (
286- < div key = { f . name } >
287- < label className = "label" > { f . name } </ label >
288- { f . type === 'bool' ? (
289- < select
290- className = "select"
291- value = { form [ f . name ] ?? 'false' }
292- onChange = { ( e ) => setForm ( ( prev ) => ( { ...prev , [ f . name ] : e . target . value } ) ) }
293- >
294- < option value = "false" > false</ option >
295- < option value = "true" > true</ option >
296- </ select >
297- ) : (
298- < input
299- className = "input"
300- type = { inputType ( f ) }
301- value = { form [ f . name ] ?? '' }
302- onChange = { ( e ) => setForm ( ( prev ) => ( { ...prev , [ f . name ] : e . target . value } ) ) }
303- placeholder = { f . type === 'reference' ? 'record id (uint256)' : f . type }
304- />
305- ) }
306- </ div >
307- ) ) }
295+ < div className = "formGrid" >
296+ { fields . map ( ( f ) => (
297+ < div key = { f . name } className = "fieldGroup" >
298+ < label className = "label" > { f . name } </ label >
299+ { f . type === 'bool' ? (
300+ < select
301+ className = "select"
302+ value = { form [ f . name ] ?? 'false' }
303+ onChange = { ( e ) => setForm ( ( prev ) => ( { ...prev , [ f . name ] : e . target . value } ) ) }
304+ >
305+ < option value = "false" > false</ option >
306+ < option value = "true" > true</ option >
307+ </ select >
308+ ) : f . type === 'image' ? (
309+ < ImageFieldInput
310+ manifest = { manifest }
311+ value = { form [ f . name ] ?? '' }
312+ disabled = { txPhase === 'submitting' || txPhase === 'submitted' || txPhase === 'confirming' }
313+ onChange = { ( next ) => setForm ( ( prev ) => ( { ...prev , [ f . name ] : next } ) ) }
314+ />
315+ ) : (
316+ < input
317+ className = "input"
318+ type = { inputType ( f ) }
319+ value = { form [ f . name ] ?? '' }
320+ onChange = { ( e ) => setForm ( ( prev ) => ( { ...prev , [ f . name ] : e . target . value } ) ) }
321+ placeholder = { f . type === 'reference' ? 'record id (uint256)' : f . type }
322+ />
323+ ) }
324+ </ div >
325+ ) ) }
326+ </ div >
308327
309- < div style = { { marginTop : 16 , display : 'flex' , gap : 10 } } >
328+ < div className = "actionGroup" style = { { marginTop : 16 } } >
310329 < button
311330 className = "btn primary"
312331 onClick = { ( ) => void submit ( ) }
0 commit comments