diff --git a/ui/js/actions/app.js b/ui/js/actions/app.js index 073ae9099..d2c7679f3 100644 --- a/ui/js/actions/app.js +++ b/ui/js/actions/app.js @@ -5,6 +5,7 @@ import { selectUpgradeDownloadDir, selectUpgradeDownloadItem, selectUpgradeFilename, + selectPageTitle, } from 'selectors/app' const {remote, ipcRenderer, shell} = require('electron'); @@ -14,11 +15,17 @@ const {download} = remote.require('electron-dl'); const fs = remote.require('fs'); export function doNavigate(path) { - return { - type: types.NAVIGATE, - data: { - path: path - } + return function(dispatch, getState) { + dispatch({ + type: types.NAVIGATE, + data: { + path, + } + }) + + const state = getState() + const pageTitle = selectPageTitle(state) + window.document.title = pageTitle } } diff --git a/ui/js/actions/content.js b/ui/js/actions/content.js index 9e9f90e3d..0e5c28086 100644 --- a/ui/js/actions/content.js +++ b/ui/js/actions/content.js @@ -20,6 +20,9 @@ import { import { selectCurrentResolvedUriClaimOutpoint, } from 'selectors/content' +import { + selectClaimsByUri, +} from 'selectors/claims' import { doOpenModal, } from 'actions/app' @@ -67,6 +70,15 @@ export function doFetchDownloadedContent() { lbry.file_list().then((fileInfos) => { const myClaimOutpoints = myClaimInfos.map(({txid, nout}) => txid + ':' + nout); + fileInfos.forEach(fileInfo => { + const uri = lbryuri.build({ + channelName: fileInfo.channel_name, + contentName: fileInfo.name, + }) + const claim = selectClaimsByUri(state)[uri] + if (!claim) dispatch(doResolveUri(uri)) + }) + dispatch({ type: types.FETCH_DOWNLOADED_CONTENT_COMPLETED, data: { diff --git a/ui/js/actions/file_info.js b/ui/js/actions/file_info.js index 77763352b..b7611fb3d 100644 --- a/ui/js/actions/file_info.js +++ b/ui/js/actions/file_info.js @@ -76,3 +76,27 @@ export function doDeleteFile(uri, fileInfo, deleteFromComputer) { lbry.removeFile(fileInfo.outpoint, deleteFromComputer, successCallback) } } + +export function doFetchDownloadedContent() { + return function(dispatch, getState) { + const state = getState() + + dispatch({ + type: types.FETCH_DOWNLOADED_CONTENT_STARTED, + }) + + lbry.claim_list_mine().then((myClaimInfos) => { + lbry.file_list().then((fileInfos) => { + const myClaimOutpoints = myClaimInfos.map(({txid, nout}) => txid + ':' + nout); + + dispatch({ + type: types.FETCH_DOWNLOADED_CONTENT_COMPLETED, + data: { + fileInfos: fileInfos.filter(({outpoint}) => !myClaimOutpoints.includes(outpoint)), + } + }) + }); + }); + } +} + diff --git a/ui/js/component/fileList/index.js b/ui/js/component/fileList/index.js index 0a6c65c70..dd0209b40 100644 --- a/ui/js/component/fileList/index.js +++ b/ui/js/component/fileList/index.js @@ -4,4 +4,10 @@ import { } from 'react-redux' import FileList from './view' -export default connect()(FileList) +const select = (state) => ({ +}) + +const perform = (dispatch) => ({ +}) + +export default connect(select, perform)(FileList) diff --git a/ui/js/component/fileList/view.jsx b/ui/js/component/fileList/view.jsx index a119f8792..2a946746c 100644 --- a/ui/js/component/fileList/view.jsx +++ b/ui/js/component/fileList/view.jsx @@ -3,90 +3,81 @@ import lbry from 'lbry.js'; import lbryuri from 'lbryuri.js'; import Link from 'component/link'; import {FormField} from 'component/form.js'; -import FileTileStream from 'component/fileTile'; +import FileTileStream from 'component/fileTileStream'; import rewards from 'rewards.js'; import lbryio from 'lbryio.js'; import {BusyMessage, Thumbnail} from 'component/common.js'; -const FileList = React.createClass({ - _sortFunctions: { - date: function(fileInfos) { - return fileInfos.slice().reverse(); - }, - title: function(fileInfos) { - return fileInfos.slice().sort(function(fileInfo1, fileInfo2) { - const title1 = fileInfo1.metadata ? fileInfo1.metadata.title.toLowerCase() : fileInfo1.name; - const title2 = fileInfo2.metadata ? fileInfo2.metadata.title.toLowerCase() : fileInfo2.name; - if (title1 < title2) { - return -1; - } else if (title1 > title2) { - return 1; - } else { - return 0; - } - }); - }, - filename: function(fileInfos) { - return fileInfos.slice().sort(function({file_name: fileName1}, {file_name: fileName2}) { - const fileName1Lower = fileName1.toLowerCase(); - const fileName2Lower = fileName2.toLowerCase(); - if (fileName1Lower < fileName2Lower) { - return -1; - } else if (fileName2Lower > fileName1Lower) { - return 1; - } else { - return 0; - } - }); - }, - }, - propTypes: { - fileInfos: React.PropTypes.array.isRequired, - hidePrices: React.PropTypes.bool, - }, - getDefaultProps: function() { - return { - hidePrices: false, - }; - }, - getInitialState: function() { - return { +class FileList extends React.Component { + constructor(props) { + super(props) + + this.state = { sortBy: 'date', - }; - }, - handleSortChanged: function(event) { - this.setState({ - sortBy: event.target.value, - }); - }, - render: function() { - var content = [], - seenUris = {}; - - const fileInfosSorted = this._sortFunctions[this.state.sortBy](this.props.fileInfos); - for (let {outpoint, name, channel_name, metadata, mime_type, claim_id, has_signature, signature_is_valid} of fileInfosSorted) { - if (seenUris[name] || !claim_id) { - continue; - } - - let streamMetadata; - if (metadata) { - streamMetadata = metadata.stream.metadata; - } else { - streamMetadata = null; - } - - - const uri = lbryuri.build({contentName: name, channelName: channel_name}); - seenUris[name] = true; - content.push() } + this._sortFunctions = { + date: function(fileInfos) { + return fileInfos.slice().reverse(); + }, + title: function(fileInfos) { + return fileInfos.slice().sort(function(fileInfo1, fileInfo2) { + const title1 = fileInfo1.metadata ? fileInfo1.metadata.stream.metadata.title.toLowerCase() : fileInfo1.name; + const title2 = fileInfo2.metadata ? fileInfo2.metadata.stream.metadata.title.toLowerCase() : fileInfo2.name; + if (title1 < title2) { + return -1; + } else if (title1 > title2) { + return 1; + } else { + return 0; + } + }) + }, + filename: function(fileInfos) { + return fileInfos.slice().sort(function({file_name: fileName1}, {file_name: fileName2}) { + const fileName1Lower = fileName1.toLowerCase(); + const fileName2Lower = fileName2.toLowerCase(); + if (fileName1Lower < fileName2Lower) { + return -1; + } else if (fileName2Lower > fileName1Lower) { + return 1; + } else { + return 0; + } + }) + }, + } + } + + handleSortChanged(event) { + this.setState({ + sortBy: event.target.value, + }) + } + + render() { + const { + handleSortChanged, + fileInfos, + hidePrices, + } = this.props + const { + sortBy, + } = this.state + const content = [] + + this._sortFunctions[sortBy](fileInfos).forEach(fileInfo => { + const uri = lbryuri.build({ + contentName: fileInfo.name, + channelName: fileInfo.channel_name, + }) + content.push() + }) return (
Sort by { ' ' } - + @@ -94,8 +85,100 @@ const FileList = React.createClass({ {content}
- ); + ) } -}); +} + +// const FileList = React.createClass({ +// _sortFunctions: { +// date: function(fileInfos) { +// return fileInfos.slice().reverse(); +// }, +// title: function(fileInfos) { +// return fileInfos.slice().sort(function(fileInfo1, fileInfo2) { +// const title1 = fileInfo1.metadata ? fileInfo1.metadata.title.toLowerCase() : fileInfo1.name; +// const title2 = fileInfo2.metadata ? fileInfo2.metadata.title.toLowerCase() : fileInfo2.name; +// if (title1 < title2) { +// return -1; +// } else if (title1 > title2) { +// return 1; +// } else { +// return 0; +// } +// }); +// }, +// filename: function(fileInfos) { +// return fileInfos.slice().sort(function({file_name: fileName1}, {file_name: fileName2}) { +// const fileName1Lower = fileName1.toLowerCase(); +// const fileName2Lower = fileName2.toLowerCase(); +// if (fileName1Lower < fileName2Lower) { +// return -1; +// } else if (fileName2Lower > fileName1Lower) { +// return 1; +// } else { +// return 0; +// } +// }); +// }, +// }, +// propTypes: { +// fileInfos: React.PropTypes.array.isRequired, +// hidePrices: React.PropTypes.bool, +// }, +// getDefaultProps: function() { +// return { +// hidePrices: false, +// }; +// }, +// getInitialState: function() { +// return { +// sortBy: 'date', +// }; +// }, +// handleSortChanged: function(event) { +// this.setState({ +// sortBy: event.target.value, +// }); +// }, +// render: function() { +// var content = [], +// seenUris = {}; + +// console.debug(this.props) + +// const fileInfosSorted = this._sortFunctions[this.state.sortBy](this.props.fileInfos); +// for (let {outpoint, name, channel_name, metadata, mime_type, claim_id, has_signature, signature_is_valid} of fileInfosSorted) { +// if (seenUris[name] || !claim_id) { +// continue; +// } + +// let streamMetadata; +// if (metadata) { +// streamMetadata = metadata.stream.metadata; +// } else { +// streamMetadata = null; +// } + + +// const uri = lbryuri.build({contentName: name, channelName: channel_name}); +// seenUris[name] = true; +// content.push() +// } + +// return ( +//
+// +// Sort by { ' ' } +// +// +// +// +// +// +// {content} +//
+// ); +// } +// }); export default FileList diff --git a/ui/js/component/header/index.js b/ui/js/component/header/index.js index 02ca2f2f8..ce5cefdd8 100644 --- a/ui/js/component/header/index.js +++ b/ui/js/component/header/index.js @@ -5,6 +5,7 @@ import { import { selectCurrentPage, selectHeaderLinks, + selectPageTitle, } from 'selectors/app' import { doNavigate, @@ -19,6 +20,7 @@ import Header from './view' const select = (state) => ({ currentPage: selectCurrentPage(state), subLinks: selectHeaderLinks(state), + pageTitle: selectPageTitle(state), }) const perform = (dispatch) => ({ diff --git a/ui/js/component/header/view.jsx b/ui/js/component/header/view.jsx index e54bf3751..92a45cad9 100644 --- a/ui/js/component/header/view.jsx +++ b/ui/js/component/header/view.jsx @@ -104,7 +104,7 @@ class WunderBar extends React.PureComponent { this.props.onSearch(searchTerm); }, 800); // 800ms delay, tweak for faster/slower } - + componentWillReceiveProps(nextProps) { if (nextProps.viewingPage !== this.props.viewingPage || nextProps.address != this.props.address) { this.setState({ address: nextProps.address, icon: nextProps.icon }); diff --git a/ui/js/page/fileListDownloaded/index.js b/ui/js/page/fileListDownloaded/index.js index ed27152c5..6d92a1867 100644 --- a/ui/js/page/fileListDownloaded/index.js +++ b/ui/js/page/fileListDownloaded/index.js @@ -3,17 +3,23 @@ import { connect } from 'react-redux' import { - selectDownloadedContentFileInfos, selectFetchingDownloadedContent, } from 'selectors/content' +import { + selectDownloadedFileInfo, +} from 'selectors/file_info' +import { + doNavigate, +} from 'actions/app' import FileListDownloaded from './view' const select = (state) => ({ - downloadedContent: selectDownloadedContentFileInfos(state), + downloadedContent: selectDownloadedFileInfo(state), fetching: selectFetchingDownloadedContent(state), }) const perform = (dispatch) => ({ + navigate: (path) => dispatch(doNavigate(path)), }) export default connect(select, perform)(FileListDownloaded) diff --git a/ui/js/page/fileListDownloaded/view.jsx b/ui/js/page/fileListDownloaded/view.jsx index 715eae94e..cf95e0224 100644 --- a/ui/js/page/fileListDownloaded/view.jsx +++ b/ui/js/page/fileListDownloaded/view.jsx @@ -14,6 +14,7 @@ class FileListDownloaded extends React.Component { const { downloadedContent, fetching, + navigate, } = this.props if (fetching) { @@ -25,7 +26,7 @@ class FileListDownloaded extends React.Component { } else if (!downloadedContent.length) { return (
- You haven't downloaded anything from LBRY yet. Go ! + You haven't downloaded anything from LBRY yet. Go navigate('discover')} label="search for your first download" />!
); } else { @@ -37,55 +38,5 @@ class FileListDownloaded extends React.Component { } } } -// const FileListDownloaded = React.createClass({ -// _isMounted: false, - -// getInitialState: function() { -// return { -// fileInfos: null, -// }; -// }, -// componentDidMount: function() { -// this._isMounted = true; -// document.title = "Downloaded Files"; - -// lbry.claim_list_mine().then((myClaimInfos) => { -// if (!this._isMounted) { return; } - -// lbry.file_list().then((fileInfos) => { -// if (!this._isMounted) { return; } - -// const myClaimOutpoints = myClaimInfos.map(({txid, nout}) => txid + ':' + nout); -// this.setState({ -// fileInfos: fileInfos.filter(({outpoint}) => !myClaimOutpoints.includes(outpoint)), -// }); -// }); -// }); -// }, -// componentWillUnmount: function() { -// this._isMounted = false; -// }, -// render: function() { -// if (this.state.fileInfos === null) { -// return ( -//
-// -//
-// ); -// } else if (!this.state.fileInfos.length) { -// return ( -//
-// You haven't downloaded anything from LBRY yet. Go ! -//
-// ); -// } else { -// return ( -//
-// -//
-// ); -// } -// } -// }); export default FileListDownloaded diff --git a/ui/js/reducers/file_info.js b/ui/js/reducers/file_info.js index 2c98cf048..0023df8f1 100644 --- a/ui/js/reducers/file_info.js +++ b/ui/js/reducers/file_info.js @@ -1,4 +1,5 @@ import * as types from 'constants/action_types' +import lbryuri from 'lbryuri' const reducers = {} const defaultState = { @@ -157,6 +158,26 @@ reducers[types.LOADING_VIDEO_FAILED] = function(state, action) { }) } +reducers[types.FETCH_DOWNLOADED_CONTENT_COMPLETED] = function(state, action) { + const { + fileInfos, + } = action.data + const newByUri = Object.assign({}, state.byUri) + + fileInfos.forEach(fileInfo => { + const uri = lbryuri.build({ + channelName: fileInfo.channel_name, + contentName: fileInfo.name, + }) + + newByUri[uri] = fileInfo + }) + + return Object.assign({}, state, { + byUri: newByUri + }) +} + export default function reducer(state = defaultState, action) { const handler = reducers[action.type]; if (handler) return handler(state, action); diff --git a/ui/js/selectors/app.js b/ui/js/selectors/app.js index 7105bc16b..a269e98f2 100644 --- a/ui/js/selectors/app.js +++ b/ui/js/selectors/app.js @@ -28,6 +28,34 @@ export const selectCurrentUri = createSelector( } ) +export const selectPageTitle = createSelector( + selectCurrentPage, + selectCurrentUri, + (page, uri) => { + switch(page) + { + case 'discover': + return 'Discover' + case 'wallet': + case 'send': + case 'receive': + case 'claim': + case 'referral': + return 'Wallet' + case 'downloaded': + return 'My Files' + case 'published': + return 'My Files' + case 'publish': + return 'Publish' + case 'help': + return 'Help' + default: + return 'LBRY'; + } + } +) + export const selectPlatform = createSelector( _selectState, (state) => state.platform diff --git a/ui/js/selectors/file_info.js b/ui/js/selectors/file_info.js index 79b28c1d6..0a114ba0f 100644 --- a/ui/js/selectors/file_info.js +++ b/ui/js/selectors/file_info.js @@ -133,3 +133,19 @@ export const makeSelectLoadingForUri = () => { (loading) => !!loading ) } + +export const selectDownloadedFileInfo = createSelector( + selectAllFileInfoByUri, + (byUri) => { + const fileInfoList = [] + Object.keys(byUri).forEach(key => { + const fileInfo = byUri[key] + + if (fileInfo.completed || fileInfo.written_bytes) { + fileInfoList.push(fileInfo) + } + }) + + return fileInfoList + } +)