Export to Gist anonymously, related to maputnik/editor#3

This commit is contained in:
jirik 2017-01-12 18:27:44 +01:00
parent 8c2b110115
commit 69a665373f
3 changed files with 165 additions and 1 deletions

View file

@ -24,6 +24,7 @@
"codemirror": "^5.18.2", "codemirror": "^5.18.2",
"color": "^1.0.3", "color": "^1.0.3",
"file-saver": "^1.3.2", "file-saver": "^1.3.2",
"github-api": "^3.0.0",
"lodash.capitalize": "^4.2.1", "lodash.capitalize": "^4.2.1",
"lodash.clonedeep": "^4.5.0", "lodash.clonedeep": "^4.5.0",
"lodash.isequal": "^4.4.0", "lodash.isequal": "^4.4.0",

View file

@ -18,6 +18,7 @@ import InspectionIcon from 'react-icons/lib/md/find-in-page'
import logoImage from '../img/maputnik.png' import logoImage from '../img/maputnik.png'
import SettingsModal from './modals/SettingsModal' import SettingsModal from './modals/SettingsModal'
import ExportModal from './modals/ExportModal'
import SourcesModal from './modals/SourcesModal' import SourcesModal from './modals/SourcesModal'
import OpenModal from './modals/OpenModal' import OpenModal from './modals/OpenModal'
@ -68,6 +69,7 @@ export default class Toolbar extends React.Component {
sources: false, sources: false,
open: false, open: false,
add: false, add: false,
export: false,
} }
} }
} }
@ -96,6 +98,12 @@ export default class Toolbar extends React.Component {
isOpen={this.state.isOpen.settings} isOpen={this.state.isOpen.settings}
onOpenToggle={this.toggleModal.bind(this, 'settings')} onOpenToggle={this.toggleModal.bind(this, 'settings')}
/> />
<ExportModal
mapStyle={this.props.mapStyle}
onStyleDownload={this.props.onStyleDownload}
isOpen={this.state.isOpen.export}
onOpenToggle={this.toggleModal.bind(this, 'export')}
/>
<OpenModal <OpenModal
isOpen={this.state.isOpen.open} isOpen={this.state.isOpen.open}
onStyleOpen={this.props.onStyleOpen} onStyleOpen={this.props.onStyleOpen}
@ -118,7 +126,10 @@ export default class Toolbar extends React.Component {
<OpenIcon /> <OpenIcon />
<IconText>Open</IconText> <IconText>Open</IconText>
</ToolbarAction> </ToolbarAction>
{this.downloadButton()} <ToolbarAction onClick={this.toggleModal.bind(this, 'export')}>
<MdFileDownload />
<IconText>Export</IconText>
</ToolbarAction>
<ToolbarAction onClick={this.toggleModal.bind(this, 'sources')}> <ToolbarAction onClick={this.toggleModal.bind(this, 'sources')}>
<SourcesIcon /> <SourcesIcon />
<IconText>Sources</IconText> <IconText>Sources</IconText>

View file

@ -0,0 +1,152 @@
import React from 'react'
import GlSpec from 'mapbox-gl-style-spec/reference/latest.js'
import InputBlock from '../inputs/InputBlock'
import StringInput from '../inputs/StringInput'
import SelectInput from '../inputs/SelectInput'
import Button from '../Button'
import Modal from './Modal'
import MdFileDownload from 'react-icons/lib/md/file-download'
import formatStyle from 'mapbox-gl-style-spec/lib/format'
import GitHub from 'github-api'
class Gist extends React.Component {
static propTypes = {
mapStyle: React.PropTypes.object.isRequired,
}
constructor(props) {
super(props);
this.state = {}
}
onSave() {
this.setState({
saving: true
});
const mapStyleStr = formatStyle(this.props.mapStyle);
const styleTitle = this.props.mapStyle.name || 'Style';
const htmlStr = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>`+styleTitle+` Preview</title>
<link rel="stylesheet" type="text/css" href="https://api.mapbox.com/mapbox-gl-js/v0.28.0/mapbox-gl.css" />
<script src="https://api.mapbox.com/mapbox-gl-js/v0.28.0/mapbox-gl.js"></script>
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>
<div id='map'></div>
<script>
var map = new mapboxgl.Map({
container: 'map',
style: 'style.json',
attributionControl: true,
hash: true
});
map.addControl(new mapboxgl.NavigationControl());
</script>
</body>
</html>
`
const gh = new GitHub();
let gist = gh.getGist(); // not a gist yet
gist.create({
public: true,
description: styleTitle + 'Preview',
files: {
"style.json": {
content: mapStyleStr
},
"index.html": {
content: htmlStr
}
}
}).then(function({data}) {
return gist.read();
}).then(function({data}) {
this.setState({
latestGist: data
});
}.bind(this));
}
renderLatestGist() {
const gist = this.state.latestGist;
const saving = this.state.saving;
if(gist) {
const user = gist.user || 'anonymous';
return <p>
Latest saved gist:{' '}
<a target="_blank" href={"https://bl.ocks.org/"+user+"/"+gist.id}>Preview</a>,{' '}
<a target="_blank" href={"https://gist.github.com/"+user+"/"+gist.id}>Source</a>
</p>
} else if(saving) {
return <p>Saving...</p>
}
}
render() {
return <div>
<Button onClick={this.onSave.bind(this)}>
<MdFileDownload />
Save to Gist (anonymous)
</Button>
{this.renderLatestGist()}
</div>
}
}
class ExportModal extends React.Component {
static propTypes = {
mapStyle: React.PropTypes.object.isRequired,
isOpen: React.PropTypes.bool.isRequired,
onOpenToggle: React.PropTypes.func.isRequired,
// Current style is requested for download
onStyleDownload: React.PropTypes.func.isRequired,
}
constructor(props) {
super(props);
}
onStyleDownload() {
const blob = new Blob([formatStyle(mapStyle)], {type: "application/json;charset=utf-8"});
saveAs(blob, mapStyle.id + ".json");
}
render() {
return <Modal
isOpen={this.props.isOpen}
onOpenToggle={this.props.onOpenToggle}
title={'Export Style'}
>
<div className="maputnik-modal-section">
<h4>Download Style</h4>
<p>
Download a JSON style to your computer.
</p>
<Button onClick={this.props.onStyleDownload}>
<MdFileDownload />
Download
</Button>
</div>
<div className="maputnik-modal-section">
<h4>Save style</h4>
<Gist mapStyle={this.props.mapStyle} />
</div>
</Modal>
}
}
export default ExportModal