Merge pull request #106 from lbryio/new-lighthouse

Update Discover and Show pages to use new Lighthouse cost reporting
This commit is contained in:
alexliebowitz 2016-12-14 13:27:46 -05:00 committed by GitHub
commit 5dbadeb941
4 changed files with 125 additions and 61 deletions

View file

@ -1,3 +1,5 @@
import lighthouse from './lighthouse.js';
var lbry = { var lbry = {
isConnected: false, isConnected: false,
rootPath: '.', rootPath: '.',
@ -179,14 +181,59 @@ lbry.getMyClaim = function(name, callback) {
lbry.call('get_my_claim', { name: name }, callback); lbry.call('get_my_claim', { name: name }, callback);
} }
lbry.getCostEstimate = function(name, callback) { lbry.getKeyFee = function(name, callback) {
lbry.call('get_est_cost', { name: name }, callback); lbry.call('get_est_cost', { name: name }, callback);
} }
lbry.getTotalCost = function(name, size, callback) {
lbry.call('get_est_cost', {
name: name,
size: size,
}, callback);
}
lbry.getPeersForBlobHash = function(blobHash, callback) { lbry.getPeersForBlobHash = function(blobHash, callback) {
lbry.call('get_peers_for_hash', { blob_hash: blobHash }, callback) lbry.call('get_peers_for_hash', { blob_hash: blobHash }, callback)
} }
lbry.getCostInfoForName = function(name, callback) {
/**
* Takes a LBRY name; will first try and calculate a total cost using
* Lighthouse. If Lighthouse can't be reached, it just retrives the
* key fee.
*
* Returns an object with members:
* - cost: Number; the calculated cost of the name
* - includes_data: Boolean; indicates whether or not the data fee info
* from Lighthouse is included.
*/
function getCostWithData(size, callback) {
lbry.getTotalCost(name, size, (cost) => {
callback({
cost: cost,
includesData: true,
});
});
}
function getCostNoData(name, callback) {
lbry.getKeyFee(name, (cost) => {
callback({
cost: cost,
includesData: false,
});
});
}
lighthouse.getSizeForName(name, (size) => {
getCostWithData(name, size, callback);
}, () => {
getCostNoData(name, callback);
}, () => {
getCostNoData(name, callback);
});
}
lbry.getFileStatus = function(name, callback) { lbry.getFileStatus = function(name, callback) {
lbry.call('get_lbry_file', { 'name': name }, callback); lbry.call('get_lbry_file', { 'name': name }, callback);
} }

View file

@ -15,23 +15,31 @@ var lighthouse = {
lbry.jsonrpc_call(this.server + this.path, method, params, callback, errorCallback, connectFailedCallback, timeout); lbry.jsonrpc_call(this.server + this.path, method, params, callback, errorCallback, connectFailedCallback, timeout);
}, },
search: function(query, callback) { search: function(query, callback, errorCallback, connectFailedCallback, timeout) {
let handleSearchFailed = function(tryNum=0) { let handleSearchFailed = function(tryNum=0) {
if (tryNum > lighthouse._max_search_tries) { if (tryNum > lighthouse._max_search_tries) {
throw new Error(`Could not connect to Lighthouse server. Last server attempted: ${lighthouse.server}`); if (connectFailedCallback) {
connectFailedCallback();
} else {
throw new Error(`Could not connect to Lighthouse server. Last server attempted: ${lighthouse.server}`);
}
} else { } else {
// Randomly choose one of the other search servers to switch to // Randomly choose one of the other search servers to switch to
let otherServers = lighthouse.servers.slice(); let otherServers = lighthouse.servers.slice();
otherServers.splice(otherServers.indexOf(lighthouse.server), 1); otherServers.splice(otherServers.indexOf(lighthouse.server), 1);
lighthouse.server = otherServers[Math.round(Math.random() * (otherServers.length - 1))]; lighthouse.server = otherServers[Math.round(Math.random() * (otherServers.length - 1))];
lighthouse.call('search', [query], callback, undefined, function() { lighthouse.call('search', [query], callback, errorCallback, function() {
handleSearchFailed(tryNum + 1); handleSearchFailed(tryNum + 1);
}, lighthouse._search_timeout); }, lighthouse._search_timeout);
} }
} }
lighthouse.call('search', [query], callback, undefined, function() { handleSearchFailed() }, lighthouse._search_timeout); lighthouse.call('search', [query], callback, errorCallback, function() { handleSearchFailed() }, lighthouse._search_timeout);
},
getSizeForName: function(name, callback, errorCallback, connectFailedCallback, timeout) {
return lighthouse.call('get_size_for_name', [name], callback, errorCallback, connectFailedCallback, timeout);
} }
}; };

