1
- import { useState , useEffect } from "react" ;
1
+ import { useState , useEffect , useRef } from "react" ;
2
2
import { BrowserRouter as Router , Routes , Route , NavLink } from "react-router-dom" ;
3
3
import {
4
4
IconButton ,
@@ -38,6 +38,7 @@ import CssBaseline from '@mui/material/CssBaseline';
38
38
import CodeIcon from '@mui/icons-material/Code' ;
39
39
import MenuIcon from '@mui/icons-material/Menu' ;
40
40
import DownloadIcon from '@mui/icons-material/Download' ;
41
+ import UploadIcon from '@mui/icons-material/Upload' ;
41
42
import "./App.css" ;
42
43
43
44
// Theme options for syntax highlighting
@@ -141,9 +142,14 @@ function App() {
141
142
setSnippets ( snippets . filter ( ( _ , i ) => i !== index ) ) ;
142
143
} ;
143
144
144
- // Download all snippets into JSON file
145
- const downloadSnippets = ( snippets ) => {
146
- const json = JSON . stringify ( snippets , null , 2 ) ; // Pretty print
145
+ // Download all snippets and languages into JSON file
146
+ const downloadSnippets = ( snippets , languages ) => {
147
+ const dataToSave = {
148
+ snippets : snippets , // Snippets array
149
+ languages : languages , // Languages array
150
+ } ;
151
+
152
+ const json = JSON . stringify ( dataToSave , null , 2 ) ; // Pretty print
147
153
const blob = new Blob ( [ json ] , { type : "application/json" } ) ;
148
154
const url = URL . createObjectURL ( blob ) ;
149
155
@@ -156,6 +162,43 @@ function App() {
156
162
URL . revokeObjectURL ( url ) ;
157
163
} ;
158
164
165
+ //ref for hidden file input
166
+ const fileInputRef = useRef ( null ) ;
167
+
168
+ // Function to trigger the file input
169
+ const handleFabClick = ( ) => {
170
+ fileInputRef . current . click ( ) ;
171
+ } ;
172
+
173
+ // Function to handle file selection
174
+ const handleFileChange = ( event ) => {
175
+ const file = event . target . files [ 0 ] ;
176
+ if ( file ) {
177
+ loadFromFile ( file ) ; // Pass the file to the parent function
178
+ }
179
+ } ;
180
+
181
+ // load snippets and languages from JSON file
182
+ const loadFromFile = ( file ) => {
183
+ if ( ! file ) return ;
184
+
185
+ const reader = new FileReader ( ) ;
186
+ reader . onload = ( e ) => {
187
+ try {
188
+ const parsedData = JSON . parse ( e . target . result ) ;
189
+ if ( parsedData . snippets && parsedData . languages ) {
190
+ setSnippets ( parsedData . snippets ) ;
191
+ setLanguages ( parsedData . languages ) ;
192
+ } else {
193
+ alert ( "Invalid JSON format" ) ;
194
+ }
195
+ } catch ( error ) {
196
+ alert ( "Error reading JSON file" ) ;
197
+ }
198
+ } ;
199
+ reader . readAsText ( file ) ;
200
+ } ;
201
+
159
202
// add a language to the list
160
203
const addLanguage = ( newLang ) => {
161
204
setLanguages ( [ ...languages , newLang ] ) ;
@@ -438,85 +481,109 @@ function App() {
438
481
}
439
482
</ Toolbar >
440
483
</ AppBar >
441
- < div className = "App-header" >
442
- { /* Routes for the different pages */ }
443
- < div style = { { width : "50%" , margin : "auto" , paddingTop : "20px" } } >
444
- < Routes >
445
- < Route
446
- path = "/"
447
- element = {
448
- < >
449
- < Box className = "view-snippets-header-box" sx = { {
450
- display : 'flex' ,
451
- justifyContent : 'space-between' ,
452
- alignItems : 'center' ,
453
- mb : 1 ,
454
- mt : 3
455
- } } >
456
- < h2 > Your Snippets</ h2 >
457
- { /* Button for downloading snippets
484
+ { /* Routes for the different pages */ }
485
+ < div className = "route-div" >
486
+ < Routes >
487
+ < Route
488
+ path = "/"
489
+ element = {
490
+ < >
491
+ < Box className = "view-snippets-header-box" sx = { {
492
+ display : 'flex' ,
493
+ justifyContent : 'space-between' ,
494
+ alignItems : 'center' ,
495
+ mb : 1 ,
496
+ mt : 3
497
+ } } >
498
+ < h2 > Your Snippets</ h2 >
499
+ { /* Buttons for downloading and uploading snippets
458
500
Located outside of the SnippetList to avoid the snippet filters that prevent app from downloading them */ }
501
+ < div className = "button-div" >
502
+ { /* Button will be disabled when no snippets are on the list */ }
459
503
< Tooltip title = "Download Snippets" >
460
504
< Fab
461
505
size = "small"
462
- onClick = { ( ) => downloadSnippets ( snippets ) }
506
+ onClick = { ( ) => downloadSnippets ( snippets , languages ) }
507
+ disabled = { snippets . length === 0 }
463
508
sx = { {
464
- backgroundColor : isDarkMode ? '#4caf50 ' : '#2e7d32 ' ,
509
+ backgroundColor : isDarkMode ? '#64B5F6 ' : '#2196F3 ' ,
465
510
color : '#ffffff' ,
466
511
'&:hover' : {
467
- backgroundColor : isDarkMode ? '#45a049 ' : '#1b5e20 ' ,
512
+ backgroundColor : isDarkMode ? '#42A5F5 ' : '#1976D2 ' ,
468
513
}
469
514
} }
470
515
>
471
516
< DownloadIcon />
472
517
</ Fab >
473
518
</ Tooltip >
474
- </ Box >
475
- < ViewSnippets
476
- snippets = { snippets }
477
- languages = { languages }
478
- onDelete = { deleteSnippet }
479
- onUpdate = { handleUpdateSnippet }
480
- isDarkMode = { isDarkMode }
481
- theme = { theme }
482
- selectedCategory = { selectedCategory }
483
- setSelectedCategory = { setSelectedCategory }
484
- selectedLanguage = { selectedLanguage }
485
- setSelectedLanguage = { setSelectedLanguage }
486
- filteredSnippets = { filteredSnippets }
487
- />
488
- </ >
489
- }
490
- />
491
- { /* only contains the SnippetForm component and props */ }
492
- < Route
493
- path = "/add"
494
- element = {
495
- < >
496
- < h2 > Add Snippet</ h2 >
497
- < SnippetForm
498
- onSave = { addSnippet }
499
- languages = { languages }
500
- isDarkMode = { isDarkMode }
501
- theme = { theme }
502
- />
503
- </ >
504
- }
505
- />
506
- < Route
507
- path = "/languages"
508
- element = {
509
- < ManageLanguages
519
+ { /* When clicked, this Fab triggers hidden input file's upload function */ }
520
+ < Tooltip title = "Upload Snippets from JSON File" >
521
+ < input
522
+ type = "file"
523
+ ref = { fileInputRef }
524
+ accept = ".json"
525
+ onChange = { handleFileChange }
526
+ />
527
+ < Fab
528
+ size = "small"
529
+ onClick = { handleFabClick }
530
+ sx = { {
531
+ backgroundColor : isDarkMode ? '#455A64' : '#B0BEC5' ,
532
+ color : '#ffffff' ,
533
+ '&:hover' : {
534
+ backgroundColor : isDarkMode ? '#546E7A' : '#90CAF9' ,
535
+ }
536
+ } }
537
+ >
538
+ < UploadIcon />
539
+ </ Fab >
540
+ </ Tooltip >
541
+ </ div >
542
+ </ Box >
543
+ < ViewSnippets
544
+ snippets = { snippets }
510
545
languages = { languages }
511
- onUpdateLanguage = { handleUpdateLanguage }
512
- onDeleteLanguage = { handleDeleteLanguage }
513
- onAddLanguage = { addLanguage }
546
+ onDelete = { deleteSnippet }
547
+ onUpdate = { handleUpdateSnippet }
514
548
isDarkMode = { isDarkMode }
549
+ theme = { theme }
550
+ selectedCategory = { selectedCategory }
551
+ setSelectedCategory = { setSelectedCategory }
552
+ selectedLanguage = { selectedLanguage }
553
+ setSelectedLanguage = { setSelectedLanguage }
554
+ filteredSnippets = { filteredSnippets }
515
555
/>
516
- }
517
- />
518
- </ Routes >
519
- </ div >
556
+ </ >
557
+ }
558
+ />
559
+ { /* only contains the SnippetForm component and props */ }
560
+ < Route
561
+ path = "/add"
562
+ element = {
563
+ < >
564
+ < h2 > Add Snippet</ h2 >
565
+ < SnippetForm
566
+ onSave = { addSnippet }
567
+ languages = { languages }
568
+ isDarkMode = { isDarkMode }
569
+ theme = { theme }
570
+ />
571
+ </ >
572
+ }
573
+ />
574
+ < Route
575
+ path = "/languages"
576
+ element = {
577
+ < ManageLanguages
578
+ languages = { languages }
579
+ onUpdateLanguage = { handleUpdateLanguage }
580
+ onDeleteLanguage = { handleDeleteLanguage }
581
+ onAddLanguage = { addLanguage }
582
+ isDarkMode = { isDarkMode }
583
+ />
584
+ }
585
+ />
586
+ </ Routes >
520
587
</ div >
521
588
</ div >
522
589
</ Router >
0 commit comments