@@ -145,8 +145,6 @@ const errors = {
145145 } ,
146146}
147147
148- // Note: qcserial unbind hint is added dynamically in the component based on device type
149-
150148
151149function LinearProgress ( { value, barColor } ) {
152150 if ( value === - 1 || value > 100 ) value = 100
@@ -501,12 +499,14 @@ const screenToStep = {
501499}
502500
503501export default function Flash ( ) {
504- const [ step , setStep ] = useState ( StepCode . INITIALIZING )
505- const [ message , setMessage ] = useState ( '' )
506- const [ progress , setProgress ] = useState ( - 1 )
507- const [ error , setError ] = useState ( ErrorCode . NONE )
508- const [ connected , setConnected ] = useState ( false )
509- const [ serial , setSerial ] = useState ( null )
502+ const [ status , setStatus ] = useState ( {
503+ step : StepCode . INITIALIZING ,
504+ message : '' ,
505+ progress : - 1 ,
506+ error : ErrorCode . NONE ,
507+ connected : false ,
508+ serial : null ,
509+ } )
510510 const [ selectedDevice , setSelectedDevice ] = useState ( null )
511511 const [ wizardScreen , setWizardScreen ] = useState ( 'landing' ) // 'landing', 'device', 'zadig', 'connect', 'unbind', 'webusb', 'flash'
512512
@@ -524,51 +524,40 @@ export default function Flash() {
524524 . then ( ( res ) => res . arrayBuffer ( ) )
525525 . then ( ( programmer ) => {
526526 // Create QDL manager with callbacks that update React state
527- qdlManager . current = new FlashManager ( programmer , {
528- onStepChange : setStep ,
529- onMessageChange : setMessage ,
530- onProgressChange : setProgress ,
531- onErrorChange : setError ,
532- onConnectionChange : setConnected ,
533- onSerialChange : setSerial
527+ qdlManager . current = new FlashManager ( programmer , ( update ) => {
528+ setStatus ( ( prev ) => ( { ...prev , ...update } ) )
534529 } )
535530
536531 // Initialize the manager
537532 return qdlManager . current . initialize ( imageManager . current )
538533 } )
539534 . catch ( ( err ) => {
540535 console . error ( 'Error initializing Flash manager:' , err )
541- setError ( ErrorCode . UNKNOWN )
536+ setStatus ( ( prev ) => ( { ... prev , error : ErrorCode . UNKNOWN } ) )
542537 } )
543538 } , [ config , imageManager . current ] )
544539
545540 // Transition to flash screen when connected
546541 useEffect ( ( ) => {
547- if ( connected && wizardScreen === 'webusb' ) {
542+ if ( status . connected && wizardScreen === 'webusb' ) {
548543 setWizardScreen ( 'flash' )
549544 }
550- } , [ connected , wizardScreen ] )
545+ } , [ status . connected , wizardScreen ] )
551546
552547 // Handle user clicking start on landing page
553548 const handleStart = ( ) => {
554- setStep ( StepCode . DEVICE_PICKER )
549+ setStatus ( ( prev ) => ( { ... prev , step : StepCode . DEVICE_PICKER } ) )
555550 setWizardScreen ( 'device' )
556551 }
557552
558553 // Handle device selection
559554 const handleDeviceSelect = ( deviceType ) => {
560555 setSelectedDevice ( deviceType )
561- if ( isWindows ) {
562- setWizardScreen ( 'zadig' )
563- } else {
564- setWizardScreen ( 'connect' )
565- }
556+ setWizardScreen ( isWindows ? 'zadig' : 'connect' )
566557 }
567558
568559 // Handle zadig done
569- const handleZadigDone = ( ) => {
570- setWizardScreen ( 'connect' )
571- }
560+ const handleZadigDone = ( ) => setWizardScreen ( 'connect' )
572561
573562 // Handle connect instructions next
574563 const handleConnectNext = ( ) => {
@@ -581,21 +570,17 @@ export default function Flash() {
581570 }
582571
583572 // Handle linux unbind done
584- const handleUnbindDone = ( ) => {
585- setWizardScreen ( 'webusb' )
586- }
573+ const handleUnbindDone = ( ) => setWizardScreen ( 'webusb' )
587574
588575 // Handle WebUSB connect button
589- const handleWebUSBConnect = ( ) => {
590- qdlManager . current ?. start ( )
591- }
576+ const handleWebUSBConnect = ( ) => qdlManager . current ?. start ( )
592577
593578 // Handle going back in wizard
594579 const handleWizardBack = ( toStep ) => {
595580 const stepName = wizardSteps [ toStep ]
596581 const backMapping = {
597582 Device : ( ) => {
598- setStep ( StepCode . DEVICE_PICKER )
583+ setStatus ( ( prev ) => ( { ... prev , step : StepCode . DEVICE_PICKER } ) )
599584 setWizardScreen ( 'device' )
600585 setSelectedDevice ( null )
601586 } ,
@@ -610,19 +595,22 @@ export default function Flash() {
610595 const handleRetry = ( ) => window . location . reload ( )
611596
612597 // Transitions & UI State
613- const uiState = steps [ step ] || { }
614- if ( error ) {
615- Object . assign ( uiState , errors [ ErrorCode . UNKNOWN ] , errors [ error ] )
598+ // FIX: use spread to avoid mutating constant 'steps'
599+ const uiState = { ...( steps [ status . step ] || { } ) }
600+ if ( status . error !== ErrorCode . NONE ) {
601+ Object . assign ( uiState , errors [ ErrorCode . UNKNOWN ] , errors [ status . error ] )
616602 }
617- let { status, description, bgColor = 'bg-gray-400' , icon = bolt , iconStyle = 'invert' , hideRetry = false } = uiState
603+ let { status : statusText , description, bgColor = 'bg-gray-400' , icon = bolt , iconStyle = 'invert' , hideRetry = false } = uiState
618604
619- if ( error === ErrorCode . LOST_CONNECTION && isLinux && selectedDevice === DeviceType . COMMA_3 ) {
605+ if ( status . error === ErrorCode . LOST_CONNECTION && isLinux && selectedDevice === DeviceType . COMMA_3 ) {
620606 description += ' Did you forget to unbind the device from qcserial?'
621607 }
622608
623- let title = ( message && ! error ) ? `${ message } ...${ progress >= 0 ? ` (${ ( progress * 100 ) . toFixed ( 0 ) } %)` : '' } ` : ( error === ErrorCode . STORAGE_SPACE ? message : status )
609+ let title = ( status . message && status . error === ErrorCode . NONE )
610+ ? `${ status . message } ...${ status . progress >= 0 ? ` (${ ( status . progress * 100 ) . toFixed ( 0 ) } %)` : '' } `
611+ : ( status . error === ErrorCode . STORAGE_SPACE ? status . message : statusText )
624612
625- if ( step >= StepCode . REPAIR_PARTITION_TABLES && step <= StepCode . FINALIZING && error === ErrorCode . NONE ) {
613+ if ( status . step >= StepCode . REPAIR_PARTITION_TABLES && status . step <= StepCode . FINALIZING && status . error === ErrorCode . NONE ) {
626614 window . addEventListener ( "beforeunload" , beforeUnloadListener , { capture : true } )
627615 } else {
628616 window . removeEventListener ( "beforeunload" , beforeUnloadListener , { capture : true } )
@@ -636,7 +624,7 @@ export default function Flash() {
636624 webusb : < WebUSBConnect onConnect = { handleWebUSBConnect } /> ,
637625 }
638626
639- if ( wizardScreen === 'landing' && ! error ) {
627+ if ( wizardScreen === 'landing' && status . error === ErrorCode . NONE ) {
640628 return (
641629 < >
642630 < ImagePreloader />
@@ -645,8 +633,8 @@ export default function Flash() {
645633 )
646634 }
647635
648- const canGoBack = ( step === StepCode . CONNECTING && ! connected ) || ( wizardStep >= 0 && wizardStep < wizardSteps . indexOf ( 'Flash' ) )
649- const activeWizardScreen = ! error && WIZARD_SCREENS [ wizardScreen ]
636+ const canGoBack = ( status . step === StepCode . CONNECTING && ! status . connected ) || ( wizardStep >= 0 && wizardStep < wizardSteps . indexOf ( 'Flash' ) )
637+ const activeWizardScreen = status . error === ErrorCode . NONE && WIZARD_SCREENS [ wizardScreen ]
650638
651639 return (
652640 < div id = "flash" className = "wizard-screen relative flex flex-col gap-8 justify-center items-center h-full" >
@@ -665,23 +653,23 @@ export default function Flash() {
665653 alt = "status"
666654 width = { 128 }
667655 height = { 128 }
668- className = { `${ iconStyle } ${ ! error && step !== StepCode . DONE ? 'animate-pulse' : '' } ` }
656+ className = { `${ iconStyle } ${ status . error === ErrorCode . NONE && status . step !== StepCode . DONE ? 'animate-pulse' : '' } ` }
669657 />
670658 </ div >
671- < div className = "w-full max-w-3xl px-8 transition-opacity duration-300" style = { { opacity : progress === - 1 ? 0 : 1 } } >
672- < LinearProgress value = { progress * 100 } barColor = { bgColor } />
659+ < div className = "w-full max-w-3xl px-8 transition-opacity duration-300" style = { { opacity : status . progress === - 1 ? 0 : 1 } } >
660+ < LinearProgress value = { status . progress * 100 } barColor = { bgColor } />
673661 </ div >
674662 < span className = "text-3xl font-mono font-light" > { title } </ span >
675663 < span className = "text-xl px-8 max-w-xl text-center" > { description } </ span >
676- { error !== ErrorCode . NONE && ! hideRetry && (
664+ { status . error !== ErrorCode . NONE && ! hideRetry && (
677665 < button
678666 className = "px-4 py-2 rounded-md bg-gray-200 hover:bg-gray-300 text-gray-800 transition-colors"
679667 onClick = { handleRetry }
680668 >
681669 Retry
682670 </ button >
683671 ) }
684- { connected && < DeviceState serial = { serial } /> }
672+ { status . connected && < DeviceState serial = { status . serial } /> }
685673 </ >
686674 ) }
687675 </ div >
0 commit comments