Some UI fixes

This commit is contained in:
Ky 2019-02-22 10:09:07 +01:00
parent 9e1620cab9
commit e01dc0eee3
11 changed files with 172 additions and 163 deletions

View File

@ -15,8 +15,6 @@ import '../../../../node_modules/video-react/dist/video-react.css';
import styles from './styles.scss'; import styles from './styles.scss';
class OnboardingVideo extends React.Component { class OnboardingVideo extends React.Component {
player = React.createRef();
hiddenPlayer = React.createRef();
componentDidMount() { componentDidMount() {
this.hiddenPlayer.current.subscribeToStateChange( this.hiddenPlayer.current.subscribeToStateChange(
@ -24,6 +22,9 @@ class OnboardingVideo extends React.Component {
); );
} }
hiddenPlayer = React.createRef();
player = React.createRef();
handleChangeState = (state, prevState) => { handleChangeState = (state, prevState) => {
const { duration } = state; const { duration } = state;
const { id } = this.props; const { id } = this.props;
@ -37,18 +38,17 @@ class OnboardingVideo extends React.Component {
const { isActive } = state; const { isActive } = state;
const { id } = this.props; const { id } = this.props;
// Manual play
if (isActive !== prevState.isActive && isActive) { if (isActive !== prevState.isActive && isActive) {
this.props.didPlayVideo(id, this.props.video.startTime); this.props.didPlayVideo(id, this.props.video.startTime);
} }
}; };
handleCurrentTimeChange = (curr) => { handleCurrentTimeChange = (curr) => {
this.props.getVideoCurrentTime(this.props.id, curr, this.props.video.duration); this.props.getVideoCurrentTime(this.props.id, curr, this.props.video.duration);
} }
afterOpenModal = () => { handleModalOpen = () => {
this.player.current.subscribeToStateChange( this.player.current.subscribeToStateChange(
this.handleChangeIsPlayingState, this.handleChangeIsPlayingState,
@ -65,24 +65,22 @@ class OnboardingVideo extends React.Component {
} }
}; };
onModalClose = () => { handleVideoPause = () => {
const { player } = this.player.current.getState();
const paused = player.paused;
if (!paused) {
this.videoPause();
}
};
videoPause = () => {
const { player } = this.player.current.getState(); const { player } = this.player.current.getState();
const currTime = player.currentTime; const currTime = player.currentTime;
this.handleCurrentTimeChange(currTime); this.handleCurrentTimeChange(currTime);
this.props.didStopVideo(this.props.id, currTime); this.props.didStopVideo(this.props.id, currTime);
}; };
handleModalClose = () => {
const { player } = this.player.current.getState();
const paused = player.paused;
if (!paused) {
this.handleVideoPause();
}
};
render() { render() {
const { video } = this.props; const { video } = this.props;
@ -94,7 +92,7 @@ class OnboardingVideo extends React.Component {
className={cn(styles.listItem, video.end ? styles.finished : '')} className={cn(styles.listItem, video.end ? styles.finished : '')}
> >
<div className={styles.thumbWrapper}> <div className={styles.thumbWrapper}>
<img src={video.preview} /> <img src={video.preview} alt="preview" />
<div className={styles.overlay} /> <div className={styles.overlay} />
<div className={styles.play} /> <div className={styles.play} />
</div> </div>
@ -105,13 +103,13 @@ class OnboardingVideo extends React.Component {
<Modal <Modal
isOpen={video.isOpen} isOpen={video.isOpen}
toggle={this.props.onClick} toggle={this.props.onClick} // eslint-disable-line react/jsx-handler-names
className={styles.videoModal} className={styles.videoModal}
onOpened={this.afterOpenModal} onOpened={this.handleModalOpen}
onClosed={this.onModalClose} onClosed={this.handleModalClose}
> >
<ModalHeader <ModalHeader
toggle={this.props.onClick} toggle={this.props.onClick} // eslint-disable-line react/jsx-handler-names
className={styles.videoModalHeader} className={styles.videoModalHeader}
> >
{video.title} {video.title}
@ -125,7 +123,7 @@ class OnboardingVideo extends React.Component {
src={video.video} src={video.video}
startTime={video.startTime} startTime={video.startTime}
preload="auto" preload="auto"
onPause={this.videoPause} onPause={this.handleVideoPause}
onplay={this.videoStart} onplay={this.videoStart}
subscribeToStateChange={this.subscribeToStateChange} subscribeToStateChange={this.subscribeToStateChange}
/> />
@ -150,21 +148,23 @@ class OnboardingVideo extends React.Component {
} }
OnboardingVideo.defaultProps = { OnboardingVideo.defaultProps = {
currTime: 0,
video: {},
setVideoDuration: () => {},
getVideoCurrentTime: () => {},
didStopVideo: () => {},
didPlayVideo: () => {}, didPlayVideo: () => {},
didStopVideo: () => {},
getVideoCurrentTime: () => {},
id: 0,
onClick: () => {},
setVideoDuration: () => {},
video: {},
}; };
OnboardingVideo.propTypes = { OnboardingVideo.propTypes = {
currTime: PropTypes.number,
videos: PropTypes.object,
setVideoDuration: PropTypes.func,
getVideoCurrentTime: PropTypes.func,
didStopVideo: PropTypes.func,
didPlayVideo: PropTypes.func, didPlayVideo: PropTypes.func,
didStopVideo: PropTypes.func,
getVideoCurrentTime: PropTypes.func,
id: PropTypes.number,
onClick: PropTypes.func,
setVideoDuration: PropTypes.func,
video: PropTypes.object,
}; };
export default OnboardingVideo; export default OnboardingVideo;

View File

@ -111,6 +111,7 @@ li.listItem {
font-family: Lato; font-family: Lato;
font-weight: bold!important; font-weight: bold!important;
font-size: 1.8rem!important; font-size: 1.8rem!important;
color: #333740;
} }
> button { > button {
margin-top: 0; margin-top: 0;

View File

@ -1,67 +0,0 @@
/**
*
* PopUpWarning
*
*/
import React from 'react';
import PropTypes from 'prop-types';
// modal
import { Button, Modal, ModalHeader, ModalBody } from 'reactstrap';
import { Player } from 'video-react';
import { FormattedMessage } from 'react-intl';
import styles from './styles.scss';
function PopUpVideo({
content,
isOpen,
onConfirm,
onlyConfirmButton,
popUpWarningType,
toggleModal,
video,
}) {
return (
<div className={styles.popUpWarningHelper}>
<Player playsInline poster="/assets/poster.png" src={video} />
{/* <Modal
isOpen={isOpen}
toggle={toggleModal}
className={styles.modalPosition}
>
<ModalHeader toggle={toggleModal} className={styles.popUpWarningHeader}>
<FormattedMessage
id={content.title || 'components.popUpWarning.title'}
/>
</ModalHeader>
<ModalBody className={styles.modalBodyHelper}>
<div>
<video controls autoPlay src={src} />
</div>
</ModalBody>
</Modal> */}
</div>
);
}
PopUpVideo.propTypes = {
content: PropTypes.shape({
cancel: PropTypes.string,
confirm: PropTypes.string,
message: PropTypes.string,
title: PropTypes.string,
}),
};
PopUpVideo.defaultProps = {
content: {
cancel: 'components.popUpWarning.button.cancel',
confirm: 'components.popUpWarning.button.confirm',
message: 'components.popUpWarning.message',
title: 'components.popUpWarning.title',
},
};
export default PopUpVideo;

View File

@ -26,6 +26,6 @@ export function emitEvent(event, properties) {
return { return {
type: EMIT_EVENT, type: EMIT_EVENT,
event, event,
properties properties,
}; };
} }

View File

@ -315,6 +315,7 @@ AdminPage.propTypes = {
appPlugins: PropTypes.array, appPlugins: PropTypes.array,
blockApp: PropTypes.bool.isRequired, blockApp: PropTypes.bool.isRequired,
disableGlobalOverlayBlocker: PropTypes.func.isRequired, disableGlobalOverlayBlocker: PropTypes.func.isRequired,
emitEvent: PropTypes.func.isRequired,
enableGlobalOverlayBlocker: PropTypes.func.isRequired, enableGlobalOverlayBlocker: PropTypes.func.isRequired,
getAdminData: PropTypes.func.isRequired, getAdminData: PropTypes.func.isRequired,
hasUserPlugin: PropTypes.bool, hasUserPlugin: PropTypes.bool,

View File

@ -16,7 +16,7 @@ function* emitter(action) {
if (uuid) { if (uuid) {
yield call( yield call(
fetch, fetch, // eslint-disable-line no-undef
requestURL, requestURL,
{ {
method: 'POST', method: 'POST',

View File

@ -9,24 +9,34 @@ import PropTypes from 'prop-types';
import cn from 'classnames'; import cn from 'classnames';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { bindActionCreators, compose } from 'redux'; import { bindActionCreators, compose } from 'redux';
import { getVideos, onClick, setVideoDuration, updateVideoStartTime, setVideoEnd, removeVideos } from './actions';
import injectSaga from 'utils/injectSaga'; import injectSaga from 'utils/injectSaga';
import injectReducer from 'utils/injectReducer'; import injectReducer from 'utils/injectReducer';
import OnboardingVideo from 'components/OnboardingVideo';
import { getVideos, onClick, removeVideos, setVideoDuration, setVideoEnd, updateVideoStartTime } from './actions';
import makeSelectOnboarding from './selectors'; import makeSelectOnboarding from './selectors';
import reducer from './reducer'; import reducer from './reducer';
import saga from './saga'; import saga from './saga';
import OnboardingVideo from 'components/OnboardingVideo';
import styles from './styles.scss'; import styles from './styles.scss';
export class Onboarding extends React.Component { export class Onboarding extends React.Component {
state = { showVideos: false }; state = { showVideos: false, percentage: 0 };
componentDidMount() { componentDidMount() {
this.setState({ showVideos: true });
this.props.getVideos(); this.props.getVideos();
}
componentWillReceiveProps(){
if(!localStorage.getItem('onboarding')) {
setTimeout(() => {
this.setState({ showVideos: true });
localStorage.setItem('onboarding', true);
}, 500);
}
this.getCompletedPercentage();
} }
componentWillUnmount() { componentWillUnmount() {
@ -34,7 +44,38 @@ export class Onboarding extends React.Component {
localStorage.removeItem('videos'); localStorage.removeItem('videos');
} }
toggleVideos = () => { setVideoEnd = () => {
this.setVideoEnd();
}
getCompletedPercentage = () => {
let videosEnd = 0;
let videos = JSON.parse(localStorage.getItem('videos'));
for (let i = 0; i < videos.length; i++) {
let video = videos[i];
if (video.end) {
videosEnd++;
}
}
this.setState({ percentage: Math.floor(videosEnd*100/videos.length)});
}
didPlayVideo = (index, currTime) => {
const eventName = `didPlay${index}GetStartedVideo`;
this.context.emitEvent(eventName, {timestamp: currTime});
}
didStopVideo = (index, currTime) => {
const eventName = `didStop${index}Video`;
this.context.emitEvent(eventName, {timestamp: currTime});
}
handleVideosToggle = () => {
// Display videos card // Display videos card
this.setState(prevState => ({ showVideos: !prevState.showVideos })); this.setState(prevState => ({ showVideos: !prevState.showVideos }));
@ -44,22 +85,6 @@ export class Onboarding extends React.Component {
this.context.emitEvent(eventName); this.context.emitEvent(eventName);
}; };
setVideoEnd = () => {
this.setVideoEnd();
}
didPlayVideo = (index, currTime) => {
const eventName = `didPlay${index}GetStartedVideo`;
this.context.emitEvent(eventName, {timestamp: currTime});
}
didStopVideo = (index, currTime) => {
const eventName = `didStop${index}Video`;
this.context.emitEvent(eventName, {timestamp: currTime});
}
updateLocalStorage = (index, current, duration) => { updateLocalStorage = (index, current, duration) => {
// Update store // Update store
this.props.updateVideoStartTime(index, current); this.props.updateVideoStartTime(index, current);
@ -67,8 +92,8 @@ export class Onboarding extends React.Component {
// Update localStorage // Update localStorage
let videosTime = JSON.parse(localStorage.getItem('videos')); let videosTime = JSON.parse(localStorage.getItem('videos'));
videosTime[index].startTime = current; videosTime[index].startTime = current;
let percent = current * 100 / duration; let percent = current * 100 / duration;
if (percent >= 80) { if (percent >= 80) {
if (videosTime[index].end === false) { if (videosTime[index].end === false) {
videosTime[index].end = true; videosTime[index].end = true;
@ -77,25 +102,21 @@ export class Onboarding extends React.Component {
} }
localStorage.setItem('videos', JSON.stringify(videosTime)); localStorage.setItem('videos', JSON.stringify(videosTime));
this.getCompletedPercentage();
}; };
// eslint-disable-line react/prefer-stateless-function // eslint-disable-line react/prefer-stateless-function
// eslint-disable-line jsx-handler-names
render() { render() {
const { videos, onClick, setVideoDuration } = this.props; const { videos, onClick, setVideoDuration } = this.props;
return ( return (
<div className={styles.videosWrapper}> <div className={cn(styles.videosWrapper, videos.length > 0 ? styles.visible : styles.hidden)}>
<div <div className={cn(styles.videosContent, this.state.showVideos ? styles.shown : styles.hide)}>
className={cn(
styles.videosContent,
this.state.showVideos ? styles.shown : styles.hide,
)}
>
<div className={styles.videosHeader}> <div className={styles.videosHeader}>
<p>Get started video</p> <p>Get started video</p>
<p>25% completed</p> <p>{this.state.percentage}% completed</p>
</div> </div>
<ul className={styles.onboardingList}> <ul className={styles.onboardingList}>
{videos.map((video, i) => { {videos.map((video, i) => {
return ( return (
@ -116,7 +137,7 @@ export class Onboarding extends React.Component {
<div className={styles.openBtn}> <div className={styles.openBtn}>
<button <button
onClick={this.toggleVideos} onClick={this.handleVideosToggle}
className={this.state.showVideos ? styles.active : ''} className={this.state.showVideos ? styles.active : ''}
> >
<i className="fa fa-question" /> <i className="fa fa-question" />
@ -132,8 +153,23 @@ Onboarding.contextTypes = {
emitEvent: PropTypes.func, emitEvent: PropTypes.func,
}; };
Onboarding.defaultProps = {
onClick: () => {},
removeVideos: () => {},
setVideoDuration: () => {},
setVideoEnd: () => {},
updateVideoStartTime: () => {},
videos: [],
};
Onboarding.propTypes = { Onboarding.propTypes = {
getVideos: PropTypes.func.isRequired, getVideos: PropTypes.func.isRequired,
onClick: PropTypes.func,
removeVideos: PropTypes.func,
setVideoDuration: PropTypes.func,
setVideoEnd: PropTypes.func,
updateVideoStartTime: PropTypes.func,
videos: PropTypes.array,
}; };
const mapStateToProps = makeSelectOnboarding(); const mapStateToProps = makeSelectOnboarding();

View File

@ -1,8 +1,8 @@
import request from 'utils/request';
import { all, call, fork, takeLatest, put } from 'redux-saga/effects';
import { GET_VIDEOS } from './constants'; import { GET_VIDEOS } from './constants';
import { getVideosSucceeded } from './actions'; import { getVideosSucceeded } from './actions';
import request from 'utils/request';
import { all, call, fork, takeLatest, put } from 'redux-saga/effects';
function* getVideos() { function* getVideos() {
try { try {
@ -23,7 +23,7 @@ function* getVideos() {
end: false, end: false,
key: i, key: i,
id: videos[i].id, id: videos[i].id,
} };
}); });
// Retrieve start time if enable in localStorage // Retrieve start time if enable in localStorage
@ -46,8 +46,7 @@ function* getVideos() {
), ),
); );
} catch (err) { } catch (err) {
console.log('err'); console.log(err); // eslint-disable-line no-console
console.log({ err });
} }
} }

View File

@ -15,6 +15,7 @@
padding: 15px 15px 0 15px; padding: 15px 15px 0 15px;
p { p {
font-family: Lato-Bold; font-family: Lato-Bold;
letter-spacing: 0.5px;
font-size: 11px; font-size: 11px;
text-transform: uppercase; text-transform: uppercase;
display: inline-block; display: inline-block;
@ -27,20 +28,25 @@
} }
} }
} }
&.visible {
opacity: 1;
}
&.hidden {
opacity: 0;
}
.videosContent { .videosContent {
min-width: 320px;
background-color: white; background-color: white;
margin-bottom: 10px; margin-bottom: 10px;
margin-right: 15px; margin-right: 15px;
box-shadow: 0 2px 4px 0 #e3e9f3; box-shadow: 0 2px 4px 0 #e3e9f3;
border-radius: 3px; border-radius: 3px;
transition: all 0.3s linear; overflow: hidden;
&.shown { &.shown {
visibility: visible; animation: fadeIn 0.5s forwards;
opacity: 1;
} }
&.hide { &.hide {
opacity: 0; animation: fadeOut 0.5s forwards;
visibility: hidden;
} }
ul { ul {
@ -75,3 +81,36 @@
} }
} }
} }
@keyframes fadeIn {
0% {
width: auto;
height: auto;
opacity: 0;
}
5% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fadeOut {
0% {
opacity: 1;
}
60% {
opacity: 0;
}
100% {
opacity: 0;
width: 0;
height: 0;
}
}