Skip to content

Commit 32d0ae2

Browse files
authored
Merge pull request #8 from COMP90082-2023-SM2/download-media
[46] Media download
2 parents 0443cf8 + aa6c9e0 commit 32d0ae2

File tree

2 files changed

+292
-26
lines changed

2 files changed

+292
-26
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
"@material-ui/core": "^4.12.3",
99
"@material-ui/icons": "^4.11.2",
1010
"@material-ui/lab": "^4.0.0-alpha.60",
11+
"@mui/material": "5.14.7",
12+
"@emotion/styled": "11.11.0",
13+
"@emotion/react": "11.11.1",
1114
"@pmmmwh/react-refresh-webpack-plugin": "0.4.3",
1215
"@svgr/webpack": "5.5.0",
1316
"@testing-library/jest-dom": "^5.11.4",

src/pages/PageFour/index.js

Lines changed: 289 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,50 @@ import {subGoal, stepInfo, allStages, steps, stepSubgoalMap, vfg, textContent,
33
getAllStages, getSteps, getStepInfo, getSubGoal, getStepSubgoalMap} from './dataUtils';
44
import Button from '@material-ui/core/Button';
55
import styles from './index.less';
6-
import Screen, { ControlPanel, StepScreen, GoalScreen } from "./screenComponents";
6+
import Screen, { ControlPanel, StepScreen, GoalScreen, SplitButton } from "./screenComponents";
77

88

9+
import Dialog from '@mui/material/Dialog';
10+
import DialogTitle from '@mui/material/DialogTitle';
11+
import DialogContent from '@mui/material/DialogContent';
12+
import TextField from '@mui/material/TextField';
13+
import DialogActions from '@mui/material/DialogActions';
14+
15+
import Menu from '@mui/material/Menu';
16+
import MenuItem from '@mui/material/MenuItem';
17+
18+
import {
19+
FormControl,
20+
RadioGroup,
21+
FormControlLabel,
22+
Radio
23+
} from '@material-ui/core';
24+
25+
import CircularProgress from "@material-ui/core/CircularProgress";
26+
27+
import Select from '@mui/material/Select';
28+
29+
import { InputLabel } from '@material-ui/core';
30+
31+
class mediaDataLabel {
32+
33+
constructor(fileType = 'vfg', startStep = 0, stopStep = 1, quality = "high") {
34+
this.fileType = fileType;
35+
this.startStep = startStep;
36+
this.stopStep = stopStep;
37+
this.quality = quality;
38+
}
39+
bodyContent() {
40+
const bodyContent = {
41+
'fileType': this.fileType,
42+
'startStep': this.startStep,
43+
'stopStep': this.stopStep,
44+
'quality': this.quality
45+
}
46+
return bodyContent;
47+
}
48+
}
49+
950
class PageFour extends React.Component {
1051

1152
constructor(props) {
@@ -30,12 +71,21 @@ class PageFour extends React.Component {
3071
pauseButtonColor: 'default',
3172
canvasWidth: 720,
3273
canvasHeight: 470,
74+
radioOption: 'all',
75+
currentDialogType: null,
76+
isLoading: false,
77+
qualityOption: 'medium',
3378
}
3479

3580
// Every function that interfaces with UI and data used
3681
// in this class needs to bind like this:
3782
this.handleOnClick = this.handleOnClick.bind(this);
3883
this.updateWindowDimensions = this.updateWindowDimensions.bind(this);
84+
this.handleMenuOpen = this.handleMenuOpen.bind(this);
85+
this.handleMenuClose = this.handleMenuClose.bind(this);
86+
this.handleOpenDialog = this.handleOpenDialog.bind(this);
87+
this.handleCloseDialog = this.handleCloseDialog.bind(this);
88+
this.handleNumberChange = this.handleNumberChange.bind(this);
3989
}
4090

4191

@@ -393,12 +443,86 @@ class PageFour extends React.Component {
393443
}
394444

395445

446+
async sendMediaRequestAndDownload(fileType, startStep, stopStep, qualityOption) {
447+
try {
448+
const vfgText = JSON.stringify(vfg);
449+
//console.log(vfgText);
450+
var label = new mediaDataLabel();
451+
// assign any parameters here. otherwise, default will be used.
452+
label.fileType = fileType;
453+
switch (fileType){
454+
case "mp4":
455+
label.startStep = startStep;
456+
label.stopStep = stopStep;
457+
label.quality = qualityOption;
458+
break;
459+
case "png":
460+
label.startStep = startStep;
461+
label.stopStep = stopStep;
462+
break;
463+
case "gif":
464+
label.startStep = startStep;
465+
label.stopStep = stopStep;
466+
label.quality = qualityOption;
467+
break;
468+
}
469+
470+
const requestData = {
471+
method: 'POST',
472+
body: JSON.stringify({
473+
'vfg': vfgText,
474+
'fileType': fileType,
475+
'params': label.bodyContent()
476+
})
477+
};
478+
479+
// For local testing, uncomment the next line and comment the following line.
480+
//const response = await fetch("http://localhost:8000/downloadVisualisation", requestData);
481+
const response = await fetch("https://planimation.planning.domains/downloadVisualisation", requestData);
482+
483+
if (response.ok) {
484+
console.log("Response was OK");
485+
const blob = await response.blob();
486+
const url = window.URL.createObjectURL(blob);
487+
const a = document.createElement('a');
488+
a.style.display = 'none';
489+
a.href = url;
490+
if (fileType == "png"){
491+
fileType = "zip";
492+
}
493+
a.download = 'planimation.'+fileType;
494+
document.body.appendChild(a);
495+
a.click();
496+
window.URL.revokeObjectURL(url);
497+
document.body.removeChild(a);
498+
} else {
499+
console.error("Failed to download the file.");
500+
}
501+
} catch (error) {
502+
console.error("Error:", error);
503+
}
504+
}
505+
506+
507+
// this function is for testing @Wenxuan
508+
async handleMediaButtonClick(fileType) {
509+
try {
510+
511+
this.sendMediaRequestAndDownload(fileType);
512+
513+
} catch(err) {
514+
console.error("There was an error: ", err);
515+
}
516+
517+
518+
}
519+
396520

397521
handleSpeedControllor = (value) => {
398522
this.setState({
399523
playSpeed: value
400-
})
401-
}
524+
});
525+
};
402526

