From 2d80c35ead52e6efcef39195070b3ef984ff5865 Mon Sep 17 00:00:00 2001 From: Sean Yesmunt Date: Wed, 31 Jan 2018 12:24:22 -0800 Subject: [PATCH 1/2] file page --- .flowconfig | 1 + flow-typed/react-markdown.js | 3 + flow-typed/render-media.js | 3 + src/renderer/component/app/view.jsx | 2 +- src/renderer/component/common.js | 58 --------- src/renderer/component/common/form.jsx | 26 ++-- src/renderer/component/common/spinner.jsx | 22 ++-- src/renderer/component/common/thumbnail.jsx | 23 ++++ src/renderer/component/fileActions/view.jsx | 57 +++++---- src/renderer/component/fileDetails/view.jsx | 113 +++++++++--------- .../component/fileDownloadLink/view.jsx | 16 +-- src/renderer/component/filePrice/view.jsx | 2 +- src/renderer/component/link/view.jsx | 5 +- .../component/subscribeButton/view.jsx | 44 ++++--- .../video/internal/loading-screen.jsx | 29 +++-- .../component/video/internal/play-button.jsx | 51 ++++---- .../component/video/internal/player.jsx | 15 ++- src/renderer/component/video/view.jsx | 95 ++++++++------- src/renderer/component/walletSendTip/view.jsx | 2 +- src/renderer/component/wunderbar/view.jsx | 3 +- src/renderer/modal/{modal.js => modal.jsx} | 71 ++++++----- src/renderer/modal/modalRemoveFile/view.jsx | 75 ++++++++---- src/renderer/page/file/index.js | 7 +- src/renderer/page/file/view.jsx | 95 ++++++++------- src/renderer/redux/actions/video.js | 10 -- src/renderer/redux/reducers/content.js | 1 + src/renderer/redux/reducers/video.js | 25 ---- src/renderer/redux/selectors/navigation.js | 13 +- src/renderer/redux/selectors/search.js | 12 +- src/renderer/redux/selectors/video.js | 6 - src/renderer/scss/_gui.scss | 74 +++++++++--- src/renderer/scss/_reset.scss | 2 +- src/renderer/scss/_vars.scss | 12 +- src/renderer/scss/all.scss | 2 - src/renderer/scss/component/_button.scss | 37 +++++- src/renderer/scss/component/_card.scss | 87 +++++++++++++- src/renderer/scss/component/_form-field.scss | 10 ++ src/renderer/scss/component/_spinner.scss | 71 +++++------ src/renderer/scss/component/_tabs.scss | 64 ---------- src/renderer/scss/component/_video.scss | 93 +++++--------- src/renderer/scss/page/_show.scss | 13 -- src/renderer/store.js | 4 +- 42 files changed, 699 insertions(+), 655 deletions(-) create mode 100644 flow-typed/react-markdown.js create mode 100644 flow-typed/render-media.js create mode 100644 src/renderer/component/common/thumbnail.jsx rename src/renderer/modal/{modal.js => modal.jsx} (62%) delete mode 100644 src/renderer/redux/actions/video.js delete mode 100644 src/renderer/redux/reducers/video.js delete mode 100644 src/renderer/redux/selectors/video.js delete mode 100644 src/renderer/scss/component/_tabs.scss delete mode 100644 src/renderer/scss/page/_show.scss diff --git a/.flowconfig b/.flowconfig index 18c5bea0e..413e454bf 100644 --- a/.flowconfig +++ b/.flowconfig @@ -20,5 +20,6 @@ module.name_mapper='^page\(.*\)$' -> '/src/renderer/page\1' module.name_mapper='^lbry\(.*\)$' -> '/src/renderer/lbry\1' module.name_mapper='^rewards\(.*\)$' -> '/src/renderer/rewards\1' module.name_mapper='^modal\(.*\)$' -> '/src/renderer/modal\1' +module.name_mapper='^app\(.*\)$' -> '/src/renderer/app\1' [strict] diff --git a/flow-typed/react-markdown.js b/flow-typed/react-markdown.js new file mode 100644 index 000000000..63b7f2aad --- /dev/null +++ b/flow-typed/react-markdown.js @@ -0,0 +1,3 @@ +declare module 'react-markdown' { + declare module.exports: any; +} diff --git a/flow-typed/render-media.js b/flow-typed/render-media.js new file mode 100644 index 000000000..acb885cc8 --- /dev/null +++ b/flow-typed/render-media.js @@ -0,0 +1,3 @@ +declare module 'render-media' { + declare module.exports: any; +} diff --git a/src/renderer/component/app/view.jsx b/src/renderer/component/app/view.jsx index 9669a7795..3368df8d7 100644 --- a/src/renderer/component/app/view.jsx +++ b/src/renderer/component/app/view.jsx @@ -53,7 +53,7 @@ class App extends React.PureComponent { const { currentStackIndex: prevStackIndex } = prevProps; const { currentStackIndex, currentPageAttributes } = this.props; - if (this.mainContent && currentStackIndex !== prevStackIndex) { + if (this.mainContent && currentStackIndex !== prevStackIndex && currentPageAttributes) { this.mainContent.scrollTop = currentPageAttributes.scrollY || 0; } } diff --git a/src/renderer/component/common.js b/src/renderer/component/common.js index e9f3c4342..43d0c69a5 100644 --- a/src/renderer/component/common.js +++ b/src/renderer/component/common.js @@ -43,62 +43,4 @@ export class CurrencySymbol extends React.PureComponent { return LBC; } } - -export class Thumbnail extends React.PureComponent { - static propTypes = { - src: PropTypes.string, - }; - - handleError() { - if (this.state.imageUrl != this._defaultImageUri) { - this.setState({ - imageUri: this._defaultImageUri, - }); - } - } - - constructor(props) { - super(props); - - this._defaultImageUri = lbry.imagePath('default-thumb.svg'); - this._maxLoadTime = 10000; - this._isMounted = false; - - this.state = { - imageUri: this.props.src || this._defaultImageUri, - }; - } - - componentDidMount() { - this._isMounted = true; - setTimeout(() => { - if (this._isMounted && !this.refs.img.complete) { - this.setState({ - imageUri: this._defaultImageUri, - }); - } - }, this._maxLoadTime); - } - - componentWillUnmount() { - this._isMounted = false; - } - - render() { - const className = this.props.className ? this.props.className : '', - otherProps = Object.assign({}, this.props); - delete otherProps.className; - return ( - { - this.handleError(); - }} - {...otherProps} - className={className} - src={this.state.imageUri} - /> - ); - } -} /* eslint-enable */ diff --git a/src/renderer/component/common/form.jsx b/src/renderer/component/common/form.jsx index fe0cf8d3d..524e89ed2 100644 --- a/src/renderer/component/common/form.jsx +++ b/src/renderer/component/common/form.jsx @@ -2,15 +2,23 @@ /* eslint-disable react/no-multi-comp */ import * as React from 'react'; import Button from 'component/link'; +import classnames from 'classnames'; type FormRowProps = { children: React.Node, + padded?: boolean, }; -export const FormRow = (props: FormRowProps) => { - const { children } = props; - return
{children}
; -}; +export class FormRow extends React.PureComponent { + static defaultProps = { + padded: false, + }; + + render() { + const { children, padded } = this.props; + return
{children}
; + } +} type FormFieldProps = { render: () => React.Node, @@ -23,13 +31,12 @@ type FormFieldProps = { export class FormField extends React.PureComponent { render() { const { render, label, prefix, postfix, error } = this.props; + /* eslint-disable jsx-a11y/label-has-for */ + // Will come back to this on the settings page + // Need htmlFor on the label return (
- {label && ( - - )} + {label && }
{prefix && {prefix}} {render()} @@ -42,6 +49,7 @@ export class FormField extends React.PureComponent { )}
); + /* eslint-enable jsx-a11y/label-has-for */ } } diff --git a/src/renderer/component/common/spinner.jsx b/src/renderer/component/common/spinner.jsx index 14938f9b9..784d71d72 100644 --- a/src/renderer/component/common/spinner.jsx +++ b/src/renderer/component/common/spinner.jsx @@ -1,14 +1,14 @@ +// @flow import React from 'react'; -import classnames from 'classnames'; -export default ({ dark, className }) => ( -
+const Spinner = () => ( +
+
+
+
+
+
+
); + +export default Spinner; diff --git a/src/renderer/component/common/thumbnail.jsx b/src/renderer/component/common/thumbnail.jsx new file mode 100644 index 000000000..30fea5b77 --- /dev/null +++ b/src/renderer/component/common/thumbnail.jsx @@ -0,0 +1,23 @@ +// @flow +import React from 'react'; +import classnames from 'classnames'; + +type Props = { + src: string, + shouldObscure: boolean, + className: ?string, +}; + +const Thumbnail = (props: Props) => { + const { className, src, shouldObscure } = props; + + return ( + {__('Image + ); +}; + +export default Thumbnail; diff --git a/src/renderer/component/fileActions/view.jsx b/src/renderer/component/fileActions/view.jsx index b2d83f3b9..1d1dcbf71 100644 --- a/src/renderer/component/fileActions/view.jsx +++ b/src/renderer/component/fileActions/view.jsx @@ -1,48 +1,53 @@ +// @flow import React from 'react'; -import Link from 'component/link'; +import Button from 'component/link'; import FileDownloadLink from 'component/fileDownloadLink'; import * as modals from 'constants/modal_types'; +import classnames from 'classnames'; -class FileActions extends React.PureComponent { +type FileInfo = { + claim_id: string, +}; + +type Props = { + uri: string, + openModal: (string, any) => void, + claimIsMine: boolean, + fileInfo: FileInfo, + vertical?: boolean, // should the buttons be stacked vertically? +}; + +class FileActions extends React.PureComponent { render() { - const { fileInfo, uri, openModal, claimIsMine } = this.props; + const { fileInfo, uri, openModal, claimIsMine, vertical } = this.props; - const claimId = fileInfo ? fileInfo.claim_id : null, - showDelete = fileInfo && Object.keys(fileInfo).length > 0; + const claimId = fileInfo ? fileInfo.claim_id : ''; + // showDelete = fileInfo && Object.keys(fileInfo).length > 0; + const showDelete = true; return ( -
+
{showDelete && ( - openModal(modals.CONFIRM_FILE_REMOVE, { uri })} /> )} {!claimIsMine && ( - )} - + {claimIsMine && ( - void, + contentType: string, +}; -class FileDetails extends React.PureComponent { - render() { - const { claim, contentType, fileInfo, metadata, openFolder, uri } = this.props; - - if (!claim || !metadata) { - return ( -
- {__('Empty claim or metadata info.')} -
- ); - } - - const { description, language, license } = metadata; - const mediaType = lbry.getMediaType(contentType); - - const downloadPath = fileInfo ? path.normalize(fileInfo.download_path) : null; +const FileDetails = (props: Props) => { + const { claim, contentType, fileInfo, metadata, openFolder } = props; + if (!claim || !metadata) { return ( -
-
- -
-
+
+ {__('Empty claim or metadata info.')} +
+ ); + } + + const { description, language, license } = metadata; + const mediaType = lbry.getMediaType(contentType); + + const downloadPath = fileInfo ? path.normalize(fileInfo.download_path) : null; + + return ( +
+
+
About
+
-
- - - - - - - - - - - - - - - {downloadPath && ( - - - - - )} - -
{__('Content-Type')}{mediaType}
{__('Language')}{language}
{__('License')}{license}
{__('Downloaded to')} - openFolder(downloadPath)}>{downloadPath} -
+
Info
+
+
+
{__('Content-Type')}
+
{mediaType}
+
{__('Language')}
+
{language}
+
{__('License')}
+
{license}
+ {downloadPath && ( + +
{__('Downloaded to')}
+
+
+
+ )} +
- ); - } -} +
+ ); +}; export default FileDetails; diff --git a/src/renderer/component/fileDownloadLink/view.jsx b/src/renderer/component/fileDownloadLink/view.jsx index 71cf39018..d3da8c581 100644 --- a/src/renderer/component/fileDownloadLink/view.jsx +++ b/src/renderer/component/fileDownloadLink/view.jsx @@ -1,3 +1,5 @@ +// I'll come back to this +/* eslint-disable */ import React from 'react'; import { BusyMessage } from 'component/common'; import Icon from 'component/common/icon'; @@ -81,9 +83,8 @@ class FileDownloadLink extends React.PureComponent { } return ( { purchaseUri(uri); @@ -91,15 +92,7 @@ class FileDownloadLink extends React.PureComponent { /> ); } else if (fileInfo && fileInfo.download_path) { - return ( - openFile()} - /> - ); + return openFile()} />; } return null; @@ -107,3 +100,4 @@ class FileDownloadLink extends React.PureComponent { } export default FileDownloadLink; +/* eslint-enable */ diff --git a/src/renderer/component/filePrice/view.jsx b/src/renderer/component/filePrice/view.jsx index 7c4d6836d..090c93126 100644 --- a/src/renderer/component/filePrice/view.jsx +++ b/src/renderer/component/filePrice/view.jsx @@ -38,7 +38,7 @@ class FilePrice extends React.PureComponent { const isEstimate = costInfo ? !costInfo.includesData : false; if (!costInfo) { - return ???; + return PRICE; } return ( diff --git a/src/renderer/component/link/view.jsx b/src/renderer/component/link/view.jsx index f53fc8723..cd962b552 100644 --- a/src/renderer/component/link/view.jsx +++ b/src/renderer/component/link/view.jsx @@ -25,6 +25,7 @@ type Props = { noStyle: ?boolean, noUnderline: ?boolean, description: ?string, + secondary: ?boolean, }; const Button = (props: Props) => { @@ -49,6 +50,7 @@ const Button = (props: Props) => { description, noStyle, noUnderline, + secondary, ...otherProps } = props; @@ -58,7 +60,8 @@ const Button = (props: Props) => { ? 'btn--no-style' : { 'btn--link': fakeLink, - 'btn--primary': !alt && !fakeLink, + 'btn--primary': !alt && !fakeLink && !secondary, // default to primary + 'btn--secondary': secondary, 'btn--alt': alt, 'btn--inverse': inverse, 'btn--disabled': disabled, diff --git a/src/renderer/component/subscribeButton/view.jsx b/src/renderer/component/subscribeButton/view.jsx index 216b0bf72..e251b40d6 100644 --- a/src/renderer/component/subscribeButton/view.jsx +++ b/src/renderer/component/subscribeButton/view.jsx @@ -1,7 +1,23 @@ +// @flow import React from 'react'; -import Link from 'component/link'; +import Button from 'component/link'; +import type { Subscription } from 'redux/reducers/subscriptions'; -export default ({ channelName, uri, subscriptions, doChannelSubscribe, doChannelUnsubscribe }) => { +type SubscribtionArgs = { + channelName: string, + uri: string, +}; + +type Props = { + channelName: ?string, + uri: ?string, + subscriptions: Array, + doChannelSubscribe: ({ channelName: string, uri: string }) => void, + doChannelUnsubscribe: SubscribtionArgs => void, +}; + +export default (props: Props) => { + const { channelName, uri, subscriptions, doChannelSubscribe, doChannelUnsubscribe } = props; const isSubscribed = subscriptions.map(subscription => subscription.channelName).indexOf(channelName) !== -1; @@ -10,18 +26,16 @@ export default ({ channelName, uri, subscriptions, doChannelSubscribe, doChannel const subscriptionLabel = isSubscribed ? __('Unsubscribe') : __('Subscribe'); return channelName && uri ? ( -
- - subscriptionHandler({ - channelName, - uri, - }) - } - /> -
+
@@ -69,4 +95,3 @@ class WalletSendTip extends React.PureComponent { } export default WalletSendTip; -/* eslint-enable */ diff --git a/src/renderer/constants/modal_types.js b/src/renderer/constants/modal_types.js index 19b86238e..dd7f0167d 100644 --- a/src/renderer/constants/modal_types.js +++ b/src/renderer/constants/modal_types.js @@ -14,3 +14,4 @@ export const TRANSACTION_FAILED = 'transaction_failed'; export const REWARD_APPROVAL_REQUIRED = 'reward_approval_required'; export const AFFIRM_PURCHASE = 'affirm_purchase'; export const CONFIRM_CLAIM_REVOKE = 'confirmClaimRevoke'; +export const SEND_TIP = 'sendTip'; diff --git a/src/renderer/modal/modalRouter/view.jsx b/src/renderer/modal/modalRouter/view.jsx index b84272a6c..16db6fee5 100644 --- a/src/renderer/modal/modalRouter/view.jsx +++ b/src/renderer/modal/modalRouter/view.jsx @@ -14,6 +14,7 @@ import ModalAffirmPurchase from 'modal/modalAffirmPurchase'; import ModalRevokeClaim from 'modal/modalRevokeClaim'; import ModalEmailCollection from '../modalEmailCollection'; import ModalPhoneCollection from '../modalPhoneCollection'; +import ModalSendTip from '../modalSendTip'; import * as modals from 'constants/modal_types'; class ModalRouter extends React.PureComponent { @@ -129,6 +130,8 @@ class ModalRouter extends React.PureComponent { return ; case modals.EMAIL_COLLECTION: return ; + case modals.SEND_TIP: + return ; default: return null; } diff --git a/src/renderer/modal/modalSendTip/index.js b/src/renderer/modal/modalSendTip/index.js new file mode 100644 index 000000000..d2b21e961 --- /dev/null +++ b/src/renderer/modal/modalSendTip/index.js @@ -0,0 +1,9 @@ +import { connect } from 'react-redux'; +import { doCloseModal } from 'redux/actions/app'; +import ModalSendTip from './view'; + +const perform = dispatch => ({ + closeModal: () => dispatch(doCloseModal()), +}); + +export default connect(null, perform)(ModalSendTip); diff --git a/src/renderer/modal/modalSendTip/view.jsx b/src/renderer/modal/modalSendTip/view.jsx new file mode 100644 index 000000000..6593d148d --- /dev/null +++ b/src/renderer/modal/modalSendTip/view.jsx @@ -0,0 +1,23 @@ +// @flow +import React from 'react'; +import { Modal } from 'modal/modal'; +import SendTip from 'component/walletSendTip'; + +type Props = { + closeModal: () => void, + uri: string, +}; + +class ModalSendTip extends React.PureComponent { + render() { + const { closeModal, uri } = this.props; + + return ( + + + + ); + } +} + +export default ModalSendTip; diff --git a/src/renderer/page/file/index.js b/src/renderer/page/file/index.js index e1f7736ec..b157ed739 100644 --- a/src/renderer/page/file/index.js +++ b/src/renderer/page/file/index.js @@ -12,6 +12,7 @@ import { import { makeSelectCostInfoForUri } from 'redux/selectors/cost_info'; import { selectShowNsfw } from 'redux/selectors/settings'; import { selectMediaPaused } from 'redux/selectors/media'; +import { doOpenModal } from 'redux/actions/app'; import FilePage from './view'; const select = (state, props) => ({ @@ -30,6 +31,7 @@ const perform = dispatch => ({ navigate: (path, params) => dispatch(doNavigate(path, params)), fetchFileInfo: uri => dispatch(doFetchFileInfo(uri)), fetchCostInfo: uri => dispatch(doFetchCostInfoForUri(uri)), + openModal: (modal, props) => dispatch(doOpenModal(modal, props)), }); export default connect(select, perform)(FilePage); diff --git a/src/renderer/page/file/view.jsx b/src/renderer/page/file/view.jsx index 56113b31f..7d68eee3b 100644 --- a/src/renderer/page/file/view.jsx +++ b/src/renderer/page/file/view.jsx @@ -1,4 +1,4 @@ -/* eslint-disable */ +// @flow import React from 'react'; import lbry from 'lbry'; import { buildURI, normalizeURI } from 'lbryURI'; @@ -12,29 +12,58 @@ import Icon from 'component/common/icon'; import WalletSendTip from 'component/walletSendTip'; import DateTime from 'component/dateTime'; import * as icons from 'constants/icons'; -import Link from 'component/link'; +import Button from 'component/link'; import SubscribeButton from 'component/subscribeButton'; import Page from 'component/page'; import classnames from 'classnames'; import player from 'render-media'; +import * as modals from 'constants/modal_types'; -class FilePage extends React.PureComponent { +type Props = { + claim: { + claim_id: string, + height: number, + channel_name: string, + value: { + publisherSignature: ?{ + certificateId: ?string + } + } + }, + fileInfo: {}, + metadata: { + title: string, + thumbnail: string, + nsfw: boolean + }, + contentType: string, + uri: string, + rewardedContentClaimIds: Array, + obscureNsfw: boolean, + playingUri: ?string, + isPaused: boolean, + openModal: (string, any) => void, + fetchFileInfo: (string) => void, + fetchCostInfo: (string) => void, +} + +class FilePage extends React.Component { componentDidMount() { this.fetchFileInfo(this.props); this.fetchCostInfo(this.props); } - componentWillReceiveProps(nextProps) { + componentWillReceiveProps(nextProps: Props) { this.fetchFileInfo(nextProps); } - fetchFileInfo(props) { + fetchFileInfo(props: Props) { if (props.fileInfo === undefined) { props.fetchFileInfo(props.uri); } } - fetchCostInfo(props) { + fetchCostInfo(props: Props) { if (props.costInfo === undefined) { props.fetchCostInfo(props.uri); } @@ -51,6 +80,7 @@ class FilePage extends React.PureComponent { obscureNsfw, playingUri, isPaused, + openModal, } = this.props; // This should be included below in the page @@ -65,9 +95,9 @@ class FilePage extends React.PureComponent { const shouldObscureThumbnail = obscureNsfw && metadata.nsfw; const thumbnail = metadata.thumbnail; const { height, channel_name: channelName, value } = claim; + const mediaType = lbry.getMediaType(contentType); const isPlayable = Object.values(player.mime).indexOf(contentType) !== -1 || mediaType === 'audio'; - const mediaType = lbry.getMediaType(contentType); const channelClaimId = value && value.publisherSignature && value.publisherSignature.certificateId; let subscriptionUri; @@ -76,7 +106,6 @@ class FilePage extends React.PureComponent { } const isPlaying = playingUri === uri && !isPaused; - console.log('isPlaying?', isPlaying); return ( @@ -112,7 +141,12 @@ class FilePage extends React.PureComponent {
- +
@@ -128,4 +162,3 @@ class FilePage extends React.PureComponent { } export default FilePage; -/* eslint-enable */ diff --git a/src/renderer/scss/component/_card.scss b/src/renderer/scss/component/_card.scss index bdaed9e18..fbdc43dac 100644 --- a/src/renderer/scss/component/_card.scss +++ b/src/renderer/scss/component/_card.scss @@ -121,7 +121,7 @@ color: var(--color-grey-dark); font-size: calc(var(--font-size-subtext-multiple) * 1em); padding-top: $spacing-vertical * 1/3; - word-break: break-all; + word-break: break-word; font-weight: 300; }