Merge branch 'master' into should-component-update
This commit is contained in:
commit
f21bf287d9
7 changed files with 226 additions and 148 deletions
|
@ -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
|
||||
|
|
14
ui/js/component/video/internal/loading-screen.jsx
Normal file
14
ui/js/component/video/internal/loading-screen.jsx
Normal file
|
@ -0,0 +1,14 @@
|
|||
import React from "react";
|
||||
|
||||
const LoadingScreen = ({ status }) =>
|
||||
<div className="video__loading-screen">
|
||||
<div>
|
||||
<div className="video__loading-spinner" />
|
||||
|
||||
<div className="video__loading-status">
|
||||
{status}
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
|
||||
export default LoadingScreen;
|
92
ui/js/component/video/internal/play-button.jsx
Normal file
92
ui/js/component/video/internal/play-button.jsx
Normal file
|
@ -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 (
|
||||
<div>
|
||||
<Link
|
||||
button={button ? button : null}
|
||||
disabled={disabled}
|
||||
label={label ? label : ""}
|
||||
className="video__play-button"
|
||||
icon={icon}
|
||||
onClick={this.onWatchClick.bind(this)}
|
||||
/>
|
||||
<Modal
|
||||
contentLabel={__("Not enough credits")}
|
||||
isOpen={modal == "notEnoughCredits"}
|
||||
onConfirmed={closeModal}
|
||||
>
|
||||
{__("You don't have enough LBRY credits to pay for this stream.")}
|
||||
</Modal>
|
||||
<Modal
|
||||
type="confirm"
|
||||
isOpen={modal == "affirmPurchaseAndPlay"}
|
||||
contentLabel={__("Confirm Purchase")}
|
||||
onConfirmed={this.onPurchaseConfirmed.bind(this)}
|
||||
onAborted={closeModal}
|
||||
>
|
||||
{__("This will purchase")} <strong>{title}</strong> {__("for")}
|
||||
{" "}<strong><FilePrice uri={uri} look="plain" /></strong>
|
||||
{" "}{__("credits")}.
|
||||
</Modal>
|
||||
<Modal
|
||||
isOpen={modal == "timedOut"}
|
||||
onConfirmed={closeModal}
|
||||
contentLabel={__("Timed Out")}
|
||||
>
|
||||
{__("Sorry, your download timed out :(")}
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default VideoPlayButton;
|
35
ui/js/component/video/internal/player.jsx
Normal file
35
ui/js/component/video/internal/player.jsx
Normal file
|
@ -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 (
|
||||
<div>
|
||||
{["audio", "application"].indexOf(mediaType) !== -1 &&
|
||||
<Thumbnail src={poster} className="video-embedded" />}
|
||||
<div ref="media" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default VideoPlayer;
|
|
@ -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 (
|
||||
<div>
|
||||
<Link
|
||||
button={button ? button : null}
|
||||
disabled={disabled}
|
||||
label={label ? label : ""}
|
||||
className="video__play-button"
|
||||
icon={icon}
|
||||
onClick={this.onWatchClick.bind(this)}
|
||||
/>
|
||||
<Modal
|
||||
contentLabel={__("Not enough credits")}
|
||||
isOpen={modal == "notEnoughCredits"}
|
||||
onConfirmed={closeModal}
|
||||
>
|
||||
{__("You don't have enough LBRY credits to pay for this stream.")}
|
||||
</Modal>
|
||||
<Modal
|
||||
type="confirm"
|
||||
isOpen={modal == "affirmPurchaseAndPlay"}
|
||||
contentLabel={__("Confirm Purchase")}
|
||||
onConfirmed={this.onPurchaseConfirmed.bind(this)}
|
||||
onAborted={closeModal}
|
||||
>
|
||||
{__("This will purchase")} <strong>{title}</strong> {__("for")}
|
||||
{" "}<strong><FilePrice uri={uri} look="plain" /></strong>
|
||||
{" "}{__("credits")}.
|
||||
</Modal>
|
||||
<Modal
|
||||
isOpen={modal == "timedOut"}
|
||||
onConfirmed={closeModal}
|
||||
contentLabel={__("Timed Out")}
|
||||
>
|
||||
{__("Sorry, your download timed out :(")}
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
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 (
|
||||
<div className={klassName}>
|
||||
{isPlaying
|
||||
? !isReadyToPlay
|
||||
? <span>
|
||||
{__(
|
||||
"this is the world's worst loading screen and we shipped our software with it anyway..."
|
||||
)}
|
||||
{" "}<br /><br />{loadStatusMessage}
|
||||
</span>
|
||||
{isPlaying &&
|
||||
(!isReadyToPlay
|
||||
? <LoadingScreen status={loadStatusMessage} />
|
||||
: <VideoPlayer
|
||||
filename={fileInfo.file_name}
|
||||
poster={poster}
|
||||
downloadPath={fileInfo.download_path}
|
||||
mediaType={mediaType}
|
||||
poster={poster}
|
||||
/>
|
||||
: <div
|
||||
className="video__cover"
|
||||
style={{ backgroundImage: 'url("' + metadata.thumbnail + '")' }}
|
||||
>
|
||||
<VideoPlayButton
|
||||
startPlaying={this.startPlaying.bind(this)}
|
||||
{...this.props}
|
||||
mediaType={mediaType}
|
||||
/>
|
||||
</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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 (
|
||||
<div>
|
||||
{["audio", "application"].indexOf(mediaType) !== -1 &&
|
||||
<Thumbnail src={poster} className="video-embedded" />}
|
||||
<div ref="media" />
|
||||
/>)}
|
||||
{!isPlaying &&
|
||||
<div
|
||||
className="video__cover"
|
||||
style={{ backgroundImage: 'url("' + metadata.thumbnail + '")' }}
|
||||
>
|
||||
<VideoPlayButton
|
||||
startPlaying={this.startPlaying.bind(this)}
|
||||
{...this.props}
|
||||
mediaType={mediaType}
|
||||
/>
|
||||
</div>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue