From bf8518d04caf33419fbe90a65d4a380777792939 Mon Sep 17 00:00:00 2001 From: Travis Eden Date: Mon, 30 Apr 2018 11:27:20 -0400 Subject: [PATCH 1/4] Autoplay: start playing/downloading video/audio/image in video component. Adds on/off checkbox on settings and file pages. Adds CHECK_SIMPLE icon (w/o circle). add changelog entry --- CHANGELOG.md | 1 + src/renderer/component/video/index.js | 10 +++- src/renderer/component/video/view.jsx | 61 ++++++++++++++++++++--- src/renderer/constants/icons.js | 1 + src/renderer/constants/settings.js | 1 + src/renderer/page/file/index.js | 6 ++- src/renderer/page/file/view.jsx | 23 +++++++++ src/renderer/page/settings/index.js | 1 + src/renderer/page/settings/view.jsx | 14 ++++++ src/renderer/redux/reducers/settings.js | 1 + src/renderer/scss/component/_card.scss | 5 ++ src/renderer/scss/component/_content.scss | 4 ++ 12 files changed, 118 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a1858f13..d31dbbc93 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/). * 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)) * Add Danger JS to automate code reviews ([#1289](https://github.com/lbryio/lbry-app/pull/1289)) + * Autoplay downloaded and free media ([#584](https://github.com/lbryio/lbry-app/pull/1453)) ### Changed * Add flair to snackbar ([#1313](https://github.com/lbryio/lbry-app/pull/1313)) diff --git a/src/renderer/component/video/index.js b/src/renderer/component/video/index.js index 671f931be..a4063b057 100644 --- a/src/renderer/component/video/index.js +++ b/src/renderer/component/video/index.js @@ -1,7 +1,8 @@ import { connect } from 'react-redux'; +import * as settings from 'constants/settings'; import { doChangeVolume } from 'redux/actions/app'; import { selectVolume } from 'redux/selectors/app'; -import { doPlayUri, doSetPlayingUri } from 'redux/actions/content'; +import { doPlayUri, doSetPlayingUri, doLoadVideo } from 'redux/actions/content'; import { doPlay, doPause, savePosition } from 'redux/actions/media'; import { makeSelectMetadataForUri, @@ -12,7 +13,10 @@ import { makeSelectLoadingForUri, makeSelectDownloadingForUri, } from 'lbry-redux'; -import { selectShowNsfw } from 'redux/selectors/settings'; +import { + makeSelectClientSetting, + selectShowNsfw +} from 'redux/selectors/settings'; import { selectMediaPaused, makeSelectMediaPositionForUri } from 'redux/selectors/media'; import { selectPlayingUri } from 'redux/selectors/content'; import Video from './view'; @@ -30,10 +34,12 @@ const select = (state, props) => ({ volume: selectVolume(state), mediaPaused: selectMediaPaused(state), mediaPosition: makeSelectMediaPositionForUri(props.uri)(state), + autoplay: makeSelectClientSetting(settings.AUTOPLAY)(state) }); const perform = dispatch => ({ play: uri => dispatch(doPlayUri(uri)), + load: uri => dispatch(doLoadVideo(uri)), cancelPlay: () => dispatch(doSetPlayingUri(null)), changeVolume: volume => dispatch(doChangeVolume(volume)), doPlay: () => dispatch(doPlay()), diff --git a/src/renderer/component/video/view.jsx b/src/renderer/component/video/view.jsx index 1cfda4078..b664ee88f 100644 --- a/src/renderer/component/video/view.jsx +++ b/src/renderer/component/video/view.jsx @@ -34,7 +34,7 @@ type Props = { mediaPosition: ?number, className: ?string, obscureNsfw: boolean, - play: string => void, + play: string => void }; class Video extends React.PureComponent { @@ -42,6 +42,52 @@ class Video extends React.PureComponent { this.props.cancelPlay(); } + componentDidMount() { + this.handleAutoplay(this.props); + } + + componentWillReceiveProps(nextProps: Props) { + this.handleAutoplay(nextProps); + } + + handleAutoplay(props: Props) { + const { + autoplay, + obscureNsfw, + playingUri, + fileInfo, + costInfo, + isDownloading, + uri, + load, + play + } = props; + + const playable = ( + autoplay && + obscureNsfw === false && + playingUri !== uri + ); + + if ( + playable && + costInfo && + costInfo.cost === 0 && + !fileInfo && + !isDownloading + ) { + load(uri); + play(uri); + } + else if ( + playable && + fileInfo && + fileInfo.blobs_completed > 0 + ) { + play(uri); + } + } + isMediaSame(nextProps: Props) { return ( this.props.fileInfo && @@ -90,14 +136,18 @@ class Video extends React.PureComponent { } const poster = metadata && metadata.thumbnail; + const layoverClass = classnames('content__cover', { 'card__media--nsfw': shouldObscureNsfw }); + const layoverStyle = !shouldObscureNsfw && poster ? { backgroundImage: `url("${poster}")` } : {}; return (
{isPlaying && (
{!isReadyToPlay ? ( - - ) : ( +
+ +
+ ) : ( {
)} {!isPlaying && ( -
+
({ playingUri: selectPlayingUri(state), isPaused: selectMediaPaused(state), claimIsMine: makeSelectClaimIsMine(props.uri)(state), + autoplay: makeSelectClientSetting(settings.AUTOPLAY)(state) }); const perform = dispatch => ({ @@ -40,6 +43,7 @@ const perform = dispatch => ({ checkSubscription: subscription => dispatch(doCheckSubscription(subscription)), openModal: (modal, props) => dispatch(doNotify(modal, props)), prepareEdit: (publishData, uri) => dispatch(doPrepareEdit(publishData, uri)), + setClientSetting: (key, value) => dispatch(doSetClientSetting(key, value)), }); export default connect(select, perform)(FilePage); diff --git a/src/renderer/page/file/view.jsx b/src/renderer/page/file/view.jsx index 5cbbad39b..e09a60947 100644 --- a/src/renderer/page/file/view.jsx +++ b/src/renderer/page/file/view.jsx @@ -7,6 +7,7 @@ import FilePrice from 'component/filePrice'; import FileDetails from 'component/fileDetails'; import FileActions from 'component/fileActions'; import UriIndicator from 'component/uriIndicator'; +import { FormField } from 'component/common/form'; import Icon from 'component/common/icon'; import DateTime from 'component/dateTime'; import * as icons from 'constants/icons'; @@ -14,6 +15,7 @@ import Button from 'component/button'; import SubscribeButton from 'component/subscribeButton'; import Page from 'component/page'; import player from 'render-media'; +import * as settings from 'constants/settings'; type Props = { claim: { @@ -45,9 +47,16 @@ type Props = { fetchFileInfo: string => void, fetchCostInfo: string => void, prepareEdit: ({}) => void, + setClientSetting: (string, boolean | string | Price) => void, }; class FilePage extends React.Component { + constructor(props: Props) { + super(props); + + (this: any).onAutoplayChange = this.onAutoplayChange.bind(this); + } + componentDidMount() { const { uri, fileInfo, fetchFileInfo, costInfo, fetchCostInfo } = this.props; @@ -88,6 +97,10 @@ class FilePage extends React.Component { } }; + onAutoplayChange(event: SyntheticInputEvent<*>) { + this.props.setClientSetting(settings.AUTOPLAY, event.target.checked); + } + render() { const { claim, @@ -102,6 +115,8 @@ class FilePage extends React.Component { claimIsMine, prepareEdit, navigate, + autoplay, + setClientSetting } = this.props; // File info @@ -144,6 +159,14 @@ class FilePage extends React.Component {
{isRewardContent && } +
diff --git a/src/renderer/page/settings/index.js b/src/renderer/page/settings/index.js index e4f4ff930..d2f72da84 100644 --- a/src/renderer/page/settings/index.js +++ b/src/renderer/page/settings/index.js @@ -26,6 +26,7 @@ const select = state => ({ language: selectCurrentLanguage(state), languages: selectLanguages(state), automaticDarkModeEnabled: makeSelectClientSetting(settings.AUTOMATIC_DARK_MODE_ENABLED)(state), + autoplay: makeSelectClientSetting(settings.AUTOPLAY)(state) }); const perform = dispatch => ({ diff --git a/src/renderer/page/settings/view.jsx b/src/renderer/page/settings/view.jsx index a3997503a..f95061776 100644 --- a/src/renderer/page/settings/view.jsx +++ b/src/renderer/page/settings/view.jsx @@ -31,6 +31,7 @@ type Props = { currentTheme: string, themes: Array, automaticDarkModeEnabled: boolean, + autoplay: boolean }; type State = { @@ -53,6 +54,7 @@ class SettingsPage extends React.PureComponent { (this: any).onShareDataChange = this.onShareDataChange.bind(this); (this: any).onThemeChange = this.onThemeChange.bind(this); (this: any).onAutomaticDarkModeChange = this.onAutomaticDarkModeChange.bind(this); + (this: any).onAutoplayChange = this.onAutoplayChange.bind(this); (this: any).clearCache = this.clearCache.bind(this); // (this: any).onLanguageChange = this.onLanguageChange.bind(this) } @@ -95,6 +97,10 @@ class SettingsPage extends React.PureComponent { this.props.setClientSetting(settings.AUTOMATIC_DARK_MODE_ENABLED, value); } + onAutoplayChange(event: SyntheticInputEvent<*>) { + this.props.setClientSetting(settings.AUTOPLAY, event.target.checked); + } + onInstantPurchaseEnabledChange(enabled: boolean) { this.props.setClientSetting(settings.INSTANT_PURCHASE_ENABLED, enabled); } @@ -138,6 +144,7 @@ class SettingsPage extends React.PureComponent { currentTheme, themes, automaticDarkModeEnabled, + autoplay } = this.props; const noDaemonSettings = !daemonSettings || Object.keys(daemonSettings).length === 0; @@ -239,6 +246,13 @@ class SettingsPage extends React.PureComponent {
{__('Content Settings')}
+ Date: Fri, 11 May 2018 10:33:33 -0400 Subject: [PATCH 2/4] fix nsfw conditions --- src/renderer/component/video/view.jsx | 58 ++++++++++----------------- 1 file changed, 21 insertions(+), 37 deletions(-) diff --git a/src/renderer/component/video/view.jsx b/src/renderer/component/video/view.jsx index b664ee88f..c1663c5e3 100644 --- a/src/renderer/component/video/view.jsx +++ b/src/renderer/component/video/view.jsx @@ -19,6 +19,7 @@ type Props = { nsfw: boolean, thumbnail: string, }, + autoplay: boolean, isLoading: boolean, isDownloading: boolean, playingUri: ?string, @@ -34,56 +35,38 @@ type Props = { mediaPosition: ?number, className: ?string, obscureNsfw: boolean, - play: string => void + play: string => void, }; class Video extends React.PureComponent { - componentWillUnmount() { - this.props.cancelPlay(); - } - componentDidMount() { this.handleAutoplay(this.props); } componentWillReceiveProps(nextProps: Props) { - this.handleAutoplay(nextProps); + if ( + this.props.autoplay !== nextProps.autoplay || + this.props.fileInfo !== nextProps.fileInfo || + this.props.isDownloading !== nextProps.isDownloading || + this.props.playingUri !== nextProps.playingUri + ) { + this.handleAutoplay(nextProps); + } + } + + componentWillUnmount() { + this.props.cancelPlay(); } handleAutoplay(props: Props) { - const { - autoplay, - obscureNsfw, - playingUri, - fileInfo, - costInfo, - isDownloading, - uri, - load, - play - } = props; + const { autoplay, playingUri, fileInfo, costInfo, isDownloading, uri, load, play, metadata } = props; - const playable = ( - autoplay && - obscureNsfw === false && - playingUri !== uri - ); + const playable = autoplay && playingUri !== uri && metadata && !metadata.nsfw; - if ( - playable && - costInfo && - costInfo.cost === 0 && - !fileInfo && - !isDownloading - ) { + if (playable && costInfo && costInfo.cost === 0 && !fileInfo && !isDownloading) { load(uri); play(uri); - } - else if ( - playable && - fileInfo && - fileInfo.blobs_completed > 0 - ) { + } else if (playable && fileInfo && fileInfo.blobs_completed > 0) { play(uri); } } @@ -137,7 +120,8 @@ class Video extends React.PureComponent { const poster = metadata && metadata.thumbnail; const layoverClass = classnames('content__cover', { 'card__media--nsfw': shouldObscureNsfw }); - const layoverStyle = !shouldObscureNsfw && poster ? { backgroundImage: `url("${poster}")` } : {}; + const layoverStyle = + !shouldObscureNsfw && poster ? { backgroundImage: `url("${poster}")` } : {}; return (
@@ -147,7 +131,7 @@ class Video extends React.PureComponent {
- ) : ( + ) : ( Date: Fri, 11 May 2018 12:36:04 -0400 Subject: [PATCH 3/4] move file page autoplay setting below channel info; lint fixes --- src/renderer/page/file/view.jsx | 27 +++++++++++++------------- src/renderer/scss/component/_card.scss | 13 ++++++------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/renderer/page/file/view.jsx b/src/renderer/page/file/view.jsx index e09a60947..d496a90b9 100644 --- a/src/renderer/page/file/view.jsx +++ b/src/renderer/page/file/view.jsx @@ -41,6 +41,7 @@ type Props = { playingUri: ?string, isPaused: boolean, claimIsMine: boolean, + autoplay: boolean, costInfo: ?{}, navigate: (string, ?{}) => void, openModal: ({ id: string }, { uri: string }) => void, @@ -78,6 +79,10 @@ class FilePage extends React.Component { } } + onAutoplayChange(event: SyntheticInputEvent<*>) { + this.props.setClientSetting(settings.AUTOPLAY, event.target.checked); + } + checkSubscription = (props: Props) => { if ( props.subscriptions @@ -97,10 +102,6 @@ class FilePage extends React.Component { } }; - onAutoplayChange(event: SyntheticInputEvent<*>) { - this.props.setClientSetting(settings.AUTOPLAY, event.target.checked); - } - render() { const { claim, @@ -116,7 +117,6 @@ class FilePage extends React.Component { prepareEdit, navigate, autoplay, - setClientSetting } = this.props; // File info @@ -159,14 +159,6 @@ class FilePage extends React.Component {
{isRewardContent && } -
@@ -200,6 +192,15 @@ class FilePage extends React.Component { )}
+
+ +
diff --git a/src/renderer/scss/component/_card.scss b/src/renderer/scss/component/_card.scss index 305fd8f33..659d35737 100644 --- a/src/renderer/scss/component/_card.scss +++ b/src/renderer/scss/component/_card.scss @@ -86,15 +86,10 @@ display: flex; align-items: center; .credit-amount, - .autoplay, .icon { margin-top: $spacing-vertical * 1/3; margin-left: $spacing-vertical * 2/3; } - - .autoplay .icon { - margin: 0 0 0 5px; - } } .card__title-identity-icons { @@ -173,12 +168,16 @@ display: flex; justify-content: space-between; align-items: center; - padding: $spacing-width 0; + padding: $spacing-width 0 0; } .card__channel-info--large { padding-top: 0; - padding-bottom: $spacing-width; + padding-bottom: 0; +} + +.autoplay div div input { + margin: 0 0 $spacing-vertical auto; } .card__content { From f231b430e6d4b18117c8e72b43734dc9cdf4e280 Mon Sep 17 00:00:00 2001 From: Sean Yesmunt Date: Fri, 11 May 2018 13:47:05 -0400 Subject: [PATCH 4/4] cleanup css --- .../component/common/form-components/form-row.jsx | 4 +++- src/renderer/page/file/view.jsx | 11 +++++++---- src/renderer/scss/component/_card.scss | 6 +----- src/renderer/scss/component/_form-field.scss | 4 ++++ 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/renderer/component/common/form-components/form-row.jsx b/src/renderer/component/common/form-components/form-row.jsx index 1216ba816..b18bcf795 100644 --- a/src/renderer/component/common/form-components/form-row.jsx +++ b/src/renderer/component/common/form-components/form-row.jsx @@ -9,6 +9,7 @@ type Props = { padded?: boolean, verticallyCentered?: boolean, stretch?: boolean, + alignRight?: boolean, }; export class FormRow extends React.PureComponent { @@ -17,7 +18,7 @@ export class FormRow extends React.PureComponent { }; render() { - const { centered, children, padded, verticallyCentered, stretch } = this.props; + const { centered, children, padded, verticallyCentered, stretch, alignRight } = this.props; return (
{ 'form-row--padded': padded, 'form-row--vertically-centered': verticallyCentered, 'form-row--stretch': stretch, + 'form-row--right': alignRight, })} > {children} diff --git a/src/renderer/page/file/view.jsx b/src/renderer/page/file/view.jsx index d496a90b9..40cc010a2 100644 --- a/src/renderer/page/file/view.jsx +++ b/src/renderer/page/file/view.jsx @@ -7,7 +7,7 @@ import FilePrice from 'component/filePrice'; import FileDetails from 'component/fileDetails'; import FileActions from 'component/fileActions'; import UriIndicator from 'component/uriIndicator'; -import { FormField } from 'component/common/form'; +import { FormField, FormRow } from 'component/common/form'; import Icon from 'component/common/icon'; import DateTime from 'component/dateTime'; import * as icons from 'constants/icons'; @@ -48,7 +48,9 @@ type Props = { fetchFileInfo: string => void, fetchCostInfo: string => void, prepareEdit: ({}) => void, - setClientSetting: (string, boolean | string | Price) => void, + setClientSetting: (string, boolean | string) => void, + checkSubscription: ({ channelName: string, uri: string }) => void, + subscriptions: Array<{}>, }; class FilePage extends React.Component { @@ -85,6 +87,7 @@ class FilePage extends React.Component { checkSubscription = (props: Props) => { if ( + props.claim.value.publisherSignature && props.subscriptions .map(subscription => subscription.channelName) .indexOf(props.claim.channel_name) !== -1 @@ -192,7 +195,7 @@ class FilePage extends React.Component { )}
-
+ { checked={autoplay} postfix={__('Autoplay')} /> -
+
diff --git a/src/renderer/scss/component/_card.scss b/src/renderer/scss/component/_card.scss index 659d35737..e190cd177 100644 --- a/src/renderer/scss/component/_card.scss +++ b/src/renderer/scss/component/_card.scss @@ -173,11 +173,7 @@ .card__channel-info--large { padding-top: 0; - padding-bottom: 0; -} - -.autoplay div div input { - margin: 0 0 $spacing-vertical auto; + padding-bottom: $spacing-vertical * 2/3; } .card__content { diff --git a/src/renderer/scss/component/_form-field.scss b/src/renderer/scss/component/_form-field.scss index 4c9950539..2be99b8ab 100644 --- a/src/renderer/scss/component/_form-field.scss +++ b/src/renderer/scss/component/_form-field.scss @@ -23,6 +23,10 @@ flex: 1; } + &.form-row--right { + justify-content: flex-end; + } + .form-field.form-field--stretch { width: 100%;