Redux #115

Merged
6ea86b96 merged 57 commits from redux into redux 2017-05-05 22:55:12 +02:00
23 changed files with 385 additions and 118 deletions
Showing only changes of commit f08ce3d300 - Show all commits

View file

@ -0,0 +1,21 @@
import * as types from 'constants/action_types'
import lbry from 'lbry'
export function doFetchUriAvailability(uri) {
return function(dispatch, getState) {
dispatch({
type: types.FETCH_AVAILABILITY_STARTED,
data: { uri }
})
lbry.get_availability({ uri }, (availability) => {
dispatch({
type: types.FETCH_AVAILABILITY_COMPLETED',
data: {
availability,
uri,
}
})
}
}
}

View file

@ -1,6 +1,7 @@
import * as types from 'constants/action_types'
import lbry from 'lbry'
import lbryio from 'lbryio'
import lbryuri from 'lbryuri'
import {
selectCurrentUri,
} from 'selectors/app'
@ -113,7 +114,9 @@ export function doFetchFeaturedContent() {
})
Object.keys(Uris).forEach((category) => {
Uris[category].forEach((uri) => dispatch(doResolveUri(uri)))
Uris[category].forEach((uri) => {
dispatch(doResolveUri(lbryuri.normalize(uri)))
})
})
}

View file

