Gist file storage

by Bret Lowrey

HTML

<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.15.0/lodash.min.js"></script>
<script src="https://unpkg.com/react@17/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js"></script>
<div id="download"></div>
<div class="component" id="fileInput"></div>

CSS

.pull-right {
  float: right !important;
}

label {
  font-size: 12px;
}

.header {
  font-weight: bold;
}

html {
  height: 80%;
}

.outputUrl {
  width: 80%;
  text-overflow: ellipsis;  
  text-align: center;
}

.content {
  height: 100%;
  margin: 5px;
}

.component {
  height: 100%;
  width: 95%;
  display: inline-block;
}

body {
  height: 100%;
  margin: 40px auto;
  max-width: 752px;
  background: #eee;
  line-height: 1.6;
  font-size: 18px;
  font-family: arial, sans-serif;
  color: #444;
  padding: 0 10px
}

label {
    max-width: 80%;
    font-size: 1.25rem;
    font-weight: 700;
    text-overflow: ellipsis;
    white-space: nowrap;
    cursor: pointer;
    display: inline-block;
    overflow: hidden;
    padding: 0.625rem 1.25rem;
}

form label {
  color: #009999;
}

form label figure {
    width: 100px;
    height: 100px;
    border-radius: 50%;
    background-color: #009999;
    display: block;
    padding: 20px;
    margin: 0 auto 10px;
}

form label svg {
    width: 100%;
    height: 100%;
    fill: #f1e5e6;
}

.inputfile {
    width: 0.1px;
    height: 0.1px;
    opacity: 0;
    position: absolute;
    z-index: -1;
}

.uploadContent {
  text-align: center;
  width: 100%;
}

.downloadContent {
  color: #009999;
}

.outputUrl {
    padding: 5px;
    border: transparent;
    border-radius: 8px;
    text-decoration: none;
    color: #666;
    font-size: 14px;
    line-height: 120%;
}

.outputUrl:hover {
    background-color: #ffffff;
}

Babel + JSX

class GistDownload extends React.Component {	 
  constructor() {
  	super();
    this.state = { 
      caption: 'Downloading...'
    };
    this.downloadGist(new URLSearchParams(window.location.search).get('url'));
  }
  
  componentDidMount() {
  }
  
  dataUrlToBlob(dataUrl) {
    var arr = dataUrl.split(','),
    mime = arr[0].match(/:(.*?);/)[1],
    bstr = atob(arr[1]),
    n = bstr.length,
    u8arr = new Uint8Array(n);
    while (n--) {
      u8arr[n] = bstr.charCodeAt(n);
    }
    return new Blob([u8arr], {
      type: mime
    });
  }
  
  downloadGist(url) {
    const saveAs = (function() {
      var a = document.createElement("a");
      document.body.appendChild(a);
      a.style = "display: none";
      return (data, fileName) => {
        const blob = new Blob([data], {
         type: "octet/stream"
        });
      const url = window.URL.createObjectURL(blob);
      a.href = url;
      a.download = fileName;
      a.click();
      window.URL.revokeObjectURL(url);
     };
    }());
    const downloading = setInterval(() => {
      let numDots = this.state.caption.split('.').length - 1;
      numDots = numDots < 3 ? numDots + 1 : 0;
      this.setState({
        caption: 'Downloading' + '.'.repeat(numDots) 
      });
     }, 500);
    
    const filename = decodeURIComponent(url.split('/').pop());
    fetch(url)
      .then(response => {
        clearInterval(downloading);
        this.setState({
          caption: '👍' 
        });
        return response.text();
      })
      .then(blob => saveAs(this.dataUrlToBlob(blob), filename));
  }
  
  handleChange(event) {
    const file = event.target.files[0];
    const reader = new FileReader();
    reader.readAsDataURL(file);
    reader.onloadend = () => {
    	this.uploadGist(file, reader.result);
  	}
  }
  
  render() {
    return (
    <div className="downloadContent">
      {this.state.caption}
    </div>
    );
  }
}

class FileInput extends React.Component {	 
  constructor() {
  	super();
   ...