Skip to content

Commit

Permalink
Drawing upload and visualization. Small improvements also to the drop…
Browse files Browse the repository at this point in the history
…zone (#2)
  • Loading branch information
texx00 committed Oct 25, 2020
1 parent 2415798 commit fb932f3
Show file tree
Hide file tree
Showing 20 changed files with 388 additions and 44 deletions.
3 changes: 1 addition & 2 deletions .flaskenv
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ FLASK_APP=server
FLASK_ENV=development

# can change this to 1 to make flask autoreload on files change (can set it from the launch.json file in vscode, for production must be 0 until a production server is setup)
# with react frontend it is not necessary to put this to 1
FLASK_DEBUG=0
FLASK_DEBUG=1
1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"react": "^17.0.1",
"react-bootstrap": "^1.4.0",
"react-dom": "^17.0.1",
"react-dropzone": "^11.2.1",
"react-multi-carousel": "^2.5.5",
"react-scripts": "4.0.0",
"react-scroll-horizontal": "^1.6.6",
Expand Down
8 changes: 1 addition & 7 deletions frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,11 @@ import Footer from './structure/Footer.js';
import Content from './structure/Content.js';
import Toasts from './structure/Toasts';

import {check_software_updates} from "./utils/SWUpdates";

import {useState, useEffect} from 'react';
import {useState} from 'react';

function App() {
const [tab, setTab] = useState("home");

useEffect(()=>{
check_software_updates(); // check for updates when the app is ready
})

function handleTab(tab){
setTab(tab);
}
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

import {check_software_updates} from "./utils/SWUpdates";

check_software_updates();

ReactDOM.render(
<React.StrictMode>
<App />
Expand Down
7 changes: 7 additions & 0 deletions frontend/src/project_defaults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@


const api_url = "http://localhost:5000/api";

const static_url = "http://localhost:5000/static";

export {api_url, static_url};
2 changes: 1 addition & 1 deletion frontend/src/structure/Content.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React, { Component} from 'react';
import {Tabs, Tab} from 'react-bootstrap';

import Home from './tabs/Home.js';
import Drawings from './tabs/Drawings';
import Drawings from './tabs/drawings/Drawings';
import Playlists from './tabs/Playlists';
import ManualControl from './tabs/ManualControl';
import Settings from './tabs/Settings';
Expand Down
1 change: 1 addition & 0 deletions frontend/src/structure/Toasts.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, { Component} from 'react';

import Toast from 'react-bootstrap/Toast';

import {show_toast} from "../SAC";
Expand Down
19 changes: 0 additions & 19 deletions frontend/src/structure/tabs/Drawings.js

This file was deleted.

41 changes: 33 additions & 8 deletions frontend/src/structure/tabs/Home.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import React, { Component } from 'react';
import Carousel from 'react-multi-carousel';
import 'react-multi-carousel/lib/styles.css';

import Section from '../../components/Section.js';
import PlaceholderCard from '../../components/PlaceholderCard.js';

import Carousel from 'react-multi-carousel';
import 'react-multi-carousel/lib/styles.css';
import UploadDrawingsModal from './drawings/UploadDrawing.js';
import DrawingCard from './drawings/DrawingCard';
import DrawingDataDownloader from './drawings/DrawingDataDownloader';

class Home extends Component{
constructor(props){
Expand All @@ -27,27 +30,45 @@ class Home extends Component{
items: 1
}
};
this.state = {show_upload: false, show_create_playlist: false, elements: []}
this.dhandler = new DrawingDataDownloader(this.setElements.bind(this));
}

componentDidMount(){
this.dhandler.requestDrawings();
}

uploadDrawingHandler(){
// TODO
console.log("Upload drawing");
handleFileUploaded(){
this.dhandler.requestDrawings();
window.show_toast("Updating drawing previews...");
}


setElements(elements){
this.setState({elements : elements});
}

newPlaylistHandler(){
console.log("Create playlist")
}

renderDrawings(list){
let result;
if (list.length>0){
result = list.map((item, index) => {return <DrawingCard element={item} key={index}/>});
}else{
result = [1,2,3,4,5,6,7].map((item, index)=>{return <PlaceholderCard key={index}/>});
}
return result;
}

render(){
return <div>
<Section sectionTitle="Drawings"
sectionButton="+ Upload new drawing"
sectionButtonHandler={this.uploadDrawingHandler.bind(this)}>
sectionButtonHandler={()=>this.setState({show_upload: true})}>
<Carousel responsive={this.carousel_responsive} ssr>
{[1,2,3,4,5,6,7].map((item, index)=>{
return <PlaceholderCard key={index}/>})}
{this.renderDrawings(this.state.elements)}
</Carousel>
</Section>
<Section sectionTitle="Playlists"
Expand All @@ -59,6 +80,10 @@ class Home extends Component{
})}
</Carousel>
</Section>
<UploadDrawingsModal
show={this.state.show_upload}
handleClose={()=>{this.setState({show_upload: false})}}
handleFileUploaded={this.handleFileUploaded.bind(this)}/>
</div>
}
}
Expand Down
52 changes: 52 additions & 0 deletions frontend/src/structure/tabs/drawings/DrawingCard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import './DrawingCard.scss';

import React, { Component } from 'react';
import { Card, Modal } from 'react-bootstrap';

import {static_url} from '../../../project_defaults';

class DrawingCard extends Component{
constructor(props){
super(props);
this.state = {show_details: false};
}

getImgUrl(){
return static_url + "/Drawings/" + this.props.element.id + "/" + this.props.element.id + ".jpg";
}

render(){
return <div>
<Card className="p-2 hover-zoom" onClick={()=>this.setState({show_details: true})}>
<div className="border-0 bg-black rounded text-dark clickable center p-0">
<img className="card-img-top rounded" src={this.getImgUrl()} alt="Not available"/>
<div className="card-img-overlay h-100 d-flex flex-column justify-content-end p-2">
<div className="card-text text-center text-dark p-1 fade-top"></div>
<div className="card-text text-center text-dark pb-1 bg-primary rounded-bottom">
{this.props.element.filename}
</div>
</div>
</div>
</Card>
<Modal show={this.state.show_details}
onHide={() => this.setState({show_details: false})}
size="lg"
centered>
<Modal.Header closeButton>
<Modal.Title >{this.props.element.filename}</Modal.Title>
</Modal.Header>
<Modal.Body>
<div className="center pb-3">
<button>Draw it now</button>
<button>+ Add to playlist</button>
</div>
<div className="center mb-5">
<img className="modal-drawing-preview" src={this.getImgUrl()} alt="Not available"/>
</div>
</Modal.Body>
</Modal>
</div>
}
}

export default DrawingCard;
4 changes: 4 additions & 0 deletions frontend/src/structure/tabs/drawings/DrawingCard.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

.modal-drawing-preview{
width: 80%;
}
21 changes: 21 additions & 0 deletions frontend/src/structure/tabs/drawings/DrawingDataDownloader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import {api_url} from "../../../project_defaults";

class DrawingDataDownloader{
// need to pass a callback as argument that will be called when the data is ready
constructor(data_callback){
this.cb = data_callback;
}

requestDrawings(){
fetch(api_url+"/drawings/")
.then(response => response.json())
.then(data => {
this.cb(data);
}).catch(error => {
console.log("There was an error");
console.log(error);
})
}
}

export default DrawingDataDownloader;
61 changes: 61 additions & 0 deletions frontend/src/structure/tabs/drawings/Drawings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React, { Component } from 'react';
import { Container, Row, Col } from 'react-bootstrap';
import Section from '../../../components/Section';
import DrawingDataDownloader from './DrawingDataDownloader';
import UploadDrawingsModal from './UploadDrawing';
import DrawingCard from './DrawingCard';

class Drawings extends Component{
constructor(props){
super(props);
this.state = {show_upload: false, loaded: false, drawings: []}
this.dhandler = new DrawingDataDownloader(this.addElements.bind(this));
}

componentDidMount(){
this.dhandler.requestDrawings();
}

addElements(data){
this.setState({drawings: data, loaded: true});
}

handleFileUploaded(){
this.dhandler.requestDrawings();
}

renderDrawings(drawings){
return drawings.map((d, index)=>{
return <Col key={index} sm={4}>
<DrawingCard element={d}/>
</Col>
});
}
// todo load more on page scroll

render(){
return <Section sectionTitle="Drawings"
sectionButton="+ Upload new drawing"
sectionButtonHandler={()=>this.setState({show_upload: true})}>

<div className={(this.state.loaded ? " d-none" : "")}>
<div className="w-100 pt-5 center">
<h1>Loading...</h1>
</div>
</div>

<Container>
<Row>
{this.renderDrawings(this.state.drawings)}
</Row>
</Container>

<UploadDrawingsModal key={2}
show={this.state.show_upload}
handleClose={()=>{this.setState({show_upload: false})}}
handleFileUploaded={this.handleFileUploaded.bind(this)}/>
</Section>
}
}

export default Drawings;
65 changes: 65 additions & 0 deletions frontend/src/structure/tabs/drawings/UploadDrawing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import "./UploadDrawing.scss";

import React, { Component } from 'react';

import {api_url} from "../../../project_defaults";

import Dropzone from 'react-dropzone';
import Modal from 'react-bootstrap/Modal';

class UploadDrawingsModal extends Component{

static defaultProps = {
playlist: 0,
show: false
}

handleClose(){
this.props.handleClose();
}

handleFiles(files){
let promises = files.map(f => {
let data = new FormData();
data.append("file", f);
data.append("filename", f.name);
return fetch(api_url + "/upload/" + this.props.playlist, {
method: "POST",
body: data
}).then((response => {
if (response.status === 200){
window.show_toast("Drawing \""+f.name+"\" uploaded successfully");
}else{
window.show_toast("There was a problem when uploading \""+f.name+"\"");
}
}));
});
// wait until all file have been laoaded to refresh the list
Promise.all(promises)
.then(()=>{this.props.handleFileUploaded()});

this.handleClose();
}

render(){
return <Modal show={this.props.show} onHide={this.handleClose.bind(this)} size="lg" centered>
<Modal.Header closeButton>
<Modal.Title >Upload new drawing</Modal.Title>
</Modal.Header>
<Modal.Body>
<Dropzone
onDrop={this.handleFiles.bind(this)}
accept={".gcode"}
noKeyboard>
{({getRootProps, getInputProps, isDragActive}) => (<div {...getRootProps()} className={"animated-background m-2 p-5 mh-100 d-flex justify-content-center align-items-center" + (isDragActive ? " drag-active" : "")}>
<input {...getInputProps()}/>
<div className="d-block text-center">Drag and drop the .gcode/.nc file here <br/>or click to open the file explorer
</div>
</div>)}
</Dropzone>
</Modal.Body>
</Modal>
}
}

export default UploadDrawingsModal;
Loading

0 comments on commit fb932f3

Please sign in to comment.