Redux #115
31 changed files with 451 additions and 687 deletions
|
@ -6,6 +6,7 @@ import {
|
||||||
selectUpgradeDownloadItem,
|
selectUpgradeDownloadItem,
|
||||||
selectUpgradeFilename,
|
selectUpgradeFilename,
|
||||||
selectPageTitle,
|
selectPageTitle,
|
||||||
|
selectCurrentPath,
|
||||||
} from 'selectors/app'
|
} from 'selectors/app'
|
||||||
|
|
||||||
const {remote, ipcRenderer, shell} = require('electron');
|
const {remote, ipcRenderer, shell} = require('electron');
|
||||||
|
@ -16,6 +17,11 @@ const fs = remote.require('fs');
|
||||||
|
|
||||||
export function doNavigate(path) {
|
export function doNavigate(path) {
|
||||||
return function(dispatch, getState) {
|
return function(dispatch, getState) {
|
||||||
|
const state = getState()
|
||||||
|
const previousPath = selectCurrentPath(state)
|
||||||
|
const previousTitle = selectPageTitle(state)
|
||||||
|
history.pushState(state, previousTitle, previousPath);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.NAVIGATE,
|
type: types.NAVIGATE,
|
||||||
data: {
|
data: {
|
||||||
|
@ -23,8 +29,8 @@ export function doNavigate(path) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const state = getState()
|
|
||||||
const pageTitle = selectPageTitle(state)
|
const pageTitle = selectPageTitle(state)
|
||||||
|
|
||||||
window.document.title = pageTitle
|
window.document.title = pageTitle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,8 +54,12 @@ export function doCloseModal() {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doHistoryBack() {
|
export function doHistoryBack() {
|
||||||
return {
|
return function(dispatch, getState) {
|
||||||
type: types.HISTORY_BACK
|
if (window.history.length > 1) {
|
||||||
|
window.history.back();
|
||||||
|
} else {
|
||||||
|
dispatch(doNavigate('discover'))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +162,7 @@ export function doCheckUpgradeAvailable() {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: types.UPDATE_VERSION,
|
type: types.UPDATE_VERSION,
|
||||||
data: {
|
data: {
|
||||||
version: versionInfo.lbrynet_version
|
version: remoteVersion,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
dispatch({
|
dispatch({
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import lbry from 'lbry'
|
||||||
import {
|
import {
|
||||||
connect
|
connect
|
||||||
} from 'react-redux'
|
} from 'react-redux'
|
||||||
|
|
|
@ -13,6 +13,7 @@ const Link = (props) => {
|
||||||
button,
|
button,
|
||||||
hidden,
|
hidden,
|
||||||
disabled,
|
disabled,
|
||||||
|
children,
|
||||||
} = props
|
} = props
|
||||||
|
|
||||||
const className = (props.className || '') +
|
const className = (props.className || '') +
|
||||||
|
@ -22,8 +23,8 @@ const Link = (props) => {
|
||||||
|
|
||||||
|
|
||||||
let content;
|
let content;
|
||||||
if (props.children) {
|
if (children) {
|
||||||
content = this.props.children
|
content = children
|
||||||
} else {
|
} else {
|
||||||
content = (
|
content = (
|
||||||
<span {... 'button' in props ? {className: 'button__content'} : {}}>
|
<span {... 'button' in props ? {className: 'button__content'} : {}}>
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import {
|
|
||||||
connect,
|
|
||||||
} from 'react-redux'
|
|
||||||
import {
|
|
||||||
selectCurrentPage,
|
|
||||||
} from 'selectors/app'
|
|
||||||
import {
|
|
||||||
doNavigate,
|
|
||||||
} from 'actions/app'
|
|
||||||
import NavMain from './view'
|
|
||||||
|
|
||||||
const select = (state) => ({
|
|
||||||
currentPage: selectCurrentPage(state)
|
|
||||||
})
|
|
||||||
|
|
||||||
const perform = (dispatch) => ({
|
|
||||||
navigate: (path) => dispatch(doNavigate(path))
|
|
||||||
})
|
|
||||||
|
|
||||||
export default connect(select, perform)(NavMain)
|
|
|
@ -1,22 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export const NavMain = (props) => {
|
|
||||||
const {
|
|
||||||
links,
|
|
||||||
currentPage,
|
|
||||||
navigate,
|
|
||||||
} = props
|
|
||||||
|
|
||||||
return (
|
|
||||||
<nav className="sub-header">{
|
|
||||||
Object.keys(links).map((link) => {
|
|
||||||
console.log(link + " vs " + currentPage);
|
|
||||||
return <a href="#" onClick={() => navigate(link)} key={link} className={link == currentPage ? 'sub-header-selected' : 'sub-header-unselected' }>
|
|
||||||
{links[link]}
|
|
||||||
</a>
|
|
||||||
})
|
|
||||||
}</nav>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NavMain
|
|
|
@ -1,7 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import {
|
|
||||||
connect,
|
|
||||||
} from 'react-redux'
|
|
||||||
import NavSettings from './view'
|
|
||||||
|
|
||||||
export default connect(null, null)(NavSettings)
|
|
|
@ -1,11 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import NavMain from 'component/navMain'
|
|
||||||
|
|
||||||
export const NavSettings = () => {
|
|
||||||
return <NavMain links={{
|
|
||||||
'settings': 'Settings',
|
|
||||||
'help' : 'Help'
|
|
||||||
}} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NavSettings
|
|
|
@ -1,7 +0,0 @@
|
||||||
import React from 'react'
|
|
||||||
import {
|
|
||||||
connect,
|
|
||||||
} from 'react-redux'
|
|
||||||
import NavWallet from './view'
|
|
||||||
|
|
||||||
export default connect(null, null)(NavWallet)
|
|
|
@ -1,13 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import NavMain from 'component/navMain'
|
|
||||||
|
|
||||||
export const NavWallet = () => {
|
|
||||||
return <NavMain links={{
|
|
||||||
'wallet': 'Overview',
|
|
||||||
'send': 'Send',
|
|
||||||
'receive': 'Receive',
|
|
||||||
'rewards': 'Rewards'
|
|
||||||
}} />
|
|
||||||
}
|
|
||||||
|
|
||||||
export default NavWallet
|
|
|
@ -4,7 +4,7 @@ import HelpPage from 'page/help';
|
||||||
import ReportPage from 'page/report.js';
|
import ReportPage from 'page/report.js';
|
||||||
import StartPage from 'page/start.js';
|
import StartPage from 'page/start.js';
|
||||||
import WalletPage from 'page/wallet';
|
import WalletPage from 'page/wallet';
|
||||||
import FilePage from 'page/filePage';
|
import ShowPage from 'page/showPage'
|
||||||
import PublishPage from 'page/publish';
|
import PublishPage from 'page/publish';
|
||||||
import DiscoverPage from 'page/discover';
|
import DiscoverPage from 'page/discover';
|
||||||
import SplashScreen from 'component/splash.js';
|
import SplashScreen from 'component/splash.js';
|
||||||
|
@ -13,6 +13,7 @@ import RewardsPage from 'page/rewards.js';
|
||||||
import FileListDownloaded from 'page/fileListDownloaded'
|
import FileListDownloaded from 'page/fileListDownloaded'
|
||||||
import FileListPublished from 'page/fileListPublished'
|
import FileListPublished from 'page/fileListPublished'
|
||||||
import ChannelPage from 'page/channel'
|
import ChannelPage from 'page/channel'
|
||||||
|
import SearchPage from 'page/search'
|
||||||
|
|
||||||
const route = (page, routesMap) => {
|
const route = (page, routesMap) => {
|
||||||
const component = routesMap[page]
|
const component = routesMap[page]
|
||||||
|
@ -36,12 +37,13 @@ const Router = (props) => {
|
||||||
'wallet': <WalletPage {...props} />,
|
'wallet': <WalletPage {...props} />,
|
||||||
'send': <WalletPage {...props} />,
|
'send': <WalletPage {...props} />,
|
||||||
'receive': <WalletPage {...props} />,
|
'receive': <WalletPage {...props} />,
|
||||||
'show': <FilePage {...props} />,
|
'show': <ShowPage {...props} />,
|
||||||
'channel': <ChannelPage {...props} />,
|
'channel': <ChannelPage {...props} />,
|
||||||
'publish': <PublishPage {...props} />,
|
'publish': <PublishPage {...props} />,
|
||||||
'developer': <DeveloperPage {...props} />,
|
'developer': <DeveloperPage {...props} />,
|
||||||
'discover': <DiscoverPage {...props} />,
|
'discover': <DiscoverPage {...props} />,
|
||||||
'rewards': <RewardsPage {...props} />,
|
'rewards': <RewardsPage {...props} />,
|
||||||
|
'search': <SearchPage {...props} />,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
23
ui/js/component/subHeader/index.js
Normal file
23
ui/js/component/subHeader/index.js
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
connect,
|
||||||
|
} from 'react-redux'
|
||||||
|
import {
|
||||||
|
selectCurrentPage,
|
||||||
|
selectHeaderLinks,
|
||||||
|
} from 'selectors/app'
|
||||||
|
import {
|
||||||
|
doNavigate,
|
||||||
|
} from 'actions/app'
|
||||||
|
import SubHeader from './view'
|
||||||
|
|
||||||
|
const select = (state, props) => ({
|
||||||
|
currentPage: selectCurrentPage(state),
|
||||||
|
subLinks: selectHeaderLinks(state),
|
||||||
|
})
|
||||||
|
|
||||||
|
const perform = (dispatch) => ({
|
||||||
|
navigate: (path) => dispatch(doNavigate(path)),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default connect(select, perform)(SubHeader)
|
28
ui/js/component/subHeader/view.jsx
Normal file
28
ui/js/component/subHeader/view.jsx
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
const SubHeader = (props) => {
|
||||||
|
const {
|
||||||
|
subLinks,
|
||||||
|
currentPage,
|
||||||
|
navigate,
|
||||||
|
modifier,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const links = []
|
||||||
|
|
||||||
|
for(let link of Object.keys(subLinks)) {
|
||||||
|
links.push(
|
||||||
|
<a href="#" onClick={() => navigate(link)} key={link} className={link == currentPage ? 'sub-header-selected' : 'sub-header-unselected' }>
|
||||||
|
{subLinks[link]}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav className={'sub-header' + (modifier ? ' sub-header--' + modifier : '')}>
|
||||||
|
{links}
|
||||||
|
</nav>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SubHeader
|
|
@ -25,8 +25,8 @@ const perform = (dispatch) => ({
|
||||||
// navigate: (path) => dispatch(doNavigate(path)),
|
// navigate: (path) => dispatch(doNavigate(path)),
|
||||||
onSearch: (query) => dispatch(doSearchContent(query)),
|
onSearch: (query) => dispatch(doSearchContent(query)),
|
||||||
onSubmit: (query) => dispatch(doSearchContent(query)),
|
onSubmit: (query) => dispatch(doSearchContent(query)),
|
||||||
// activateSearch: () => dispatch(doActivateSearch()),
|
activateSearch: () => dispatch(doActivateSearch()),
|
||||||
// deactivateSearch: () => setTimeout(() => { dispatch(doDeactivateSearch()) }, 50),
|
deactivateSearch: () => setTimeout(() => { dispatch(doDeactivateSearch()) }, 50),
|
||||||
})
|
})
|
||||||
|
|
||||||
export default connect(select, perform)(Wunderbar)
|
export default connect(select, perform)(Wunderbar)
|
||||||
|
|
|
@ -61,6 +61,8 @@ class WunderBar extends React.PureComponent {
|
||||||
isActive: true
|
isActive: true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.props.activateSearch()
|
||||||
|
|
||||||
this._focusPending = true;
|
this._focusPending = true;
|
||||||
//below is hacking, improved when we have proper routing
|
//below is hacking, improved when we have proper routing
|
||||||
if (!this.state.address.startsWith('lbry://') && this.state.icon !== "icon-search") //onFocus, if they are not on an exact URL or a search page, clear the bar
|
if (!this.state.address.startsWith('lbry://') && this.state.icon !== "icon-search") //onFocus, if they are not on an exact URL or a search page, clear the bar
|
||||||
|
@ -71,6 +73,7 @@ class WunderBar extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
onBlur() {
|
onBlur() {
|
||||||
|
this.props.deactivateSearch()
|
||||||
let commonState = {isActive: false};
|
let commonState = {isActive: false};
|
||||||
if (this._resetOnNextBlur) {
|
if (this._resetOnNextBlur) {
|
||||||
this.setState(Object.assign({}, this._stateBeforeSearch, commonState));
|
this.setState(Object.assign({}, this._stateBeforeSearch, commonState));
|
||||||
|
|
|
@ -8,6 +8,7 @@ import rewards from 'rewards.js';
|
||||||
import lbryio from 'lbryio.js';
|
import lbryio from 'lbryio.js';
|
||||||
import {BusyMessage, Thumbnail} from 'component/common.js';
|
import {BusyMessage, Thumbnail} from 'component/common.js';
|
||||||
import FileList from 'component/fileList'
|
import FileList from 'component/fileList'
|
||||||
|
import SubHeader from 'component/subHeader'
|
||||||
|
|
||||||
class FileListDownloaded extends React.Component {
|
class FileListDownloaded extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
|
@ -17,25 +18,21 @@ class FileListDownloaded extends React.Component {
|
||||||
navigate,
|
navigate,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
|
let content
|
||||||
if (fetching) {
|
if (fetching) {
|
||||||
return (
|
content = <BusyMessage message="Loading" />
|
||||||
<main className="page">
|
|
||||||
<BusyMessage message="Loading" />
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
} else if (!downloadedContent.length) {
|
} else if (!downloadedContent.length) {
|
||||||
return (
|
content = <span>You haven't downloaded anything from LBRY yet. Go <Link href="#" onClick={() => navigate('discover')} label="search for your first download" />!</span>
|
||||||
<main className="page">
|
|
||||||
<span>You haven't downloaded anything from LBRY yet. Go <Link href="#" onClick={() => navigate('discover')} label="search for your first download" />!</span>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return (
|
content = <FileList fileInfos={downloadedContent} hidePrices={true} />
|
||||||
<main className="page">
|
|
||||||
<FileList fileInfos={downloadedContent} hidePrices={true} />
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="page">
|
||||||
|
<SubHeader />
|
||||||
|
{content}
|
||||||
|
</main>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@ import rewards from 'rewards.js';
|
||||||
import lbryio from 'lbryio.js';
|
import lbryio from 'lbryio.js';
|
||||||
import {BusyMessage, Thumbnail} from 'component/common.js';
|
import {BusyMessage, Thumbnail} from 'component/common.js';
|
||||||
import FileList from 'component/fileList'
|
import FileList from 'component/fileList'
|
||||||
|
import SubHeader from 'component/subHeader'
|
||||||
|
|
||||||
class FileListPublished extends React.Component {
|
class FileListPublished extends React.Component {
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
|
@ -15,17 +16,20 @@ class FileListPublished extends React.Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
_requestPublishReward() {
|
_requestPublishReward() {
|
||||||
lbryio.call('reward', 'list', {}).then(function(userRewards) {
|
// TODO this is throwing an error now
|
||||||
//already rewarded
|
// Error: LBRY internal API is disabled
|
||||||
if (userRewards.filter(function (reward) {
|
//
|
||||||
return reward.RewardType == rewards.TYPE_FIRST_PUBLISH && reward.TransactionID
|
// lbryio.call('reward', 'list', {}).then(function(userRewards) {
|
||||||
}).length) {
|
// //already rewarded
|
||||||
return
|
// if (userRewards.filter(function (reward) {
|
||||||
}
|
// return reward.RewardType == rewards.TYPE_FIRST_PUBLISH && reward.TransactionID
|
||||||
else {
|
// }).length) {
|
||||||
rewards.claimReward(rewards.TYPE_FIRST_PUBLISH).catch(() => {})
|
// return
|
||||||
}
|
// }
|
||||||
})
|
// else {
|
||||||
|
// rewards.claimReward(rewards.TYPE_FIRST_PUBLISH).catch(() => {})
|
||||||
|
// }
|
||||||
|
// })
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -35,92 +39,22 @@ class FileListPublished extends React.Component {
|
||||||
navigate,
|
navigate,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
|
let content
|
||||||
if (fetching) {
|
if (fetching) {
|
||||||
return (
|
content = <BusyMessage message="Loading" />
|
||||||
<main className="page">
|
|
||||||
<BusyMessage message="Loading" />
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
} else if (!publishedContent.length) {
|
} else if (!publishedContent.length) {
|
||||||
return (
|
content = <span>You haven't downloaded anything from LBRY yet. Go <Link href="#" onClick={() => navigate('discover')} label="search for your first download" />!</span>
|
||||||
<main className="page">
|
|
||||||
<span>You haven't downloaded anything from LBRY yet. Go <Link href="#" onClick={() => navigate('discover')} label="search for your first download" />!</span>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
return (
|
content = <FileList fileInfos={publishedContent} hidePrices={true} />
|
||||||
<main className="page">
|
|
||||||
<FileList fileInfos={publishedContent} hidePrices={true} />
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="page">
|
||||||
|
<SubHeader />
|
||||||
|
{content}
|
||||||
|
</main>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// const FileListPublished = React.createClass({
|
|
||||||
// _isMounted: false,
|
|
||||||
|
|
||||||
// getInitialState: function () {
|
|
||||||
// return {
|
|
||||||
// fileInfos: null,
|
|
||||||
// };
|
|
||||||
// },
|
|
||||||
// _requestPublishReward: function() {
|
|
||||||
// lbryio.call('reward', 'list', {}).then(function(userRewards) {
|
|
||||||
// //already rewarded
|
|
||||||
// if (userRewards.filter(function (reward) {
|
|
||||||
// return reward.RewardType == rewards.TYPE_FIRST_PUBLISH && reward.TransactionID;
|
|
||||||
// }).length) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// rewards.claimReward(rewards.TYPE_FIRST_PUBLISH).catch(() => {})
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// },
|
|
||||||
// componentDidMount: function () {
|
|
||||||
// this._isMounted = true;
|
|
||||||
// this._requestPublishReward();
|
|
||||||
// document.title = "Published Files";
|
|
||||||
|
|
||||||
// lbry.claim_list_mine().then((claimInfos) => {
|
|
||||||
// if (!this._isMounted) { return; }
|
|
||||||
|
|
||||||
// lbry.file_list().then((fileInfos) => {
|
|
||||||
// if (!this._isMounted) { return; }
|
|
||||||
|
|
||||||
// const myClaimOutpoints = claimInfos.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 (
|
|
||||||
// <main className="page">
|
|
||||||
// <BusyMessage message="Loading" />
|
|
||||||
// </main>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// else if (!this.state.fileInfos.length) {
|
|
||||||
// return (
|
|
||||||
// <main className="page">
|
|
||||||
// <span>You haven't published anything to LBRY yet.</span> Try <Link href="?publish" label="publishing" />!
|
|
||||||
// </main>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// return (
|
|
||||||
// <main className="page">
|
|
||||||
// <FileList fileInfos={this.state.fileInfos} />
|
|
||||||
// </main>
|
|
||||||
// );
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
export default FileListPublished
|
export default FileListPublished
|
||||||
|
|
|
@ -45,323 +45,85 @@ const FormatItem = (props) => {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let FilePage = React.createClass({
|
const FilePage = (props) => {
|
||||||
_isMounted: false,
|
const {
|
||||||
|
claim,
|
||||||
|
navigate,
|
||||||
|
claim: {
|
||||||
|
txid,
|
||||||
|
nout,
|
||||||
|
has_signature: hasSignature,
|
||||||
|
signature_is_valid: signatureIsValid,
|
||||||
|
value,
|
||||||
|
value: {
|
||||||
|
stream,
|
||||||
|
stream: {
|
||||||
|
metadata,
|
||||||
|
source,
|
||||||
|
metadata: {
|
||||||
|
title,
|
||||||
|
} = {},
|
||||||
|
source: {
|
||||||
|
contentType,
|
||||||
|
} = {},
|
||||||
|
} = {},
|
||||||
|
} = {},
|
||||||
|
},
|
||||||
|
uri,
|
||||||
|
isDownloaded,
|
||||||
|
fileInfo,
|
||||||
|
costInfo,
|
||||||
|
costInfo: {
|
||||||
|
cost,
|
||||||
|
includesData: costIncludesData,
|
||||||
|
} = {},
|
||||||
|
} = props
|
||||||
|
|
||||||
propTypes: {
|
const outpoint = txid + ':' + nout;
|
||||||
uri: React.PropTypes.string,
|
const uriLookupComplete = !!claim && Object.keys(claim).length
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
const channelUriObj = lbryuri.parse(uri)
|
||||||
return {
|
delete channelUriObj.path;
|
||||||
cost: null,
|
delete channelUriObj.contentName;
|
||||||
costIncludesData: null,
|
const channelUri = signatureIsValid && hasSignature && channelUriObj.isChannel ? lbryuri.build(channelUriObj, false) : null;
|
||||||
isDownloaded: null,
|
const uriIndicator = <UriIndicator uri={uri} />
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
return (
|
||||||
this._isMounted = false;
|
<main className="main--single-column">
|
||||||
},
|
<section className="show-page-media">
|
||||||
|
{ contentType && contentType.startsWith('video/') ?
|
||||||
componentWillReceiveProps: function(nextProps) {
|
<Video className="video-embedded" uri={uri} metadata={metadata} outpoint={outpoint} /> :
|
||||||
if (nextProps.outpoint != this.props.outpoint || nextProps.uri != this.props.uri) {
|
(Object.keys(metadata).length > 0 ? <Thumbnail src={metadata.thumbnail} /> : <Thumbnail />) }
|
||||||
this.loadCostAndFileState(nextProps.uri, nextProps.outpoint);
|
</section>
|
||||||
}
|
<section className="card">
|
||||||
},
|
|
||||||
|
|
||||||
componentWillMount: function() {
|
|
||||||
this._isMounted = true;
|
|
||||||
this.loadCostAndFileState(this.props.uri, this.props.outpoint);
|
|
||||||
},
|
|
||||||
|
|
||||||
loadCostAndFileState: function(uri, outpoint) {
|
|
||||||
lbry.file_list({outpoint: outpoint}).then((fileInfo) => {
|
|
||||||
if (this._isMounted) {
|
|
||||||
this.setState({
|
|
||||||
isDownloaded: fileInfo.length > 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
lbry.getCostInfo(uri).then(({cost, includesData}) => {
|
|
||||||
if (this._isMounted) {
|
|
||||||
this.setState({
|
|
||||||
cost: cost,
|
|
||||||
costIncludesData: includesData,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
const metadata = this.props.metadata,
|
|
||||||
title = metadata ? this.props.metadata.title : this.props.uri,
|
|
||||||
uriIndicator = <UriIndicator uri={uri} hasSignature={hasSignature} signatureIsValid={signatureIsValid} />
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className="main--single-column">
|
|
||||||
<section className="show-page-media">
|
|
||||||
{ this.props.contentType && this.props.contentType.startsWith('video/') ?
|
|
||||||
<Video className="video-embedded" uri={this.props.uri} metadata={metadata} outpoint={this.props.outpoint} /> :
|
|
||||||
(metadata ? <Thumbnail src={metadata.thumbnail} /> : <Thumbnail />) }
|
|
||||||
</section>
|
|
||||||
<section className="card">
|
|
||||||
<div className="card__inner">
|
|
||||||
<div className="card__title-identity">
|
|
||||||
{isDownloaded === false
|
|
||||||
? <span style={{float: "right"}}><FilePrice uri={lbryuri.normalize(uri)} metadata={metadata} /></span>
|
|
||||||
: null}
|
|
||||||
<h1>{title}</h1>
|
|
||||||
<div className="card__subtitle">
|
|
||||||
{ this.props.channelUri ?
|
|
||||||
<Link href={"?show=" + this.props.channelUri }>{uriIndicator}</Link> :
|
|
||||||
uriIndicator}
|
|
||||||
</div>
|
|
||||||
<div className="card__actions">
|
|
||||||
<FileActions uri={uri} outpoint={outpoint} metadata={metadata} contentType={contentType} /></div>
|
|
||||||
</div>
|
|
||||||
<div className="card__content card__subtext card__subtext card__subtext--allow-newlines">
|
|
||||||
{metadata.description}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{ metadata ?
|
|
||||||
<div className="card__content">
|
|
||||||
<FormatItem metadata={metadata} contentType={this.state.contentType} cost={this.state.cost} uri={this.props.uri} outpoint={this.props.outpoint} costIncludesData={this.state.costIncludesData} />
|
|
||||||
</div> : '' }
|
|
||||||
<div className="card__content">
|
|
||||||
<Link href="https://lbry.io/dmca" label="report" className="button-text-help" />
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let ShowPage = React.createClass({
|
|
||||||
_uri: null,
|
|
||||||
_isMounted: false,
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
uri: React.PropTypes.string,
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
outpoint: null,
|
|
||||||
metadata: null,
|
|
||||||
contentType: null,
|
|
||||||
hasSignature: false,
|
|
||||||
claimType: null,
|
|
||||||
signatureIsValid: false,
|
|
||||||
cost: null,
|
|
||||||
costIncludesData: null,
|
|
||||||
uriLookupComplete: null,
|
|
||||||
isFailed: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
this._isMounted = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillReceiveProps: function(nextProps) {
|
|
||||||
if (nextProps.uri != this.props.uri) {
|
|
||||||
this.setState(this.getInitialState());
|
|
||||||
this.loadUri(nextProps.uri);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillMount: function() {
|
|
||||||
this._isMounted = true;
|
|
||||||
this.loadUri(this.props.uri);
|
|
||||||
},
|
|
||||||
|
|
||||||
loadUri: function(uri) {
|
|
||||||
this._uri = lbryuri.normalize(uri);
|
|
||||||
|
|
||||||
lbry.resolve({uri: this._uri}).then((resolveData) => {
|
|
||||||
const isChannel = resolveData && resolveData.claims_in_channel;
|
|
||||||
if (!this._isMounted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (resolveData) {
|
|
||||||
let newState = { uriLookupComplete: true }
|
|
||||||
if (!isChannel) {
|
|
||||||
let {claim: {txid: txid, nout: nout, has_signature: has_signature, signature_is_valid: signature_is_valid, value: {stream: {metadata: metadata, source: {contentType: contentType}}}}} = resolveData;
|
|
||||||
|
|
||||||
Object.assign(newState, {
|
|
||||||
claimType: "file",
|
|
||||||
metadata: metadata,
|
|
||||||
outpoint: txid + ':' + nout,
|
|
||||||
hasSignature: has_signature,
|
|
||||||
signatureIsValid: signature_is_valid,
|
|
||||||
contentType: contentType
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
lbry.setTitle(metadata.title ? metadata.title : this._uri)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
let {certificate: {txid: txid, nout: nout, has_signature: has_signature}} = resolveData;
|
|
||||||
Object.assign(newState, {
|
|
||||||
claimType: "channel",
|
|
||||||
outpoint: txid + ':' + nout,
|
|
||||||
txid: txid,
|
|
||||||
metadata: {
|
|
||||||
title:resolveData.certificate.name
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState(newState);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
this.setState(Object.assign({}, this.getInitialState(), {
|
|
||||||
uriLookupComplete: true,
|
|
||||||
isFailed: true
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
const metadata = this.state.metadata,
|
|
||||||
title = metadata ? this.state.metadata.title : this._uri;
|
|
||||||
|
|
||||||
let innerContent = "";
|
|
||||||
|
|
||||||
if (!this.state.uriLookupComplete || this.state.isFailed) {
|
|
||||||
innerContent = <section className="card">
|
|
||||||
<div className="card__inner">
|
<div className="card__inner">
|
||||||
<div className="card__title-identity"><h1>{title}</h1></div>
|
<div className="card__title-identity">
|
||||||
|
{isDownloaded === false
|
||||||
|
? <span style={{float: "right"}}><FilePrice uri={lbryuri.normalize(uri)} /></span>
|
||||||
|
: null}
|
||||||
|
<h1>{title}</h1>
|
||||||
|
<div className="card__subtitle">
|
||||||
|
{ channelUri ?
|
||||||
|
<Link href={"?show=" + channelUri }>{uriIndicator}</Link> :
|
||||||
|
uriIndicator}
|
||||||
|
</div>
|
||||||
|
<div className="card__actions">
|
||||||
|
<FileActions uri={uri} /></div>
|
||||||
|
</div>
|
||||||
|
<div className="card__content card__subtext card__subtext card__subtext--allow-newlines">
|
||||||
|
{metadata.description}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{ metadata ?
|
||||||
|
<div className="card__content">
|
||||||
|
<FormatItem metadata={metadata} contentType={contentType} cost={cost} uri={uri} outpoint={outpoint} costIncludesData={costIncludesData} />
|
||||||
|
</div> : '' }
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
{ this.state.uriLookupComplete ?
|
<Link href="https://lbry.io/dmca" label="report" className="button-text-help" />
|
||||||
<p>This location is not yet in use. { ' ' }<Link href="?publish" label="Put something here" />.</p> :
|
|
||||||
<BusyMessage message="Loading magic decentralized data..." />
|
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</section>;
|
</section>
|
||||||
} else {
|
</main>
|
||||||
let channelUriObj = lbryuri.parse(this._uri)
|
)
|
||||||
delete channelUriObj.path;
|
}
|
||||||
delete channelUriObj.contentName;
|
|
||||||
const channelUri = this.state.signatureIsValid && this.state.hasSignature && channelUriObj.isChannel ? lbryuri.build(channelUriObj, false) : null;
|
|
||||||
innerContent = <FilePage
|
|
||||||
uri={this._uri}
|
|
||||||
channelUri={channelUri}
|
|
||||||
outpoint={this.state.outpoint}
|
|
||||||
metadata={metadata}
|
|
||||||
contentType={this.state.contentType}
|
|
||||||
hasSignature={this.state.hasSignature}
|
|
||||||
signatureIsValid={this.state.signatureIsValid}
|
|
||||||
/>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <main className="main--single-column">{innerContent}</main>;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default FilePage;
|
export default FilePage;
|
||||||
|
|
||||||
//
|
|
||||||
// const ShowPage = (props) => {
|
|
||||||
// const {
|
|
||||||
// claim,
|
|
||||||
// navigate,
|
|
||||||
// claim: {
|
|
||||||
// txid,
|
|
||||||
// nout,
|
|
||||||
// has_signature: hasSignature,
|
|
||||||
// signature_is_valid: signatureIsValid,
|
|
||||||
// value,
|
|
||||||
// value: {
|
|
||||||
// stream,
|
|
||||||
// stream: {
|
|
||||||
// metadata,
|
|
||||||
// source,
|
|
||||||
// metadata: {
|
|
||||||
// title,
|
|
||||||
// } = {},
|
|
||||||
// source: {
|
|
||||||
// contentType,
|
|
||||||
// } = {},
|
|
||||||
// } = {},
|
|
||||||
// } = {},
|
|
||||||
// },
|
|
||||||
// uri,
|
|
||||||
// isDownloaded,
|
|
||||||
// fileInfo,
|
|
||||||
// costInfo,
|
|
||||||
// costInfo: {
|
|
||||||
// cost,
|
|
||||||
// includesData: costIncludesData,
|
|
||||||
// } = {},
|
|
||||||
// } = props
|
|
||||||
//
|
|
||||||
// const outpoint = txid + ':' + nout;
|
|
||||||
// const uriLookupComplete = !!claim && Object.keys(claim).length
|
|
||||||
//
|
|
||||||
// if (props.isFailed) {
|
|
||||||
// return (
|
|
||||||
// <main className="main--single-column">
|
|
||||||
// <section className="card">
|
|
||||||
// <div className="card__inner">
|
|
||||||
// <div className="card__title-identity"><h1>{uri}</h1></div>
|
|
||||||
// </div>
|
|
||||||
// <div className="card__content">
|
|
||||||
// <p>
|
|
||||||
// This location is not yet in use.
|
|
||||||
// { ' ' }
|
|
||||||
// <Link href="#" onClick={() => navigate('publish')} label="Put something here" />.
|
|
||||||
// </p>
|
|
||||||
// </div>
|
|
||||||
// </section>
|
|
||||||
// </main>
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return (
|
|
||||||
// <main className="main--single-column">
|
|
||||||
// <section className="show-page-media">
|
|
||||||
// { contentType && contentType.startsWith('video/') ?
|
|
||||||
// <Video className="video-embedded" uri={uri} metadata={metadata} outpoint={outpoint} /> :
|
|
||||||
// (metadata ? <Thumbnail src={metadata.thumbnail} /> : <Thumbnail />) }
|
|
||||||
// </section>
|
|
||||||
// <section className="card">
|
|
||||||
// <div className="card__inner">
|
|
||||||
// <div className="card__title-identity">
|
|
||||||
// {isDownloaded === false
|
|
||||||
// ? <span style={{float: "right"}}><FilePrice uri={lbryuri.normalize(uri)} /></span>
|
|
||||||
// : null}
|
|
||||||
// <h1>{title}</h1>
|
|
||||||
// { uriLookupComplete ?
|
|
||||||
// <div>
|
|
||||||
// <div className="card__subtitle">
|
|
||||||
// <UriIndicator uri={uri} />
|
|
||||||
// </div>
|
|
||||||
// <div className="card__actions">
|
|
||||||
// <FileActions uri={uri} outpoint={outpoint} metadata={metadata} contentType={contentType} />
|
|
||||||
// </div>
|
|
||||||
// </div> : '' }
|
|
||||||
// </div>
|
|
||||||
// { uriLookupComplete ?
|
|
||||||
// <div>
|
|
||||||
// <div className="card__content card__subtext card__subtext card__subtext--allow-newlines">
|
|
||||||
// {metadata.description}
|
|
||||||
// </div>
|
|
||||||
// </div>
|
|
||||||
// : <div className="card__content"><BusyMessage message="Loading magic decentralized data..." /></div> }
|
|
||||||
// </div>
|
|
||||||
// { metadata ?
|
|
||||||
// <div className="card__content">
|
|
||||||
// <FormatItem metadata={metadata} contentType={contentType} cost={cost} uri={uri} outpoint={outpoint} costIncludesData={costIncludesData} />
|
|
||||||
// </div> : '' }
|
|
||||||
// <div className="card__content">
|
|
||||||
// <Link href="https://lbry.io/dmca" label="report" className="button-text-help" />
|
|
||||||
// </div>
|
|
||||||
// </section>
|
|
||||||
// </main>
|
|
||||||
// )
|
|
||||||
// }
|
|
|
@ -2,7 +2,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbry from 'lbry.js';
|
import lbry from 'lbry.js';
|
||||||
import Link from 'component/link';
|
import Link from 'component/link';
|
||||||
import NavSettings from 'component/navSettings';
|
import SubHeader from 'component/subHeader'
|
||||||
import {version as uiVersion} from 'json!../../../package.json';
|
import {version as uiVersion} from 'json!../../../package.json';
|
||||||
|
|
||||||
var HelpPage = React.createClass({
|
var HelpPage = React.createClass({
|
||||||
|
@ -50,7 +50,7 @@ var HelpPage = React.createClass({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="main--single-column">
|
<main className="main--single-column">
|
||||||
<NavSettings />
|
<SubHeader />
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<div className="card__title-primary">
|
<div className="card__title-primary">
|
||||||
<h3>Read the FAQ</h3>
|
<h3>Read the FAQ</h3>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import lbryio from 'lbryio';
|
import lbryio from 'lbryio';
|
||||||
import {CreditAmount, Icon} from 'component/common.js';
|
import {CreditAmount, Icon} from 'component/common.js';
|
||||||
import NavWallet from 'component/navWallet';
|
import SubHeader from 'component/subHeader'
|
||||||
import {RewardLink} from 'component/reward-link';
|
import {RewardLink} from 'component/reward-link';
|
||||||
|
|
||||||
const RewardTile = React.createClass({
|
const RewardTile = React.createClass({
|
||||||
|
@ -55,7 +55,7 @@ export let RewardsPage = React.createClass({
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<main className="main--single-column">
|
<main className="main--single-column">
|
||||||
<NavWallet />
|
<SubHeader />
|
||||||
<div>
|
<div>
|
||||||
{!this.state.userRewards
|
{!this.state.userRewards
|
||||||
? (this.state.failed ? <div className="empty">Failed to load rewards.</div> : '')
|
? (this.state.failed ? <div className="empty">Failed to load rewards.</div> : '')
|
||||||
|
|
|
@ -1,165 +0,0 @@
|
||||||
import React from 'react';
|
|
||||||
import lbry from '../lbry.js';
|
|
||||||
import lbryio from '../lbryio.js';
|
|
||||||
import lbryuri from '../lbryuri.js';
|
|
||||||
import lighthouse from '../lighthouse.js';
|
|
||||||
import {FileTile, FileTileStream} from '../component/file-tile.js';
|
|
||||||
import {Link} from '../component/link';
|
|
||||||
import {ToolTip} from '../component/tooltip.js';
|
|
||||||
import {BusyMessage} from '../component/common.js';
|
|
||||||
|
|
||||||
var SearchNoResults = React.createClass({
|
|
||||||
render: function() {
|
|
||||||
return <section>
|
|
||||||
<span className="empty">
|
|
||||||
No one has checked anything in for {this.props.query} yet.
|
|
||||||
<Link label="Be the first" href="?publish" />
|
|
||||||
</span>
|
|
||||||
</section>;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var SearchResultList = React.createClass({
|
|
||||||
render: function() {
|
|
||||||
var rows = [],
|
|
||||||
seenNames = {}; //fix this when the search API returns claim IDs
|
|
||||||
|
|
||||||
for (let {name, claim, claim_id, channel_name, channel_id, txid, nout} of this.props.results) {
|
|
||||||
const uri = lbryuri.build({
|
|
||||||
channelName: channel_name,
|
|
||||||
contentName: name,
|
|
||||||
claimId: channel_id || claim_id,
|
|
||||||
});
|
|
||||||
|
|
||||||
rows.push(
|
|
||||||
<FileTileStream key={uri} uri={uri} outpoint={txid + ':' + nout} metadata={claim.stream.metadata} contentType={claim.stream.source.contentType} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<div>{rows}</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let SearchResults = React.createClass({
|
|
||||||
propTypes: {
|
|
||||||
query: React.PropTypes.string.isRequired
|
|
||||||
},
|
|
||||||
|
|
||||||
_isMounted: false,
|
|
||||||
|
|
||||||
search: function(term) {
|
|
||||||
lighthouse.search(term).then(this.searchCallback);
|
|
||||||
if (!this.state.searching) {
|
|
||||||
this.setState({ searching: true })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillMount: function () {
|
|
||||||
this._isMounted = true;
|
|
||||||
this.search(this.props.query);
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillReceiveProps: function (nextProps) {
|
|
||||||
if (nextProps.query != this.props.query) {
|
|
||||||
this.search(nextProps.query);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount: function () {
|
|
||||||
this._isMounted = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function () {
|
|
||||||
return {
|
|
||||||
results: [],
|
|
||||||
searching: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
searchCallback: function (results) {
|
|
||||||
if (this._isMounted) //could have canceled while results were pending, in which case nothing to do
|
|
||||||
{
|
|
||||||
this.setState({
|
|
||||||
results: results,
|
|
||||||
searching: false //multiple searches can be out, we're only done if we receive one we actually care about
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function () {
|
|
||||||
return this.state.searching ?
|
|
||||||
<BusyMessage message="Looking up the Dewey Decimals" /> :
|
|
||||||
(this.state.results && this.state.results.length ?
|
|
||||||
<SearchResultList results={this.state.results} /> :
|
|
||||||
<SearchNoResults query={this.props.query} />);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let SearchPage = React.createClass({
|
|
||||||
|
|
||||||
_isMounted: false,
|
|
||||||
|
|
||||||
propTypes: {
|
|
||||||
query: React.PropTypes.string.isRequired
|
|
||||||
},
|
|
||||||
|
|
||||||
isValidUri: function(query) {
|
|
||||||
try {
|
|
||||||
lbryuri.parse(query);
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillMount: function() {
|
|
||||||
this._isMounted = true;
|
|
||||||
lighthouse.search(this.props.query).then(this.searchCallback);
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount: function() {
|
|
||||||
this._isMounted = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
getInitialState: function() {
|
|
||||||
return {
|
|
||||||
results: [],
|
|
||||||
searching: true
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
searchCallback: function(results) {
|
|
||||||
if (this._isMounted) //could have canceled while results were pending, in which case nothing to do
|
|
||||||
{
|
|
||||||
this.setState({
|
|
||||||
results: results,
|
|
||||||
searching: false //multiple searches can be out, we're only done if we receive one we actually care about
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render: function() {
|
|
||||||
return (
|
|
||||||
<main className="main--single-column">
|
|
||||||
{ this.isValidUri(this.props.query) ?
|
|
||||||
<section className="section-spaced">
|
|
||||||
<h3 className="card-row__header">
|
|
||||||
Exact URL
|
|
||||||
<ToolTip label="?" body="This is the resolution of a LBRY URL and not controlled by LBRY Inc." className="tooltip--header"/>
|
|
||||||
</h3>
|
|
||||||
<FileTile uri={this.props.query} showEmpty={true} />
|
|
||||||
</section> : '' }
|
|
||||||
<section className="section-spaced">
|
|
||||||
<h3 className="card-row__header">
|
|
||||||
Search Results for {this.props.query}
|
|
||||||
<ToolTip label="?" body="These search results are provided by LBRY Inc." className="tooltip--header"/>
|
|
||||||
</h3>
|
|
||||||
<SearchResults query={this.props.query} />
|
|
||||||
</section>
|
|
||||||
</main>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
export default SearchPage;
|
|
30
ui/js/page/search/index.js
Normal file
30
ui/js/page/search/index.js
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
connect,
|
||||||
|
} from 'react-redux'
|
||||||
|
import {
|
||||||
|
doSearchContent,
|
||||||
|
} from 'actions/search'
|
||||||
|
import {
|
||||||
|
selectIsSearching,
|
||||||
|
selectSearchQuery,
|
||||||
|
selectCurrentSearchResults,
|
||||||
|
selectSearchActivated,
|
||||||
|
} from 'selectors/search'
|
||||||
|
import {
|
||||||
|
doNavigate,
|
||||||
|
} from 'actions/app'
|
||||||
|
import SearchPage from './view'
|
||||||
|
|
||||||
|
const select = (state) => ({
|
||||||
|
isSearching: selectIsSearching(state),
|
||||||
|
query: selectSearchQuery(state),
|
||||||
|
results: selectCurrentSearchResults(state),
|
||||||
|
searchActive: selectSearchActivated(state),
|
||||||
|
})
|
||||||
|
|
||||||
|
const perform = (dispatch) => ({
|
||||||
|
navigate: (path) => dispatch(doNavigate(path)),
|
||||||
|
})
|
||||||
|
|
||||||
|
export default connect(select, perform)(SearchPage)
|
92
ui/js/page/search/view.jsx
Normal file
92
ui/js/page/search/view.jsx
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
import React from 'react';
|
||||||
|
import lbry from 'lbry';
|
||||||
|
import lbryio from 'lbryio';
|
||||||
|
import lbryuri from 'lbryuri';
|
||||||
|
import lighthouse from 'lighthouse';
|
||||||
|
import FileTile from 'component/fileTile'
|
||||||
|
import FileTileStream from 'component/fileTileStream'
|
||||||
|
import Link from 'component/link'
|
||||||
|
import {ToolTip} from 'component/tooltip.js';
|
||||||
|
import {BusyMessage} from 'component/common.js';
|
||||||
|
|
||||||
|
const SearchNoResults = (props) => {
|
||||||
|
const {
|
||||||
|
navigate,
|
||||||
|
query,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
return <section>
|
||||||
|
<span className="empty">
|
||||||
|
No one has checked anything in for {query} yet.
|
||||||
|
<Link label="Be the first" href="#" onClick={() => navigate('publish')} />
|
||||||
|
</span>
|
||||||
|
</section>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SearchResultList = (props) => {
|
||||||
|
const {
|
||||||
|
results,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const rows = [],
|
||||||
|
seenNames = {}; //fix this when the search API returns claim IDs
|
||||||
|
|
||||||
|
for (let {name, claim, claim_id, channel_name, channel_id, txid, nout} of results) {
|
||||||
|
const uri = lbryuri.build({
|
||||||
|
channelName: channel_name,
|
||||||
|
contentName: name,
|
||||||
|
claimId: channel_id || claim_id,
|
||||||
|
});
|
||||||
|
|
||||||
|
rows.push(
|
||||||
|
<FileTileStream key={uri} uri={uri} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div>{rows}</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const SearchResults = (props) => {
|
||||||
|
const {
|
||||||
|
searching,
|
||||||
|
results,
|
||||||
|
query,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
searching ?
|
||||||
|
<BusyMessage message="Looking up the Dewey Decimals" /> :
|
||||||
|
(results && results.length) ?
|
||||||
|
<SearchResultList {...props} /> :
|
||||||
|
<SearchNoResults {...props} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const SearchPage = (props) => {
|
||||||
|
const isValidUri = (query) => true
|
||||||
|
const {
|
||||||
|
query,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="main--single-column">
|
||||||
|
{ isValidUri(query) ?
|
||||||
|
<section className="section-spaced">
|
||||||
|
<h3 className="card-row__header">
|
||||||
|
Exact URL
|
||||||
|
<ToolTip label="?" body="This is the resolution of a LBRY URL and not controlled by LBRY Inc." className="tooltip--header" />
|
||||||
|
</h3>
|
||||||
|
<FileTile uri={query} showEmpty={true} />
|
||||||
|
</section> : '' }
|
||||||
|
<section className="section-spaced">
|
||||||
|
<h3 className="card-row__header">
|
||||||
|
Search Results for {query}
|
||||||
|
<ToolTip label="?" body="These search results are provided by LBRY, Inc." className="tooltip--header" />
|
||||||
|
</h3>
|
||||||
|
<SearchResults {...props} />
|
||||||
|
</section>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default SearchPage;
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {FormField, FormRow} from '../component/form.js';
|
import {FormField, FormRow} from '../component/form.js';
|
||||||
import NavSettings from 'component/navSettings';
|
import SubHeader from 'component/subHeader'
|
||||||
import lbry from '../lbry.js';
|
import lbry from '../lbry.js';
|
||||||
|
|
||||||
var SettingsPage = React.createClass({
|
var SettingsPage = React.createClass({
|
||||||
|
@ -91,7 +91,7 @@ var SettingsPage = React.createClass({
|
||||||
*/
|
*/
|
||||||
return (
|
return (
|
||||||
<main className="main--single-column">
|
<main className="main--single-column">
|
||||||
<NavSettings />
|
<SubHeader />
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<h3>Download Directory</h3>
|
<h3>Download Directory</h3>
|
||||||
|
|
35
ui/js/page/showPage/index.js
Normal file
35
ui/js/page/showPage/index.js
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import React from 'react'
|
||||||
|
import {
|
||||||
|
connect
|
||||||
|
} from 'react-redux'
|
||||||
|
import {
|
||||||
|
selectCurrentUri,
|
||||||
|
} from 'selectors/app'
|
||||||
|
import {
|
||||||
|
selectCurrentUriIsDownloaded,
|
||||||
|
} from 'selectors/file_info'
|
||||||
|
import {
|
||||||
|
selectCurrentUriClaim,
|
||||||
|
} from 'selectors/claims'
|
||||||
|
import {
|
||||||
|
selectCurrentUriFileInfo,
|
||||||
|
} from 'selectors/file_info'
|
||||||
|
import {
|
||||||
|
selectCurrentUriCostInfo,
|
||||||
|
} from 'selectors/cost_info'
|
||||||
|
import ShowPage from './view'
|
||||||
|
|
||||||
|
const select = (state) => ({
|
||||||
|
claim: selectCurrentUriClaim(state),
|
||||||
|
uri: selectCurrentUri(state),
|
||||||
|
isDownloaded: selectCurrentUriIsDownloaded(state),
|
||||||
|
fileInfo: selectCurrentUriFileInfo(state),
|
||||||
|
costInfo: selectCurrentUriCostInfo(state),
|
||||||
|
isFailed: false,
|
||||||
|
claimType: 'file',
|
||||||
|
})
|
||||||
|
|
||||||
|
const perform = (dispatch) => ({
|
||||||
|
})
|
||||||
|
|
||||||
|
export default connect(select, perform)(ShowPage)
|
72
ui/js/page/showPage/view.jsx
Normal file
72
ui/js/page/showPage/view.jsx
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
BusyMessage,
|
||||||
|
} from 'component/common';
|
||||||
|
import FilePage from 'page/filePage'
|
||||||
|
|
||||||
|
const ShowPage = (props) => {
|
||||||
|
const {
|
||||||
|
claim,
|
||||||
|
navigate,
|
||||||
|
claim: {
|
||||||
|
txid,
|
||||||
|
nout,
|
||||||
|
has_signature: hasSignature,
|
||||||
|
signature_is_valid: signatureIsValid,
|
||||||
|
value,
|
||||||
|
value: {
|
||||||
|
stream,
|
||||||
|
stream: {
|
||||||
|
metadata,
|
||||||
|
source,
|
||||||
|
metadata: {
|
||||||
|
title,
|
||||||
|
} = {},
|
||||||
|
source: {
|
||||||
|
contentType,
|
||||||
|
} = {},
|
||||||
|
} = {},
|
||||||
|
} = {},
|
||||||
|
},
|
||||||
|
uri,
|
||||||
|
isDownloaded,
|
||||||
|
fileInfo,
|
||||||
|
costInfo,
|
||||||
|
costInfo: {
|
||||||
|
cost,
|
||||||
|
includesData: costIncludesData,
|
||||||
|
} = {},
|
||||||
|
isFailed,
|
||||||
|
claimType,
|
||||||
|
} = props
|
||||||
|
|
||||||
|
const outpoint = txid + ':' + nout;
|
||||||
|
const uriLookupComplete = !!claim && Object.keys(claim).length
|
||||||
|
const pageTitle = metadata ? metadata.title : uri;
|
||||||
|
|
||||||
|
let innerContent = "";
|
||||||
|
|
||||||
|
if (!uriLookupComplete || isFailed) {
|
||||||
|
innerContent = <section className="card">
|
||||||
|
<div className="card__inner">
|
||||||
|
<div className="card__title-identity"><h1>{pageTitle}</h1></div>
|
||||||
|
</div>
|
||||||
|
<div className="card__content">
|
||||||
|
{ uriLookupComplete ?
|
||||||
|
<p>This location is not yet in use. { ' ' }<Link href="#" onClick={() => navigate('publish')} label="Put something here" />.</p> :
|
||||||
|
<BusyMessage message="Loading magic decentralized data..." />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</section>;
|
||||||
|
} else if (claimType == "channel") {
|
||||||
|
innerContent = <ChannelPage title={uri} />
|
||||||
|
} else {
|
||||||
|
innerContent = <FilePage uri={uri} />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className="main--single-column">{innerContent}</main>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ShowPage
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||||
import lbry from 'lbry.js';
|
import lbry from 'lbry.js';
|
||||||
import Link from 'component/link';
|
import Link from 'component/link';
|
||||||
import Modal from 'component/modal';
|
import Modal from 'component/modal';
|
||||||
import NavWallet from 'component/navWallet';
|
import SubHeader from 'component/subHeader'
|
||||||
import {
|
import {
|
||||||
FormField,
|
FormField,
|
||||||
FormRow
|
FormRow
|
||||||
|
@ -248,7 +248,7 @@ const WalletPage = (props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<main className="main--single-column">
|
<main className="main--single-column">
|
||||||
<NavWallet />
|
<SubHeader />
|
||||||
<section className="card">
|
<section className="card">
|
||||||
<div className="card__title-primary">
|
<div className="card__title-primary">
|
||||||
<h3>Balance</h3>
|
<h3>Balance</h3>
|
||||||
|
|
|
@ -8,7 +8,6 @@ const defaultState = {
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
upgradeSkipped: sessionStorage.getItem('upgradeSkipped'),
|
upgradeSkipped: sessionStorage.getItem('upgradeSkipped'),
|
||||||
daemonReady: false,
|
daemonReady: false,
|
||||||
platform: window.navigator.platform,
|
|
||||||
obscureNsfw: !lbry.getClientSetting('showNsfw'),
|
obscureNsfw: !lbry.getClientSetting('showNsfw'),
|
||||||
hidePrice: false,
|
hidePrice: false,
|
||||||
hasSignature: false,
|
hasSignature: false,
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import {createSelector} from 'reselect'
|
import {createSelector} from 'reselect'
|
||||||
|
import {
|
||||||
|
selectIsSearching,
|
||||||
|
selectSearchActivated,
|
||||||
|
} from 'selectors/search'
|
||||||
|
|
||||||
export const _selectState = state => state.app || {}
|
export const _selectState = state => state.app || {}
|
||||||
|
|
||||||
|
@ -14,7 +18,12 @@ export const selectCurrentPath = createSelector(
|
||||||
|
|
||||||
export const selectCurrentPage = createSelector(
|
export const selectCurrentPage = createSelector(
|
||||||
selectCurrentPath,
|
selectCurrentPath,
|
||||||
(path) => path.split('=')[0]
|
selectSearchActivated,
|
||||||
|
(path, searchActivated) => {
|
||||||
|
if (searchActivated) return 'search'
|
||||||
|
|
||||||
|
return path.split('=')[0]
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
export const selectCurrentUri = createSelector(
|
export const selectCurrentUri = createSelector(
|
||||||
|
@ -198,6 +207,12 @@ export const selectHeaderLinks = createSelector(
|
||||||
'downloaded': 'Downloaded',
|
'downloaded': 'Downloaded',
|
||||||
'published': 'Published',
|
'published': 'Published',
|
||||||
};
|
};
|
||||||
|
case 'settings':
|
||||||
|
case 'help':
|
||||||
|
return {
|
||||||
|
'settings': 'Settings',
|
||||||
|
'help': 'Help',
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,7 @@
|
||||||
"node-sass": "^3.13.0",
|
"node-sass": "^3.13.0",
|
||||||
"webpack": "^1.13.3",
|
"webpack": "^1.13.3",
|
||||||
"webpack-dev-server": "^2.4.4",
|
"webpack-dev-server": "^2.4.4",
|
||||||
|
"webpack-notifier": "^1.5.0",
|
||||||
"webpack-target-electron-renderer": "^0.4.0"
|
"webpack-target-electron-renderer": "^0.4.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
const WebpackNotifierPlugin = require('webpack-notifier')
|
||||||
|
|
||||||
const appPath = path.resolve(__dirname, 'js');
|
const appPath = path.resolve(__dirname, 'js');
|
||||||
|
|
||||||
const PATHS = {
|
const PATHS = {
|
||||||
|
@ -21,6 +23,9 @@ module.exports = {
|
||||||
root: appPath,
|
root: appPath,
|
||||||
extensions: ['', '.js', '.jsx', '.css'],
|
extensions: ['', '.js', '.jsx', '.css'],
|
||||||
},
|
},
|
||||||
|
plugins: [
|
||||||
|
new WebpackNotifierPlugin(),
|
||||||
|
],
|
||||||
module: {
|
module: {
|
||||||
preLoaders: [
|
preLoaders: [
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue