Merge branch 'master' into master
This commit is contained in:
commit
c6684054d5
21 changed files with 347 additions and 46 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -3,3 +3,5 @@
|
||||||
/static/daemon/lbrynet*
|
/static/daemon/lbrynet*
|
||||||
/static/locales
|
/static/locales
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
|
package-lock.json
|
||||||
|
.idea/
|
|
@ -6,7 +6,9 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/).
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
* Add ability to resend verification email ([#1492](https://github.com/lbryio/lbry-app/issues/1492))
|
* Add ability to resend verification email ([#1492](https://github.com/lbryio/lbry-app/issues/1492))
|
||||||
|
* Add Narrative about Feature Request on Help Page and Report Page ([#1551](https://github.com/lbryio/lbry-app/pull/1551))
|
||||||
* Add keyboard shortcut to quit the app on Windows ([#1202](https://github.com/lbryio/lbry-app/pull/1202))
|
* 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))
|
* 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))
|
* Add referral FAQ to Invites screen([#1314](https://github.com/lbryio/lbry-app/pull/1314))
|
||||||
|
|
|
@ -147,7 +147,7 @@ There are a few tools integrated to the project that will ease the process of de
|
||||||
If someone has been officially assigned an issue via Github's assignment system, it is also not
|
If someone has been officially assigned an issue via Github's assignment system, it is also not
|
||||||
available. Contributors are encouraged to ask if they have any questions about issue availability.
|
available. Contributors are encouraged to ask if they have any questions about issue availability.
|
||||||
* The [changelog](https://github.com/lbryio/lbry-app/blob/master/CHANGELOG.md) should be updated to
|
* The [changelog](https://github.com/lbryio/lbry-app/blob/master/CHANGELOG.md) should be updated to
|
||||||
include a referene to the fix/change/addition. See previous entries for format.
|
include a reference to the fix/change/addition. See previous entries for format.
|
||||||
* Once the pull request is visible in the LBRY repo, a LBRY team member will review it and make sure
|
* Once the pull request is visible in the LBRY repo, a LBRY team member will review it and make sure
|
||||||
it is up to our standards. At this point, the contributor may have to change his or her code based
|
it is up to our standards. At this point, the contributor may have to change his or her code based
|
||||||
on our suggestions and comments.
|
on our suggestions and comments.
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
"formik": "^0.10.4",
|
"formik": "^0.10.4",
|
||||||
"hast-util-sanitize": "^1.1.2",
|
"hast-util-sanitize": "^1.1.2",
|
||||||
"keytar": "^4.2.1",
|
"keytar": "^4.2.1",
|
||||||
"lbry-redux": "lbryio/lbry-redux#543af2fcee7e4c45ccaf73af7b47d4b1a5d8ad44",
|
"lbry-redux": "lbryio/lbry-redux#7759bc6e8c482bed173d1f10aee6f6f9a439a15a",
|
||||||
"localforage": "^1.7.1",
|
"localforage": "^1.7.1",
|
||||||
"mixpanel-browser": "^2.17.1",
|
"mixpanel-browser": "^2.17.1",
|
||||||
"moment": "^2.22.0",
|
"moment": "^2.22.0",
|
||||||
|
|
|
@ -9,6 +9,8 @@ type Props = {
|
||||||
type: string,
|
type: string,
|
||||||
currentPath: ?string,
|
currentPath: ?string,
|
||||||
onFileChosen: (string, string) => void,
|
onFileChosen: (string, string) => void,
|
||||||
|
fileLabel: ?string,
|
||||||
|
directoryLabel?: string,
|
||||||
};
|
};
|
||||||
|
|
||||||
class FileSelector extends React.PureComponent<Props> {
|
class FileSelector extends React.PureComponent<Props> {
|
||||||
|
@ -47,15 +49,14 @@ class FileSelector extends React.PureComponent<Props> {
|
||||||
input: ?HTMLInputElement;
|
input: ?HTMLInputElement;
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { type, currentPath } = this.props;
|
const { type, currentPath, fileLabel, directoryLabel } = this.props;
|
||||||
|
|
||||||
|
const label =
|
||||||
|
type === 'file' ? fileLabel || __('Choose File') : directoryLabel || __('Choose Directory');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FormRow verticallyCentered padded>
|
<FormRow verticallyCentered padded>
|
||||||
<Button
|
<Button button="primary" onClick={() => this.handleButtonClick()} label={label} />
|
||||||
button="primary"
|
|
||||||
onClick={() => this.handleButtonClick()}
|
|
||||||
label={type === 'file' ? __('Choose File') : __('Choose Directory')}
|
|
||||||
/>
|
|
||||||
<input
|
<input
|
||||||
webkitdirectory="true"
|
webkitdirectory="true"
|
||||||
className="input-copyable"
|
className="input-copyable"
|
||||||
|
|
|
@ -7,6 +7,7 @@ import ChannelSection from 'component/selectChannel';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import type { PublishParams, UpdatePublishFormData } from 'redux/reducers/publish';
|
import type { PublishParams, UpdatePublishFormData } from 'redux/reducers/publish';
|
||||||
import FileSelector from 'component/common/file-selector';
|
import FileSelector from 'component/common/file-selector';
|
||||||
|
import SelectThumbnail from 'component/selectThumbnail';
|
||||||
import { COPYRIGHT, OTHER } from 'constants/licenses';
|
import { COPYRIGHT, OTHER } from 'constants/licenses';
|
||||||
import { CHANNEL_NEW, CHANNEL_ANONYMOUS, MINIMUM_PUBLISH_BID } from 'constants/claim';
|
import { CHANNEL_NEW, CHANNEL_ANONYMOUS, MINIMUM_PUBLISH_BID } from 'constants/claim';
|
||||||
import * as icons from 'constants/icons';
|
import * as icons from 'constants/icons';
|
||||||
|
@ -21,6 +22,8 @@ type Props = {
|
||||||
editingURI: ?string,
|
editingURI: ?string,
|
||||||
title: ?string,
|
title: ?string,
|
||||||
thumbnail: ?string,
|
thumbnail: ?string,
|
||||||
|
uploadThumbnailStatus: ?string,
|
||||||
|
thumbnailPath: ?string,
|
||||||
description: ?string,
|
description: ?string,
|
||||||
language: string,
|
language: string,
|
||||||
nsfw: boolean,
|
nsfw: boolean,
|
||||||
|
@ -49,7 +52,8 @@ type Props = {
|
||||||
clearPublish: () => void,
|
clearPublish: () => void,
|
||||||
resolveUri: string => void,
|
resolveUri: string => void,
|
||||||
scrollToTop: () => void,
|
scrollToTop: () => void,
|
||||||
prepareEdit: ({}, string) => void,
|
prepareEdit: ({}) => void,
|
||||||
|
resetThumbnailStatus: () => void,
|
||||||
};
|
};
|
||||||
|
|
||||||
class PublishForm extends React.PureComponent<Props> {
|
class PublishForm extends React.PureComponent<Props> {
|
||||||
|
@ -67,7 +71,10 @@ class PublishForm extends React.PureComponent<Props> {
|
||||||
(this: any).getNewUri = this.getNewUri.bind(this);
|
(this: any).getNewUri = this.getNewUri.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a new uri to be used in the form and begins to resolve that uri for bid help text
|
componentWillMount() {
|
||||||
|
this.props.resetThumbnailStatus();
|
||||||
|
}
|
||||||
|
|
||||||
getNewUri(name: string, channel: string) {
|
getNewUri(name: string, channel: string) {
|
||||||
const { resolveUri } = this.props;
|
const { resolveUri } = this.props;
|
||||||
// If they are midway through a channel creation, treat it as anonymous until it completes
|
// If they are midway through a channel creation, treat it as anonymous until it completes
|
||||||
|
@ -267,6 +274,7 @@ class PublishForm extends React.PureComponent<Props> {
|
||||||
editingURI,
|
editingURI,
|
||||||
title,
|
title,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
|
uploadThumbnailStatus,
|
||||||
description,
|
description,
|
||||||
language,
|
language,
|
||||||
nsfw,
|
nsfw,
|
||||||
|
@ -289,6 +297,8 @@ class PublishForm extends React.PureComponent<Props> {
|
||||||
bidError,
|
bidError,
|
||||||
publishing,
|
publishing,
|
||||||
clearPublish,
|
clearPublish,
|
||||||
|
thumbnailPath,
|
||||||
|
resetThumbnailStatus,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const formDisabled = (!filePath && !editingURI) || publishing;
|
const formDisabled = (!filePath && !editingURI) || publishing;
|
||||||
|
@ -349,18 +359,6 @@ class PublishForm extends React.PureComponent<Props> {
|
||||||
onChange={e => updatePublishForm({ title: e.target.value })}
|
onChange={e => updatePublishForm({ title: e.target.value })}
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<FormRow padded>
|
|
||||||
<FormField
|
|
||||||
stretch
|
|
||||||
type="text"
|
|
||||||
name="content_thumbnail"
|
|
||||||
label={__('Thumbnail')}
|
|
||||||
placeholder="http://spee.ch/mylogo"
|
|
||||||
value={thumbnail}
|
|
||||||
disabled={formDisabled}
|
|
||||||
onChange={e => updatePublishForm({ thumbnail: e.target.value })}
|
|
||||||
/>
|
|
||||||
</FormRow>
|
|
||||||
<FormRow padded>
|
<FormRow padded>
|
||||||
<FormField
|
<FormField
|
||||||
stretch
|
stretch
|
||||||
|
@ -375,6 +373,24 @@ class PublishForm extends React.PureComponent<Props> {
|
||||||
</FormRow>
|
</FormRow>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<section className="card card--section">
|
||||||
|
<div className="card__title">{__('Thumbnail')}</div>
|
||||||
|
<div className="card__subtitle">
|
||||||
|
{__(
|
||||||
|
'Upload your thumbnail to spee.ch, or enter the url manually. Learn more about spee.ch '
|
||||||
|
)}
|
||||||
|
<Button button="link" label={__('here')} href="https://spee.ch/about" />.
|
||||||
|
</div>
|
||||||
|
<SelectThumbnail
|
||||||
|
thumbnailPath={thumbnailPath}
|
||||||
|
thumbnail={thumbnail}
|
||||||
|
uploadThumbnailStatus={uploadThumbnailStatus}
|
||||||
|
updatePublishForm={updatePublishForm}
|
||||||
|
formDisabled={formDisabled}
|
||||||
|
resetThumbnailStatus={resetThumbnailStatus}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
<section className="card card--section">
|
<section className="card card--section">
|
||||||
<div className="card__title">{__('Price')}</div>
|
<div className="card__title">{__('Price')}</div>
|
||||||
<div className="card__subtitle">{__('How much will this content cost?')}</div>
|
<div className="card__subtitle">{__('How much will this content cost?')}</div>
|
||||||
|
|
12
src/renderer/component/selectThumbnail/index.js
Normal file
12
src/renderer/component/selectThumbnail/index.js
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { doNotify } from 'lbry-redux';
|
||||||
|
import SelectThumbnail from './view';
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
openModal: (modal, props) => dispatch(doNotify(modal, props)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
null,
|
||||||
|
perform
|
||||||
|
)(SelectThumbnail);
|
88
src/renderer/component/selectThumbnail/view.jsx
Normal file
88
src/renderer/component/selectThumbnail/view.jsx
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
// @flow
|
||||||
|
import { STATUSES, MODALS } from 'lbry-redux';
|
||||||
|
import React from 'react';
|
||||||
|
import { FormField, FormRow } from 'component/common/form';
|
||||||
|
import FileSelector from 'component/common/file-selector';
|
||||||
|
import Button from 'component/button';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
thumbnail: ?string,
|
||||||
|
formDisabled: boolean,
|
||||||
|
uploadThumbnailStatus: string,
|
||||||
|
thumbnailPath: ?string,
|
||||||
|
openModal: ({ id: string }, {}) => void,
|
||||||
|
updatePublishForm: ({}) => void,
|
||||||
|
resetThumbnailStatus: () => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
class SelectThumbnail extends React.PureComponent<Props> {
|
||||||
|
render() {
|
||||||
|
const {
|
||||||
|
thumbnail,
|
||||||
|
formDisabled,
|
||||||
|
uploadThumbnailStatus: status,
|
||||||
|
openModal,
|
||||||
|
updatePublishForm,
|
||||||
|
thumbnailPath,
|
||||||
|
resetThumbnailStatus,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{status === STATUSES.API_DOWN || status === STATUSES.MANUAL ? (
|
||||||
|
<FormRow padded>
|
||||||
|
<FormField
|
||||||
|
stretch
|
||||||
|
type="text"
|
||||||
|
name="content_thumbnail"
|
||||||
|
label={__('Url')}
|
||||||
|
placeholder="http://spee.ch/mylogo"
|
||||||
|
value={thumbnail}
|
||||||
|
disabled={formDisabled}
|
||||||
|
onChange={e => updatePublishForm({ thumbnail: e.target.value })}
|
||||||
|
/>
|
||||||
|
</FormRow>
|
||||||
|
) : (
|
||||||
|
<div className="form-row--padded">
|
||||||
|
{(status === STATUSES.READY || status === STATUSES.COMPLETE) && (
|
||||||
|
<FileSelector
|
||||||
|
currentPath={thumbnailPath}
|
||||||
|
fileLabel={__('Choose Thumbnail')}
|
||||||
|
onFileChosen={path => openModal({ id: MODALS.CONFIRM_THUMBNAIL_UPLOAD }, { path })}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{status === STATUSES.COMPLETE && (
|
||||||
|
<div>
|
||||||
|
<p>
|
||||||
|
Upload complete. View it{' '}
|
||||||
|
<Button button="link" href={thumbnail} label={__('here')} />.
|
||||||
|
</p>
|
||||||
|
<Button button="link" label={__('New thumbnail')} onClick={resetThumbnailStatus} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="card__actions">
|
||||||
|
{status === STATUSES.READY && (
|
||||||
|
<Button
|
||||||
|
button="link"
|
||||||
|
label={__('Or enter a URL manually')}
|
||||||
|
onClick={() => updatePublishForm({ uploadThumbnailStatus: STATUSES.MANUAL })}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{status === STATUSES.MANUAL && (
|
||||||
|
<Button
|
||||||
|
button="link"
|
||||||
|
label={__('Use thumbnail upload tool')}
|
||||||
|
onClick={() => updatePublishForm({ uploadThumbnailStatus: STATUSES.READY })}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{status === STATUSES.IN_PROGRESS && <p>{__('Uploading thumbnail')}...</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SelectThumbnail;
|
|
@ -4,18 +4,22 @@ import * as icons from 'constants/icons';
|
||||||
import Button from 'component/button';
|
import Button from 'component/button';
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
uri: ?string,
|
claimId: ?string,
|
||||||
|
claimName: ?string,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default (props: Props) => {
|
export default (props: Props) => {
|
||||||
const { uri } = props;
|
const { claimId, claimName } = props;
|
||||||
|
const speechURL = claimName.startsWith('@')
|
||||||
|
? `${claimName}:${claimId}`
|
||||||
|
: `${claimId}/${claimName}`;
|
||||||
|
|
||||||
return uri ? (
|
return claimId && claimName ? (
|
||||||
<Button
|
<Button
|
||||||
iconRight={icons.GLOBE}
|
icon={icons.GLOBE}
|
||||||
button="alt"
|
button="alt"
|
||||||
label={__('View on Web')}
|
label={__('View on Web')}
|
||||||
href={`http://spee.ch/${uri}`}
|
href={`http://spee.ch/${speechURL}`}
|
||||||
/>
|
/>
|
||||||
) : null;
|
) : null;
|
||||||
};
|
};
|
||||||
|
|
5
src/renderer/constants/thumbnail_upload_statuses.js
Normal file
5
src/renderer/constants/thumbnail_upload_statuses.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export const API_DOWN = 'apiDown';
|
||||||
|
export const READY = 'ready';
|
||||||
|
export const IN_PROGRESS = 'inProgress';
|
||||||
|
export const COMPLETE = 'complete';
|
||||||
|
export const MANUAL = 'manual';
|
21
src/renderer/modal/modalConfirmThumbnailUpload/index.js
Normal file
21
src/renderer/modal/modalConfirmThumbnailUpload/index.js
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { doHideNotification } from 'lbry-redux';
|
||||||
|
import { doUploadThumbnail, doUpdatePublishForm } from 'redux/actions/publish';
|
||||||
|
import { selectPublishFormValues } from 'redux/selectors/publish';
|
||||||
|
import ModalConfirmThumbnailUpload from './view';
|
||||||
|
|
||||||
|
const select = state => {
|
||||||
|
const publishState = selectPublishFormValues(state);
|
||||||
|
return { nsfw: publishState.nsfw };
|
||||||
|
};
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
closeModal: () => dispatch(doHideNotification()),
|
||||||
|
upload: (path, nsfw = false) => dispatch(doUploadThumbnail(path, nsfw)),
|
||||||
|
updatePublishForm: value => dispatch(doUpdatePublishForm(value)),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(
|
||||||
|
select,
|
||||||
|
perform
|
||||||
|
)(ModalConfirmThumbnailUpload);
|
48
src/renderer/modal/modalConfirmThumbnailUpload/view.jsx
Normal file
48
src/renderer/modal/modalConfirmThumbnailUpload/view.jsx
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import { Modal } from 'modal/modal';
|
||||||
|
import { FormField } from 'component/common/form';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
upload: (string, boolean) => void,
|
||||||
|
path: string,
|
||||||
|
nsfw: boolean,
|
||||||
|
closeModal: () => void,
|
||||||
|
updatePublishForm: ({}) => void,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModalConfirmThumbnailUpload extends React.PureComponent<Props> {
|
||||||
|
upload() {
|
||||||
|
const { upload, updatePublishForm, closeModal, path, nsfw } = this.props;
|
||||||
|
upload(path, nsfw);
|
||||||
|
updatePublishForm({ thumbnailPath: path });
|
||||||
|
closeModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { closeModal, path, updatePublishForm, nsfw } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen
|
||||||
|
contentLabel={__('Confirm Thumbnail Upload')}
|
||||||
|
type="confirm"
|
||||||
|
confirmButtonLabel={__('Upload')}
|
||||||
|
onConfirmed={() => this.upload()}
|
||||||
|
onAborted={closeModal}
|
||||||
|
>
|
||||||
|
<p>{__('Are you sure you want to upload this thumbnail to spee.ch')}?</p>
|
||||||
|
<blockquote>{path}</blockquote>
|
||||||
|
<FormField
|
||||||
|
type="checkbox"
|
||||||
|
name="content_is_mature"
|
||||||
|
postfix={__('Mature audiences only')}
|
||||||
|
checked={nsfw}
|
||||||
|
onChange={event => updatePublishForm({ nsfw: event.target.checked })}
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModalConfirmThumbnailUpload;
|
|
@ -22,8 +22,13 @@ import ModalConfirmTransaction from 'modal/modalConfirmTransaction';
|
||||||
import ModalSendTip from '../modalSendTip';
|
import ModalSendTip from '../modalSendTip';
|
||||||
import ModalPublish from '../modalPublish';
|
import ModalPublish from '../modalPublish';
|
||||||
import ModalOpenExternalLink from '../modalOpenExternalLink';
|
import ModalOpenExternalLink from '../modalOpenExternalLink';
|
||||||
|
import ModalConfirmThumbnailUpload from 'modal/modalConfirmThumbnailUpload';
|
||||||
|
|
||||||
class ModalRouter extends React.PureComponent {
|
type Props = {
|
||||||
|
modal: string,
|
||||||
|
};
|
||||||
|
|
||||||
|
class ModalRouter extends React.PureComponent<Props> {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
|
@ -56,7 +61,7 @@ class ModalRouter extends React.PureComponent {
|
||||||
|
|
||||||
if (
|
if (
|
||||||
transitionModal &&
|
transitionModal &&
|
||||||
(transitionModal != this.state.lastTransitionModal || page != this.state.lastTransitionPage)
|
(transitionModal !== this.state.lastTransitionModal || page !== this.state.lastTransitionPage)
|
||||||
) {
|
) {
|
||||||
openModal({ id: transitionModal });
|
openModal({ id: transitionModal });
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -158,6 +163,8 @@ class ModalRouter extends React.PureComponent {
|
||||||
return <ModalOpenExternalLink {...notificationProps} />;
|
return <ModalOpenExternalLink {...notificationProps} />;
|
||||||
case MODALS.CONFIRM_TRANSACTION:
|
case MODALS.CONFIRM_TRANSACTION:
|
||||||
return <ModalConfirmTransaction {...notificationProps} />;
|
return <ModalConfirmTransaction {...notificationProps} />;
|
||||||
|
case MODALS.CONFIRM_THUMBNAIL_UPLOAD:
|
||||||
|
return <ModalConfirmThumbnailUpload {...notificationProps} />;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ class ChannelPage extends React.PureComponent<Props> {
|
||||||
<h1>{name}</h1>
|
<h1>{name}</h1>
|
||||||
<div className="card__actions card__actions--no-margin">
|
<div className="card__actions card__actions--no-margin">
|
||||||
<SubscribeButton uri={permanentUrl} channelName={name} />
|
<SubscribeButton uri={permanentUrl} channelName={name} />
|
||||||
<ViewOnWebButton uri={`${name}:${claimId}`} />
|
<ViewOnWebButton claimId={claimId} claimName={name} />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section>{contentList}</section>
|
<section>{contentList}</section>
|
||||||
|
|
|
@ -183,7 +183,7 @@ class FilePage extends React.Component<Props> {
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<Button
|
<Button
|
||||||
button="alt"
|
button="alt"
|
||||||
iconRight="Send"
|
icon="Send"
|
||||||
label={__('Enjoy this? Send a tip')}
|
label={__('Enjoy this? Send a tip')}
|
||||||
onClick={() => openModal({ id: MODALS.SEND_TIP }, { uri })}
|
onClick={() => openModal({ id: MODALS.SEND_TIP }, { uri })}
|
||||||
/>
|
/>
|
||||||
|
@ -191,12 +191,7 @@ class FilePage extends React.Component<Props> {
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)}
|
)}
|
||||||
{speechSharable && (
|
{speechSharable && (
|
||||||
<ViewOnWebButton
|
<ViewOnWebButton claimId={claim.claim_id} claimName={claim.name} />
|
||||||
uri={buildURI({
|
|
||||||
claimId: claim.claim_id,
|
|
||||||
contentName: claim.name,
|
|
||||||
}).slice(7)}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -109,13 +109,15 @@ class HelpPage extends React.PureComponent {
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="card card--section">
|
<section className="card card--section">
|
||||||
<div className="card__title">{__('Report a Bug')}</div>
|
<div className="card__title">{__('Report a Bug or Suggest a New Feature')}</div>
|
||||||
<p className="card__subtitle">{__('Did you find something wrong?')}</p>
|
<p className="card__subtitle">
|
||||||
|
{__('Did you find something wrong? Think LBRY could add something useful and cool?')}
|
||||||
|
</p>
|
||||||
|
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
<Button
|
<Button
|
||||||
navigate="/report"
|
navigate="/report"
|
||||||
label={__('Submit a Bug Report')}
|
label={__('Submit a Bug Report/Feature Request')}
|
||||||
icon={icons.REPORT}
|
icon={icons.REPORT}
|
||||||
button="primary"
|
button="primary"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
import { doNavigate } from 'redux/actions/navigation';
|
import { doNavigate } from 'redux/actions/navigation';
|
||||||
import { selectPublishFormValues } from 'redux/selectors/publish';
|
import { selectPublishFormValues } from 'redux/selectors/publish';
|
||||||
import {
|
import {
|
||||||
|
doResetThumbnailStatus,
|
||||||
doClearPublish,
|
doClearPublish,
|
||||||
doUpdatePublishForm,
|
doUpdatePublishForm,
|
||||||
doPublish,
|
doPublish,
|
||||||
|
@ -55,7 +56,8 @@ const perform = dispatch => ({
|
||||||
resolveUri: uri => dispatch(doResolveUri(uri)),
|
resolveUri: uri => dispatch(doResolveUri(uri)),
|
||||||
publish: params => dispatch(doPublish(params)),
|
publish: params => dispatch(doPublish(params)),
|
||||||
navigate: path => dispatch(doNavigate(path)),
|
navigate: path => dispatch(doNavigate(path)),
|
||||||
prepareEdit: (claim, uri) => dispatch(doPrepareEdit(claim, uri)),
|
prepareEdit: claim => dispatch(doPrepareEdit(claim)),
|
||||||
|
resetThumbnailStatus: () => dispatch(doResetThumbnailStatus()),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(PublishPage);
|
export default connect(select, perform)(PublishPage);
|
||||||
|
|
|
@ -49,10 +49,10 @@ class ReportPage extends React.Component {
|
||||||
<Page>
|
<Page>
|
||||||
<section className="card card--section">
|
<section className="card card--section">
|
||||||
<div className="card__content">
|
<div className="card__content">
|
||||||
<div className="card__title">{__('Report an Issue')}</div>
|
<div className="card__title">{__('Report an Issue/Request a Feature')}</div>
|
||||||
<p>
|
<p>
|
||||||
{__(
|
{__(
|
||||||
'Please describe the problem you experienced and any information you think might be useful to us. Links to screenshots are great!'
|
'Please describe the problem you experienced or the feature you want to see and any information you think might be useful to us. Links to screenshots are great!'
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
<FormRow>
|
<FormRow>
|
||||||
|
@ -65,7 +65,7 @@ class ReportPage extends React.Component {
|
||||||
onChange={event => {
|
onChange={event => {
|
||||||
this.onMessageChange(event);
|
this.onMessageChange(event);
|
||||||
}}
|
}}
|
||||||
placeholder={__('Description of your issue')}
|
placeholder={__('Description of your issue or feature request')}
|
||||||
/>
|
/>
|
||||||
</FormRow>
|
</FormRow>
|
||||||
<div className="card__actions">
|
<div className="card__actions">
|
||||||
|
|
|
@ -6,6 +6,8 @@ import {
|
||||||
doNotify,
|
doNotify,
|
||||||
MODALS,
|
MODALS,
|
||||||
selectMyChannelClaims,
|
selectMyChannelClaims,
|
||||||
|
STATUSES,
|
||||||
|
batchActions,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { selectPendingPublishes } from 'redux/selectors/publish';
|
import { selectPendingPublishes } from 'redux/selectors/publish';
|
||||||
import type {
|
import type {
|
||||||
|
@ -13,6 +15,8 @@ import type {
|
||||||
UpdatePublishFormAction,
|
UpdatePublishFormAction,
|
||||||
PublishParams,
|
PublishParams,
|
||||||
} from 'redux/reducers/publish';
|
} from 'redux/reducers/publish';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
type Action = UpdatePublishFormAction | { type: ACTIONS.CLEAR_PUBLISH };
|
type Action = UpdatePublishFormAction | { type: ACTIONS.CLEAR_PUBLISH };
|
||||||
type PromiseAction = Promise<Action>;
|
type PromiseAction = Promise<Action>;
|
||||||
|
@ -30,6 +34,90 @@ export const doUpdatePublishForm = (publishFormValue: UpdatePublishFormData) =>
|
||||||
data: { ...publishFormValue },
|
data: { ...publishFormValue },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const doResetThumbnailStatus = () => (dispatch: Dispatch): PromiseAction => {
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.UPDATE_PUBLISH_FORM,
|
||||||
|
data: {
|
||||||
|
thumbnailPath: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return fetch('https://spee.ch/api/channel/availability/@testing')
|
||||||
|
.then(() =>
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.UPDATE_PUBLISH_FORM,
|
||||||
|
data: {
|
||||||
|
uploadThumbnailStatus: STATUSES.READY,
|
||||||
|
thumbnail: '',
|
||||||
|
nsfw: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.catch(() =>
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.UPDATE_PUBLISH_FORM,
|
||||||
|
data: {
|
||||||
|
uploadThumbnailStatus: STATUSES.API_DOWN,
|
||||||
|
thumbnail: '',
|
||||||
|
nsfw: false,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const doUploadThumbnail = (filePath: string, nsfw: boolean) => (dispatch: Dispatch) => {
|
||||||
|
const thumbnail = fs.readFileSync(filePath);
|
||||||
|
const fileExt = path.extname(filePath);
|
||||||
|
|
||||||
|
const makeid = () => {
|
||||||
|
let text = '';
|
||||||
|
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
for (let i = 0; i < 24; i += 1) text += possible.charAt(Math.floor(Math.random() * 62));
|
||||||
|
return text;
|
||||||
|
};
|
||||||
|
|
||||||
|
const uploadError = (error = '') =>
|
||||||
|
dispatch(
|
||||||
|
batchActions(
|
||||||
|
{
|
||||||
|
type: ACTIONS.UPDATE_PUBLISH_FORM,
|
||||||
|
data: { uploadThumbnailStatus: STATUSES.API_DOWN },
|
||||||
|
},
|
||||||
|
dispatch(doNotify({ id: MODALS.ERROR, error }))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: ACTIONS.UPDATE_PUBLISH_FORM,
|
||||||
|
data: { uploadThumbnailStatus: STATUSES.IN_PROGRESS },
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = new FormData();
|
||||||
|
const name = makeid();
|
||||||
|
const blob = new Blob([thumbnail], { type: `image/${fileExt.slice(1)}` });
|
||||||
|
data.append('name', name);
|
||||||
|
data.append('file', blob);
|
||||||
|
data.append('nsfw', nsfw.toString());
|
||||||
|
return fetch('https://spee.ch/api/claim/publish', {
|
||||||
|
method: 'POST',
|
||||||
|
body: data,
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(
|
||||||
|
json =>
|
||||||
|
json.success
|
||||||
|
? dispatch({
|
||||||
|
type: ACTIONS.UPDATE_PUBLISH_FORM,
|
||||||
|
data: {
|
||||||
|
uploadThumbnailStatus: STATUSES.COMPLETE,
|
||||||
|
thumbnail: `${json.data.url}${fileExt}`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
: uploadError('Upload failed')
|
||||||
|
)
|
||||||
|
.catch(err => uploadError(err.message));
|
||||||
|
};
|
||||||
|
|
||||||
export const doPrepareEdit = (claim: any, uri: string) => (dispatch: Dispatch) => {
|
export const doPrepareEdit = (claim: any, uri: string) => (dispatch: Dispatch) => {
|
||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
|
@ -39,6 +127,7 @@ export const doPrepareEdit = (claim: any, uri: string) => (dispatch: Dispatch) =
|
||||||
stream: { metadata },
|
stream: { metadata },
|
||||||
},
|
},
|
||||||
} = claim;
|
} = claim;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
author,
|
author,
|
||||||
description,
|
description,
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
import { handleActions } from 'util/redux-utils';
|
import { handleActions } from 'util/redux-utils';
|
||||||
import { buildURI } from 'lbry-redux';
|
import { buildURI } from 'lbry-redux';
|
||||||
import * as ACTIONS from 'constants/action_types';
|
import * as ACTIONS from 'constants/action_types';
|
||||||
|
import * as STATUSES from 'constants/thumbnail_upload_statuses';
|
||||||
import { CHANNEL_ANONYMOUS } from 'constants/claim';
|
import { CHANNEL_ANONYMOUS } from 'constants/claim';
|
||||||
|
|
||||||
type PublishState = {
|
type PublishState = {
|
||||||
|
@ -14,6 +15,8 @@ type PublishState = {
|
||||||
},
|
},
|
||||||
title: string,
|
title: string,
|
||||||
thumbnail: string,
|
thumbnail: string,
|
||||||
|
thumbnailPath: string,
|
||||||
|
uploadThumbnailStatus: string,
|
||||||
description: string,
|
description: string,
|
||||||
language: string,
|
language: string,
|
||||||
tosAccepted: boolean,
|
tosAccepted: boolean,
|
||||||
|
@ -38,6 +41,8 @@ export type UpdatePublishFormData = {
|
||||||
},
|
},
|
||||||
title?: string,
|
title?: string,
|
||||||
thumbnail?: string,
|
thumbnail?: string,
|
||||||
|
uploadThumbnailStatus?: string,
|
||||||
|
thumbnailPath?: string,
|
||||||
description?: string,
|
description?: string,
|
||||||
language?: string,
|
language?: string,
|
||||||
tosAccepted?: boolean,
|
tosAccepted?: boolean,
|
||||||
|
@ -96,6 +101,8 @@ const defaultState: PublishState = {
|
||||||
},
|
},
|
||||||
title: '',
|
title: '',
|
||||||
thumbnail: '',
|
thumbnail: '',
|
||||||
|
thumbnailPath: '',
|
||||||
|
uploadThumbnailStatus: STATUSES.API_DOWN,
|
||||||
description: '',
|
description: '',
|
||||||
language: 'en',
|
language: 'en',
|
||||||
nsfw: false,
|
nsfw: false,
|
||||||
|
|
|
@ -5647,9 +5647,9 @@ lazy-val@^1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.3.tgz#bb97b200ef00801d94c317e29dc6ed39e31c5edc"
|
resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.3.tgz#bb97b200ef00801d94c317e29dc6ed39e31c5edc"
|
||||||
|
|
||||||
lbry-redux@lbryio/lbry-redux#543af2fcee7e4c45ccaf73af7b47d4b1a5d8ad44:
|
lbry-redux@lbryio/lbry-redux#7759bc6e8c482bed173d1f10aee6f6f9a439a15a:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/543af2fcee7e4c45ccaf73af7b47d4b1a5d8ad44"
|
resolved "https://codeload.github.com/lbryio/lbry-redux/tar.gz/7759bc6e8c482bed173d1f10aee6f6f9a439a15a"
|
||||||
dependencies:
|
dependencies:
|
||||||
proxy-polyfill "0.1.6"
|
proxy-polyfill "0.1.6"
|
||||||
reselect "^3.0.0"
|
reselect "^3.0.0"
|
||||||
|
|
Loading…
Add table
Reference in a new issue