Add mobx. Add scratchpad

This commit is contained in:
Alfred Melch 2019-04-30 14:06:37 +02:00
parent fcae9e9282
commit 22cbb6c93a
15 changed files with 2043 additions and 276 deletions

View File

@ -2,6 +2,10 @@
# pylint: disable=invalid-name # pylint: disable=invalid-name
import os import os
import io
import base64
from urllib.parse import urlparse
from PIL import Image from PIL import Image
from flask import Blueprint, render_template, session, redirect, url_for, \ from flask import Blueprint, render_template, session, redirect, url_for, \
request, flash, current_app, abort, send_from_directory request, flash, current_app, abort, send_from_directory
@ -59,12 +63,22 @@ def create_tile(z, x, y):
if not valid_tile(z, x, y): if not valid_tile(z, x, y):
return abort(404) return abort(404)
if 'file' not in request.files: image_bytes = None
# try to read file from request.files (byte-stream)
if 'file' in request.files:
image_bytes = request.files['file'].stream
# try to read file from form (base64 encoded dataURL)
if image_bytes is None and ('file' in request.form):
parsed_url = urlparse(request.form['file'])
head, data = parsed_url.path.split(',', 1)
image_bytes = io.BytesIO(base64.b64decode(data))
if image_bytes is None:
flash('No file part') flash('No file part')
return redirect(url_for('index')) return redirect(url_for('index'))
file = request.files['file']
try: try:
image = Image.open(file.stream) image = Image.open(image_bytes)
image = image.resize((256, 256)) image = image.resize((256, 256))
except OSError: except OSError:
flash('cannot read image') flash('cannot read image')

File diff suppressed because one or more lines are too long

View File

@ -27,3 +27,15 @@ main {
.flex > *[main] { .flex > *[main] {
flex: 1; flex: 1;
} }
.tile-image {
border: 2px solid black;
box-shadow: 5px 5px 10px 0px rgba(0,0,0,0.5);
width: 256px;
height: 256px;
}
.current-tile-upload {
display: flex;
justify-content: space-around;
}

View File

@ -2,18 +2,23 @@
{% block body %} {% block body %}
<h1>Wundertile - A collaborative TileLayer</h1> <section class="intro">
<h1>Wundertile - A collaborative TileLayer</h1>
<p>Hello Friend, this Website Lorem ipsum dolor sit amet consectetur adipisicing elit. Corrupti adipisci est quo libero odit modi iste corporis debitis tenetur sunt similique alias consequuntur, cupiditate suscipit impedit maiores, recusandae fugiat. Obcaecati?</p>
</section>
<p>Hello Friend, this Website Lorem ipsum dolor sit amet consectetur adipisicing elit. Corrupti adipisci est quo libero odit modi iste corporis debitis tenetur sunt similique alias consequuntur, cupiditate suscipit impedit maiores, recusandae fugiat. Obcaecati?</p> <section class="map">
<div id="map"></div>
</section>
<section id="currentTile">
</section>
<section class="about">
<h2>About</h2>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Iusto sed vero, doloribus, dolor laborum cumque ab quo odio dolorem voluptas asperiores debitis reiciendis? Sint aut, iusto sapiente eaque ipsa dolorum.</p>
<p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Error veniam consectetur accusamus distinctio fugit, omnis voluptas. Iure voluptatum omnis unde quas voluptates suscipit neque libero a recusandae quisquam, dolorum voluptate?</p>
</section>
<div id="map"></div>
<h2>Current tile</h2>
<div id="currentTile"></div>
<h2>About</h2>
<p>Lorem ipsum dolor sit, amet consectetur adipisicing elit. Iusto sed vero, doloribus, dolor laborum cumque ab quo odio dolorem voluptas asperiores debitis reiciendis? Sint aut, iusto sapiente eaque ipsa dolorum.</p>
<p>Lorem ipsum, dolor sit amet consectetur adipisicing elit. Error veniam consectetur accusamus distinctio fugit, omnis voluptas. Iure voluptatum omnis unde quas voluptates suscipit neque libero a recusandae quisquam, dolorum voluptate?</p>
<script src="{{ url_for('static', filename='bundle.js') }}"></script> <script src="{{ url_for('static', filename='bundle.js') }}"></script>
{% endblock %} {% endblock %}

15
web/.babelrc Normal file
View File

@ -0,0 +1,15 @@
{
"presets": [
["@babel/preset-env", {
"modules": false,
"targets": {
"browsers": ["last 2 versions", "IE >= 9"]
},
}]
],
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose" : true }],
["@babel/plugin-transform-react-jsx", {"pragma": "h"}]
]
}

View File

@ -1,4 +1,5 @@
{ {
"extends": "standard" "extends": ["standard", "standard-preact"],
"parser": "babel-eslint"
} }

View File

@ -1,4 +1,3 @@
import './src/map.js' import './src/map.js'
import './src/currentTile.js'
console.log('Hello World')
console.log('lala')

