diff --git a/CHANGELOG.md b/CHANGELOG.md index c5c1bb1dc..e1bfc9fc3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ Web UI version numbers should always match the corresponding version of LBRY App ## [Unreleased] ### Added * More file types, like audio and documents, can be streamed and/or served from the app - * + * Videos now have a classy loading spinner ### Changed * All UI strings are now rendered according to gettext standard, in prep for i18n diff --git a/ui/js/component/video/internal/loading-screen.jsx b/ui/js/component/video/internal/loading-screen.jsx new file mode 100644 index 000000000..2698e824e --- /dev/null +++ b/ui/js/component/video/internal/loading-screen.jsx @@ -0,0 +1,14 @@ +import React from "react"; + +const LoadingScreen = ({ status }) => +
+
+
+ +
+ {status} +
+
+
; + +export default LoadingScreen; diff --git a/ui/js/component/video/internal/play-button.jsx b/ui/js/component/video/internal/play-button.jsx new file mode 100644 index 000000000..69834a599 --- /dev/null +++ b/ui/js/component/video/internal/play-button.jsx @@ -0,0 +1,92 @@ +import React from "react"; +import FilePrice from "component/filePrice"; +import Link from "component/link"; +import Modal from "component/modal"; + +class VideoPlayButton extends React.Component { + onPurchaseConfirmed() { + this.props.closeModal(); + this.props.startPlaying(); + this.props.loadVideo(this.props.uri); + } + + onWatchClick() { + this.props.purchaseUri(this.props.uri).then(() => { + if (!this.props.modal) { + this.props.startPlaying(); + } + }); + } + + render() { + const { + button, + label, + metadata, + metadata: { title }, + uri, + modal, + closeModal, + isLoading, + costInfo, + fileInfo, + mediaType, + } = this.props; + + /* + title={ + isLoading ? "Video is Loading" : + !costInfo ? "Waiting on cost info..." : + fileInfo === undefined ? "Waiting on file info..." : "" + } + */ + + const disabled = + isLoading || + fileInfo === undefined || + (fileInfo === null && (!costInfo || costInfo.cost === undefined)); + const icon = ["audio", "video"].indexOf(mediaType) !== -1 + ? "icon-play" + : "icon-folder-o"; + + return ( +
+ + + {__("You don't have enough LBRY credits to pay for this stream.")} + + + {__("This will purchase")} {title} {__("for")} + {" "} + {" "}{__("credits")}. + + + {__("Sorry, your download timed out :(")} + +
+ ); + } +} + +export default VideoPlayButton; diff --git a/ui/js/component/video/internal/player.jsx b/ui/js/component/video/internal/player.jsx new file mode 100644 index 000000000..7685c3112 --- /dev/null +++ b/ui/js/component/video/internal/player.jsx @@ -0,0 +1,35 @@ +import React from "react"; +import { Thumbnail } from "component/common"; +import player from "render-media"; +import fs from "fs"; + +class VideoPlayer extends React.Component { + componentDidMount() { + const elem = this.refs.media; + const { downloadPath, filename } = this.props; + const file = { + name: filename, + createReadStream: opts => { + return fs.createReadStream(downloadPath, opts); + }, + }; + player.append(file, elem, { + autoplay: true, + controls: true, + }); + } + + render() { + const { downloadPath, mediaType, poster } = this.props; + + return ( +
+ {["audio", "application"].indexOf(mediaType) !== -1 && + } +
+
+ ); + } +} + +export default VideoPlayer; diff --git a/ui/js/component/video/view.jsx b/ui/js/component/video/view.jsx index 8ca38acfd..b5ab5580a 100644 --- a/ui/js/component/video/view.jsx +++ b/ui/js/component/video/view.jsx @@ -1,96 +1,8 @@ import React from "react"; -import FilePrice from "component/filePrice"; -import Link from "component/link"; -import Modal from "component/modal"; import lbry from "lbry"; -import { Thumbnail } from "component/common"; - -class VideoPlayButton extends React.PureComponent { - onPurchaseConfirmed() { - this.props.closeModal(); - this.props.startPlaying(); - this.props.loadVideo(this.props.uri); - } - - onWatchClick() { - this.props.purchaseUri(this.props.uri).then(() => { - if (!this.props.modal) { - this.props.startPlaying(); - } - }); - } - - render() { - const { - button, - label, - className, - metadata, - metadata: { title }, - uri, - modal, - closeModal, - isLoading, - costInfo, - fileInfo, - mediaType, - } = this.props; - - /* - title={ - isLoading ? "Video is Loading" : - !costInfo ? "Waiting on cost info..." : - fileInfo === undefined ? "Waiting on file info..." : "" - } - */ - - const disabled = - isLoading || - fileInfo === undefined || - (fileInfo === null && (!costInfo || costInfo.cost === undefined)); - const icon = ["audio", "video"].indexOf(mediaType) !== -1 - ? "icon-play" - : "icon-folder-o"; - - return ( -
- - - {__("You don't have enough LBRY credits to pay for this stream.")} - - - {__("This will purchase")} {title} {__("for")} - {" "} - {" "}{__("credits")}. - - - {__("Sorry, your download timed out :(")} - -
- ); - } -} +import VideoPlayer from "./internal/player"; +import VideoPlayButton from "./internal/play-button"; +import LoadingScreen from "./internal/loading-screen"; class Video extends React.PureComponent { constructor(props) { @@ -148,64 +60,26 @@ class Video extends React.PureComponent { return (
- {isPlaying - ? !isReadyToPlay - ? - {__( - "this is the world's worst loading screen and we shipped our software with it anyway..." - )} - {" "}

{loadStatusMessage} -
+ {isPlaying && + (!isReadyToPlay + ? : - :
- -
} -
- ); - } -} - -const from = require("from2"); -const player = require("render-media"); -const fs = require("fs"); - -class VideoPlayer extends React.PureComponent { - componentDidMount() { - const elem = this.refs.media; - const { downloadPath, filename } = this.props; - const file = { - name: filename, - createReadStream: opts => { - return fs.createReadStream(downloadPath, opts); - }, - }; - player.append(file, elem, { - autoplay: true, - controls: true, - }); - } - - render() { - const { downloadPath, mediaType, poster } = this.props; - - return ( -
- {["audio", "application"].indexOf(mediaType) !== -1 && - } -
+ />)} + {!isPlaying && +
+ +
}
); } diff --git a/ui/js/page/publish/view.jsx b/ui/js/page/publish/view.jsx index bfe9d7773..89db3c109 100644 --- a/ui/js/page/publish/view.jsx +++ b/ui/js/page/publish/view.jsx @@ -373,7 +373,7 @@ class PublishPage extends React.PureComponent { lbry .channel_new({ channel_name: newChannelName, - amount: parseInt(this.state.newChannelBid), + amount: parseFloat(this.state.newChannelBid), }) .then( () => { @@ -594,6 +594,7 @@ class PublishPage extends React.PureComponent { className="form-field__input--inline" step="0.01" placeholder="1.00" + min="0.01" onChange={event => this.handleFeeAmountChange(event)} /> {" "} @@ -752,6 +753,7 @@ class PublishPage extends React.PureComponent { label={__("Deposit")} postfix="LBC" step="0.01" + min="0" type="number" helper={lbcInputHelp} onChange={event => { diff --git a/ui/scss/component/_video.scss b/ui/scss/component/_video.scss index 739dc1b26..e956afe80 100644 --- a/ui/scss/component/_video.scss +++ b/ui/scss/component/_video.scss @@ -13,7 +13,6 @@ video { color: white; } - .video-embedded { max-width: $width-page-constrained; max-height: $height-video-embedded; @@ -28,12 +27,73 @@ video { &.video--active { /*background: none;*/ } + .plyr { top: 50%; transform: translateY(-50%); } } + +.video__loading-screen { + height: 100%; + display: flex; + justify-content: center; + align-items: center; +} + +.video__loading-spinner { + position: relative; + width: 11em; + height: 11em; + margin: 20px auto; + font-size: 3px; + border-radius: 50%; + + background: linear-gradient(to right, #ffffff 10%, rgba(255, 255, 255, 0) 50%); + animation: spin 1.4s infinite linear; + transform: translateZ(0); + + @keyframes spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } + } + + &:before, + &:after { + content: ''; + position: absolute; + top: 0; + left: 0; + } + + &:before { + width: 50%; + height: 50%; + background: #ffffff; + border-radius: 100% 0 0 0; + } + + &:after { + height: 75%; + width: 75%; + margin: auto; + bottom: 0; + right: 0; + background: black; + border-radius: 50%; + } +} + +.video__loading-status { + padding-top: 20px; + color: white; +} + .video__cover { text-align: center; height: 100%; @@ -44,6 +104,7 @@ video { position: relative; .video__play-button { @include absolute-center(); } } + .video__play-button { position: absolute; width: 100%; @@ -61,4 +122,4 @@ video { opacity: 1; transition: opacity $transition-standard; } -} \ No newline at end of file +}