2020 */
2121import * as Antd from 'antd' ;
2222import * as React from 'react' ;
23+ import { RcFile , UploadRequestOption } from 'rc-upload/lib/interface' ;
2324import * as commonStorage from '../storage/common_storage' ;
2425import * as createPythonFiles from '../storage/create_python_files' ;
2526import * as I18Next from 'react-i18next' ;
@@ -38,6 +39,8 @@ import {
3839 BgColorsOutlined ,
3940 GlobalOutlined ,
4041 CheckOutlined ,
42+ DownloadOutlined ,
43+ UploadOutlined ,
4144} from '@ant-design/icons' ;
4245import FileManageModal from './FileManageModal' ;
4346import ProjectManageModal from './ProjectManageModal' ;
@@ -180,6 +183,7 @@ export function Component(props: MenuProps): React.JSX.Element {
180183 const [ noProjects , setNoProjects ] = React . useState < boolean > ( false ) ;
181184 const [ aboutDialogVisible , setAboutDialogVisible ] = React . useState < boolean > ( false ) ;
182185 const [ themeModalOpen , setThemeModalOpen ] = React . useState < boolean > ( false ) ;
186+ const [ showUploadAndDownload , _setShowUploadAndDownload ] = React . useState ( false ) ;
183187
184188 const handleThemeChange = ( newTheme : string ) => {
185189 props . setTheme ( newTheme ) ;
@@ -322,6 +326,81 @@ export function Component(props: MenuProps): React.JSX.Element {
322326 }
323327 } ;
324328
329+ // TODO: Add UI for the download action.
330+ /** Handles the download action to generate and download json files. */
331+ const handleDownload = async ( ) : Promise < void > => {
332+ if ( ! props . project || ! props . storage ) {
333+ return ;
334+ }
335+
336+ try {
337+ const blobUrl = await props . storage . downloadProject ( props . project . projectName ) ;
338+ const filename = props . project . projectName + commonStorage . UPLOAD_DOWNLOAD_FILE_EXTENSION ;
339+
340+ // Create a temporary link to download the file
341+ const link = document . createElement ( 'a' ) ;
342+ link . href = blobUrl ;
343+ link . download = filename ;
344+ document . body . appendChild ( link ) ;
345+ link . click ( ) ;
346+ document . body . removeChild ( link ) ;
347+
348+ // Clean up the blob URL
349+ URL . revokeObjectURL ( blobUrl ) ;
350+ } catch ( error ) {
351+ console . error ( 'Failed to download project:' , error ) ;
352+ props . setAlertErrorMessage ( t ( 'DOWNLOAD_FAILED' ) || 'Failed to download project' ) ;
353+ }
354+ }
355+
356+ // TODO: Add UI for the upload action.
357+ /** Handles the upload action to upload a previously downloaded project. */
358+ const handleUpload = ( ) : Antd . UploadProps | null => {
359+ if ( ! props . storage ) {
360+ return null ;
361+ }
362+
363+ const uploadProps : Antd . UploadProps = {
364+ accept : commonStorage . UPLOAD_DOWNLOAD_FILE_EXTENSION ,
365+ beforeUpload : ( file ) => {
366+ const isBlocks = file . name . endsWith ( commonStorage . UPLOAD_DOWNLOAD_FILE_EXTENSION )
367+ if ( ! isBlocks ) {
368+ // TODO: i18n
369+ props . setAlertErrorMessage ( file . name + ' is not a blocks file' ) ;
370+ return false ;
371+ }
372+ return isBlocks || Antd . Upload . LIST_IGNORE ;
373+ } ,
374+ onChange : ( _info ) => {
375+ } ,
376+ customRequest : ( options : UploadRequestOption ) => {
377+ const reader = new FileReader ( ) ;
378+ reader . onload = ( event ) => {
379+ if ( ! event . target ) {
380+ return ;
381+ }
382+ const dataUrl = event . target . result as string ;
383+ const existingProjectNames : string [ ] = [ ] ;
384+ projects . forEach ( project => {
385+ existingProjectNames . push ( project . projectName ) ;
386+ } ) ;
387+ const file = options . file as RcFile ;
388+ const uploadProjectName = commonStorage . makeUploadProjectName ( file . name , existingProjectNames ) ;
389+ if ( props . storage ) {
390+ props . storage . uploadProject ( uploadProjectName , dataUrl ) ;
391+ }
392+ } ;
393+ reader . onerror = ( _error ) => {
394+ console . log ( 'Error reading file: ' + reader . error ) ;
395+ // TODO: i18n
396+ props . setAlertErrorMessage ( t ( 'UPLOAD_FAILED' ) || 'Failed to upload project' ) ;
397+ } ;
398+ reader . readAsDataURL ( options . file as Blob ) ;
399+ } ,
400+ } ;
401+ return uploadProps ;
402+ } ;
403+
325404 /** Handles closing the file management modal. */
326405 const handleFileModalClose = ( ) : void => {
327406 console . log ( 'Modal onCancel called' ) ;
@@ -382,6 +461,30 @@ export function Component(props: MenuProps): React.JSX.Element {
382461 items = { menuItems }
383462 onClick = { handleClick }
384463 />
464+ { showUploadAndDownload ? (
465+ < div >
466+ < Antd . Upload
467+ { ...handleUpload ( ) }
468+ showUploadList = { false }
469+ >
470+ < Antd . Button
471+ icon = { < UploadOutlined /> }
472+ size = "small"
473+ style = { { color : 'white' } }
474+ />
475+ </ Antd . Upload >
476+ < Antd . Button
477+ icon = { < DownloadOutlined /> }
478+ size = "small"
479+ disabled = { ! props . project }
480+ onClick = { handleDownload }
481+ style = { { color : 'white' } }
482+ />
483+ </ div >
484+ ) : (
485+ < div >
486+ </ div >
487+ ) }
385488 < AboutDialog
386489 visible = { aboutDialogVisible }
387490 onClose = { ( ) => setAboutDialogVisible ( false ) }
0 commit comments