import React from 'react'; import lbry from '../lbry.js'; import {Link, DownloadLink, WatchLink} from '../component/link.js'; import {Thumbnail, TruncatedText, CreditAmount} from '../component/common.js'; let FileTile = React.createClass({ _isMounted: false, _fileInfoCheckInterval: 5000, propTypes: { metadata: React.PropTypes.object.isRequired, fileInfo: React.PropTypes.string, name: React.PropTypes.string, sdHash: React.PropTypes.string, available: React.PropTypes.bool, isMine: React.PropTypes.bool, local: React.PropTypes.bool, cost: React.PropTypes.number, costIncludesData: React.PropTypes.bool, }, updateFileInfo: function(progress=null) { const updateFileInfoCallback = ((fileInfo) => { if (!this._isMounted || 'fileInfo' in this.props) { /** * The component was unmounted, or a file info data structure has now been provided by the * containing component. */ return; } this.setState({ fileInfo: fileInfo || null, local: !!fileInfo, }); setTimeout(() => { this.updateFileInfo() }, this._fileInfoCheckInterval); }); if ('sdHash' in this.props) { lbry.getFileInfoBySdHash(this.props.sdHash, updateFileInfoCallback); this.getIsMineIfNeeded(this.props.sdHash); } else if ('name' in this.props) { lbry.getFileInfoByName(this.props.name, (fileInfo) => { this.getIsMineIfNeeded(fileInfo.sd_hash); updateFileInfoCallback(fileInfo); }); } else { throw new Error("No progress, stream name or sd hash passed to FileTile"); } }, getIsMineIfNeeded: function(sdHash) { if (this.state.isMine !== null) { // The info was already provided by this.props.isMine return; } lbry.getMyClaims((claimsInfo) => { for (let {value} of claimsInfo) { if (JSON.parse(value).sources.lbry_sd_hash == sdHash) { this.setState({ isMine: true, }); return; } } this.setState({ isMine: false, }); }); }, getInitialState: function() { return { downloading: false, isHovered: false, cost: null, costIncludesData: null, fileInfo: 'fileInfo' in this.props ? this.props.fileInfo : null, isMine: 'isMine' in this.props ? this.props.isMine : null, local: 'local' in this.props ? this.props.local : null, } }, getDefaultProps: function() { return { compact: false, } }, handleMouseOver: function() { this.setState({ isHovered: true, }); }, handleMouseOut: function() { this.setState({ isHovered: false, }); }, componentWillMount: function() { this.updateFileInfo(); if ('cost' in this.props) { this.setState({ cost: this.props.cost, costIncludesData: this.props.costIncludesData, }); } else { lbry.getCostInfoForName(this.props.name, ({cost, includesData}) => { this.setState({ cost: cost, costIncludesData: includesData, }); }); } }, componentDidMount: function() { this._isMounted = true; }, componentWillUnmount: function() { this._isMounted = false; }, render: function() { if (this.state.isMine === null || this.state.local === null) { // Can't render until we know whether we own the file and if we have a local copy return null; } const obscureNsfw = !lbry.getClientSetting('showNsfw') && this.props.nsfw; let downloadLinkExtraProps = {}; if (this.state.fileInfo === null) { downloadLinkExtraProps.state = 'not-started'; } else if (!this.state.fileInfo.completed) { downloadLinkExtraProps.state = 'downloading'; const {written_bytes, total_bytes, path} = this.state.fileInfo; downloadLinkExtraProps.progress = written_bytes / total_bytes; } else { downloadLinkExtraProps.state = 'done'; downloadLinkExtraProps.path = this.state.fileInfo.download_path; } return ( <section className={ 'file-tile card ' + (obscureNsfw ? 'card-obscured ' : '') + (this.props.compact ? 'file-tile--compact' : '')} onMouseEnter={this.handleMouseOver} onMouseLeave={this.handleMouseOut}> <div className="row-fluid card-content file-tile__row"> <div className="span3"> <a href={'/?show=' + this.props.name}><Thumbnail className="file-tile__thumbnail" src={this.props.metadata.thumbnail} alt={'Photo for ' + (this.props.metadata.title || this.props.name)} /></a> </div> <div className="span9"> {this.state.cost !== null && !this.state.local ? <span className="file-tile__cost"> <CreditAmount amount={this.state.cost} isEstimate={!this.state.costIncludesData}/> </span> : null} <div className="meta"><a href={'/?show=' + this.props.name}>lbry://{this.props.name}</a></div> <h3 className={'file-tile__title ' + (this.props.compact ? 'file-tile__title--compact' : '')}> <a href={'/?show=' + this.props.name}> <TruncatedText lines={3}> {this.props.metadata.title} </TruncatedText> </a> </h3> <div> {this.props.metadata.content_type.startsWith('video/') ? <WatchLink streamName={this.props.name} button="primary" /> : null} {!this.props.isMine ? <DownloadLink streamName={this.props.name} metadata={this.props.metadata} button="text" {... downloadLinkExtraProps}/> : null} </div> <p className="file-tile__description"> <TruncatedText lines={3}> {this.props.metadata.description} </TruncatedText> </p> </div> </div> {obscureNsfw && this.state.isHovered ? <div className='card-overlay'> <p> This content is Not Safe For Work. To view adult content, please change your <Link href="?settings" label="Settings" />. </p> </div> : null} </section> ); } }); export default FileTile;