Merge branch 'v16-tip' into v16

This commit is contained in:
Jeremy Kauffman 2017-09-17 16:34:14 -04:00
commit f68136bd79
31 changed files with 412 additions and 399 deletions

View file

@ -20,7 +20,7 @@
"electron-rebuild": "^1.5.11"
},
"lbrySettings": {
"lbrynetDaemonVersion": "0.16.0rc8",
"lbrynetDaemonVersion": "0.16.0rc9",
"lbrynetDaemonUrlTemplate": "https://github.com/lbryio/lbry/releases/download/vDAEMONVER/lbrynet-daemon-vDAEMONVER-OSNAME.zip"
},
"license": "MIT"

View file

@ -1,56 +0,0 @@
import lbry from "lbry";
import { selectBalance } from "selectors/wallet";
import { doOpenModal, doShowSnackBar } from "actions/app";
import * as types from "constants/action_types";
import * as modals from "constants/modal_types";
export function doSendSupport(amount, claim_id) {
return function(dispatch, getState) {
const state = getState();
const balance = selectBalance(state);
if (balance - amount <= 0) {
return dispatch(doOpenModal(modals.INSUFFICIENT_BALANCE));
}
dispatch({
type: types.SUPPORT_TRANSACTION_STARTED,
});
const successCallback = results => {
if (results.txid) {
dispatch({
type: types.SUPPORT_TRANSACTION_COMPLETED,
});
dispatch(
doShowSnackBar({
message: __(`You sent ${amount} LBC as support, Mahalo!`),
linkText: __("History"),
linkTarget: __("/wallet"),
})
);
} else {
dispatch({
type: types.SUPPORT_TRANSACTION_FAILED,
data: { error: results },
});
dispatch(doOpenModal(modals.TRANSACTION_FAILED));
}
};
const errorCallback = error => {
dispatch({
type: types.SUPPORT_TRANSACTION_FAILED,
data: { error: error.message },
});
dispatch(doOpenModal(modals.TRANSACTION_FAILED));
};
lbry
.wallet_send({
claim_id: claim_id,
amount: amount,
})
.then(successCallback, errorCallback);
};
}

View file

