Add missing DAEMON_READY reducer #107
22 changed files with 198 additions and 284 deletions
2
ui/dist/index.html
vendored
2
ui/dist/index.html
vendored
|
@ -7,7 +7,7 @@
|
||||||
<link href='https://fonts.googleapis.com/css?family=Raleway:600,300' rel='stylesheet' type='text/css'>
|
<link href='https://fonts.googleapis.com/css?family=Raleway:600,300' rel='stylesheet' type='text/css'>
|
||||||
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400italic,600italic,600' rel='stylesheet' type='text/css'>
|
<link href='https://fonts.googleapis.com/css?family=Source+Sans+Pro:400,400italic,600italic,600' rel='stylesheet' type='text/css'>
|
||||||
<link href="./css/all.css" rel="stylesheet" type="text/css" media="screen,print" />
|
<link href="./css/all.css" rel="stylesheet" type="text/css" media="screen,print" />
|
||||||
<link href="./js/mediaelement/mediaelementplayer.css" rel="stylesheet" type="text/css" />
|
<link rel="stylesheet" href="https://cdn.plyr.io/2.0.12/plyr.css">
|
||||||
<link rel="icon" type="image/png" href="./img/fav/favicon-32x32.png" sizes="32x32">
|
<link rel="icon" type="image/png" href="./img/fav/favicon-32x32.png" sizes="32x32">
|
||||||
<link rel="icon" type="image/png" href="./img/fav/favicon-194x194.png" sizes="194x194">
|
<link rel="icon" type="image/png" href="./img/fav/favicon-194x194.png" sizes="194x194">
|
||||||
<link rel="icon" type="image/png" href="./img/fav/favicon-96x96.png" sizes="96x96">
|
<link rel="icon" type="image/png" href="./img/fav/favicon-96x96.png" sizes="96x96">
|
||||||
|
|
|
@ -240,7 +240,11 @@ export function doLoadVideo() {
|
||||||
})
|
})
|
||||||
|
|
||||||
lbry.get({ uri }).then(streamInfo => {
|
lbry.get({ uri }).then(streamInfo => {
|
||||||
if (streamInfo === null || typeof streamInfo !== 'object') {
|
const timeout = streamInfo === null ||
|
||||||
|
typeof streamInfo !== 'object' ||
|
||||||
|
streamInfo.error == 'Timeout'
|
||||||
|
|
||||||
|
if(timeout) {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.LOADING_VIDEO_FAILED,
|
type: types.LOADING_VIDEO_FAILED,
|
||||||
data: { uri }
|
data: { uri }
|
||||||
|
@ -264,6 +268,12 @@ export function doWatchVideo() {
|
||||||
const alreadyDownloading = !!downloadingByUri[uri]
|
const alreadyDownloading = !!downloadingByUri[uri]
|
||||||
const { cost } = costInfo
|
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
|
// we already fully downloaded the file
|
||||||
if (fileInfo && fileInfo.completed) {
|
if (fileInfo && fileInfo.completed) {
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
|
|
|
@ -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 <span className="empty">Anonymous</span>;
|
|
||||||
}
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<span>
|
|
||||||
{channelUri} {' '}
|
|
||||||
{ !this.props.signatureIsValid ?
|
|
||||||
<Icon icon={icon} className={`channel-indicator__icon channel-indicator__icon--${modifier}`} /> :
|
|
||||||
'' }
|
|
||||||
</span>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default UriIndicator;
|
|
|
@ -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 = <Link button="text" label="Download" icon="icon-download" onClick={this.onDownloadClick} />;
|
|
||||||
// } 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 = <span className="button__content"><Icon icon="icon-download" /><span>{label}</span></span>;
|
|
||||||
|
|
||||||
// linkBlock = (
|
|
||||||
// <div className="faux-button-block file-actions__download-status-bar button-set-item">
|
|
||||||
// <div className="faux-button-block file-actions__download-status-bar-overlay" style={{ width: progress + '%' }}>{labelWithIcon}</div>
|
|
||||||
// {labelWithIcon}
|
|
||||||
// </div>
|
|
||||||
// );
|
|
||||||
// } else {
|
|
||||||
// linkBlock = <Link label="Open" button="text" icon="icon-folder-open" onClick={this.onOpenClick} />;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const uri = lbryuri.normalize(this.props.uri);
|
|
||||||
// const title = this.props.metadata ? this.props.metadata.title : uri;
|
|
||||||
// return (
|
|
||||||
// <div>
|
|
||||||
// {this.state.fileInfo !== null || this.state.fileInfo.isMine
|
|
||||||
// ? linkBlock
|
|
||||||
// : null}
|
|
||||||
// { showMenu ?
|
|
||||||
// <DropDownMenu>
|
|
||||||
// <DropDownMenuItem key={0} onClick={this.handleRevealClicked} label={openInFolderMessage} />
|
|
||||||
// <DropDownMenuItem key={1} onClick={this.handleRemoveClicked} label="Remove..." />
|
|
||||||
// </DropDownMenu> : '' }
|
|
||||||
// <Modal type="confirm" isOpen={this.state.modal == 'affirmPurchase'}
|
|
||||||
// contentLabel="Confirm Purchase" onConfirmed={this.onAffirmPurchase} onAborted={this.closeModal}>
|
|
||||||
// Are you sure you'd like to buy <strong>{title}</strong> for <strong><FilePrice uri={uri} look="plain" /></strong> credits?
|
|
||||||
// </Modal>
|
|
||||||
// <Modal isOpen={this.state.modal == 'notEnoughCredits'} contentLabel="Not enough credits"
|
|
||||||
// onConfirmed={this.closeModal}>
|
|
||||||
// You don't have enough LBRY credits to pay for this stream.
|
|
||||||
// </Modal>
|
|
||||||
// <Modal isOpen={this.state.modal == 'timedOut'} contentLabel="Download failed"
|
|
||||||
// onConfirmed={this.closeModal}>
|
|
||||||
// LBRY was unable to download the stream <strong>{uri}</strong>.
|
|
||||||
// </Modal>
|
|
||||||
// <Modal isOpen={this.state.modal == 'confirmRemove'} contentLabel="Not enough credits"
|
|
||||||
// type="confirm" confirmButtonLabel="Remove" onConfirmed={this.handleRemoveConfirmed}
|
|
||||||
// onAborted={this.closeModal}>
|
|
||||||
// <p>Are you sure you'd like to remove <cite>{title}</cite> from LBRY?</p>
|
|
||||||
|
|
||||||
// <label><FormField type="checkbox" checked={this.state.deleteChecked} onClick={this.handleDeleteCheckboxClicked} /> Delete this file from my computer</label>
|
|
||||||
// </Modal>
|
|
||||||
// </div>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
class FileActions extends React.Component {
|
class FileActions extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props)
|
super(props)
|
||||||
|
|
|
@ -17,6 +17,9 @@ import {
|
||||||
import {
|
import {
|
||||||
makeSelectFileInfoForUri,
|
makeSelectFileInfoForUri,
|
||||||
} from 'selectors/file_info'
|
} from 'selectors/file_info'
|
||||||
|
import {
|
||||||
|
makeSelectResolvingUri,
|
||||||
|
} from 'selectors/content'
|
||||||
import FileCardStream from './view'
|
import FileCardStream from './view'
|
||||||
|
|
||||||
const makeSelect = () => {
|
const makeSelect = () => {
|
||||||
|
@ -24,6 +27,8 @@ const makeSelect = () => {
|
||||||
const selectFileInfoForUri = makeSelectFileInfoForUri()
|
const selectFileInfoForUri = makeSelectFileInfoForUri()
|
||||||
const selectMetadataForUri = makeSelectMetadataForUri()
|
const selectMetadataForUri = makeSelectMetadataForUri()
|
||||||
const selectSourceForUri = makeSelectSourceForUri()
|
const selectSourceForUri = makeSelectSourceForUri()
|
||||||
|
const selectResolvingUri = makeSelectResolvingUri()
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
claim: selectClaimForUri(state, props),
|
claim: selectClaimForUri(state, props),
|
||||||
fileInfo: selectFileInfoForUri(state, props),
|
fileInfo: selectFileInfoForUri(state, props),
|
||||||
|
@ -32,6 +37,7 @@ const makeSelect = () => {
|
||||||
hasSignature: false,
|
hasSignature: false,
|
||||||
metadata: selectMetadataForUri(state, props),
|
metadata: selectMetadataForUri(state, props),
|
||||||
source: selectSourceForUri(state, props),
|
source: selectSourceForUri(state, props),
|
||||||
|
isResolvingUri: selectResolvingUri(state, props),
|
||||||
})
|
})
|
||||||
|
|
||||||
return select
|
return select
|
||||||
|
|
|
@ -4,7 +4,7 @@ import lbryuri from 'lbryuri.js';
|
||||||
import Link from 'component/link';
|
import Link from 'component/link';
|
||||||
import {Thumbnail, TruncatedText,} from 'component/common';
|
import {Thumbnail, TruncatedText,} from 'component/common';
|
||||||
import FilePrice from 'component/filePrice'
|
import FilePrice from 'component/filePrice'
|
||||||
import UriIndicator from 'component/channel-indicator';
|
import UriIndicator from 'component/uriIndicator';
|
||||||
|
|
||||||
class FileCardStream extends React.Component {
|
class FileCardStream extends React.Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -56,37 +56,43 @@ class FileCardStream extends React.Component {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (!this.props.metadata) {
|
const {
|
||||||
// return null
|
metadata,
|
||||||
// }
|
isResolvingUri,
|
||||||
|
navigate,
|
||||||
|
hidePrice,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
const uri = lbryuri.normalize(this.props.uri);
|
const uri = lbryuri.normalize(this.props.uri);
|
||||||
const metadata = this.props.metadata;
|
|
||||||
const isConfirmed = !!metadata;
|
const isConfirmed = !!metadata;
|
||||||
const title = isConfirmed ? metadata.title : uri;
|
const title = isConfirmed ? metadata.title : uri;
|
||||||
const obscureNsfw = this.props.obscureNsfw && isConfirmed && metadata.nsfw;
|
const obscureNsfw = this.props.obscureNsfw && isConfirmed && metadata.nsfw;
|
||||||
const primaryUrl = 'show=' + uri;
|
const primaryUrl = 'show=' + uri;
|
||||||
|
let description = ""
|
||||||
|
if (isConfirmed) {
|
||||||
|
description = metadata.description
|
||||||
|
} else if (isResolvingUri) {
|
||||||
|
description = "Loading..."
|
||||||
|
} else {
|
||||||
|
description = <span className="empty">This file is pending confirmation</span>
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={ 'card card--small card--link ' + (obscureNsfw ? 'card--obscured ' : '') } onMouseEnter={this.handleMouseOver.bind(this)} onMouseLeave={this.handleMouseOut.bind(this)}>
|
<section className={ 'card card--small card--link ' + (obscureNsfw ? 'card--obscured ' : '') } onMouseEnter={this.handleMouseOver.bind(this)} onMouseLeave={this.handleMouseOut.bind(this)}>
|
||||||
<div className="card__inner">
|
<div className="card__inner">
|
||||||
<a href="#" onClick={() => this.props.navigate(primaryUrl)} className="card__link">
|
<a href="#" onClick={() => navigate(primaryUrl)} className="card__link">
|
||||||
<div className="card__title-identity">
|
<div className="card__title-identity">
|
||||||
<h5 title={title}><TruncatedText lines={1}>{title}</TruncatedText></h5>
|
<h5 title={title}><TruncatedText lines={1}>{title}</TruncatedText></h5>
|
||||||
<div className="card__subtitle">
|
<div className="card__subtitle">
|
||||||
{ !this.props.hidePrice ? <span style={{float: "right"}}><FilePrice uri={lbryuri.normalize(this.props.uri)} /></span> : null}
|
{ !hidePrice ? <span style={{float: "right"}}><FilePrice uri={uri} /></span> : null}
|
||||||
<UriIndicator uri={uri} metadata={metadata} contentType={this.props.contentType}
|
<UriIndicator uri={uri} />
|
||||||
hasSignature={this.props.hasSignature} signatureIsValid={this.props.signatureIsValid} />
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{metadata &&
|
{metadata &&
|
||||||
<div className="card__media" style={{ backgroundImage: "url('" + metadata.thumbnail + "')" }}></div>
|
<div className="card__media" style={{ backgroundImage: "url('" + metadata.thumbnail + "')" }}></div>
|
||||||
}
|
}
|
||||||
<div className="card__content card__subtext card__subtext--two-lines">
|
<div className="card__content card__subtext card__subtext--two-lines">
|
||||||
<TruncatedText lines={2}>
|
<TruncatedText lines={2}>{description}</TruncatedText>
|
||||||
{isConfirmed
|
|
||||||
? metadata.description
|
|
||||||
: <span className="empty">This file is pending confirmation.</span>}
|
|
||||||
</TruncatedText>
|
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
{this.state.showNsfwHelp && this.state.hovered
|
{this.state.showNsfwHelp && this.state.hovered
|
||||||
|
|
|
@ -20,6 +20,9 @@ import {
|
||||||
import {
|
import {
|
||||||
selectObscureNsfw,
|
selectObscureNsfw,
|
||||||
} from 'selectors/app'
|
} from 'selectors/app'
|
||||||
|
import {
|
||||||
|
makeSelectResolvingUri,
|
||||||
|
} from 'selectors/content'
|
||||||
import FileTileStream from './view'
|
import FileTileStream from './view'
|
||||||
|
|
||||||
const makeSelect = () => {
|
const makeSelect = () => {
|
||||||
|
@ -29,6 +32,7 @@ const makeSelect = () => {
|
||||||
const selectAvailabilityForUri = makeSelectAvailabilityForUri()
|
const selectAvailabilityForUri = makeSelectAvailabilityForUri()
|
||||||
const selectMetadataForUri = makeSelectMetadataForUri()
|
const selectMetadataForUri = makeSelectMetadataForUri()
|
||||||
const selectSourceForUri = makeSelectSourceForUri()
|
const selectSourceForUri = makeSelectSourceForUri()
|
||||||
|
const selectResolvingUri = makeSelectResolvingUri()
|
||||||
|
|
||||||
const select = (state, props) => ({
|
const select = (state, props) => ({
|
||||||
claim: selectClaimForUri(state, props),
|
claim: selectClaimForUri(state, props),
|
||||||
|
@ -38,6 +42,7 @@ const makeSelect = () => {
|
||||||
obscureNsfw: selectObscureNsfw(state),
|
obscureNsfw: selectObscureNsfw(state),
|
||||||
metadata: selectMetadataForUri(state, props),
|
metadata: selectMetadataForUri(state, props),
|
||||||
source: selectSourceForUri(state, props),
|
source: selectSourceForUri(state, props),
|
||||||
|
isResolvingUri: selectResolvingUri(state, props),
|
||||||
})
|
})
|
||||||
|
|
||||||
return select
|
return select
|
||||||
|
|
|
@ -5,7 +5,7 @@ import Link from 'component/link';
|
||||||
import FileActions from 'component/fileActions';
|
import FileActions from 'component/fileActions';
|
||||||
import {Thumbnail, TruncatedText,} from 'component/common.js';
|
import {Thumbnail, TruncatedText,} from 'component/common.js';
|
||||||
import FilePrice from 'component/filePrice'
|
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*/
|
/*should be merged into FileTile once FileTile is refactored to take a single id*/
|
||||||
class FileTileStream extends React.Component {
|
class FileTileStream extends React.Component {
|
||||||
|
@ -63,7 +63,9 @@ class FileTileStream extends React.Component {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
metadata,
|
metadata,
|
||||||
|
isResolvingUri,
|
||||||
navigate,
|
navigate,
|
||||||
|
hidePrice,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
const uri = lbryuri.normalize(this.props.uri);
|
const uri = lbryuri.normalize(this.props.uri);
|
||||||
|
@ -71,6 +73,15 @@ class FileTileStream extends React.Component {
|
||||||
const title = isConfirmed ? metadata.title : uri;
|
const title = isConfirmed ? metadata.title : uri;
|
||||||
const obscureNsfw = this.props.obscureNsfw && isConfirmed && metadata.nsfw;
|
const obscureNsfw = this.props.obscureNsfw && isConfirmed && metadata.nsfw;
|
||||||
|
|
||||||
|
let description = ""
|
||||||
|
if (isConfirmed) {
|
||||||
|
description = metadata.description
|
||||||
|
} else if (isResolvingUri) {
|
||||||
|
description = "Loading..."
|
||||||
|
} else {
|
||||||
|
description = <span className="empty">This file is pending confirmation</span>
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={ 'file-tile card ' + (obscureNsfw ? 'card--obscured ' : '') } onMouseEnter={this.handleMouseOver.bind(this)} onMouseLeave={this.handleMouseOut.bind(this)}>
|
<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={"row-fluid card__inner file-tile__row"}>
|
||||||
|
@ -101,11 +112,7 @@ class FileTileStream extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<p className="file-tile__description">
|
<p className="file-tile__description">
|
||||||
<TruncatedText lines={2}>
|
<TruncatedText lines={2}>{description}</TruncatedText>
|
||||||
{isConfirmed
|
|
||||||
? metadata.description
|
|
||||||
: <span className="empty">This file is pending confirmation.</span>}
|
|
||||||
</TruncatedText>
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Icon} from './common.js';
|
import lbry from 'lbry'
|
||||||
import Modal from '../component/modal.js';
|
import {Icon} from 'component/common';
|
||||||
import rewards from '../rewards.js';
|
import Modal from 'component/modal';
|
||||||
|
import rewards from 'rewards';
|
||||||
|
import Link from 'component/link'
|
||||||
|
|
||||||
export let RewardLink = React.createClass({
|
export let RewardLink = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
|
|
@ -6,7 +6,7 @@ import ReportPage from 'page/report.js';
|
||||||
import StartPage from 'page/start.js';
|
import StartPage from 'page/start.js';
|
||||||
import WalletPage from 'page/wallet';
|
import WalletPage from 'page/wallet';
|
||||||
import ShowPage from 'page/showPage';
|
import ShowPage from 'page/showPage';
|
||||||
import PublishPage from 'page/publish.js';
|
import PublishPage from 'page/publish';
|
||||||
import DiscoverPage from 'page/discover';
|
import DiscoverPage from 'page/discover';
|
||||||
import SplashScreen from 'component/splash.js';
|
import SplashScreen from 'component/splash.js';
|
||||||
import RewardsPage from 'page/rewards.js';
|
import RewardsPage from 'page/rewards.js';
|
||||||
|
|
20
ui/js/component/uriIndicator/index.js
Normal file
20
ui/js/component/uriIndicator/index.js
Normal file
|
@ -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)
|
44
ui/js/component/uriIndicator/view.jsx
Normal file
44
ui/js/component/uriIndicator/view.jsx
Normal file
|
@ -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 <span className="empty">Anonymous</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (
|
||||||
|
<span>
|
||||||
|
{channelUri} {' '}
|
||||||
|
{ !signatureIsValid ?
|
||||||
|
<Icon icon={icon} className={`channel-indicator__icon channel-indicator__icon--${modifier}`} /> :
|
||||||
|
'' }
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default UriIndicator;
|
|
@ -84,7 +84,7 @@ const FeaturedCategory = (props) => {
|
||||||
{category &&
|
{category &&
|
||||||
<ToolTip label="What's this?" body={communityCategoryToolTipText} className="tooltip--header" />}
|
<ToolTip label="What's this?" body={communityCategoryToolTipText} className="tooltip--header" />}
|
||||||
</h3>
|
</h3>
|
||||||
{names && names.map(name => <FileTile key={name} displayStyle="card" uri={name} />)}
|
{names && names.map(name => <FileTile key={name} displayStyle="card" uri={lbryuri.normalize(name)} />)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
17
ui/js/page/publish/index.js
Normal file
17
ui/js/page/publish/index.js
Normal file
|
@ -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)
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbry from '../lbry.js';
|
import lbry from 'lbry';
|
||||||
import {FormField, FormRow} from '../component/form.js';
|
import {FormField, FormRow} from 'component/form.js';
|
||||||
import Link from '../component/link';
|
import Link from 'component/link';
|
||||||
import rewards from '../rewards.js';
|
import rewards from 'rewards';
|
||||||
import lbryio from '../lbryio.js';
|
import lbryio from 'lbryio';
|
||||||
import Modal from '../component/modal.js';
|
import Modal from 'component/modal';
|
||||||
|
|
||||||
var PublishPage = React.createClass({
|
var PublishPage = React.createClass({
|
||||||
_requiredFields: ['meta_title', 'name', 'bid', 'tos_agree'],
|
_requiredFields: ['meta_title', 'name', 'bid', 'tos_agree'],
|
||||||
|
@ -148,7 +148,7 @@ var PublishPage = React.createClass({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
handlePublishStartedConfirmed: function() {
|
handlePublishStartedConfirmed: function() {
|
||||||
window.location = "?published";
|
this.props.navigate('published')
|
||||||
},
|
},
|
||||||
handlePublishError: function(error) {
|
handlePublishError: function(error) {
|
||||||
this.setState({
|
this.setState({
|
|
@ -1,10 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbry from '../lbry.js';
|
import lbry from 'lbry';
|
||||||
import lbryio from '../lbryio.js';
|
import lbryio from 'lbryio';
|
||||||
import {CreditAmount, Icon} from '../component/common.js';
|
import {CreditAmount, Icon} from 'component/common.js';
|
||||||
import rewards from '../rewards.js';
|
import rewards from 'rewards';
|
||||||
import Modal from '../component/modal.js';
|
import Modal from 'component/modal';
|
||||||
import {RewardLink} from '../component/reward-link.js';
|
import {RewardLink} from 'component/reward-link';
|
||||||
|
|
||||||
const RewardTile = React.createClass({
|
const RewardTile = React.createClass({
|
||||||
propTypes: {
|
propTypes: {
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
import FilePrice from 'component/filePrice'
|
import FilePrice from 'component/filePrice'
|
||||||
import FileActions from 'component/fileActions';
|
import FileActions from 'component/fileActions';
|
||||||
import Link from 'component/link';
|
import Link from 'component/link';
|
||||||
import UriIndicator from 'component/channel-indicator.js';
|
import UriIndicator from 'component/uriIndicator';
|
||||||
|
|
||||||
const FormatItem = (props) => {
|
const FormatItem = (props) => {
|
||||||
const {
|
const {
|
||||||
|
@ -105,7 +105,7 @@ const ShowPage = (props) => {
|
||||||
{ uriLookupComplete ?
|
{ uriLookupComplete ?
|
||||||
<div>
|
<div>
|
||||||
<div className="card__subtitle">
|
<div className="card__subtitle">
|
||||||
<UriIndicator uri={uri} hasSignature={hasSignature} signatureIsValid={signatureIsValid} />
|
<UriIndicator uri={uri} />
|
||||||
</div>
|
</div>
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
<FileActions uri={uri} outpoint={outpoint} metadata={metadata} contentType={contentType} />
|
<FileActions uri={uri} outpoint={outpoint} metadata={metadata} contentType={contentType} />
|
||||||
|
|
|
@ -11,8 +11,7 @@ import lbryio from 'lbryio';
|
||||||
import rewards from 'rewards';
|
import rewards from 'rewards';
|
||||||
import LoadScreen from 'component/load_screen'
|
import LoadScreen from 'component/load_screen'
|
||||||
|
|
||||||
const fs = require('fs');
|
const plyr = require('plyr')
|
||||||
const VideoStream = require('videostream');
|
|
||||||
|
|
||||||
class WatchLink extends React.Component {
|
class WatchLink extends React.Component {
|
||||||
confirmPurchaseClick() {
|
confirmPurchaseClick() {
|
||||||
|
@ -164,18 +163,22 @@ class VideoPlayer extends React.PureComponent {
|
||||||
const elem = this.refs.video
|
const elem = this.refs.video
|
||||||
const {
|
const {
|
||||||
downloadPath,
|
downloadPath,
|
||||||
|
contentType,
|
||||||
} = this.props
|
} = this.props
|
||||||
const mediaFile = {
|
const players = plyr.setup(elem)
|
||||||
createReadStream: (opts) =>
|
players[0].play()
|
||||||
fs.createReadStream(downloadPath, opts)
|
|
||||||
}
|
|
||||||
const videostream = VideoStream(mediaFile, elem)
|
|
||||||
elem.play()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const {
|
||||||
|
downloadPath,
|
||||||
|
contentType,
|
||||||
|
} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<video controls id="video" ref="video"></video>
|
<video controls id="video" ref="video">
|
||||||
|
<source src={downloadPath} type={contentType} />
|
||||||
|
</video>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,12 @@ const defaultState = {
|
||||||
hasSignature: false,
|
hasSignature: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reducers[types.DAEMON_READY] = function(state, action) {
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
daemonReady: true,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
reducers[types.NAVIGATE] = function(state, action) {
|
reducers[types.NAVIGATE] = function(state, action) {
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
currentPath: action.data.path,
|
currentPath: action.data.path,
|
||||||
|
|
|
@ -39,8 +39,7 @@ export const selectPageTitle = createSelector(
|
||||||
case 'wallet':
|
case 'wallet':
|
||||||
case 'send':
|
case 'send':
|
||||||
case 'receive':
|
case 'receive':
|
||||||
case 'claim':
|
case 'rewards':
|
||||||
case 'referral':
|
|
||||||
return 'Wallet'
|
return 'Wallet'
|
||||||
case 'downloaded':
|
case 'downloaded':
|
||||||
return 'My Files'
|
return 'My Files'
|
||||||
|
@ -129,14 +128,12 @@ export const selectHeaderLinks = createSelector(
|
||||||
case 'wallet':
|
case 'wallet':
|
||||||
case 'send':
|
case 'send':
|
||||||
case 'receive':
|
case 'receive':
|
||||||
case 'claim':
|
case 'rewards':
|
||||||
case 'referral':
|
|
||||||
return {
|
return {
|
||||||
'wallet' : 'Overview',
|
'wallet' : 'Overview',
|
||||||
'send' : 'Send',
|
'send' : 'Send',
|
||||||
'receive' : 'Receive',
|
'receive' : 'Receive',
|
||||||
'claim' : 'Claim Beta Code',
|
'rewards': 'Rewards',
|
||||||
'referral' : 'Check Referral Credit',
|
|
||||||
};
|
};
|
||||||
case 'downloaded':
|
case 'downloaded':
|
||||||
case 'published':
|
case 'published':
|
||||||
|
|
|
@ -96,3 +96,19 @@ export const shouldFetchPublishedContent = createSelector(
|
||||||
return true
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
"intersection-observer": "^0.2.1",
|
"intersection-observer": "^0.2.1",
|
||||||
"mediaelement": "^2.23.4",
|
"mediaelement": "^2.23.4",
|
||||||
"node-sass": "^3.8.0",
|
"node-sass": "^3.8.0",
|
||||||
|
"plyr": "^2.0.12",
|
||||||
"rc-progress": "^2.0.6",
|
"rc-progress": "^2.0.6",
|
||||||
"react": "^15.4.0",
|
"react": "^15.4.0",
|
||||||
"react-dom": "^15.4.0",
|
"react-dom": "^15.4.0",
|
||||||
|
@ -34,8 +35,7 @@
|
||||||
"redux": "^3.6.0",
|
"redux": "^3.6.0",
|
||||||
"redux-logger": "^3.0.1",
|
"redux-logger": "^3.0.1",
|
||||||
"redux-thunk": "^2.2.0",
|
"redux-thunk": "^2.2.0",
|
||||||
"reselect": "^3.0.0",
|
"reselect": "^3.0.0"
|
||||||
"videostream": "^2.4.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel": "^6.5.2",
|
"babel": "^6.5.2",
|
||||||
|
|
Loading…
Reference in a new issue