React router #343

Merged
bones7242 merged 96 commits from react-router into master 2018-02-15 08:02:17 +01:00
9 changed files with 165 additions and 132 deletions
Showing only changes of commit 0e88bb4c60 - Show all commits

View file

@ -55,14 +55,14 @@ module.exports = {
claimId, claimId,
}; };
}, },
parseName: function (name) { parseClaim: function (claim) {
logger.debug('parsing name:', name); logger.debug('parsing name:', claim);
const componentsRegex = new RegExp( const componentsRegex = new RegExp(
'([^:$#/.]*)' + // name (stops at the first modifier) '([^:$#/.]*)' + // name (stops at the first modifier)
'([:$#.]?)([^/]*)' // modifier separator, modifier (stops at the first path separator or end) '([:$#.]?)([^/]*)' // modifier separator, modifier (stops at the first path separator or end)
); );
const [proto, claimName, modifierSeperator, modifier] = componentsRegex const [proto, claimName, modifierSeperator, modifier] = componentsRegex
.exec(name) .exec(claim)
.map(match => match || null); .map(match => match || null);
logger.debug(`${proto}, ${claimName}, ${modifierSeperator}, ${modifier}`); logger.debug(`${proto}, ${claimName}, ${modifierSeperator}, ${modifier}`);
@ -75,7 +75,6 @@ module.exports = {
throw new Error(`Invalid characters in claim name: ${nameBadChars.join(', ')}.`); throw new Error(`Invalid characters in claim name: ${nameBadChars.join(', ')}.`);
} }
// Validate and process modifier // Validate and process modifier
let isServeRequest = false;
if (modifierSeperator) { if (modifierSeperator) {
if (!modifier) { if (!modifier) {
throw new Error(`No file extension provided after separator ${modifierSeperator}.`); throw new Error(`No file extension provided after separator ${modifierSeperator}.`);
@ -83,11 +82,29 @@ module.exports = {
if (modifierSeperator !== '.') { if (modifierSeperator !== '.') {
throw new Error(`The ${modifierSeperator} modifier is not supported in the claim name`); throw new Error(`The ${modifierSeperator} modifier is not supported in the claim name`);
} }
isServeRequest = true;
} }
// return results
return { return {
claimName, claimName,
isServeRequest, };
},
parseModifier: function (claim) {
logger.debug('parsing modifier:', claim);
const componentsRegex = new RegExp(
'([^:$#/.]*)' + // name (stops at the first modifier)
'([:$#.]?)([^/]*)' // modifier separator, modifier (stops at the first path separator or end)
);
const [proto, claimName, modifierSeperator, modifier] = componentsRegex
.exec(claim)
.map(match => match || null);
logger.debug(`${proto}, ${claimName}, ${modifierSeperator}, ${modifier}`);
// Validate and process modifier
let hasFileExtension = false;
if (modifierSeperator) {
hasFileExtension = true;
}
return {
hasFileExtension,
}; };
}, },
}; };

View file

@ -1,9 +1,9 @@
neb-b commented 2018-02-05 20:47:37 +01:00 (Migrated from github.com)
Review

Why do you do const that = this?

Why do you do `const that = this`?
neb-b commented 2018-02-05 20:52:52 +01:00 (Migrated from github.com)
Review

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same <AssetDisplay /> it will make these requests again, even if you just made them a second ago

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same `<AssetDisplay />` it will make these requests again, even if you just made them a second ago
bones7242 commented 2018-02-07 00:13:24 +01:00 (Migrated from github.com)
Review

I had a misunderstanding of how the this context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.

I had a misunderstanding of how the `this` context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.
neb-b commented 2018-02-05 20:47:37 +01:00 (Migrated from github.com)
Review

Why do you do const that = this?