@ -1,5 +1,6 @@
import * as types from "constants/action_types";
import {
computePageFromPath,
selectPageTitle,
selectCurrentPage,
selectCurrentParams,
@ -20,9 +21,17 @@ export function doNavigate(path, params = {}, options = {}) {
url += "?" + toQueryString(params);
}
dispatch(doChangePath(url, options));
const state = getState(),
currentPage = selectCurrentPage(state),
nextPage = computePageFromPath(path),
scrollY = options.scrollY;
const pageTitle = selectPageTitle(getState()) + " - LBRY";
if (currentPage != nextPage) {
//I wasn't seeing it scroll to the proper position without this -- possibly because the page isn't fully rendered? Not sure - Jeremy
setTimeout(() => {
window.scrollTo(0, scrollY ? scrollY : 0);
}, 100);
}
dispatch({
type: types.HISTORY_NAVIGATE,
@ -45,31 +54,6 @@ export function doAuthNavigate(pathAfterAuth = null, params = {}) {
};
}
export function doChangePath(path, options = {}) {
return function(dispatch, getState) {
dispatch({
type: types.CHANGE_PATH,
data: {
path,
},
});
const state = getState();
const scrollY = options.scrollY;
//I wasn't seeing it scroll to the proper position without this -- possibly because the page isn't fully rendered? Not sure - Jeremy
setTimeout(() => {
window.scrollTo(0, scrollY ? scrollY : 0);
}, 100);
const currentPage = selectCurrentPage(state);
if (currentPage === "search") {
const params = selectCurrentParams(state);
dispatch(doSearch(params.query));
}
};
}
export function doHistoryTraverse(dispatch, state, modifier) {
const stack = selectHistoryStack(state),
index = selectHistoryIndex(state) + modifier;

View file

@ -6,6 +6,7 @@ import {
selectBalance,
} from "selectors/wallet";
import { doOpenModal, doShowSnackBar } from "actions/app";
import { doNavigate } from "actions/navigation";
import * as modals from "constants/modal_types";
export function doUpdateBalance(balance) {
@ -143,3 +144,55 @@ export function doSetDraftTransactionAddress(address) {
data: { address },
};
}
export function doSendSupport(amount, claim_id, uri) {
return function(dispatch, getState) {
const state = getState();
const balance = selectBalance(state);
if (balance - amount <= 0) {
return dispatch(doOpenModal(modals.INSUFFICIENT_BALANCE));
}
dispatch({
type: types.SUPPORT_TRANSACTION_STARTED,
});
const successCallback = results => {
if (results.txid) {
dispatch({
type: types.SUPPORT_TRANSACTION_COMPLETED,
});
dispatch(
doShowSnackBar({
message: __(`You sent ${amount} LBC as support, Mahalo!`),
linkText: __("History"),
linkTarget: __("/wallet"),
})
);
dispatch(doNavigate("/show", { uri }));
} else {
dispatch({
type: types.SUPPORT_TRANSACTION_FAILED,
data: { error: results.code },
});
dispatch(doOpenModal(modals.TRANSACTION_FAILED));
}
};
const errorCallback = error => {
dispatch({
type: types.SUPPORT_TRANSACTION_FAILED,
data: { error: error.code },
});
dispatch(doOpenModal(modals.TRANSACTION_FAILED));
};
lbry
.wallet_send({
claim_id: claim_id,
amount: amount,
})
.then(successCallback, errorCallback);
};
}

View file

@ -1,13 +1,12 @@
import React from "react";
import { connect } from "react-redux";
import { selectPlatform } from "selectors/app";
import { makeSelectFileInfoForUri } from "selectors/file_info";
import { makeSelectCostInfoForUri } from "selectors/cost_info";
import { doOpenModal } from "actions/app";
import { doFetchAvailability } from "actions/availability";
import { doOpenFileInShell, doOpenFileInFolder } from "actions/file_info";
import { doOpenFileInShell } from "actions/file_info";
import { makeSelectClaimIsMine } from "selectors/claims";
import { doPurchaseUri, doLoadVideo, doStartDownload } from "actions/content";
import { doPurchaseUri, doStartDownload } from "actions/content";
import { doNavigate } from "actions/navigation";
import FileActions from "./view";
@ -20,11 +19,11 @@ const select = (state, props) => ({
const perform = dispatch => ({
checkAvailability: uri => dispatch(doFetchAvailability(uri)),
navigate: (path, params) => dispatch(doNavigate(path, params)),
openInShell: fileInfo => dispatch(doOpenFileInShell(fileInfo)),
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
startDownload: uri => dispatch(doPurchaseUri(uri)),
restartDownload: (uri, outpoint) => dispatch(doStartDownload(uri, outpoint)),
editClaim: claimId => dispatch(doNavigate("/publish", { id: claimId })),
});
export default connect(select, perform)(FileActions);

View file

@ -4,24 +4,11 @@ import FileDownloadLink from "component/fileDownloadLink";
import * as modals from "constants/modal_types";
class FileActions extends React.PureComponent {
handleSupportButtonClicked() {
this.props.onTipShow();
}
render() {
const {
fileInfo,
uri,
openModal,
claimIsMine,
editClaim,
checkAvailability,
} = this.props;
const { fileInfo, uri, openModal, claimIsMine } = this.props;
const claimId = fileInfo ? fileInfo.claim_id : null,
metadata = fileInfo ? fileInfo.metadata : null,
showMenu = fileInfo && Object.keys(fileInfo).length > 0,
title = metadata ? metadata.title : uri;
showDelete = fileInfo && Object.keys(fileInfo).length > 0;
return (
<section className="card__actions">
@ -30,22 +17,25 @@ class FileActions extends React.PureComponent {
button="text"
icon="icon-edit"
label={__("Edit")}
onClick={() => editClaim(claimId)}
navigate="/publish"
navigateParams={{ id: claimId }}
/>}
<FileDownloadLink uri={uri} />
<Link
button="text"
icon="icon-gift"
label={__("Support")}
onClick={this.handleSupportButtonClicked.bind(this)}
/>
<Link
button="text"
icon="icon-trash"
label={__("Remove")}
className="card__action--right"
onClick={() => openModal(modals.CONFIRM_FILE_REMOVE, { uri })}
navigate="/show"
navigateParams={{ uri, tab: "tip" }}
/>
{showDelete &&
<Link
button="text"
icon="icon-trash"
label={__("Remove")}
className="card__action--right"
onClick={() => openModal(modals.CONFIRM_FILE_REMOVE, { uri })}
/>}
</section>
);
}

View file

@ -0,0 +1,16 @@
import React from "react";
import { connect } from "react-redux";
import {
makeSelectClaimForUri,
makeSelectContentTypeForUri,
makeSelectMetadataForUri,
} from "selectors/claims";
import FileDetails from "./view";
const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state),
contentType: makeSelectContentTypeForUri(props.uri)(state),
metadata: makeSelectMetadataForUri(props.uri)(state),
});
export default connect(select, null)(FileDetails);

View file

@ -0,0 +1,64 @@
import React from "react";
import ReactMarkdown from "react-markdown";
import lbry from "lbry.js";
import FileActions from "component/fileActions";
import Link from "component/link";
import DateTime from "component/dateTime";
class FileDetails extends React.PureComponent {
render() {
const { claim, contentType, metadata, uri } = this.props;
if (!claim || !metadata) {
return (
<div className="card__content">
<span className="empty">{__("Empty claim or metadata info.")}</span>
</div>
);
}
const { description, language, license } = metadata;
const { height } = claim;
const mediaType = lbry.getMediaType(contentType);
return (
<div>
<FileActions uri={uri} />
<div className="card__content card__subtext card__subtext--allow-newlines">
<ReactMarkdown
source={description || ""}
escapeHtml={true}
disallowedTypes={["Heading", "HtmlInline", "HtmlBlock"]}
/>
</div>
<div className="card__content">
<table className="table-standard table-stretch">
<tbody>
<tr>
<td>{__("Published on")}</td>
<td><DateTime block={height} /></td>
</tr>
<tr>
<td>{__("Content-Type")}</td><td>{mediaType}</td>
</tr>
<tr>
<td>{__("Language")}</td><td>{language}</td>
</tr>
<tr>
<td>{__("License")}</td><td>{license}</td>
</tr>
</tbody>
</table>
<p>
<Link
href={`https://lbry.io/dmca?claim_id=${claim.claim_id}`}
label={__("report")}
/>
</p>
</div>
</div>
);
}
}
export default FileDetails;

View file

@ -49,7 +49,17 @@ const FileListSearchResults = props => {
class FileListSearch extends React.PureComponent {
componentWillMount() {
this.props.search(this.props.query);
this.doSearch(this.props);
}
componentWillReceiveProps(props) {
if (props.query != this.props.query) {
this.doSearch(props);
}
}
doSearch(props) {
this.props.search(props.query);
}
render() {

View file

@ -4,7 +4,7 @@ import { doNavigate } from "actions/navigation";
import Link from "./view";
const perform = dispatch => ({
doNavigate: path => dispatch(doNavigate(path)),
doNavigate: (path, params) => dispatch(doNavigate(path, params)),
});
export default connect(null, perform)(Link);

View file

@ -12,6 +12,7 @@ const Link = props => {
disabled,
children,
navigate,
navigateParams,
doNavigate,
} = props;
@ -23,7 +24,7 @@ const Link = props => {
const onClick = !props.onClick && navigate
? () => {
doNavigate(navigate);
doNavigate(navigate, navigateParams || {});
}
: props.onClick;

View file

@ -1,12 +0,0 @@
import React from "react";
import { connect } from "react-redux";
import { doSendSupport } from "actions/claims";
import TipLink from "./view";
const select = state => ({});
const perform = dispatch => ({
sendSupport: (amount, claim_id) => dispatch(doSendSupport(amount, claim_id)),
});
export default connect(select, perform)(TipLink);

View file

@ -1,71 +0,0 @@
import React from "react";
import Link from "component/link";
import { FormRow } from "component/form";
class TipLink extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
tipAmount: 1.0,
};
}
handleSendButtonClicked() {
let claim_id = this.props.claim_id;
let amount = this.state.tipAmount;
this.props.sendSupport(amount, claim_id);
this.props.onTipHide();
}
handleSupportCancelButtonClicked() {
this.props.onTipHide();
}
handleSupportPriceChange(event) {
this.setState({
tipAmount: Number(event.target.value),
});
}
render() {
return (
<div className="card__content">
<div className="card__title-primary">
<h4>{__("Support")}</h4>
</div>
<div className="card__content">
{__(
"Support the creator and the success of their content by sending a tip. "
)}
<Link label={__("Learn more")} href="https://lbry.io/faq/tipping" />
</div>
<div className="card__content">
<FormRow
label={__("Amount")}
postfix={__("LBC")}
min="0"
step="0.1"
type="number"
placeholder="1.00"
onChange={event => this.handleSupportPriceChange(event)}
/>
</div>
<div className="card__actions">
<Link
label={__("Send")}
button="primary"
onClick={this.handleSendButtonClicked.bind(this)}
/>
<Link
label={__("Cancel")}
button="alt"
onClick={this.handleSupportCancelButtonClicked.bind(this)}
/>
</div>
</div>
);
}
}
export default TipLink;

View file

@ -125,13 +125,26 @@ class TransactionTableBody extends React.PureComponent {
render() {
const { transactions, filter } = this.props;
let transactionList = transactions
.filter(this.filterList, this)
.filter(this.removeFeeTx, this)
.map(this.renderBody, this);
if (transactionList.length == 0) {
return (
<tbody>
<tr>
<td className="empty" colSpan="3">
{__("There are no transactions of this type.")}
</td>
</tr>
</tbody>
);
}
return (
<tbody>
{transactions
.filter(this.filterList, this)
.filter(this.removeFeeTx, this)
.map(this.renderBody, this)}
{transactionList}
</tbody>
);
}

View file

@ -1,5 +1,7 @@
import React from "react";
import { Icon } from "component/common";
import Link from "component/link";
import lbryuri from "lbryuri.js";
class UriIndicator extends React.PureComponent {
componentWillMount() {
@ -19,7 +21,7 @@ class UriIndicator extends React.PureComponent {
}
render() {
const { claim, uri, isResolvingUri } = this.props;
const { claim, link, uri, isResolvingUri } = this.props;
if (isResolvingUri && !claim) {
return <span className="empty">Validating...</span>;
@ -33,21 +35,30 @@ class UriIndicator extends React.PureComponent {
channel_name: channelName,
has_signature: hasSignature,
signature_is_valid: signatureIsValid,
value,
} = claim;
const channelClaimId =
value &&
value.publisherSignature &&
value.publisherSignature.certificateId;
if (!hasSignature || !channelName) {
return <span className="empty">Anonymous</span>;
}
let icon, modifier;
let icon, channelLink, modifier;
if (signatureIsValid) {
modifier = "valid";
channelLink = link
? lbryuri.build({ channelName, claimId: channelClaimId }, false)
: false;
} else {
icon = "icon-times-circle";
modifier = "invalid";
}
return (
const inner = (
<span>
{channelName} {" "}
{!signatureIsValid
@ -58,6 +69,16 @@ class UriIndicator extends React.PureComponent {
: ""}
</span>
);
if (!channelLink) {
return inner;
}
return (
<Link navigate="/show" navigateParams={{ uri: channelLink }}>
{inner}
</Link>
);
}
}

View file

@ -0,0 +1,18 @@
import React from "react";
import { connect } from "react-redux";
import { doSendSupport } from "actions/wallet";
import WalletSendTip from "./view";
import { makeSelectTitleForUri } from "selectors/claims";
import { selectIsSendingSupport } from "selectors/wallet";
const select = (state, props) => ({
isPending: selectIsSendingSupport(state),
title: makeSelectTitleForUri(props.uri)(state),
});
const perform = dispatch => ({
sendSupport: (amount, claim_id, uri) =>
dispatch(doSendSupport(amount, claim_id, uri)),
});
export default connect(select, perform)(WalletSendTip);

View file

@ -0,0 +1,79 @@
import React from "react";
import Link from "component/link";
import { FormRow } from "component/form";
import UriIndicator from "component/uriIndicator";
class WalletSendTip extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
amount: 0.0,
};
}
handleSendButtonClicked() {
const { claim_id, uri } = this.props;
let amount = this.state.amount;
this.props.sendSupport(amount, claim_id, uri);
}
handleSupportPriceChange(event) {
this.setState({
amount: Number(event.target.value),
});
}
render() {
const { errorMessage, isPending, title, uri } = this.props;
return (
<div>
<div className="card__title-primary">
<h1>{__("Support")} <UriIndicator uri={uri} /></h1>
</div>
<div className="card__content">
<FormRow
label={__("Amount")}
postfix={__("LBC")}
min="0"
step="0.1"
type="number"
errorMessage={errorMessage}
helper={
<span>
{__(
'This will appear as a tip for "%s" located at %s.',
title,
uri
) + " "}
<Link
label={__("Learn more")}
href="https://lbry.io/faq/tipping"
/>
</span>
}
placeholder="1.00"
onChange={event => this.handleSupportPriceChange(event)}
/>
<div className="form-row-submit">
<Link
label={__("Send")}
button="primary"
disabled={isPending}
onClick={this.handleSendButtonClicked.bind(this)}
/>
<Link
label={__("Cancel")}
button="alt"
navigate="/show"
navigateParams={{ uri }}
/>
</div>
</div>
</div>
);
}
}
export default WalletSendTip;

View file

@ -9,7 +9,6 @@ export const DAEMON_VERSION_MISMATCH = "DAEMON_VERSION_MISMATCH";
export const VOLUME_CHANGED = "VOLUME_CHANGED";
// Navigation
export const CHANGE_PATH = "CHANGE_PATH";
export const CHANGE_AFTER_AUTH_PATH = "CHANGE_AFTER_AUTH_PATH";
export const WINDOW_SCROLLED = "WINDOW_SCROLLED";
export const HISTORY_NAVIGATE = "HISTORY_NAVIGATE";
@ -40,8 +39,11 @@ export const SEND_TRANSACTION_STARTED = "SEND_TRANSACTION_STARTED";
export const SEND_TRANSACTION_COMPLETED = "SEND_TRANSACTION_COMPLETED";
export const SEND_TRANSACTION_FAILED = "SEND_TRANSACTION_FAILED";
export const FETCH_BLOCK_SUCCESS = "FETCH_BLOCK_SUCCESS";
export const SUPPORT_TRANSACTION_STARTED = "SUPPORT_TRANSACTION_STARTED";
export const SUPPORT_TRANSACTION_COMPLETED = "SUPPORT_TRANSACTION_COMPLETED";
export const SUPPORT_TRANSACTION_FAILED = "SUPPORT_TRANSACTION_FAILED";
// Content
// Claims
export const FETCH_FEATURED_CONTENT_STARTED = "FETCH_FEATURED_CONTENT_STARTED";
export const FETCH_FEATURED_CONTENT_COMPLETED =
"FETCH_FEATURED_CONTENT_COMPLETED";
@ -57,6 +59,19 @@ export const FETCH_CHANNEL_CLAIM_COUNT_COMPLETED =
export const FETCH_CLAIM_LIST_MINE_STARTED = "FETCH_CLAIM_LIST_MINE_STARTED";
export const FETCH_CLAIM_LIST_MINE_COMPLETED =
"FETCH_CLAIM_LIST_MINE_COMPLETED";
export const ABANDON_CLAIM_STARTED = "ABANDON_CLAIM_STARTED";
export const ABANDON_CLAIM_SUCCEEDED = "ABANDON_CLAIM_SUCCEEDED";
export const FETCH_CHANNEL_LIST_MINE_STARTED =
"FETCH_CHANNEL_LIST_MINE_STARTED";
export const FETCH_CHANNEL_LIST_MINE_COMPLETED =
"FETCH_CHANNEL_LIST_MINE_COMPLETED";
export const CREATE_CHANNEL_STARTED = "CREATE_CHANNEL_STARTED";
export const CREATE_CHANNEL_COMPLETED = "CREATE_CHANNEL_COMPLETED";
export const PUBLISH_STARTED = "PUBLISH_STARTED";
export const PUBLISH_COMPLETED = "PUBLISH_COMPLETED";
export const PUBLISH_FAILED = "PUBLISH_FAILED";
// Files
export const FILE_LIST_STARTED = "FILE_LIST_STARTED";
export const FILE_LIST_SUCCEEDED = "FILE_LIST_SUCCEEDED";
export const FETCH_FILE_INFO_STARTED = "FETCH_FILE_INFO_STARTED";
@ -73,17 +88,6 @@ 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 FILE_DELETE = "FILE_DELETE";
export const ABANDON_CLAIM_STARTED = "ABANDON_CLAIM_STARTED";
export const ABANDON_CLAIM_SUCCEEDED = "ABANDON_CLAIM_SUCCEEDED";
export const FETCH_CHANNEL_LIST_MINE_STARTED =
"FETCH_CHANNEL_LIST_MINE_STARTED";
export const FETCH_CHANNEL_LIST_MINE_COMPLETED =
"FETCH_CHANNEL_LIST_MINE_COMPLETED";
export const CREATE_CHANNEL_STARTED = "CREATE_CHANNEL_STARTED";
export const CREATE_CHANNEL_COMPLETED = "CREATE_CHANNEL_COMPLETED";
export const PUBLISH_STARTED = "PUBLISH_STARTED";
export const PUBLISH_COMPLETED = "PUBLISH_COMPLETED";
export const PUBLISH_FAILED = "PUBLISH_FAILED";
// Search
export const SEARCH_STARTED = "SEARCH_STARTED";
@ -132,11 +136,6 @@ export const CLAIM_REWARD_FAILURE = "CLAIM_REWARD_FAILURE";
export const CLAIM_REWARD_CLEAR_ERROR = "CLAIM_REWARD_CLEAR_ERROR";
export const FETCH_REWARD_CONTENT_COMPLETED = "FETCH_REWARD_CONTENT_COMPLETED";
// Supports
export const SUPPORT_TRANSACTION_STARTED = "SUPPORT_TRANSACTION_STARTED";
export const SUPPORT_TRANSACTION_COMPLETED = "SUPPORT_TRANSACTION_COMPLETED";
export const SUPPORT_TRANSACTION_FAILED = "SUPPORT_TRANSACTION_FAILED";
//Language
export const DOWNLOAD_LANGUAGE_SUCCEEDED = "DOWNLOAD_LANGUAGE_SUCCEEDED";
export const DOWNLOAD_LANGUAGE_FAILED = "DOWNLOAD_LANGUAGE_FAILED";

View file

@ -2,16 +2,13 @@ import React from "react";
import { connect } from "react-redux";
import { doCloseModal } from "actions/app";
import { doDeleteFileAndGoBack } from "actions/file_info";
import {
makeSelectMetadataForUri,
makeSelectClaimIsMine,
} from "selectors/claims";
import { makeSelectTitleForUri, makeSelectClaimIsMine } from "selectors/claims";
import { makeSelectFileInfoForUri } from "selectors/file_info";
import ModalRemoveFile from "./view";
const select = (state, props) => ({
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
metadata: makeSelectMetadataForUri(props.uri)(state),
title: makeSelectTitleForUri(props.uri)(state),
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
});

View file

@ -30,7 +30,7 @@ class ModalRemoveFile extends React.PureComponent {
closeModal,
deleteFile,
fileInfo: { outpoint },
metadata: { title },
title,
} = this.props;
const { deleteChecked, abandonClaimChecked } = this.state;

View file

@ -13,6 +13,7 @@ import {
import { makeSelectCostInfoForUri } from "selectors/cost_info";
import { selectShowNsfw } from "selectors/settings";
import FilePage from "./view";
import { makeSelectCurrentParam } from "../../selectors/navigation";
const select = (state, props) => ({
claim: makeSelectClaimForUri(props.uri)(state),
@ -20,6 +21,7 @@ const select = (state, props) => ({
costInfo: makeSelectCostInfoForUri(props.uri)(state),
metadata: makeSelectMetadataForUri(props.uri)(state),
obscureNsfw: !selectShowNsfw(state),
tab: makeSelectCurrentParam("tab")(state),
fileInfo: makeSelectFileInfoForUri(props.uri)(state),
rewardedContentClaimIds: selectRewardContentClaimIds(state, props),
});

View file

@ -1,55 +1,15 @@
import React from "react";
import ReactMarkdown from "react-markdown";
import lbry from "lbry.js";
import lbryuri from "lbryuri.js";
import Video from "component/video";
import TipLink from "component/tipLink";
import { Thumbnail } from "component/common";
import FilePrice from "component/filePrice";
import FileActions from "component/fileActions";
import Link from "component/link";
import FileDetails from "component/fileDetails";
import UriIndicator from "component/uriIndicator";
import IconFeatured from "component/iconFeatured";
import DateTime from "component/dateTime";
const FormatItem = props => {
const {
publishedDate,
contentType,
claim: { height },
metadata: { language, license },
} = props;
const mediaType = lbry.getMediaType(contentType);
return (
<table className="table-standard table-stretch">
<tbody>
<tr>
<td>{__("Published on")}</td><td><DateTime block={height} /></td>
</tr>
<tr>
<td>{__("Content-Type")}</td><td>{mediaType}</td>
</tr>
<tr>
<td>{__("Language")}</td><td>{language}</td>
</tr>
<tr>
<td>{__("License")}</td><td>{license}</td>
</tr>
</tbody>
</table>
);
};
import WalletSendTip from "component/walletSendTip";
class FilePage extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
showTipBox: false,
};
}
componentDidMount() {
this.fetchFileInfo(this.props);
this.fetchCostInfo(this.props);
@ -71,29 +31,18 @@ class FilePage extends React.PureComponent {
}
}
handleTipShow() {
this.setState({
showTipBox: true,
});
}
handleTipHide() {
this.setState({
showTipBox: false,
});
}
render() {
const {
claim,
fileInfo,
metadata,
contentType,
tab,
uri,
rewardedContentClaimIds,
} = this.props;
const { showTipBox } = this.state;
const showTipBox = tab == "tip";
if (!claim || !metadata) {
return (
@ -101,24 +50,7 @@ class FilePage extends React.PureComponent {
);
}
const {
txid,
nout,
channel_name: channelName,
has_signature: hasSignature,
signature_is_valid: signatureIsValid,
value,
} = claim;
const outpoint = txid + ":" + nout;
const title = metadata.title;
const channelClaimId = claim.value && claim.value.publisherSignature
? claim.value.publisherSignature.certificateId
: null;
const channelUri = signatureIsValid && hasSignature && channelName
? lbryuri.build({ channelName, claimId: channelClaimId }, false)
: null;
const uriIndicator = <UriIndicator uri={uri} />;
const isRewardContent = rewardedContentClaimIds.includes(claim.claim_id);
const mediaType = lbry.getMediaType(contentType);
const player = require("render-media");
@ -128,7 +60,7 @@ class FilePage extends React.PureComponent {
mediaType === "audio";
return (
<main className="main--single-column">
<div>
<section className="show-page-media">
{isPlayable
? <Video className="video-embedded" uri={uri} />
@ -138,64 +70,28 @@ class FilePage extends React.PureComponent {
</section>
<section className={"card " + (obscureNsfw ? "card--obscured " : "")}>
<div className="card__inner">
<div className="card__title-identity">
{!fileInfo || fileInfo.written_bytes <= 0
? <span style={{ float: "right" }}>
<FilePrice uri={lbryuri.normalize(uri)} />
{isRewardContent && <span>{" "}<IconFeatured /></span>}
</span>
: null}
<h1>{title}</h1>
<div className="card__subtitle">
{channelUri
? <Link
onClick={() =>
this.props.navigate("/show", { uri: channelUri })}
>
{uriIndicator}
</Link>
: uriIndicator}
</div>
<FileActions
uri={uri}
onTipShow={this.handleTipShow.bind(this)}
/>
</div>
{!showTipBox &&
<div className="card__content card__subtext card__subtext card__subtext--allow-newlines">
<ReactMarkdown
source={(metadata && metadata.description) || ""}
escapeHtml={true}
disallowedTypes={["Heading", "HtmlInline", "HtmlBlock"]}
/>
{(!tab || tab === "details") &&
<div>
{" "} {" "}
<div className="card__title-identity">
{!fileInfo || fileInfo.written_bytes <= 0
? <span style={{ float: "right" }}>
<FilePrice uri={lbryuri.normalize(uri)} />
{isRewardContent && <span>{" "}<IconFeatured /></span>}
</span>
: null}
<h1>{title}</h1>
<div className="card__subtitle">
<UriIndicator uri={uri} link={true} />
</div>
</div>
<FileDetails uri={uri} />
</div>}
{tab === "tip" &&
<WalletSendTip claim_id={claim.claim_id} uri={uri} />}
</div>
{metadata && claim && !showTipBox
? <div className="card__content">
<FormatItem
metadata={metadata}
contentType={contentType}
claim={claim}
/>
</div>
: ""}
{showTipBox
? <TipLink
onTipShow={this.handleTipShow.bind(this)}
onTipHide={this.handleTipHide.bind(this)}
claim_id={claim.claim_id}
/>
: ""}
{!showTipBox &&
<div className="card__content">
<Link
href={`https://lbry.io/dmca?claim_id=${claim.claim_id}`}
label={__("report")}
className="button-text-help"
/>
</div>}
</section>
</main>
</div>
);
}
}

View file

@ -1,6 +1,7 @@
import * as types from "constants/action_types";
const reducers = {};
const defaultState = {};
reducers[types.RESOLVE_URI_COMPLETED] = function(state, action) {
@ -189,31 +190,6 @@ reducers[types.CREATE_CHANNEL_COMPLETED] = function(state, action) {
});
};
reducers[types.SUPPORT_TRANSACTION_STARTED] = function(state, action) {
const newSupportTransaction = Object.assign({}, state.supportTransaction, {
sendingSupport: true,
});
return Object.assign({}, state, {
supportTransaction: newSupportTransaction,
});
};
reducers[types.SUPPORT_TRANSACTION_COMPLETED] = function(state, action) {
return Object.assign({}, state);
};
reducers[types.SUPPORT_TRANSACTION_FAILED] = function(state, action) {
const newSupportTransaction = Object.assign({}, state.supportTransaction, {
sendingSupport: false,
error: action.data.error,
});
return Object.assign({}, state, {
supportTransaction: newSupportTransaction,
});
};
export default function reducer(state = defaultState, action) {
const handler = reducers[action.type];
if (handler) return handler(state, action);

View file

@ -24,12 +24,6 @@ reducers[types.DAEMON_READY] = function(state, action) {
});
};
reducers[types.CHANGE_PATH] = function(state, action) {
return Object.assign({}, state, {
currentPath: action.data.path,
});
};
reducers[types.CHANGE_AFTER_AUTH_PATH] = function(state, action) {
return Object.assign({}, state, {
pathAfterAuth: action.data.path,
@ -38,15 +32,16 @@ reducers[types.CHANGE_AFTER_AUTH_PATH] = function(state, action) {
reducers[types.HISTORY_NAVIGATE] = (state, action) => {
const { stack, index } = state;
let newState = {};
const path = action.data.url;
// Check for duplicated
let newState = {
currentPath: path,
};
if (action.data.index >= 0) {
newState.index = action.data.index;
} else if (!stack[index] || stack[index].path !== path) {
// ^ Check for duplicated
newState.stack = [...stack.slice(0, index + 1), { path, scrollY: 0 }];
newState.index = newState.stack.length - 1;
}

View file

@ -1,5 +1,4 @@
import * as types from "constants/action_types";
import lbryuri from "lbryuri";
const reducers = {};
const defaultState = {};
@ -9,7 +8,6 @@ reducers[types.SEARCH_STARTED] = function(state, action) {
return Object.assign({}, state, {
searching: true,
query: query,
});
};
@ -31,7 +29,6 @@ reducers[types.SEARCH_COMPLETED] = function(state, action) {
reducers[types.SEARCH_CANCELLED] = function(state, action) {
return Object.assign({}, state, {
searching: false,
query: undefined,
});
};

View file

@ -15,6 +15,7 @@ const defaultState = {
receiveAddress: address,
gettingNewAddress: false,
draftTransaction: buildDraftTransaction(),
sendingSupport: false,
};
reducers[types.FETCH_TRANSACTIONS_STARTED] = function(state, action) {
@ -125,6 +126,25 @@ reducers[types.SEND_TRANSACTION_FAILED] = function(state, action) {
});
};
reducers[types.SUPPORT_TRANSACTION_STARTED] = function(state, action) {
return Object.assign({}, state, {
sendingSupport: true,
});
};
reducers[types.SUPPORT_TRANSACTION_COMPLETED] = function(state, action) {
return Object.assign({}, state, {
sendingSupport: false,
});
};
reducers[types.SUPPORT_TRANSACTION_FAILED] = function(state, action) {
return Object.assign({}, state, {
error: action.data.error,
sendingSupport: false,
});
};
reducers[types.FETCH_BLOCK_SUCCESS] = (state, action) => {
const { block, block: { height } } = action.data,
blocks = Object.assign({}, state.blocks);

View file

@ -66,7 +66,7 @@ export const selectAllFetchingChannelClaims = createSelector(
);
export const makeSelectFetchingChannelClaims = uri => {
createSelector(
return createSelector(
selectAllFetchingChannelClaims,
fetching => fetching && fetching[uri]
);
@ -97,6 +97,13 @@ export const makeSelectMetadataForUri = uri => {
});
};
export const makeSelectTitleForUri = uri => {
return createSelector(
makeSelectMetadataForUri(uri),
metadata => metadata && metadata.title
);
};
export const makeSelectContentTypeForUri = uri => {
return createSelector(makeSelectClaimForUri(uri), claim => {
const source =

View file

@ -10,8 +10,11 @@ export const selectCurrentPath = createSelector(
state => state.currentPath
);
export const computePageFromPath = path =>
path.replace(/^\//, "").split("?")[0];
export const selectCurrentPage = createSelector(selectCurrentPath, path => {
return path.replace(/^\//, "").split("?")[0];
return computePageFromPath(path);
});
export const selectCurrentParams = createSelector(selectCurrentPath, path => {

View file

@ -8,8 +8,9 @@ import {
export const _selectState = state => state.search || {};
export const selectSearchQuery = createSelector(
_selectState,
state => state.query
selectCurrentPage,
selectCurrentParams,
(page, params) => (page === "search" ? params && params.query : null)
);
export const selectIsSearching = createSelector(

View file

@ -61,6 +61,11 @@ export const selectIsFetchingTransactions = createSelector(
state => state.fetchingTransactions
);
export const selectIsSendingSupport = createSelector(
_selectState,
state => state.sendingSupport
);
export const selectReceiveAddress = createSelector(
_selectState,
state => state.receiveAddress

View file

@ -7,6 +7,9 @@
border-radius: var(--card-radius);
margin-bottom: var(--card-margin);
overflow: auto;
//below added to prevent scrollbar on long titles when show page loads, would prefer a cleaner CSS solution
overflow-x: hidden;
}
.card--obscured
{
@ -62,6 +65,9 @@
.card__content {
margin-top: var(--card-margin);
margin-bottom: var(--card-margin);
table:not(:last-child) {
margin-bottom: var(--card-margin);
}
}
$font-size-subtext-multiple: 0.82;
.card__subtext {