1
1
import { useEffect , useRef , useState } from 'react'
2
2
3
- import { FlashManager , StepCode , ErrorCode } from '../utils/manager'
3
+ import { FlashManager , StepCode } from '../utils/manager'
4
4
import { useImageManager } from '../utils/image'
5
- import { isLinux } from '../utils/platform '
5
+ import { parseError } from '../utils/errors '
6
6
import config from '../config'
7
7
8
8
import bolt from '../assets/bolt.svg'
@@ -13,6 +13,16 @@ import done from '../assets/done.svg'
13
13
import exclamation from '../assets/exclamation.svg'
14
14
import systemUpdate from '../assets/system_update_c3.svg'
15
15
16
+ // Icons mapped by name for error handling
17
+ const icons = {
18
+ bolt,
19
+ cable,
20
+ deviceExclamation,
21
+ deviceQuestion,
22
+ done,
23
+ exclamation,
24
+ systemUpdate
25
+ }
16
26
17
27
const steps = {
18
28
[ StepCode . INITIALIZING ] : {
@@ -64,60 +74,6 @@ const steps = {
64
74
} ,
65
75
}
66
76
67
- const errors = {
68
- [ ErrorCode . UNKNOWN ] : {
69
- status : 'Unknown error' ,
70
- description : 'An unknown error has occurred. Unplug your device, restart your browser and try again.' ,
71
- bgColor : 'bg-red-500' ,
72
- icon : exclamation ,
73
- } ,
74
- [ ErrorCode . REQUIREMENTS_NOT_MET ] : {
75
- status : 'Requirements not met' ,
76
- description : 'Your system does not meet the requirements to flash your device. Make sure to use a browser which ' +
77
- 'supports WebUSB and is up to date.' ,
78
- } ,
79
- [ ErrorCode . STORAGE_SPACE ] : {
80
- description : 'Your system does not have enough space available to download AGNOS. Your browser may be restricting' +
81
- ' the available space if you are in a private, incognito or guest session.' ,
82
- } ,
83
- [ ErrorCode . UNRECOGNIZED_DEVICE ] : {
84
- status : 'Unrecognized device' ,
85
- description : 'The device connected to your computer is not supported. Try using a different cable, USB port, or ' +
86
- 'computer. If the problem persists, join the #hw-three-3x channel on Discord for help.' ,
87
- bgColor : 'bg-yellow-500' ,
88
- icon : deviceQuestion ,
89
- } ,
90
- [ ErrorCode . LOST_CONNECTION ] : {
91
- status : 'Lost connection' ,
92
- description : 'The connection to your device was lost. Unplug your device and try again.' ,
93
- icon : cable ,
94
- } ,
95
- [ ErrorCode . REPAIR_PARTITION_TABLES_FAILED ] : {
96
- status : 'Repairing partition tables failed' ,
97
- description : 'Your device\'s partition tables could not be repaired. Try using a different cable, USB port, or ' +
98
- 'computer. If the problem persists, join the #hw-three-3x channel on Discord for help.' ,
99
- icon : deviceExclamation ,
100
- } ,
101
- [ ErrorCode . ERASE_FAILED ] : {
102
- status : 'Erase failed' ,
103
- description : 'The device could not be erased. Try using a different cable, USB port, or computer. If the problem ' +
104
- 'persists, join the #hw-three-3x channel on Discord for help.' ,
105
- icon : deviceExclamation ,
106
- } ,
107
- [ ErrorCode . FLASH_SYSTEM_FAILED ] : {
108
- status : 'Flash failed' ,
109
- description : 'AGNOS could not be flashed to your device. Try using a different cable, USB port, or computer. If ' +
110
- 'the problem persists, join the #hw-three-3x channel on Discord for help.' ,
111
- icon : deviceExclamation ,
112
- } ,
113
- }
114
-
115
- if ( isLinux ) {
116
- // this is likely in StepCode.CONNECTING
117
- errors [ ErrorCode . LOST_CONNECTION ] . description += ' Did you forget to unbind the device from qcserial?'
118
- }
119
-
120
-
121
77
function LinearProgress ( { value, barColor } ) {
122
78
if ( value === - 1 || value > 100 ) value = 100
123
79
return (
@@ -130,7 +86,6 @@ function LinearProgress({ value, barColor }) {
130
86
)
131
87
}
132
88
133
-
134
89
function USBIndicator ( ) {
135
90
return < div className = "flex flex-row gap-2" >
136
91
< svg
@@ -149,7 +104,6 @@ function USBIndicator() {
149
104
</ div >
150
105
}
151
106
152
-
153
107
function SerialIndicator ( { serial } ) {
154
108
return < div className = "flex flex-row gap-2" >
155
109
< span >
@@ -159,7 +113,6 @@ function SerialIndicator({ serial }) {
159
113
</ div >
160
114
}
161
115
162
-
163
116
function DeviceState ( { serial } ) {
164
117
return (
165
118
< div
@@ -173,19 +126,17 @@ function DeviceState({ serial }) {
173
126
)
174
127
}
175
128
176
-
177
129
function beforeUnloadListener ( event ) {
178
130
// NOTE: not all browsers will show this message
179
131
event . preventDefault ( )
180
132
return ( event . returnValue = "Flash in progress. Are you sure you want to leave?" )
181
133
}
182
134
183
-
184
135
export default function Flash ( ) {
185
136
const [ step , setStep ] = useState ( StepCode . INITIALIZING )
186
137
const [ message , setMessage ] = useState ( '' )
187
138
const [ progress , setProgress ] = useState ( - 1 )
188
- const [ error , setError ] = useState ( ErrorCode . NONE )
139
+ const [ error , setError ] = useState ( null )
189
140
const [ connected , setConnected ] = useState ( false )
190
141
const [ serial , setSerial ] = useState ( null )
191
142
@@ -210,36 +161,59 @@ export default function Flash() {
210
161
211
162
// Initialize the manager
212
163
qdlManager . current . initialize ( imageManager . current )
213
- } ) ;
164
+ . catch ( err => setError ( err ) )
165
+ } )
166
+ . catch ( err => setError ( err ) )
214
167
} , [ config , imageManager . current ] )
215
168
216
169
// Handle user clicking the start button
217
- const handleStart = ( ) => qdlManager . current ?. start ( )
170
+ const handleStart = async ( ) => {
171
+ if ( ! qdlManager . current ) return
172
+
173
+ try {
174
+ await qdlManager . current . start ( )
175
+ } catch ( err ) {
176
+ // Error is already set via onErrorChange callback
177
+ console . error ( 'Flash failed:' , err )
178
+ }
179
+ }
180
+
218
181
const canStart = step === StepCode . READY && ! error
219
182
220
183
// Handle retry on error
221
184
const handleRetry = ( ) => window . location . reload ( )
222
185
186
+ // Get the UI state based on current step or error
223
187
const uiState = steps [ step ]
188
+ let errorState = { }
189
+
224
190
if ( error ) {
225
- Object . assign ( uiState , errors [ ErrorCode . UNKNOWN ] , errors [ error ] )
191
+ errorState = parseError ( error )
192
+ // Map icon name to actual icon
193
+ if ( errorState . icon && icons [ errorState . icon ] ) {
194
+ errorState . icon = icons [ errorState . icon ]
195
+ } else {
196
+ errorState . icon = exclamation
197
+ }
226
198
}
227
- const { status, description, bgColor, icon, iconStyle = 'invert' } = uiState
228
199
200
+ // Combine step state with error state if there's an error
201
+ const { status, description, bgColor, icon, iconStyle = 'invert' } =
202
+ error ? { ...uiState , ...errorState } : uiState
203
+
204
+ // Determine the title to display
229
205
let title
230
206
if ( message && ! error ) {
231
207
title = message + '...'
232
208
if ( progress >= 0 ) {
233
209
title += ` (${ ( progress * 100 ) . toFixed ( 0 ) } %)`
234
210
}
235
- } else if ( error === ErrorCode . STORAGE_SPACE ) {
236
- title = message
237
211
} else {
238
212
title = status
239
213
}
240
214
241
215
// warn the user if they try to leave the page while flashing
242
- if ( step >= StepCode . FLASH_GPT && step <= StepCode . FLASH_SYSTEM ) {
216
+ if ( step >= StepCode . REPAIR_PARTITION_TABLES && step <= StepCode . FINALIZING ) {
243
217
window . addEventListener ( "beforeunload" , beforeUnloadListener , { capture : true } )
244
218
} else {
245
219
window . removeEventListener ( "beforeunload" , beforeUnloadListener , { capture : true } )
@@ -254,7 +228,7 @@ export default function Flash() {
254
228
>
255
229
< img
256
230
src = { icon }
257
- alt = "cable "
231
+ alt = "status icon "
258
232
width = { 128 }
259
233
height = { 128 }
260
234
className = { `${ iconStyle } ${ ! error && step !== StepCode . DONE ? 'animate-pulse' : '' } ` }
@@ -264,15 +238,15 @@ export default function Flash() {
264
238
< LinearProgress value = { progress * 100 } barColor = { bgColor } />
265
239
</ div >
266
240
< span className = "text-3xl dark:text-white font-mono font-light" > { title } </ span >
267
- < span className = "text-xl dark:text-white px-8 max-w-xl" > { description } </ span >
241
+ < span className = "text-xl dark:text-white px-8 max-w-xl text-center " > { description } </ span >
268
242
{ error && (
269
243
< button
270
244
className = "px-4 py-2 rounded-md bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-800 dark:text-gray-200 transition-colors"
271
245
onClick = { handleRetry }
272
246
>
273
247
Retry
274
248
</ button >
275
- ) || false }
249
+ ) }
276
250
{ connected && < DeviceState connected = { connected } serial = { serial } /> }
277
251
</ div >
278
252
)
0 commit comments