feat: setup Danger #1289
183 changed files with 1966 additions and 3930 deletions
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -8,11 +8,23 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
|||
### Added
|
||||
* Add keyboard shortcut to quit the app on Windows ([#1202](https://github.com/lbryio/lbry-app/pull/1202))
|
||||
* Build for both architectures (x86 and x64) for Windows ([#1262](https://github.com/lbryio/lbry-app/pull/1262))
|
||||
* Add referral FAQ to Invites screen([#1314](https://github.com/lbryio/lbry-app/pull/1314))
|
||||
* Show exact wallet balance on mouse hover over ([#1305](https://github.com/lbryio/lbry-app/pull/1305))
|
||||
* New dark mode ([#1269](https://github.com/lbryio/lbry-app/pull/1269))
|
||||
* Pre-fill publish URL after clicking "Put something here" link ([#1303](https://github.com/lbryio/lbry-app/pull/1303))
|
||||
|
||||
### Changed
|
||||
* Add flair to snackbar ([#1313](https://github.com/lbryio/lbry-app/pull/1313))
|
||||
|
||||
### Fixed
|
||||
* Black screen on macOS after maximizing LBRY and then closing ([#1235](https://github.com/lbryio/lbry-app/pull/1235))
|
||||
|
||||
### Fixed
|
||||
* Black screen on macOS after maximizing LBRY and then closing ([#1235](https://github.com/lbryio/lbry-app/pull/1235))
|
||||
* Fix dark theme ([#1034](https://github.com/lbryio/lbry-app/issues/1034))
|
||||
* Fix download percentage indicator overlay ([#1271](https://github.com/lbryio/lbry-app/issues/1271))
|
||||
* Fix alternate row shading for transactions on dark theme ([#1355](https://github.com/lbryio/lbry-app/issues/#1355))
|
||||
* Fix Description box on Publish (dark theme) ([#1356](https://github.com/lbryio/lbry-app/issues/#1356))
|
||||
|
||||
|
||||
## [0.21.2] - 2018-03-22
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
"find-process": "^1.1.0",
|
||||
"formik": "^0.10.4",
|
||||
"keytar": "^4.2.1",
|
||||
"lbry-redux": "lbryio/lbry-redux",
|
||||
"localforage": "^1.7.1",
|
||||
"mixpanel-browser": "^2.17.1",
|
||||
"moment": "^2.22.0",
|
||||
|
@ -60,7 +61,7 @@
|
|||
"react-modal": "^3.1.7",
|
||||
"react-paginate": "^5.2.1",
|
||||
"react-redux": "^5.0.3",
|
||||
"react-simplemde-editor": "^3.6.11",
|
||||
"react-simplemde-editor": "3.6.11",
|
||||
"react-transition-group": "1.x",
|
||||
"redux": "^3.6.0",
|
||||
"redux-logger": "^3.0.1",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doShowSnackBar } from 'redux/actions/app';
|
||||
import { doNotify } from 'lbry-redux';
|
||||
import Address from './view';
|
||||
|
||||
export default connect(null, {
|
||||
doShowSnackBar,
|
||||
doNotify,
|
||||
})(Address);
|
||||
|
|
|
@ -7,7 +7,7 @@ import * as icons from 'constants/icons';
|
|||
|
||||
type Props = {
|
||||
address: string,
|
||||
doShowSnackBar: ({ message: string }) => void,
|
||||
doNotify: ({ message: string, displayType: Array<string> }) => void,
|
||||
};
|
||||
|
||||
export default class Address extends React.PureComponent<Props> {
|
||||
|
@ -20,10 +20,10 @@ export default class Address extends React.PureComponent<Props> {
|
|||
input: ?HTMLInputElement;
|
||||
|
||||
render() {
|
||||
const { address, doShowSnackBar } = this.props;
|
||||
const { address, doNotify } = this.props;
|
||||
|
||||
return (
|
||||
<FormRow verticallyCentered padded>
|
||||
<FormRow verticallyCentered padded stretch>
|
||||
<input
|
||||
className="input-copyable form-field__input"
|
||||
readOnly
|
||||
|
@ -43,7 +43,10 @@ export default class Address extends React.PureComponent<Props> {
|
|||
icon={icons.CLIPBOARD}
|
||||
onClick={() => {
|
||||
clipboard.writeText(address);
|
||||
doShowSnackBar({ message: __('Address copied') });
|
||||
doNotify({
|
||||
message: __('Address copied'),
|
||||
displayType: ['snackbar'],
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</FormRow>
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
import { connect } from 'react-redux';
|
||||
import {
|
||||
selectPageTitle,
|
||||
selectHistoryIndex,
|
||||
selectActiveHistoryEntry,
|
||||
} from 'redux/selectors/navigation';
|
||||
import { selectPageTitle, selectHistoryIndex, selectActiveHistoryEntry } from 'lbry-redux';
|
||||
import { doRecordScroll } from 'redux/actions/navigation';
|
||||
import { selectUser } from 'redux/selectors/user';
|
||||
import { doAlertError } from 'redux/actions/app';
|
||||
import { doRecordScroll } from 'redux/actions/navigation';
|
||||
import App from './view';
|
||||
|
||||
const select = state => ({
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import Button from './view';
|
||||
|
|
|
@ -22,9 +22,7 @@ const autoThumbColors = [
|
|||
];
|
||||
|
||||
class CardMedia extends React.PureComponent<Props> {
|
||||
getAutoThumbClass = () => {
|
||||
return autoThumbColors[Math.floor(Math.random() * autoThumbColors.length)];
|
||||
};
|
||||
getAutoThumbClass = () => autoThumbColors[Math.floor(Math.random() * autoThumbColors.length)];
|
||||
|
||||
render() {
|
||||
const { thumbnail, nsfw } = this.props;
|
||||
|
|
|
@ -158,7 +158,7 @@ class CardVerify extends React.Component {
|
|||
render() {
|
||||
return (
|
||||
<Button
|
||||
button="alt"
|
||||
button="primary"
|
||||
label={this.props.label}
|
||||
icon={icons.LOCK}
|
||||
disabled={this.props.disabled || this.state.open || this.hasPendingClick}
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||
import { doResolveUri, makeSelectClaimForUri, makeSelectIsUriResolving } from 'lbry-redux';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { doResolveUri } from 'redux/actions/content';
|
||||
import { makeSelectTotalItemsForChannel } from 'redux/selectors/content';
|
||||
import { makeSelectIsUriResolving } from 'redux/selectors/content';
|
||||
import ChannelTile from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
|
|
|
@ -5,12 +5,10 @@ type Props = {
|
|||
message: ?string,
|
||||
};
|
||||
|
||||
const BusyIndicator = (props: Props) => {
|
||||
return (
|
||||
<span className="busy-indicator">
|
||||
{props.message} <span className="busy-indicator__loader" />
|
||||
</span>
|
||||
);
|
||||
};
|
||||
const BusyIndicator = (props: Props) => (
|
||||
<span className="busy-indicator">
|
||||
{props.message} <span className="busy-indicator__loader" />
|
||||
</span>
|
||||
);
|
||||
|
||||
export default BusyIndicator;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { normalizeURI } from 'lbryURI';
|
||||
import { normalizeURI } from 'lbry-redux';
|
||||
import ToolTip from 'component/common/tooltip';
|
||||
import FileCard from 'component/fileCard';
|
||||
import Button from 'component/button';
|
||||
|
@ -234,8 +234,7 @@ class CategoryList extends React.PureComponent<Props, State> {
|
|||
}}
|
||||
className="card-row__scrollhouse"
|
||||
>
|
||||
{names &&
|
||||
names.map(name => <FileCard key={name} displayStyle="card" uri={normalizeURI(name)} />)}
|
||||
{names && names.map(name => <FileCard key={name} uri={normalizeURI(name)} />)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -25,9 +25,10 @@ export class FormFieldPrice extends React.PureComponent<Props> {
|
|||
|
||||
handleAmountChange(event: SyntheticInputEvent<*>) {
|
||||
const { price, onChange } = this.props;
|
||||
const amount = event.target.value ? parseFloat(event.target.value) : '';
|
||||
onChange({
|
||||
currency: price.currency,
|
||||
amount: parseFloat(event.target.value),
|
||||
amount,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -50,7 +51,7 @@ export class FormFieldPrice extends React.PureComponent<Props> {
|
|||
type="number"
|
||||
className="form-field input--price-amount"
|
||||
min={min}
|
||||
value={price.amount || ''}
|
||||
value={price.amount}
|
||||
onChange={this.handleAmountChange}
|
||||
placeholder={placeholder || 5}
|
||||
disabled={disabled}
|
||||
|
|
|
@ -36,6 +36,8 @@ export class FormField extends React.PureComponent<Props> {
|
|||
...inputProps
|
||||
} = this.props;
|
||||
|
||||
const errorMessage = typeof error === 'object' ? error.message : error;
|
||||
|
||||
let input;
|
||||
if (type) {
|
||||
if (type === 'select') {
|
||||
|
@ -54,6 +56,8 @@ export class FormField extends React.PureComponent<Props> {
|
|||
/>
|
||||
</div>
|
||||
);
|
||||
} else if (type === 'textarea') {
|
||||
input = <textarea type={type} id={name} {...inputProps} />;
|
||||
} else {
|
||||
input = <input type={type} id={name} {...inputProps} />;
|
||||
}
|
||||
|
@ -65,13 +69,13 @@ export class FormField extends React.PureComponent<Props> {
|
|||
'form-field--stretch': stretch || type === 'markdown',
|
||||
})}
|
||||
>
|
||||
{(label || error) && (
|
||||
{(label || errorMessage) && (
|
||||
<label
|
||||
className={classnames('form-field__label', { 'form-field__error': error })}
|
||||
className={classnames('form-field__label', { 'form-field__error': errorMessage })}
|
||||
htmlFor={name}
|
||||
>
|
||||
{!error && label}
|
||||
{error}
|
||||
{!errorMessage && label}
|
||||
{errorMessage}
|
||||
</label>
|
||||
)}
|
||||
<div
|
||||
|
|
|
@ -8,6 +8,7 @@ type Props = {
|
|||
children: React.Node,
|
||||
padded?: boolean,
|
||||
verticallyCentered?: boolean,
|
||||
stretch?: boolean,
|
||||
};
|
||||
|
||||
export class FormRow extends React.PureComponent<Props> {
|
||||
|
@ -16,13 +17,14 @@ export class FormRow extends React.PureComponent<Props> {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { centered, children, padded, verticallyCentered } = this.props;
|
||||
const { centered, children, padded, verticallyCentered, stretch } = this.props;
|
||||
return (
|
||||
<div
|
||||
className={classnames('form-row', {
|
||||
'form-row--centered': centered,
|
||||
'form-row--padded': padded,
|
||||
'form-row--vertically-centered': verticallyCentered,
|
||||
'form-row--stretch': stretch,
|
||||
})}
|
||||
>
|
||||
{children}
|
||||
|
|
|
@ -8,7 +8,6 @@ const RED_COLOR = '#e2495e';
|
|||
|
||||
type Props = {
|
||||
icon: string,
|
||||
size?: number,
|
||||
};
|
||||
|
||||
class IconComponent extends React.PureComponent<Props> {
|
||||
|
|
|
@ -1,18 +1,30 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import QRCodeElement from 'qrcode.react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
type Props = {
|
||||
value: string,
|
||||
paddingRight?: boolean,
|
||||
};
|
||||
|
||||
const QRCode = (props: Props) => {
|
||||
const { value } = props;
|
||||
return (
|
||||
<div className="qr-code">
|
||||
<QRCodeElement value={value} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
class QRCode extends React.Component<Props> {
|
||||
static defaultProps = {
|
||||
paddingRight: false,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { value, paddingRight } = this.props;
|
||||
return (
|
||||
<div
|
||||
className={classnames('qr-code', {
|
||||
'qr-code--right-padding': paddingRight,
|
||||
})}
|
||||
>
|
||||
<QRCodeElement value={value} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default QRCode;
|
||||
|
|
|
@ -9,7 +9,7 @@ type Props = {
|
|||
const TransactionLink = (props: Props) => {
|
||||
const { id } = props;
|
||||
|
||||
const href = `https://explorer.lbry.io/#!/transaction/${id}`;
|
||||
const href = `https://explorer.lbry.io/tx/${id}`;
|
||||
const label = id.substr(0, 7);
|
||||
|
||||
return <Button button="link" href={href} label={label} />;
|
||||
|
|
|
@ -6,12 +6,10 @@ type Props = {
|
|||
children: React.Node,
|
||||
};
|
||||
|
||||
const TruncatedText = (props: Props) => {
|
||||
return (
|
||||
<span className="truncated-text" style={{ WebkitLineClamp: props.lines }}>
|
||||
{props.children}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
const TruncatedText = (props: Props) => (
|
||||
<span className="truncated-text" style={{ WebkitLineClamp: props.lines }}>
|
||||
{props.children}
|
||||
</span>
|
||||
);
|
||||
|
||||
export default TruncatedText;
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { makeSelectBlockDate } from 'redux/selectors/wallet';
|
||||
import { doFetchBlock } from 'redux/actions/wallet';
|
||||
import { doFetchBlock, makeSelectBlockDate } from 'lbry-redux';
|
||||
import DateTime from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { makeSelectFileInfoForUri } from 'redux/selectors/file_info';
|
||||
import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info';
|
||||
import { doOpenModal } from 'redux/actions/app';
|
||||
import { makeSelectClaimIsMine } from 'redux/selectors/claims';
|
||||
import {
|
||||
makeSelectCostInfoForUri,
|
||||
makeSelectFileInfoForUri,
|
||||
makeSelectClaimIsMine,
|
||||
doNotify,
|
||||
} from 'lbry-redux';
|
||||
import FileActions from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
|
@ -14,7 +15,7 @@ const select = (state, props) => ({
|
|||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
|
||||
openModal: (modal, props) => dispatch(doNotify(modal, props)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(FileActions);
|
||||
|
|
|
@ -12,7 +12,7 @@ type FileInfo = {
|
|||
|
||||
type Props = {
|
||||
uri: string,
|
||||
openModal: (string, any) => void,
|
||||
openModal: ({ id: string }, { uri: string }) => void,
|
||||
claimIsMine: boolean,
|
||||
fileInfo: FileInfo,
|
||||
vertical?: boolean, // should the buttons be stacked vertically?
|
||||
|
@ -33,7 +33,7 @@ class FileActions extends React.PureComponent<Props> {
|
|||
className="btn--file-actions"
|
||||
icon={icons.TRASH}
|
||||
description={__('Delete')}
|
||||
onClick={() => openModal(modals.CONFIRM_FILE_REMOVE, { uri })}
|
||||
onClick={() => openModal({ id: modals.CONFIRM_FILE_REMOVE }, { uri })}
|
||||
/>
|
||||
)}
|
||||
{!claimIsMine && (
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
doResolveUri,
|
||||
makeSelectClaimForUri,
|
||||
makeSelectMetadataForUri,
|
||||
makeSelectFileInfoForUri,
|
||||
makeSelectIsUriResolving,
|
||||
} from 'lbry-redux';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { doResolveUri } from 'redux/actions/content';
|
||||
import { selectRewardContentClaimIds } from 'redux/selectors/content';
|
||||
import { selectShowNsfw } from 'redux/selectors/settings';
|
||||
import { makeSelectClaimForUri, makeSelectMetadataForUri } from 'redux/selectors/claims';
|
||||
import { makeSelectFileInfoForUri } from 'redux/selectors/file_info';
|
||||
import { makeSelectIsUriResolving, selectRewardContentClaimIds } from 'redux/selectors/content';
|
||||
import { selectPendingPublish } from 'redux/selectors/publish';
|
||||
import FileCard from './view';
|
||||
|
||||
const select = (state, props) => {
|
||||
let claim;
|
||||
let fileInfo;
|
||||
let metadata;
|
||||
let isResolvingUri;
|
||||
|
||||
const pendingPublish = selectPendingPublish(props.uri)(state);
|
||||
let pendingPublish;
|
||||
if (props.checkPending) {
|
||||
pendingPublish = selectPendingPublish(props.uri)(state);
|
||||
}
|
||||
|
||||
const fileCardInfo = pendingPublish || {
|
||||
claim: makeSelectClaimForUri(props.uri)(state),
|
||||
|
|
|
@ -1,19 +1,17 @@
|
|||
// @flow
|
||||
import * as React from 'react';
|
||||
import { normalizeURI } from 'lbryURI';
|
||||
import { normalizeURI } from 'lbry-redux';
|
||||
import Button from 'component/button';
|
||||
import CardMedia from 'component/cardMedia';
|
||||
import TruncatedText from 'component/common/truncated-text';
|
||||
import Icon from 'component/common/icon';
|
||||
import FilePrice from 'component/filePrice';
|
||||
import UriIndicator from 'component/uriIndicator';
|
||||
import NsfwOverlay from 'component/nsfwOverlay';
|
||||
import * as icons from 'constants/icons';
|
||||
import classnames from 'classnames';
|
||||
|
||||
// TODO: iron these out
|
||||
type Props = {
|
||||
isResolvingUri: boolean,
|
||||
resolveUri: string => void,
|
||||
uri: string,
|
||||
claim: ?{ claim_id: string },
|
||||
fileInfo: ?{},
|
||||
|
@ -78,22 +76,41 @@ class FileCard extends React.PureComponent<Props> {
|
|||
<CardMedia nsfw={shouldObscureNsfw} thumbnail={thumbnail} />
|
||||
<div className="card-media__internal-links">{showPrice && <FilePrice uri={uri} />}</div>
|
||||
|
||||
<div className="card__title-identity">
|
||||
<div className="card__title--small">
|
||||
<TruncatedText lines={3}>{title}</TruncatedText>
|
||||
{shouldObscureNsfw ? (
|
||||
<div className="card__title-identity">
|
||||
<div className="card__title--small">
|
||||
<TruncatedText lines={3}>
|
||||
{__('This content is obscured because it is NSFW. You can change this in ')}
|
||||
<Button
|
||||
button="link"
|
||||
label={__('Settings.')}
|
||||
onClick={e => {
|
||||
// Don't propagate to the onClick handler of parent element
|
||||
e.stopPropagation();
|
||||
navigate('/settings');
|
||||
}}
|
||||
/>
|
||||
</TruncatedText>
|
||||
</div>
|
||||
</div>
|
||||
<div className="card__subtitle card__subtitle--file-info">
|
||||
{pending ? (
|
||||
<div>Pending...</div>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<UriIndicator uri={uri} link />
|
||||
{isRewardContent && <Icon icon={icons.FEATURED} />}
|
||||
{fileInfo && <Icon icon={icons.LOCAL} />}
|
||||
</React.Fragment>
|
||||
)}
|
||||
) : (
|
||||
<div className="card__title-identity">
|
||||
<div className="card__title--small">
|
||||
<TruncatedText lines={3}>{title}</TruncatedText>
|
||||
</div>
|
||||
<div className="card__subtitle card__subtitle--file-info">
|
||||
{pending ? (
|
||||
<div>Pending...</div>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<UriIndicator uri={uri} link />
|
||||
{isRewardContent && <Icon icon={icons.FEATURED} />}
|
||||
{fileInfo && <Icon icon={icons.LOCAL} />}
|
||||
</React.Fragment>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
/* eslint-enable jsx-a11y/click-events-have-key-events */
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
makeSelectClaimForUri,
|
||||
makeSelectContentTypeForUri,
|
||||
makeSelectMetadataForUri,
|
||||
} from 'redux/selectors/claims';
|
||||
makeSelectFileInfoForUri,
|
||||
} from 'lbry-redux';
|
||||
import { doOpenFileInFolder } from 'redux/actions/file';
|
||||
import FileDetails from './view';
|
||||
import { doOpenFileInFolder } from 'redux/actions/file_info';
|
||||
import { makeSelectFileInfoForUri } from 'redux/selectors/file_info';
|
||||
|
||||
const select = (state, props) => ({
|
||||
claim: makeSelectClaimForUri(props.uri)(state),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
import * as React from 'react';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import lbry from 'lbry';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import Button from 'component/button';
|
||||
import path from 'path';
|
||||
|
||||
|
@ -31,7 +31,7 @@ const FileDetails = (props: Props) => {
|
|||
}
|
||||
|
||||
const { description, language, license } = metadata;
|
||||
const mediaType = lbry.getMediaType(contentType);
|
||||
const mediaType = Lbry.getMediaType(contentType);
|
||||
|
||||
const downloadPath = fileInfo ? path.normalize(fileInfo.download_path) : null;
|
||||
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
makeSelectFileInfoForUri,
|
||||
makeSelectDownloadingForUri,
|
||||
makeSelectLoadingForUri,
|
||||
} from 'redux/selectors/file_info';
|
||||
import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info';
|
||||
makeSelectCostInfoForUri,
|
||||
} from 'lbry-redux';
|
||||
import { doFetchAvailability } from 'redux/actions/availability';
|
||||
import { doOpenFileInShell } from 'redux/actions/file_info';
|
||||
import { doOpenFileInShell } from 'redux/actions/file';
|
||||
import { doPurchaseUri, doStartDownload } from 'redux/actions/content';
|
||||
import { doPause } from 'redux/actions/media';
|
||||
import FileDownloadLink from './view';
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import FileList from './view';
|
||||
import { selectClaimsById } from 'redux/selectors/claims';
|
||||
import { selectClaimsById } from 'lbry-redux';
|
||||
|
||||
const select = state => ({
|
||||
claimsById: selectClaimsById(state),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import * as React from 'react';
|
||||
import { buildURI } from 'lbryURI';
|
||||
import { buildURI } from 'lbry-redux';
|
||||
import { FormField } from 'component/common/form';
|
||||
import FileCard from 'component/fileCard';
|
||||
|
||||
|
@ -22,7 +22,10 @@ type FileInfo = {
|
|||
|
||||
type Props = {
|
||||
hideFilter: boolean,
|
||||
sortByHeight?: boolean,
|
||||
claimsById: Array<{}>,
|
||||
fileInfos: Array<FileInfo>,
|
||||
checkPending?: boolean,
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
@ -41,6 +44,8 @@ class FileList extends React.PureComponent<Props, State> {
|
|||
sortBy: 'dateNew',
|
||||
};
|
||||
|
||||
(this: any).handleSortChanged = this.handleSortChanged.bind(this);
|
||||
|
||||
this.sortFunctions = {
|
||||
dateNew: fileInfos =>
|
||||
this.props.sortByHeight
|
||||
|
@ -136,26 +141,35 @@ class FileList extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { fileInfos, hideFilter } = this.props;
|
||||
const { fileInfos, hideFilter, checkPending } = this.props;
|
||||
const { sortBy } = this.state;
|
||||
const content = [];
|
||||
|
||||
this.sortFunctions[sortBy](fileInfos).forEach(fileInfo => {
|
||||
const { channel_name: channelName, name: claimName, claim_id: claimId } = fileInfo;
|
||||
const {
|
||||
channel_name: channelName,
|
||||
name: claimName,
|
||||
claim_name: claimNameDownloaded,
|
||||
claim_id: claimId,
|
||||
} = fileInfo;
|
||||
const uriParams = {};
|
||||
|
||||
// This is unfortunate
|
||||
// https://github.com/lbryio/lbry/issues/1159
|
||||
const name = claimName || claimNameDownloaded;
|
||||
|
||||
if (channelName) {
|
||||
uriParams.channelName = channelName;
|
||||
uriParams.contentName = claimName;
|
||||
uriParams.contentName = name;
|
||||
uriParams.claimId = this.getChannelSignature(fileInfo);
|
||||
} else {
|
||||
uriParams.claimId = claimId;
|
||||
uriParams.claimName = claimName;
|
||||
uriParams.claimName = name;
|
||||
}
|
||||
|
||||
const uri = buildURI(uriParams);
|
||||
|
||||
content.push(<FileCard key={claimName} uri={uri} showPrice={false} />);
|
||||
content.push(<FileCard key={uri} uri={uri} checkPending={checkPending} />);
|
||||
});
|
||||
|
||||
return (
|
||||
|
@ -168,7 +182,8 @@ class FileList extends React.PureComponent<Props, State> {
|
|||
value={sortBy}
|
||||
onChange={this.handleSortChanged}
|
||||
>
|
||||
<option value="date">{__('Date')}</option>
|
||||
<option value="dateNew">{__('Newest First')}</option>
|
||||
<option value="dateOld">{__('Oldest First')}</option>
|
||||
<option value="title">{__('Title')}</option>
|
||||
</FormField>
|
||||
)}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doSearch } from 'redux/actions/search';
|
||||
import { makeSelectSearchUris, selectIsSearching } from 'redux/selectors/search';
|
||||
import { selectSearchDownloadUris } from 'redux/selectors/file_info';
|
||||
import {
|
||||
doSearch,
|
||||
makeSelectSearchUris,
|
||||
selectIsSearching,
|
||||
selectSearchDownloadUris,
|
||||
} from 'lbry-redux';
|
||||
import FileListSearch from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
|
|
|
@ -2,14 +2,12 @@
|
|||
import React from 'react';
|
||||
import FileTile from 'component/fileTile';
|
||||
import ChannelTile from 'component/channelTile';
|
||||
import { parseURI } from 'lbryURI';
|
||||
import { parseURI } from 'lbry-redux';
|
||||
import debounce from 'util/debounce';
|
||||
|
||||
const SEARCH_DEBOUNCE_TIME = 800;
|
||||
|
||||
const NoResults = () => {
|
||||
return <div className="file-tile">{__('No results')}</div>;
|
||||
};
|
||||
const NoResults = () => <div className="file-tile">{__('No results')}</div>;
|
||||
|
||||
type Props = {
|
||||
search: string => void,
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doFetchCostInfoForUri } from 'redux/actions/cost_info';
|
||||
import {
|
||||
doFetchCostInfoForUri,
|
||||
makeSelectCostInfoForUri,
|
||||
makeSelectFetchingCostInfoForUri,
|
||||
} from 'redux/selectors/cost_info';
|
||||
import { makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||
makeSelectClaimForUri,
|
||||
} from 'lbry-redux';
|
||||
import FilePrice from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
doResolveUri,
|
||||
makeSelectClaimForUri,
|
||||
makeSelectMetadataForUri,
|
||||
makeSelectFileInfoForUri,
|
||||
makeSelectIsUriResolving,
|
||||
} from 'lbry-redux';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { doResolveUri } from 'redux/actions/content';
|
||||
import { makeSelectClaimForUri, makeSelectMetadataForUri } from 'redux/selectors/claims';
|
||||
import { makeSelectFileInfoForUri } from 'redux/selectors/file_info';
|
||||
import { selectShowNsfw } from 'redux/selectors/settings';
|
||||
import { makeSelectIsUriResolving, selectRewardContentClaimIds } from 'redux/selectors/content';
|
||||
import { doClearPublish, doUpdatePublishForm } from 'redux/actions/publish';
|
||||
import { selectRewardContentClaimIds } from 'redux/selectors/content';
|
||||
import FileTile from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
|
@ -17,8 +20,10 @@ const select = (state, props) => ({
|
|||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
clearPublish: () => dispatch(doClearPublish()),
|
||||
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
||||
resolveUri: uri => dispatch(doResolveUri(uri)),
|
||||
updatePublishForm: value => dispatch(doUpdatePublishForm(value)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(FileTile);
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
// @flow
|
||||
import * as React from 'react';
|
||||
import * as icons from 'constants/icons';
|
||||
import { normalizeURI, isURIClaimable, parseURI } from 'lbryURI';
|
||||
import { normalizeURI, isURIClaimable, parseURI } from 'lbry-redux';
|
||||
import CardMedia from 'component/cardMedia';
|
||||
import TruncatedText from 'component/common/truncated-text';
|
||||
import FilePrice from 'component/filePrice';
|
||||
import Icon from 'component/common/icon';
|
||||
import Button from 'component/button';
|
||||
import classnames from 'classnames';
|
||||
|
@ -25,6 +24,8 @@ type Props = {
|
|||
metadata: {},
|
||||
resolveUri: string => void,
|
||||
navigate: (string, ?{}) => void,
|
||||
clearPublish: () => void,
|
||||
updatePublishForm: () => void,
|
||||
};
|
||||
|
||||
class FileTile extends React.PureComponent<Props> {
|
||||
|
@ -55,6 +56,8 @@ class FileTile extends React.PureComponent<Props> {
|
|||
fullWidth,
|
||||
showLocal,
|
||||
isDownloaded,
|
||||
clearPublish,
|
||||
updatePublishForm,
|
||||
} = this.props;
|
||||
|
||||
const uri = normalizeURI(this.props.uri);
|
||||
|
@ -69,7 +72,7 @@ class FileTile extends React.PureComponent<Props> {
|
|||
let name;
|
||||
let channel;
|
||||
if (claim) {
|
||||
name = claim.name;
|
||||
({ name } = claim);
|
||||
channel = claim.channel_name;
|
||||
}
|
||||
|
||||
|
@ -79,6 +82,9 @@ class FileTile extends React.PureComponent<Props> {
|
|||
'file-tile--fullwidth': fullWidth,
|
||||
})}
|
||||
onClick={onClick}
|
||||
onKeyUp={onClick}
|
||||
role="button"
|
||||
tabIndex="0"
|
||||
>
|
||||
<CardMedia title={title || name} thumbnail={thumbnail} />
|
||||
<div className="file-tile__info">
|
||||
|
@ -101,7 +107,11 @@ class FileTile extends React.PureComponent<Props> {
|
|||
label={__('Put something here!')}
|
||||
onClick={e => {
|
||||
// avoid navigating to /show from clicking on the section
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
// strip prefix from the Uri and use that as navigation parameter
|
||||
const nameFromUri = uri.replace(/lbry:\/\//g, '').replace(/-/g, ' ');
|
||||
clearPublish(); // to remove any existing publish data
|
||||
updatePublishForm({ name: nameFromUri }); // to populate the name
|
||||
navigate('/publish');
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -1,22 +1,21 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { selectBalance } from 'lbry-redux';
|
||||
import { formatCredits } from 'util/formatCredits';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { selectIsUpgradeAvailable, selectAutoUpdateDownloaded } from 'redux/selectors/app';
|
||||
import { formatCredits } from 'util/formatCredits';
|
||||
import { selectBalance } from 'redux/selectors/wallet';
|
||||
import Header from './view';
|
||||
import { doDownloadUpgradeRequested } from 'redux/actions/app';
|
||||
import Header from './view';
|
||||
|
||||
const select = state => ({
|
||||
isUpgradeAvailable: selectIsUpgradeAvailable(state),
|
||||
autoUpdateDownloaded: selectAutoUpdateDownloaded(state),
|
||||
balance: formatCredits(selectBalance(state) || 0, 2),
|
||||
balance: selectBalance(state),
|
||||
isUpgradeAvailable: selectIsUpgradeAvailable(state),
|
||||
roundedBalance: formatCredits(selectBalance(state) || 0, 2),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
back: () => dispatch(doHistoryBack()),
|
||||
forward: () => dispatch(doHistoryForward()),
|
||||
downloadUpgradeRequested: () => dispatch(doDownloadUpgradeRequested()),
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(Header);
|
||||
|
|
|
@ -5,20 +5,22 @@ import WunderBar from 'component/wunderbar';
|
|||
import * as icons from 'constants/icons';
|
||||
|
||||
type Props = {
|
||||
autoUpdateDownloaded: boolean,
|
||||
balance: string,
|
||||
navigate: any => void,
|
||||
downloadUpgradeRequested: any => void,
|
||||
isUpgradeAvailable: boolean,
|
||||
autoUpdateDownloaded: boolean,
|
||||
navigate: any => void,
|
||||
roundedBalance: string,
|
||||
};
|
||||
|
||||
const Header = (props: Props) => {
|
||||
const {
|
||||
autoUpdateDownloaded,
|
||||
balance,
|
||||
downloadUpgradeRequested,
|
||||
isUpgradeAvailable,
|
||||
navigate,
|
||||
downloadUpgradeRequested,
|
||||
autoUpdateDownloaded,
|
||||
roundedBalance,
|
||||
} = props;
|
||||
|
||||
const showUpgradeButton =
|
||||
|
@ -37,7 +39,10 @@ const Header = (props: Props) => {
|
|||
`${balance}`
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<span className="btn__label--balance">You have</span> <span>{balance} LBC</span>
|
||||
<span className="btn__label--balance" title={`${balance} LBC`}>
|
||||
You have
|
||||
</span>{' '}
|
||||
<span title={`${balance} LBC`}>{roundedBalance} LBC</span>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
|
@ -48,6 +53,7 @@ const Header = (props: Props) => {
|
|||
<Button
|
||||
uppercase
|
||||
button="primary"
|
||||
className="btn--header-publish"
|
||||
onClick={() => navigate('/publish')}
|
||||
icon={icons.UPLOAD}
|
||||
label={isUpgradeAvailable ? '' : __('Publish')}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
import React from 'react';
|
||||
import BusyIndicator from 'component/common/busy-indicator';
|
||||
import CreditAmount from 'component/common/credit-amount';
|
||||
import Button from 'component/button';
|
||||
import { Form, FormRow, FormField, Submit } from 'component/common/form';
|
||||
|
||||
class FormInviteNew extends React.PureComponent {
|
||||
|
@ -69,6 +70,7 @@ class InviteNew extends React.PureComponent {
|
|||
return (
|
||||
<section className="card card--section">
|
||||
<div className="card__title">{__('Invite a Friend')}</div>
|
||||
|
||||
<div className="card__subtitle">
|
||||
{__("Or an enemy. Or your cousin Jerry, who you're kind of unsure about.")}
|
||||
</div>
|
||||
|
@ -87,6 +89,11 @@ class InviteNew extends React.PureComponent {
|
|||
rewardAmount={rewardAmount}
|
||||
/>
|
||||
</div>
|
||||
<p className="help help--padded">
|
||||
{__('Read our')}{' '}
|
||||
<Button button="link" label={__('FAQ')} href="https://lbry.io/faq/referrals" />{' '}
|
||||
{__('to learn more about referrals')}.
|
||||
</p>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import NsfwOverlay from './view';
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import { connect } from 'react-redux';
|
||||
import {
|
||||
selectBalance,
|
||||
selectPageTitle,
|
||||
selectIsBackDisabled,
|
||||
selectIsForwardDisabled,
|
||||
selectNavLinks,
|
||||
} from 'redux/selectors/navigation';
|
||||
} from 'lbry-redux';
|
||||
import { doNavigate, doHistoryBack, doHistoryForward } from 'redux/actions/navigation';
|
||||
import { doDownloadUpgrade } from 'redux/actions/app';
|
||||
import { selectIsUpgradeAvailable } from 'redux/selectors/app';
|
||||
import { formatCredits } from 'util/formatCredits';
|
||||
import { selectBalance } from 'redux/selectors/wallet';
|
||||
import Page from './view';
|
||||
|
||||
const select = state => ({
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { selectBalance } from 'lbry-redux';
|
||||
import PublishForm from './view';
|
||||
|
||||
export default connect(null, null)(PublishForm);
|
||||
|
|
|
@ -7,7 +7,7 @@ type Props = {
|
|||
editingURI: ?string,
|
||||
isResolvingUri: boolean,
|
||||
winningBidForClaimUri: ?number,
|
||||
claimIsMine: ?boolean,
|
||||
myClaimForUri: ?{},
|
||||
onEditMyClaim: any => void,
|
||||
};
|
||||
|
||||
|
@ -18,7 +18,7 @@ class BidHelpText extends React.PureComponent<Props> {
|
|||
editingURI,
|
||||
isResolvingUri,
|
||||
winningBidForClaimUri,
|
||||
claimIsMine,
|
||||
myClaimForUri,
|
||||
onEditMyClaim,
|
||||
} = this.props;
|
||||
|
||||
|
@ -34,12 +34,12 @@ class BidHelpText extends React.PureComponent<Props> {
|
|||
return __('Checking the winning claim amount...');
|
||||
}
|
||||
|
||||
if (claimIsMine) {
|
||||
if (myClaimForUri) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{__('You already have a claim at')}
|
||||
{` ${uri} `}
|
||||
<Button button="link" label="Edit it" onClick={onEditMyClaim} />
|
||||
<Button button="link" label="Edit it" onClick={() => onEditMyClaim(myClaimForUri, uri)} />
|
||||
<br />
|
||||
{__('Publishing will update your existing claim.')}
|
||||
</React.Fragment>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import * as React from 'react';
|
||||
import { isNameValid, buildURI, regexInvalidURI } from 'lbryURI';
|
||||
import { isNameValid, buildURI, regexInvalidURI } from 'lbry-redux';
|
||||
import { Form, FormField, FormRow, FormFieldPrice, Submit } from 'component/common/form';
|
||||
import Button from 'component/button';
|
||||
import ChannelSection from 'component/selectChannel';
|
||||
|
@ -38,6 +38,11 @@ type Props = {
|
|||
winningBidForClaimUri: number,
|
||||
myClaimForUri: ?{
|
||||
amount: number,
|
||||
value: {
|
||||
stream: {
|
||||
source: { source: string },
|
||||
},
|
||||
},
|
||||
},
|
||||
licenseType: string,
|
||||
otherLicenseDescription: ?string,
|
||||
|
@ -50,7 +55,7 @@ type Props = {
|
|||
clearPublish: () => void,
|
||||
resolveUri: string => void,
|
||||
scrollToTop: () => void,
|
||||
prepareEdit: ({}) => void,
|
||||
prepareEdit: ({}, uri) => void,
|
||||
};
|
||||
|
||||
class PublishForm extends React.PureComponent<Props> {
|
||||
|
@ -68,85 +73,43 @@ class PublishForm extends React.PureComponent<Props> {
|
|||
(this: any).getNewUri = this.getNewUri.bind(this);
|
||||
}
|
||||
|
||||
handlePublish() {
|
||||
const {
|
||||
publish,
|
||||
filePath,
|
||||
bid,
|
||||
title,
|
||||
thumbnail,
|
||||
description,
|
||||
language,
|
||||
nsfw,
|
||||
channel,
|
||||
licenseType,
|
||||
licenseUrl,
|
||||
otherLicenseDescription,
|
||||
copyrightNotice,
|
||||
name,
|
||||
contentIsFree,
|
||||
price,
|
||||
uri,
|
||||
} = this.props;
|
||||
// Returns a new uri to be used in the form and begins to resolve that uri for bid help text
|
||||
getNewUri(name: string, channel: string) {
|
||||
const { resolveUri } = this.props;
|
||||
// If they are midway through a channel creation, treat it as anonymous until it completes
|
||||
const channelName = channel === CHANNEL_ANONYMOUS || channel === CHANNEL_NEW ? '' : channel;
|
||||
|
||||
let publishingLicense;
|
||||
switch (licenseType) {
|
||||
case COPYRIGHT:
|
||||
publishingLicense = copyrightNotice;
|
||||
break;
|
||||
case OTHER:
|
||||
publishingLicense = otherLicenseDescription;
|
||||
break;
|
||||
default:
|
||||
publishingLicense = licenseType;
|
||||
let uri;
|
||||
try {
|
||||
uri = buildURI({ contentName: name, channelName });
|
||||
} catch (e) {
|
||||
// something wrong with channel or name
|
||||
}
|
||||
|
||||
const publishingLicenseUrl = licenseType === COPYRIGHT ? '' : licenseUrl;
|
||||
|
||||
const publishParams = {
|
||||
filePath,
|
||||
bid,
|
||||
title,
|
||||
thumbnail,
|
||||
description,
|
||||
language,
|
||||
nsfw,
|
||||
channel,
|
||||
license: publishingLicense,
|
||||
licenseUrl: publishingLicenseUrl,
|
||||
otherLicenseDescription,
|
||||
copyrightNotice,
|
||||
name,
|
||||
contentIsFree,
|
||||
price,
|
||||
uri,
|
||||
};
|
||||
|
||||
publish(publishParams);
|
||||
}
|
||||
|
||||
handleCancelPublish() {
|
||||
const { clearPublish, scrollToTop } = this.props;
|
||||
scrollToTop();
|
||||
clearPublish();
|
||||
}
|
||||
|
||||
editExistingClaim() {
|
||||
const { myClaimForUri, prepareEdit, scrollToTop } = this.props;
|
||||
if (myClaimForUri) {
|
||||
prepareEdit(myClaimForUri);
|
||||
scrollToTop();
|
||||
if (uri) {
|
||||
resolveUri(uri);
|
||||
return uri;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
handleFileChange(filePath: string, fileName: string) {
|
||||
const { updatePublishForm, channel } = this.props;
|
||||
const parsedFileName = fileName.replace(regexInvalidURI, '');
|
||||
const uri = this.getNewUri(parsedFileName, channel);
|
||||
const { updatePublishForm, channel, name } = this.props;
|
||||
const newFileParams: {
|
||||
filePath: string,
|
||||
name?: string,
|
||||
uri?: string,
|
||||
} = { filePath };
|
||||
|
||||
if (filePath) {
|
||||
updatePublishForm({ filePath, name: parsedFileName, uri });
|
||||
if (!name) {
|
||||
const parsedFileName = fileName.replace(regexInvalidURI, '');
|
||||
const uri = this.getNewUri(parsedFileName, channel);
|
||||
newFileParams.name = parsedFileName;
|
||||
newFileParams.uri = uri;
|
||||
}
|
||||
|
||||
updatePublishForm(newFileParams);
|
||||
}
|
||||
|
||||
handleNameChange(name: ?string) {
|
||||
|
@ -196,25 +159,82 @@ class PublishForm extends React.PureComponent<Props> {
|
|||
updatePublishForm({ bid, bidError });
|
||||
}
|
||||
|
||||
// Returns a new uri to be used in the form and begins to resolve that uri for bid help text
|
||||
getNewUri(name: string, channel: string) {
|
||||
const { resolveUri } = this.props;
|
||||
// If they are midway through a channel creation, treat it as anonymous until it completes
|
||||
const channelName = channel === CHANNEL_ANONYMOUS || channel === CHANNEL_NEW ? '' : channel;
|
||||
editExistingClaim(myClaimForUri: ?{}, uri: string) {
|
||||
const { prepareEdit, scrollToTop } = this.props;
|
||||
if (myClaimForUri) {
|
||||
prepareEdit(myClaimForUri, uri);
|
||||
scrollToTop();
|
||||
}
|
||||
}
|
||||
|
||||
let uri;
|
||||
try {
|
||||
uri = buildURI({ contentName: name, channelName });
|
||||
} catch (e) {
|
||||
// something wrong with channel or name
|
||||
handleCancelPublish() {
|
||||
const { clearPublish, scrollToTop } = this.props;
|
||||
scrollToTop();
|
||||
clearPublish();
|
||||
}
|
||||
|
||||
handlePublish() {
|
||||
const {
|
||||
publish,
|
||||
filePath,
|
||||
bid,
|
||||
title,
|
||||
thumbnail,
|
||||
description,
|
||||
language,
|
||||
nsfw,
|
||||
channel,
|
||||
licenseType,
|
||||
licenseUrl,
|
||||
otherLicenseDescription,
|
||||
copyrightNotice,
|
||||
name,
|
||||
contentIsFree,
|
||||
price,
|
||||
uri,
|
||||
myClaimForUri,
|
||||
} = this.props;
|
||||
|
||||
let publishingLicense;
|
||||
switch (licenseType) {
|
||||
case COPYRIGHT:
|
||||
publishingLicense = copyrightNotice;
|
||||
break;
|
||||
case OTHER:
|
||||
publishingLicense = otherLicenseDescription;
|
||||
break;
|
||||
default:
|
||||
publishingLicense = licenseType;
|
||||
}
|
||||
|
||||
if (uri) {
|
||||
resolveUri(uri);
|
||||
return uri;
|
||||
const publishingLicenseUrl = licenseType === COPYRIGHT ? '' : licenseUrl;
|
||||
|
||||
const publishParams = {
|
||||
filePath,
|
||||
bid,
|
||||
title,
|
||||
thumbnail,
|
||||
description,
|
||||
language,
|
||||
nsfw,
|
||||
channel,
|
||||
license: publishingLicense,
|
||||
licenseUrl: publishingLicenseUrl,
|
||||
otherLicenseDescription,
|
||||
copyrightNotice,
|
||||
name,
|
||||
contentIsFree,
|
||||
price,
|
||||
uri,
|
||||
};
|
||||
|
||||
// Editing a claim
|
||||
if (!filePath && myClaimForUri) {
|
||||
const { source } = myClaimForUri.value.stream;
|
||||
publishParams.sources = source;
|
||||
}
|
||||
|
||||
return '';
|
||||
publish(publishParams);
|
||||
}
|
||||
|
||||
checkIsFormValid() {
|
||||
|
@ -280,7 +300,8 @@ class PublishForm extends React.PureComponent<Props> {
|
|||
const formDisabled = (!filePath && !editingURI) || publishing;
|
||||
const formValid = this.checkIsFormValid();
|
||||
|
||||
const isStillEditing = editingURI === uri;
|
||||
const simpleUri = uri && uri.split('#')[0];
|
||||
const isStillEditing = editingURI === simpleUri;
|
||||
let submitLabel;
|
||||
if (isStillEditing) {
|
||||
submitLabel = !publishing ? __('Edit') : __('Editing...');
|
||||
|
@ -374,14 +395,14 @@ class PublishForm extends React.PureComponent<Props> {
|
|||
disabled={formDisabled}
|
||||
onChange={() => updatePublishForm({ contentIsFree: false })}
|
||||
/>
|
||||
|
||||
<FormFieldPrice
|
||||
name="content_cost_amount"
|
||||
min="0"
|
||||
price={price}
|
||||
onChange={newPrice => updatePublishForm({ price: newPrice })}
|
||||
disabled={formDisabled || contentIsFree}
|
||||
/>
|
||||
{!contentIsFree && (
|
||||
<FormFieldPrice
|
||||
name="content_cost_amount"
|
||||
min="0"
|
||||
price={price}
|
||||
onChange={newPrice => updatePublishForm({ price: newPrice })}
|
||||
/>
|
||||
)}
|
||||
{price.currency !== 'LBC' && (
|
||||
<p className="form-field__help">
|
||||
{__(
|
||||
|
@ -414,7 +435,9 @@ class PublishForm extends React.PureComponent<Props> {
|
|||
<FormField
|
||||
stretch
|
||||
prefix={`lbry://${
|
||||
channel === CHANNEL_ANONYMOUS || channel === CHANNEL_NEW ? '' : `${channel}/`
|
||||
!channel || channel === CHANNEL_ANONYMOUS || channel === CHANNEL_NEW
|
||||
? ''
|
||||
: `${channel}/`
|
||||
}`}
|
||||
type="text"
|
||||
name="content_name"
|
||||
|
@ -424,11 +447,11 @@ class PublishForm extends React.PureComponent<Props> {
|
|||
error={nameError}
|
||||
helper={
|
||||
<BidHelpText
|
||||
uri={uri}
|
||||
uri={simpleUri}
|
||||
editingURI={editingURI}
|
||||
isResolvingUri={isResolvingUri}
|
||||
winningBidForClaimUri={winningBidForClaimUri}
|
||||
claimIsMine={!!myClaimForUri}
|
||||
myClaimForUri={myClaimForUri}
|
||||
onEditMyClaim={this.editExistingClaim}
|
||||
/>
|
||||
}
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import {
|
||||
makeSelectClaimRewardError,
|
||||
|
|
|
@ -1,14 +1,28 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import Modal from 'modal/modal';
|
||||
import { Modal } from 'modal/modal';
|
||||
import Button from 'component/button';
|
||||
|
||||
const RewardLink = props => {
|
||||
const { reward, button, claimReward, clearError, errorMessage, label, isPending } = props;
|
||||
type Reward = {
|
||||
reward_amount: number,
|
||||
};
|
||||
|
||||
type Props = {
|
||||
isPending: boolean,
|
||||
label: ?string,
|
||||
errorMessage: ?string,
|
||||
reward: Reward,
|
||||
clearError: Reward => void,
|
||||
claimReward: Reward => void,
|
||||
};
|
||||
|
||||
const RewardLink = (props: Props) => {
|
||||
const { reward, claimReward, clearError, errorMessage, label, isPending } = props;
|
||||
|
||||
return !reward ? null : (
|
||||
<div className="reward-link">
|
||||
<Button
|
||||
button="primary"
|
||||
button="link"
|
||||
disabled={isPending}
|
||||
label={isPending ? __('Claiming...') : label || `${__('Get')} ${reward.reward_amount} LBC`}
|
||||
onClick={() => {
|
||||
|
@ -16,6 +30,7 @@ const RewardLink = props => {
|
|||
}}
|
||||
/>
|
||||
{errorMessage ? (
|
||||
// TODO: This should be moved to redux
|
||||
<Modal
|
||||
isOpen
|
||||
contentLabel="Reward Claim Error"
|
||||
|
@ -32,4 +47,5 @@ const RewardLink = props => {
|
|||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default RewardLink;
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import Router from './view.jsx';
|
||||
import { selectCurrentPage, selectCurrentParams } from 'redux/selectors/navigation.js';
|
||||
import { selectCurrentPage, selectCurrentParams } from 'lbry-redux';
|
||||
import Router from './view';
|
||||
|
||||
const select = state => ({
|
||||
params: selectCurrentParams(state),
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import React from 'react';
|
||||
import SettingsPage from 'page/settings';
|
||||
import HelpPage from 'page/help';
|
||||
import ReportPage from 'page/report.js';
|
||||
import ReportPage from 'page/report';
|
||||
import WalletPage from 'page/wallet';
|
||||
import GetCreditsPage from '../../page/getCredits';
|
||||
import GetCreditsPage from 'page/getCredits';
|
||||
import SendReceivePage from 'page/sendCredits';
|
||||
import ShowPage from 'page/show';
|
||||
import PublishPage from 'page/publish';
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import { connect } from 'react-redux';
|
||||
import SelectChannel from './view';
|
||||
import { selectMyChannelClaims, selectFetchingMyChannels } from 'redux/selectors/claims';
|
||||
import { selectBalance, selectMyChannelClaims, selectFetchingMyChannels } from 'lbry-redux';
|
||||
import { doFetchChannelListMine, doCreateChannel } from 'redux/actions/content';
|
||||
import { selectBalance } from 'redux/selectors/wallet';
|
||||
|
||||
const select = state => ({
|
||||
channels: selectMyChannelClaims(state),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { isNameValid } from 'lbryURI';
|
||||
import { isNameValid } from 'lbry-redux';
|
||||
import { FormRow, FormField } from 'component/common/form';
|
||||
import BusyIndicator from 'component/common/busy-indicator';
|
||||
import Button from 'component/button';
|
||||
|
|
|
@ -6,8 +6,7 @@ import {
|
|||
clearShapeShift,
|
||||
getActiveShift,
|
||||
} from 'redux/actions/shape_shift';
|
||||
import { doShowSnackBar } from 'redux/actions/app';
|
||||
import { selectReceiveAddress } from 'redux/selectors/wallet';
|
||||
import { selectReceiveAddress } from 'lbry-redux';
|
||||
import { selectShapeShift } from 'redux/selectors/shape_shift';
|
||||
import ShapeShift from './view';
|
||||
|
||||
|
@ -22,5 +21,4 @@ export default connect(select, {
|
|||
createShapeShift,
|
||||
clearShapeShift,
|
||||
getActiveShift,
|
||||
doShowSnackBar,
|
||||
})(ShapeShift);
|
||||
|
|
|
@ -16,7 +16,6 @@ type Props = {
|
|||
shiftOrderId: ?string,
|
||||
originCoinDepositMax: ?number,
|
||||
clearShapeShift: Dispatch,
|
||||
doShowSnackBar: Dispatch,
|
||||
getActiveShift: Dispatch,
|
||||
shapeShiftRate: ?number,
|
||||
originCoinDepositMax: ?number,
|
||||
|
@ -25,8 +24,6 @@ type Props = {
|
|||
};
|
||||
|
||||
class ActiveShapeShift extends React.PureComponent<Props> {
|
||||
continousFetch: ?number;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.continousFetch = undefined;
|
||||
|
@ -59,6 +56,8 @@ class ActiveShapeShift extends React.PureComponent<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
continousFetch: ?number;
|
||||
|
||||
render() {
|
||||
const {
|
||||
shiftCoinType,
|
||||
|
@ -68,7 +67,6 @@ class ActiveShapeShift extends React.PureComponent<Props> {
|
|||
shiftState,
|
||||
originCoinDepositMax,
|
||||
clearShapeShift,
|
||||
doShowSnackBar,
|
||||
shapeShiftRate,
|
||||
originCoinDepositFee,
|
||||
originCoinDepositMin,
|
||||
|
@ -95,8 +93,8 @@ class ActiveShapeShift extends React.PureComponent<Props> {
|
|||
|
||||
{shiftDepositAddress && (
|
||||
<FormRow verticallyCentered padded>
|
||||
<Address address={shiftDepositAddress} showCopyButton />
|
||||
<QRCode value={shiftDepositAddress} />
|
||||
<QRCode value={shiftDepositAddress} paddingRight />
|
||||
<Address address={shiftDepositAddress} showCopyButton padded />
|
||||
</FormRow>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -19,7 +19,6 @@ type Props = {
|
|||
isSubmitting: boolean,
|
||||
shiftSupportedCoins: Array<string>,
|
||||
originCoin: string,
|
||||
updating: boolean,
|
||||
getCoinStats: Dispatch,
|
||||
originCoinDepositFee: number,
|
||||
originCoinDepositMin: string,
|
||||
|
@ -38,7 +37,6 @@ export default (props: Props) => {
|
|||
isSubmitting,
|
||||
shiftSupportedCoins,
|
||||
originCoin,
|
||||
updating,
|
||||
getCoinStats,
|
||||
originCoinDepositMax,
|
||||
originCoinDepositMin,
|
||||
|
@ -51,7 +49,7 @@ export default (props: Props) => {
|
|||
prefix={__('Exchange')}
|
||||
postfix={__('for LBC')}
|
||||
type="select"
|
||||
name="origin_coin"
|
||||
name="originCoin"
|
||||
onChange={e => {
|
||||
getCoinStats(e.target.value);
|
||||
handleChange(e);
|
||||
|
@ -76,7 +74,7 @@ export default (props: Props) => {
|
|||
label={__('Return address')}
|
||||
error={touched.returnAddress && !!errors.returnAddress && errors.returnAddress}
|
||||
type="text"
|
||||
name="return_address"
|
||||
name="returnAddress"
|
||||
className="input--address"
|
||||
placeholder={getExampleAddress(originCoin)}
|
||||
onChange={handleChange}
|
||||
|
|
|
@ -14,14 +14,16 @@ type Props = {
|
|||
createShapeShift: Dispatch,
|
||||
clearShapeShift: Dispatch,
|
||||
getActiveShift: Dispatch,
|
||||
doShowSnackBar: Dispatch,
|
||||
shapeShiftInit: Dispatch,
|
||||
receiveAddress: string,
|
||||
};
|
||||
|
||||
class ShapeShift extends React.PureComponent<Props> {
|
||||
componentDidMount() {
|
||||
const { shapeShiftInit, shapeShift: { hasActiveShift, shiftSupportedCoins } } = this.props;
|
||||
const {
|
||||
shapeShiftInit,
|
||||
shapeShift: { hasActiveShift, shiftSupportedCoins },
|
||||
} = this.props;
|
||||
|
||||
if (!hasActiveShift && !shiftSupportedCoins.length) {
|
||||
// calls shapeshift to see list of supported coins for shifting
|
||||
|
@ -37,7 +39,6 @@ class ShapeShift extends React.PureComponent<Props> {
|
|||
shapeShift,
|
||||
clearShapeShift,
|
||||
getActiveShift,
|
||||
doShowSnackBar,
|
||||
} = this.props;
|
||||
|
||||
const {
|
||||
|
@ -107,7 +108,6 @@ class ShapeShift extends React.PureComponent<Props> {
|
|||
shiftOrderId={shiftOrderId}
|
||||
shiftState={shiftState}
|
||||
clearShapeShift={clearShapeShift}
|
||||
doShowSnackBar={doShowSnackBar}
|
||||
originCoinDepositMax={originCoinDepositMax}
|
||||
originCoinDepositMin={originCoinDepositMin}
|
||||
originCoinDepositFee={originCoinDepositFee}
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doNavigate, doHistoryBack, doHistoryForward } from 'redux/actions/navigation';
|
||||
import {
|
||||
selectIsBackDisabled,
|
||||
selectIsForwardDisabled,
|
||||
selectNavLinks,
|
||||
} from 'redux/selectors/navigation';
|
||||
import { selectIsBackDisabled, selectIsForwardDisabled, selectNavLinks } from 'lbry-redux';
|
||||
import { selectNotifications } from 'redux/selectors/subscriptions';
|
||||
import SideBar from './view';
|
||||
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doRemoveSnackBarSnack } from 'redux/actions/app';
|
||||
import { selectSnackBarSnacks } from 'redux/selectors/app';
|
||||
import { selectSnack, doHideNotification } from 'lbry-redux';
|
||||
import SnackBar from './view';
|
||||
|
||||
const perform = dispatch => ({
|
||||
removeSnack: () => dispatch(doRemoveSnackBarSnack()),
|
||||
removeSnack: () => dispatch(doHideNotification()),
|
||||
});
|
||||
|
||||
const select = state => ({
|
||||
snacks: selectSnackBarSnacks(state),
|
||||
snack: selectSnack(state),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(SnackBar);
|
||||
|
|
|
@ -1,35 +1,47 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import Button from 'component/button';
|
||||
|
||||
class SnackBar extends React.PureComponent {
|
||||
constructor(props) {
|
||||
type Props = {
|
||||
removeSnack: any => void,
|
||||
snack: ?{
|
||||
linkTarget: ?string,
|
||||
linkText: ?string,
|
||||
message: string,
|
||||
},
|
||||
};
|
||||
|
||||
class SnackBar extends React.PureComponent<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this._displayTime = 5; // in seconds
|
||||
this._hideTimeout = null;
|
||||
this.displayTime = 5; // in seconds
|
||||
this.hideTimeout = null;
|
||||
}
|
||||
|
||||
render() {
|
||||
const { snacks, removeSnack } = this.props;
|
||||
const { snack, removeSnack } = this.props;
|
||||
|
||||
if (!snacks.length) {
|
||||
this._hideTimeout = null; // should be unmounting anyway, but be safe?
|
||||
if (!snack) {
|
||||
this.hideTimeout = null; // should be unmounting anyway, but be safe?
|
||||
return null;
|
||||
}
|
||||
|
||||
const snack = snacks[0];
|
||||
const { message, linkText, linkTarget } = snack;
|
||||
|
||||
if (this._hideTimeout === null) {
|
||||
this._hideTimeout = setTimeout(() => {
|
||||
this._hideTimeout = null;
|
||||
if (this.hideTimeout === null) {
|
||||
this.hideTimeout = setTimeout(() => {
|
||||
this.hideTimeout = null;
|
||||
removeSnack();
|
||||
}, this._displayTime * 1000);
|
||||
}, this.displayTime * 1000);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="snack-bar">
|
||||
{message}
|
||||
<div className="snack-bar__message">
|
||||
<div>ⓘ</div>
|
||||
<div>{message}</div>
|
||||
</div>
|
||||
{linkText &&
|
||||
linkTarget && (
|
||||
<Button navigate={linkTarget} className="snack-bar__action" label={linkText} />
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import * as React from 'react';
|
||||
import lbry from 'lbry';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import LoadScreen from './internal/load-screen';
|
||||
import ModalIncompatibleDaemon from 'modal/modalIncompatibleDaemon';
|
||||
import ModalUpgrade from 'modal/modalUpgrade';
|
||||
|
@ -31,7 +31,7 @@ export class SplashScreen extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
updateStatus() {
|
||||
lbry.status().then(status => {
|
||||
Lbry.status().then(status => {
|
||||
this._updateStatusCallback(status);
|
||||
});
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ export class SplashScreen extends React.PureComponent<Props, State> {
|
|||
isRunning: true,
|
||||
});
|
||||
|
||||
lbry.resolve({ uri: 'lbry://one' }).then(() => {
|
||||
Lbry.resolve({ uri: 'lbry://one' }).then(() => {
|
||||
// Only leave the load screen if the daemon version matched;
|
||||
// otherwise we'll notify the user at the end of the load screen.
|
||||
|
||||
|
@ -83,8 +83,7 @@ export class SplashScreen extends React.PureComponent<Props, State> {
|
|||
componentDidMount() {
|
||||
const { checkDaemonVersion } = this.props;
|
||||
|
||||
lbry
|
||||
.connect()
|
||||
Lbry.connect()
|
||||
.then(checkDaemonVersion)
|
||||
.then(() => {
|
||||
this.updateStatus();
|
||||
|
|
|
@ -1,18 +0,0 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { selectCurrentPage, selectHeaderLinks } from 'redux/selectors/navigation';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { selectNotifications } from 'redux/selectors/subscriptions';
|
||||
import SubHeader from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
currentPage: selectCurrentPage(state),
|
||||
subLinks: selectHeaderLinks(state),
|
||||
notifications: selectNotifications(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(SubHeader);
|
|
@ -1,42 +0,0 @@
|
|||
import React from 'react';
|
||||
import Link from 'component/link';
|
||||
import classnames from 'classnames';
|
||||
import * as NOTIFICATION_TYPES from 'constants/notification_types';
|
||||
|
||||
const SubHeader = props => {
|
||||
const { subLinks, currentPage, navigate, fullWidth, smallMargin, notifications } = props;
|
||||
|
||||
const badges = Object.keys(notifications).reduce(
|
||||
(acc, cur) => (notifications[cur].type === NOTIFICATION_TYPES.DOWNLOADING ? acc : acc + 1),
|
||||
0
|
||||
);
|
||||
|
||||
const links = [];
|
||||
|
||||
for (const link of Object.keys(subLinks)) {
|
||||
links.push(
|
||||
<Link
|
||||
onClick={event => navigate(`/${link}`, event)}
|
||||
key={link}
|
||||
className={link == currentPage ? 'sub-header-selected' : 'sub-header-unselected'}
|
||||
>
|
||||
{subLinks[link] === 'Subscriptions' && badges
|
||||
? `Subscriptions (${badges})`
|
||||
: subLinks[link]}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<nav
|
||||
className={classnames('sub-header', {
|
||||
'sub-header--full-width': fullWidth,
|
||||
'sub-header--small-margin': smallMargin,
|
||||
})}
|
||||
>
|
||||
{links}
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default SubHeader;
|
|
@ -1,6 +1,6 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doChannelSubscribe, doChannelUnsubscribe } from 'redux/actions/subscriptions';
|
||||
import { doOpenModal } from 'redux/actions/app';
|
||||
import { doNotify } from 'lbry-redux';
|
||||
import { selectSubscriptions } from 'redux/selectors/subscriptions';
|
||||
import SubscribeButton from './view';
|
||||
|
||||
|
@ -11,5 +11,5 @@ const select = (state, props) => ({
|
|||
export default connect(select, {
|
||||
doChannelSubscribe,
|
||||
doChannelUnsubscribe,
|
||||
doOpenModal,
|
||||
doNotify,
|
||||
})(SubscribeButton);
|
||||
|
|
|
@ -16,7 +16,7 @@ type Props = {
|
|||
subscriptions: Array<Subscription>,
|
||||
doChannelSubscribe: ({ channelName: string, uri: string }) => void,
|
||||
doChannelUnsubscribe: SubscribtionArgs => void,
|
||||
doOpenModal: string => void,
|
||||
doNotify: ({ id: string }) => void,
|
||||
};
|
||||
|
||||
export default (props: Props) => {
|
||||
|
@ -26,7 +26,7 @@ export default (props: Props) => {
|
|||
subscriptions,
|
||||
doChannelSubscribe,
|
||||
doChannelUnsubscribe,
|
||||
doOpenModal,
|
||||
doNotify,
|
||||
} = props;
|
||||
|
||||
const isSubscribed =
|
||||
|
@ -42,7 +42,7 @@ export default (props: Props) => {
|
|||
label={subscriptionLabel}
|
||||
onClick={() => {
|
||||
if (!subscriptions.length) {
|
||||
doOpenModal(modals.FIRST_SUBSCRIPTION);
|
||||
doNotify({ id: modals.FIRST_SUBSCRIPTION });
|
||||
}
|
||||
subscriptionHandler({
|
||||
channelName,
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { doOpenModal } from 'redux/actions/app';
|
||||
import { selectClaimedRewardsByTransactionId } from 'redux/selectors/rewards';
|
||||
import { selectAllMyClaimsByOutpoint } from 'redux/selectors/claims';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { doNotify } from 'lbry-redux';
|
||||
import { selectAllMyClaimsByOutpoint } from 'lbry-redux';
|
||||
import TransactionList from './view';
|
||||
|
||||
const select = state => ({
|
||||
|
@ -13,7 +12,7 @@ const select = state => ({
|
|||
|
||||
const perform = dispatch => ({
|
||||
navigate: (path, params) => dispatch(doNavigate(path, params)),
|
||||
openModal: (modal, props) => dispatch(doOpenModal(modal, props)),
|
||||
openModal: (modal, props) => dispatch(doNotify(modal, props)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(TransactionList);
|
||||
|
|
|
@ -4,7 +4,7 @@ import ButtonTransaction from 'component/common/transaction-link';
|
|||
import CreditAmount from 'component/common/credit-amount';
|
||||
import DateTime from 'component/dateTime';
|
||||
import Button from 'component/button';
|
||||
import { buildURI } from 'lbryURI';
|
||||
import { buildURI } from 'lbry-redux';
|
||||
import * as txnTypes from 'constants/transaction_types';
|
||||
import type { Transaction } from '../view';
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ type Props = {
|
|||
slim?: boolean,
|
||||
transactions: Array<Transaction>,
|
||||
rewards: {},
|
||||
openModal: (string, any) => void,
|
||||
openModal: ({ id: string }, { nout: number, txid: string }) => void,
|
||||
myClaims: any,
|
||||
};
|
||||
|
||||
|
@ -65,7 +65,7 @@ class TransactionList extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
revokeClaim(txid: string, nout: number) {
|
||||
this.props.openModal(modals.CONFIRM_CLAIM_REVOKE, { txid, nout });
|
||||
this.props.openModal({ id: modals.CONFIRM_CLAIM_REVOKE }, { txid, nout });
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doFetchTransactions } from 'redux/actions/wallet';
|
||||
import {
|
||||
selectBalance,
|
||||
doFetchTransactions,
|
||||
selectRecentTransactions,
|
||||
selectHasTransactions,
|
||||
selectIsFetchingTransactions,
|
||||
} from 'redux/selectors/wallet';
|
||||
|
||||
} from 'lbry-redux';
|
||||
import TransactionListRecent from './view';
|
||||
|
||||
const select = state => ({
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
import React from 'react';
|
||||
import { normalizeURI } from 'lbryURI';
|
||||
import { connect } from 'react-redux';
|
||||
import { doResolveUri } from 'redux/actions/content';
|
||||
import { makeSelectIsUriResolving } from 'redux/selectors/content';
|
||||
import { makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||
import {
|
||||
normalizeURI,
|
||||
doResolveUri,
|
||||
makeSelectIsUriResolving,
|
||||
makeSelectClaimForUri,
|
||||
} from 'lbry-redux';
|
||||
import UriIndicator from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import Button from 'component/button';
|
||||
import { buildURI } from 'lbryURI';
|
||||
import { buildURI } from 'lbry-redux';
|
||||
import classnames from 'classnames';
|
||||
// import Icon from 'component/common/icon';
|
||||
|
||||
|
|
|
@ -1,19 +1,33 @@
|
|||
// I'll come back to this
|
||||
/* eslint-disable */
|
||||
// @flow
|
||||
import React from 'react';
|
||||
import Button from 'component/button';
|
||||
import { Form, FormField, Submit } from 'component/common/form';
|
||||
import { Form, FormField, FormRow, Submit } from 'component/common/form';
|
||||
|
||||
class UserEmailVerify extends React.PureComponent {
|
||||
constructor(props) {
|
||||
type Props = {
|
||||
cancelButton: React.Node,
|
||||
errorMessage: ?string,
|
||||
email: string,
|
||||
isPending: boolean,
|
||||
verifyUserEmail: (string, string) => void,
|
||||
verifyUserEmailFailure: string => void,
|
||||
};
|
||||
|
||||
type State = {
|
||||
code: string,
|
||||
};
|
||||
|
||||
class UserEmailVerify extends React.PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
code: '',
|
||||
};
|
||||
|
||||
(this: any).handleSubmit = this.handleSubmit.bind(this);
|
||||
}
|
||||
|
||||
handleCodeChanged(event) {
|
||||
handleCodeChanged(event: SyntheticInputEvent<*>) {
|
||||
this.setState({
|
||||
code: String(event.target.value).trim(),
|
||||
});
|
||||
|
@ -31,31 +45,30 @@ class UserEmailVerify extends React.PureComponent {
|
|||
|
||||
render() {
|
||||
const { cancelButton, errorMessage, email, isPending } = this.props;
|
||||
// <FormField
|
||||
// label={__('Verification Code')}
|
||||
// errorMessage={errorMessage}
|
||||
// render{() => (
|
||||
// <input
|
||||
// name="code"
|
||||
// value={this.state.code}
|
||||
// onChange={event => {
|
||||
// this.handleCodeChanged(event);
|
||||
// }}
|
||||
// />
|
||||
// )}
|
||||
// />
|
||||
|
||||
return (
|
||||
<Form onSubmit={this.handleSubmit.bind(this)}>
|
||||
<Form onSubmit={this.handleSubmit}>
|
||||
<p>Please enter the verification code emailed to {email}.</p>
|
||||
{/* render help separately so it always shows */}
|
||||
<div className="form-field__helper">
|
||||
<FormRow>
|
||||
<FormField
|
||||
stretch
|
||||
name="code"
|
||||
type="text"
|
||||
placeholder="eyJyZWNhcHRjaGEiOiIw..."
|
||||
label={__('Verification Code')}
|
||||
error={errorMessage}
|
||||
value={this.state.code}
|
||||
onChange={event => this.handleCodeChanged(event)}
|
||||
/>
|
||||
</FormRow>
|
||||
<div className="help">
|
||||
<p>
|
||||
{__('Email')} <Button href="mailto:help@lbry.io" label="help@lbry.io" /> or join our{' '}
|
||||
<Button href="https://chat.lbry.io" label="chat" />{' '}
|
||||
{__('Email')} <Button button="link" href="mailto:help@lbry.io" label="help@lbry.io" />{' '}
|
||||
or join our <Button button="link" href="https://chat.lbry.io" label="chat" />{' '}
|
||||
{__('if you encounter any trouble with your code.')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="form-row-submit">
|
||||
<div className="card__actions">
|
||||
<Submit label={__('Verify')} disabled={isPending} />
|
||||
{cancelButton}
|
||||
</div>
|
||||
|
@ -65,4 +78,3 @@ class UserEmailVerify extends React.PureComponent {
|
|||
}
|
||||
|
||||
export default UserEmailVerify;
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// I'll come back to this
|
||||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import { Form, FormRow, FormField } from 'component/common/form';
|
||||
import { Form, FormRow, FormField, Submit } from 'component/common/form';
|
||||
|
||||
const os = require('os').type();
|
||||
const countryCodes = require('country-data')
|
||||
|
@ -77,37 +77,30 @@ class UserPhoneNew extends React.PureComponent {
|
|||
)}
|
||||
</p>
|
||||
<Form onSubmit={this.handleSubmit.bind(this)}>
|
||||
<div className="form-row-phone">
|
||||
<FormRow>
|
||||
<FormField type="select" name="country-codes" onChange={this.handleSelect.bind(this)}>
|
||||
{countryCodes.map((country, index) => (
|
||||
<option key={index} value={country.countryCallingCode}>
|
||||
{os === 'Darwin' ? country.emoji : `(${country.alpha2})`}{' '}
|
||||
{country.countryCallingCode}
|
||||
</option>
|
||||
))}
|
||||
</FormField>
|
||||
<FormField
|
||||
onChange={this.handleSelect.bind(this)}
|
||||
render={() => (
|
||||
<select>
|
||||
{countryCodes.map((country, index) => (
|
||||
<option key={index} value={country.countryCallingCode}>
|
||||
{os === 'Darwin' ? country.emoji : `(${country.alpha2})`}{' '}
|
||||
{country.countryCallingCode}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
/>
|
||||
<FormField
|
||||
errorMessage={phoneErrorMessage}
|
||||
type="text"
|
||||
placeholder={this.state.country_code === '+1' ? '(555) 555-5555' : '5555555555'}
|
||||
name="phone"
|
||||
value={this.state.phone}
|
||||
error={phoneErrorMessage}
|
||||
onChange={event => {
|
||||
this.handleChanged(event);
|
||||
}}
|
||||
render={() => (
|
||||
<input
|
||||
type="text"
|
||||
placeholder={this.state.country_code === '+1' ? '(555) 555-5555' : '5555555555'}
|
||||
name="phone"
|
||||
value={this.state.phone}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormRow>
|
||||
<div className="card__actions card__actions--center">
|
||||
<Submit label="Submit" disabled={isPending} />
|
||||
{cancelButton}
|
||||
</div>
|
||||
<Submit label="Submit" disabled={isPending} />
|
||||
{cancelButton}
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
import Button from 'component/button';
|
||||
import { Form, FormElement, Submit } from 'component/common/form';
|
||||
import { Form, FormField, Submit } from 'component/common/form';
|
||||
|
||||
class UserPhoneVerify extends React.PureComponent {
|
||||
constructor(props) {
|
||||
|
@ -37,31 +37,27 @@ class UserPhoneVerify extends React.PureComponent {
|
|||
{__(
|
||||
`Please enter the verification code sent to +${countryCode}${phone}. Didn't receive it? `
|
||||
)}
|
||||
<Button onClick={this.reset.bind(this)} label="Go back." />
|
||||
<Button button="link" onClick={this.reset.bind(this)} label="Go back." />
|
||||
</p>
|
||||
<FormElement
|
||||
<FormField
|
||||
type="text"
|
||||
name="code"
|
||||
value={this.state.code}
|
||||
onChange={event => {
|
||||
this.handleCodeChanged(event);
|
||||
}}
|
||||
label={__('Verification Code')}
|
||||
errorMessage={phoneErrorMessage}
|
||||
render={() => (
|
||||
<input
|
||||
type="text"
|
||||
name="code"
|
||||
value={this.state.code}
|
||||
onChange={event => {
|
||||
this.handleCodeChanged(event);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
error={phoneErrorMessage}
|
||||
/>
|
||||
{/* render help separately so it always shows */}
|
||||
<div className="form-field__helper">
|
||||
<div className="meta">
|
||||
<p>
|
||||
{__('Email')} <Button href="mailto:help@lbry.io" label="help@lbry.io" /> or join our{' '}
|
||||
<Button href="https://chat.lbry.io" label="chat" />{' '}
|
||||
{__('Email')} <Button button="link" href="mailto:help@lbry.io" label="help@lbry.io" />{' '}
|
||||
or join our <Button button="link" href="https://chat.lbry.io" label="chat" />{' '}
|
||||
{__('if you encounter any trouble with your code.')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="form-row-submit">
|
||||
<div className="card__actions card__actions--center">
|
||||
<Submit label={__('Verify')} />
|
||||
{cancelButton}
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { doUserIdentityVerify } from 'redux/actions/user';
|
||||
|
@ -10,7 +9,7 @@ import {
|
|||
} from 'redux/selectors/user';
|
||||
import UserVerify from './view';
|
||||
import { selectCurrentModal } from 'redux/selectors/app';
|
||||
import { doOpenModal } from 'redux/actions/app';
|
||||
import { doNotify } from 'lbry-redux';
|
||||
import { PHONE_COLLECTION } from 'constants/modal_types';
|
||||
|
||||
const select = (state, props) => {
|
||||
|
@ -27,7 +26,7 @@ const select = (state, props) => {
|
|||
const perform = dispatch => ({
|
||||
navigate: uri => dispatch(doNavigate(uri)),
|
||||
verifyUserIdentity: token => dispatch(doUserIdentityVerify(token)),
|
||||
verifyPhone: () => dispatch(doOpenModal(PHONE_COLLECTION)),
|
||||
verifyPhone: () => dispatch(doNotify({ id: PHONE_COLLECTION })),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(UserVerify);
|
||||
|
|
|
@ -1,31 +1,31 @@
|
|||
/* eslint-disable */
|
||||
import React from 'react';
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import Button from 'component/button';
|
||||
import CardVerify from 'component/cardVerify';
|
||||
import lbryio from 'lbryio.js';
|
||||
import lbryio from 'lbryio';
|
||||
import * as icons from 'constants/icons';
|
||||
|
||||
class UserVerify extends React.PureComponent {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
type Props = {
|
||||
errorMessage: ?string,
|
||||
isPending: boolean,
|
||||
navigate: string => void,
|
||||
verifyUserIdentity: string => void,
|
||||
verifyPhone: () => void,
|
||||
};
|
||||
|
||||
this.state = {
|
||||
code: '',
|
||||
};
|
||||
class UserVerify extends React.PureComponent<Props> {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
(this: any).onToken = this.onToken.bind(this);
|
||||
}
|
||||
|
||||
handleCodeChanged(event) {
|
||||
this.setState({
|
||||
code: event.target.value,
|
||||
});
|
||||
}
|
||||
|
||||
onToken(data) {
|
||||
onToken(data: { id: string }) {
|
||||
this.props.verifyUserIdentity(data.id);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { errorMessage, isPending, navigate, verifyPhone, modal } = this.props;
|
||||
const { errorMessage, isPending, navigate, verifyPhone } = this.props;
|
||||
return (
|
||||
<React.Fragment>
|
||||
<section className="card card--section">
|
||||
|
@ -39,20 +39,18 @@ class UserVerify extends React.PureComponent {
|
|||
</div>
|
||||
</section>
|
||||
<section className="card card--section">
|
||||
<div className="card__title">
|
||||
<h3>{__('1) Proof via Credit')}</h3>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
<div className="card__title">{__('1) Proof via Credit')}</div>
|
||||
<p className="card__content">
|
||||
{`${__(
|
||||
'If you have a valid credit or debit card, you can use it to instantly prove your humanity.'
|
||||
)} ${__('There is no charge at all for this, now or in the future.')} `}
|
||||
</div>
|
||||
</p>
|
||||
<div className="card__actions">
|
||||
{errorMessage && <p className="form-field__error">{errorMessage}</p>}
|
||||
<CardVerify
|
||||
label={__('Perform Card Verification')}
|
||||
disabled={isPending}
|
||||
token={this.onToken.bind(this)}
|
||||
token={this.onToken}
|
||||
stripeKey={lbryio.getStripeToken()}
|
||||
/>
|
||||
</div>
|
||||
|
@ -60,6 +58,7 @@ class UserVerify extends React.PureComponent {
|
|||
<div className="meta">
|
||||
{__('A $1 authorization may temporarily appear with your provider.')}{' '}
|
||||
<Button
|
||||
button="link"
|
||||
href="https://lbry.io/faq/identity-requirements"
|
||||
label={__('Read more about why we do this.')}
|
||||
/>
|
||||
|
@ -67,20 +66,18 @@ class UserVerify extends React.PureComponent {
|
|||
</div>
|
||||
</section>
|
||||
<section className="card card--section">
|
||||
<div className="card__title">
|
||||
<h3>{__('2) Proof via Phone')}</h3>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
<div className="card__title">{__('2) Proof via Phone')}</div>
|
||||
<p className="card__content">
|
||||
{`${__(
|
||||
'You will receive an SMS text message confirming that your phone number is correct.'
|
||||
)}`}
|
||||
</div>
|
||||
</p>
|
||||
<div className="card__actions">
|
||||
<Button
|
||||
onClick={() => {
|
||||
verifyPhone();
|
||||
}}
|
||||
button="alt"
|
||||
button="primary"
|
||||
icon={icons.PHONE}
|
||||
label={__('Submit Phone Number')}
|
||||
/>
|
||||
|
@ -88,14 +85,12 @@ class UserVerify extends React.PureComponent {
|
|||
<div className="card__content">
|
||||
<div className="meta">
|
||||
{__('Standard messaging rates apply. Having trouble?')}{' '}
|
||||
<Button href="https://lbry.io/faq/phone" label={__('Read more.')} />
|
||||
<Button button="link" href="https://lbry.io/faq/phone" label={__('Read more.')} />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
<section className="card card--form">
|
||||
<div className="card__title">
|
||||
<h3>{__('3) Proof via Chat')}</h3>
|
||||
</div>
|
||||
<section className="card card--section">
|
||||
<div className="card__title">{__('3) Proof via Chat')}</div>
|
||||
<div className="card__content">
|
||||
<p>
|
||||
{__(
|
||||
|
@ -111,25 +106,25 @@ class UserVerify extends React.PureComponent {
|
|||
<div className="card__actions">
|
||||
<Button
|
||||
href="https://chat.lbry.io"
|
||||
button="alt"
|
||||
button="primary"
|
||||
icon={icons.MESSAGE}
|
||||
label={__('Join LBRY Chat')}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<section className="card card--section">
|
||||
<div className="card__title">
|
||||
<h5>{__('Or, Skip It Entirely')}</h5>
|
||||
</div>
|
||||
<div className="card__content">
|
||||
<p className="meta">
|
||||
{__(
|
||||
'You can continue without this step, but you will not be eligible to earn rewards.'
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="card__title">{__('Or, Skip It Entirely')}</div>
|
||||
<p className="card__content">
|
||||
{__(
|
||||
'You can continue without this step, but you will not be eligible to earn rewards.'
|
||||
)}
|
||||
</p>
|
||||
<div className="card__actions">
|
||||
<Button onClick={() => navigate('/discover')} button="alt" label={__('Skip Rewards')} />
|
||||
<Button
|
||||
onClick={() => navigate('/discover')}
|
||||
button="primary"
|
||||
label={__('Skip Rewards')}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</React.Fragment>
|
||||
|
@ -138,4 +133,3 @@ class UserVerify extends React.PureComponent {
|
|||
}
|
||||
|
||||
export default UserVerify;
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doChangeVolume } from 'redux/actions/app';
|
||||
import { selectVolume } from 'redux/selectors/app';
|
||||
import { doPlayUri, doSetPlayingUri } from 'redux/actions/content';
|
||||
import { doPlay, doPause, savePosition } from 'redux/actions/media';
|
||||
import { makeSelectMetadataForUri, makeSelectContentTypeForUri } from 'redux/selectors/claims';
|
||||
import {
|
||||
makeSelectMetadataForUri,
|
||||
makeSelectContentTypeForUri,
|
||||
makeSelectCostInfoForUri,
|
||||
makeSelectClaimForUri,
|
||||
makeSelectFileInfoForUri,
|
||||
makeSelectLoadingForUri,
|
||||
makeSelectDownloadingForUri,
|
||||
} from 'redux/selectors/file_info';
|
||||
import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info';
|
||||
} from 'lbry-redux';
|
||||
import { selectShowNsfw } from 'redux/selectors/settings';
|
||||
import { selectMediaPaused, makeSelectMediaPositionForUri } from 'redux/selectors/media';
|
||||
import Video from './view';
|
||||
import { selectPlayingUri } from 'redux/selectors/content';
|
||||
import { makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||
import Video from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
claim: makeSelectClaimForUri(props.uri)(state),
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import lbry from 'lbry';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import classnames from 'classnames';
|
||||
import VideoPlayer from './internal/player';
|
||||
import VideoPlayButton from './internal/play-button';
|
||||
|
@ -75,7 +75,7 @@ class Video extends React.PureComponent<Props> {
|
|||
const isPlaying = playingUri === uri;
|
||||
const isReadyToPlay = fileInfo && fileInfo.written_bytes > 0;
|
||||
const shouldObscureNsfw = obscureNsfw && metadata && metadata.nsfw;
|
||||
const mediaType = lbry.getMediaType(contentType, fileInfo && fileInfo.file_name);
|
||||
const mediaType = Lbry.getMediaType(contentType, fileInfo && fileInfo.file_name);
|
||||
|
||||
let loadStatusMessage = '';
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doCheckAddressIsMine, doGetNewAddress } from 'redux/actions/wallet';
|
||||
import { selectReceiveAddress, selectGettingNewAddress } from 'redux/selectors/wallet';
|
||||
import {
|
||||
doCheckAddressIsMine,
|
||||
doGetNewAddress,
|
||||
selectReceiveAddress,
|
||||
selectGettingNewAddress,
|
||||
} from 'lbry-redux';
|
||||
import WalletAddress from './view';
|
||||
|
||||
const select = state => ({
|
||||
|
|
|
@ -13,7 +13,12 @@ type Props = {
|
|||
|
||||
class WalletAddress extends React.PureComponent<Props> {
|
||||
componentWillMount() {
|
||||
this.props.checkAddressIsMine(this.props.receiveAddress);
|
||||
const { checkAddressIsMine, receiveAddress, getNewAddress } = this.props;
|
||||
if (!receiveAddress) {
|
||||
getNewAddress();
|
||||
} else {
|
||||
checkAddressIsMine(receiveAddress);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { selectBalance } from 'redux/selectors/wallet';
|
||||
import { selectBalance } from 'lbry-redux';
|
||||
import WalletBalance from './view';
|
||||
|
||||
const select = state => ({
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doSendDraftTransaction } from 'redux/actions/wallet';
|
||||
import { selectBalance } from 'redux/selectors/wallet';
|
||||
import { doSendDraftTransaction, selectBalance } from 'lbry-redux';
|
||||
import WalletSend from './view';
|
||||
|
||||
const perform = dispatch => ({
|
||||
|
|
|
@ -51,6 +51,7 @@ class WalletSend extends React.PureComponent<Props> {
|
|||
postfix={__('LBC')}
|
||||
className="input--price-amount"
|
||||
min="0"
|
||||
step="any"
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
value={values.amount}
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doSendSupport } from 'redux/actions/wallet';
|
||||
import {
|
||||
doSendSupport,
|
||||
makeSelectTitleForUri,
|
||||
makeSelectClaimForUri,
|
||||
selectIsSendingSupport,
|
||||
} from 'lbry-redux';
|
||||
import WalletSendTip from './view';
|
||||
import { makeSelectTitleForUri } from 'redux/selectors/claims';
|
||||
import { selectIsSendingSupport } from 'redux/selectors/wallet';
|
||||
|
||||
const select = (state, props) => ({
|
||||
isPending: selectIsSendingSupport(state),
|
||||
title: makeSelectTitleForUri(props.uri)(state),
|
||||
claim: makeSelectClaimForUri(props.uri)(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
sendSupport: (amount, claim_id, uri) => dispatch(doSendSupport(amount, claim_id, uri)),
|
||||
sendSupport: (amount, claimId, uri) => dispatch(doSendSupport(amount, claimId, uri)),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(WalletSendTip);
|
||||
|
|
|
@ -5,9 +5,9 @@ import { FormField } from 'component/common/form';
|
|||
import UriIndicator from 'component/uriIndicator';
|
||||
|
||||
type Props = {
|
||||
claim_id: string,
|
||||
uri: string,
|
||||
title: string,
|
||||
claim: { claim_id: string },
|
||||
errorMessage: string,
|
||||
isPending: boolean,
|
||||
sendSupport: (number, string, string) => void,
|
||||
|
@ -31,7 +31,8 @@ class WalletSendTip extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
handleSendButtonClicked() {
|
||||
const { claim_id: claimId, uri, sendSupport, sendTipCallback } = this.props;
|
||||
const { claim, uri, sendSupport, sendTipCallback } = this.props;
|
||||
const { claim_id: claimId } = claim;
|
||||
const { amount } = this.state;
|
||||
|
||||
sendSupport(amount, claimId, uri);
|
||||
|
@ -49,7 +50,7 @@ class WalletSendTip extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { errorMessage, isPending, title, uri, onCancel } = this.props;
|
||||
const { title, errorMessage, isPending, uri, onCancel } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
import * as MODALS from 'constants/modal_types';
|
||||
import { connect } from 'react-redux';
|
||||
import { normalizeURI } from 'lbryURI';
|
||||
import { selectState as selectSearch, selectWunderBarAddress } from 'redux/selectors/search';
|
||||
import { doUpdateSearchQuery } from 'redux/actions/search';
|
||||
import { normalizeURI } from 'lbry-redux';
|
||||
import {
|
||||
selectSearchState as selectSearch,
|
||||
selectWunderBarAddress,
|
||||
doUpdateSearchQuery,
|
||||
doNotify,
|
||||
} from 'lbry-redux';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { doOpenModal } from 'redux/actions/app';
|
||||
import Wunderbar from './view';
|
||||
|
||||
const select = state => {
|
||||
|
@ -24,7 +27,7 @@ const select = state => {
|
|||
const perform = dispatch => ({
|
||||
onSearch: query => {
|
||||
dispatch(doUpdateSearchQuery(query));
|
||||
dispatch(doOpenModal(MODALS.SEARCH));
|
||||
dispatch(doNotify({ id: MODALS.SEARCH }));
|
||||
},
|
||||
onSubmit: (uri, extraParams) => dispatch(doNavigate('/show', { uri, ...extraParams })),
|
||||
updateSearchQuery: query => dispatch(doUpdateSearchQuery(query)),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import classnames from 'classnames';
|
||||
import { normalizeURI } from 'lbryURI';
|
||||
import { normalizeURI } from 'lbry-redux';
|
||||
import Icon from 'component/common/icon';
|
||||
import { parseQueryParams } from 'util/query_params';
|
||||
import * as icons from 'constants/icons';
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
export const OPEN_MODAL = 'OPEN_MODAL';
|
||||
export const CLOSE_MODAL = 'CLOSE_MODAL';
|
||||
export const SHOW_SNACKBAR = 'SHOW_SNACKBAR';
|
||||
export const REMOVE_SNACKBAR_SNACK = 'REMOVE_SNACKBAR_SNACK';
|
||||
export const WINDOW_FOCUSED = 'WINDOW_FOCUSED';
|
||||
export const DAEMON_READY = 'DAEMON_READY';
|
||||
export const DAEMON_VERSION_MATCH = 'DAEMON_VERSION_MATCH';
|
||||
|
|
|
@ -7,12 +7,8 @@ import { ipcRenderer, remote, shell } from 'electron';
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import {
|
||||
doConditionalAuthNavigate,
|
||||
doDaemonReady,
|
||||
doShowSnackBar,
|
||||
doAutoUpdate,
|
||||
} from 'redux/actions/app';
|
||||
import { doConditionalAuthNavigate, doDaemonReady, doAutoUpdate } from 'redux/actions/app';
|
||||
import { doNotify } from 'lbry-redux';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { doDownloadLanguages, doUpdateIsNightAsync } from 'redux/actions/settings';
|
||||
import { doUserEmailVerify } from 'redux/actions/user';
|
||||
|
@ -38,7 +34,12 @@ ipcRenderer.on('open-uri-requested', (event, uri, newSession) => {
|
|||
app.store.dispatch(doConditionalAuthNavigate(newSession));
|
||||
app.store.dispatch(doUserEmailVerify(verification.token, verification.recaptcha));
|
||||
} else {
|
||||
app.store.dispatch(doShowSnackBar({ message: 'Invalid Verification URI' }));
|
||||
app.store.dispatch(
|
||||
doNotify({
|
||||
message: 'Invalid Verification URI',
|
||||
displayType: ['snackbar'],
|
||||
})
|
||||
);
|
||||
}
|
||||
} else {
|
||||
app.store.dispatch(doNavigate('/show', { uri }));
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
const jsonrpc = {};
|
||||
|
||||
jsonrpc.call = (
|
||||
connectionString,
|
||||
method,
|
||||
params,
|
||||
callback,
|
||||
errorCallback,
|
||||
connectFailedCallback
|
||||
) => {
|
||||
function checkAndParse(response) {
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return response.json();
|
||||
}
|
||||
return response.json().then(json => {
|
||||
let error;
|
||||
if (json.error) {
|
||||
error = new Error(json.error.message);
|
||||
} else {
|
||||
error = new Error('Protocol error with unknown response signature');
|
||||
}
|
||||
return Promise.reject(error);
|
||||
});
|
||||
}
|
||||
|
||||
const counter = parseInt(sessionStorage.getItem('JSONRPCCounter') || 0, 10);
|
||||
const url = connectionString;
|
||||
const options = {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
method,
|
||||
params,
|
||||
id: counter,
|
||||
}),
|
||||
};
|
||||
|
||||
sessionStorage.setItem('JSONRPCCounter', counter + 1);
|
||||
|
||||
return fetch(url, options)
|
||||
.then(checkAndParse)
|
||||
.then(response => {
|
||||
const error = response.error || (response.result && response.result.error);
|
||||
|
||||
if (!error && typeof callback === 'function') {
|
||||
return callback(response.result);
|
||||
}
|
||||
|
||||
if (error && typeof errorCallback === 'function') {
|
||||
return errorCallback(error);
|
||||
}
|
||||
|
||||
const errorEvent = new CustomEvent('unhandledError', {
|
||||
detail: {
|
||||
connectionString,
|
||||
method,
|
||||
params,
|
||||
code: error.code,
|
||||
message: error.message || error,
|
||||
data: error.data,
|
||||
},
|
||||
});
|
||||
document.dispatchEvent(errorEvent);
|
||||
|
||||
return Promise.resolve();
|
||||
})
|
||||
.catch(error => {
|
||||
if (connectFailedCallback) {
|
||||
return connectFailedCallback(error);
|
||||
}
|
||||
|
||||
const errorEvent = new CustomEvent('unhandledError', {
|
||||
detail: {
|
||||
connectionString,
|
||||
method,
|
||||
params,
|
||||
code: error.response && error.response.status,
|
||||
message: __('Connection to API server failed'),
|
||||
},
|
||||
});
|
||||
document.dispatchEvent(errorEvent);
|
||||
return Promise.resolve();
|
||||
});
|
||||
};
|
||||
|
||||
export default jsonrpc;
|
|
@ -1,151 +0,0 @@
|
|||
import { ipcRenderer } from 'electron';
|
||||
import jsonrpc from 'jsonrpc';
|
||||
|
||||
const CHECK_DAEMON_STARTED_TRY_NUMBER = 200;
|
||||
|
||||
const Lbry = {
|
||||
isConnected: false,
|
||||
daemonConnectionString: 'http://localhost:5279',
|
||||
pendingPublishTimeout: 20 * 60 * 1000,
|
||||
};
|
||||
|
||||
function apiCall(method, params, resolve, reject) {
|
||||
return jsonrpc.call(Lbry.daemonConnectionString, method, params, resolve, reject, reject);
|
||||
}
|
||||
|
||||
const lbryProxy = new Proxy(Lbry, {
|
||||
get(target, name) {
|
||||
if (name in target) {
|
||||
return target[name];
|
||||
}
|
||||
|
||||
return (params = {}) =>
|
||||
new Promise((resolve, reject) => {
|
||||
apiCall(name, params, resolve, reject);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
function getLocal(key, fallback = undefined) {
|
||||
const itemRaw = localStorage.getItem(key);
|
||||
return itemRaw === null ? fallback : JSON.parse(itemRaw);
|
||||
}
|
||||
|
||||
function setLocal(key, value) {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
}
|
||||
|
||||
// core
|
||||
Lbry.connectPromise = null;
|
||||
Lbry.connect = () => {
|
||||
if (Lbry.connectPromise === null) {
|
||||
Lbry.connectPromise = new Promise((resolve, reject) => {
|
||||
let tryNum = 0;
|
||||
|
||||
// Check every half second to see if the daemon is accepting connections
|
||||
function checkDaemonStarted() {
|
||||
tryNum += 1;
|
||||
lbryProxy
|
||||
.status()
|
||||
.then(resolve)
|
||||
.catch(() => {
|
||||
if (tryNum <= CHECK_DAEMON_STARTED_TRY_NUMBER) {
|
||||
setTimeout(checkDaemonStarted, tryNum < 50 ? 400 : 1000);
|
||||
} else {
|
||||
reject(new Error('Unable to connect to LBRY'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
checkDaemonStarted();
|
||||
});
|
||||
}
|
||||
|
||||
return Lbry.connectPromise;
|
||||
};
|
||||
|
||||
Lbry.imagePath = file => `${staticResourcesPath}/img/${file}`;
|
||||
|
||||
Lbry.getAppVersionInfo = () =>
|
||||
new Promise(resolve => {
|
||||
ipcRenderer.once('version-info-received', (event, versionInfo) => {
|
||||
resolve(versionInfo);
|
||||
});
|
||||
ipcRenderer.send('version-info-requested');
|
||||
});
|
||||
|
||||
Lbry.getMediaType = (contentType, fileName) => {
|
||||
if (contentType) {
|
||||
return /^[^/]+/.exec(contentType)[0];
|
||||
} else if (fileName) {
|
||||
const dotIndex = fileName.lastIndexOf('.');
|
||||
if (dotIndex === -1) {
|
||||
return 'unknown';
|
||||
}
|
||||
|
||||
const ext = fileName.substr(dotIndex + 1);
|
||||
if (/^mp4|m4v|webm|flv|f4v|ogv$/i.test(ext)) {
|
||||
return 'video';
|
||||
} else if (/^mp3|m4a|aac|wav|flac|ogg|opus$/i.test(ext)) {
|
||||
return 'audio';
|
||||
} else if (/^html|htm|xml|pdf|odf|doc|docx|md|markdown|txt|epub|org$/i.test(ext)) {
|
||||
return 'document';
|
||||
}
|
||||
return 'unknown';
|
||||
}
|
||||
return 'unknown';
|
||||
};
|
||||
|
||||
/**
|
||||
* Wrappers for API methods to simulate missing or future behavior. Unlike the old-style stubs,
|
||||
* these are designed to be transparent wrappers around the corresponding API methods.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns results from the file_list API method, plus dummy entries for pending publishes.
|
||||
* (If a real publish with the same claim name is found, the pending publish will be ignored and removed.)
|
||||
*/
|
||||
Lbry.file_list = (params = {}) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const { claim_name: claimName, channel_name: channelName, outpoint } = params;
|
||||
|
||||
apiCall(
|
||||
'file_list',
|
||||
params,
|
||||
fileInfos => {
|
||||
resolve(fileInfos);
|
||||
},
|
||||
reject
|
||||
);
|
||||
});
|
||||
|
||||
Lbry.claim_list_mine = (params = {}) =>
|
||||
new Promise((resolve, reject) => {
|
||||
apiCall(
|
||||
'claim_list_mine',
|
||||
params,
|
||||
claims => {
|
||||
resolve(claims);
|
||||
},
|
||||
reject
|
||||
);
|
||||
});
|
||||
|
||||
Lbry.resolve = (params = {}) =>
|
||||
new Promise((resolve, reject) => {
|
||||
apiCall(
|
||||
'resolve',
|
||||
params,
|
||||
data => {
|
||||
if ('uri' in params) {
|
||||
// If only a single URI was requested, don't nest the results in an object
|
||||
resolve(data && data[params.uri] ? data[params.uri] : {});
|
||||
} else {
|
||||
resolve(data || {});
|
||||
}
|
||||
},
|
||||
reject
|
||||
);
|
||||
});
|
||||
|
||||
export default lbryProxy;
|
|
@ -1,230 +0,0 @@
|
|||
const channelNameMinLength = 1;
|
||||
const claimIdMaxLength = 40;
|
||||
|
||||
export const regexInvalidURI = /[^A-Za-z0-9-]/g;
|
||||
export const regexAddress = /^b(?=[^0OIl]{32,33})[0-9A-Za-z]{32,33}$/;
|
||||
|
||||
/**
|
||||
* Parses a LBRY name into its component parts. Throws errors with user-friendly
|
||||
* messages for invalid names.
|
||||
*
|
||||
* N.B. that "name" indicates the value in the name position of the URI. For
|
||||
* claims for channel content, this will actually be the channel name, and
|
||||
* the content name is in the path (e.g. lbry://@channel/content)
|
||||
*
|
||||
* In most situations, you'll want to use the contentName and channelName keys
|
||||
* and ignore the name key.
|
||||
*
|
||||
* Returns a dictionary with keys:
|
||||
* - name (string): The value in the "name" position in the URI. Note that this
|
||||
* could be either content name or channel name; see above.
|
||||
* - path (string, if persent)
|
||||
* - claimSequence (int, if present)
|
||||
* - bidPosition (int, if present)
|
||||
* - claimId (string, if present)
|
||||
* - isChannel (boolean)
|
||||
* - contentName (string): For anon claims, the name; for channel claims, the path
|
||||
* - channelName (string, if present): Channel name without @
|
||||
*/
|
||||
export function parseURI(URI, requireProto = false) {
|
||||
// Break into components. Empty sub-matches are converted to null
|
||||
const componentsRegex = new RegExp(
|
||||
'^((?:lbry://)?)' + // protocol
|
||||
'([^:$#/]*)' + // claim name (stops at the first separator or end)
|
||||
'([:$#]?)([^/]*)' + // modifier separator, modifier (stops at the first path separator or end)
|
||||
'(/?)(.*)' // path separator, path
|
||||
);
|
||||
const [proto, claimName, modSep, modVal, pathSep, path] = componentsRegex
|
||||
.exec(URI)
|
||||
.slice(1)
|
||||
.map(match => match || null);
|
||||
|
||||
let contentName;
|
||||
|
||||
// Validate protocol
|
||||
if (requireProto && !proto) {
|
||||
throw new Error(__('LBRY URIs must include a protocol prefix (lbry://).'));
|
||||
}
|
||||
|
||||
// Validate and process name
|
||||
if (!claimName) {
|
||||
throw new Error(__('URI does not include name.'));
|
||||
}
|
||||
|
||||
const isChannel = claimName.startsWith('@');
|
||||
const channelName = isChannel ? claimName.slice(1) : claimName;
|
||||
|
||||
if (isChannel) {
|
||||
if (!channelName) {
|
||||
throw new Error(__('No channel name after @.'));
|
||||
}
|
||||
|
||||
if (channelName.length < channelNameMinLength) {
|
||||
throw new Error(__(`Channel names must be at least %s characters.`, channelNameMinLength));
|
||||
}
|
||||
|
||||
contentName = path;
|
||||
}
|
||||
|
||||
const nameBadChars = (channelName || claimName).match(regexInvalidURI);
|
||||
if (nameBadChars) {
|
||||
throw new Error(
|
||||
__(
|
||||
`Invalid character %s in name: %s.`,
|
||||
nameBadChars.length === 1 ? '' : 's',
|
||||
nameBadChars.join(', ')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Validate and process modifier (claim ID, bid position or claim sequence)
|
||||
let claimId;
|
||||
let claimSequence;
|
||||
let bidPosition;
|
||||
if (modSep) {
|
||||
if (!modVal) {
|
||||
throw new Error(__(`No modifier provided after separator %s.`, modSep));
|
||||
}
|
||||
|
||||
if (modSep === '#') {
|
||||
claimId = modVal;
|
||||
} else if (modSep === ':') {
|
||||
claimSequence = modVal;
|
||||
} else if (modSep === '$') {
|
||||
bidPosition = modVal;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
claimId &&
|
||||
(claimId.length > claimIdMaxLength || !claimId.match(/^[0-9a-f]+$/)) &&
|
||||
!claimId.match(/^pending/) // ought to be dropped when savePendingPublish drops hack
|
||||
) {
|
||||
throw new Error(__(`Invalid claim ID %s.`, claimId));
|
||||
}
|
||||
|
||||
if (claimSequence && !claimSequence.match(/^-?[1-9][0-9]*$/)) {
|
||||
throw new Error(__('Claim sequence must be a number.'));
|
||||
}
|
||||
|
||||
if (bidPosition && !bidPosition.match(/^-?[1-9][0-9]*$/)) {
|
||||
throw new Error(__('Bid position must be a number.'));
|
||||
}
|
||||
|
||||
// Validate and process path
|
||||
if (path) {
|
||||
if (!isChannel) {
|
||||
throw new Error(__('Only channel URIs may have a path.'));
|
||||
}
|
||||
|
||||
const pathBadChars = path.match(regexInvalidURI);
|
||||
if (pathBadChars) {
|
||||
throw new Error(__(`Invalid character in path: %s`, pathBadChars.join(', ')));
|
||||
}
|
||||
|
||||
contentName = path;
|
||||
} else if (pathSep) {
|
||||
throw new Error(__('No path provided after /'));
|
||||
}
|
||||
|
||||
return {
|
||||
claimName,
|
||||
path,
|
||||
isChannel,
|
||||
...(contentName ? { contentName } : {}),
|
||||
...(channelName ? { channelName } : {}),
|
||||
...(claimSequence ? { claimSequence: parseInt(claimSequence, 10) } : {}),
|
||||
...(bidPosition ? { bidPosition: parseInt(bidPosition, 10) } : {}),
|
||||
...(claimId ? { claimId } : {}),
|
||||
...(path ? { path } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes an object in the same format returned by parse() and builds a URI.
|
||||
*
|
||||
* The channelName key will accept names with or without the @ prefix.
|
||||
*/
|
||||
export function buildURI(URIObj, includeProto = true) {
|
||||
const { claimId, claimSequence, bidPosition, contentName, channelName } = URIObj;
|
||||
|
||||
let { claimName, path } = URIObj;
|
||||
|
||||
if (channelName) {
|
||||
const channelNameFormatted = channelName.startsWith('@') ? channelName : `@${channelName}`;
|
||||
if (!claimName) {
|
||||
claimName = channelNameFormatted;
|
||||
} else if (claimName !== channelNameFormatted) {
|
||||
throw new Error(
|
||||
__(
|
||||
'Received a channel content URI, but claim name and channelName do not match. "name" represents the value in the name position of the URI (lbry://name...), which for channel content will be the channel name. In most cases, to construct a channel URI you should just pass channelName and contentName.'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (contentName) {
|
||||
if (!claimName) {
|
||||
claimName = contentName;
|
||||
} else if (!path) {
|
||||
path = contentName;
|
||||
}
|
||||
if (path && path !== contentName) {
|
||||
throw new Error(
|
||||
__(
|
||||
'Path and contentName do not match. Only one is required; most likely you wanted contentName.'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
(includeProto ? 'lbry://' : '') +
|
||||
claimName +
|
||||
(claimId ? `#${claimId}` : '') +
|
||||
(claimSequence ? `:${claimSequence}` : '') +
|
||||
(bidPosition ? `${bidPosition}` : '') +
|
||||
(path ? `/${path}` : '')
|
||||
);
|
||||
}
|
||||
|
||||
/* Takes a parseable LBRY URI and converts it to standard, canonical format */
|
||||
export function normalizeURI(URI) {
|
||||
if (URI.match(/pending_claim/)) return URI;
|
||||
|
||||
const { claimName, path, bidPosition, claimSequence, claimId } = parseURI(URI);
|
||||
return buildURI({ claimName, path, claimSequence, bidPosition, claimId });
|
||||
}
|
||||
|
||||
export function isURIValid(URI) {
|
||||
let parts;
|
||||
try {
|
||||
parts = parseURI(normalizeURI(URI));
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return parts && parts.claimName;
|
||||
}
|
||||
|
||||
export function isNameValid(claimName, checkCase = true) {
|
||||
const regexp = new RegExp('^[a-z0-9-]+$', checkCase ? '' : 'i');
|
||||
return regexp.test(claimName);
|
||||
}
|
||||
|
||||
export function isURIClaimable(URI) {
|
||||
let parts;
|
||||
try {
|
||||
parts = parseURI(normalizeURI(URI));
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
parts &&
|
||||
parts.claimName &&
|
||||
!parts.claimId &&
|
||||
!parts.bidPosition &&
|
||||
!parts.claimSequence &&
|
||||
!parts.isChannel &&
|
||||
!parts.path
|
||||
);
|
||||
}
|
|
@ -1,38 +1,16 @@
|
|||
import { ipcRenderer } from 'electron';
|
||||
import Lbry from 'lbry';
|
||||
import { Lbry } from 'lbry-redux';
|
||||
import querystring from 'querystring';
|
||||
|
||||
const Lbryio = {
|
||||
enabled: true,
|
||||
authenticationPromise: null,
|
||||
exchangePromise: null,
|
||||
exchangeLastFetched: null,
|
||||
};
|
||||
|
||||
const CONNECTION_STRING = process.env.LBRY_APP_API_URL
|
||||
? process.env.LBRY_APP_API_URL.replace(/\/*$/, '/') // exactly one slash at the end
|
||||
: 'https://api.lbry.io/';
|
||||
|
||||
const EXCHANGE_RATE_TIMEOUT = 20 * 60 * 1000;
|
||||
|
||||
Lbryio.getExchangeRates = () => {
|
||||
if (
|
||||
!Lbryio.exchangeLastFetched ||
|
||||
Date.now() - Lbryio.exchangeLastFetched > EXCHANGE_RATE_TIMEOUT
|
||||
) {
|
||||
Lbryio.exchangePromise = new Promise((resolve, reject) => {
|
||||
Lbryio.call('lbc', 'exchange_rate', {}, 'get', true)
|
||||
.then(({ lbc_usd: LBC_USD, lbc_btc: LBC_BTC, btc_usd: BTC_USD }) => {
|
||||
const rates = { LBC_USD, LBC_BTC, BTC_USD };
|
||||
resolve(rates);
|
||||
})
|
||||
.catch(reject);
|
||||
});
|
||||
Lbryio.exchangeLastFetched = Date.now();
|
||||
}
|
||||
return Lbryio.exchangePromise;
|
||||
};
|
||||
|
||||
Lbryio.call = (resource, action, params = {}, method = 'get') => {
|
||||
if (!Lbryio.enabled) {
|
||||
console.log(__('Internal API disabled'));
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doCloseModal } from 'redux/actions/app';
|
||||
import { doLoadVideo, doSetPlayingUri } from 'redux/actions/content';
|
||||
import { makeSelectMetadataForUri } from 'redux/selectors/claims';
|
||||
import { doHideNotification, makeSelectMetadataForUri } from 'lbry-redux';
|
||||
import ModalAffirmPurchase from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
|
@ -12,9 +10,9 @@ const select = (state, props) => ({
|
|||
const perform = dispatch => ({
|
||||
cancelPurchase: () => {
|
||||
dispatch(doSetPlayingUri(null));
|
||||
dispatch(doCloseModal());
|
||||
dispatch(doHideNotification());
|
||||
},
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
closeModal: () => dispatch(doHideNotification()),
|
||||
loadVideo: uri => dispatch(doLoadVideo(uri)),
|
||||
});
|
||||
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doCloseModal } from 'redux/actions/app';
|
||||
import { doHideNotification } from 'lbry-redux';
|
||||
import ModalAuthFailure from './view';
|
||||
|
||||
const select = state => ({});
|
||||
|
||||
const perform = dispatch => ({
|
||||
close: () => dispatch(doCloseModal()),
|
||||
close: () => dispatch(doHideNotification()),
|
||||
});
|
||||
|
||||
export default connect(null, null)(ModalAuthFailure);
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doCloseModal, doAutoUpdateDeclined } from 'redux/actions/app';
|
||||
import { doAutoUpdateDeclined } from 'redux/actions/app';
|
||||
import { doHideNotification } from 'lbry-redux';
|
||||
import ModalAutoUpdateConfirm from './view';
|
||||
|
||||
const perform = dispatch => ({
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
closeModal: () => dispatch(doHideNotification()),
|
||||
declineAutoUpdate: () => dispatch(doAutoUpdateDeclined()),
|
||||
});
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doCloseModal, doAutoUpdateDeclined } from 'redux/actions/app';
|
||||
import { doAutoUpdateDeclined } from 'redux/actions/app';
|
||||
import { doHideNotification } from 'lbry-redux';
|
||||
import ModalAutoUpdateDownloaded from './view';
|
||||
|
||||
const perform = dispatch => ({
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
closeModal: () => dispatch(doHideNotification()),
|
||||
declineAutoUpdate: () => dispatch(doAutoUpdateDeclined()),
|
||||
});
|
||||
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
import React from 'react';
|
||||
import { ipcRenderer } from 'electron';
|
||||
import { Modal } from 'modal/modal';
|
||||
import { Line } from 'rc-progress';
|
||||
import Button from 'component/button';
|
||||
|
||||
const { ipcRenderer } = require('electron');
|
||||
|
||||
class ModalAutoUpdateDownloaded extends React.PureComponent {
|
||||
render() {
|
||||
const { closeModal, declineAutoUpdate } = this.props;
|
||||
|
@ -32,6 +30,14 @@ class ModalAutoUpdateDownloaded extends React.PureComponent {
|
|||
'A new version of LBRY has been released, downloaded, and is ready for you to use pending a restart.'
|
||||
)}
|
||||
</p>
|
||||
<p className="meta text-center">
|
||||
{__('Want to know what has changed?')} See the{' '}
|
||||
<Button
|
||||
button="link"
|
||||
label={__('release notes')}
|
||||
href="https://github.com/lbryio/lbry-app/releases"
|
||||
/>.
|
||||
</p>
|
||||
</section>
|
||||
</Modal>
|
||||
);
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doCloseModal } from 'redux/actions/app';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import { doSetClientSetting } from 'redux/actions/settings';
|
||||
import { selectUserIsRewardApproved } from 'redux/selectors/user';
|
||||
import { selectBalance } from 'redux/selectors/wallet';
|
||||
import { selectBalance, doHideNotification } from 'lbry-redux';
|
||||
import { selectUnclaimedRewardValue } from 'redux/selectors/rewards';
|
||||
import * as settings from 'constants/settings';
|
||||
import ModalCreditIntro from './view';
|
||||
|
@ -18,11 +17,11 @@ const perform = dispatch => () => ({
|
|||
addBalance: () => {
|
||||
dispatch(doSetClientSetting(settings.CREDIT_REQUIRED_ACKNOWLEDGED, true));
|
||||
dispatch(doNavigate('/getcredits'));
|
||||
dispatch(doCloseModal());
|
||||
dispatch(doHideNotification());
|
||||
},
|
||||
closeModal: () => {
|
||||
dispatch(doSetClientSetting(settings.CREDIT_REQUIRED_ACKNOWLEDGED, true));
|
||||
dispatch(doCloseModal());
|
||||
dispatch(doHideNotification());
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -21,24 +21,24 @@ const ModalCreditIntro = props => {
|
|||
</p>
|
||||
{currentBalance <= 0 && (
|
||||
<p>
|
||||
You currently have <CreditAmount amount={currentBalance} />, so the actions you can take
|
||||
are limited.
|
||||
You currently have <CreditAmount noStyle amount={currentBalance} />, so the actions you
|
||||
can take are limited.
|
||||
</p>
|
||||
)}
|
||||
<p>
|
||||
There are a variety of ways to get credits, including more than{' '}
|
||||
{totalRewardValue ? (
|
||||
<CreditAmount amount={totalRewardRounded} />
|
||||
<CreditAmount noStyle amount={totalRewardRounded} />
|
||||
) : (
|
||||
<span className="credit-amount">{__('?? credits')}</span>
|
||||
)}{' '}
|
||||
{__(' in free rewards for participating in the LBRY beta.')}
|
||||
</p>
|
||||
|
||||
<div className="modal__buttons">
|
||||
<div className="card__actions card__actions--center">
|
||||
<Button button="primary" onClick={addBalance} label={__('Get Credits')} />
|
||||
<Button
|
||||
button="alt"
|
||||
button="link"
|
||||
onClick={closeModal}
|
||||
label={currentBalance <= 0 ? __('Use Without LBC') : __('Meh, Not Now')}
|
||||
/>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import React from 'react';
|
||||
import * as settings from 'constants/settings';
|
||||
import { connect } from 'react-redux';
|
||||
import { doCloseModal } from 'redux/actions/app';
|
||||
import { doHideNotification } from 'lbry-redux';
|
||||
import { doSetClientSetting } from 'redux/actions/settings';
|
||||
import { selectEmailToVerify, selectUser } from 'redux/selectors/user';
|
||||
import ModalEmailCollection from './view';
|
||||
|
@ -14,7 +13,7 @@ const select = state => ({
|
|||
const perform = dispatch => () => ({
|
||||
closeModal: () => {
|
||||
dispatch(doSetClientSetting(settings.EMAIL_COLLECTION_ACKNOWLEDGED, true));
|
||||
dispatch(doCloseModal());
|
||||
dispatch(doHideNotification());
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doCloseModal } from 'redux/actions/app';
|
||||
import { doHideNotification } from 'lbry-redux';
|
||||
import ModalError from './view';
|
||||
|
||||
const perform = dispatch => ({
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
closeModal: () => dispatch(doHideNotification()),
|
||||
});
|
||||
|
||||
export default connect(null, perform)(ModalError);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import lbry from 'lbry';
|
||||
import Native from 'native';
|
||||
import { ExpandableModal } from 'modal/modal';
|
||||
|
||||
class ModalError extends React.PureComponent {
|
||||
|
@ -8,7 +8,7 @@ class ModalError extends React.PureComponent {
|
|||
|
||||
const errorObj = typeof error === 'string' ? { message: error } : error;
|
||||
|
||||
const error_key_labels = {
|
||||
const errorKeyLabels = {
|
||||
connectionString: __('API connection string'),
|
||||
method: __('Method'),
|
||||
params: __('Parameters'),
|
||||
|
@ -20,7 +20,7 @@ class ModalError extends React.PureComponent {
|
|||
const errorInfoList = [];
|
||||
for (const key of Object.keys(errorObj)) {
|
||||
const val = typeof errorObj[key] === 'string' ? errorObj[key] : JSON.stringify(errorObj[key]);
|
||||
const label = error_key_labels[key];
|
||||
const label = errorKeyLabels[key];
|
||||
errorInfoList.push(
|
||||
<li key={key}>
|
||||
<strong>{label}</strong>: <code>{val}</code>
|
||||
|
@ -42,7 +42,11 @@ class ModalError extends React.PureComponent {
|
|||
|
||||
<div className="error-modal__content">
|
||||
<div>
|
||||
<img className="error-modal__warning-symbol" src={lbry.imagePath('warning.png')} />
|
||||
<img
|
||||
alt=""
|
||||
className="error-modal__warning-symbol"
|
||||
src={Native.imagePath('warning.png')}
|
||||
/>
|
||||
</div>
|
||||
<p>
|
||||
{__(
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { doCloseModal } from 'redux/actions/app';
|
||||
import { makeSelectMetadataForUri } from 'redux/selectors/claims';
|
||||
import { doHideNotification, makeSelectMetadataForUri } from 'lbry-redux';
|
||||
import ModalFileTimeout from './view';
|
||||
|
||||
const select = state => ({
|
||||
const select = (state, props) => ({
|
||||
metadata: makeSelectMetadataForUri(props.uri)(state),
|
||||
});
|
||||
|
||||
const perform = dispatch => ({
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
closeModal: () => dispatch(doHideNotification()),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(ModalFileTimeout);
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
import React from 'react';
|
||||
import rewards from 'rewards';
|
||||
import { connect } from 'react-redux';
|
||||
import { doCloseModal } from 'redux/actions/app';
|
||||
import { doHideNotification } from 'lbry-redux';
|
||||
import { makeSelectRewardByType } from 'redux/selectors/rewards';
|
||||
import ModalFirstReward from './view';
|
||||
|
||||
const select = (state, props) => {
|
||||
const select = state => {
|
||||
const selectReward = makeSelectRewardByType();
|
||||
|
||||
return {
|
||||
|
@ -14,7 +13,7 @@ const select = (state, props) => {
|
|||
};
|
||||
|
||||
const perform = dispatch => ({
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
closeModal: () => dispatch(doHideNotification()),
|
||||
});
|
||||
|
||||
export default connect(select, perform)(ModalFirstReward);
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { doCloseModal } from 'redux/actions/app';
|
||||
import { doHideNotification } from 'lbry-redux';
|
||||
import { doNavigate } from 'redux/actions/navigation';
|
||||
import ModalFirstSubscription from './view';
|
||||
|
||||
const perform = dispatch => () => ({
|
||||
closeModal: () => dispatch(doCloseModal()),
|
||||
closeModal: () => dispatch(doHideNotification()),
|
||||
navigate: path => dispatch(doNavigate(path)),
|
||||
});
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue