mirror of
https://github.com/strapi/strapi.git
synced 2025-12-27 23:24:03 +00:00
Remove saga from Onboarding container and simplified the logic a little
Signed-off-by: soupette <cyril.lpz@gmail.com>
This commit is contained in:
parent
825721713a
commit
41d649f725
@ -30,8 +30,7 @@ import LocaleToggle from '../LocaleToggle';
|
||||
import HomePage from '../HomePage';
|
||||
import Marketplace from '../Marketplace';
|
||||
import NotFoundPage from '../NotFoundPage';
|
||||
// import Onboarding from '../Onboarding';
|
||||
import OnboardingVideos from '../OnboardingVideos';
|
||||
import OnboardingVideos from '../Onboarding';
|
||||
import SettingsPage from '../SettingsPage';
|
||||
import PluginDispatcher from '../PluginDispatcher';
|
||||
import {
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cn from 'classnames';
|
||||
import { isNaN } from 'lodash';
|
||||
|
||||
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
|
||||
import { Player } from 'video-react';
|
||||
@ -17,7 +18,9 @@ import Li from './Li';
|
||||
|
||||
class OnboardingVideo extends React.Component {
|
||||
componentDidMount() {
|
||||
this.hiddenPlayer.current.subscribeToStateChange(this.handleChangeState);
|
||||
if (this.hiddenPlayer.current) {
|
||||
this.hiddenPlayer.current.subscribeToStateChange(this.handleChangeState);
|
||||
}
|
||||
}
|
||||
|
||||
hiddenPlayer = React.createRef();
|
||||
@ -28,7 +31,7 @@ class OnboardingVideo extends React.Component {
|
||||
const { duration } = state;
|
||||
const { id } = this.props;
|
||||
|
||||
if (duration !== prevState.duration) {
|
||||
if (duration !== prevState.duration && !isNaN(duration)) {
|
||||
this.props.setVideoDuration(id, duration);
|
||||
}
|
||||
};
|
||||
@ -32,6 +32,7 @@ const fadeOut = keyframes`
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
max-width: ${({ isOpen }) => (isOpen ? null : '0px')};
|
||||
position: fixed;
|
||||
right: 15px;
|
||||
bottom: 15px;
|
||||
|
||||
@ -1,64 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Onboarding actions
|
||||
*
|
||||
*/
|
||||
|
||||
import { GET_VIDEOS, GET_VIDEOS_SUCCEEDED, SHOULD_OPEN_MODAL, ON_CLICK, SET_VIDEOS_DURATION, UPDATE_VIDEO_START_TIME, SET_VIDEO_END, REMOVE_VIDEOS } from './constants';
|
||||
|
||||
export function getVideos() {
|
||||
return {
|
||||
type: GET_VIDEOS,
|
||||
};
|
||||
}
|
||||
|
||||
export function getVideosSucceeded(videos) {
|
||||
return {
|
||||
type: GET_VIDEOS_SUCCEEDED,
|
||||
videos,
|
||||
};
|
||||
}
|
||||
|
||||
export function shouldOpenModal(opened) {
|
||||
return {
|
||||
type: SHOULD_OPEN_MODAL,
|
||||
opened,
|
||||
};
|
||||
}
|
||||
|
||||
export function onClick(e) {
|
||||
return {
|
||||
type: ON_CLICK,
|
||||
index: parseInt(e.currentTarget.id, 10),
|
||||
};
|
||||
}
|
||||
|
||||
export function setVideoDuration(index, duration) {
|
||||
return {
|
||||
type: SET_VIDEOS_DURATION,
|
||||
index: parseInt(index, 10),
|
||||
duration: parseFloat(duration, 10),
|
||||
};
|
||||
}
|
||||
|
||||
export function updateVideoStartTime(index, startTime) {
|
||||
return {
|
||||
type: UPDATE_VIDEO_START_TIME,
|
||||
index: parseInt(index, 10),
|
||||
startTime: parseFloat(startTime, 10),
|
||||
};
|
||||
}
|
||||
|
||||
export function setVideoEnd(index, end) {
|
||||
return {
|
||||
type: SET_VIDEO_END,
|
||||
index: parseInt(index, 10),
|
||||
end,
|
||||
};
|
||||
}
|
||||
|
||||
export function removeVideos() {
|
||||
return {
|
||||
type: REMOVE_VIDEOS,
|
||||
};
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Onboarding constants
|
||||
*
|
||||
*/
|
||||
|
||||
export const GET_VIDEOS = 'StrapiAdmin/Onboarding/GET_VIDEOS';
|
||||
export const GET_VIDEOS_SUCCEEDED =
|
||||
'StrapiAdmin/Onboarding/GET_VIDEOS_SUCCEEDED';
|
||||
export const SHOULD_OPEN_MODAL = 'StrapiAdmin/Onboarding/SHOULD_OPEN_MODAL';
|
||||
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';
|
||||
@ -1,217 +1,136 @@
|
||||
/**
|
||||
*
|
||||
* Onboarding
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cn from 'classnames';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators, compose } from 'redux';
|
||||
import React, { useEffect, useReducer, memo } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { GlobalContext } from 'strapi-helper-plugin';
|
||||
import injectSaga from '../../utils/injectSaga';
|
||||
import injectReducer from '../../utils/injectReducer';
|
||||
import OnboardingVideo from '../../components/OnboardingVideo';
|
||||
import axios from 'axios';
|
||||
import cn from 'classnames';
|
||||
import { useGlobalContext } from 'strapi-helper-plugin';
|
||||
import formatVideoArray from './utils/formatAndStoreVideoArray';
|
||||
import Video from './Video';
|
||||
import Wrapper from './Wrapper';
|
||||
import {
|
||||
getVideos,
|
||||
onClick,
|
||||
removeVideos,
|
||||
setVideoDuration,
|
||||
setVideoEnd,
|
||||
updateVideoStartTime,
|
||||
} from './actions';
|
||||
import makeSelectOnboarding from './selectors';
|
||||
import reducer from './reducer';
|
||||
import saga from './saga';
|
||||
import init from './init';
|
||||
import reducer, { initialState } from './reducer';
|
||||
|
||||
/* eslint-disable react/no-array-index-key */
|
||||
const OnboardingVideos = () => {
|
||||
const { emitEvent } = useGlobalContext();
|
||||
const [reducerState, dispatch] = useReducer(reducer, initialState, init);
|
||||
const { isLoading, isOpen, videos } = reducerState.toJS();
|
||||
|
||||
export class Onboarding extends React.Component {
|
||||
state = { showVideos: false };
|
||||
useEffect(() => {
|
||||
const getData = async () => {
|
||||
try {
|
||||
const { data } = await axios.get('https://strapi.io/videos', {
|
||||
timeout: 1000,
|
||||
});
|
||||
const { didWatchVideos, videos } = formatVideoArray(data);
|
||||
|
||||
componentDidMount() {
|
||||
this.props.getVideos();
|
||||
dispatch({
|
||||
type: 'GET_DATA_SUCCEEDED',
|
||||
didWatchVideos,
|
||||
videos,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
dispatch({
|
||||
type: 'HIDE_VIDEO_ONBOARDING',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getData();
|
||||
}, []);
|
||||
|
||||
// Hide the player in case of request error
|
||||
if (isLoading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { shouldOpenModal } = this.props;
|
||||
|
||||
if (shouldOpenModal !== prevProps.shouldOpenModal && shouldOpenModal) {
|
||||
this.handleOpenModal();
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.props.removeVideos();
|
||||
}
|
||||
|
||||
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 });
|
||||
};
|
||||
|
||||
handleOpenModal = () => this.setState({ showVideos: true });
|
||||
|
||||
handleVideosToggle = () => {
|
||||
this.setState(prevState => ({ showVideos: !prevState.showVideos }));
|
||||
|
||||
const { showVideos } = this.state;
|
||||
const eventName = showVideos
|
||||
const handleClick = () => {
|
||||
const eventName = isOpen
|
||||
? 'didOpenGetStartedVideoContainer'
|
||||
: 'didCloseGetStartedVideoContainer';
|
||||
|
||||
this.context.emitEvent(eventName);
|
||||
dispatch({ type: 'SET_IS_OPEN' });
|
||||
emitEvent(eventName);
|
||||
};
|
||||
const handleClickOpenVideo = videoIndexToOpen => {
|
||||
dispatch({
|
||||
type: 'TOGGLE_VIDEO_MODAL',
|
||||
videoIndexToOpen,
|
||||
});
|
||||
};
|
||||
const handleUpdateVideoStartTime = (videoIndex, elapsedTime) => {
|
||||
dispatch({
|
||||
type: 'UPDATE_VIDEO_STARTED_TIME_AND_PLAYED_INFOS',
|
||||
videoIndex,
|
||||
elapsedTime,
|
||||
});
|
||||
};
|
||||
const setVideoDuration = (videoIndex, duration) => {
|
||||
dispatch({
|
||||
type: 'SET_VIDEO_DURATION',
|
||||
duration,
|
||||
videoIndex,
|
||||
});
|
||||
};
|
||||
|
||||
updateCurrentTime = (index, current, duration) => {
|
||||
this.props.updateVideoStartTime(index, current);
|
||||
const hasVideos = videos.length > 0;
|
||||
const className = hasVideos ? 'visible' : 'hidden';
|
||||
|
||||
const percent = (current * 100) / duration;
|
||||
const video = this.props.videos[index];
|
||||
|
||||
if (percent >= 80) {
|
||||
if (video.end === false) {
|
||||
this.updateEnd(index);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
updateEnd = index => {
|
||||
this.props.setVideoEnd(index, true);
|
||||
};
|
||||
|
||||
static contextType = GlobalContext;
|
||||
|
||||
render() {
|
||||
const { videos, onClick, setVideoDuration } = this.props;
|
||||
const { showVideos } = this.state;
|
||||
const style = showVideos ? {} : { maxWidth: 0 };
|
||||
|
||||
return (
|
||||
<Wrapper
|
||||
style={style}
|
||||
className={cn(videos.length > 0 ? 'visible' : 'hidden')}
|
||||
>
|
||||
<div
|
||||
style={style}
|
||||
className={cn(
|
||||
'videosContent',
|
||||
this.state.showVideos ? 'shown' : 'hide'
|
||||
)}
|
||||
>
|
||||
<div className="videosHeader">
|
||||
<p>
|
||||
<FormattedMessage id="app.components.Onboarding.title" />
|
||||
</p>
|
||||
{videos.length && (
|
||||
<p>
|
||||
{Math.floor(
|
||||
(videos.filter(v => v.end).length * 100) / videos.length
|
||||
)}
|
||||
<FormattedMessage id="app.components.Onboarding.label.completed" />
|
||||
</p>
|
||||
return (
|
||||
<Wrapper className={className} isOpen={isOpen}>
|
||||
<div className={cn('videosContent', isOpen ? 'shown' : 'hide')}>
|
||||
<div className="videosHeader">
|
||||
<p>
|
||||
<FormattedMessage id="app.components.Onboarding.title" />
|
||||
</p>
|
||||
<p>
|
||||
{Math.floor(
|
||||
(videos.filter(v => v.end).length * 100) / videos.length
|
||||
)}
|
||||
</div>
|
||||
<ul className="onboardingList">
|
||||
{videos.map((video, i) => {
|
||||
return (
|
||||
<OnboardingVideo
|
||||
key={i}
|
||||
id={i}
|
||||
video={video}
|
||||
onClick={onClick}
|
||||
setVideoDuration={setVideoDuration}
|
||||
getVideoCurrentTime={this.updateCurrentTime}
|
||||
didPlayVideo={this.didPlayVideo}
|
||||
didStopVideo={this.didStopVideo}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<FormattedMessage id="app.components.Onboarding.label.completed" />
|
||||
</p>
|
||||
</div>
|
||||
<ul className="onboardingList">
|
||||
{videos.map((video, index) => {
|
||||
return (
|
||||
<Video
|
||||
key={video.id || index}
|
||||
id={index}
|
||||
video={video}
|
||||
onClick={() => handleClickOpenVideo(index)}
|
||||
setVideoDuration={(_, duration) => {
|
||||
setVideoDuration(index, duration);
|
||||
}}
|
||||
getVideoCurrentTime={(_, elapsedTime) => {
|
||||
handleUpdateVideoStartTime(index, elapsedTime);
|
||||
}}
|
||||
didPlayVideo={(_, elapsedTime) => {
|
||||
const eventName = `didPlay${index}GetStartedVideo`;
|
||||
|
||||
<div className="openBtn">
|
||||
<button
|
||||
onClick={this.handleVideosToggle}
|
||||
className={this.state.showVideos ? 'active' : ''}
|
||||
type="button"
|
||||
>
|
||||
<i className="fa fa-question" />
|
||||
<i className="fa fa-times" />
|
||||
<span />
|
||||
</button>
|
||||
</div>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
emitEvent(eventName, { timestamp: elapsedTime });
|
||||
}}
|
||||
didStopVideo={(_, elapsedTime) => {
|
||||
const eventName = `didStop${index}Video`;
|
||||
|
||||
Onboarding.defaultProps = {
|
||||
onClick: () => {},
|
||||
removeVideos: () => {},
|
||||
setVideoDuration: () => {},
|
||||
setVideoEnd: () => {},
|
||||
shouldOpenModal: false,
|
||||
videos: [],
|
||||
updateVideoStartTime: () => {},
|
||||
};
|
||||
|
||||
Onboarding.propTypes = {
|
||||
getVideos: PropTypes.func.isRequired,
|
||||
onClick: PropTypes.func,
|
||||
removeVideos: PropTypes.func,
|
||||
setVideoDuration: PropTypes.func,
|
||||
setVideoEnd: PropTypes.func,
|
||||
shouldOpenModal: PropTypes.bool,
|
||||
updateVideoStartTime: PropTypes.func,
|
||||
videos: PropTypes.array,
|
||||
};
|
||||
|
||||
const mapStateToProps = makeSelectOnboarding();
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return bindActionCreators(
|
||||
{
|
||||
getVideos,
|
||||
onClick,
|
||||
setVideoDuration,
|
||||
updateVideoStartTime,
|
||||
setVideoEnd,
|
||||
removeVideos,
|
||||
},
|
||||
dispatch
|
||||
emitEvent(eventName, { timestamp: elapsedTime });
|
||||
}}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="openBtn">
|
||||
<button
|
||||
onClick={handleClick}
|
||||
className={isOpen ? 'active' : ''}
|
||||
type="button"
|
||||
>
|
||||
<i className="fa fa-question" />
|
||||
<i className="fa fa-times" />
|
||||
<span />
|
||||
</button>
|
||||
</div>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const withConnect = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
);
|
||||
|
||||
/* Remove this line if the container doesn't have a route and
|
||||
* check the documentation to see how to create the container's store
|
||||
*/
|
||||
const withReducer = injectReducer({ key: 'onboarding', reducer });
|
||||
|
||||
/* Remove the line below the container doesn't have a route and
|
||||
* check the documentation to see how to create the container's store
|
||||
*/
|
||||
const withSaga = injectSaga({ key: 'onboarding', saga });
|
||||
|
||||
export default compose(
|
||||
withReducer,
|
||||
withSaga,
|
||||
withConnect
|
||||
)(Onboarding);
|
||||
export default memo(OnboardingVideos);
|
||||
|
||||
@ -1,78 +1,67 @@
|
||||
/*
|
||||
*
|
||||
* Onboarding reducer
|
||||
*
|
||||
*/
|
||||
|
||||
import { fromJS } from 'immutable';
|
||||
import {
|
||||
GET_VIDEOS_SUCCEEDED,
|
||||
SHOULD_OPEN_MODAL,
|
||||
ON_CLICK,
|
||||
SET_VIDEOS_DURATION,
|
||||
UPDATE_VIDEO_START_TIME,
|
||||
SET_VIDEO_END,
|
||||
REMOVE_VIDEOS,
|
||||
} from './constants';
|
||||
|
||||
const initialState = fromJS({
|
||||
videos: fromJS([]),
|
||||
isLoading: true,
|
||||
isOpen: false,
|
||||
videos: [],
|
||||
});
|
||||
|
||||
function onboardingReducer(state = initialState, action) {
|
||||
const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case GET_VIDEOS_SUCCEEDED:
|
||||
return state.update('videos', () => fromJS(action.videos));
|
||||
case SHOULD_OPEN_MODAL:
|
||||
return state.update('shouldOpenModal', () => action.opened);
|
||||
case ON_CLICK:
|
||||
return state.updateIn(['videos'], list => {
|
||||
return list.reduce((acc, current, index) => {
|
||||
if (index === action.index) {
|
||||
return acc.updateIn([index, 'isOpen'], v => !v);
|
||||
case 'GET_DATA_SUCCEEDED':
|
||||
return state
|
||||
.update('isOpen', () => !action.didWatchVideos)
|
||||
.update('isLoading', () => false)
|
||||
.update('videos', () => fromJS(action.videos));
|
||||
case 'SET_IS_OPEN':
|
||||
return state.update('isOpen', v => !v);
|
||||
case 'SET_VIDEO_DURATION':
|
||||
return state.updateIn(['videos', action.videoIndex, 'duration'], () => {
|
||||
return parseFloat(action.duration, 10);
|
||||
});
|
||||
case 'TOGGLE_VIDEO_MODAL':
|
||||
return state.update('videos', list => {
|
||||
return list.map((item, index) => {
|
||||
if (index === action.videoIndexToOpen) {
|
||||
return item.update('isOpen', v => !v);
|
||||
}
|
||||
|
||||
return acc.updateIn([index, 'isOpen'], () => false);
|
||||
}, list);
|
||||
return item.set('isOpen', false);
|
||||
});
|
||||
});
|
||||
case SET_VIDEOS_DURATION:
|
||||
return state.updateIn(
|
||||
['videos', action.index, 'duration'],
|
||||
() => action.duration
|
||||
case 'UPDATE_VIDEO_STARTED_TIME_AND_PLAYED_INFOS': {
|
||||
const updatedState = state.updateIn(
|
||||
['videos', action.videoIndex],
|
||||
video => {
|
||||
const elapsedTime = parseFloat(action.elapsedTime, 10);
|
||||
const videoDuration = parseFloat(video.get('duration', 10));
|
||||
const percentElapsedTime = (elapsedTime * 100) / videoDuration;
|
||||
|
||||
return video
|
||||
.update('startTime', () => elapsedTime)
|
||||
.update('end', oldValue => {
|
||||
if (oldValue === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return percentElapsedTime > 80;
|
||||
});
|
||||
}
|
||||
);
|
||||
case UPDATE_VIDEO_START_TIME: {
|
||||
const storedVideos = JSON.parse(localStorage.getItem('videos'));
|
||||
const videos = state.updateIn(['videos'], list => {
|
||||
return list.reduce((acc, current, index) => {
|
||||
if (index === action.index) {
|
||||
storedVideos[index].startTime = action.startTime;
|
||||
|
||||
return acc.updateIn([index, 'startTime'], () => action.startTime);
|
||||
}
|
||||
const videos = updatedState
|
||||
.get('videos')
|
||||
.map(video => video.set('isOpen', false));
|
||||
|
||||
storedVideos[index].startTime = 0;
|
||||
// Update the local storage
|
||||
localStorage.setItem('videos', JSON.stringify(videos.toJS()));
|
||||
|
||||
return acc.updateIn([index, 'startTime'], () => 0);
|
||||
}, list);
|
||||
});
|
||||
|
||||
localStorage.setItem('videos', JSON.stringify(storedVideos));
|
||||
|
||||
return videos;
|
||||
return updatedState;
|
||||
}
|
||||
case SET_VIDEO_END: {
|
||||
const storedVideos = JSON.parse(localStorage.getItem('videos'));
|
||||
|
||||
storedVideos[action.index].end = action.end;
|
||||
localStorage.setItem('videos', JSON.stringify(storedVideos));
|
||||
|
||||
return state.updateIn(['videos', action.index, 'end'], () => action.end);
|
||||
}
|
||||
case REMOVE_VIDEOS:
|
||||
return initialState;
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default onboardingReducer;
|
||||
export default reducer;
|
||||
export { initialState };
|
||||
|
||||
@ -1,72 +0,0 @@
|
||||
/* eslint-disable no-console */
|
||||
import { request } from 'strapi-helper-plugin';
|
||||
import { all, call, fork, takeLatest, put } from 'redux-saga/effects';
|
||||
|
||||
import { GET_VIDEOS } from './constants';
|
||||
import { getVideosSucceeded, shouldOpenModal } from './actions';
|
||||
|
||||
function* getVideos() {
|
||||
try {
|
||||
const data = yield call(
|
||||
request,
|
||||
'https://strapi.io/videos',
|
||||
{
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
false,
|
||||
true,
|
||||
{ noAuth: true }
|
||||
);
|
||||
|
||||
const storedVideo = JSON.parse(localStorage.getItem('videos')) || null;
|
||||
|
||||
const videos = data
|
||||
.map(video => {
|
||||
const { end, startTime } = storedVideo
|
||||
? storedVideo.find(v => v.order === video.order)
|
||||
: { end: false, startTime: 0 };
|
||||
|
||||
return {
|
||||
...video,
|
||||
duration: null,
|
||||
end,
|
||||
isOpen: false,
|
||||
key: video.order,
|
||||
startTime,
|
||||
};
|
||||
})
|
||||
.sort((a, b) => a.order - b.order);
|
||||
|
||||
localStorage.setItem('videos', JSON.stringify(videos));
|
||||
|
||||
yield put(getVideosSucceeded(videos));
|
||||
|
||||
const isFirstTime = JSON.parse(localStorage.getItem('onboarding')) || null;
|
||||
|
||||
if (isFirstTime === null) {
|
||||
yield new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, 500);
|
||||
});
|
||||
|
||||
yield put(shouldOpenModal(true));
|
||||
localStorage.setItem('onboarding', true);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err); // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
function* defaultSaga() {
|
||||
try {
|
||||
yield all([fork(takeLatest, GET_VIDEOS, getVideos)]);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
export default defaultSaga;
|
||||
@ -1,25 +0,0 @@
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
/**
|
||||
* Direct selector to the onboarding state domain
|
||||
*/
|
||||
const selectOnboardingDomain = () => (state) => state.get('onboarding');
|
||||
|
||||
/**
|
||||
* Other specific selectors
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Default selector used by Onboarding
|
||||
*/
|
||||
|
||||
const makeSelectOnboarding = () => createSelector(
|
||||
selectOnboardingDomain(),
|
||||
(substate) => substate.toJS()
|
||||
);
|
||||
|
||||
export default makeSelectOnboarding;
|
||||
export {
|
||||
selectOnboardingDomain,
|
||||
};
|
||||
@ -1,6 +1,6 @@
|
||||
const formatVideosArray = array => {
|
||||
const alreadyFetchedVideos = JSON.parse(localStorage.getItem('videos')) || [];
|
||||
const didWatchVideos = alreadyFetchedVideos.length !== array.length;
|
||||
const didWatchVideos = alreadyFetchedVideos.length === array.length;
|
||||
let videos;
|
||||
|
||||
if (!didWatchVideos) {
|
||||
@ -1,127 +0,0 @@
|
||||
import styled, { keyframes } from 'styled-components';
|
||||
|
||||
const fadeIn = keyframes`
|
||||
0% {
|
||||
width: auto;
|
||||
height: auto;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
5% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
`;
|
||||
const fadeOut = keyframes`
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
60% {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const Wrapper = styled.div`
|
||||
position: fixed;
|
||||
right: 15px;
|
||||
bottom: 15px;
|
||||
button,
|
||||
button:focus,
|
||||
a {
|
||||
cursor: pointer;
|
||||
outline: 0;
|
||||
}
|
||||
p {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.videosHeader {
|
||||
padding: 25px 15px 0 15px;
|
||||
p {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
width: 50%;
|
||||
font-family: Lato;
|
||||
font-weight: bold;
|
||||
font-size: 11px;
|
||||
color: #5c5f66;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
&:last-of-type {
|
||||
color: #5a9e06;
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.visible {
|
||||
opacity: 1;
|
||||
z-index: 10;
|
||||
}
|
||||
&.hidden {
|
||||
opacity: 0;
|
||||
}
|
||||
.videosContent {
|
||||
min-width: 320px;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 15px;
|
||||
background-color: white;
|
||||
box-shadow: 0 2px 4px 0 #e3e9f3;
|
||||
border-radius: 3px;
|
||||
overflow: hidden;
|
||||
&.shown {
|
||||
animation: ${fadeIn} 0.5s forwards;
|
||||
}
|
||||
&.hide {
|
||||
min-width: 0;
|
||||
animation: ${fadeOut} 0.5s forwards;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0 0 10px 0;
|
||||
margin-bottom: 0;
|
||||
list-style: none;
|
||||
}
|
||||
}
|
||||
.openBtn {
|
||||
float: right;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
button {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
color: white;
|
||||
background: #0e7de7;
|
||||
box-shadow: 0px 2px 4px 0px rgba(227, 233, 243, 1);
|
||||
i,
|
||||
svg {
|
||||
margin: auto;
|
||||
}
|
||||
i:last-of-type,
|
||||
svg:last-of-type {
|
||||
display: none;
|
||||
}
|
||||
&.active {
|
||||
i:first-of-type,
|
||||
svg:first-of-type {
|
||||
display: none;
|
||||
}
|
||||
i:last-of-type,
|
||||
svg:last-of-type {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export default Wrapper;
|
||||
@ -1,44 +0,0 @@
|
||||
import React, { useEffect, useReducer } from 'react';
|
||||
import axios from 'axios';
|
||||
import Wrapper from './Wrapper';
|
||||
import formatVideoArray from './utils/formatAndStoreVideoArray';
|
||||
import init from './init';
|
||||
import reducer, { initialState } from './reducer';
|
||||
|
||||
const OnboardingVideos = () => {
|
||||
const [reducerState, dispatch] = useReducer(reducer, initialState, init);
|
||||
const { shouldShowVideoOnboarding } = reducerState.toJS();
|
||||
|
||||
useEffect(() => {
|
||||
const getData = async () => {
|
||||
try {
|
||||
const { data } = await axios.get('https://strapi.io/videos', {
|
||||
timeout: 1000,
|
||||
});
|
||||
const { didWatchVideos, videos } = formatVideoArray(data);
|
||||
|
||||
dispatch({
|
||||
type: 'GET_DATA_SUCCEEDED',
|
||||
didWatchVideos,
|
||||
videos,
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
dispatch({
|
||||
type: 'HIDE_VIDEO_ONBOARDING',
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getData();
|
||||
}, []);
|
||||
|
||||
// Hide the player in case of request error
|
||||
if (!shouldShowVideoOnboarding) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <Wrapper>COMING SOON</Wrapper>;
|
||||
};
|
||||
|
||||
export default OnboardingVideos;
|
||||
@ -1,19 +0,0 @@
|
||||
import { fromJS } from 'immutable';
|
||||
|
||||
const initialState = fromJS({
|
||||
isOpened: false,
|
||||
shouldShowVideoOnboarding: true,
|
||||
videos: [],
|
||||
});
|
||||
|
||||
const reducer = (state, action) => {
|
||||
switch (action.type) {
|
||||
case 'HIDE_VIDEO_ONBOARDING':
|
||||
return state.update('shouldShowVideoOnboarding', () => false);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default reducer;
|
||||
export { initialState };
|
||||
Loading…
x
Reference in New Issue
Block a user