diff --git a/ui/dist/index.html b/ui/dist/index.html index b4239bc9a..8fbf0b5b6 100644 --- a/ui/dist/index.html +++ b/ui/dist/index.html @@ -7,7 +7,7 @@ - + diff --git a/ui/js/actions/content.js b/ui/js/actions/content.js index d887fc318..fb24a0458 100644 --- a/ui/js/actions/content.js +++ b/ui/js/actions/content.js @@ -240,7 +240,11 @@ export function doLoadVideo() { }) lbry.get({ uri }).then(streamInfo => { - if (streamInfo === null || typeof streamInfo !== 'object') { + const timeout = streamInfo === null || + typeof streamInfo !== 'object' || + streamInfo.error == 'Timeout' + + if(timeout) { dispatch({ type: types.LOADING_VIDEO_FAILED, data: { uri } @@ -264,6 +268,12 @@ export function doWatchVideo() { const alreadyDownloading = !!downloadingByUri[uri] const { cost } = costInfo + // BUG if you delete a file from the file system system you're going to be + // asked to pay for it again. We need to check if the file is in the blobs + // here and then dispatch doLoadVideo() which will reconstruct it again from + // the blobs. Or perhaps there's another way to see if a file was already + // purchased? + // we already fully downloaded the file if (fileInfo && fileInfo.completed) { return Promise.resolve() diff --git a/ui/js/component/channel-indicator.js b/ui/js/component/channel-indicator.js deleted file mode 100644 index e19850c28..000000000 --- a/ui/js/component/channel-indicator.js +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; -import lbry from '../lbry.js'; -import lbryuri from '../lbryuri.js'; -import {Icon} from './common.js'; - -const UriIndicator = React.createClass({ - propTypes: { - uri: React.PropTypes.string.isRequired, - hasSignature: React.PropTypes.bool.isRequired, - signatureIsValid: React.PropTypes.bool, - }, - render: function() { - - const uriObj = lbryuri.parse(this.props.uri); - - if (!this.props.hasSignature || !uriObj.isChannel) { - return Anonymous; - } - - const channelUriObj = Object.assign({}, uriObj); - delete channelUriObj.path; - delete channelUriObj.contentName; - const channelUri = lbryuri.build(channelUriObj, false); - - let icon, modifier; - if (this.props.signatureIsValid) { - modifier = 'valid'; - } else { - icon = 'icon-times-circle'; - modifier = 'invalid'; - } - return ( - - {channelUri} {' '} - { !this.props.signatureIsValid ? - : - '' } - - ); - } -}); - -export default UriIndicator; \ No newline at end of file diff --git a/ui/js/component/fileActions/view.jsx b/ui/js/component/fileActions/view.jsx index 5fe6a2dea..5d74949bc 100644 --- a/ui/js/component/fileActions/view.jsx +++ b/ui/js/component/fileActions/view.jsx @@ -116,188 +116,6 @@ class FileActionsRow extends React.Component { } } -// const FileActionsRow = React.createClass({ -// _isMounted: false, -// _fileInfoSubscribeId: null, - -// propTypes: { -// uri: React.PropTypes.string, -// outpoint: React.PropTypes.string.isRequired, -// metadata: React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.string]), -// contentType: React.PropTypes.string.isRequired, -// }, -// getInitialState: function() { -// return { -// fileInfo: null, -// modal: null, -// menuOpen: false, -// deleteChecked: false, -// attemptingDownload: false, -// attemptingRemove: false, -// } -// }, -// onFileInfoUpdate: function(fileInfo) { -// if (this._isMounted) { -// this.setState({ -// fileInfo: fileInfo ? fileInfo : false, -// attemptingDownload: fileInfo ? false : this.state.attemptingDownload -// }); -// } -// }, -// tryDownload: function() { -// this.setState({ -// attemptingDownload: true, -// attemptingRemove: false -// }); -// lbry.getCostInfo(this.props.uri).then(({cost}) => { -// lbry.getBalance((balance) => { -// if (cost > balance) { -// this.setState({ -// modal: 'notEnoughCredits', -// attemptingDownload: false, -// }); -// } else if (this.state.affirmedPurchase) { -// lbry.get({uri: this.props.uri}).then((streamInfo) => { -// if (streamInfo === null || typeof streamInfo !== 'object') { -// this.setState({ -// modal: 'timedOut', -// attemptingDownload: false, -// }); -// } -// }); -// } else { -// this.setState({ -// attemptingDownload: false, -// modal: 'affirmPurchase' -// }) -// } -// }); -// }); -// }, -// closeModal: function() { -// this.setState({ -// modal: null, -// }) -// }, -// onDownloadClick: function() { -// if (!this.state.fileInfo && !this.state.attemptingDownload) { -// this.tryDownload(); -// } -// }, -// onOpenClick: function() { -// if (this.state.fileInfo && this.state.fileInfo.download_path) { -// shell.openItem(this.state.fileInfo.download_path); -// } -// }, -// handleDeleteCheckboxClicked: function(event) { -// this.setState({ -// deleteChecked: event.target.checked, -// }); -// }, -// handleRevealClicked: function() { -// if (this.state.fileInfo && this.state.fileInfo.download_path) { -// shell.showItemInFolder(this.state.fileInfo.download_path); -// } -// }, -// handleRemoveClicked: function() { -// this.setState({ -// modal: 'confirmRemove', -// }); -// }, -// handleRemoveConfirmed: function() { -// lbry.removeFile(this.props.outpoint, this.state.deleteChecked); -// this.setState({ -// modal: null, -// fileInfo: false, -// attemptingDownload: false -// }); -// }, -// onAffirmPurchase: function() { -// this.setState({ -// affirmedPurchase: true, -// modal: null -// }); -// this.tryDownload(); -// }, -// openMenu: function() { -// this.setState({ -// menuOpen: !this.state.menuOpen, -// }); -// }, -// componentDidMount: function() { -// this._isMounted = true; -// this._fileInfoSubscribeId = lbry.fileInfoSubscribe(this.props.outpoint, this.onFileInfoUpdate); -// }, -// componentWillUnmount: function() { -// this._isMounted = false; -// if (this._fileInfoSubscribeId) { -// lbry.fileInfoUnsubscribe(this.props.outpoint, this._fileInfoSubscribeId); -// } -// }, -// render: function() { -// if (this.state.fileInfo === null) -// { -// return null; -// } - -// const openInFolderMessage = window.navigator.platform.startsWith('Mac') ? 'Open in Finder' : 'Open in Folder', -// showMenu = !!this.state.fileInfo; - -// let linkBlock; -// if (this.state.fileInfo === false && !this.state.attemptingDownload) { -// linkBlock = ; -// } else if (this.state.attemptingDownload || (!this.state.fileInfo.completed && !this.state.fileInfo.isMine)) { -// const -// progress = this.state.fileInfo ? this.state.fileInfo.written_bytes / this.state.fileInfo.total_bytes * 100 : 0, -// label = this.state.fileInfo ? progress.toFixed(0) + '% complete' : 'Connecting...', -// labelWithIcon = {label}; - -// linkBlock = ( -//
-//
{labelWithIcon}
-// {labelWithIcon} -//
-// ); -// } else { -// linkBlock = ; -// } - -// const uri = lbryuri.normalize(this.props.uri); -// const title = this.props.metadata ? this.props.metadata.title : uri; -// return ( -//
-// {this.state.fileInfo !== null || this.state.fileInfo.isMine -// ? linkBlock -// : null} -// { showMenu ? -// -// -// -// : '' } -// -// Are you sure you'd like to buy {title} for credits? -// -// -// You don't have enough LBRY credits to pay for this stream. -// -// -// LBRY was unable to download the stream {uri}. -// -// -//

Are you sure you'd like to remove {title} from LBRY?

- -// -//
-//
-// ); -// } -// }); - class FileActions extends React.Component { constructor(props) { super(props) diff --git a/ui/js/component/fileCardStream/index.js b/ui/js/component/fileCardStream/index.js index 14e3dc7f9..1a183846f 100644 --- a/ui/js/component/fileCardStream/index.js +++ b/ui/js/component/fileCardStream/index.js @@ -17,6 +17,9 @@ import { import { makeSelectFileInfoForUri, } from 'selectors/file_info' +import { + makeSelectResolvingUri, +} from 'selectors/content' import FileCardStream from './view' const makeSelect = () => { @@ -24,6 +27,8 @@ const makeSelect = () => { const selectFileInfoForUri = makeSelectFileInfoForUri() const selectMetadataForUri = makeSelectMetadataForUri() const selectSourceForUri = makeSelectSourceForUri() + const selectResolvingUri = makeSelectResolvingUri() + const select = (state, props) => ({ claim: selectClaimForUri(state, props), fileInfo: selectFileInfoForUri(state, props), @@ -32,6 +37,7 @@ const makeSelect = () => { hasSignature: false, metadata: selectMetadataForUri(state, props), source: selectSourceForUri(state, props), + isResolvingUri: selectResolvingUri(state, props), }) return select diff --git a/ui/js/component/fileCardStream/view.jsx b/ui/js/component/fileCardStream/view.jsx index c3954d9b7..72284eb79 100644 --- a/ui/js/component/fileCardStream/view.jsx +++ b/ui/js/component/fileCardStream/view.jsx @@ -4,7 +4,7 @@ import lbryuri from 'lbryuri.js'; import Link from 'component/link'; import {Thumbnail, TruncatedText,} from 'component/common'; import FilePrice from 'component/filePrice' -import UriIndicator from 'component/channel-indicator'; +import UriIndicator from 'component/uriIndicator'; class FileCardStream extends React.Component { constructor(props) { @@ -56,37 +56,43 @@ class FileCardStream extends React.Component { return null; } - // if (!this.props.metadata) { - // return null - // } + const { + metadata, + isResolvingUri, + navigate, + hidePrice, + } = 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 primaryUrl = 'show=' + uri; + let description = "" + if (isConfirmed) { + description = metadata.description + } else if (isResolvingUri) { + description = "Loading..." + } else { + description = This file is pending confirmation + } + return (
- this.props.navigate(primaryUrl)} className="card__link"> + navigate(primaryUrl)} className="card__link">
{title}
- { !this.props.hidePrice ? : null} - + { !hidePrice ? : null} +
{metadata &&
}
- - {isConfirmed - ? metadata.description - : This file is pending confirmation.} - + {description}
{this.state.showNsfwHelp && this.state.hovered diff --git a/ui/js/component/fileTileStream/index.js b/ui/js/component/fileTileStream/index.js index d212dfdae..80807f125 100644 --- a/ui/js/component/fileTileStream/index.js +++ b/ui/js/component/fileTileStream/index.js @@ -20,6 +20,9 @@ import { import { selectObscureNsfw, } from 'selectors/app' +import { + makeSelectResolvingUri, +} from 'selectors/content' import FileTileStream from './view' const makeSelect = () => { @@ -29,6 +32,7 @@ const makeSelect = () => { const selectAvailabilityForUri = makeSelectAvailabilityForUri() const selectMetadataForUri = makeSelectMetadataForUri() const selectSourceForUri = makeSelectSourceForUri() + const selectResolvingUri = makeSelectResolvingUri() const select = (state, props) => ({ claim: selectClaimForUri(state, props), @@ -38,6 +42,7 @@ const makeSelect = () => { obscureNsfw: selectObscureNsfw(state), metadata: selectMetadataForUri(state, props), source: selectSourceForUri(state, props), + isResolvingUri: selectResolvingUri(state, props), }) return select diff --git a/ui/js/component/fileTileStream/view.jsx b/ui/js/component/fileTileStream/view.jsx index 2130df284..6a80772eb 100644 --- a/ui/js/component/fileTileStream/view.jsx +++ b/ui/js/component/fileTileStream/view.jsx @@ -5,7 +5,7 @@ import Link from 'component/link'; import FileActions from 'component/fileActions'; import {Thumbnail, TruncatedText,} from 'component/common.js'; import FilePrice from 'component/filePrice' -import UriIndicator from 'component/channel-indicator.js'; +import UriIndicator from 'component/uriIndicator'; /*should be merged into FileTile once FileTile is refactored to take a single id*/ class FileTileStream extends React.Component { @@ -63,7 +63,9 @@ class FileTileStream extends React.Component { const { metadata, + isResolvingUri, navigate, + hidePrice, } = this.props const uri = lbryuri.normalize(this.props.uri); @@ -71,6 +73,15 @@ class FileTileStream extends React.Component { const title = isConfirmed ? metadata.title : uri; const obscureNsfw = this.props.obscureNsfw && isConfirmed && metadata.nsfw; + let description = "" + if (isConfirmed) { + description = metadata.description + } else if (isResolvingUri) { + description = "Loading..." + } else { + description = This file is pending confirmation + } + return (
@@ -101,11 +112,7 @@ class FileTileStream extends React.Component {

- - {isConfirmed - ? metadata.description - : This file is pending confirmation.} - + {description}

diff --git a/ui/js/component/reward-link.js b/ui/js/component/reward-link.js index d003e0303..b6ae09fed 100644 --- a/ui/js/component/reward-link.js +++ b/ui/js/component/reward-link.js @@ -1,7 +1,9 @@ import React from 'react'; -import {Icon} from './common.js'; -import Modal from '../component/modal.js'; -import rewards from '../rewards.js'; +import lbry from 'lbry' +import {Icon} from 'component/common'; +import Modal from 'component/modal'; +import rewards from 'rewards'; +import Link from 'component/link' export let RewardLink = React.createClass({ propTypes: { diff --git a/ui/js/component/router/view.jsx b/ui/js/component/router/view.jsx index 7fa7df107..cb57dc4a8 100644 --- a/ui/js/component/router/view.jsx +++ b/ui/js/component/router/view.jsx @@ -6,7 +6,7 @@ import ReportPage from 'page/report.js'; import StartPage from 'page/start.js'; import WalletPage from 'page/wallet'; import ShowPage from 'page/showPage'; -import PublishPage from 'page/publish.js'; +import PublishPage from 'page/publish'; import DiscoverPage from 'page/discover'; import SplashScreen from 'component/splash.js'; import RewardsPage from 'page/rewards.js'; diff --git a/ui/js/component/uriIndicator/index.js b/ui/js/component/uriIndicator/index.js new file mode 100644 index 000000000..6cadf91b3 --- /dev/null +++ b/ui/js/component/uriIndicator/index.js @@ -0,0 +1,20 @@ +import React from 'react' +import { + connect, +} from 'react-redux' +import { + makeSelectClaimForUri, +} from 'selectors/claims' +import UriIndicator from './view' + +const makeSelect = () => { + const selectClaimForUri = makeSelectClaimForUri() + + const select = (state, props) => ({ + claim: selectClaimForUri(state, props), + }) + + return select +} + +export default connect(makeSelect, null)(UriIndicator) diff --git a/ui/js/component/uriIndicator/view.jsx b/ui/js/component/uriIndicator/view.jsx new file mode 100644 index 000000000..cc3d4f0eb --- /dev/null +++ b/ui/js/component/uriIndicator/view.jsx @@ -0,0 +1,44 @@ +import React from 'react'; +import lbry from 'lbry'; +import lbryuri from 'lbryuri'; +import {Icon} from 'component/common'; + +const UriIndicator = (props) => { + const { + uri, + claim: { + has_signature: hasSignature, + signature_is_valid: signatureIsValid, + } = {}, + } = props + + const uriObj = lbryuri.parse(uri); + + if (!hasSignature || !uriObj.isChannel) { + return Anonymous; + } + + const channelUriObj = Object.assign({}, uriObj); + delete channelUriObj.path; + delete channelUriObj.contentName; + const channelUri = lbryuri.build(channelUriObj, false); + + let icon, modifier; + if (signatureIsValid) { + modifier = 'valid'; + } else { + icon = 'icon-times-circle'; + modifier = 'invalid'; + } + + return ( + + {channelUri} {' '} + { !signatureIsValid ? + : + '' } + + ) +} + +export default UriIndicator; \ No newline at end of file diff --git a/ui/js/page/discover/view.jsx b/ui/js/page/discover/view.jsx index 618ef675c..7bca2f55f 100644 --- a/ui/js/page/discover/view.jsx +++ b/ui/js/page/discover/view.jsx @@ -84,7 +84,7 @@ const FeaturedCategory = (props) => { {category && } - {names && names.map(name => )} + {names && names.map(name => )} ) } diff --git a/ui/js/page/publish/index.js b/ui/js/page/publish/index.js new file mode 100644 index 000000000..29347b6ed --- /dev/null +++ b/ui/js/page/publish/index.js @@ -0,0 +1,17 @@ +import React from 'react' +import { + connect, +} from 'react-redux' +import { + doNavigate, +} from 'actions/app' +import PublishPage from './view' + +const select = (state) => ({ +}) + +const perform = (dispatch) => ({ + navigate: (path) => dispatch(doNavigate(path)), +}) + +export default connect(select, perform)(PublishPage) diff --git a/ui/js/page/publish.js b/ui/js/page/publish/view.jsx similarity index 98% rename from ui/js/page/publish.js rename to ui/js/page/publish/view.jsx index ad600f19d..24a311853 100644 --- a/ui/js/page/publish.js +++ b/ui/js/page/publish/view.jsx @@ -1,10 +1,10 @@ import React from 'react'; -import lbry from '../lbry.js'; -import {FormField, FormRow} from '../component/form.js'; -import Link from '../component/link'; -import rewards from '../rewards.js'; -import lbryio from '../lbryio.js'; -import Modal from '../component/modal.js'; +import lbry from 'lbry'; +import {FormField, FormRow} from 'component/form.js'; +import Link from 'component/link'; +import rewards from 'rewards'; +import lbryio from 'lbryio'; +import Modal from 'component/modal'; var PublishPage = React.createClass({ _requiredFields: ['meta_title', 'name', 'bid', 'tos_agree'], @@ -148,7 +148,7 @@ var PublishPage = React.createClass({ }); }, handlePublishStartedConfirmed: function() { - window.location = "?published"; + this.props.navigate('published') }, handlePublishError: function(error) { this.setState({ diff --git a/ui/js/page/rewards.js b/ui/js/page/rewards.js index 5bb55f9ff..2fd58db3a 100644 --- a/ui/js/page/rewards.js +++ b/ui/js/page/rewards.js @@ -1,10 +1,10 @@ import React from 'react'; -import lbry from '../lbry.js'; -import lbryio from '../lbryio.js'; -import {CreditAmount, Icon} from '../component/common.js'; -import rewards from '../rewards.js'; -import Modal from '../component/modal.js'; -import {RewardLink} from '../component/reward-link.js'; +import lbry from 'lbry'; +import lbryio from 'lbryio'; +import {CreditAmount, Icon} from 'component/common.js'; +import rewards from 'rewards'; +import Modal from 'component/modal'; +import {RewardLink} from 'component/reward-link'; const RewardTile = React.createClass({ propTypes: { diff --git a/ui/js/page/showPage/view.jsx b/ui/js/page/showPage/view.jsx index 9620d2c74..be6c28785 100644 --- a/ui/js/page/showPage/view.jsx +++ b/ui/js/page/showPage/view.jsx @@ -11,7 +11,7 @@ import { import FilePrice from 'component/filePrice' import FileActions from 'component/fileActions'; import Link from 'component/link'; -import UriIndicator from 'component/channel-indicator.js'; +import UriIndicator from 'component/uriIndicator'; const FormatItem = (props) => { const { @@ -105,7 +105,7 @@ const ShowPage = (props) => { { uriLookupComplete ?
- +
diff --git a/ui/js/page/video/view.jsx b/ui/js/page/video/view.jsx index c71307119..2669cffe5 100644 --- a/ui/js/page/video/view.jsx +++ b/ui/js/page/video/view.jsx @@ -11,8 +11,7 @@ import lbryio from 'lbryio'; import rewards from 'rewards'; import LoadScreen from 'component/load_screen' -const fs = require('fs'); -const VideoStream = require('videostream'); +const plyr = require('plyr') class WatchLink extends React.Component { confirmPurchaseClick() { @@ -164,18 +163,22 @@ class VideoPlayer extends React.PureComponent { const elem = this.refs.video const { downloadPath, + contentType, } = this.props - const mediaFile = { - createReadStream: (opts) => - fs.createReadStream(downloadPath, opts) - } - const videostream = VideoStream(mediaFile, elem) - elem.play() + const players = plyr.setup(elem) + players[0].play() } render() { + const { + downloadPath, + contentType, + } = this.props + return ( - + ) } } diff --git a/ui/js/reducers/app.js b/ui/js/reducers/app.js index 13b1bb6b9..380c0f2c9 100644 --- a/ui/js/reducers/app.js +++ b/ui/js/reducers/app.js @@ -16,6 +16,12 @@ const defaultState = { hasSignature: false, } +reducers[types.DAEMON_READY] = function(state, action) { + return Object.assign({}, state, { + daemonReady: true, + }) +} + reducers[types.NAVIGATE] = function(state, action) { return Object.assign({}, state, { currentPath: action.data.path, diff --git a/ui/js/selectors/app.js b/ui/js/selectors/app.js index a269e98f2..e0d1a7a43 100644 --- a/ui/js/selectors/app.js +++ b/ui/js/selectors/app.js @@ -39,8 +39,7 @@ export const selectPageTitle = createSelector( case 'wallet': case 'send': case 'receive': - case 'claim': - case 'referral': + case 'rewards': return 'Wallet' case 'downloaded': return 'My Files' @@ -129,14 +128,12 @@ export const selectHeaderLinks = createSelector( case 'wallet': case 'send': case 'receive': - case 'claim': - case 'referral': + case 'rewards': return { 'wallet' : 'Overview', 'send' : 'Send', 'receive' : 'Receive', - 'claim' : 'Claim Beta Code', - 'referral' : 'Check Referral Credit', + 'rewards': 'Rewards', }; case 'downloaded': case 'published': diff --git a/ui/js/selectors/content.js b/ui/js/selectors/content.js index be38c21b3..87b91182d 100644 --- a/ui/js/selectors/content.js +++ b/ui/js/selectors/content.js @@ -96,3 +96,19 @@ export const shouldFetchPublishedContent = createSelector( return true } ) + +export const selectResolvingUris = createSelector( + _selectState, + (state) => state.resolvingUris || [] +) + +const selectResolvingUri = (state, props) => { + return selectResolvingUris(state).indexOf(props.uri) != -1 +} + +export const makeSelectResolvingUri = () => { + return createSelector( + selectResolvingUri, + (resolving) => resolving + ) +} diff --git a/ui/package.json b/ui/package.json index 8f7e754e1..ee8db2a1a 100644 --- a/ui/package.json +++ b/ui/package.json @@ -26,6 +26,7 @@ "intersection-observer": "^0.2.1", "mediaelement": "^2.23.4", "node-sass": "^3.8.0", + "plyr": "^2.0.12", "rc-progress": "^2.0.6", "react": "^15.4.0", "react-dom": "^15.4.0", @@ -34,8 +35,7 @@ "redux": "^3.6.0", "redux-logger": "^3.0.1", "redux-thunk": "^2.2.0", - "reselect": "^3.0.0", - "videostream": "^2.4.2" + "reselect": "^3.0.0" }, "devDependencies": { "babel": "^6.5.2",