1import React, { Component } from 'react' 2import PropTypes from 'prop-types' 3import { MessageUtils } from '../../flow/utils.js' 4 5export default function withContentLoader(View) { 6 7 return class extends React.Component { 8 static displayName = View.displayName || View.name 9 static matches = View.matches 10 11 static propTypes = { 12 ...View.propTypes, 13 content: PropTypes.string, // mark as non-required 14 flow: PropTypes.object.isRequired, 15 message: PropTypes.object.isRequired, 16 } 17 18 constructor(props) { 19 super(props) 20 this.state = { 21 content: undefined, 22 request: undefined, 23 } 24 } 25 26 componentWillMount() { 27 this.updateContent(this.props) 28 } 29 30 componentWillReceiveProps(nextProps) { 31 if ( 32 nextProps.message.content !== this.props.message.content || 33 nextProps.message.contentHash !== this.props.message.contentHash || 34 nextProps.contentView !== this.props.contentView 35 ) { 36 this.updateContent(nextProps) 37 } 38 } 39 40 componentWillUnmount() { 41 if (this.state.request) { 42 this.state.request.abort() 43 } 44 } 45 46 updateContent(props) { 47 if (this.state.request) { 48 this.state.request.abort() 49 } 50 // We have a few special cases where we do not need to make an HTTP request. 51 if (props.message.content !== undefined) { 52 return this.setState({request: undefined, content: props.message.content}) 53 } 54 if (props.message.contentLength === 0 || props.message.contentLength === null) { 55 return this.setState({request: undefined, content: ""}) 56 } 57 58 let requestUrl = MessageUtils.getContentURL(props.flow, props.message, props.contentView) 59 60 // We use XMLHttpRequest instead of fetch() because fetch() is not (yet) abortable. 61 let request = new XMLHttpRequest(); 62 request.addEventListener("load", this.requestComplete.bind(this, request)); 63 request.addEventListener("error", this.requestFailed.bind(this, request)); 64 request.open("GET", requestUrl); 65 request.send(); 66 this.setState({request, content: undefined}) 67 } 68 69 requestComplete(request, e) { 70 if (request !== this.state.request) { 71 return // Stale request 72 } 73 this.setState({ 74 content: request.responseText, 75 request: undefined 76 }) 77 } 78 79 requestFailed(request, e) { 80 if (request !== this.state.request) { 81 return // Stale request 82 } 83 console.error(e) 84 // FIXME: Better error handling 85 this.setState({ 86 content: "Error getting content.", 87 request: undefined 88 }) 89 } 90 91 render() { 92 return this.state.content !== undefined ? ( 93 <View content={this.state.content} {...this.props}/> 94 ) : ( 95 <div className="text-center"> 96 <i className="fa fa-spinner fa-spin"></i> 97 </div> 98 ) 99 } 100 } 101}; 102