Why do you do `const that = this`?
neb-b commented 2018-02-05 20:52:52 +01:00 (Migrated from github.com)
Review

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same <AssetDisplay /> it will make these requests again, even if you just made them a second ago

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same `<AssetDisplay />` it will make these requests again, even if you just made them a second ago
bones7242 commented 2018-02-07 00:13:24 +01:00 (Migrated from github.com)
Review

I had a misunderstanding of how the this context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.

I had a misunderstanding of how the `this` context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.
import React from 'react'; import React from 'react';
const AssetDisplay = ({ claimName, claimId }) => { const AssetDisplay = ({ name, claimId }) => {
neb-b commented 2018-02-05 20:47:37 +01:00 (Migrated from github.com)
Review

Why do you do const that = this?

Why do you do `const that = this`?
neb-b commented 2018-02-05 20:52:52 +01:00 (Migrated from github.com)
Review

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same <AssetDisplay /> it will make these requests again, even if you just made them a second ago

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same `<AssetDisplay />` it will make these requests again, even if you just made them a second ago
bones7242 commented 2018-02-07 00:13:24 +01:00 (Migrated from github.com)
Review

I had a misunderstanding of how the this context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.

I had a misunderstanding of how the `this` context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.
neb-b commented 2018-02-05 20:47:37 +01:00 (Migrated from github.com)
Review

Why do you do const that = this?

Why do you do `const that = this`?
neb-b commented 2018-02-05 20:52:52 +01:00 (Migrated from github.com)
Review

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same <AssetDisplay /> it will make these requests again, even if you just made them a second ago

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same `<AssetDisplay />` it will make these requests again, even if you just made them a second ago
bones7242 commented 2018-02-07 00:13:24 +01:00 (Migrated from github.com)
Review

I had a misunderstanding of how the this context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.

I had a misunderstanding of how the `this` context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.
return ( return (
<div> <div>
<p>display {claimName}#{claimId} here</p> <p>display {name}#{claimId} here</p>
neb-b commented 2018-02-05 20:47:37 +01:00 (Migrated from github.com)
Review

Why do you do const that = this?

Why do you do `const that = this`?
neb-b commented 2018-02-05 20:52:52 +01:00 (Migrated from github.com)
Review

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same <AssetDisplay /> it will make these requests again, even if you just made them a second ago

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same `<AssetDisplay />` it will make these requests again, even if you just made them a second ago
bones7242 commented 2018-02-07 00:13:24 +01:00 (Migrated from github.com)
Review

I had a misunderstanding of how the this context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.

I had a misunderstanding of how the `this` context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.
neb-b commented 2018-02-05 20:47:37 +01:00 (Migrated from github.com)
Review

Why do you do const that = this?

Why do you do `const that = this`?
neb-b commented 2018-02-05 20:52:52 +01:00 (Migrated from github.com)
Review

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same <AssetDisplay /> it will make these requests again, even if you just made them a second ago

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same `<AssetDisplay />` it will make these requests again, even if you just made them a second ago
bones7242 commented 2018-02-07 00:13:24 +01:00 (Migrated from github.com)
Review

I had a misunderstanding of how the this context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.

I had a misunderstanding of how the `this` context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.
</div> </div>
); );
}; };

neb-b commented 2018-02-05 20:47:37 +01:00 (Migrated from github.com)
Review

Why do you do const that = this?

Why do you do `const that = this`?
neb-b commented 2018-02-05 20:52:52 +01:00 (Migrated from github.com)
Review

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same <AssetDisplay /> it will make these requests again, even if you just made them a second ago

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same `<AssetDisplay />` it will make these requests again, even if you just made them a second ago
bones7242 commented 2018-02-07 00:13:24 +01:00 (Migrated from github.com)
Review

I had a misunderstanding of how the this context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.

I had a misunderstanding of how the `this` context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.
neb-b commented 2018-02-05 20:47:37 +01:00 (Migrated from github.com)
Review

Why do you do const that = this?

Why do you do `const that = this`?
neb-b commented 2018-02-05 20:52:52 +01:00 (Migrated from github.com)
Review

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same <AssetDisplay /> it will make these requests again, even if you just made them a second ago

