updated ShowPage component to use redux store

This commit is contained in:
bill bittner 2018-02-01 14:29:33 -08:00
parent 69ed241d1c
commit ff86b0aa81
13 changed files with 256 additions and 114 deletions

30
react/actions/show.js Normal file
View file

@ -0,0 +1,30 @@
import * as actions from 'constants/show_action_types';
// export action creators
export function updateClaimRequest (claim) {
return {
type: actions.CLAIM_REQUEST_UPDATE,
claim,
};
};
export function updateChannelRequest (channel) {
return {
type: actions.CHANNEL_REQUEST_UPDATE,
channel,
};
};
export function updateChannelData (channelData) {
return {
type: actions.CHANNEL_DATA_UPDATE,
channelData,
};
};
export function updateClaimData (claimData) {
return {
type: actions.CHANNEL_DATA_UPDATE,
};
};

View file

@ -1,41 +1,31 @@
import React from 'react'; import React from 'react';
import ProgressBar from 'components/ProgressBar'; import ProgressBar from 'components/ProgressBar';
import Request from 'utils/request'; import Request from 'utils/request';
import { LOCAL_CHECK, SEARCHING, UNAVAILABLE, AVAILABLE } from 'constants/asset_display_states';
const LOCAL_CHECK = 'LOCAL_CHECK'; import PropTypes from 'prop-types';
const SEARCHING = 'SEARCHING';
const UNAVAILABLE = 'UNAVAILABLE';
const AVAILABLE = 'AVAILABLE';
class AssetDisplay extends React.Component { class AssetDisplay extends React.Component {
constructor (props) { constructor (props) {
super(props); super(props);
this.state = { this.state = {
error : null, error : null,
status : LOCAL_CHECK, status: LOCAL_CHECK,
thumbnail : this.props.thumbnail,
src : `/${this.props.claimId}}/${this.props.name}.${this.props.fileExt}`,
name : this.props.name,
claimId : this.props.claimId,
fileExt : this.props.fileExt,
contentType: this.props.contentType,
}; };
this.checkIfLocalFileAvailable = this.checkIfLocalFileAvailable.bind(this); this.isLocalFileAvailableOnServer = this.isLocalFileAvailableOnServer.bind(this);
this.triggerGetAssetOnSpeech = this.triggerGetAssetOnSpeech.bind(this); this.triggerGetAssetOnServer = this.triggerGetAssetOnServer.bind(this);
} }
componentDidMount () { componentDidMount () {
const that = this; const that = this;
this.checkIfLocalFileAvailable() this.isLocalFileAvailableOnServer()
.then(isAvailable => { .then(isAvailable => {
if (!isAvailable) { if (!isAvailable) {
console.log('file is not yet available'); console.log('file is not yet available');
that.setState({status: SEARCHING}); that.setState({status: SEARCHING});
return that.triggerGetAssetOnSpeech(); return that.triggerGetAssetOnServer();
} }
}) })
.then(() => { .then(() => {
that.setState({status: AVAILABLE}); that.setState({status: AVAILABLE});
that.addPlayPauseToVideoToBody();
}) })
.catch(error => { .catch(error => {
that.setState({ that.setState({
@ -44,7 +34,7 @@ class AssetDisplay extends React.Component {
}); });
}); });
} }
checkIfLocalFileAvailable () { isLocalFileAvailableOnServer () {
console.log(`checking if file is available for ${this.props.name}#${this.props.claimId}`); console.log(`checking if file is available for ${this.props.name}#${this.props.claimId}`);
const url = `/api/file-is-available/${this.props.name}/${this.props.claimId}`; const url = `/api/file-is-available/${this.props.name}/${this.props.claimId}`;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -58,8 +48,8 @@ class AssetDisplay extends React.Component {
}); });
}); });
} }
triggerGetAssetOnSpeech () { triggerGetAssetOnServer () {
console.log(`getting claim for ${this.props.name}#${this.props.claimId}`) console.log(`getting claim for ${this.props.name}#${this.props.claimId}`);
const url = `/api/claim-get/${this.props.name}/${this.props.claimId}`; const url = `/api/claim-get/${this.props.name}/${this.props.claimId}`;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
Request(url) Request(url)
@ -72,29 +62,6 @@ class AssetDisplay extends React.Component {
}); });
}); });
} }
addPlayPauseToVideoToBody () {
const that = this;
const video = document.getElementById('video');
if (video) {
// add event listener for click
video.addEventListener('click', () => {
that.playOrPause(video);
});
// add event listener for space bar
document.body.onkeyup = (event) => {
if (event.keyCode === 32) {
that.playOrPause(video);
}
};
}
}
playOrPause (video) {
if (video.paused === true) {
video.play();
} else {
video.pause();
}
}
render () { render () {
return ( return (
<div id="asset-display-component"> <div id="asset-display-component">
@ -113,27 +80,27 @@ class AssetDisplay extends React.Component {
} }
{(this.state.status === AVAILABLE) && {(this.state.status === AVAILABLE) &&
(() => { (() => {
switch (this.state.contentType) { switch (this.props.contentType) {
case 'image/jpeg': case 'image/jpeg':
case 'image/jpg': case 'image/jpg':
case 'image/png': case 'image/png':
return ( return (
<img className="asset" src={this.state.src} alt={this.state.name}/> <img className="asset" src={this.props.src} alt={this.props.name}/>
); );
case 'image/gif': case 'image/gif':
return ( return (
<img className="asset" src={this.state.src} alt={this.state.name}/> <img className="asset" src={this.props.src} alt={this.props.name}/>
); );
case 'video/mp4': case 'video/mp4':
return ( return (
<video id="video" className="asset" controls poster={this.state.thumbnail}> <video id="video" className="asset" controls poster={this.props.thumbnail}>
<source src={this.state.src}/> <source src={this.props.src}/>
<p>Your browser does not support the <code>video</code> element.</p> <p>Your browser does not support the <code>video</code> element.</p>
</video> </video>
); );
default: default:
return ( return (
<p>unsupported file type</p> <p>Unsupported file type</p>
); );
} }
})() })()
@ -143,11 +110,13 @@ class AssetDisplay extends React.Component {
} }
}; };
// required props AssetDisplay.propTypes = {
// name name : PropTypes.string.isRequired,
// claimId claimId : PropTypes.string.isRequired,
// thumbnail src : PropTypes.string.isRequired,
// contentType contentType: PropTypes.string.isRequired,
// file extension fileExt : PropTypes.string.isRequired,
thumbnail : PropTypes.string.isRequired,
};
export default AssetDisplay; export default AssetDisplay;

View file

@ -27,9 +27,10 @@ class ShowDetails extends React.Component {
<AssetDisplay <AssetDisplay
name={this.props.claimData.name} name={this.props.claimData.name}
claimId={this.props.claimData.claimId} claimId={this.props.claimData.claimId}
thumbnail={this.props.claimData.thumbnail} src={`/${this.props.claimId}/${this.props.name}.${this.props.fileExt}`}
contentType={this.props.claimData.contentType} contentType={this.props.claimData.contentType}
fileExt={this.props.claimData.fileExt} fileExt={this.props.claimData.fileExt}
thumbnail={this.props.claimData.thumbnail}
/> />
</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">

View file

@ -12,8 +12,12 @@ class ShowLite extends React.Component {
{this.props.claimData && {this.props.claimData &&
<div> <div>
<AssetDisplay <AssetDisplay
claimName={this.props.claimData.name} name={this.props.claimData.name}
claimId={this.props.claimData.claimId} claimId={this.props.claimData.claimId}
src={`/${this.props.claimId}/${this.props.name}.${this.props.fileExt}`}
contentType={this.props.claimData.contentType}
fileExt={this.props.claimData.fileExt}
thumbnail={this.props.claimData.thumbnail}
/> />
<Link id="asset-boilerpate" className="link--primary fine-print" to={`/${this.props.claimData.claimId}/${this.props.claimData.name}`}>hosted via Spee.ch</Link> <Link id="asset-boilerpate" className="link--primary fine-print" to={`/${this.props.claimData.claimId}/${this.props.claimData.name}`}>hosted via Spee.ch</Link>
</div> </div>

View file

@ -0,0 +1,4 @@
export const LOCAL_CHECK = 'LOCAL_CHECK';
export const SEARCHING = 'SEARCHING';
export const UNAVAILABLE = 'UNAVAILABLE';
export const AVAILABLE = 'AVAILABLE';

View file

@ -0,0 +1,4 @@
export const CLAIM_REQUEST_UPDATE = 'CLAIM_REQUEST_UPDATE';
export const CHANNEL_REQUEST_UPDATE = 'CHANNEL_REQUEST_UPDATE';
export const CHANNEL_DATA_UPDATE = 'CHANNEL_DATA_UPDATE';
export const CLAIM_DATA_UPDATE = 'CLAIM_DATA_UPDATE';

View file

@ -1,6 +1,6 @@
import {connect} from 'react-redux'; import {connect} from 'react-redux';
import {setPublishInChannel, updateSelectedChannel, updateError} from 'actions/publish'; import {setPublishInChannel, updateSelectedChannel, updateError} from 'actions/publish';
import View from './view.jsx'; import View from './view';
const mapStateToProps = ({ channel, publish }) => { const mapStateToProps = ({ channel, publish }) => {
return { return {

View file

@ -0,0 +1,23 @@
import { updateChannelRequest, updateClaimRequest } from 'actions/show';
import { connect } from 'react-redux';
import View from './view';
const mapStateToProps = ({ show }) => {
return {
channel: show.request.channel,
claim : show.request.claim,
};
};
const mapDispatchToProps = dispatch => {
return {
onChannelRequest: (channel) => {
dispatch(updateChannelRequest(channel));
},
onClaimRequest: (claim) => {
dispatch(updateClaimRequest(claim));
},
};
};
export default connect(mapStateToProps, mapDispatchToProps)(View);

View file

@ -4,17 +4,11 @@ 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';
const CHANNEL = 'CHANNEL';
const ASSET = 'ASSET';
class ShowPage extends React.Component { class ShowPage extends React.Component {
constructor (props) { constructor (props) {
super(props); super(props);
this.state = { this.state = {
error : null, error: null,
identifier : null,
claim : null,
isServeRequest: null,
}; };
this.parseUrlAndUpdateState = this.parseUrlAndUpdateState.bind(this); this.parseUrlAndUpdateState = this.parseUrlAndUpdateState.bind(this);
this.parseAndUpdateIdentifierAndClaim = this.parseAndUpdateIdentifierAndClaim.bind(this); this.parseAndUpdateIdentifierAndClaim = this.parseAndUpdateIdentifierAndClaim.bind(this);
@ -40,31 +34,36 @@ class ShowPage extends React.Component {
} }
this.parseAndUpdateClaimOnly(claim); this.parseAndUpdateClaimOnly(claim);
} }
parseAndUpdateIdentifierAndClaim (identifier, claim) { parseAndUpdateIdentifierAndClaim (modifier, claim) {
// handle case of identifier and claim // handle case of identifier and claim
// this is a request for an asset // this is a request for an asset
// claim will be an asset claim // claim will be an asset claim
// the identifier could be a channel or a claim id // the identifier could be a channel or a claim id
let isChannel, channelName, channelClaimId, claimId, claimName, isServeRequest; let isChannel, channelName, channelClaimId, claimId, claimName, extension;
try { try {
({ isChannel, channelName, channelClaimId, claimId } = lbryUri.parseIdentifier(identifier)); ({ isChannel, channelName, channelClaimId, claimId } = lbryUri.parseIdentifier(modifier));
({ claimName, isServeRequest } = lbryUri.parseClaim(claim)); ({ claimName, extension } = lbryUri.parseClaim(claim));
} catch (error) { } catch (error) {
return this.setState({error: error.message}); return this.setState({error: error.message});
} }
// set state // update the store
return this.setState({ let requestedClaim = {
identifier: { name : claimName,
isChannel, modifier: {
channelName, id : null,
channelClaimId, channel: null,
claimId,
}, },
claim: { extension,
claimName, };
}, if (isChannel) {
isServeRequest, requestedClaim['modifier']['channel'] = {
}); name: channelName,
id : channelClaimId,
};
} else {
requestedClaim['modifier']['id'] = claimId;
}
return this.props.onClaimRequest(requestedClaim);
} }
parseAndUpdateClaimOnly (claim) { parseAndUpdateClaimOnly (claim) {
// handle case of just claim // handle case of just claim
@ -76,27 +75,27 @@ class ShowPage extends React.Component {
} catch (error) { } catch (error) {
return this.setState({error: error.message}); return this.setState({error: error.message});
} }
// return early if this request is for a channel
if (isChannel) { if (isChannel) {
return this.setState({ const requestedChannel = {
claim: { name: channelName,
isChannel, id : channelClaimId,
channelName, }
channelClaimId, return this.props.onChannelRequest(requestedChannel);
},
});
} }
let claimName, isServeRequest; // if not for a channel, parse the claim request
let claimName, extension; // if I am destructuring below, do I still need to declare these here?
try { try {
({claimName, isServeRequest} = lbryUri.parseClaim(claim)); ({claimName, extension} = lbryUri.parseClaim(claim));
} catch (error) { } catch (error) {
return this.setState({error: error.message}); return this.setState({error: error.message});
} }
this.setState({ const requestedClaim = {
claim: { name : claimName,
claimName, modifier: null,
}, extension,
isServeRequest, }
}); this.props.onClaimRequest(requestedClaim);
} }
render () { render () {
console.log('rendering ShowPage'); console.log('rendering ShowPage');
@ -128,4 +127,8 @@ class ShowPage extends React.Component {
} }
}; };
// props
// channel
// show
export default ShowPage; export default ShowPage;

View file

@ -1,8 +1,10 @@
import { combineReducers } from 'redux'; import { combineReducers } from 'redux';
import PublishReducer from 'reducers/publish'; import PublishReducer from 'reducers/publish';
import ChannelReducer from 'reducers/channel'; import ChannelReducer from 'reducers/channel';
import ShowReducer from 'reducers/show';
export default combineReducers({ export default combineReducers({
channel: ChannelReducer, channel: ChannelReducer,
publish: PublishReducer, publish: PublishReducer,
show : ShowReducer,
}); });

104
react/reducers/show.js Normal file
View file

@ -0,0 +1,104 @@
import * as actions from 'constants/show_action_types';
const initialState = {
request: {
channel: {
name: null,
id : null,
},
claim: {
name : null,
modifier: {
id : null,
channel: {
name: null,
id : null,
},
},
extension: null,
},
},
channelData: {
channelName : null,
claims : null,
currentPage : null,
previousPage: null,
totalPages : null,
totalResults: null,
},
claimData: {
FileId : null,
address : null,
amount : null,
author : null,
certificateId : null,
channelName : null,
claimId : null,
claimSequence : null,
claimType : null,
contentType : null,
createdAt : null,
decodedClaim : null,
depth : null,
description : null,
effectiveAmount: null,
fileExt : null,
hasSignature : null,
height : null,
hex : null,
host : null,
id : null,
language : null,
license : null,
licenseUrl : null,
metadataVersion: null,
name : null,
nout : null,
nsfw : null,
outpoint : null,
preview : null,
source : null,
sourceType : null,
sourceVersion : null,
streamVersion : null,
thumbnail : null,
title : null,
txid : null,
updatedAt : null,
validAtHeight : null,
valueVersion : null,
},
};
/*
Reducers describe how the application's state changes in response to actions
*/
export default function (state = initialState, action) {
switch (action.type) {
case actions.CLAIM_REQUEST_UPDATE:
return Object.assign({}, state, {
request: {
channel: null,
claim : action.claim,
},
});
case actions.CHANNEL_REQUEST_UPDATE:
return Object.assign({}, state, {
request: {
channel: action.channel,
claim : null,
},
});
case actions.CHANNEL_DATA_UPDATE:
return Object.assign({}, state, {
channelData: action.channelData,
});
case actions.CLAIM_DATA_UPDATE:
return Object.assign({}, state, {
claimData: action.claimData,
});
default:
return state;
}
}

View file

@ -6,7 +6,7 @@ import { BrowserRouter, Route, Switch } from 'react-router-dom';
import PublishPage from 'components/PublishPage'; import PublishPage from 'components/PublishPage';
import AboutPage from 'components/AboutPage'; import AboutPage from 'components/AboutPage';
import LoginPage from 'components/LoginPage'; import LoginPage from 'components/LoginPage';
import ShowPage from 'components/ShowPage'; import ShowPage from 'containers/ShowPage';
const Root = ({ store }) => ( const Root = ({ store }) => (
<Provider store={store}> <Provider store={store}>

View file

@ -49,20 +49,20 @@ module.exports = {
return { return {
isChannel, isChannel,
channelName, channelName,
channelClaimId, channelClaimId: channelClaimId || null,
claimId, claimId : claimId || null,
}; };
}, },
parseClaim: 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 extension)
'([:$#.]?)([^/]*)' // modifier separator, modifier (stops at the first path separator or end) '([:$#.]?)([^/]*)' // extension separator, extension (stops at the first path separator or end)
); );
const [proto, claimName, modifierSeperator, modifier] = componentsRegex const [proto, claimName, extensionSeperator, extension] = componentsRegex
.exec(name) .exec(name)
.map(match => match || null); .map(match => match || null);
console.log(`${proto}, ${claimName}, ${modifierSeperator}, ${modifier}`); console.log(`${proto}, ${claimName}, ${extensionSeperator}, ${extension}`);
// Validate and process name // Validate and process name
if (!claimName) { if (!claimName) {
@ -72,20 +72,18 @@ module.exports = {
if (nameBadChars) { if (nameBadChars) {
throw new Error(`Check your URL. 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 extension
let isServeRequest = false; if (extensionSeperator) {
if (modifierSeperator) { if (!extension) {
if (!modifier) { throw new Error(`Check your URL. No file extension provided after separator "${extensionSeperator}".`);
throw new Error(`Check your URL. No file extension provided after separator "${modifierSeperator}".`);
} }
if (modifierSeperator !== '.') { if (extensionSeperator !== '.') {
throw new Error(`Check your URL. The "${modifierSeperator}" modifier is not supported in the claim name.`); throw new Error(`Check your URL. The "${extensionSeperator}" separator is not supported in the claim name.`);
} }
isServeRequest = true;
} }
return { return {
claimName, claimName,
isServeRequest, extension: extension || null,
}; };
}, },
}; };