@@ -66,10 +66,46 @@ export function Settings() {
66
66
ref . current = true ;
67
67
} , [ ] ) ;
68
68
69
- function onFileChange ( e : ChangeEvent < HTMLInputElement > ) {
69
+ async function handleFaviconChange ( e : ChangeEvent < HTMLInputElement > ) {
70
70
const file = e . target . files ?. [ 0 ] ;
71
71
if ( file ) {
72
- client . wp . post ( {
72
+ const MAX_FILE_SIZE = 10 * 1024 * 1024 ; // 10MB
73
+ if ( file . size > MAX_FILE_SIZE ) {
74
+ showAlert (
75
+ t ( "upload.failed$size" , {
76
+ size : MAX_FILE_SIZE / 1024 / 1024 ,
77
+ } ) ,
78
+ ) ;
79
+ return ;
80
+ }
81
+ await client . favicon
82
+ . post (
83
+ {
84
+ file : file ,
85
+ } ,
86
+ {
87
+ headers : headersWithAuth ( ) ,
88
+ } ,
89
+ )
90
+ . then ( ( { data } ) => {
91
+ if ( data && typeof data !== "string" ) {
92
+ showAlert ( t ( "settings.favicon.update.success" ) ) ;
93
+ }
94
+ } )
95
+ . catch ( ( err ) => {
96
+ showAlert (
97
+ t ( "settings.favicon.update.failed$message" , {
98
+ message : err . message ,
99
+ } ) ,
100
+ ) ;
101
+ } ) ;
102
+ }
103
+ }
104
+
105
+ async function onFileChange ( e : ChangeEvent < HTMLInputElement > ) {
106
+ const file = e . target . files ?. [ 0 ] ;
107
+ if ( file ) {
108
+ await client . wp . post ( {
73
109
data : file ,
74
110
} , {
75
111
headers : headersWithAuth ( )
@@ -106,7 +142,13 @@ export function Settings() {
106
142
< ItemSwitch title = { t ( 'settings.comment.enable.title' ) } description = { t ( 'settings.comment.enable.desc' ) } type = "client" configKey = "comment.enabled" />
107
143
< ItemSwitch title = { t ( 'settings.counter.enable.title' ) } description = { t ( 'settings.counter.enable.desc' ) } type = "client" configKey = "counter.enabled" />
108
144
< ItemSwitch title = { t ( 'settings.rss.title' ) } description = { t ( 'settings.rss.desc' ) } type = "client" configKey = "rss" />
109
- < ItemInput title = { t ( 'settings.favicon.title' ) } description = { t ( 'settings.favicon.desc' ) } type = "client" configKey = "favicon" configKeyTitle = "Favicon" />
145
+ < ItemWithUpload
146
+ title = { t ( "settings.favicon.title" ) }
147
+ description = { t ( "settings.favicon.desc" ) }
148
+ // @see https://developers.cloudflare.com/images/transform-images/#supported-input-formats
149
+ accept = "image/jpeg,image/png,image/gif,image/webp,image/svg+xml"
150
+ onFileChange = { handleFaviconChange }
151
+ />
110
152
< ItemInput title = { t ( 'settings.footer.title' ) } description = { t ( 'settings.footer.desc' ) } type = "client" configKey = "footer" configKeyTitle = "Footer HTML" />
111
153
< ItemButton title = { t ( 'settings.cache.clear.title' ) } description = { t ( 'settings.cache.clear.desc' ) } buttonTitle = { t ( 'clear' ) } onConfirm = { async ( ) => {
112
154
await client . config . cache . delete ( undefined , {
@@ -119,6 +161,7 @@ export function Settings() {
119
161
} )
120
162
} } alertTitle = { t ( 'settings.cache.clear.confirm.title' ) } alertDescription = { t ( 'settings.cache.clear.confirm.desc' ) } />
121
163
< ItemWithUpload title = { t ( 'settings.wordpress.title' ) } description = { t ( 'settings.wordpress.desc' ) }
164
+ accept = "application/xml"
122
165
onFileChange = { onFileChange } />
123
166
</ div >
124
167
</ main >
@@ -406,25 +449,60 @@ function ItemButton({
406
449
) ;
407
450
}
408
451
409
- function ItemWithUpload ( { title, description, onFileChange } : { title : string , description : string , onFileChange : ( e : ChangeEvent < HTMLInputElement > ) => void } ) {
452
+ function ItemWithUpload ( {
453
+ title,
454
+ description,
455
+ accept,
456
+ onFileChange,
457
+ } : {
458
+ title : string ;
459
+ description : string ;
460
+ onFileChange : ( e : ChangeEvent < HTMLInputElement > ) => Promise < void > ;
461
+ accept : string ;
462
+ } ) {
410
463
const inputRef = useRef < HTMLInputElement > ( null ) ;
464
+ const [ loading , setLoading ] = useState ( false ) ;
411
465
const { t } = useTranslation ( ) ;
466
+
467
+ const handleFileChange = async ( e : ChangeEvent < HTMLInputElement > ) => {
468
+ setLoading ( true ) ;
469
+ try {
470
+ await onFileChange ( e ) ;
471
+ } finally {
472
+ setLoading ( false ) ;
473
+ }
474
+ } ;
475
+
412
476
return (
413
477
< div className = "flex flex-col w-full items-start" >
414
478
< div className = "flex flex-row justify-between w-full items-center" >
415
479
< div className = "flex flex-col" >
416
- < p className = "text-lg font-bold dark:text-white" >
417
- { title }
418
- </ p >
419
- < p className = "text-xs text-neutral-500" >
420
- { description }
421
- </ p >
480
+ < p className = "text-lg font-bold dark:text-white" > { title } </ p >
481
+ < p className = "text-xs text-neutral-500" > { description } </ p >
482
+ </ div >
483
+ < div className = "flex flex-row items-center justify-center space-x-4" >
484
+ { loading && (
485
+ < ReactLoading
486
+ width = "1em"
487
+ height = "1em"
488
+ type = "spin"
489
+ color = "#FC466B"
490
+ />
491
+ ) }
492
+ < input
493
+ ref = { inputRef }
494
+ type = "file"
495
+ className = "hidden"
496
+ accept = { accept }
497
+ onChange = { handleFileChange }
498
+ />
499
+ < Button
500
+ onClick = { ( ) => {
501
+ inputRef . current ?. click ( ) ;
502
+ } }
503
+ title = { t ( "upload.title" ) }
504
+ />
422
505
</ div >
423
- < input ref = { inputRef } type = "file" className = "hidden" accept = "application/xml"
424
- onChange = { onFileChange } />
425
- < Button onClick = { ( ) => {
426
- inputRef . current ?. click ( ) ;
427
- } } title = { t ( 'upload.title' ) } />
428
506
</ div >
429
507
</ div >
430
508
) ;
0 commit comments