Skip to content

Commit bccbeb5

Browse files
authored
Merge pull request #184 from Indu61/feature/file-import-export-option-119
Feature/file import export option 119
2 parents 6d7ca3d + e1d4e54 commit bccbeb5

File tree

1 file changed

+209
-1
lines changed

1 file changed

+209
-1
lines changed

src/pages/EditorComponent.js

Lines changed: 209 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { Avatar, Button, CircularProgress, styled } from "@mui/material";
44
import Box from "@mui/material/Box";
55
import { useSnackbar } from "notistack";
66
import React, { useCallback, useEffect, useRef, useState } from "react";
7-
import { FaPlay } from "react-icons/fa";
7+
import { FaPlay, FaFileUpload, FaFileDownload } from "react-icons/fa";
8+
// import { FaFileUpload } from "react-icons/fa";
89
import GithubSignIn from "../components/GithubSignIn";
910
import GoogleSignIn from "../components/GoogleSignIn";
1011
import "../components/css/EditorComponent.css";
@@ -22,6 +23,7 @@ import {
2223
} from "../constants/constants";
2324
import { useAuth } from "../context/AuthContext";
2425
import Footer from "../components/Footer";
26+
// import FileUploadIcon from "@mui/icons-material/FileUpload";
2527

2628
const StyledButton = styled(Button)({
2729
display: "flex",
@@ -101,6 +103,8 @@ function EditorComponent() {
101103
};
102104

103105
useEffect(() => {
106+
if (isImportingRef.current) return;
107+
104108
const selectedLanguage = LANGUAGES.find(
105109
(lang) => lang.DEFAULT_LANGUAGE === currentLanguage
106110
);
@@ -206,6 +210,96 @@ function EditorComponent() {
206210
}
207211
}, [enqueueSnackbar, languageDetails]);
208212

213+
// import file
214+
const [isImporting, setIsImporting] = React.useState(false);
215+
const isImportingRef = useRef(false);
216+
const fileInputRef = React.useRef(null);
217+
218+
const handleFileImport = (e) => {
219+
const file = e.target.files[0];
220+
if (!file) return;
221+
222+
setCode("");
223+
if (fileInputRef.current) {
224+
fileInputRef.current.value = "";
225+
}
226+
227+
isImportingRef.current = true;
228+
setIsImporting(true);
229+
230+
const extension = file.name.split(".").pop().toLowerCase();
231+
232+
const languageMap = {
233+
js: "Javascript",
234+
py: "Python3",
235+
cpp: "C++",
236+
java: "Java",
237+
};
238+
239+
const languageName = languageMap[extension];
240+
const selectedLanguage = LANGUAGES.find(
241+
(lang) => lang.NAME === languageName
242+
);
243+
if (!selectedLanguage) {
244+
console.error("Unsupported file type");
245+
enqueueSnackbar("Unsupported file type", { variant: "error" });
246+
isImportingRef.current = false;
247+
setIsImporting(false);
248+
return;
249+
}
250+
const reader = new FileReader();
251+
reader.onload = (event) => {
252+
setCurrentLanguage(selectedLanguage.DEFAULT_LANGUAGE);
253+
setLanguageDetails({
254+
ID: selectedLanguage.ID,
255+
NAME: selectedLanguage.NAME,
256+
DEFAULT_LANGUAGE: selectedLanguage.DEFAULT_LANGUAGE,
257+
LANGUAGE_NAME: selectedLanguage.NAME,
258+
});
259+
setCode(event.target.result);
260+
// console.log("file code ", event.target.result);
261+
setOutput("");
262+
setIsImporting(false);
263+
};
264+
reader.onerror = () => {
265+
console.error("Error reading file");
266+
isImportingRef.current = false;
267+
setIsImporting(false);
268+
};
269+
reader.readAsText(file);
270+
};
271+
272+
// download file
273+
const [isDownloading, setDownloading] = React.useState(false);
274+
const exportFile = () => {
275+
if (!code) return;
276+
277+
setDownloading(true);
278+
const fileContent = code;
279+
280+
const extensionMap = {
281+
javascript: "js",
282+
python: "py",
283+
cpp: "cpp",
284+
java: "java",
285+
};
286+
287+
const extension = extensionMap[languageDetails.DEFAULT_LANGUAGE] || "txt";
288+
289+
const blob = new Blob([fileContent], { type: "text/plain" });
290+
const url = URL.createObjectURL(blob);
291+
const link = document.createElement("a");
292+
293+
link.href = url;
294+
link.download = `code.${extension}`;
295+
document.body.appendChild(link);
296+
link.click();
297+
298+
document.body.removeChild(link);
299+
URL.revokeObjectURL(url);
300+
setDownloading(false);
301+
};
302+
209303
const handleEditorDidMount = useCallback(
210304
(editor, monaco) => {
211305
console.log("Editor mounted"); // Debug log
@@ -244,6 +338,7 @@ function EditorComponent() {
244338
}, [handleEditorDidMount]);
245339

246340
function handleLanguageChange(_, value) {
341+
if (isImporting) return;
247342
setCurrentLanguage(value.DEFAULT_LANGUAGE);
248343
setOutput("");
249344
setCode(code ? code : value.HELLO_WORLD);
@@ -273,6 +368,119 @@ function EditorComponent() {
273368
className="sidebar"
274369
style={{ display: "flex", flexDirection: "column" }}
275370
>
371+
{/* import and export btn */}
372+
<div style={{ display: "flex", flexDirection: "row", gap: "0.5rem" }}>
373+
<StyledButton
374+
onClick={() => fileInputRef.current.click()}
375+
disabled={isImporting}
376+
sx={(theme) => ({
377+
padding: "8px 10px",
378+
backgroundColor: theme.palette.primary.main,
379+
color: theme.palette.primary.contrastText,
380+
border: `1px solid ${theme.palette.primary.dark}`,
381+
borderRadius: "8px",
382+
fontSize: "0.875rem",
383+
fontWeight: 500,
384+
cursor: "pointer",
385+
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.08)",
386+
transition: "all 0.2s ease",
387+
display: "flex",
388+
alignItems: "center",
389+
justifyContent: "center",
390+
gap: "8px",
391+
"&:hover": {
392+
backgroundColor: theme.palette.primary.dark,
393+
boxShadow: "0 4px 8px rgba(0, 0, 0, 0.12)",
394+
transform: "translateY(-1px)",
395+
},
396+
"&:active": {
397+
transform: "translateY(0)",
398+
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
399+
},
400+
"&:disabled": {
401+
backgroundColor: theme.palette.action.disabled,
402+
color: theme.palette.action.disabledBackground,
403+
cursor: "not-allowed",
404+
transform: "none",
405+
},
406+
"@media (max-width: 768px)": {
407+
padding: "8px 12px",
408+
fontSize: "0.8125rem",
409+
},
410+
})}
411+
>
412+
{isImporting ? (
413+
<>
414+
<CircularProgress size={16} color="inherit" />
415+
Importing...
416+
</>
417+
) : (
418+
<>
419+
<FaFileUpload fontSize="small" />
420+
Import
421+
</>
422+
)}
423+
</StyledButton>
424+
<input
425+
type="file"
426+
ref={fileInputRef}
427+
style={{ display: "none" }}
428+
accept=".java,.js,.py,.cpp"
429+
onChange={handleFileImport}
430+
/>
431+
432+
<StyledButton
433+
onClick={exportFile}
434+
sx={(theme) => ({
435+
padding: "8px 10px",
436+
backgroundColor: theme.palette.primary.main,
437+
color: theme.palette.primary.contrastText,
438+
border: `1px solid ${theme.palette.primary.dark}`,
439+
borderRadius: "8px",
440+
fontSize: "0.875rem",
441+
fontWeight: 500,
442+
cursor: "pointer",
443+
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.08)",
444+
transition: "all 0.2s ease",
445+
display: "flex",
446+
alignItems: "center",
447+
justifyContent: "center",
448+
gap: "8px",
449+
"&:hover": {
450+
backgroundColor: theme.palette.primary.dark,
451+
boxShadow: "0 4px 8px rgba(0, 0, 0, 0.12)",
452+
transform: "translateY(-1px)",
453+
},
454+
"&:active": {
455+
transform: "translateY(0)",
456+
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
457+
},
458+
"&:disabled": {
459+
backgroundColor: theme.palette.action.disabled,
460+
color: theme.palette.action.disabledBackground,
461+
cursor: "not-allowed",
462+
transform: "none",
463+
},
464+
"@media (max-width: 768px)": {
465+
padding: "8px 12px",
466+
fontSize: "0.8125rem",
467+
},
468+
})}
469+
>
470+
{isDownloading ? (
471+
<>
472+
<CircularProgress size={16} color="inherit" />
473+
Exporting...
474+
</>
475+
) : (
476+
<>
477+
<FaFileDownload fontSize="small" />
478+
Export
479+
</>
480+
)}
481+
</StyledButton>
482+
</div>
483+
276484
{getLanguageLogoById(languageDetails.ID)}
277485
<div style={{ fontWeight: "bold" }}>
278486
{languageDetails.LANGUAGE_NAME}

0 commit comments

Comments
 (0)