Skip to content

Commit 482234b

Browse files
committed
Finished implementation of loading JSON file to app; refactored download snippets function to save languages as well
1 parent a1a03e8 commit 482234b

File tree

2 files changed

+149
-67
lines changed

2 files changed

+149
-67
lines changed

src/App.css

+15
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
margin-right: 20px;
1111
}
1212

13+
.button-div {
14+
display: flex;
15+
gap: 8px;
16+
}
17+
1318
.component-div {
1419
padding-bottom: 20px;
1520
}
@@ -49,6 +54,12 @@
4954
flex-grow: 1;
5055
}
5156

57+
.route-div {
58+
width: 50%;
59+
margin: auto;
60+
padding-top: 20px;
61+
}
62+
5263
.snippet-dropdown {
5364
padding: 8px;
5465
border: 1px solid #ccc;
@@ -69,6 +80,10 @@
6980
margin-top: 20px;
7081
}
7182

83+
input[type="file"] {
84+
display: none;
85+
}
86+
7287
textarea {
7388
width: 100%;
7489
min-height: 200px;

src/App.jsx

+134-67
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useState, useEffect } from "react";
1+
import { useState, useEffect, useRef } from "react";
22
import { BrowserRouter as Router, Routes, Route, NavLink } from "react-router-dom";
33
import {
44
IconButton,
@@ -38,6 +38,7 @@ import CssBaseline from '@mui/material/CssBaseline';
3838
import CodeIcon from '@mui/icons-material/Code';
3939
import MenuIcon from '@mui/icons-material/Menu';
4040
import DownloadIcon from '@mui/icons-material/Download';
41+
import UploadIcon from '@mui/icons-material/Upload';
4142
import "./App.css";
4243

4344
// Theme options for syntax highlighting
@@ -141,9 +142,14 @@ function App() {
141142
setSnippets(snippets.filter((_, i) => i !== index));
142143
};
143144

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
147153
const blob = new Blob([json], { type: "application/json" });
148154
const url = URL.createObjectURL(blob);
149155

@@ -156,6 +162,43 @@ function App() {
156162
URL.revokeObjectURL(url);
157163
};
158164

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+
159202
// add a language to the list
160203
const addLanguage = (newLang) => {
161204
setLanguages([...languages, newLang]);
@@ -438,85 +481,109 @@ function App() {
438481
}
439482
</Toolbar>
440483
</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
458500
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 */}
459503
<Tooltip title="Download Snippets">
460504
<Fab
461505
size="small"
462-
onClick={() => downloadSnippets(snippets)}
506+
onClick={() => downloadSnippets(snippets, languages)}
507+
disabled={snippets.length === 0}
463508
sx={{
464-
backgroundColor: isDarkMode ? '#4caf50' : '#2e7d32',
509+
backgroundColor: isDarkMode ? '#64B5F6' : '#2196F3',
465510
color: '#ffffff',
466511
'&:hover': {
467-
backgroundColor: isDarkMode ? '#45a049' : '#1b5e20',
512+
backgroundColor: isDarkMode ? '#42A5F5' : '#1976D2',
468513
}
469514
}}
470515
>
471516
<DownloadIcon />
472517
</Fab>
473518
</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}
510545
languages={languages}
511-
onUpdateLanguage={handleUpdateLanguage}
512-
onDeleteLanguage={handleDeleteLanguage}
513-
onAddLanguage={addLanguage}
546+
onDelete={deleteSnippet}
547+
onUpdate={handleUpdateSnippet}
514548
isDarkMode={isDarkMode}
549+
theme={theme}
550+
selectedCategory={selectedCategory}
551+
setSelectedCategory={setSelectedCategory}
552+
selectedLanguage={selectedLanguage}
553+
setSelectedLanguage={setSelectedLanguage}
554+
filteredSnippets={filteredSnippets}
515555
/>
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>
520587
</div>
521588
</div>
522589
</Router>

0 commit comments

Comments
 (0)