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 ProgressBar from 'components/ProgressBar';
import Request from 'utils/request';
const LOCAL_CHECK = 'LOCAL_CHECK';
const SEARCHING = 'SEARCHING';
const UNAVAILABLE = 'UNAVAILABLE';
const AVAILABLE = 'AVAILABLE';
import { LOCAL_CHECK, SEARCHING, UNAVAILABLE, AVAILABLE } from 'constants/asset_display_states';
import PropTypes from 'prop-types';
class AssetDisplay extends React.Component {
constructor (props) {
super(props);
this.state = {
error : null,
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,
status: LOCAL_CHECK,
};
this.checkIfLocalFileAvailable = this.checkIfLocalFileAvailable.bind(this);
this.triggerGetAssetOnSpeech = this.triggerGetAssetOnSpeech.bind(this);
this.isLocalFileAvailableOnServer = this.isLocalFileAvailableOnServer.bind(this);
this.triggerGetAssetOnServer = this.triggerGetAssetOnServer.bind(this);
}
componentDidMount () {
const that = this;
this.checkIfLocalFileAvailable()
this.isLocalFileAvailableOnServer()
.then(isAvailable => {
if (!isAvailable) {
console.log('file is not yet available');
that.setState({status: SEARCHING});
return that.triggerGetAssetOnSpeech();
return that.triggerGetAssetOnServer();
}
})
.then(() => {
that.setState({status: AVAILABLE});
that.addPlayPauseToVideoToBody();
})
.catch(error => {
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}`);
const url = `/api/file-is-available/${this.props.name}/${this.props.claimId}`;
return new Promise((resolve, reject) => {
@ -58,8 +48,8 @@ class AssetDisplay extends React.Component {
});
});
}
triggerGetAssetOnSpeech () {
console.log(`getting claim for ${this.props.name}#${this.props.claimId}`)
triggerGetAssetOnServer () {
console.log(`getting claim for ${this.props.name}#${this.props.claimId}`);
const url = `/api/claim-get/${this.props.name}/${this.props.claimId}`;
return new Promise((resolve, reject) => {
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 () {
return (
<div id="asset-display-component">
@ -113,27 +80,27 @@ class AssetDisplay extends React.Component {
}
{(this.state.status === AVAILABLE) &&
(() => {
switch (this.state.contentType) {
switch (this.props.contentType) {
case 'image/jpeg':
case 'image/jpg':
case 'image/png':
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':
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':
return (
<video id="video" className="asset" controls poster={this.state.thumbnail}>
<source src={this.state.src}/>
<video id="video" className="asset" controls poster={this.props.thumbnail}>
<source src={this.props.src}/>
<p>Your browser does not support the <code>video</code> element.</p>
</video>
);
default:
return (
<p>unsupported file type</p>
<p>Unsupported file type</p>
);
}
})()
@ -143,11 +110,13 @@ class AssetDisplay extends React.Component {
}
};
// required props
// name
// claimId
// thumbnail
// contentType
// file extension
AssetDisplay.propTypes = {
name : PropTypes.string.isRequired,
claimId : PropTypes.string.isRequired,
src : PropTypes.string.isRequired,
contentType: PropTypes.string.isRequired,
fileExt : PropTypes.string.isRequired,
thumbnail : PropTypes.string.isRequired,
};
export default AssetDisplay;

View file

@ -27,9 +27,10 @@ class ShowDetails extends React.Component {
<AssetDisplay
name={this.props.claimData.name}
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}
fileExt={this.props.claimData.fileExt}
thumbnail={this.props.claimData.thumbnail}
/>
</div>
</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 &&
<div>
<AssetDisplay
claimName={this.props.claimData.name}
name={this.props.claimData.name}
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>
</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 {setPublishInChannel, updateSelectedChannel, updateError} from 'actions/publish';
import View from './view.jsx';
import View from './view';
const mapStateToProps = ({ channel, publish }) => {
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 lbryUri from 'utils/lbryUri';
const CHANNEL = 'CHANNEL';
const ASSET = 'ASSET';
class ShowPage extends React.Component {
constructor (props) {
super(props);
this.state = {
error : null,
identifier : null,
claim : null,
isServeRequest: null,
error: null,
};
this.parseUrlAndUpdateState = this.parseUrlAndUpdateState.bind(this);
this.parseAndUpdateIdentifierAndClaim = this.parseAndUpdateIdentifierAndClaim.bind(this);
@ -40,31 +34,36 @@ class ShowPage extends React.Component {
}
this.parseAndUpdateClaimOnly(claim);
}
parseAndUpdateIdentifierAndClaim (identifier, claim) {
parseAndUpdateIdentifierAndClaim (modifier, claim) {
// handle case of identifier and claim
// this is a request for an asset
// claim will be an asset claim
// 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 {
({ isChannel, channelName, channelClaimId, claimId } = lbryUri.parseIdentifier(identifier));
({ claimName, isServeRequest } = lbryUri.parseClaim(claim));
({ isChannel, channelName, channelClaimId, claimId } = lbryUri.parseIdentifier(modifier));
({ claimName, extension } = lbryUri.parseClaim(claim));
} catch (error) {
return this.setState({error: error.message});
}
// set state
return this.setState({
identifier: {
isChannel,
channelName,
channelClaimId,
claimId,
// update the store
let requestedClaim = {
name : claimName,
modifier: {
id : null,
channel: null,
},
claim: {
claimName,
},
isServeRequest,
});
extension,
};
if (isChannel) {
requestedClaim['modifier']['channel'] = {
name: channelName,
id : channelClaimId,
};
} else {
requestedClaim['modifier']['id'] = claimId;
}
return this.props.onClaimRequest(requestedClaim);
}
parseAndUpdateClaimOnly (claim) {
// handle case of just claim
@ -76,27 +75,27 @@ class ShowPage extends React.Component {
} catch (error) {
return this.setState({error: error.message});
}
// return early if this request is for a channel
if (isChannel) {
return this.setState({
claim: {
isChannel,
channelName,
channelClaimId,
},
});
const requestedChannel = {
name: channelName,
id : channelClaimId,
}
let claimName, isServeRequest;
return this.props.onChannelRequest(requestedChannel);
}
// 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 {
({claimName, isServeRequest} = lbryUri.parseClaim(claim));
({claimName, extension} = lbryUri.parseClaim(claim));
} catch (error) {
return this.setState({error: error.message});
}
this.setState({
claim: {
claimName,
},
isServeRequest,
});
const requestedClaim = {
name : claimName,
modifier: null,
extension,
}
this.props.onClaimRequest(requestedClaim);
}
render () {
console.log('rendering ShowPage');
@ -128,4 +127,8 @@ class ShowPage extends React.Component {
}
};
// props
// channel
// show
export default ShowPage;

View file

@ -1,8 +1,10 @@
import { combineReducers } from 'redux';
import PublishReducer from 'reducers/publish';
import ChannelReducer from 'reducers/channel';
import ShowReducer from 'reducers/show';
export default combineReducers({
channel: ChannelReducer,
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 AboutPage from 'components/AboutPage';
import LoginPage from 'components/LoginPage';
import ShowPage from 'components/ShowPage';
import ShowPage from 'containers/ShowPage';
const Root = ({ store }) => (
<Provider store={store}>

View file

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