diff --git a/ui/js/actions/availability.js b/ui/js/actions/availability.js index d5d14fa19..74c85eaa3 100644 --- a/ui/js/actions/availability.js +++ b/ui/js/actions/availability.js @@ -1,5 +1,8 @@ import * as types from 'constants/action_types' import lbry from 'lbry' +import { + selectCurrentUri, +} from 'selectors/app' export function doFetchUriAvailability(uri) { return function(dispatch, getState) { @@ -8,14 +11,29 @@ export function doFetchUriAvailability(uri) { data: { uri } }) - lbry.get_availability({ uri }, (availability) => { + const successCallback = (availability) => { dispatch({ - type: types.FETCH_AVAILABILITY_COMPLETED', + type: types.FETCH_AVAILABILITY_COMPLETED, data: { availability, 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)) } } diff --git a/ui/js/actions/content.js b/ui/js/actions/content.js index 76f7d5af8..9e9f90e3d 100644 --- a/ui/js/actions/content.js +++ b/ui/js/actions/content.js @@ -23,6 +23,9 @@ import { import { doOpenModal, } from 'actions/app' +import { + doFetchCostInfoForUri, +} from 'actions/cost_info' import batchActions from 'util/batchActions' export function doResolveUri(uri) { @@ -46,6 +49,8 @@ export function doResolveUri(uri) { certificate, } }) + + dispatch(doFetchCostInfoForUri(uri)) }) } } diff --git a/ui/js/actions/cost_info.js b/ui/js/actions/cost_info.js index 2d513e578..278f1d7b4 100644 --- a/ui/js/actions/cost_info.js +++ b/ui/js/actions/cost_info.js @@ -4,11 +4,8 @@ import { } from 'selectors/app' import lbry from 'lbry' -export function doFetchCurrentUriCostInfo() { +export function doFetchCostInfoForUri(uri) { return function(dispatch, getState) { - const state = getState() - const uri = selectCurrentUri(state) - dispatch({ type: types.FETCH_COST_INFO_STARTED, 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)) + } +} + diff --git a/ui/js/actions/file_info.js b/ui/js/actions/file_info.js index 9005e6f01..77763352b 100644 --- a/ui/js/actions/file_info.js +++ b/ui/js/actions/file_info.js @@ -6,6 +6,13 @@ import { import { selectCurrentUriClaimOutpoint, } from 'selectors/claims' +import { + doCloseModal, +} from 'actions/app' + +const { + shell, +} = require('electron') export function doFetchCurrentUriFileInfo() { 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) + } +} diff --git a/ui/js/component/common.js b/ui/js/component/common.js index 8da20ca8e..aac91004a 100644 --- a/ui/js/component/common.js +++ b/ui/js/component/common.js @@ -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 ???; - } - - return - } -}); - var addressStyle = { fontFamily: '"Consolas", "Lucida Console", "Adobe Source Code Pro", monospace', }; diff --git a/ui/js/component/fileActions/index.js b/ui/js/component/fileActions/index.js index d17e57af8..19e66dfdb 100644 --- a/ui/js/component/fileActions/index.js +++ b/ui/js/component/fileActions/index.js @@ -6,16 +6,62 @@ import { selectObscureNsfw, selectHidePrice, selectHasSignature, + selectPlatform, } 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' -const select = (state) => ({ - obscureNsfw: selectObscureNsfw(state), - hidePrice: selectHidePrice(state), - hasSignature: selectHasSignature(state), -}) +const makeSelect = () => { + const selectFileInfoForUri = makeSelectFileInfoForUri() + const selectAvailabilityForUri = makeSelectAvailabilityForUri() + const selectDownloadingForUri = makeSelectDownloadingForUri() + const selectLoadingForUri = makeSelectLoadingForUri() -const perform = { + const select = (state, props) => ({ + obscureNsfw: selectObscureNsfw(state), + hidePrice: selectHidePrice(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), + }) + + 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) diff --git a/ui/js/component/fileActions/view.jsx b/ui/js/component/fileActions/view.jsx index 96267dbd8..8b880502d 100644 --- a/ui/js/component/fileActions/view.jsx +++ b/ui/js/component/fileActions/view.jsx @@ -1,149 +1,66 @@ import React from 'react'; import lbry from 'lbry'; 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 {FormField} from 'component/form'; import Link from 'component/link'; import {ToolTip} from 'component/tooltip'; import {DropDownMenu, DropDownMenuItem} from 'component/menu'; -const {shell} = require('electron'); - -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, +class FileActionsRow extends React.Component { + constructor(props) { + super(props) + this.state = { 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) { + } + + handleDeleteCheckboxClicked(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) + }) + } + + render() { + const { + fileInfo, + platform, + downloading, + loading, + uri, + deleteFile, + openInFolder, + openInShell, + modal, + openModal, + affirmPurchase, + closeModal, + downloadClick, + } = this.props + + const { + deleteChecked, + } = this.state + + const metadata = fileInfo ? fileInfo.metadata : null + + if (!fileInfo) { return null; } - const openInFolderMessage = window.navigator.platform.startsWith('Mac') ? 'Open in Finder' : 'Open in Folder', - showMenu = !!this.state.fileInfo; + const openInFolderMessage = platform.startsWith('Mac') ? 'Open in Finder' : 'Open in Folder', + showMenu = Object.keys(fileInfo).length != 0; let linkBlock; - if (this.state.fileInfo === false && !this.state.attemptingDownload) { - linkBlock = ; - } else if (this.state.attemptingDownload || (!this.state.fileInfo.completed && !this.state.fileInfo.isMine)) { + if (Object.keys(fileInfo).length == 0 && !downloading && !loading) { + linkBlock = ; + } else if (downloading || loading) { 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...', + progress = (fileInfo && fileInfo.written_bytes) ? fileInfo.written_bytes / fileInfo.total_bytes * 100 : 0, + label = fileInfo ? progress.toFixed(0) + '% complete' : 'Connecting...', labelWithIcon = {label}; linkBlock = ( @@ -153,101 +70,253 @@ const FileActionsRow = React.createClass({ ); } else { - linkBlock = ; + linkBlock = openInShell(fileInfo)} />; } - const uri = lbryuri.normalize(this.props.uri); - const title = this.props.metadata ? this.props.metadata.title : uri; + const title = metadata ? metadata.title : uri; return (
- {this.state.fileInfo !== null || this.state.fileInfo.isMine + {fileInfo !== null || fileInfo.isMine ? linkBlock : null} { showMenu ? - - + openInFolder(fileInfo)} label={openInFolderMessage} /> + openModal('confirmRemove')} label="Remove..." /> : '' } - - Are you sure you'd like to buy {title} for credits? + + 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}. - + deleteFile(uri, fileInfo, deleteChecked)} + onAborted={closeModal}>

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

- +
); } -}); +} -const FileActions = React.createClass({ - _isMounted: false, - _fileInfoSubscribeId: null, +// 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, - }, - getInitialState: function() { - return { +// 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) + this._isMounted = false + this._fileInfoSubscribeId = null + this.state = { available: true, forceShowActions: false, fileInfo: null, } - }, - onShowFileActionsRowClicked: function() { + } + + onShowFileActionsRowClicked() { this.setState({ 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); + } + + render() { + const { + fileInfo, + availability, + } = this.props - lbry.get_availability({uri: this.props.uri}, (availability) => { - if (this._isMounted) { - this.setState({ - available: availability > 0, - }); - } - }, () => { - // 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) { return null; } @@ -255,18 +324,17 @@ const FileActions = React.createClass({ return (
{ fileInfo || this.state.available || this.state.forceShowActions - ? + ? :
Content unavailable.
- +
}
); } -}); +} export default FileActions diff --git a/ui/js/component/fileCardStream/view.jsx b/ui/js/component/fileCardStream/view.jsx index d2bd9a1c3..a3b6d9b56 100644 --- a/ui/js/component/fileCardStream/view.jsx +++ b/ui/js/component/fileCardStream/view.jsx @@ -2,7 +2,8 @@ import React from 'react'; import lbry from 'lbry.js'; import lbryuri from 'lbryuri.js'; 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'; class FileCardStream extends React.Component { @@ -72,7 +73,7 @@ class FileCardStream extends React.Component {
{title}
- { !this.props.hidePrice ? : null} + { !this.props.hidePrice ? : null}
diff --git a/ui/js/component/filePrice/index.js b/ui/js/component/filePrice/index.js new file mode 100644 index 000000000..c5a9497d0 --- /dev/null +++ b/ui/js/component/filePrice/index.js @@ -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) diff --git a/ui/js/component/filePrice/view.jsx b/ui/js/component/filePrice/view.jsx new file mode 100644 index 000000000..17b830bf2 --- /dev/null +++ b/ui/js/component/filePrice/view.jsx @@ -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 ???; + } + + return +} + +export default FilePrice diff --git a/ui/js/component/fileTileStream/view.jsx b/ui/js/component/fileTileStream/view.jsx index f5a0c218a..fd3a2b32c 100644 --- a/ui/js/component/fileTileStream/view.jsx +++ b/ui/js/component/fileTileStream/view.jsx @@ -3,7 +3,8 @@ import lbry from 'lbry.js'; import lbryuri from 'lbryuri.js'; import Link from 'component/link'; 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'; /*should be merged into FileTile once FileTile is refactored to take a single id*/ diff --git a/ui/js/component/video/index.js b/ui/js/component/video/index.js index e258e55f7..94978f702 100644 --- a/ui/js/component/video/index.js +++ b/ui/js/component/video/index.js @@ -16,8 +16,6 @@ import { selectLoadingCurrentUri, selectCurrentUriFileReadyToPlay, selectCurrentUriIsPlaying, -} from 'selectors/content' -import { selectCurrentUriFileInfo, selectDownloadingCurrentUri, } from 'selectors/file_info' diff --git a/ui/js/constants/action_types.js b/ui/js/constants/action_types.js index cb5657d59..f00739ade 100644 --- a/ui/js/constants/action_types.js +++ b/ui/js/constants/action_types.js @@ -56,6 +56,8 @@ export const DOWNLOADING_COMPLETED = 'DOWNLOADING_COMPLETED' export const PLAY_VIDEO_STARTED = 'PLAY_VIDEO_STARTED' export const FETCH_AVAILABILITY_STARTED = 'FETCH_AVAILABILITY_STARTED' 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 export const SEARCH_STARTED = 'SEARCH_STARTED' diff --git a/ui/js/lbry.js b/ui/js/lbry.js index d3c02e231..630f6db63 100644 --- a/ui/js/lbry.js +++ b/ui/js/lbry.js @@ -301,7 +301,7 @@ lbry.getMyClaims = function(callback) { lbry.removeFile = function(outpoint, deleteTargetFile=true, callback) { this._removedFiles.push(outpoint); - this._updateFileInfoSubscribers(outpoint); + // this._updateFileInfoSubscribers(outpoint); lbry.file_delete({ outpoint: outpoint, diff --git a/ui/js/page/showPage/view.jsx b/ui/js/page/showPage/view.jsx index 9ba48cbf3..2a3498bf9 100644 --- a/ui/js/page/showPage/view.jsx +++ b/ui/js/page/showPage/view.jsx @@ -6,9 +6,9 @@ import Video from 'component/video' import { TruncatedText, Thumbnail, - FilePrice, - BusyMessage -} from 'component/common.js'; + BusyMessage, +} from 'component/common'; +import FilePrice from 'component/filePrice' import FileActions from 'component/fileActions'; import Link from 'component/link'; import UriIndicator from 'component/channel-indicator.js'; @@ -121,7 +121,7 @@ let FilePage = React.createClass({ render: function() { const metadata = this.props.metadata, title = metadata ? this.props.metadata.title : this.props.uri, - uriIndicator = ; + uriIndicator = return (
@@ -134,14 +134,14 @@ let FilePage = React.createClass({
{this.state.isDownloaded === false - ? + ? : null}

{title}

{ this.props.channelUri ? {uriIndicator} : uriIndicator} -
+
@@ -288,4 +288,6 @@ let ShowPage = React.createClass({ return
{innerContent}
; } -}); \ No newline at end of file +}); + +export default ShowPage; diff --git a/ui/js/reducers/content.js b/ui/js/reducers/content.js index a5e4eba74..6c36daf0c 100644 --- a/ui/js/reducers/content.js +++ b/ui/js/reducers/content.js @@ -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) { const handler = reducers[action.type]; if (handler) return handler(state, action); diff --git a/ui/js/reducers/file_info.js b/ui/js/reducers/file_info.js index 1467f0ee8..2c98cf048 100644 --- a/ui/js/reducers/file_info.js +++ b/ui/js/reducers/file_info.js @@ -41,13 +41,20 @@ reducers[types.DOWNLOADING_STARTED] = function(state, action) { } = action.data const newByUri = Object.assign({}, state.byUri) 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 + delete newLoadingByUri[uri] + newLoading.byUri = newLoadingByUri return Object.assign({}, state, { downloading: newDownloading, byUri: newByUri, + loading: newLoading, }) } @@ -75,13 +82,78 @@ reducers[types.DOWNLOADING_COMPLETED] = function(state, action) { } = action.data const newByUri = Object.assign({}, state.byUri) const newDownloading = Object.assign({}, state.downloading) + const newDownloadingByUri = Object.assign({}, newDownloading.byUri) newByUri[uri] = fileInfo - delete newDownloading[uri] + delete newDownloadingByUri[uri] + newDownloading.byUri = newDownloadingByUri return Object.assign({}, state, { 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, }) } diff --git a/ui/js/selectors/availability.js b/ui/js/selectors/availability.js index 0588c1528..7aeaa6330 100644 --- a/ui/js/selectors/availability.js +++ b/ui/js/selectors/availability.js @@ -1,6 +1,11 @@ import { createSelector, } from 'reselect' +import { + selectDaemonReady, + selectCurrentPage, + selectCurrentUri, +} from 'selectors/app' const _selectState = state => state.availability @@ -40,3 +45,30 @@ export const makeSelectFetchingAvailabilityForUri = () => { (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 + } +) diff --git a/ui/js/selectors/content.js b/ui/js/selectors/content.js index c8c7a1386..be38c21b3 100644 --- a/ui/js/selectors/content.js +++ b/ui/js/selectors/content.js @@ -4,14 +4,6 @@ import { selectCurrentPage, selectCurrentUri, } from 'selectors/app' -import { - selectCurrentUriCostInfo, - selectFetchingCurrentUriCostInfo, -} from 'selectors/cost_info' -import { - selectCurrentUriFileInfo, - selectFetchingCurrentUriFileInfo, -} from 'selectors/file_info' export const _selectState = state => state.content || {} @@ -50,13 +42,6 @@ export const selectFetchingFileInfos = createSelector( (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( _selectState, (state) => !!state.fetchingDownloadedContent @@ -97,22 +82,6 @@ export const selectPublishedContent = createSelector( (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( selectDaemonReady, selectCurrentPage, diff --git a/ui/js/selectors/cost_info.js b/ui/js/selectors/cost_info.js index 55a1416ea..d250aaf19 100644 --- a/ui/js/selectors/cost_info.js +++ b/ui/js/selectors/cost_info.js @@ -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 + ) +} diff --git a/ui/js/selectors/file_info.js b/ui/js/selectors/file_info.js index c26b5dcc8..79b28c1d6 100644 --- a/ui/js/selectors/file_info.js +++ b/ui/js/selectors/file_info.js @@ -54,7 +54,11 @@ export const selectDownloadingCurrentUri = createSelector( export const selectCurrentUriIsDownloaded = createSelector( selectCurrentUriFileInfo, (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 ) } + +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 + ) +} diff --git a/ui/js/triggers.js b/ui/js/triggers.js index 0d35f97d7..41d0e0763 100644 --- a/ui/js/triggers.js +++ b/ui/js/triggers.js @@ -13,6 +13,9 @@ import { import { shouldFetchCurrentUriCostInfo, } from 'selectors/cost_info' +import { + shouldFetchCurrentUriAvailability, +} from 'selectors/availability' import { doFetchTransactions, doGetNewAddress, @@ -28,6 +31,9 @@ import { import { doFetchCurrentUriCostInfo, } from 'actions/cost_info' +import { + doFetchCurrentUriAvailability, +} from 'actions/availability' const triggers = [] @@ -66,6 +72,11 @@ triggers.push({ action: doFetchCurrentUriCostInfo, }) +triggers.push({ + selector: shouldFetchCurrentUriAvailability, + action: doFetchCurrentUriAvailability, +}) + const runTriggers = function() { triggers.forEach(function(trigger) { const state = app.store.getState();