1819
web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,9 +12,19 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"@babel/cli": "^7.4.4",
"@babel/core": "^7.4.4",
"@babel/plugin-proposal-class-properties": "^7.4.4",
"@babel/plugin-proposal-decorators": "^7.4.4",
"@babel/plugin-transform-react-jsx": "^7.3.0",
"@babel/preset-env": "^7.4.4",
"babel-eslint": "^10.0.1",
"babel-loader": "^8.0.5",
"babel-plugin-jsx-pragmatic": "^1.0.2",
"css-loader": "^2.1.1", "css-loader": "^2.1.1",
"eslint": "^5.16.0", "eslint": "^5.16.0",
"eslint-config-standard": "^12.0.0", "eslint-config-standard": "^12.0.0",
"eslint-config-standard-preact": "^1.1.6",
"eslint-plugin-import": "^2.17.2", "eslint-plugin-import": "^2.17.2",
"eslint-plugin-node": "^8.0.1", "eslint-plugin-node": "^8.0.1",
"eslint-plugin-promise": "^4.1.1", "eslint-plugin-promise": "^4.1.1",
@ -28,6 +38,9 @@
"webpack-dev-server": "^3.3.1" "webpack-dev-server": "^3.3.1"
}, },
"dependencies": { "dependencies": {
"leaflet": "^1.4.0" "leaflet": "^1.4.0",
"mobx": "^5.9.4",
"mobx-preact": "^3.0.0",
"preact": "^8.4.2"
} }
} }

View File

@ -1,12 +1,69 @@
import { Component, h, render } from 'preact'
import { observer } from 'mobx-preact'
import { coordsToPath } from './util.js' import { coordsToPath } from './util.js'
import state from './state.js'
import { Scratchpad } from './scratchpad.js'
const root = document.getElementById('currentTile') const root = document.getElementById('currentTile')
export const render = (coords) => { @observer
root.innerHTML = ` class CurrentTileComponent extends Component {
<div class="flex"> render () {
<img src="/tiles${coordsToPath(coords)}"> let coordsPath = '/tiles/' + coordsToPath(state.coords)
</div> return (
Hello World! ${JSON.stringify(coords)} <div>
` <h2>Current Tile: {coordsToPath(state.coords)}</h2>
} <div className='current-tile-upload'>
<SubmitDrawingForm tilePath={coordsPath} />
<UploadForm tilePath={coordsPath} />
<img src={coordsPath} className='tile-image' />
</div>
</div>
)
}
handleClick (e) {
console.log(this.scratchpad.image)
}
}
class SubmitDrawingForm extends Component {
render (props) {
console.log('submDrawing', props)
return (
(
<div className='uploadForm'>
<Scratchpad ref={s => { this.scratchpad = s }} />
<form
action={props.tilePath}
method='POST'
enctype='multipart/form-data'
onSubmit={(e) => this.handleSubmit(e)}>
<input type='hidden' name='file' ref={f => { this.file = f }} />
<input type='submit' value='Upload Scratch' />
</form>
</div>
)
)
}
handleSubmit (e) {
console.log(this.file, this.scratchpad)
this.file.value = this.scratchpad.image
}
}
class UploadForm extends Component {
render (props) {
console.log(props)
return (
<div className='uploadForm'>
<form action={props.tilePath} method='POST' enctype='multipart/form-data'>
<input type='file' name='file' /><br />
<input type='submit' value='Upload' />
</form>
</div>
)
}
}
export const CurrentTile = render(<CurrentTileComponent />, root)

View File

@ -1,7 +1,7 @@
import { map as LMap, tileLayer } from 'leaflet' import { map as LMap, tileLayer } from 'leaflet'
import 'leaflet/dist/leaflet.css' import 'leaflet/dist/leaflet.css'
import { render } from './currentTile' import state from './state.js'
export const map = LMap('map') export const map = LMap('map')
map.setView([0, 0], 1) map.setView([0, 0], 1)
@ -28,6 +28,5 @@ const getTileCoords = (lat, lon, zoom) => {
// demo: how to get tile coords on click // demo: how to get tile coords on click
map.on('click', (e) => { map.on('click', (e) => {
let coords = getTileCoords(e.latlng.lat, e.latlng.lng, map.getZoom()) state.coords = getTileCoords(e.latlng.lat, e.latlng.lng, map.getZoom())
render(coords)
}) })

47
web/src/scratchpad.js Normal file
View File

@ -0,0 +1,47 @@
import { Component, h } from 'preact'
export class Scratchpad extends Component {
draw = false
render () {
return (
<div>
<canvas
width='256' height='256'
className='tile-image'
onMouseDown={(e) => this.handleMouseDown(e)}
onMouseMove={(e) => this.handleMouseMove(e)}
onMouseUp={(e) => this.handleMouseUp(e)}
onMouseOut={(e) => this.handleMouseOut(e)}
ref={c => { this.canvas = c }}
/>
</div>
)
}
get ctx () {
return this.canvas.getContext('2d')
}
get image () {
return this.canvas.toDataURL()
}
handleMouseDown (e) {
this.draw = true
this.ctx.beginPath()
this.ctx.moveTo(e.layerX, e.layerY)
}
handleMouseMove (e) {
if (this.draw === true) {
this.ctx.lineTo(e.layerX, e.layerY)
this.ctx.stroke()
}
}
handleMouseUp (e) {
this.draw = false
}
handleMouseOut (e) {
}
}

11
web/src/state.js Normal file
View File

@ -0,0 +1,11 @@
import { observable } from 'mobx'
class State {
@observable coords = { x: 0, y: 0, z: 0 }
setState (newState) {
this.state = { ...this.state, ...newState }
}
}
export default new State()

View File

@ -1 +1 @@
export const coordsToPath = coords => `/${coords.z}/${coords.x}/${coords.y}` export const coordsToPath = coords => `${coords.z}/${coords.x}/${coords.y}`

View File

@ -8,6 +8,9 @@ module.exports = {
}, },
module: { module: {
rules: [{ rules: [{
test: /\.js$/,
use: ['babel-loader']
}, {
test: /\.css$/, test: /\.css$/,
use: ['style-loader', 'css-loader'] use: ['style-loader', 'css-loader']
}, { }, {