View file

@ -46,8 +46,7 @@ var SearchResults = React.createClass({
var mediaType = lbry.getMediaType(result.value.content_type); var mediaType = lbry.getMediaType(result.value.content_type);
rows.push( rows.push(
<SearchResultRow key={result.name} name={result.name} title={result.value.title} imgUrl={result.value.thumbnail} <SearchResultRow key={result.name} name={result.name} title={result.value.title} imgUrl={result.value.thumbnail}
description={result.value.description} cost={result.cost} nsfw={result.value.nsfw} description={result.value.description} nsfw={result.value.nsfw} mediaType={mediaType} />
mediaType={mediaType} />
); );
}); });
return ( return (
@ -93,6 +92,8 @@ var SearchResultRow = React.createClass({
return { return {
downloading: false, downloading: false,
isHovered: false, isHovered: false,
cost: null,
costIncludesData: null,
} }
}, },
handleMouseOver: function() { handleMouseOver: function() {
@ -105,6 +106,21 @@ var SearchResultRow = React.createClass({
isHovered: false, isHovered: false,
}); });
}, },
componentWillMount: function() {
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,
});
});
}
},
render: function() { render: function() {
var obscureNsfw = !lbry.getClientSetting('showNsfw') && this.props.nsfw; var obscureNsfw = !lbry.getClientSetting('showNsfw') && this.props.nsfw;
if (!this.props.compact) { if (!this.props.compact) {
@ -122,9 +138,11 @@ var SearchResultRow = React.createClass({
<a href={'/?show=' + this.props.name}><Thumbnail src={this.props.imgUrl} alt={'Photo for ' + (this.props.title || this.props.name)} style={searchRowImgStyle} /></a> <a href={'/?show=' + this.props.name}><Thumbnail src={this.props.imgUrl} alt={'Photo for ' + (this.props.title || this.props.name)} style={searchRowImgStyle} /></a>
</div> </div>
<div className="span9"> <div className="span9">
<span style={searchRowCostStyle}> {this.state.cost !== null
<CreditAmount amount={this.props.cost} isEstimate={!this.props.available}/> ? <span style={searchRowCostStyle}>
</span> <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> <div className="meta"><a href={'/?show=' + this.props.name}>lbry://{this.props.name}</a></div>
<h3 style={titleStyle}> <h3 style={titleStyle}>
<a href={'/?show=' + this.props.name}> <a href={'/?show=' + this.props.name}>
@ -173,7 +191,7 @@ var FeaturedContentItem = React.createClass({
return { return {
metadata: null, metadata: null,
title: null, title: null,
amount: 0.0, cost: null,
overlayShowing: false, overlayShowing: false,
}; };
}, },
@ -183,21 +201,18 @@ var FeaturedContentItem = React.createClass({
}, },
componentDidMount: function() { componentDidMount: function() {
this.resolveSearch = true; this._isMounted = true;
lighthouse.search(this.props.name, function(results) { lbry.resolveName(this.props.name, (metadata) => {
var result = results[0]; if (!this._isMounted) {
var metadata = result.value; return;
if (this.resolveSearch)
{
this.setState({
metadata: metadata,
amount: result.cost,
available: result.available,
title: metadata && metadata.title ? metadata.title : ('lbry://' + this.props.name),
});
} }
}.bind(this));
this.setState({
metadata: metadata,
title: metadata && metadata.title ? metadata.title : ('lbry://' + this.props.name),
});
});
}, },
render: function() { render: function() {
@ -209,7 +224,7 @@ var FeaturedContentItem = React.createClass({
return (<div style={featuredContentItemContainerStyle}> return (<div style={featuredContentItemContainerStyle}>
<SearchResultRow name={this.props.name} title={this.state.title} imgUrl={this.state.metadata.thumbnail} <SearchResultRow name={this.props.name} title={this.state.title} imgUrl={this.state.metadata.thumbnail}
description={this.state.metadata.description} mediaType={lbry.getMediaType(this.state.metadata.content_type)} description={this.state.metadata.description} mediaType={lbry.getMediaType(this.state.metadata.content_type)}
cost={this.state.amount} nsfw={this.state.metadata.nsfw} available={this.state.available} compact /> nsfw={this.state.metadata.nsfw} compact />
</div>); </div>);
} }
}); });

View file

@ -16,9 +16,9 @@ var formatItemImgStyle = {
var FormatItem = React.createClass({ var FormatItem = React.createClass({
propTypes: { propTypes: {
claimInfo: React.PropTypes.object, claimInfo: React.PropTypes.object,
amount: React.PropTypes.number, cost: React.PropTypes.number,
name: React.PropTypes.string, name: React.PropTypes.string,
available: React.PropTypes.bool, costIncludesData: React.PropTypes.bool,
}, },
render: function() { render: function() {
@ -31,8 +31,8 @@ var FormatItem = React.createClass({
var license = claimInfo.license; var license = claimInfo.license;
var fileContentType = (claimInfo.content_type || claimInfo['content-type']); var fileContentType = (claimInfo.content_type || claimInfo['content-type']);
var mediaType = lbry.getMediaType(fileContentType); var mediaType = lbry.getMediaType(fileContentType);
var available = this.props.available; var costIncludesData = this.props.costIncludesData;
var amount = this.props.amount || 0.0; var cost = this.props.cost || 0.0;
return ( return (
<div className="row-fluid"> <div className="row-fluid">
@ -48,7 +48,7 @@ var FormatItem = React.createClass({
<td>Content-Type</td><td>{fileContentType}</td> <td>Content-Type</td><td>{fileContentType}</td>
</tr> </tr>
<tr> <tr>
<td>Cost</td><td><CreditAmount amount={amount} isEstimate={!available}/></td> <td>Cost</td><td><CreditAmount amount={cost} isEstimate={!costIncludesData}/></td>
</tr> </tr>
<tr> <tr>
<td>Author</td><td>{author}</td> <td>Author</td><td>{author}</td>
@ -78,9 +78,9 @@ var FormatItem = React.createClass({
var FormatsSection = React.createClass({ var FormatsSection = React.createClass({
propTypes: { propTypes: {
claimInfo: React.PropTypes.object, claimInfo: React.PropTypes.object,
amount: React.PropTypes.number, cost: React.PropTypes.number,
name: React.PropTypes.string, name: React.PropTypes.string,
available: React.PropTypes.bool, costIncludesData: React.PropTypes.bool,
}, },
render: function() { render: function() {
var name = this.props.name; var name = this.props.name;
@ -102,7 +102,7 @@ var FormatsSection = React.createClass({
{/* In future, anticipate multiple formats, just a guess at what it could look like {/* In future, anticipate multiple formats, just a guess at what it could look like
// var formats = this.props.claimInfo.formats // var formats = this.props.claimInfo.formats
// return (<tbody>{formats.map(function(format,i){ */} // return (<tbody>{formats.map(function(format,i){ */}
<FormatItem claimInfo={format} amount={this.props.amount} name={this.props.name} available={this.props.available} /> <FormatItem claimInfo={format} cost={this.props.cost} name={this.props.name} costIncludesData={this.props.costIncludesData} />
{/* })}</tbody>); */} {/* })}</tbody>); */}
</div>); </div>);
} }
@ -114,50 +114,44 @@ var DetailPage = React.createClass({
}, },
getInitialState: function() { getInitialState: function() {
return { return {
claimInfo: null, metadata: null,
amount: null, cost: null,
searching: true, costIncludesData: null,
matchFound: null, nameLookupComplete: null,
}; };
}, },
componentWillMount: function() { componentWillMount: function() {
document.title = 'lbry://' + this.props.name; document.title = 'lbry://' + this.props.name;
lighthouse.search(this.props.name, (results) => { lbry.resolveName(this.props.name, (metadata) => {
var result = results[0]; this.setState({
metadata: metadata,
nameLookupComplete: true,
});
});
if (result.name != this.props.name) { lbry.getCostInfoForName(this.props.name, ({cost, includesData}) => {
this.setState({ this.setState({
searching: false, cost: cost,
matchFound: false, costIncludesData: includesData,
}); });
} else {
this.setState({
amount: result.cost,
available: result.available,
claimInfo: result.value,
searching: false,
matchFound: true,
});
}
}); });
}, },
render: function() { render: function() {
if (this.state.claimInfo == null && this.state.searching) { if (this.state.metadata == null) {
// Still waiting for metadata
return null; return null;
} }
var name = this.props.name; const name = this.props.name;
var available = this.state.available; const costIncludesData = this.state.costIncludesData;
var claimInfo = this.state.claimInfo; const metadata = this.state.metadata;
var amount = this.state.amount; const cost = this.state.cost;
return ( return (
<main> <main>
<section className="card"> <section className="card">
{this.state.matchFound ? ( {this.state.nameLookupComplete ? (
<FormatsSection name={name} claimInfo={claimInfo} amount={amount} available={available} /> <FormatsSection name={name} claimInfo={metadata} cost={cost} costIncludesData={costIncludesData} />
) : ( ) : (
<div> <div>
<h2>No content</h2> <h2>No content</h2>