I think this is another piece you can move entirely into redux. Currently if this component is rendered, then a user navigates away and comes back to the same `<AssetDisplay />` it will make these requests again, even if you just made them a second ago
bones7242 commented 2018-02-07 00:13:24 +01:00 (Migrated from github.com)
Review

I had a misunderstanding of how the this context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.

I had a misunderstanding of how the `this` context works and when I needed to pass this in to a function manually. I was able to remove it from the app in multiple places where it isn't necessary.

View file

@ -54,7 +54,7 @@ class AssetInfo extends React.Component {
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
<div className="row row--padded row--wide row--no-top"> <div className="row row--padded row--wide row--no-top">
<div id="show-short-link"> <div id="show-short-link">
<div className="column column--2 column--med-10"> <div className="column column--2 column--med-10">
<a className="link--primary" href={`/${this.props.shortId}/${this.props.name}.${this.props.fileExt}`}><span <a className="link--primary" href={`/${this.props.shortClaimId}/${this.props.name}.${this.props.fileExt}`}><span
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
className="text">Link:</span></a> className="text">Link:</span></a>
</div> </div>
<div className="column column--8 column--med-10"> <div className="column column--8 column--med-10">
@ -63,7 +63,7 @@ class AssetInfo extends React.Component {
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
<div className="input-error" id="input-error-copy-short-link" hidden="true">error here</div> <div className="input-error" id="input-error-copy-short-link" hidden="true">error here</div>
<input type="text" id="short-link" className="input-disabled input-text--full-width" readOnly <input type="text" id="short-link" className="input-disabled input-text--full-width" readOnly
spellCheck="false" spellCheck="false"
value={`${this.props.host}/${this.props.shortId}/${this.props.name}.${this.props.fileExt}`} value={`${this.props.host}/${this.props.shortClaimId}/${this.props.name}.${this.props.fileExt}`}
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
onClick={this.select}/> onClick={this.select}/>
</div> </div>
<div className="column column--1"> </div> <div className="column column--1"> </div>
@ -114,13 +114,13 @@ class AssetInfo extends React.Component {
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
<div <div
className="row row--short row--wide flex-container--row flex-container--space-between-bottom flex-container--wrap"> className="row row--short row--wide flex-container--row flex-container--space-between-bottom flex-container--wrap">
<a className="link--primary" target="_blank" <a className="link--primary" target="_blank"
href={`https://twitter.com/intent/tweet?text=${this.props.host}/${this.props.shortId}/${this.props.name}`}>twitter</a> href={`https://twitter.com/intent/tweet?text=${this.props.host}/${this.props.shortClaimId}/${this.props.name}`}>twitter</a>
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
<a className="link--primary" target="_blank" <a className="link--primary" target="_blank"
href={`https://www.facebook.com/sharer/sharer.php?u=${this.props.host}/${this.props.shortId}/${this.props.name}`}>facebook</a> href={`https://www.facebook.com/sharer/sharer.php?u=${this.props.host}/${this.props.shortClaimId}/${this.props.name}`}>facebook</a>
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
<a className="link--primary" target="_blank" <a className="link--primary" target="_blank"
href={`http://tumblr.com/widgets/share/tool?canonicalUrl=${this.props.host}/${this.props.shortId}/${this.props.name}`}>tumblr</a> href={`http://tumblr.com/widgets/share/tool?canonicalUrl=${this.props.host}/${this.props.shortClaimId}/${this.props.name}`}>tumblr</a>
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
<a className="link--primary" target="_blank" <a className="link--primary" target="_blank"
href={`https://www.reddit.com/submit?url=${this.props.host}/${this.props.shortId}/${this.props.name}&title=${this.props.name}`}>reddit</a> href={`https://www.reddit.com/submit?url=${this.props.host}/${this.props.shortClaimId}/${this.props.name}&title=${this.props.name}`}>reddit</a>
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
</div> </div>
</div> </div>
</div> </div>
@ -168,6 +168,6 @@ class AssetInfo extends React.Component {
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
}; };
// required props // required props
// {channelName, certificateId, description, shortId, name, fileExt, claimId, contentType, thumbnail, host} // {channelName, certificateId, description, shortClaimId, name, fileExt, claimId, contentType, thumbnail, host}
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
export default AssetInfo; export default AssetInfo;

neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.
neb-b commented 2018-02-05 20:39:44 +01:00 (Migrated from github.com)
Review

This should be a button if it isn't linking anywhere.

This should be a `button` if it isn't linking anywhere.

View file

@ -0,0 +1,20 @@
import React from 'react';
import NavBar from 'containers/NavBar';
class ErrorPage extends React.Component {
render () {
return (
<div>
<NavBar/>
<div className="row row--padded">
<p>{this.props.error}</p>
</div>
</div>
);
}
};
// required props
// error
export default ErrorPage;

View file

@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import ShowLite from 'components/ShowAssetLite'; import ShowAssetLite from 'components/ShowAssetLite';
import ShowDetails from 'components/ShowAssetDetails'; import ShowAssetDetails from 'components/ShowAssetDetails';
import request from 'utils/request'; import request from 'utils/request';
class ShowAsset extends React.Component { class ShowAsset extends React.Component {
@ -83,14 +83,14 @@ class ShowAsset extends React.Component {
render () { render () {
if (this.props.isServeRequest) { if (this.props.isServeRequest) {
return ( return (
<ShowLite <ShowAssetLite
error={this.state.error} error={this.state.error}
claimData={this.state.claimData} claimData={this.state.claimData}
/> />
); );
} }
return ( return (
<ShowDetails <ShowAssetDetails
error={this.state.error} error={this.state.error}
claimData={this.state.claimData} claimData={this.state.claimData}
/> />

View file

@ -13,7 +13,9 @@ class ShowDetails extends React.Component {
<div> <div>
<NavBar/> <NavBar/>
{this.props.error && {this.props.error &&
<p>{this.props.error}</p> <div className="row row--padded">
<p>{this.props.error}</p>
</div>
} }
{this.props.claimData && {this.props.claimData &&
<div className="row row--tall row--padded"> <div className="row row--tall row--padded">
@ -23,13 +25,24 @@ class ShowDetails extends React.Component {
<div className="column column--5 column--sml-10 align-content-top"> <div className="column column--5 column--sml-10 align-content-top">
<div className="row row--padded"> <div className="row row--padded">
<AssetDisplay <AssetDisplay
claimName={this.props.claimData.name} name={this.props.claimData.name}
claimId={this.props.claimData.claimId} claimId={this.props.claimData.claimId}
/> />
</div> </div>
</div><div className="column column--5 column--sml-10 align-content-top"> </div><div className="column column--5 column--sml-10 align-content-top">
<div className="row row--padded"> <div className="row row--padded">
<AssetInfo claimId={this.props.claimData.claimId}/> <AssetInfo
channelName={this.props.claimData.channelName}
certificateId={this.props.claimData.certificateId}
description={this.props.claimData.description}
shortClaimId={this.props.claimData.shortClaimId}
name={this.props.claimData.name}
fileExt={this.props.claimData.fileExt}
claimId={this.props.claimData.claimId}
contentType={this.props.claimData.contentType}
thumbnail={this.props.claimData.thumbnail}
host={this.props.claimData.host}
/>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,4 +1,5 @@
import React from 'react'; import React from 'react';
import ErrorPage from 'components/ErrorPage';
import ShowAsset from 'components/ShowAsset'; import ShowAsset from 'components/ShowAsset';
import ShowChannel from 'components/ShowChannel'; import ShowChannel from 'components/ShowChannel';
import lbryUri from 'utils/lbryUri'; import lbryUri from 'utils/lbryUri';
@ -10,6 +11,7 @@ class ShowPage extends React.Component {
constructor (props) { constructor (props) {
super(props); super(props);
this.state = { this.state = {
error : null,
identifier : null, identifier : null,
claim : null, claim : null,
isServeRequest: null, isServeRequest: null,
@ -46,9 +48,9 @@ class ShowPage extends React.Component {
let isChannel, channelName, channelClaimId, claimId, claimName, isServeRequest; let isChannel, channelName, channelClaimId, claimId, claimName, isServeRequest;
try { try {
({ isChannel, channelName, channelClaimId, claimId } = lbryUri.parseIdentifier(identifier)); ({ isChannel, channelName, channelClaimId, claimId } = lbryUri.parseIdentifier(identifier));
({ claimName, isServeRequest } = lbryUri.parseName(claim)); ({ claimName, isServeRequest } = lbryUri.parseClaim(claim));
} catch (error) { } catch (error) {
return console.log('error:', error); return this.setState({error: error.message});
} }
// set state // set state
return this.setState({ return this.setState({
@ -72,7 +74,7 @@ class ShowPage extends React.Component {
try { try {
({ isChannel, channelName, channelClaimId } = lbryUri.parseIdentifier(claim)); ({ isChannel, channelName, channelClaimId } = lbryUri.parseIdentifier(claim));
} catch (error) { } catch (error) {
return console.log('error:', error); return this.setState({error: error.message});
} }
if (isChannel) { if (isChannel) {
return this.setState({ return this.setState({
@ -85,9 +87,9 @@ class ShowPage extends React.Component {
} }
let claimName, isServeRequest; let claimName, isServeRequest;
try { try {
({claimName, isServeRequest} = lbryUri.parseName(claim)); ({claimName, isServeRequest} = lbryUri.parseClaim(claim));
} catch (error) { } catch (error) {
return console.log('error:', error); return this.setState({error: error.message});
} }
this.setState({ this.setState({
claim: { claim: {
@ -98,6 +100,11 @@ class ShowPage extends React.Component {
} }
render () { render () {
console.log('rendering ShowPage'); console.log('rendering ShowPage');
if (this.state.error) {
return (
<ErrorPage error={this.state.error}/>
);
}
if (this.state.claim) { if (this.state.claim) {
if (this.state.claim.isChannel) { if (this.state.claim.isChannel) {
return ( return (

View file

@ -16,18 +16,18 @@ module.exports = {
// Validate and process name // Validate and process name
if (!value) { if (!value) {
throw new Error(`Check your url. No channel name provided before "${modifierSeperator}"`); throw new Error(`Check your URL. No channel name provided before "${modifierSeperator}"`);
} }
const isChannel = value.startsWith(module.exports.CHANNEL_CHAR); const isChannel = value.startsWith(module.exports.CHANNEL_CHAR);
const channelName = isChannel ? value : null; const channelName = isChannel ? value : null;
let claimId; let claimId;
if (isChannel) { if (isChannel) {
if (!channelName) { if (!channelName) {
throw new Error('No channel name after @.'); throw new Error('Check your URL. No channel name after "@".');
} }
const nameBadChars = (channelName).match(module.exports.REGEXP_INVALID_CHANNEL); const nameBadChars = (channelName).match(module.exports.REGEXP_INVALID_CHANNEL);
if (nameBadChars) { if (nameBadChars) {
throw new Error(`Invalid characters in channel name: ${nameBadChars.join(', ')}.`); throw new Error(`Check your URL. Invalid characters in channel name: "${nameBadChars.join(', ')}".`);
} }
} else { } else {
claimId = value; claimId = value;
@ -37,13 +37,13 @@ module.exports = {
let channelClaimId; let channelClaimId;
if (modifierSeperator) { if (modifierSeperator) {
if (!modifier) { if (!modifier) {
throw new Error(`No modifier provided after separator "${modifierSeperator}"`); throw new Error(`Check your URL. No modifier provided after separator "${modifierSeperator}"`);
} }
if (modifierSeperator === ':') { if (modifierSeperator === ':') {
channelClaimId = modifier; channelClaimId = modifier;
} else { } else {
throw new Error(`The "${modifierSeperator}" modifier is not currently supported`); throw new Error(`Check your URL. The "${modifierSeperator}" modifier is not currently supported`);
} }
} }
return { return {
@ -53,7 +53,7 @@ module.exports = {
claimId, claimId,
}; };
}, },
parseName: function (name) { parseClaim: function (name) {
console.log('parsing name:', name); console.log('parsing name:', name);
const componentsRegex = new RegExp( const componentsRegex = new RegExp(
'([^:$#/.]*)' + // name (stops at the first modifier) '([^:$#/.]*)' + // name (stops at the first modifier)
@ -66,20 +66,20 @@ module.exports = {
// Validate and process name // Validate and process name
if (!claimName) { if (!claimName) {
throw new Error('No claim name provided before .'); throw new Error('Check your URL. No claim name provided before "."');
} }
const nameBadChars = (claimName).match(module.exports.REGEXP_INVALID_CLAIM); const nameBadChars = (claimName).match(module.exports.REGEXP_INVALID_CLAIM);
if (nameBadChars) { if (nameBadChars) {
throw new Error(`Invalid characters in claim name: ${nameBadChars.join(', ')}.`); throw new Error(`Check your URL. Invalid characters in claim name: "${nameBadChars.join(', ')}".`);
} }
// Validate and process modifier // Validate and process modifier
let isServeRequest = false; let isServeRequest = false;
if (modifierSeperator) { if (modifierSeperator) {
if (!modifier) { if (!modifier) {
throw new Error(`No file extension provided after separator ${modifierSeperator}.`); throw new Error(`Check your URL. No file extension provided after separator "${modifierSeperator}".`);
} }
if (modifierSeperator !== '.') { if (modifierSeperator !== '.') {
throw new Error(`The ${modifierSeperator} modifier is not supported in the claim name`); throw new Error(`Check your URL. The "${modifierSeperator}" modifier is not supported in the claim name.`);
} }
isServeRequest = true; isServeRequest = true;
} }

View file

@ -2,13 +2,10 @@ const logger = require('winston');
const { getClaimId, getLocalFileRecord } = require('../controllers/serveController.js'); const { getClaimId, getLocalFileRecord } = require('../controllers/serveController.js');
const serveHelpers = require('../helpers/serveHelpers.js'); const serveHelpers = require('../helpers/serveHelpers.js');
const { handleRequestError } = require('../helpers/errorHandlers.js'); const { handleRequestError } = require('../helpers/errorHandlers.js');
const { postToStats } = require('../helpers/statsHelpers.js');
const db = require('../models');
const lbryUri = require('../helpers/lbryUri.js'); const lbryUri = require('../helpers/lbryUri.js');
const SERVE = 'SERVE'; const SERVE = 'SERVE';
const SHOW = 'SHOW'; const SHOW = 'SHOW';
const SHOWLITE = 'SHOWLITE';
const NO_CHANNEL = 'NO_CHANNEL'; const NO_CHANNEL = 'NO_CHANNEL';
const NO_CLAIM = 'NO_CLAIM'; const NO_CLAIM = 'NO_CLAIM';
const NO_FILE = 'NO_FILE'; const NO_FILE = 'NO_FILE';
@ -39,49 +36,23 @@ function clientWantsAsset ({accept, range}) {
return imageIsWanted || videoIsWanted; return imageIsWanted || videoIsWanted;
} }
function determineResponseType (isServeRequest, headers) { function determineResponseType (hasFileExtension, headers) {
let responseType; let responseType;
if (isServeRequest) { if (hasFileExtension) {
responseType = SERVE; responseType = SERVE; // assume a serve request if file extension is present
if (clientAcceptsHtml(headers)) { // this is in case a serve request comes from a browser if (clientAcceptsHtml(headers)) { // if the request comes from a browser, change it to a show request
responseType = SHOWLITE; responseType = SHOW;
} }
} else { } else {
responseType = SHOW; responseType = SHOW;
if (clientWantsAsset(headers) && requestIsFromBrowser(headers)) { // this is in case someone embeds a show url if (clientWantsAsset(headers) && requestIsFromBrowser(headers)) { // this is in case someone embeds a show url
logger.debug('Show request came from browser and wants an image/video; changing response to serve.'); logger.debug('Show request came from browser but wants an image/video. Changing response to serve...');
responseType = SERVE; responseType = SERVE;
} }
} }
return responseType; return responseType;
} }
// function showAssetToClient (claimId, name, res) {
// return Promise
// .all([db.Claim.resolveClaim(name, claimId), db.Claim.getShortClaimIdFromLongClaimId(claimId, name)])
// .then(([claimInfo, shortClaimId]) => {
// // logger.debug('claimInfo:', claimInfo);
// // logger.debug('shortClaimId:', shortClaimId);
// return serveHelpers.showFile(claimInfo, shortClaimId, res);
// })
// .catch(error => {
// throw error;
// });
// }
//
// function showLiteAssetToClient (claimId, name, res) {
// return Promise
// .all([db.Claim.resolveClaim(name, claimId), db.Claim.getShortClaimIdFromLongClaimId(claimId, name)])
// .then(([claimInfo, shortClaimId]) => {
// // logger.debug('claimInfo:', claimInfo);
// // logger.debug('shortClaimId:', shortClaimId);
// return serveHelpers.showFileLite(claimInfo, shortClaimId, res);
// })
// .catch(error => {
// throw error;
// });
// }
function serveAssetToClient (claimId, name, res) { function serveAssetToClient (claimId, name, res) {
return getLocalFileRecord(claimId, name) return getLocalFileRecord(claimId, name)
.then(fileInfo => { .then(fileInfo => {
@ -115,82 +86,87 @@ function logRequestData (responseType, claimName, channelName, claimId) {
module.exports = (app) => { module.exports = (app) => {
// route to serve a specific asset using the channel or claim id // route to serve a specific asset using the channel or claim id
app.get('/:identifier/:name', ({ headers, ip, originalUrl, params }, res) => { app.get('/:identifier/:claim', ({ headers, ip, originalUrl, params }, res) => {
let isChannel, channelName, channelClaimId, claimId, claimName, isServeRequest; // decide if this is a show request
let hasFileExtension;
try {
({ hasFileExtension } = lbryUri.parseModifier(params.claim));
} catch (error) {
return res.status(200).json({success: false, message: error.message});
}
let responseType = determineResponseType(hasFileExtension, headers);
if (responseType !== SERVE) {
return res.status(200).render('index');
}
// parse the claim
let claimName;
try {
({ claimName } = lbryUri.parseClaim(params.claim));
} catch (error) {
return res.status(200).json({success: false, message: error.message});
}
// parse the identifier
let isChannel, channelName, channelClaimId, claimId;
try { try {
({ isChannel, channelName, channelClaimId, claimId } = lbryUri.parseIdentifier(params.identifier)); ({ isChannel, channelName, channelClaimId, claimId } = lbryUri.parseIdentifier(params.identifier));
({ claimName, isServeRequest } = lbryUri.parseName(params.name));
} catch (error) { } catch (error) {
return handleRequestError(originalUrl, ip, error, res); return handleRequestError(originalUrl, ip, error, res);
} }
if (!isChannel) { if (!isChannel) {
[claimId, claimName] = flipClaimNameAndIdForBackwardsCompatibility(claimId, claimName); [claimId, claimName] = flipClaimNameAndIdForBackwardsCompatibility(claimId, claimName);
} }
let responseType = determineResponseType(isServeRequest, headers);
// log the request data for debugging // log the request data for debugging
logRequestData(responseType, claimName, channelName, claimId); logRequestData(responseType, claimName, channelName, claimId);
// if a serve request, serve, otherwise send the react app // get the claim Id and then serve the asset
if (responseType === SERVE) { getClaimId(channelName, channelClaimId, claimName, claimId)
// get the claim Id and then serve/show the asset .then(fullClaimId => {
getClaimId(channelName, channelClaimId, claimName, claimId) if (fullClaimId === NO_CLAIM) {
.then(fullClaimId => { return res.status(200).json({success: false, message: 'no claim id could be found'});
if (fullClaimId === NO_CLAIM) { } else if (fullClaimId === NO_CHANNEL) {
return res.status(200).json({success: false, message: 'no claim id could be found'}); return res.status(200).json({success: false, message: 'no channel id could be found'});
} else if (fullClaimId === NO_CHANNEL) { }
return res.status(200).json({success: false, message: 'no channel id could be found'}); serveAssetToClient(fullClaimId, claimName, res);
} // postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'success');
serveAssetToClient(fullClaimId, claimName, res); })
// postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'success'); .catch(error => {
}) handleRequestError(originalUrl, ip, error, res);
.catch(error => { // postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'fail');
handleRequestError(originalUrl, ip, error, res); });
// postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'fail');
});
} else {
res.status(200).render('index');
}
}); });
// route to serve the winning asset at a claim or a channel page // route to serve the winning asset at a claim or a channel page
app.get('/:identifier', ({ headers, ip, originalUrl, params, query }, res) => { app.get('/:claim', ({ headers, ip, originalUrl, params, query }, res) => {
let isChannel, channelName, channelClaimId; // decide if this is a show request
let hasFileExtension;
try { try {
({ isChannel, channelName, channelClaimId } = lbryUri.parseIdentifier(params.identifier)); ({ hasFileExtension } = lbryUri.parseModifier(params.claim));
// log the result
logger.debug(`isChannel: ${isChannel}, channelName: ${channelName}, channelClaimId: ${channelClaimId}`);
} catch (error) { } catch (error) {
return handleRequestError(originalUrl, ip, error, res); return res.status(200).json({success: false, message: error.message});
} }
if (isChannel) { let responseType = determineResponseType(hasFileExtension, headers);
// handle showing the channel page if (responseType !== SERVE) {
return res.status(200).render('index'); return res.status(200).render('index');
} else {
let claimName, isServeRequest;
try {
({claimName, isServeRequest} = lbryUri.parseName(params.identifier));
} catch (error) {
return handleRequestError(originalUrl, ip, error, res);
}
let responseType = determineResponseType(isServeRequest, headers);
// log the request data for debugging
logRequestData(responseType, claimName, null, null);
// if a serve request, serve, otherwise send the react app
if (responseType === SERVE) {
// get the claim Id and then serve/show the asset
getClaimId(null, null, claimName, null)
.then(fullClaimId => {
if (fullClaimId === NO_CLAIM) {
return res.status(200).render('index');
}
serveAssetToClient(fullClaimId, claimName, res);
// postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'success');
})
.catch(error => {
handleRequestError(originalUrl, ip, error, res);
// postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'fail');
});
} else {
res.status(200).render('index');
}
} }
// parse the claim
let claimName;
try {
({claimName} = lbryUri.parseClaim(params.claim));
} catch (error) {
return res.status(200).json({success: false, message: error.message});
}
// log the request data for debugging
logRequestData(responseType, claimName, null, null);
// get the claim Id and then serve the asset
getClaimId(null, null, claimName, null)
.then(fullClaimId => {
if (fullClaimId === NO_CLAIM) {
return res.status(200).render('index');
}
serveAssetToClient(fullClaimId, claimName, res);
// postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'success');
})
.catch(error => {
handleRequestError(originalUrl, ip, error, res);
// postToStats(responseType, originalUrl, ip, claimName, fullClaimId, 'fail');
});
}); });
}; };