@ -32,7 +32,7 @@ export function doSearchContent(query) {
contentName: result.name,
claimId: result.channel_id || result.claim_id,
})
dispatch(doResolveUri(uri.split('://')[1]))
dispatch(doResolveUri(uri))
})
dispatch({

View file

@ -0,0 +1,21 @@
import React from 'react'
import {
connect,
} from 'react-redux'
import {
selectObscureNsfw,
selectHidePrice,
selectHasSignature,
} from 'selectors/app'
import FileActions from './view'
const select = (state) => ({
obscureNsfw: selectObscureNsfw(state),
hidePrice: selectHidePrice(state),
hasSignature: selectHasSignature(state),
})
const perform = {
}
export default connect(select, perform)(FileActions)

View file

@ -1,16 +1,16 @@
import React from 'react';
import lbry from '../lbry.js';
import lbryuri from '../lbryuri.js';
import lbry from 'lbry';
import lbryuri from 'lbryuri';
import {Icon, FilePrice} from 'component/common';
import {Modal} from 'component/modal';
import {FormField} from 'component/form';
import Link from 'component/link';
import {Icon, FilePrice} from '../component/common.js';
import Modal from './modal.js';
import FormField from './form.js';
import {ToolTip} from '../component/tooltip.js';
import {DropDownMenu, DropDownMenuItem} from './menu.js';
import {ToolTip} from 'component/tooltip';
import {DropDownMenu, DropDownMenuItem} from 'component/menu';
const {shell} = require('electron');
let FileActionsRow = React.createClass({
const FileActionsRow = React.createClass({
_isMounted: false,
_fileInfoSubscribeId: null,
@ -192,7 +192,7 @@ let FileActionsRow = React.createClass({
}
});
export let FileActions = React.createClass({
const FileActions = React.createClass({
_isMounted: false,
_fileInfoSubscribeId: null,
@ -268,3 +268,5 @@ export let FileActions = React.createClass({
</section>);
}
});
export default FileActions

View file

@ -5,13 +5,40 @@ import {
import {
doNavigate,
} from 'actions/app'
import {
selectHidePrice,
selectObscureNsfw,
} from 'selectors/app'
import {
makeSelectClaimForUri,
makeSelectSourceForUri,
makeSelectMetadataForUri,
} from 'selectors/claims'
import {
makeSelectFileInfoForUri,
} from 'selectors/file_info'
import FileCardStream from './view'
const select = (state) => ({
})
const makeSelect = () => {
const selectClaimForUri = makeSelectClaimForUri()
const selectFileInfoForUri = makeSelectFileInfoForUri()
const selectMetadataForUri = makeSelectMetadataForUri()
const selectSourceForUri = makeSelectSourceForUri()
const select = (state, props) => ({
claim: selectClaimForUri(state, props),
fileInfo: selectFileInfoForUri(state, props),
hidePrice: selectHidePrice(state),
obscureNsfw: selectObscureNsfw(state),
hasSignature: false,
metadata: selectMetadataForUri(state, props),
source: selectSourceForUri(state, props),
})
return select
}
const perform = (dispatch) => ({
navigate: (path) => dispatch(doNavigate(path)),
})
export default connect(select, perform)(FileCardStream)
export default connect(makeSelect, perform)(FileCardStream)

View file

@ -2,70 +2,63 @@ import React from 'react';
import lbry from 'lbry.js';
import lbryuri from 'lbryuri.js';
import Link from 'component/link';
import {FileActions} from 'component/file-actions.js';
import {Thumbnail, TruncatedText, FilePrice} from 'component/common.js';
import UriIndicator from 'component/channel-indicator.js';
import {Thumbnail, TruncatedText, FilePrice} from 'component/common';
import UriIndicator from 'component/channel-indicator';
const FileCardStream = React.createClass({
_fileInfoSubscribeId: null,
_isMounted: null,
_metadata: null,
propTypes: {
uri: React.PropTypes.string,
claimInfo: React.PropTypes.object,
outpoint: React.PropTypes.string,
hideOnRemove: React.PropTypes.bool,
hidePrice: React.PropTypes.bool,
obscureNsfw: React.PropTypes.bool
},
getInitialState: function() {
return {
class FileCardStream extends React.Component {
constructor(props) {
super(props)
this._fileInfoSubscribeId = null
this._isMounted = null
this._metadata = null
this.state = {
showNsfwHelp: false,
isHidden: false,
}
},
getDefaultProps: function() {
return {
obscureNsfw: !lbry.getClientSetting('showNsfw'),
hidePrice: false,
hasSignature: false,
}
},
componentDidMount: function() {
}
componentDidMount() {
this._isMounted = true;
if (this.props.hideOnRemove) {
this._fileInfoSubscribeId = lbry.fileInfoSubscribe(this.props.outpoint, this.onFileInfoUpdate);
}
},
componentWillUnmount: function() {
}
componentWillUnmount() {
if (this._fileInfoSubscribeId) {
lbry.fileInfoUnsubscribe(this.props.outpoint, this._fileInfoSubscribeId);
}
},
onFileInfoUpdate: function(fileInfo) {
}
onFileInfoUpdate(fileInfo) {
if (!fileInfo && this._isMounted && this.props.hideOnRemove) {
this.setState({
isHidden: true
});
}
},
handleMouseOver: function() {
}
handleMouseOver() {
this.setState({
hovered: true,
});
},
handleMouseOut: function() {
}
handleMouseOut() {
this.setState({
hovered: false,
});
},
render: function() {
}
render() {
if (this.state.isHidden) {
return null;
}
if (!this.props.metadata) {
return null
}
const uri = lbryuri.normalize(this.props.uri);
const metadata = this.props.metadata;
const isConfirmed = !!metadata;
@ -73,13 +66,13 @@ const FileCardStream = React.createClass({
const obscureNsfw = this.props.obscureNsfw && isConfirmed && metadata.nsfw;
const primaryUrl = 'show=' + uri;
return (
<section className={ 'card card--small card--link ' + (obscureNsfw ? 'card--obscured ' : '') } onMouseEnter={this.handleMouseOver} onMouseLeave={this.handleMouseOut}>
<section className={ 'card card--small card--link ' + (obscureNsfw ? 'card--obscured ' : '') } onMouseEnter={this.handleMouseOver.bind(this)} onMouseLeave={this.handleMouseOut.bind(this)}>
<div className="card__inner">
<a href="#" onClick={() => this.props.navigate(primaryUrl)} className="card__link">
<div className="card__title-identity">
<h5 title={title}><TruncatedText lines={1}>{title}</TruncatedText></h5>
<div className="card__subtitle">
{ !this.props.hidePrice ? <span style={{float: "right"}}><FilePrice uri={this.props.uri} metadata={metadata} /></span> : null}
{ !this.props.hidePrice ? <span style={{float: "right"}}><FilePrice uri={this.props.uri} /></span> : null}
<UriIndicator uri={uri} metadata={metadata} contentType={this.props.contentType}
hasSignature={this.props.hasSignature} signatureIsValid={this.props.signatureIsValid} />
</div>
@ -105,6 +98,6 @@ const FileCardStream = React.createClass({
</section>
);
}
});
}
export default FileCardStream

View file

@ -79,9 +79,7 @@ const FileList = React.createClass({
const uri = lbryuri.build({contentName: name, channelName: channel_name});
seenUris[name] = true;
content.push(<FileTileStream key={outpoint} outpoint={outpoint} uri={uri} hideOnRemove={true}
hidePrice={this.props.hidePrices} metadata={streamMetadata} contentType={mime_type}
hasSignature={has_signature} signatureIsValid={signature_is_valid} />);
content.push(<FileTileStream key={outpoint} uri={uri} hideOnRemove={true} />)
}
return (

View file

@ -3,15 +3,25 @@ import {
connect
} from 'react-redux'
import {
selectClaimsByUri,
makeSelectClaimForUri,
} from 'selectors/claims'
import {
makeSelectFileInfoForUri,
} from 'selectors/file_info'
import FileTile from './view'
const select = (state) => ({
claims: (uri) => selectClaimsByUri(state)[uri],
})
const makeSelect = () => {
const selectClaimForUri = makeSelectClaimForUri()
const selectFileInfoForUri = makeSelectFileInfoForUri()
const select = (state, props) => ({
claim: selectClaimForUri(state, props),
fileInfo: selectFileInfoForUri(state, props),
})
return select
}
const perform = (dispatch) => ({
})
export default connect(select, perform)(FileTile)
export default connect(makeSelect, perform)(FileTile)

View file

@ -10,11 +10,11 @@ class FileTile extends React.Component {
render() {
const {
displayStyle,
uri
uri,
claim,
} = this.props
const claimInfo = this.props.claims(uri)
if(!claimInfo) {
if(!claim) {
if (displayStyle == 'card') {
return <FileCardStream uri={uri} />
}
@ -22,9 +22,9 @@ class FileTile extends React.Component {
}
return displayStyle == 'card' ?
<FileCardStream uri={uri} />
<FileCardStream uri={uri} />
:
<FileTileStream uri={uri} key={uri} />
<FileTileStream uri={uri} key={uri} />
}
}

View file

@ -5,13 +5,46 @@ import {
import {
doNavigate,
} from 'actions/app'
import {
makeSelectClaimForUri,
makeSelectSourceForUri,
makeSelectMetadataForUri,
} from 'selectors/claims'
import {
makeSelectFileInfoForUri,
} from 'selectors/file_info'
import {
makeSelectFetchingAvailabilityForUri,
makeSelectAvailabilityForUri,
} from 'selectors/availability'
import {
selectObscureNsfw,
} from 'selectors/app'
import FileTileStream from './view'
const select = (state) => ({
})
const makeSelect = () => {
const selectClaimForUri = makeSelectClaimForUri()
const selectFileInfoForUri = makeSelectFileInfoForUri()
const selectFetchingAvailabilityForUri = makeSelectFetchingAvailabilityForUri()
const selectAvailabilityForUri = makeSelectAvailabilityForUri()
const selectMetadataForUri = makeSelectMetadataForUri()
const selectSourceForUri = makeSelectSourceForUri()
const select = (state, props) => ({
claim: selectClaimForUri(state, props),
fileInfo: selectFileInfoForUri(state, props),
fetchingAvailability: selectFetchingAvailabilityForUri(state, props),
selectAvailabilityForUri: selectAvailabilityForUri(state, props),
obscureNswf: selectObscureNsfw(state),
metadata: selectMetadataForUri(state, props),
source: selectSourceForUri(state, props),
})
return select
}
const perform = (dispatch) => ({
navigate: (path) => dispatch(doNavigate(path))
})
export default connect(select, perform)(FileTileStream)
export default connect(makeSelect, perform)(FileTileStream)

View file

@ -2,86 +2,76 @@ import React from 'react';
import lbry from 'lbry.js';
import lbryuri from 'lbryuri.js';
import Link from 'component/link';
import {
FileActions
} from 'component/file-actions.js';
import FileActions from 'component/fileActions';
import {Thumbnail, TruncatedText, FilePrice} from 'component/common.js';
import UriIndicator from 'component/channel-indicator.js';
/*should be merged into FileTile once FileTile is refactored to take a single id*/
const FileTileStream = React.createClass({
_fileInfoSubscribeId: null,
_isMounted: null,
propTypes: {
uri: React.PropTypes.string,
metadata: React.PropTypes.object,
contentType: React.PropTypes.string.isRequired,
outpoint: React.PropTypes.string,
hasSignature: React.PropTypes.bool,
signatureIsValid: React.PropTypes.bool,
hideOnRemove: React.PropTypes.bool,
hidePrice: React.PropTypes.bool,
obscureNsfw: React.PropTypes.bool
},
getInitialState: function() {
return {
class FileTileStream extends React.Component {
constructor(props) {
super(props)
this._fileInfoSubscribeId = null
this._isMounted = null
this.state = {
showNsfwHelp: false,
isHidden: false,
}
},
getDefaultProps: function() {
return {
obscureNsfw: !lbry.getClientSetting('showNsfw'),
hidePrice: false,
hasSignature: false,
}
},
componentDidMount: function() {
}
componentDidMount() {
this._isMounted = true;
if (this.props.hideOnRemove) {
this._fileInfoSubscribeId = lbry.fileInfoSubscribe(this.props.outpoint, this.onFileInfoUpdate);
}
},
componentWillUnmount: function() {
}
componentWillUnmount() {
if (this._fileInfoSubscribeId) {
lbry.fileInfoUnsubscribe(this.props.outpoint, this._fileInfoSubscribeId);
}
},
onFileInfoUpdate: function(fileInfo) {
}
onFileInfoUpdate(fileInfo) {
if (!fileInfo && this._isMounted && this.props.hideOnRemove) {
this.setState({
isHidden: true
});
}
},
handleMouseOver: function() {
}
handleMouseOver() {
if (this.props.obscureNsfw && this.props.metadata && this.props.metadata.nsfw) {
this.setState({
showNsfwHelp: true,
});
}
},
handleMouseOut: function() {
}
handleMouseOut() {
if (this.state.showNsfwHelp) {
this.setState({
showNsfwHelp: false,
});
}
},
render: function() {
}
render() {
if (this.state.isHidden) {
return null;
}
const {
metadata,
navigate,
} = this.props
const uri = lbryuri.normalize(this.props.uri);
const metadata = this.props.metadata;
const isConfirmed = !!metadata;
const title = isConfirmed ? metadata.title : uri;
const obscureNsfw = this.props.obscureNsfw && isConfirmed && metadata.nsfw;
const { navigate } = this.props
return (
<section className={ 'file-tile card ' + (obscureNsfw ? 'card--obscured ' : '') } onMouseEnter={this.handleMouseOver} onMouseLeave={this.handleMouseOut}>
<section className={ 'file-tile card ' + (obscureNsfw ? 'card--obscured ' : '') } onMouseEnter={this.handleMouseOver.bind(this)} onMouseLeave={this.handleMouseOut.bind(this)}>
<div className={"row-fluid card__inner file-tile__row"}>
<div className="span3 file-tile__thumbnail-container">
<a href="#" onClick={() => navigate(`show=${uri}`)}><Thumbnail className="file-tile__thumbnail" {... metadata && metadata.thumbnail ? {src: metadata.thumbnail} : {}} alt={'Photo for ' + this.props.uri} /></a>
@ -101,7 +91,6 @@ const FileTileStream = React.createClass({
</h3>
</div>
<div className="card__actions">
<FileActions uri={this.props.uri} outpoint={this.props.outpoint} metadata={metadata} contentType={this.props.contentType} />
</div>
<div className="card__content">
<p className="file-tile__description">
@ -125,6 +114,6 @@ const FileTileStream = React.createClass({
</section>
);
}
});
}
export default FileTileStream

View file

@ -54,6 +54,8 @@ export const DOWNLOADING_STARTED = 'DOWNLOADING_STARTED'
export const DOWNLOADING_PROGRESSED = 'DOWNLOADING_PROGRESSED'
export const DOWNLOADING_COMPLETED = 'DOWNLOADING_COMPLETED'
export const PLAY_VIDEO_STARTED = 'PLAY_VIDEO_STARTED'
export const FETCH_AVAILABILITY_STARTED = 'FETCH_AVAILABILITY_STARTED'
export const FETCH_AVAILABILITY_COMPLETED = 'FETCH_AVAILABILITY_COMPLETED'
// Search
export const SEARCH_STARTED = 'SEARCH_STARTED'

View file

@ -9,7 +9,7 @@ import {
FilePrice,
BusyMessage
} from 'component/common.js';
import {FileActions} from 'component/file-actions.js';
import FileActions from 'component/fileActions';
import Link from 'component/link';
import UriIndicator from 'component/channel-indicator.js';

View file

@ -1,4 +1,5 @@
import * as types from 'constants/action_types'
import lbry from 'lbry'
const reducers = {}
const defaultState = {
@ -9,6 +10,9 @@ const defaultState = {
upgradeSkipped: sessionStorage.getItem('upgradeSkipped'),
daemonReady: false,
platform: window.navigator.platform,
obscureNsfw: !lbry.getClientSetting('showNsfw'),
hidePrice: false,
hasSignature: false,
}
reducers[types.NAVIGATE] = function(state, action) {

View file

@ -0,0 +1,45 @@
import * as types from 'constants/action_types'
const reducers = {}
const defaultState = {
}
reducers[types.FETCH_AVAILABILITY_STARTED] = function(state, action) {
const {
uri,
} = action.data
const newFetching = Object.assign({}, state.fetching)
const newByUri = Object.assign({}, newFetching.byUri)
newByUri[uri] = true
newFetching.byUri = newByUri
return Object.assign({}, state, {
fetching: newFetching,
})
}
reducers[types.FETCH_AVAILABILITY_COMPLETED] = function(state, action) {
const {
uri,
availability,
} = action.data
const newFetching = Object.assign({}, state.fetching)
const newFetchingByUri = Object.assign({}, newFetching.byUri)
const newAvailabilityByUri = Object.assign({}, state.byUri)
delete newFetchingByUri[uri]
newFetching.byUri = newFetchingByUri
newAvailabilityByUri[uri] = availability
return Object.assign({}, state, {
fetching: newFetching,
byUri: newAvailabilityByUri
})
}
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);
return state;
}

View file

@ -1,4 +1,5 @@
import * as types from 'constants/action_types'
import lbryuri from 'lbryuri'
const reducers = {}
const defaultState = {

View file

@ -1,4 +1,5 @@
import * as types from 'constants/action_types'
import lbryuri from 'lbryuri'
const reducers = {}
const defaultState = {
@ -18,10 +19,11 @@ reducers[types.SEARCH_STARTED] = function(state, action) {
reducers[types.SEARCH_COMPLETED] = function(state, action) {
const {
query,
results,
} = action.data
const oldResults = Object.assign({}, state.results)
const newByQuery = Object.assign({}, oldResults.byQuery)
newByQuery[query] = action.data.results
newByQuery[query] = results
const newResults = Object.assign({}, oldResults, {
byQuery: newByQuery
})

View file

@ -19,7 +19,13 @@ export const selectCurrentPage = createSelector(
export const selectCurrentUri = createSelector(
selectCurrentPath,
(path) => path.split('://')[1]
(path) => {
if (path.match(/=/)) {
return path.split('=')[1]
} else {
return undefined
}
}
)
export const selectPlatform = createSelector(
@ -145,3 +151,18 @@ export const selectDaemonReady = createSelector(
_selectState,
(state) => state.daemonReady
)
export const selectObscureNsfw = createSelector(
_selectState,
(state) => !!state.obscureNsfw
)
export const selectHidePrice = createSelector(
_selectState,
(state) => !!state.hidePrice
)
export const selectHasSignature = createSelector(
_selectState,
(state) => !!state.hasSignature
)

View file

@ -0,0 +1,42 @@
import {
createSelector,
} from 'reselect'
const _selectState = state => state.availability
export const selectAvailabilityByUri = createSelector(
_selectState,
(state) => state.byUri || {}
)
export const selectFetchingAvailability = createSelector(
_selectState,
(state) => state.fetching || {}
)
export const selectFetchingAvailabilityByUri = createSelector(
selectFetchingAvailability,
(fetching) => fetching.byUri || {}
)
const selectAvailabilityForUri = (state, props) => {
return selectAvailabilityByUri(state)[props.uri]
}
export const makeSelectAvailabilityForUri = () => {
return createSelector(
selectAvailabilityForUri,
(availability) => availability
)
}
const selectFetchingAvailabilityForUri = (state, props) => {
return selectFetchingAvailabilityByUri(state)[props.uri]
}
export const makeSelectFetchingAvailabilityForUri = () => {
return createSelector(
selectFetchingAvailabilityForUri,
(fetching) => fetching
)
}

View file

@ -1,6 +1,7 @@
import {
createSelector,
} from 'reselect'
import lbryuri from 'lbryuri'
import {
selectCurrentUri,
} from 'selectors/app'
@ -22,3 +23,43 @@ export const selectCurrentUriClaimOutpoint = createSelector(
selectCurrentUriClaim,
(claim) => `${claim.txid}:${claim.nout}`
)
const selectClaimForUri = (state, props) => {
const uri = lbryuri.normalize(props.uri)
return selectClaimsByUri(state)[uri]
}
export const makeSelectClaimForUri = () => {
return createSelector(
selectClaimForUri,
(claim) => claim
)
}
const selectMetadataForUri = (state, props) => {
const claim = selectClaimForUri(state, props)
const metadata = claim && claim.value && claim.value.stream && claim.value.stream.metadata
return metadata ? metadata : undefined
}
export const makeSelectMetadataForUri = () => {
return createSelector(
selectMetadataForUri,
(metadata) => metadata
)
}
const selectSourceForUri = (state, props) => {
const claim = selectClaimForUri(state, props)
const source = claim && claim.value && claim.value.stream && claim.value.stream.source
return source ? source : undefined
}
export const makeSelectSourceForUri = () => {
return createSelector(
selectSourceForUri,
(source) => source
)
}

View file

@ -72,3 +72,13 @@ export const shouldFetchCurrentUriFileInfo = createSelector(
}
)
const selectFileInfoForUri = (state, props) => {
return selectAllFileInfoByUri(state)[props.uri]
}
export const makeSelectFileInfoForUri = () => {
return createSelector(
selectFileInfoForUri,
(fileInfo) => fileInfo
)
}

View file

@ -6,6 +6,7 @@ import {
createLogger
} from 'redux-logger'
import appReducer from 'reducers/app';
import availabilityReducer from 'reducers/availability'
import certificatesReducer from 'reducers/certificates'
import claimsReducer from 'reducers/claims'
import contentReducer from 'reducers/content';
@ -47,6 +48,7 @@ function enableBatching(reducer) {
const reducers = redux.combineReducers({
app: appReducer,
availability: availabilityReducer,
certificates: certificatesReducer,
claims: claimsReducer,
fileInfo: fileInfoReducer,