Add videos to be played across all pages. #1523
14 changed files with 154 additions and 10 deletions
14
src/renderer/component/overlay/index.js
Normal file
14
src/renderer/component/overlay/index.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { selectShowOverlay } from 'redux/selectors/media';
|
||||||
|
import { doHideOverlay } from 'redux/actions/media';
|
||||||
|
import Overlay from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
showOverlay: selectShowOverlay(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
const perform = dispatch => ({
|
||||||
|
doCloseOverlay: dispatch(doHideOverlay()),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, perform)(Overlay);
|
16
src/renderer/component/overlay/view.jsx
Normal file
16
src/renderer/component/overlay/view.jsx
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
showOverlay: ?boolean,
|
||||||
|
children: ?React.node,
|
||||||
|
};
|
||||||
|
|
||||||
|
class Overlay extends React.PureComponent<Props> {
|
||||||
|
render() {
|
||||||
|
const { showOverlay, children } = this.props;
|
||||||
|
return <div className="overlay">{showOverlay ? children : ''}</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Overlay;
|
|
@ -3,7 +3,7 @@ import * as settings from 'constants/settings';
|
||||||
import { doChangeVolume } from 'redux/actions/app';
|
import { doChangeVolume } from 'redux/actions/app';
|
||||||
import { selectVolume } from 'redux/selectors/app';
|
import { selectVolume } from 'redux/selectors/app';
|
||||||
import { doPlayUri, doSetPlayingUri, doLoadVideo } from 'redux/actions/content';
|
import { doPlayUri, doSetPlayingUri, doLoadVideo } from 'redux/actions/content';
|
||||||
import { doPlay, doPause, savePosition } from 'redux/actions/media';
|
import { doPlay, doPause, savePosition, doHideOverlay, doShowOverlay } from 'redux/actions/media';
|
||||||
import {
|
import {
|
||||||
makeSelectMetadataForUri,
|
makeSelectMetadataForUri,
|
||||||
makeSelectContentTypeForUri,
|
makeSelectContentTypeForUri,
|
||||||
|
@ -15,7 +15,11 @@ import {
|
||||||
selectSearchBarFocused,
|
selectSearchBarFocused,
|
||||||
} from 'lbry-redux';
|
} from 'lbry-redux';
|
||||||
import { makeSelectClientSetting, selectShowNsfw } from 'redux/selectors/settings';
|
import { makeSelectClientSetting, selectShowNsfw } from 'redux/selectors/settings';
|
||||||
import { selectMediaPaused, makeSelectMediaPositionForUri } from 'redux/selectors/media';
|
import {
|
||||||
|
selectMediaPaused,
|
||||||
|
makeSelectMediaPositionForUri,
|
||||||
|
selectShowOverlay,
|
||||||
|
} from 'redux/selectors/media';
|
||||||
import { selectPlayingUri } from 'redux/selectors/content';
|
import { selectPlayingUri } from 'redux/selectors/content';
|
||||||
import Video from './view';
|
import Video from './view';
|
||||||
|
|
||||||
|
@ -34,15 +38,18 @@ const select = (state, props) => ({
|
||||||
mediaPosition: makeSelectMediaPositionForUri(props.uri)(state),
|
mediaPosition: makeSelectMediaPositionForUri(props.uri)(state),
|
||||||
autoplay: makeSelectClientSetting(settings.AUTOPLAY)(state),
|
autoplay: makeSelectClientSetting(settings.AUTOPLAY)(state),
|
||||||
searchBarFocused: selectSearchBarFocused(state),
|
searchBarFocused: selectSearchBarFocused(state),
|
||||||
|
showOverlay: selectShowOverlay(state),
|
||||||
|
overlayed: props.overlayed,
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = dispatch => ({
|
||||||
play: uri => dispatch(doPlayUri(uri)),
|
play: uri => dispatch(doPlayUri(uri)),
|
||||||
load: uri => dispatch(doLoadVideo(uri)),
|
load: uri => dispatch(doLoadVideo(uri)),
|
||||||
cancelPlay: () => dispatch(doSetPlayingUri(null)),
|
|
||||||
changeVolume: volume => dispatch(doChangeVolume(volume)),
|
changeVolume: volume => dispatch(doChangeVolume(volume)),
|
||||||
doPlay: () => dispatch(doPlay()),
|
doPlay: () => dispatch(doPlay()),
|
||||||
doPause: () => dispatch(doPause()),
|
doPause: () => dispatch(doPause()),
|
||||||
|
doShowOverlay: () => dispatch(doShowOverlay()),
|
||||||
|
doHideOverlay: () => dispatch(doHideOverlay()),
|
||||||
savePosition: (claimId, position) => dispatch(savePosition(claimId, position)),
|
savePosition: (claimId, position) => dispatch(savePosition(claimId, position)),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import LoadingScreen from './internal/loading-screen';
|
||||||
const SPACE_BAR_KEYCODE = 32;
|
const SPACE_BAR_KEYCODE = 32;
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
cancelPlay: () => void,
|
|
||||||
fileInfo: {
|
fileInfo: {
|
||||||
outpoint: string,
|
outpoint: string,
|
||||||
file_name: string,
|
file_name: string,
|
||||||
|
@ -34,12 +33,16 @@ type Props = {
|
||||||
doPlay: () => void,
|
doPlay: () => void,
|
||||||
doPause: () => void,
|
doPause: () => void,
|
||||||
savePosition: (string, number) => void,
|
savePosition: (string, number) => void,
|
||||||
|
doShowOverlay: () => void,
|
||||||
|
doHideOverlay: () => void,
|
||||||
mediaPaused: boolean,
|
mediaPaused: boolean,
|
||||||
mediaPosition: ?number,
|
mediaPosition: ?number,
|
||||||
className: ?string,
|
className: ?string,
|
||||||
obscureNsfw: boolean,
|
obscureNsfw: boolean,
|
||||||
play: string => void,
|
play: string => void,
|
||||||
searchBarFocused: boolean,
|
searchBarFocused: boolean,
|
||||||
|
showOverlay: boolean,
|
||||||
|
overlayed: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Video extends React.PureComponent<Props> {
|
class Video extends React.PureComponent<Props> {
|
||||||
|
@ -53,6 +56,11 @@ class Video extends React.PureComponent<Props> {
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.handleAutoplay(this.props);
|
this.handleAutoplay(this.props);
|
||||||
window.addEventListener('keydown', this.handleKeyDown);
|
window.addEventListener('keydown', this.handleKeyDown);
|
||||||
|
|
||||||
|
const { showOverlay, doHideOverlay, uri, playingUri, overlayed } = this.props;
|
||||||
|
if (showOverlay && uri === playingUri && !overlayed) {
|
||||||
|
doHideOverlay();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillReceiveProps(nextProps: Props) {
|
componentWillReceiveProps(nextProps: Props) {
|
||||||
|
@ -67,7 +75,10 @@ class Video extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
this.props.cancelPlay();
|
const { overlayed, doShowOverlay } = this.props;
|
||||||
|
if (!overlayed) {
|
||||||
|
doShowOverlay();
|
||||||
|
}
|
||||||
window.removeEventListener('keydown', this.handleKeyDown);
|
window.removeEventListener('keydown', this.handleKeyDown);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +122,10 @@ class Video extends React.PureComponent<Props> {
|
||||||
}
|
}
|
||||||
|
|
||||||
playContent() {
|
playContent() {
|
||||||
const { play, uri } = this.props;
|
const { play, uri, showOverlay, playingUri, doHideOverlay } = this.props;
|
||||||
|
if (playingUri && showOverlay) {
|
||||||
|
doHideOverlay();
|
||||||
|
}
|
||||||
play(uri);
|
play(uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
9
src/renderer/component/videoOverlay/index.js
Normal file
9
src/renderer/component/videoOverlay/index.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
import { selectPlayingUri } from 'redux/selectors/content';
|
||||||
|
import VideoOverlay from './view';
|
||||||
|
|
||||||
|
const select = state => ({
|
||||||
|
playingUri: selectPlayingUri(state),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default connect(select, null)(VideoOverlay);
|
25
src/renderer/component/videoOverlay/view.jsx
Normal file
25
src/renderer/component/videoOverlay/view.jsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import Video from 'component/video';
|
||||||
|
import FileActions from 'component/fileActions';
|
||||||
|
import Overlay from 'component/overlay';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
playingUri: ?string,
|
||||||
|
};
|
||||||
|
|
||||||
|
class VideoOverlay extends React.Component<Props> {
|
||||||
|
render() {
|
||||||
|
const { playingUri } = this.props;
|
||||||
|
return (
|
||||||
|
<Overlay>
|
||||||
|
<div className="card-media__internal-links">
|
||||||
|
<FileActions uri={playingUri} vertical />
|
||||||
|
</div>
|
||||||
|
{playingUri ? <Video className="content__embedded" uri={playingUri} overlayed /> : ''}
|
||||||
|
</Overlay>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default VideoOverlay;
|
|
@ -187,6 +187,9 @@ export const SET_VIDEO_PAUSE = 'SET_VIDEO_PAUSE';
|
||||||
export const MEDIA_PLAY = 'MEDIA_PLAY';
|
export const MEDIA_PLAY = 'MEDIA_PLAY';
|
||||||
export const MEDIA_PAUSE = 'MEDIA_PAUSE';
|
export const MEDIA_PAUSE = 'MEDIA_PAUSE';
|
||||||
export const MEDIA_POSITION = 'MEDIA_POSITION';
|
export const MEDIA_POSITION = 'MEDIA_POSITION';
|
||||||
|
// Overlay Media
|
||||||
|
export const SHOW_OVERLAY_MEDIA = 'SHOW_OVERLAY_MEDIA';
|
||||||
|
export const HIDE_OVERLAY_MEDIA = 'HIDE_OVERLAY_MEDIA';
|
||||||
|
|
||||||
// Publishing
|
// Publishing
|
||||||
export const CLEAR_PUBLISH = 'CLEAR_PUBLISH';
|
export const CLEAR_PUBLISH = 'CLEAR_PUBLISH';
|
||||||
|
|
|
@ -16,6 +16,7 @@ import 'scss/all.scss';
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
import app from './app';
|
import app from './app';
|
||||||
import analytics from './analytics';
|
import analytics from './analytics';
|
||||||
|
import VideoOverlay from './component/videoOverlay/';
|
||||||
|
|
||||||
const { autoUpdater } = remote.require('electron-updater');
|
const { autoUpdater } = remote.require('electron-updater');
|
||||||
|
|
||||||
|
@ -131,6 +132,7 @@ const init = () => {
|
||||||
<div>
|
<div>
|
||||||
<App />
|
<App />
|
||||||
<SnackBar />
|
<SnackBar />
|
||||||
|
<VideoOverlay />
|
||||||
</div>
|
</div>
|
||||||
</Provider>,
|
</Provider>,
|
||||||
document.getElementById('app')
|
document.getElementById('app')
|
||||||
|
|
|
@ -26,3 +26,13 @@ export function savePosition(claimId: String, position: Number) {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const doShowOverlay = () => (dispatch: Dispatch) =>
|
||||||
|
dispatch({
|
||||||
|
type: actions.SHOW_OVERLAY_MEDIA,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const doHideOverlay = () => (dispatch: Dispatch) =>
|
||||||
|
dispatch({
|
||||||
|
type: actions.HIDE_OVERLAY_MEDIA,
|
||||||
|
});
|
||||||
|
|
|
@ -2,6 +2,7 @@ import * as ACTIONS from 'constants/action_types';
|
||||||
|
|
||||||
const reducers = {};
|
const reducers = {};
|
||||||
const defaultState = {
|
const defaultState = {
|
||||||
|
showOverlay: false,
|
||||||
playingUri: null,
|
playingUri: null,
|
||||||
currentlyIsPlaying: false,
|
currentlyIsPlaying: false,
|
||||||
rewardedContentClaimIds: [],
|
rewardedContentClaimIds: [],
|
||||||
|
|
|
@ -3,6 +3,7 @@ import * as actions from 'constants/action_types';
|
||||||
import { handleActions } from 'util/redux-utils';
|
import { handleActions } from 'util/redux-utils';
|
||||||
|
|
||||||
export type MediaState = {
|
export type MediaState = {
|
||||||
|
showOverlay: Boolean,
|
||||||
paused: Boolean,
|
paused: Boolean,
|
||||||
positions: {
|
positions: {
|
||||||
[string]: number,
|
[string]: number,
|
||||||
|
@ -12,21 +13,22 @@ export type MediaState = {
|
||||||
export type Action = any;
|
export type Action = any;
|
||||||
export type Dispatch = (action: Action) => any;
|
export type Dispatch = (action: Action) => any;
|
||||||
|
|
||||||
const defaultState = { paused: true, positions: {} };
|
const defaultState = { paused: true, positions: {}, showOverlay: false };
|
||||||
|
|
||||||
export default handleActions(
|
export default handleActions(
|
||||||
{
|
{
|
||||||
[actions.MEDIA_PLAY]: (state: MediaState, action: Action) => ({
|
// if parameters: state: MediaState, action: Action
|
||||||
|
[actions.MEDIA_PLAY]: (state: MediaState) => ({
|
||||||
...state,
|
...state,
|
||||||
paused: false,
|
paused: false,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[actions.MEDIA_PAUSE]: (state: MediaState, action: Action) => ({
|
[actions.MEDIA_PAUSE]: (state: MediaState) => ({
|
||||||
...state,
|
...state,
|
||||||
paused: true,
|
paused: true,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
[actions.MEDIA_POSITION]: (state: MediaState, action: Action) => {
|
[actions.MEDIA_POSITION]: (state: MediaState) => {
|
||||||
const { outpoint, position } = action.data;
|
const { outpoint, position } = action.data;
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
@ -36,6 +38,16 @@ export default handleActions(
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
[actions.SHOW_OVERLAY_MEDIA]: (state: MediaState) => ({
|
||||||
|
...state,
|
||||||
|
showOverlay: true,
|
||||||
|
}),
|
||||||
|
|
||||||
|
[actions.HIDE_OVERLAY_MEDIA]: (state: MediaState) => ({
|
||||||
|
...state,
|
||||||
|
showOverlay: false,
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
defaultState
|
defaultState
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,3 +10,5 @@ export const makeSelectMediaPositionForUri = uri =>
|
||||||
const outpoint = `${claim.txid}:${claim.nout}`;
|
const outpoint = `${claim.txid}:${claim.nout}`;
|
||||||
return state.positions[outpoint] || null;
|
return state.positions[outpoint] || null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const selectShowOverlay = createSelector(selectState, state => state.showOverlay);
|
||||||
|
|
|
@ -24,3 +24,4 @@
|
||||||
@import 'component/_nav.scss';
|
@import 'component/_nav.scss';
|
||||||
@import 'component/_file-list.scss';
|
@import 'component/_file-list.scss';
|
||||||
@import 'component/_search.scss';
|
@import 'component/_search.scss';
|
||||||
|
@import 'component/_overlay.scss';
|
||||||
|
|
28
src/renderer/scss/component/_overlay.scss
Normal file
28
src/renderer/scss/component/_overlay.scss
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
.overlay {
|
||||||
|
position: fixed;
|
||||||
|
max-height: 50%;
|
||||||
|
max-width: 50%;
|
||||||
|
width: 20%;
|
||||||
|
height: inherit;
|
||||||
|
bottom: 1%;
|
||||||
|
right: 1%;
|
||||||
|
z-index: 3;
|
||||||
|
box-shadow: var(--box-shadow-layer);
|
||||||
|
|
||||||
|
&:hover .button-close {
|
||||||
|
display: inline-block;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
position: absolute;
|
||||||
|
height: 22px;
|
||||||
|
width: 22px;
|
||||||
|
top: 0px;
|
||||||
|
right: 0px;
|
||||||
|
color: #000;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.overlay .button-close {
|
||||||
|
display: none;
|
||||||
|
}
|
Loading…
Reference in a new issue