@@ -4,7 +4,8 @@ import { Avatar, Button, CircularProgress, styled } from "@mui/material";
44import Box from "@mui/material/Box" ;
55import { useSnackbar } from "notistack" ;
66import 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";
89import GithubSignIn from "../components/GithubSignIn" ;
910import GoogleSignIn from "../components/GoogleSignIn" ;
1011import "../components/css/EditorComponent.css" ;
@@ -22,6 +23,7 @@ import {
2223} from "../constants/constants" ;
2324import { useAuth } from "../context/AuthContext" ;
2425import Footer from "../components/Footer" ;
26+ // import FileUploadIcon from "@mui/icons-material/FileUpload";
2527
2628const 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