From 393aa9129c5e676e4a1ab64ccc7f7fa67caa22fd Mon Sep 17 00:00:00 2001 From: 6ea86b96 <6ea86b96@gmail.com> Date: Sun, 23 Apr 2017 21:01:00 +0700 Subject: [PATCH] Featured content --- ui/js/actions/content.js | 27 +++ ui/js/component/fileCardStream/index.js | 7 + ui/js/component/fileCardStream/view.jsx | 110 +++++++++ ui/js/component/fileTile/index.js | 4 + ui/js/component/fileTile/view.jsx | 295 ++---------------------- ui/js/constants/action_types.js | 2 + ui/js/page/discover/view.jsx | 1 + ui/js/reducers/content.js | 42 ++++ ui/js/selectors/content.js | 5 + 9 files changed, 219 insertions(+), 274 deletions(-) create mode 100644 ui/js/component/fileCardStream/index.js create mode 100644 ui/js/component/fileCardStream/view.jsx diff --git a/ui/js/actions/content.js b/ui/js/actions/content.js index a4e582ffe..08411bab3 100644 --- a/ui/js/actions/content.js +++ b/ui/js/actions/content.js @@ -2,6 +2,29 @@ import * as types from 'constants/action_types' import lbry from 'lbry' import lbryio from 'lbryio'; +export function doResolveUri(dispatch, uri) { + dispatch({ + type: types.RESOLVE_URI_STARTED, + data: { uri } + }) + + lbry.resolve({uri: uri}).then((resolutionInfo) => { + const { + claim, + certificate, + } = resolutionInfo + + dispatch({ + type: types.RESOLVE_URI_COMPLETED, + data: { + uri, + claim, + certificate, + } + }) + }) +} + export function doFetchFeaturedContent() { return function(dispatch, getState) { const state = getState() @@ -18,6 +41,10 @@ export function doFetchFeaturedContent() { uris: Uris, } }) + + Object.keys(Uris).forEach((category) => { + Uris[category].forEach((uri) => doResolveUri(dispatch, uri)) + }) } const failure = () => { diff --git a/ui/js/component/fileCardStream/index.js b/ui/js/component/fileCardStream/index.js new file mode 100644 index 000000000..ec4800f32 --- /dev/null +++ b/ui/js/component/fileCardStream/index.js @@ -0,0 +1,7 @@ +import React from 'react' +import { + connect +} from 'react-redux' +import FileCardStream from './view' + +export default connect()(FileCardStream) diff --git a/ui/js/component/fileCardStream/view.jsx b/ui/js/component/fileCardStream/view.jsx new file mode 100644 index 000000000..1dca3d48f --- /dev/null +++ b/ui/js/component/fileCardStream/view.jsx @@ -0,0 +1,110 @@ +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'; + +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 { + showNsfwHelp: false, + isHidden: false, + } + }, + getDefaultProps: function() { + return { + obscureNsfw: !lbry.getClientSetting('showNsfw'), + hidePrice: false, + hasSignature: false, + } + }, + componentDidMount: function() { + this._isMounted = true; + if (this.props.hideOnRemove) { + this._fileInfoSubscribeId = lbry.fileInfoSubscribe(this.props.outpoint, this.onFileInfoUpdate); + } + }, + componentWillUnmount: function() { + if (this._fileInfoSubscribeId) { + lbry.fileInfoUnsubscribe(this.props.outpoint, this._fileInfoSubscribeId); + } + }, + onFileInfoUpdate: function(fileInfo) { + if (!fileInfo && this._isMounted && this.props.hideOnRemove) { + this.setState({ + isHidden: true + }); + } + }, + handleMouseOver: function() { + this.setState({ + hovered: true, + }); + }, + handleMouseOut: function() { + this.setState({ + hovered: false, + }); + }, + render: function() { + if (this.state.isHidden) { + return null; + } + + 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 primaryUrl = '?show=' + uri; + return ( +
+
+ +
+
{title}
+
+ { !this.props.hidePrice ? : null} + +
+
+
+
+ + {isConfirmed + ? metadata.description + : This file is pending confirmation.} + +
+
+ {this.state.showNsfwHelp && this.state.hovered + ?
+

+ This content is Not Safe For Work. + To view adult content, please change your . +

+
+ : null} +
+
+ ); + } +}); + +export default FileCardStream diff --git a/ui/js/component/fileTile/index.js b/ui/js/component/fileTile/index.js index b410bc9c2..e24dade66 100644 --- a/ui/js/component/fileTile/index.js +++ b/ui/js/component/fileTile/index.js @@ -2,9 +2,13 @@ import React from 'react' import { connect } from 'react-redux' +import { + selectResolvedUris, +} from 'selectors/content' import FileTile from './view' const select = (state) => ({ + resolvedUris: selectResolvedUris(state), }) const perform = (dispatch) => ({ diff --git a/ui/js/component/fileTile/view.jsx b/ui/js/component/fileTile/view.jsx index b85b0ec6a..b80616dcc 100644 --- a/ui/js/component/fileTile/view.jsx +++ b/ui/js/component/fileTile/view.jsx @@ -2,283 +2,30 @@ 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 FileCardStream from 'component/fileCardStream' +import FileTileStream from 'component/fileTileStream' +import FileActions from 'component/fileActions'; -/*should be merged into FileTile once FileTile is refactored to take a single id*/ -export let FileTileStream = React.createClass({ - _fileInfoSubscribeId: null, - _isMounted: null, +class FileTile extends React.Component { + render() { + const { + displayStyle, + uri, + claim, + } = this.props - 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 { - showNsfwHelp: false, - isHidden: false, - } - }, - getDefaultProps: function() { - return { - obscureNsfw: !lbry.getClientSetting('showNsfw'), - hidePrice: false, - hasSignature: false, - } - }, - componentDidMount: function() { - this._isMounted = true; - if (this.props.hideOnRemove) { - this._fileInfoSubscribeId = lbry.fileInfoSubscribe(this.props.outpoint, this.onFileInfoUpdate); - } - }, - componentWillUnmount: function() { - if (this._fileInfoSubscribeId) { - lbry.fileInfoUnsubscribe(this.props.outpoint, this._fileInfoSubscribeId); - } - }, - onFileInfoUpdate: function(fileInfo) { - if (!fileInfo && this._isMounted && this.props.hideOnRemove) { - this.setState({ - isHidden: true - }); - } - }, - handleMouseOver: function() { - if (this.props.obscureNsfw && this.props.metadata && this.props.metadata.nsfw) { - this.setState({ - showNsfwHelp: true, - }); - } - }, - handleMouseOut: function() { - if (this.state.showNsfwHelp) { - this.setState({ - showNsfwHelp: false, - }); - } - }, - render: function() { - if (this.state.isHidden) { - return null; - } - - 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 primaryUrl = "?show=" + uri; - return ( -
- -
-
-
-
-
- { !this.props.hidePrice - ? - : null} -
{uri}
-

{title}

-
-
- - {isConfirmed - ? metadata.description - : This file is pending confirmation.} - -
-
-
-
- {this.state.showNsfwHelp - ?
-

- This content is Not Safe For Work. - To view adult content, please change your . -

-
- : null} -
- ); - } -}); - -export let 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 { - showNsfwHelp: false, - isHidden: false, - } - }, - getDefaultProps: function() { - return { - obscureNsfw: !lbry.getClientSetting('showNsfw'), - hidePrice: false, - hasSignature: false, - } - }, - componentDidMount: function() { - this._isMounted = true; - if (this.props.hideOnRemove) { - this._fileInfoSubscribeId = lbry.fileInfoSubscribe(this.props.outpoint, this.onFileInfoUpdate); - } - }, - componentWillUnmount: function() { - if (this._fileInfoSubscribeId) { - lbry.fileInfoUnsubscribe(this.props.outpoint, this._fileInfoSubscribeId); - } - }, - onFileInfoUpdate: function(fileInfo) { - if (!fileInfo && this._isMounted && this.props.hideOnRemove) { - this.setState({ - isHidden: true - }); - } - }, - handleMouseOver: function() { - this.setState({ - hovered: true, - }); - }, - handleMouseOut: function() { - this.setState({ - hovered: false, - }); - }, - render: function() { - if (this.state.isHidden) { - return null; - } - - 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 primaryUrl = '?show=' + uri; - return ( -
-
- -
-
{title}
-
- { !this.props.hidePrice ? : null} - -
-
-
-
- - {isConfirmed - ? metadata.description - : This file is pending confirmation.} - -
-
- {this.state.showNsfwHelp && this.state.hovered - ?
-

- This content is Not Safe For Work. - To view adult content, please change your . -

-
- : null} -
-
- ); - } -}); - -let FileTile = React.createClass({ - _isMounted: false, - _isResolvePending: false, - - propTypes: { - uri: React.PropTypes.string.isRequired, - }, - - getInitialState: function() { - return { - outpoint: null, - claimInfo: null - } - }, - resolve: function(uri) { - this._isResolvePending = true; - lbry.resolve({uri: uri}).then((resolutionInfo) => { - this._isResolvePending = false; - if (this._isMounted && resolutionInfo && resolutionInfo.claim && resolutionInfo.claim.value && - resolutionInfo.claim.value.stream && resolutionInfo.claim.value.stream.metadata) { - // In case of a failed lookup, metadata will be null, in which case the component will never display - this.setState({ - claimInfo: resolutionInfo.claim, - }); + if(!claim) { + if (displayStyle == 'card') { + return } - }); - }, - componentWillReceiveProps: function(nextProps) { - if (nextProps.uri != this.props.uri) { - this.setState(this.getInitialState()); - this.resolve(nextProps.uri); - } - }, - componentDidMount: function() { - this._isMounted = true; - this.resolve(this.props.uri); - }, - componentWillUnmount: function() { - this._isMounted = false; - }, - render: function() { - if (!this.state.claimInfo) { - if (this.props.displayStyle == 'card') { - return - } - if (this.props.showEmpty) - { - return this._isResolvePending ? - : -
{lbryuri.normalize(this.props.uri)} is unclaimed.
; - } - return null; + return null } - const {txid, nout, has_signature, signature_is_valid, - value: {stream: {metadata, source: {contentType}}}} = this.state.claimInfo; - - return this.props.displayStyle == 'card' ? - : - ; + return displayStyle == 'card' ? + + : + } -}); \ No newline at end of file +} + +export default FileTile \ No newline at end of file diff --git a/ui/js/constants/action_types.js b/ui/js/constants/action_types.js index e0fae5d24..0172662f6 100644 --- a/ui/js/constants/action_types.js +++ b/ui/js/constants/action_types.js @@ -37,3 +37,5 @@ export const SEND_TRANSACTION_FAILED = 'SEND_TRANSACTION_FAILED' // Content export const FETCH_FEATURED_CONTENT_STARTED = 'FETCH_FEATURED_CONTENT_STARTED' export const FETCH_FEATURED_CONTENT_COMPLETED = 'FETCH_FEATURED_CONTENT_COMPLETED' +export const RESOLVE_URI_STARTED = 'RESOLVE_URI_STARTED' +export const RESOLVE_URI_COMPLETED = 'RESOLVE_URI_COMPLETED' diff --git a/ui/js/page/discover/view.jsx b/ui/js/page/discover/view.jsx index a8ad512b7..30d434ef3 100644 --- a/ui/js/page/discover/view.jsx +++ b/ui/js/page/discover/view.jsx @@ -11,6 +11,7 @@ const communityCategoryToolTipText = ('Community Content is a public space where const FeaturedCategory = (props) => { const { category, + resolvedUris, names, } = props diff --git a/ui/js/reducers/content.js b/ui/js/reducers/content.js index aefdb11c7..5b9cc2e04 100644 --- a/ui/js/reducers/content.js +++ b/ui/js/reducers/content.js @@ -24,6 +24,48 @@ reducers[types.FETCH_FEATURED_CONTENT_COMPLETED] = function(state, action) { }) } +reducers[types.RESOLVE_URI_STARTED] = function(state, action) { + const { + uri + } = action.data + + const oldResolving = state.resolvingUris || [] + const newResolving = Object.assign([], oldResolving) + if (newResolving.indexOf(uri) == -1) newResolving.push(uri) + + return Object.assign({}, state, { + resolvingUris: newResolving + }) +} + +reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) { + const { + uri, + claim, + certificate, + } = action.data + const resolvedUris = Object.assign({}, state.resolvedUris) + const resolvingUris = state.resolvingUris + const index = state.resolvingUris.indexOf(uri) + const newResolvingUris = [ + ...resolvingUris.slice(0, index), + ...resolvingUris.slice(index + 1) + ] + + resolvedUris[uri] = { + claim: claim, + certificate: certificate, + } + + + const newState = Object.assign({}, state, { + resolvedUris: resolvedUris, + resolvingUris: newResolvingUris, + }) + + return Object.assign({}, state, newState) +} + export default function reducer(state = defaultState, action) { const handler = reducers[action.type]; if (handler) return handler(state, action); diff --git a/ui/js/selectors/content.js b/ui/js/selectors/content.js index 7e5905ecb..e2222cb91 100644 --- a/ui/js/selectors/content.js +++ b/ui/js/selectors/content.js @@ -35,3 +35,8 @@ export const shouldFetchFeaturedContent = createSelector( return true } ) + +export const selectResolvedUris = createSelector( + _selectState, + (state) => state.resolvedUris || {} +)