Redux #115

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

View file

@ -1,6 +1,12 @@
import * as types from 'constants/action_types'
import lbry from 'lbry'
import lbryio from 'lbryio';
import {
selectCurrentUri,
} from 'selectors/app'
import {
selectCurrentResolvedUriClaimOutpoint,
} from 'selectors/content'
export function doResolveUri(dispatch, uri) {
dispatch({
@ -25,6 +31,34 @@ export function doResolveUri(dispatch, uri) {
})
}
export function doFetchCurrentUriFileInfo() {
return function(dispatch, getState) {
const state = getState()
const uri = selectCurrentUri(state)
const outpoint = selectCurrentResolvedUriClaimOutpoint(state)
console.log(outpoint)
dispatch({
type: types.FETCH_FILE_INFO_STARTED,
data: {
uri,
outpoint,
}
})
lbry.file_list({ outpoint }).then(fileInfo => {
dispatch({
type: types.FETCH_FILE_INFO_COMPLETED,
data: {
uri,
fileInfo,
}
})
})
}
}
export function doFetchDownloadedContent() {
return function(dispatch, getState) {
const state = getState()
@ -100,3 +134,27 @@ export function doFetchFeaturedContent() {
.then(success, failure)
}
}
export function doFetchCurrentUriCostInfo() {
return function(dispatch, getState) {
const state = getState()
const uri = selectCurrentUri(state)
dispatch({
type: types.FETCH_COST_INFO_STARTED,
data: {
uri,
}
})
lbry.getCostInfo(uri).then(costInfo => {
dispatch({
type: types.FETCH_COST_INFO_COMPLETED,
data: {
uri,
costInfo,
}
})
})
}
}

36
ui/js/actions/rewards.js Normal file
View file

@ -0,0 +1,36 @@
import * as types from 'constants/action_types'
import lbry from 'lbry'
import lbryio from 'lbryio';
import rewards from 'rewards'
export function doFetchRewards() {
return function(dispatch, getState) {
const state = getState()
dispatch({
type: types.FETCH_REWARDS_STARTED,
})
lbryio.call('reward', 'list', {}).then(function(userRewards) {
dispatch({
type: types.FETCH_REWARDS_COMPLETED,
data: { userRewards }
})
});
}
}
export function doClaimReward(rewardType) {
return function(dispatch, getState) {
try {
rewards.claimReward(rewards[rewardType])
dispatch({
type: types.REWARD_CLAIMED,
data: {
reward: rewards[rewardType]
}
})
} catch(err) {
}
}
}

View file

@ -2,6 +2,16 @@ import React from 'react'
import {
connect
} from 'react-redux'
import {
doNavigate,
} from 'actions/app'
import FileCardStream from './view'
export default connect()(FileCardStream)
const select = (state) => ({
})
const perform = (dispatch) => ({
navigate: (path) => dispatch(doNavigate(path)),
})
export default connect(select, perform)(FileCardStream)

View file

@ -71,11 +71,11 @@ const FileCardStream = React.createClass({
const isConfirmed = !!metadata;
const title = isConfirmed ? metadata.title : uri;
const obscureNsfw = this.props.obscureNsfw && isConfirmed && metadata.nsfw;
const primaryUrl = '?show=' + uri;
const primaryUrl = 'show=' + uri;
return (
<section className={ 'card card--small card--link ' + (obscureNsfw ? 'card--obscured ' : '') } onMouseEnter={this.handleMouseOver} onMouseLeave={this.handleMouseOut}>
<div className="card__inner">
<a href={primaryUrl} className="card__link">
<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">

View file

@ -4,7 +4,7 @@ import HelpPage from 'page/help';
import ReportPage from 'page/report.js';
import StartPage from 'page/start.js';
import WalletPage from 'page/wallet';
import DetailPage from 'page/show.js';
import ShowPage from 'page/showPage';
import PublishPage from 'page/publish.js';
import DiscoverPage from 'page/discover';
import SplashScreen from 'component/splash.js';
@ -35,7 +35,7 @@ const Router = (props) => {
'wallet': <WalletPage {...props} />,
'send': <WalletPage {...props} />,
'receive': <WalletPage {...props} />,
'show': <DetailPage {...props} />,
'show': <ShowPage {...props} />,
'publish': <PublishPage {...props} />,
'developer': <DeveloperPage {...props} />,
'discover': <DiscoverPage {...props} />,

View file

@ -43,3 +43,7 @@ export const FETCH_DOWNLOADED_CONTENT_STARTED = 'FETCH_DOWNLOADED_CONTENT_STARTE
export const FETCH_DOWNLOADED_CONTENT_COMPLETED = 'FETCH_DOWNLOADED_CONTENT_COMPLETED'
export const FETCH_PUBLISHED_CONTENT_STARTED = 'FETCH_PUBLISHED_CONTENT_STARTED'
export const FETCH_PUBLISHED_CONTENT_COMPLETED = 'FETCH_PUBLISHED_CONTENT_COMPLETED'
export const FETCH_FILE_INFO_STARTED = 'FETCH_FILE_INFO_STARTED'
export const FETCH_FILE_INFO_COMPLETED = 'FETCH_FILE_INFO_COMPLETED'
export const FETCH_COST_INFO_STARTED = 'FETCH_COST_INFO_STARTED'
export const FETCH_COST_INFO_COMPLETED = 'FETCH_COST_INFO_COMPLETED'

View file

@ -14,7 +14,7 @@ const FeaturedCategory = (props) => {
resolvedUris,
names,
} = props
return <div className="card-row card-row--small">
<h3 className="card-row__header">{category}
{category && category.match(/^community/i) && <ToolTip label="What's this?" body={communityCategoryToolTipText} className="tooltip--header" />}

View file

@ -0,0 +1,27 @@
import React from 'react'
import {
connect
} from 'react-redux'
import {
selectCurrentUri,
} from 'selectors/app'
import {
selectCurrentResolvedUriClaim,
selectCurrentUriIsDownloaded,
selectCurrentUriFileInfo,
selectCurrentUriCostInfo,
} from 'selectors/content'
import ShowPage from './view'
const select = (state) => ({
claim: selectCurrentResolvedUriClaim(state),
uri: selectCurrentUri(state),
isDownloaded: selectCurrentUriIsDownloaded(state),
fileInfo: selectCurrentUriFileInfo(state),
costInfo: selectCurrentUriCostInfo(state),
})
const perform = (dispatch) => ({
})
export default connect(select, perform)(ShowPage)

View file

@ -1,12 +1,17 @@
import React from 'react';
import lbry from '../lbry.js';
import lighthouse from '../lighthouse.js';
import lbryuri from '../lbryuri.js';
import Video from 'component/video'
import {TruncatedText, Thumbnail, FilePrice, BusyMessage} from '../component/common.js';
import {FileActions} from '../component/file-actions.js';
import UriIndicator from '../component/channel-indicator.js';
import lbry from 'lbry.js';
import lighthouse from 'lighthouse.js';
import lbryuri from 'lbryuri.js';
import {Video} from 'page/watch.js'
import {
TruncatedText,
Thumbnail,
FilePrice,
BusyMessage
} from 'component/common.js';
import {FileActions} from 'component/file-actions.js';
import Link from 'component/link';
import UriIndicator from 'component/channel-indicator.js';
var FormatItem = React.createClass({
propTypes: {
@ -16,11 +21,8 @@ var FormatItem = React.createClass({
outpoint: React.PropTypes.string,
},
render: function() {
const {author, language, license} = this.props.metadata;
if (!this.props.contentType && [author, language, license].filter((val) => {return !!val; }).length === 0) {
return null;
}
const {thumbnail, author, title, description, language, license} = this.props.metadata;
const mediaType = lbry.getMediaType(this.props.contentType);
return (
<table className="table-standard">
@ -243,21 +245,21 @@ let ShowPage = React.createClass({
render: function() {
const metadata = this.state.metadata,
title = metadata ? this.state.metadata.title : this._uri;
title = metadata ? this.state.metadata.title : this._uri;
let innerContent = "";
if (!this.state.uriLookupComplete || this.state.isFailed) {
innerContent = <section className="card">
<div className="card__inner">
<div className="card__title-identity"><h1>{title}</h1></div>
</div>
<div className="card__content">
{ this.state.uriLookupComplete ?
<p>This location is not yet in use. { ' ' }<Link href="?publish" label="Put something here" />.</p> :
<BusyMessage message="Loading magic decentralized data..." />
}
</div>
<div className="card__inner">
<div className="card__title-identity"><h1>{title}</h1></div>
</div>
<div className="card__content">
{ this.state.uriLookupComplete ?
<p>This location is not yet in use. { ' ' }<Link href="?publish" label="Put something here" />.</p> :
<BusyMessage message="Loading magic decentralized data..." />
}
</div>
</section>;
} else if (this.state.claimType == "channel") {
innerContent = <ChannelPage title={this._uri} />
@ -282,3 +284,87 @@ let ShowPage = React.createClass({
});
export default ShowPage;
//
// const ShowPage = (props) => {
// const {
// claim,
// uri,
// isDownloaded,
// fileInfo,
// costInfo,
// } = props
// const {
// txid,
// nout,
// has_signature,
// signature_is_valid,
// value,
// } = claim
// const {
// stream,
// } = value
// const {
// metadata,
// source,
// } = stream
// const {
// title,
// } = metadata
// const {
// contentType,
// } = source
// const {
// cost,
// includesData,
// } = costInfo
// const costIncludesData = includesData
//
// const outpoint = txid + ':' + nout;
// const uriLookupComplete = !!claim
// const hasSignature = has_signature
// const signatureIsValid = signature_is_valid
//
// return (
// <main className="main--single-column">
// <section className="show-page-media">
// { contentType && contentType.startsWith('video/') ?
// <Video className="video-embedded" uri={uri} metadata={metadata} outpoint={outpoint} /> :
// (metadata ? <Thumbnail src={metadata.thumbnail} /> : <Thumbnail />) }
// </section>
// <section className="card">
// <div className="card__inner">
// <div className="card__title-identity">
// {isDownloaded === false
// ? <span style={{float: "right"}}><FilePrice uri={uri} metadata={metadata} /></span>
// : null}
// <h1>{title}</h1>
// { uriLookupComplete ?
// <div>
// <div className="card__subtitle">
// <UriIndicator uri={uri} hasSignature={hasSignature} signatureIsValid={signatureIsValid} />
// </div>
// <div className="card__actions">
// <FileActions uri={uri} outpoint={outpoint} metadata={metadata} contentType={contentType} />
// </div>
// </div> : '' }
// </div>
// { uriLookupComplete ?
// <div>
// <div className="card__content card__subtext card__subtext card__subtext--allow-newlines">
// {metadata.description}
// </div>
// </div>
// : <div className="card__content"><BusyMessage message="Loading magic decentralized data..." /></div> }
// </div>
// { metadata ?
// <div className="card__content">
// <FormatItem metadata={metadata} contentType={contentType} cost={cost} uri={uri} outpoint={outpoint} costIncludesData={costIncludesData} />
// </div> : '' }
// <div className="card__content">
// <Link href="https://lbry.io/dmca" label="report" className="button-text-help" />
// </div>
// </section>
// </main>
// )
// }

View file

@ -3,7 +3,7 @@ import * as types from 'constants/action_types'
const reducers = {}
const defaultState = {
isLoaded: false,
currentPage: 'discover',
currentPath: 'discover',
platform: process.platform,
drawerOpen: sessionStorage.getItem('drawerOpen') || true,
upgradeSkipped: sessionStorage.getItem('upgradeSkipped'),
@ -12,7 +12,7 @@ const defaultState = {
reducers[types.NAVIGATE] = function(state, action) {
return Object.assign({}, state, {
currentPage: action.data.path
currentPath: action.data.path,
})
}

View file

@ -106,6 +106,78 @@ reducers[types.FETCH_PUBLISHED_CONTENT_COMPLETED] = function(state, action) {
})
}
reducers[types.FETCH_FILE_INFO_STARTED] = function(state, action) {
const {
uri,
output,
} = action.data
const newFetchingFileInfos = Object.assign({}, state.fetchingFileInfos)
newFetchingFileInfos[uri] = true
return Object.assign({}, state, {
fetchingFileInfos: newFetchingFileInfos,
})
}
reducers[types.FETCH_FILE_INFO_COMPLETED] = function(state, action) {
const {
uri,
fileInfo,
} = action.data
const newFetchingFileInfos = Object.assign({}, state.fetchingFileInfos)
const fileInfos = Object.assign({}, state.fileInfos)
const byUri = Object.assign({}, fileInfos.byUri)
byUri[uri] = fileInfo
delete newFetchingFileInfos[uri]
const newFileInfos = Object.assign({}, fileInfos, {
byUri: byUri,
})
return Object.assign({}, state, {
fetchingFileInfos: newFetchingFileInfos,
fileInfos: newFileInfos,
})
}
reducers[types.FETCH_COST_INFO_STARTED] = function(state, action) {
const {
uri,
} = action.data
const fetchingCostInfos = Object.assign({}, state.fetchingCostInfos)
fetchingCostInfos[uri] = true
return Object.assign({}, state, {
fetchingCostInfos,
})
}
reducers[types.FETCH_COST_INFO_COMPLETED] = function(state, action) {
const {
uri,
costInfo,
} = action.data
const newFetchingCostInfos = Object.assign({}, state.fetchingCostInfos)
const costInfos = Object.assign({}, state.costInfos)
const byUri = Object.assign({}, costInfos.byUri)
byUri[uri] = costInfo
delete newFetchingCostInfos[uri]
const newCostInfos = Object.assign({}, costInfos, {
byUri: byUri,
})
return Object.assign({}, state, {
fetchingCostInfos: newFetchingCostInfos,
costInfos: newCostInfos,
})
}
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);

11
ui/js/reducers/rewards.js Normal file
View file

@ -0,0 +1,11 @@
import * as types from 'constants/action_types'
const reducers = {}
const defaultState = {
}
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);
return state;
}

View file

@ -4,23 +4,27 @@ export const _selectState = state => state.app || {}
export const selectIsLoaded = createSelector(
_selectState,
(state) => {
return state.isLoaded
}
(state) => state.isLoaded
)
export const selectCurrentPath = createSelector(
_selectState,
(state) => state.currentPath
)
export const selectCurrentPage = createSelector(
_selectState,
(state) => {
return state.currentPage
}
selectCurrentPath,
(path) => path.split('=')[0]
)
export const selectCurrentUri = createSelector(
selectCurrentPath,
(path) => path.split('://')[1]
)
export const selectPlatform = createSelector(
_selectState,
(state) => {
return state.platform
}
(state) => state.platform
)
export const selectUpdateUrl = createSelector(

View file

@ -2,6 +2,7 @@ import { createSelector } from 'reselect'
import {
selectDaemonReady,
selectCurrentPage,
selectCurrentUri,
} from 'selectors/app'
export const _selectState = state => state.content || {}
@ -41,6 +42,109 @@ export const selectResolvedUris = createSelector(
(state) => state.resolvedUris || {}
)
export const selectCurrentResolvedUri = createSelector(
selectCurrentUri,
selectResolvedUris,
(uri, resolvedUris) => resolvedUris[uri] || {}
)
export const selectCurrentResolvedUriClaim = createSelector(
selectCurrentResolvedUri,
(uri) => uri.claim || {}
)
export const selectCurrentResolvedUriClaimOutpoint = createSelector(
selectCurrentResolvedUriClaim,
(claim) => `${claim.txid}:${claim.nout}`
)
export const selectFileInfos = createSelector(
_selectState,
(state) => state.fileInfos || {}
)
export const selectFileInfosByUri = createSelector(
selectFileInfos,
(fileInfos) => fileInfos.byUri || {}
)
export const selectCurrentUriFileInfo = createSelector(
selectCurrentUri,
selectFileInfosByUri,
(uri, byUri) => byUri[uri]
)
export const selectCurrentUriIsDownloaded = createSelector(
selectCurrentUriFileInfo,
(fileInfo) => fileInfo && fileInfo.length > 0
)
export const selectFetchingFileInfos = createSelector(
_selectState,
(state) => state.fetchingFileInfos || {}
)
export const selectIsFetchingCurrentUriFileInfo = createSelector(
selectFetchingFileInfos,
selectCurrentUri,
(fetching, uri) => !!fetching[uri]
)
export const selectCostInfos = createSelector(
_selectState,
(state) => state.costInfos || {}
)
export const selectCostInfosByUri = createSelector(
selectCostInfos,
(costInfos) => costInfos.byUri || {}
)
export const selectFetchingCostInfos = createSelector(
_selectState,
(state) => state.fetchingCostInfos || {}
)
export const selectIsFetchingCurrentUriCostInfo = createSelector(
selectFetchingCostInfos,
selectCurrentUri,
(fetching, uri) => !!fetching[uri]
)
export const selectCurrentUriCostInfo = createSelector(
selectCurrentUri,
selectCostInfosByUri,
(uri, byUri) => byUri[uri] || {}
)
export const shouldFetchCurrentUriCostInfo = createSelector(
selectCurrentPage,
selectCurrentUri,
selectIsFetchingCurrentUriCostInfo,
selectCurrentUriCostInfo,
(page, uri, fetching, costInfo) => {
if (page != 'show') return false
if (fetching) return false
if (Object.keys(costInfo).length != 0) return false
return true
}
)
export const shouldFetchCurrentUriFileInfo = createSelector(
selectCurrentPage,
selectCurrentUri,
selectIsFetchingCurrentUriFileInfo,
selectCurrentUriFileInfo,
(page, uri, fetching, fileInfo) => {
if (page != 'show') return false
if (fetching) return false
if (fileInfo != undefined) return false
return true
}
)
export const selectFetchingDownloadedContent = createSelector(
_selectState,
(state) => !!state.fetchingDownloadedContent

View file

@ -0,0 +1,3 @@
import { createSelector } from 'reselect'
export const _selectState = state => state.rewards || {}

View file

@ -95,7 +95,7 @@ export const shouldCheckAddressIsMine = createSelector(
export const selectDraftTransaction = createSelector(
_selectState,
(state) => state.draftTransaction || buildDraftTransaction()
(state) => state.draftTransaction || {}
)
export const selectDraftTransactionAmount = createSelector(

View file

@ -7,6 +7,7 @@ import {
} from 'redux-logger'
import appReducer from 'reducers/app';
import contentReducer from 'reducers/content';
import rewardsReducer from 'reducers/rewards'
import walletReducer from 'reducers/wallet'
function isFunction(object) {
@ -20,6 +21,7 @@ function isNotFunction(object) {
const reducers = redux.combineReducers({
app: appReducer,
content: contentReducer,
rewards: rewardsReducer,
wallet: walletReducer,
});
@ -32,10 +34,10 @@ if (env === 'development') {
middleware.push(logger)
}
var createStoreWithMiddleware = redux.compose(
const createStoreWithMiddleware = redux.compose(
redux.applyMiddleware(...middleware)
)(redux.createStore);
var reduxStore = createStoreWithMiddleware(reducers);
const reduxStore = createStoreWithMiddleware(reducers);
export default reduxStore;

View file

@ -6,6 +6,8 @@ import {
shouldFetchFeaturedContent,
shouldFetchDownloadedContent,
shouldFetchPublishedContent,
shouldFetchCurrentUriFileInfo,
shouldFetchCurrentUriCostInfo,
} from 'selectors/content'
import {
doFetchTransactions,
@ -15,6 +17,8 @@ import {
doFetchFeaturedContent,
doFetchDownloadedContent,
doFetchPublishedContent,
doFetchCurrentUriFileInfo,
doFetchCurrentUriCostInfo,
} from 'actions/content'
const triggers = []
@ -44,7 +48,15 @@ triggers.push({
action: doFetchPublishedContent,
})
console.log(triggers)
triggers.push({
selector: shouldFetchCurrentUriFileInfo,
action: doFetchCurrentUriFileInfo,
})
triggers.push({
selector: shouldFetchCurrentUriCostInfo,
action: doFetchCurrentUriCostInfo,
})
const runTriggers = function() {
triggers.forEach(function(trigger) {