Redux #115
22 changed files with 635 additions and 334 deletions
|
@ -1,5 +1,8 @@
|
||||||
import * as types from 'constants/action_types'
|
import * as types from 'constants/action_types'
|
||||||
import lbry from 'lbry'
|
import lbry from 'lbry'
|
||||||
|
import {
|
||||||
|
selectCurrentUri,
|
||||||
|
} from 'selectors/app'
|
||||||
|
|
||||||
export function doFetchUriAvailability(uri) {
|
export function doFetchUriAvailability(uri) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
|
@ -8,14 +11,29 @@ export function doFetchUriAvailability(uri) {
|
||||||
data: { uri }
|
data: { uri }
|
||||||
})
|
})
|
||||||
|
|
||||||
lbry.get_availability({ uri }, (availability) => {
|
const successCallback = (availability) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.FETCH_AVAILABILITY_COMPLETED',
|
type: types.FETCH_AVAILABILITY_COMPLETED,
|
||||||
data: {
|
data: {
|
||||||
availability,
|
availability,
|
||||||
uri,
|
uri,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const errorCallback = () => {
|
||||||
|
console.debug('error')
|
||||||
|
}
|
||||||
|
|
||||||
|
lbry.get_availability({ uri }, successCallback, errorCallback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doFetchCurrentUriAvailability() {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
const state = getState()
|
||||||
|
const uri = selectCurrentUri(state)
|
||||||
|
|
||||||
|
dispatch(doFetchUriAvailability(uri))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,9 @@ import {
|
||||||
import {
|
import {
|
||||||
doOpenModal,
|
doOpenModal,
|
||||||
} from 'actions/app'
|
} from 'actions/app'
|
||||||
|
import {
|
||||||
|
doFetchCostInfoForUri,
|
||||||
|
} from 'actions/cost_info'
|
||||||
import batchActions from 'util/batchActions'
|
import batchActions from 'util/batchActions'
|
||||||
|
|
||||||
export function doResolveUri(uri) {
|
export function doResolveUri(uri) {
|
||||||
|
@ -46,6 +49,8 @@ export function doResolveUri(uri) {
|
||||||
certificate,
|
certificate,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
dispatch(doFetchCostInfoForUri(uri))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,11 +4,8 @@ import {
|
||||||
} from 'selectors/app'
|
} from 'selectors/app'
|
||||||
import lbry from 'lbry'
|
import lbry from 'lbry'
|
||||||
|
|
||||||
export function doFetchCurrentUriCostInfo() {
|
export function doFetchCostInfoForUri(uri) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
const state = getState()
|
|
||||||
const uri = selectCurrentUri(state)
|
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.FETCH_COST_INFO_STARTED,
|
type: types.FETCH_COST_INFO_STARTED,
|
||||||
data: {
|
data: {
|
||||||
|
@ -28,3 +25,12 @@ export function doFetchCurrentUriCostInfo() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function doFetchCurrentUriCostInfo() {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
const state = getState()
|
||||||
|
const uri = selectCurrentUri(state)
|
||||||
|
|
||||||
|
dispatch(doFetchCostInfoForUri(uri))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,13 @@ import {
|
||||||
import {
|
import {
|
||||||
selectCurrentUriClaimOutpoint,
|
selectCurrentUriClaimOutpoint,
|
||||||
} from 'selectors/claims'
|
} from 'selectors/claims'
|
||||||
|
import {
|
||||||
|
doCloseModal,
|
||||||
|
} from 'actions/app'
|
||||||
|
|
||||||
|
const {
|
||||||
|
shell,
|
||||||
|
} = require('electron')
|
||||||
|
|
||||||
export function doFetchCurrentUriFileInfo() {
|
export function doFetchCurrentUriFileInfo() {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
|
@ -32,3 +39,40 @@ export function doFetchCurrentUriFileInfo() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function doOpenFileInShell(fileInfo) {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
shell.openItem(fileInfo.download_path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doOpenFileInFolder(fileInfo) {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
shell.showItemInFolder(fileInfo.download_path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function doDeleteFile(uri, fileInfo, deleteFromComputer) {
|
||||||
|
return function(dispatch, getState) {
|
||||||
|
dispatch({
|
||||||
|
type: types.DELETE_FILE_STARTED,
|
||||||
|
data: {
|
||||||
|
uri,
|
||||||
|
fileInfo,
|
||||||
|
deleteFromComputer,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const successCallback = () => {
|
||||||
|
dispatch({
|
||||||
|
type: types.DELETE_FILE_COMPLETED,
|
||||||
|
data: {
|
||||||
|
uri,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
dispatch(doCloseModal())
|
||||||
|
}
|
||||||
|
|
||||||
|
lbry.removeFile(fileInfo.outpoint, deleteFromComputer, successCallback)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -82,54 +82,6 @@ export let CreditAmount = React.createClass({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export let FilePrice = React.createClass({
|
|
||||||
_isMounted: false,
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
uri: React.PropTypes.string.isRequired,
|
|
||||||
look: React.PropTypes.oneOf(['indicator', 'plain']),
|
|
||||||
},
|
|
||||||
|
|
||||||
getDefaultProps: function() {
|
|
||||||
return {
|
|
||||||
look: 'indicator',
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillMount: function() {
|
|
||||||
this.setState({
|
|
||||||
cost: null,
|
|
||||||
isEstimate: null,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount: function() {
|
|
||||||
this._isMounted = true;
|
|
||||||
lbry.getCostInfo(this.props.uri).then(({cost, includesData}) => {
|
|
||||||
if (this._isMounted) {
|
|
||||||
this.setState({
|
|
||||||
cost: cost,
|
|
||||||
isEstimate: !includesData,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, (err) => {
|
|
||||||
// If we get an error looking up cost information, do nothing
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
this._isMounted = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
if (this.state.cost === null) {
|
|
||||||
return <span className={`credit-amount credit-amount--${this.props.look}`}>???</span>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <CreditAmount label={false} amount={this.state.cost} isEstimate={this.state.isEstimate} showFree={true} />
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var addressStyle = {
|
var addressStyle = {
|
||||||
fontFamily: '"Consolas", "Lucida Console", "Adobe Source Code Pro", monospace',
|
fontFamily: '"Consolas", "Lucida Console", "Adobe Source Code Pro", monospace',
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,16 +6,62 @@ import {
|
||||||
selectObscureNsfw,
|
selectObscureNsfw,
|
||||||
selectHidePrice,
|
selectHidePrice,
|
||||||
selectHasSignature,
|
selectHasSignature,
|
||||||
|
selectPlatform,
|
||||||
} from 'selectors/app'
|
} from 'selectors/app'
|
||||||
|
import {
|
||||||
|
makeSelectFileInfoForUri,
|
||||||
|
makeSelectDownloadingForUri,
|
||||||
|
makeSelectLoadingForUri,
|
||||||
|
} from 'selectors/file_info'
|
||||||
|
import {
|
||||||
|
makeSelectAvailabilityForUri,
|
||||||
|
} from 'selectors/availability'
|
||||||
|
import {
|
||||||
|
selectCurrentModal,
|
||||||
|
} from 'selectors/app'
|
||||||
|
import {
|
||||||
|
doCloseModal,
|
||||||
|
doOpenModal,
|
||||||
|
} from 'actions/app'
|
||||||
|
import {
|
||||||
|
doOpenFileInShell,
|
||||||
|
doOpenFileInFolder,
|
||||||
|
doDeleteFile,
|
||||||
|
} from 'actions/file_info'
|
||||||
|
import {
|
||||||
|
doWatchVideo,
|
||||||
|
} from 'actions/content'
|
||||||
import FileActions from './view'
|
import FileActions from './view'
|
||||||
|
|
||||||
const select = (state) => ({
|
const makeSelect = () => {
|
||||||
|
const selectFileInfoForUri = makeSelectFileInfoForUri()
|
||||||
|
const selectAvailabilityForUri = makeSelectAvailabilityForUri()
|
||||||
|
const selectDownloadingForUri = makeSelectDownloadingForUri()
|
||||||
|
const selectLoadingForUri = makeSelectLoadingForUri()
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
obscureNsfw: selectObscureNsfw(state),
|
obscureNsfw: selectObscureNsfw(state),
|
||||||
hidePrice: selectHidePrice(state),
|
hidePrice: selectHidePrice(state),
|
||||||
hasSignature: selectHasSignature(state),
|
hasSignature: selectHasSignature(state),
|
||||||
})
|
fileInfo: selectFileInfoForUri(state, props),
|
||||||
|
availability: selectAvailabilityForUri(state, props),
|
||||||
|
platform: selectPlatform(state),
|
||||||
|
modal: selectCurrentModal(state),
|
||||||
|
downloading: selectDownloadingForUri(state, props),
|
||||||
|
loading: selectLoadingForUri(state, props),
|
||||||
|
})
|
||||||
|
|
||||||
const perform = {
|
return select
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect(select, perform)(FileActions)
|
const perform = (dispatch) => ({
|
||||||
|
closeModal: () => dispatch(doCloseModal()),
|
||||||
|
openInFolder: (fileInfo) => dispatch(doOpenFileInFolder(fileInfo)),
|
||||||
|
openInShell: (fileInfo) => dispatch(doOpenFileInShell(fileInfo)),
|
||||||
|
affirmPurchase: () => console.log('affirm purchase'),
|
||||||
|
deleteFile: (fileInfo, deleteFromComputer) => dispatch(doDeleteFile(fileInfo, deleteFromComputer)),
|
||||||
|
openModal: (modal) => dispatch(doOpenModal(modal)),
|
||||||
|
downloadClick: () => dispatch(doWatchVideo()),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default connect(makeSelect, perform)(FileActions)
|
||||||
|
|
|
@ -1,149 +1,66 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbry from 'lbry';
|
import lbry from 'lbry';
|
||||||
import lbryuri from 'lbryuri';
|
import lbryuri from 'lbryuri';
|
||||||
import {Icon, FilePrice} from 'component/common';
|
import {Icon,} from 'component/common';
|
||||||
|
import FilePrice from 'component/filePrice'
|
||||||
import {Modal} from 'component/modal';
|
import {Modal} from 'component/modal';
|
||||||
import {FormField} from 'component/form';
|
import {FormField} from 'component/form';
|
||||||
import Link from 'component/link';
|
import Link from 'component/link';
|
||||||
import {ToolTip} from 'component/tooltip';
|
import {ToolTip} from 'component/tooltip';
|
||||||
import {DropDownMenu, DropDownMenuItem} from 'component/menu';
|
import {DropDownMenu, DropDownMenuItem} from 'component/menu';
|
||||||
|
|
||||||
const {shell} = require('electron');
|
class FileActionsRow extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
const FileActionsRow = React.createClass({
|
super(props)
|
||||||
_isMounted: false,
|
this.state = {
|
||||||
_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,
|
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() {
|
handleDeleteCheckboxClicked(event) {
|
||||||
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({
|
this.setState({
|
||||||
deleteChecked: event.target.checked,
|
deleteChecked: event.target.checked,
|
||||||
});
|
})
|
||||||
},
|
|
||||||
handleRevealClicked: function() {
|
|
||||||
if (this.state.fileInfo && this.state.fileInfo.download_path) {
|
|
||||||
shell.showItemInFolder(this.state.fileInfo.download_path);
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
handleRemoveClicked: function() {
|
render() {
|
||||||
this.setState({
|
const {
|
||||||
modal: 'confirmRemove',
|
fileInfo,
|
||||||
});
|
platform,
|
||||||
},
|
downloading,
|
||||||
handleRemoveConfirmed: function() {
|
loading,
|
||||||
lbry.removeFile(this.props.outpoint, this.state.deleteChecked);
|
uri,
|
||||||
this.setState({
|
deleteFile,
|
||||||
modal: null,
|
openInFolder,
|
||||||
fileInfo: false,
|
openInShell,
|
||||||
attemptingDownload: false
|
modal,
|
||||||
});
|
openModal,
|
||||||
},
|
affirmPurchase,
|
||||||
onAffirmPurchase: function() {
|
closeModal,
|
||||||
this.setState({
|
downloadClick,
|
||||||
affirmedPurchase: true,
|
} = this.props
|
||||||
modal: null
|
|
||||||
});
|
const {
|
||||||
this.tryDownload();
|
deleteChecked,
|
||||||
},
|
} = this.state
|
||||||
openMenu: function() {
|
|
||||||
this.setState({
|
const metadata = fileInfo ? fileInfo.metadata : null
|
||||||
menuOpen: !this.state.menuOpen,
|
|
||||||
});
|
if (!fileInfo)
|
||||||
},
|
|
||||||
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const openInFolderMessage = window.navigator.platform.startsWith('Mac') ? 'Open in Finder' : 'Open in Folder',
|
const openInFolderMessage = platform.startsWith('Mac') ? 'Open in Finder' : 'Open in Folder',
|
||||||
showMenu = !!this.state.fileInfo;
|
showMenu = Object.keys(fileInfo).length != 0;
|
||||||
|
|
||||||
let linkBlock;
|
let linkBlock;
|
||||||
if (this.state.fileInfo === false && !this.state.attemptingDownload) {
|
if (Object.keys(fileInfo).length == 0 && !downloading && !loading) {
|
||||||
linkBlock = <Link button="text" label="Download" icon="icon-download" onClick={this.onDownloadClick} />;
|
linkBlock = <Link button="text" label="Download" icon="icon-download" onClick={downloadClick} />;
|
||||||
} else if (this.state.attemptingDownload || (!this.state.fileInfo.completed && !this.state.fileInfo.isMine)) {
|
} else if (downloading || loading) {
|
||||||
const
|
const
|
||||||
progress = this.state.fileInfo ? this.state.fileInfo.written_bytes / this.state.fileInfo.total_bytes * 100 : 0,
|
progress = (fileInfo && fileInfo.written_bytes) ? fileInfo.written_bytes / fileInfo.total_bytes * 100 : 0,
|
||||||
label = this.state.fileInfo ? progress.toFixed(0) + '% complete' : 'Connecting...',
|
label = fileInfo ? progress.toFixed(0) + '% complete' : 'Connecting...',
|
||||||
labelWithIcon = <span className="button__content"><Icon icon="icon-download" /><span>{label}</span></span>;
|
labelWithIcon = <span className="button__content"><Icon icon="icon-download" /><span>{label}</span></span>;
|
||||||
|
|
||||||
linkBlock = (
|
linkBlock = (
|
||||||
|
@ -153,101 +70,253 @@ const FileActionsRow = React.createClass({
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
linkBlock = <Link label="Open" button="text" icon="icon-folder-open" onClick={this.onOpenClick} />;
|
linkBlock = <Link label="Open" button="text" icon="icon-folder-open" onClick={() => openInShell(fileInfo)} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uri = lbryuri.normalize(this.props.uri);
|
const title = metadata ? metadata.title : uri;
|
||||||
const title = this.props.metadata ? this.props.metadata.title : uri;
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{this.state.fileInfo !== null || this.state.fileInfo.isMine
|
{fileInfo !== null || fileInfo.isMine
|
||||||
? linkBlock
|
? linkBlock
|
||||||
: null}
|
: null}
|
||||||
{ showMenu ?
|
{ showMenu ?
|
||||||
<DropDownMenu>
|
<DropDownMenu>
|
||||||
<DropDownMenuItem key={0} onClick={this.handleRevealClicked} label={openInFolderMessage} />
|
<DropDownMenuItem key={0} onClick={() => openInFolder(fileInfo)} label={openInFolderMessage} />
|
||||||
<DropDownMenuItem key={1} onClick={this.handleRemoveClicked} label="Remove..." />
|
<DropDownMenuItem key={1} onClick={() => openModal('confirmRemove')} label="Remove..." />
|
||||||
</DropDownMenu> : '' }
|
</DropDownMenu> : '' }
|
||||||
<Modal type="confirm" isOpen={this.state.modal == 'affirmPurchase'}
|
<Modal type="confirm" isOpen={modal == 'affirmPurchase'}
|
||||||
contentLabel="Confirm Purchase" onConfirmed={this.onAffirmPurchase} onAborted={this.closeModal}>
|
contentLabel="Confirm Purchase" onConfirmed={affirmPurchase} onAborted={closeModal}>
|
||||||
Are you sure you'd like to buy <strong>{title}</strong> for <strong><FilePrice uri={uri} metadata={this.props.metadata} label={false} look="plain" /></strong> credits?
|
Are you sure you'd like to buy <strong>{title}</strong> for <strong><FilePrice uri={uri} look="plain" /></strong> credits?
|
||||||
</Modal>
|
</Modal>
|
||||||
<Modal isOpen={this.state.modal == 'notEnoughCredits'} contentLabel="Not enough credits"
|
<Modal isOpen={modal == 'notEnoughCredits'} contentLabel="Not enough credits"
|
||||||
onConfirmed={this.closeModal}>
|
onConfirmed={closeModal}>
|
||||||
You don't have enough LBRY credits to pay for this stream.
|
You don't have enough LBRY credits to pay for this stream.
|
||||||
</Modal>
|
</Modal>
|
||||||
<Modal isOpen={this.state.modal == 'timedOut'} contentLabel="Download failed"
|
<Modal isOpen={modal == 'timedOut'} contentLabel="Download failed"
|
||||||
onConfirmed={this.closeModal}>
|
onConfirmed={closeModal}>
|
||||||
LBRY was unable to download the stream <strong>{uri}</strong>.
|
LBRY was unable to download the stream <strong>{uri}</strong>.
|
||||||
</Modal>
|
</Modal>
|
||||||
<Modal isOpen={this.state.modal == 'confirmRemove'} contentLabel="Not enough credits"
|
<Modal isOpen={modal == 'confirmRemove'}
|
||||||
type="confirm" confirmButtonLabel="Remove" onConfirmed={this.handleRemoveConfirmed}
|
contentLabel="Not enough credits"
|
||||||
onAborted={this.closeModal}>
|
type="confirm"
|
||||||
|
confirmButtonLabel="Remove"
|
||||||
|
onConfirmed={() => deleteFile(uri, fileInfo, deleteChecked)}
|
||||||
|
onAborted={closeModal}>
|
||||||
<p>Are you sure you'd like to remove <cite>{title}</cite> from LBRY?</p>
|
<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>
|
<label><FormField type="checkbox" checked={deleteChecked} onClick={this.handleDeleteCheckboxClicked.bind(this)} /> Delete this file from my computer</label>
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
const FileActions = React.createClass({
|
// const FileActionsRow = React.createClass({
|
||||||
_isMounted: false,
|
// _isMounted: false,
|
||||||
_fileInfoSubscribeId: null,
|
// _fileInfoSubscribeId: null,
|
||||||
|
|
||||||
propTypes: {
|
// propTypes: {
|
||||||
uri: React.PropTypes.string,
|
// uri: React.PropTypes.string,
|
||||||
outpoint: React.PropTypes.string.isRequired,
|
// outpoint: React.PropTypes.string.isRequired,
|
||||||
metadata: React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.string]),
|
// metadata: React.PropTypes.oneOfType([React.PropTypes.object, React.PropTypes.string]),
|
||||||
contentType: React.PropTypes.string,
|
// contentType: React.PropTypes.string.isRequired,
|
||||||
},
|
// },
|
||||||
getInitialState: function() {
|
// getInitialState: function() {
|
||||||
return {
|
// 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 {
|
||||||
|
constructor(props) {
|
||||||
|
super(props)
|
||||||
|
this._isMounted = false
|
||||||
|
this._fileInfoSubscribeId = null
|
||||||
|
this.state = {
|
||||||
available: true,
|
available: true,
|
||||||
forceShowActions: false,
|
forceShowActions: false,
|
||||||
fileInfo: null,
|
fileInfo: null,
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
onShowFileActionsRowClicked: function() {
|
|
||||||
|
onShowFileActionsRowClicked() {
|
||||||
this.setState({
|
this.setState({
|
||||||
forceShowActions: true,
|
forceShowActions: true,
|
||||||
});
|
});
|
||||||
},
|
|
||||||
onFileInfoUpdate: function(fileInfo) {
|
|
||||||
if (this.isMounted) {
|
|
||||||
this.setState({
|
|
||||||
fileInfo: fileInfo,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
componentDidMount: function() {
|
|
||||||
this._isMounted = true;
|
|
||||||
this._fileInfoSubscribeId = lbry.fileInfoSubscribe(this.props.outpoint, this.onFileInfoUpdate);
|
|
||||||
|
|
||||||
lbry.get_availability({uri: this.props.uri}, (availability) => {
|
render() {
|
||||||
if (this._isMounted) {
|
const {
|
||||||
this.setState({
|
fileInfo,
|
||||||
available: availability > 0,
|
availability,
|
||||||
});
|
} = this.props
|
||||||
}
|
|
||||||
}, () => {
|
|
||||||
// Take any error to mean the file is unavailable
|
|
||||||
if (this._isMounted) {
|
|
||||||
this.setState({
|
|
||||||
available: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
this._isMounted = false;
|
|
||||||
if (this._fileInfoSubscribeId) {
|
|
||||||
lbry.fileInfoUnsubscribe(this.props.outpoint, this._fileInfoSubscribeId);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
render: function() {
|
|
||||||
const fileInfo = this.state.fileInfo;
|
|
||||||
if (fileInfo === null) {
|
if (fileInfo === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -255,18 +324,17 @@ const FileActions = React.createClass({
|
||||||
return (<section className="file-actions">
|
return (<section className="file-actions">
|
||||||
{
|
{
|
||||||
fileInfo || this.state.available || this.state.forceShowActions
|
fileInfo || this.state.available || this.state.forceShowActions
|
||||||
? <FileActionsRow outpoint={this.props.outpoint} metadata={this.props.metadata} uri={this.props.uri}
|
? <FileActionsRow {...this.props} />
|
||||||
contentType={this.props.contentType} />
|
|
||||||
: <div>
|
: <div>
|
||||||
<div className="button-set-item empty">Content unavailable.</div>
|
<div className="button-set-item empty">Content unavailable.</div>
|
||||||
<ToolTip label="Why?"
|
<ToolTip label="Why?"
|
||||||
body="The content on LBRY is hosted by its users. It appears there are no users connected that have this file at the moment."
|
body="The content on LBRY is hosted by its users. It appears there are no users connected that have this file at the moment."
|
||||||
className="button-set-item" />
|
className="button-set-item" />
|
||||||
<Link label="Try Anyway" onClick={this.onShowFileActionsRowClicked} className="button-text button-set-item" />
|
<Link label="Try Anyway" onClick={this.onShowFileActionsRowClicked.bind(this)} className="button-text button-set-item" />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</section>);
|
</section>);
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
export default FileActions
|
export default FileActions
|
||||||
|
|
|
@ -2,7 +2,8 @@ import React from 'react';
|
||||||
import lbry from 'lbry.js';
|
import lbry from 'lbry.js';
|
||||||
import lbryuri from 'lbryuri.js';
|
import lbryuri from 'lbryuri.js';
|
||||||
import Link from 'component/link';
|
import Link from 'component/link';
|
||||||
import {Thumbnail, TruncatedText, FilePrice} from 'component/common';
|
import {Thumbnail, TruncatedText,} from 'component/common';
|
||||||
|
import FilePrice from 'component/filePrice'
|
||||||
import UriIndicator from 'component/channel-indicator';
|
import UriIndicator from 'component/channel-indicator';
|
||||||
|
|
||||||
class FileCardStream extends React.Component {
|
class FileCardStream extends React.Component {
|
||||||
|
@ -72,7 +73,7 @@ class FileCardStream extends React.Component {
|
||||||
<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={this.props.uri} /></span> : null}
|
{ !this.props.hidePrice ? <span style={{float: "right"}}><FilePrice uri={lbryuri.normalize(this.props.uri)} /></span> : null}
|
||||||
<UriIndicator uri={uri} metadata={metadata} contentType={this.props.contentType}
|
<UriIndicator uri={uri} metadata={metadata} contentType={this.props.contentType}
|
||||||
hasSignature={this.props.hasSignature} signatureIsValid={this.props.signatureIsValid} />
|
hasSignature={this.props.hasSignature} signatureIsValid={this.props.signatureIsValid} />
|
||||||
</div>
|
</div>
|
||||||
|
|
22
ui/js/component/filePrice/index.js
Normal file
22
ui/js/component/filePrice/index.js
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
connect,
|
||||||
|
} from 'react-redux'
|
||||||
|
import {
|
||||||
|
makeSelectCostInfoForUri,
|
||||||
|
} from 'selectors/cost_info'
|
||||||
|
import FilePrice from './view'
|
||||||
|
|
||||||
|
const makeSelect = () => {
|
||||||
|
const selectCostInfoForUri = makeSelectCostInfoForUri()
|
||||||
|
const select = (state, props) => ({
|
||||||
|
costInfo: selectCostInfoForUri(state, props),
|
||||||
|
})
|
||||||
|
|
||||||
|
return select
|
||||||
|
}
|
||||||
|
|
||||||
|
const perform = (dispatch) => ({
|
||||||
|
})
|
||||||
|
|
||||||
|
export default connect(makeSelect, perform)(FilePrice)
|
21
ui/js/component/filePrice/view.jsx
Normal file
21
ui/js/component/filePrice/view.jsx
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
CreditAmount,
|
||||||
|
} from 'component/common'
|
||||||
|
|
||||||
|
const FilePrice = (props) => {
|
||||||
|
const {
|
||||||
|
costInfo,
|
||||||
|
look = 'indicator',
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const isEstimate = costInfo ? !costInfo.includesData : null
|
||||||
|
|
||||||
|
if (!costInfo) {
|
||||||
|
return <span className={`credit-amount credit-amount--${look}`}>???</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <CreditAmount label={false} amount={costInfo.cost} isEstimate={isEstimate} showFree={true} />
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FilePrice
|
|
@ -3,7 +3,8 @@ import lbry from 'lbry.js';
|
||||||
import lbryuri from 'lbryuri.js';
|
import lbryuri from 'lbryuri.js';
|
||||||
import Link from 'component/link';
|
import Link from 'component/link';
|
||||||
import FileActions from 'component/fileActions';
|
import FileActions from 'component/fileActions';
|
||||||
import {Thumbnail, TruncatedText, FilePrice} from 'component/common.js';
|
import {Thumbnail, TruncatedText,} from 'component/common.js';
|
||||||
|
import FilePrice from 'component/filePrice'
|
||||||
import UriIndicator from 'component/channel-indicator.js';
|
import UriIndicator from 'component/channel-indicator.js';
|
||||||
|
|
||||||
/*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*/
|
||||||
|
|
|
@ -16,8 +16,6 @@ import {
|
||||||
selectLoadingCurrentUri,
|
selectLoadingCurrentUri,
|
||||||
selectCurrentUriFileReadyToPlay,
|
selectCurrentUriFileReadyToPlay,
|
||||||
selectCurrentUriIsPlaying,
|
selectCurrentUriIsPlaying,
|
||||||
} from 'selectors/content'
|
|
||||||
import {
|
|
||||||
selectCurrentUriFileInfo,
|
selectCurrentUriFileInfo,
|
||||||
selectDownloadingCurrentUri,
|
selectDownloadingCurrentUri,
|
||||||
} from 'selectors/file_info'
|
} from 'selectors/file_info'
|
||||||
|
|
|
@ -56,6 +56,8 @@ export const DOWNLOADING_COMPLETED = 'DOWNLOADING_COMPLETED'
|
||||||
export const PLAY_VIDEO_STARTED = 'PLAY_VIDEO_STARTED'
|
export const PLAY_VIDEO_STARTED = 'PLAY_VIDEO_STARTED'
|
||||||
export const FETCH_AVAILABILITY_STARTED = 'FETCH_AVAILABILITY_STARTED'
|
export const FETCH_AVAILABILITY_STARTED = 'FETCH_AVAILABILITY_STARTED'
|
||||||
export const FETCH_AVAILABILITY_COMPLETED = 'FETCH_AVAILABILITY_COMPLETED'
|
export const FETCH_AVAILABILITY_COMPLETED = 'FETCH_AVAILABILITY_COMPLETED'
|
||||||
|
export const DELETE_FILE_STARTED = 'DELETE_FILE_STARTED'
|
||||||
|
export const DELETE_FILE_COMPLETED = 'DELETE_FILE_COMPLETED'
|
||||||
|
|
||||||
// Search
|
// Search
|
||||||
export const SEARCH_STARTED = 'SEARCH_STARTED'
|
export const SEARCH_STARTED = 'SEARCH_STARTED'
|
||||||
|
|
|
@ -301,7 +301,7 @@ lbry.getMyClaims = function(callback) {
|
||||||
|
|
||||||
lbry.removeFile = function(outpoint, deleteTargetFile=true, callback) {
|
lbry.removeFile = function(outpoint, deleteTargetFile=true, callback) {
|
||||||
this._removedFiles.push(outpoint);
|
this._removedFiles.push(outpoint);
|
||||||
this._updateFileInfoSubscribers(outpoint);
|
// this._updateFileInfoSubscribers(outpoint);
|
||||||
|
|
||||||
lbry.file_delete({
|
lbry.file_delete({
|
||||||
outpoint: outpoint,
|
outpoint: outpoint,
|
||||||
|
|
|
@ -6,9 +6,9 @@ import Video from 'component/video'
|
||||||
import {
|
import {
|
||||||
TruncatedText,
|
TruncatedText,
|
||||||
Thumbnail,
|
Thumbnail,
|
||||||
FilePrice,
|
BusyMessage,
|
||||||
BusyMessage
|
} from 'component/common';
|
||||||
} from 'component/common.js';
|
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/channel-indicator.js';
|
||||||
|
@ -121,7 +121,7 @@ let FilePage = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
const metadata = this.props.metadata,
|
const metadata = this.props.metadata,
|
||||||
title = metadata ? this.props.metadata.title : this.props.uri,
|
title = metadata ? this.props.metadata.title : this.props.uri,
|
||||||
uriIndicator = <UriIndicator uri={this.props.uri} hasSignature={this.props.hasSignature} signatureIsValid={this.props.signatureIsValid} />;
|
uriIndicator = <UriIndicator uri={uri} hasSignature={hasSignature} signatureIsValid={signatureIsValid} />
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="main--single-column">
|
<main className="main--single-column">
|
||||||
|
@ -134,7 +134,7 @@ let FilePage = React.createClass({
|
||||||
<div className="card__inner">
|
<div className="card__inner">
|
||||||
<div className="card__title-identity">
|
<div className="card__title-identity">
|
||||||
{this.state.isDownloaded === false
|
{this.state.isDownloaded === false
|
||||||
? <span style={{float: "right"}}><FilePrice uri={this.props.uri} metadata={metadata} /></span>
|
? <span style={{float: "right"}}><FilePrice uri={lbryuri.normalize(uri)} metadata={metadata} /></span>
|
||||||
: null}
|
: null}
|
||||||
<h1>{title}</h1>
|
<h1>{title}</h1>
|
||||||
<div className="card__subtitle">
|
<div className="card__subtitle">
|
||||||
|
@ -289,3 +289,5 @@ let ShowPage = React.createClass({
|
||||||
return <main className="main--single-column">{innerContent}</main>;
|
return <main className="main--single-column">{innerContent}</main>;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export default ShowPage;
|
||||||
|
|
|
@ -96,36 +96,6 @@ reducers[types.FETCH_PUBLISHED_CONTENT_COMPLETED] = function(state, action) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
reducers[types.LOADING_VIDEO_STARTED] = function(state, action) {
|
|
||||||
const {
|
|
||||||
uri,
|
|
||||||
} = action.data
|
|
||||||
const newLoading = Object.assign({}, state.loading)
|
|
||||||
const newByUri = Object.assign({}, newLoading.byUri)
|
|
||||||
|
|
||||||
newByUri[uri] = true
|
|
||||||
newLoading.byUri = newByUri
|
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
loading: newLoading,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
reducers[types.LOADING_VIDEO_FAILED] = function(state, action) {
|
|
||||||
const {
|
|
||||||
uri,
|
|
||||||
} = action.data
|
|
||||||
const newLoading = Object.assign({}, state.loading)
|
|
||||||
const newByUri = Object.assign({}, newLoading.byUri)
|
|
||||||
|
|
||||||
delete newByUri[uri]
|
|
||||||
newLoading.byUri = newByUri
|
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
|
||||||
loading: newLoading,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function reducer(state = defaultState, action) {
|
export default function reducer(state = defaultState, action) {
|
||||||
const handler = reducers[action.type];
|
const handler = reducers[action.type];
|
||||||
if (handler) return handler(state, action);
|
if (handler) return handler(state, action);
|
||||||
|
|
|
@ -41,13 +41,20 @@ reducers[types.DOWNLOADING_STARTED] = function(state, action) {
|
||||||
} = action.data
|
} = action.data
|
||||||
const newByUri = Object.assign({}, state.byUri)
|
const newByUri = Object.assign({}, state.byUri)
|
||||||
const newDownloading = Object.assign({}, state.downloading)
|
const newDownloading = Object.assign({}, state.downloading)
|
||||||
|
const newDownloadingByUri = Object.assign({}, newDownloading.byUri)
|
||||||
|
const newLoading = Object.assign({}, state.loading)
|
||||||
|
const newLoadingByUri = Object.assign({}, newLoading)
|
||||||
|
|
||||||
newDownloading[uri] = true
|
newDownloadingByUri[uri] = true
|
||||||
|
newDownloading.byUri = newDownloadingByUri
|
||||||
newByUri[uri] = fileInfo
|
newByUri[uri] = fileInfo
|
||||||
|
delete newLoadingByUri[uri]
|
||||||
|
newLoading.byUri = newLoadingByUri
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
downloading: newDownloading,
|
downloading: newDownloading,
|
||||||
byUri: newByUri,
|
byUri: newByUri,
|
||||||
|
loading: newLoading,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,13 +82,78 @@ reducers[types.DOWNLOADING_COMPLETED] = function(state, action) {
|
||||||
} = action.data
|
} = action.data
|
||||||
const newByUri = Object.assign({}, state.byUri)
|
const newByUri = Object.assign({}, state.byUri)
|
||||||
const newDownloading = Object.assign({}, state.downloading)
|
const newDownloading = Object.assign({}, state.downloading)
|
||||||
|
const newDownloadingByUri = Object.assign({}, newDownloading.byUri)
|
||||||
|
|
||||||
newByUri[uri] = fileInfo
|
newByUri[uri] = fileInfo
|
||||||
delete newDownloading[uri]
|
delete newDownloadingByUri[uri]
|
||||||
|
newDownloading.byUri = newDownloadingByUri
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
byUri: newByUri,
|
byUri: newByUri,
|
||||||
downloading: newDownloading
|
downloading: newDownloading,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.DELETE_FILE_STARTED] = function(state, action) {
|
||||||
|
const {
|
||||||
|
uri,
|
||||||
|
} = action.data
|
||||||
|
const newDeleting = Object.assign({}, state.deleting)
|
||||||
|
const newByUri = Object.assign({}, newDeleting.byUri)
|
||||||
|
|
||||||
|
newByUri[uri] = true
|
||||||
|
newDeleting.byUri = newByUri
|
||||||
|
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
deleting: newDeleting,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.DELETE_FILE_COMPLETED] = function(state, action) {
|
||||||
|
const {
|
||||||
|
uri,
|
||||||
|
} = action.data
|
||||||
|
const newDeleting = Object.assign({}, state.deleting)
|
||||||
|
const newDeletingByUri = Object.assign({}, newDeleting.byUri)
|
||||||
|
const newByUri = Object.assign({}, state.byUri)
|
||||||
|
|
||||||
|
delete newDeletingByUri[uri]
|
||||||
|
newDeleting.byUri = newDeletingByUri
|
||||||
|
delete newByUri[uri]
|
||||||
|
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
deleting: newDeleting,
|
||||||
|
byUri: newByUri,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.LOADING_VIDEO_STARTED] = function(state, action) {
|
||||||
|
const {
|
||||||
|
uri,
|
||||||
|
} = action.data
|
||||||
|
const newLoading = Object.assign({}, state.loading)
|
||||||
|
const newByUri = Object.assign({}, newLoading.byUri)
|
||||||
|
|
||||||
|
newByUri[uri] = true
|
||||||
|
newLoading.byUri = newByUri
|
||||||
|
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
loading: newLoading,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
reducers[types.LOADING_VIDEO_FAILED] = function(state, action) {
|
||||||
|
const {
|
||||||
|
uri,
|
||||||
|
} = action.data
|
||||||
|
const newLoading = Object.assign({}, state.loading)
|
||||||
|
const newByUri = Object.assign({}, newLoading.byUri)
|
||||||
|
|
||||||
|
delete newByUri[uri]
|
||||||
|
newLoading.byUri = newByUri
|
||||||
|
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
loading: newLoading,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
import {
|
import {
|
||||||
createSelector,
|
createSelector,
|
||||||
} from 'reselect'
|
} from 'reselect'
|
||||||
|
import {
|
||||||
|
selectDaemonReady,
|
||||||
|
selectCurrentPage,
|
||||||
|
selectCurrentUri,
|
||||||
|
} from 'selectors/app'
|
||||||
|
|
||||||
const _selectState = state => state.availability
|
const _selectState = state => state.availability
|
||||||
|
|
||||||
|
@ -40,3 +45,30 @@ export const makeSelectFetchingAvailabilityForUri = () => {
|
||||||
(fetching) => fetching
|
(fetching) => fetching
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const selectFetchingAvailabilityForCurrentUri = createSelector(
|
||||||
|
selectCurrentUri,
|
||||||
|
selectFetchingAvailabilityByUri,
|
||||||
|
(uri, byUri) => byUri[uri]
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectAvailabilityForCurrentUri = createSelector(
|
||||||
|
selectCurrentUri,
|
||||||
|
selectAvailabilityByUri,
|
||||||
|
(uri, byUri) => byUri[uri]
|
||||||
|
)
|
||||||
|
|
||||||
|
export const shouldFetchCurrentUriAvailability = createSelector(
|
||||||
|
selectDaemonReady,
|
||||||
|
selectCurrentPage,
|
||||||
|
selectFetchingAvailabilityForCurrentUri,
|
||||||
|
selectAvailabilityForCurrentUri,
|
||||||
|
(daemonReady, page, fetching, availability) => {
|
||||||
|
if (!daemonReady) return false
|
||||||
|
if (page != 'show') return false
|
||||||
|
if (fetching) return false
|
||||||
|
if (availability) return false
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
|
@ -4,14 +4,6 @@ import {
|
||||||
selectCurrentPage,
|
selectCurrentPage,
|
||||||
selectCurrentUri,
|
selectCurrentUri,
|
||||||
} from 'selectors/app'
|
} from 'selectors/app'
|
||||||
import {
|
|
||||||
selectCurrentUriCostInfo,
|
|
||||||
selectFetchingCurrentUriCostInfo,
|
|
||||||
} from 'selectors/cost_info'
|
|
||||||
import {
|
|
||||||
selectCurrentUriFileInfo,
|
|
||||||
selectFetchingCurrentUriFileInfo,
|
|
||||||
} from 'selectors/file_info'
|
|
||||||
|
|
||||||
export const _selectState = state => state.content || {}
|
export const _selectState = state => state.content || {}
|
||||||
|
|
||||||
|
@ -50,13 +42,6 @@ export const selectFetchingFileInfos = createSelector(
|
||||||
(state) => state.fetchingFileInfos || {}
|
(state) => state.fetchingFileInfos || {}
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO make this smarter so it doesn't start playing and immediately freeze
|
|
||||||
// while downloading more.
|
|
||||||
export const selectCurrentUriFileReadyToPlay = createSelector(
|
|
||||||
selectCurrentUriFileInfo,
|
|
||||||
(fileInfo) => (fileInfo || {}).written_bytes > 0
|
|
||||||
)
|
|
||||||
|
|
||||||
export const selectFetchingDownloadedContent = createSelector(
|
export const selectFetchingDownloadedContent = createSelector(
|
||||||
_selectState,
|
_selectState,
|
||||||
(state) => !!state.fetchingDownloadedContent
|
(state) => !!state.fetchingDownloadedContent
|
||||||
|
@ -97,22 +82,6 @@ export const selectPublishedContent = createSelector(
|
||||||
(state) => state.publishedContent || {}
|
(state) => state.publishedContent || {}
|
||||||
)
|
)
|
||||||
|
|
||||||
export const selectLoading = createSelector(
|
|
||||||
_selectState,
|
|
||||||
(state) => state.loading || {}
|
|
||||||
)
|
|
||||||
|
|
||||||
export const selectLoadingByUri = createSelector(
|
|
||||||
selectLoading,
|
|
||||||
(loading) => loading.byUri || {}
|
|
||||||
)
|
|
||||||
|
|
||||||
export const selectLoadingCurrentUri = createSelector(
|
|
||||||
selectLoadingByUri,
|
|
||||||
selectCurrentUri,
|
|
||||||
(byUri, uri) => !!byUri[uri]
|
|
||||||
)
|
|
||||||
|
|
||||||
export const shouldFetchPublishedContent = createSelector(
|
export const shouldFetchPublishedContent = createSelector(
|
||||||
selectDaemonReady,
|
selectDaemonReady,
|
||||||
selectCurrentPage,
|
selectCurrentPage,
|
||||||
|
|
|
@ -42,3 +42,13 @@ export const shouldFetchCurrentUriCostInfo = createSelector(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const selectCostInfoForUri = (state, props) => {
|
||||||
|
return selectAllCostInfoByUri(state)[props.uri]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const makeSelectCostInfoForUri = () => {
|
||||||
|
return createSelector(
|
||||||
|
selectCostInfoForUri,
|
||||||
|
(costInfo) => costInfo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -54,7 +54,11 @@ export const selectDownloadingCurrentUri = createSelector(
|
||||||
export const selectCurrentUriIsDownloaded = createSelector(
|
export const selectCurrentUriIsDownloaded = createSelector(
|
||||||
selectCurrentUriFileInfo,
|
selectCurrentUriFileInfo,
|
||||||
(fileInfo) => {
|
(fileInfo) => {
|
||||||
return fileInfo && (fileInfo.written_bytes > 0 || fileInfo.completed)
|
if (!fileInfo) return false
|
||||||
|
if (!fileInfo.completed) return false
|
||||||
|
if (!fileInfo.written_bytes > 0) return false
|
||||||
|
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -82,3 +86,50 @@ export const makeSelectFileInfoForUri = () => {
|
||||||
(fileInfo) => fileInfo
|
(fileInfo) => fileInfo
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const selectDownloadingForUri = (state, props) => {
|
||||||
|
const byUri = selectDownloadingByUri(state)
|
||||||
|
return byUri[props.uri]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const makeSelectDownloadingForUri = () => {
|
||||||
|
return createSelector(
|
||||||
|
selectDownloadingForUri,
|
||||||
|
(downloadingForUri) => !!downloadingForUri
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const selectLoading = createSelector(
|
||||||
|
_selectState,
|
||||||
|
(state) => state.loading || {}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectLoadingByUri = createSelector(
|
||||||
|
selectLoading,
|
||||||
|
(loading) => loading.byUri || {}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const selectLoadingCurrentUri = createSelector(
|
||||||
|
selectLoadingByUri,
|
||||||
|
selectCurrentUri,
|
||||||
|
(byUri, uri) => !!byUri[uri]
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO make this smarter so it doesn't start playing and immediately freeze
|
||||||
|
// while downloading more.
|
||||||
|
export const selectCurrentUriFileReadyToPlay = createSelector(
|
||||||
|
selectCurrentUriFileInfo,
|
||||||
|
(fileInfo) => (fileInfo || {}).written_bytes > 0
|
||||||
|
)
|
||||||
|
|
||||||
|
const selectLoadingForUri = (state, props) => {
|
||||||
|
const byUri = selectLoadingByUri(state)
|
||||||
|
return byUri[props.uri]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const makeSelectLoadingForUri = () => {
|
||||||
|
return createSelector(
|
||||||
|
selectLoadingForUri,
|
||||||
|
(loading) => !!loading
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,9 @@ import {
|
||||||
import {
|
import {
|
||||||
shouldFetchCurrentUriCostInfo,
|
shouldFetchCurrentUriCostInfo,
|
||||||
} from 'selectors/cost_info'
|
} from 'selectors/cost_info'
|
||||||
|
import {
|
||||||
|
shouldFetchCurrentUriAvailability,
|
||||||
|
} from 'selectors/availability'
|
||||||
import {
|
import {
|
||||||
doFetchTransactions,
|
doFetchTransactions,
|
||||||
doGetNewAddress,
|
doGetNewAddress,
|
||||||
|
@ -28,6 +31,9 @@ import {
|
||||||
import {
|
import {
|
||||||
doFetchCurrentUriCostInfo,
|
doFetchCurrentUriCostInfo,
|
||||||
} from 'actions/cost_info'
|
} from 'actions/cost_info'
|
||||||
|
import {
|
||||||
|
doFetchCurrentUriAvailability,
|
||||||
|
} from 'actions/availability'
|
||||||
|
|
||||||
const triggers = []
|
const triggers = []
|
||||||
|
|
||||||
|
@ -66,6 +72,11 @@ triggers.push({
|
||||||
action: doFetchCurrentUriCostInfo,
|
action: doFetchCurrentUriCostInfo,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
triggers.push({
|
||||||
|
selector: shouldFetchCurrentUriAvailability,
|
||||||
|
action: doFetchCurrentUriAvailability,
|
||||||
|
})
|
||||||
|
|
||||||
const runTriggers = function() {
|
const runTriggers = function() {
|
||||||
triggers.forEach(function(trigger) {
|
triggers.forEach(function(trigger) {
|
||||||
const state = app.store.getState();
|
const state = app.store.getState();
|
||||||
|
|
Loading…
Reference in a new issue