403527

404528
/**
@@ -413,52 +537,191 @@ class PageFour extends React.Component {
413537
}
414538

415539

540+
handleMenuOpen = (event) => {
541+
this.setState({ anchorEl: event.currentTarget });
542+
};
543+
544+
handleMenuClose = () => {
545+
this.setState({ anchorEl: null });
546+
};
547+
548+
handleOpenDialog = (type) => {
549+
this.setState({
550+
isModalOpen: true,
551+
currentDialogType: type
552+
});
553+
}
554+
555+
556+
handleCloseDialog = () => {
557+
this.setState({ isModalOpen: false });
558+
}
559+
560+
handleNumberChange = (e, numberIndex) => {
561+
this.setState({ [numberIndex]: e.target.value });
562+
}
563+
564+
handleRadioChange = (event) => {
565+
this.setState({
566+
radioOption: event.target.value
567+
});
568+
}
569+
570+
571+
handleDownload = async () => {
572+
this.setState({ isloading: true });
573+
if (this.state.radioOption === 'all') {
574+
await this.sendMediaRequestAndDownload(this.state.currentDialogType, 0, 9999, this.state.qualityOption);
575+
} else {
576+
const { number1, number2 } = this.state;
577+
await this.sendMediaRequestAndDownload(this.state.currentDialogType, number1, number2, this.state.qualityOption);
578+
}
579+
this.handleCloseDialog();
580+
setTimeout(() => {
581+
this.setState({ isloading: false });
582+
}, 2000);
583+
}
584+
585+
handleQualityChange = (event) => {
586+
this.setState({ qualityOption: event.target.value });
587+
};
588+
589+
590+
416591

417592
render() {
418593
// Get all sprites
419594
let sprites = this.state.drawSprites;
420595
// Sort sprites by their depth
421-
sprites && sprites.sort((itemA, itemB) => itemA.depth - itemB.depth)
596+
sprites && sprites.sort((itemA, itemB) => itemA.depth - itemB.depth);
422597

423598
return (
424-
<div className={styles.container} ref={(ref)=>this.refDom=ref}>
599+
<div className={styles.container} ref={(ref) => this.refDom = ref}>
425600
<div className={styles.left}>
426-
<StepScreen stepInfoIndex={this.state.stepInfoIndex} stepItem={this.stepItem} stepInfo={stepInfo} onStepClick={this.handleStepsClick}/>
601+
<StepScreen stepInfoIndex={this.state.stepInfoIndex} stepItem={this.stepItem} stepInfo={stepInfo} onStepClick={this.handleStepsClick} />
427602
</div>
428603
<div className={styles.middle}>
429-
<Screen canvasWidth={this.state.canvasWidth} canvasHeight={this.state.canvasHeight} sprites={this.state.drawSprites} vfg={vfg} />
604+
<Screen canvasWidth={this.state.canvasWidth} canvasHeight={this.state.canvasHeight} sprites={this.state.drawSprites} vfg={vfg} />
430605
<div className={styles.btn_box}>
431606
<div>
432-
<ControlPanel
433-
playButtonColor={this.state.playButtonColor}
434-
pauseButtonColor={this.state.pauseButtonColor}
435-
stepInfoIndex={this.state.stepInfoIndex}
436-
onPreviousClick={this.handlePreviousClick}
437-
onStartClick={this.handleStartClick}
438-
onPauseClick={this.handlePauseClick}
439-
onNextClick={this.handleNextClick}
440-
onResetClick={this.handleResetClick}
441-
onSpeedControllor={this.handleSpeedControllor}></ControlPanel>
607+
<ControlPanel
608+
playButtonColor={this.state.playButtonColor}
609+
pauseButtonColor={this.state.pauseButtonColor}
610+
stepInfoIndex={this.state.stepInfoIndex}
611+
onPreviousClick={this.handlePreviousClick}
612+
onStartClick={this.handleStartClick}
613+
onPauseClick={this.handlePauseClick}
614+
onNextClick={this.handleNextClick}
615+
onResetClick={this.handleResetClick}
616+
onSpeedControllor={this.handleSpeedControllor}>
617+
</ControlPanel>
442618
</div>
443619
</div>
444620
</div>
445-
621+
446622
<div className={styles.right}>
447-
<div style={{marginTop:'5px', marginBottom:'5px', width: '220px'}}>
448-
<Button variant="contained" color="primary" size="small" onClick={()=> {this.handleShowFinalGoalClick()}}>
623+
<div style={{ marginTop: '5px', marginBottom: '5px', width: '220px' }}>
624+
<Button variant="contained" color="primary" size="small" onClick={() => { this.handleShowFinalGoalClick() }}>
449625
Show the Goal
450626
</Button>
451627
&nbsp;&nbsp;
452-
<Button variant="contained" color="primary" size="small" onClick={()=> {this.handleExportClick()}}>
628+
<Button variant="contained" color="primary" size="small" onClick={this.handleMenuOpen}>
453629
Export
454630
</Button>
631+
{this.state.isloading && <CircularProgress size={24} />}
632+
<Menu
633+
anchorEl={this.state.anchorEl}
634+
open={Boolean(this.state.anchorEl)}
635+
onClose={this.handleMenuClose}
636+
>
637+
<MenuItem onClick={() => { this.handleExportClick(); this.handleMenuClose(); }}>
638+
Export .vfg
639+
</MenuItem>
640+
<MenuItem onClick={() => { this.handleOpenDialog("png"); this.handleMenuClose(); }}>
641+
Export .png
642+
</MenuItem>
643+
<MenuItem onClick={() => { this.handleOpenDialog("gif"); this.handleMenuClose(); }}>
644+
Export .gif
645+
</MenuItem>
646+
<MenuItem onClick={() => { this.handleOpenDialog("mp4"); this.handleMenuClose(); }}>
647+
Export .mp4
648+
</MenuItem>
649+
</Menu>
650+
<Dialog open={this.state.isModalOpen} onClose={this.handleCloseDialog}>
651+
<DialogTitle>Download as {this.state.currentDialogType}</DialogTitle>
652+
<DialogContent>
653+
<FormControl component="fieldset">
654+
<RadioGroup
655+
value={this.state.radioOption}
656+
onChange={this.handleRadioChange}
657+
>
658+
<FormControlLabel
659+
value="all"
660+
control={<Radio />}
661+
label="Download All"
662+
/>
663+
<FormControlLabel
664+
value="range"
665+
control={<Radio />}
666+
label="Specify range"
667+
/>
668+
</RadioGroup>
669+
</FormControl>
670+
{/* No need for quality option for PNGs */}
671+
672+
673+
{this.state.radioOption === 'range' && (
674+
<>
675+
<div><small>Please enter a step range within 0 and {Number(steps.length) - 1}.</small></div>
676+
<TextField
677+
autoFocus
678+
margin="dense"
679+
id="number1"
680+
label="start"
681+
type="number"
682+
fullWidth
683+
value={this.state.number1}
684+
onChange={(e) => this.handleNumberChange(e, 'number1')}
685+
/>
686+
<TextField
687+
margin="dense"
688+
id="number2"
689+
label="end"
690+
type="number"
691+
fullWidth
692+
value={this.state.number2}
693+
onChange={(e) => this.handleNumberChange(e, 'number2')}
694+
/>
695+
</>
696+
)}
697+
{this.state.currentDialogType !== "png" && (
698+
<>
699+
<FormControl fullWidth>
700+
<InputLabel id="quality-label">Quality</InputLabel>
701+
<Select
702+
labelId="quality-label"
703+
value={this.state.qualityOption}
704+
onChange={this.handleQualityChange}
705+
>
706+
<MenuItem value="low">Low</MenuItem>
707+
<MenuItem value="medium">Medium</MenuItem>
708+
<MenuItem value="high">High</MenuItem>
709+
</Select>
710+
</FormControl>
711+
</>
712+
)}
713+
</DialogContent>
714+
<DialogActions>
715+
<Button onClick={this.handleCloseDialog} color="primary">Cancel</Button>
716+
<Button onClick={this.handleDownload} color="primary">Confirm</Button>
717+
</DialogActions>
718+
</Dialog>
455719
</div>
456720
<GoalScreen sprites={sprites} subGoal={subGoal} selectedSubGoals={this.state.selectedSubGoals}
457-
showKey={this.state.showKey} onSubItemClick={this.handleSubItemClick} onSubgoalStepItemClick={this.handleSubgoalStepItemClick}/>
721+
showKey={this.state.showKey} onSubItemClick={this.handleSubItemClick} onSubgoalStepItemClick={this.handleSubgoalStepItemClick} />
458722
</div>
459723
</div>
460-
);
724+
);
461725
}
462-
}
463-
464-
export default PageFour;
726+
}
727+
export default PageFour;

0 commit comments

Comments
 (0)