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

View File

@ -111,6 +111,7 @@ li.listItem {
font-family: Lato;
font-weight: bold!important;
font-size: 1.8rem!important;
color: #333740;
}
> button {
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 {
type: EMIT_EVENT,
event,
properties
properties,
};
}

View File

@ -284,7 +284,7 @@ export class AdminPage extends React.Component {
isOpen={this.props.blockApp && this.props.showGlobalAppBlocker}
{...this.props.overlayBlockerData}
/>
{this.shouldDisplayLogout() && <Onboarding/>}
{this.shouldDisplayLogout() && <Onboarding />}
</div>
);
}
@ -315,6 +315,7 @@ AdminPage.propTypes = {
appPlugins: PropTypes.array,
blockApp: PropTypes.bool.isRequired,
disableGlobalOverlayBlocker: PropTypes.func.isRequired,
emitEvent: PropTypes.func.isRequired,
enableGlobalOverlayBlocker: PropTypes.func.isRequired,
getAdminData: PropTypes.func.isRequired,
hasUserPlugin: PropTypes.bool,

View File

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

View File

@ -7,8 +7,8 @@
export const GET_VIDEOS = 'StrapiAdmin/Onboarding/GET_VIDEOS';
export const GET_VIDEOS_SUCCEEDED =
'StrapiAdmin/Onboarding/GET_VIDEOS_SUCCEEDED';
export const ON_CLICK = 'StrapiAdmin/Onboarding/ON_CLICK';
export const SET_VIDEOS_DURATION = 'StrapiAdmin/Onboarding/SET_VIDEOS_DURATION';
export const UPDATE_VIDEO_START_TIME = 'StrapiAdmin/Onboarding/UPDATE_VIDEO_START_TIME';
export const SET_VIDEO_END = 'StrapiAdmin/Onboarding/SET_VIDEO_END';
export const REMOVE_VIDEOS = 'StrapiAdmin/Onboarding/REMOVE_VIDEOS';
export const ON_CLICK = 'StrapiAdmin/Onboarding/ON_CLICK';
export const SET_VIDEOS_DURATION = 'StrapiAdmin/Onboarding/SET_VIDEOS_DURATION';
export const UPDATE_VIDEO_START_TIME = 'StrapiAdmin/Onboarding/UPDATE_VIDEO_START_TIME';
export const SET_VIDEO_END = 'StrapiAdmin/Onboarding/SET_VIDEO_END';
export const REMOVE_VIDEOS = 'StrapiAdmin/Onboarding/REMOVE_VIDEOS';

View File

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

View File

@ -1,20 +1,20 @@
import request from 'utils/request';
import { all, call, fork, takeLatest, put } from 'redux-saga/effects';
import { GET_VIDEOS } from './constants';
import { getVideosSucceeded } from './actions';
import request from 'utils/request';
import { all, call, fork, takeLatest, put } from 'redux-saga/effects';
function* getVideos() {
try {
const videos = yield call(request, 'https://strapi.io/videos', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
false,
true,
{ noAuth: true },
},
false,
true,
{ noAuth: true },
);
let currTimes = Array.apply(null, Array(videos.length)).map((e, i) => {
@ -23,7 +23,7 @@ function* getVideos() {
end: false,
key: i,
id: videos[i].id,
}
};
});
// Retrieve start time if enable in localStorage
@ -46,8 +46,7 @@ function* getVideos() {
),
);
} catch (err) {
console.log('err');
console.log({ err });
console.log(err); // eslint-disable-line no-console
}
}

View File

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