fetch all topics, show random topic with 4 videos in home container

This commit is contained in:
Sanjay Bhangar 2018-12-07 18:59:16 +02:00
parent 461ed1af37
commit e7471420d2
16 changed files with 319 additions and 117 deletions

View File

@ -1,3 +1,4 @@
export const TEST_ACTION = 'TEST_ACTION';
export const START_LOADING_VIDEOS = 'START_LOADING_VIDEOS';
export const RECEIVE_VIDEOS = 'RECEIVE_VIDEOS';
export const START_LOADING_ALL_TOPICS = 'START_LOADING_ALL_TOPICS';
export const LOADED_ALL_TOPICS = 'LOADED_ALL_TOPICS';
export const START_LOADING_RANDOM_TOPIC = 'START_LOADING_RANDOM_TOPIC';
export const LOADED_RANDOM_TOPIC = 'LOADED_RANDOM_TOPIC';

7
src/actions/errors.js Normal file
View File

@ -0,0 +1,7 @@
export function APIError(error) {
return {
type: 'ERROR',
payload: error
}
}

View File

@ -1,49 +0,0 @@
import config from '../config';
import { START_LOADING_VIDEOS, RECEIVE_VIDEOS } from './action_types';
function fetchVideos() {
console.log('fetchVideos called');
return dispatch => {
console.log('inside dispatch function');
dispatch(startLoadingVideos());
const params = {
"keys":["editable","modified","title","source","project","topic","language","duration","id"],
"query": {
"conditions":[],
"operator":"&"
},
"range":[0,100],
"sort": [{"key":"title","operator":"+"}]
};
let formData = new FormData();
formData.append('action', 'find');
formData.append('data', JSON.stringify(params));
fetch(config.apiUrl, {
method: 'POST',
// headers: {
// "Content-Type": "application/x-www-form-urlencoded"
// },
body: formData
})
.then(response => response.json())
.then(json => {
dispatch(receiveVideos(json.data.items));
});
}
}
function startLoadingVideos() {
return {
type: START_LOADING_VIDEOS
}
}
function receiveVideos(videos) {
return {
type: RECEIVE_VIDEOS,
payload: videos
}
}
export default fetchVideos;

View File

@ -1,10 +0,0 @@
import { TEST_ACTION } from './action_types';
function testAction(data) {
return {
type: TEST_ACTION,
payload: data
}
}
export default testAction;

64
src/actions/topics.js Normal file
View File

@ -0,0 +1,64 @@
import { fetchAllTopics, fetchVideosByTopic } from '../utils/api';
import { APIError } from './errors';
import { getItem, setItem } from '../utils/safe-storage';
import getRandomTopic from '../utils/get-random-topic';
import {
START_LOADING_ALL_TOPICS,
LOADED_ALL_TOPICS,
START_LOADING_RANDOM_TOPIC,
LOADED_RANDOM_TOPIC
} from './action_types';
export function getAllTopics() {
return dispatch => {
dispatch(startLoadingAllTopics());
fetchAllTopics()
.then(topics => {
dispatch(loadedTopics(topics));
}).catch(error => {
dispatch(APIError(error));
})
};
};
function startLoadingAllTopics() {
return {
type: START_LOADING_ALL_TOPICS
};
};
function loadedTopics(topics) {
setItem('allTopics', JSON.stringify(topics));
setItem('topicsUpdatedAt', new Date());
return {
type: LOADED_ALL_TOPICS,
payload: topics
};
};
export function getRandomTopicVideos(allTopics, numVideos=4) {
return dispatch => {
const randomTopic = getRandomTopic(allTopics);
dispatch(loadingRandomTopicVideos());
fetchVideosByTopic(randomTopic, 0, 4)
.then(videos => {
dispatch(loadedRandomTopicVideos(randomTopic, videos));
});
};
};
function loadedRandomTopicVideos(topic, videos) {
return {
type: LOADED_RANDOM_TOPIC,
payload: {
topic: topic,
videos: videos
}
};
};
function loadingRandomTopicVideos() {
return {
type: START_LOADING_RANDOM_TOPIC
};
};

View File

@ -1,5 +1,6 @@
import Grid from 'react-css-grid'
import React from 'react';
import PropTypes from 'prop-types';
import VideoItem from "./VideoItem";
import SectionHeading from "./SectionHeading";
@ -8,14 +9,14 @@ class RandomTopic extends React.Component {
render() {
return (
<section>
<SectionHeading title="Topic Name"/>
<SectionHeading title={ this.props.topicName } />
<Grid
width={450}
gap={16}>
<VideoItem/>
<VideoItem/>
<VideoItem/>
<VideoItem/>
{ this.props.videos.map(video =>
<VideoItem id={ video.id } title={ video.title } />
)}
</Grid>
<a href="/topics">See All videos</a>
@ -25,4 +26,10 @@ class RandomTopic extends React.Component {
}
}
RandomTopic.propTypes = {
topicName: PropTypes.string,
videos: PropTypes.array,
loading: PropTypes.bool
};
export default (RandomTopic);

View File

@ -1,15 +1,15 @@
import React from 'react';
import PropTypes from 'prop-types';
import { getThumbnail } from '../utils/video';
class VideoItem extends React.Component {
render() {
return (
<section className="video-item">
<section className="video-thumbnail-container">
<img className="video-thumbnail" src={require('../assets/images/img2.png')} alt=""/>
<img className="video-thumbnail" src={ getThumbnail(this.props.id) } alt=""/>
</section>
<h3 className="video-title">Random Title</h3>
<h3 className="video-title">{ this.props.title }</h3>
</section>
)
}

View File

@ -1,55 +1,57 @@
import React from 'react';
import {connect} from 'react-redux';
import testAction from '../actions/test';
import fetchVideos from '../actions/fetchVideos';
import { getAllTopics, getRandomTopicVideos } from '../actions/topics';
import Search from '../components/Search';
import Footer from '../components/Footer';
import RandomLocation from "../components/RandomLocation";
import RandomDate from "../components/RandomDate";
import RandomTopic from "../components/RandomTopic";
import Header from "../components/Header";
class Home extends React.Component {
clickBtn() {
this.props.testAction('some new value');
componentDidMount() {
if (this.props.allTopics && this.props.allTopics.length > 0) {
this.props.getRandomTopicVideos(this.props.allTopics);
} else {
this.props.getAllTopics();
}
}
componentWillReceiveProps(nextProps) {
//FIXME: this is quite ugly
// We are trying to figure out if allTopics is loaded but no request made for random topic yet,
// and then we want to load the random topic. But there should be a cleaner way to do this.
if (!nextProps.loadingAllTopics && !this.props.randomTopic && (!this.props.loadingRandomTopic || nextProps.loadingRandomTopic)) {
this.props.getRandomTopicVideos(this.props.allTopics);
}
}
render() {
return (
<div>
<RandomTopic/>
<RandomTopic
topicName={ this.props.randomTopic || ''}
videos = { this.props.randomTopicVideos || [] }
loading = { this.props.loadingAllTopics || this.props.loadingRandomTopic }
/>
<RandomLocation/>
<RandomDate/>
</div>
);
//
// <div>
// This is home. {this.props.test}
// <div>
// <Link to="/topics">Go to topics</Link>
// </div>
// <div>
// <button onClick={this.clickBtn.bind(this)}>Click me</button>
// <button onClick={this.props.fetchVideos}>Fetch videos</button>
// </div>
// <div>
// {this.props.loading && 'Loading...'}
// {this.props.videos.map(video => {
// return (<div key={video.id}>{video.title}</div>);
// })}
// </div>
// </div>
}
}
const mapStateToProps = state => ({
test: state.home.test,
loading: state.home.loading,
videos: state.home.videos
allTopics: state.topics.allTopics,
loadingAllTopics: state.topics.loadingAllTopics,
randomTopic: state.topics.randomTopic,
loadingRandomTopic: state.topics.loadingRandomTopic,
randomTopicVideos: state.topics.randomTopicVideos
});
const mapDispatchToProps = dispatch => ({
testAction: value => dispatch(testAction(value)),
fetchVideos: () => dispatch(fetchVideos())
getAllTopics: () => dispatch(getAllTopics()),
getRandomTopicVideos: allTopics => dispatch(getRandomTopicVideos(allTopics))
});
export default connect(mapStateToProps, mapDispatchToProps)(Home);

View File

@ -1,9 +1,9 @@
import { combineReducers } from 'redux';
import HomeReducer from './home'
import TopicsReducer from './topics'
const rootReducer = combineReducers({
home: HomeReducer
topics: TopicsReducer
});
export default rootReducer;

35
src/reducers/topics.js Normal file
View File

@ -0,0 +1,35 @@
import {
START_LOADING_ALL_TOPICS,
LOADED_ALL_TOPICS,
START_LOADING_RANDOM_TOPIC,
LOADED_RANDOM_TOPIC
} from '../actions/action_types';
export default function(state={}, action) {
switch (action.type) {
case START_LOADING_ALL_TOPICS:
return Object.assign({}, state, { loadingAllTopics: true });
case LOADED_ALL_TOPICS:
return Object.assign({}, state, {
loadingAllTopics: false,
allTopics: action.payload
});
case START_LOADING_RANDOM_TOPIC:
return Object.assign({}, state, {
loadingRandomTopic: true
});
case LOADED_RANDOM_TOPIC:
return Object.assign({}, state, {
loadingRandomTopic: false,
randomTopic: action.payload.topic,
randomTopicVideos: action.payload.videos
});
default:
return state;
}
};

View File

@ -1,15 +1,9 @@
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers';
import getInitialState from '../utils/get-initial-state';
// Add initial state here
const initialState = {
'home': {
'test': 'foo',
'loading': false,
'videos': []
}
};
const initialState = getInitialState();
const store = createStore(
rootReducer,

View File

@ -102,7 +102,7 @@ export function fetchVideosByX(key, value, startRange, endRange, sortKey='random
"query": {
"conditions": [{
"key": key,
"value": encodeURIComponent(value),
"value": value,
"operator": "=="
}],
"operator": "&"

View File

@ -0,0 +1,14 @@
import { getItem } from './safe-storage';
export default function getInitialState() {
let initialState = {
topics: {}
};
const allTopics = getItem('allTopics');
if (allTopics) {
initialState.topics.allTopics = JSON.parse(allTopics);
initialState.topics.loadingAllTopics = false;
}
return initialState;
}

View File

@ -0,0 +1,6 @@
export default function getRandomTopic(allTopics, minVideos=4) {
const validTopics = allTopics.filter(topic => topic.items >= minVideos);
console.log('valid topics', validTopics);
return validTopics[Math.floor(Math.random() * validTopics.length)].name;
}

40
src/utils/safe-storage.js Normal file
View File

@ -0,0 +1,40 @@
// Transparent wrapper over Window.localStorage
// Adheres to the Web Storage API:
// https://developer.mozilla.org/en-US/docs/Web/API/Storage
/**
* Wraps localStorage.getItem in a try/catch. Return null
* if the key does not exist or localStorage fails.
*/
function getItem(key) {
try {
return localStorage.getItem(key) || null;
} catch (err) {
console.warn('Could not read from localStorage.');
return null;
}
}
/**
* Wraps localStorage.setItem in a try/catch.
*/
function setItem(key, value) {
try {
localStorage.setItem(key, value);
} catch (err) {
console.warn('Could not write to localStorage.');
}
}
/**
* Wraps localStorage.removeItem in a try/catch.
*/
function removeItem(key) {
try {
localStorage.removeItem(key);
} catch (err) {
console.warn('Could not delete from localStorage.');
}
}
export { getItem, setItem, removeItem };

109
yarn.lock
View File

@ -1088,7 +1088,7 @@ arrify@^1.0.0, arrify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
asap@~2.0.6:
asap@~2.0.3, asap@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
@ -1630,6 +1630,13 @@ buffer@^4.3.0:
ieee754 "^1.1.4"
isarray "^1.0.0"
buffer@^5.0.3:
version "5.2.1"
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.2.1.tgz#dd57fa0f109ac59c602479044dca7b8b3d0b71d6"
dependencies:
base64-js "^1.0.2"
ieee754 "^1.1.4"
builtin-modules@^1.0.0:
version "1.1.1"
resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
@ -2056,6 +2063,10 @@ core-js@2.5.7, core-js@^2.4.0, core-js@^2.5.0:
version "2.5.7"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
core-js@^1.0.0:
version "1.2.7"
resolved "http://registry.npmjs.org/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
core-util-is@1.0.2, core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@ -2139,6 +2150,10 @@ crypto-browserify@^3.11.0:
randombytes "^2.0.0"
randomfill "^1.0.3"
css-color-keywords@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05"
css-color-names@0.0.4, css-color-names@^0.0.4:
version "0.0.4"
resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0"
@ -2197,6 +2212,14 @@ css-selector-tokenizer@^0.7.0:
fastparse "^1.1.1"
regexpu-core "^1.0.0"
css-to-react-native@^2.0.3:
version "2.2.2"
resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-2.2.2.tgz#c077d0f7bf3e6c915a539e7325821c9dd01f9965"
dependencies:
css-color-keywords "^1.0.0"
fbjs "^0.8.5"
postcss-value-parser "^3.3.0"
css-tree@1.0.0-alpha.28:
version "1.0.0-alpha.28"
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.28.tgz#8e8968190d886c9477bc8d61e96f61af3f7ffa7f"
@ -2668,6 +2691,12 @@ encodeurl@~1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
encoding@^0.1.11:
version "0.1.12"
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
dependencies:
iconv-lite "~0.4.13"
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
version "1.4.1"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43"
@ -3146,6 +3175,18 @@ fb-watchman@^2.0.0:
dependencies:
bser "^2.0.0"
fbjs@^0.8.5, fbjs@^0.8.9:
version "0.8.17"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
dependencies:
core-js "^1.0.0"
isomorphic-fetch "^2.1.1"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
setimmediate "^1.0.5"
ua-parser-js "^0.7.18"
figgy-pudding@^3.1.0, figgy-pudding@^3.5.1:
version "3.5.1"
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790"
@ -3710,6 +3751,10 @@ hoek@4.x.x:
version "4.2.1"
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb"
hoist-non-react-statics@^1.2.0:
version "1.2.0"
resolved "http://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb"
hoist-non-react-statics@^2.5.0:
version "2.5.5"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
@ -3857,7 +3902,7 @@ iconv-lite@0.4.23:
dependencies:
safer-buffer ">= 2.1.2 < 3"
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4:
iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@^0.4.4, iconv-lite@~0.4.13:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
dependencies:
@ -4252,7 +4297,7 @@ is-root@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.0.0.tgz#838d1e82318144e5a6f77819d90207645acc7019"
is-stream@^1.1.0:
is-stream@^1.0.1, is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
@ -4312,6 +4357,13 @@ isobject@^3.0.0, isobject@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
isomorphic-fetch@^2.1.1:
version "2.2.1"
resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
dependencies:
node-fetch "^1.0.1"
whatwg-fetch ">=0.10.0"
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
@ -5384,6 +5436,13 @@ no-case@^2.2.0:
dependencies:
lower-case "^1.1.1"
node-fetch@^1.0.1:
version "1.7.3"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
dependencies:
encoding "^0.1.11"
is-stream "^1.0.1"
node-forge@0.7.5:
version "0.7.5"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.5.tgz#6c152c345ce11c52f465c2abd957e8639cd674df"
@ -6545,6 +6604,12 @@ promise@8.0.2:
dependencies:
asap "~2.0.6"
promise@^7.1.1:
version "7.3.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
dependencies:
asap "~2.0.3"
prompts@^0.1.9:
version "0.1.14"
resolved "https://registry.yarnpkg.com/prompts/-/prompts-0.1.14.tgz#a8e15c612c5c9ec8f8111847df3337c9cbd443b2"
@ -6552,7 +6617,7 @@ prompts@^0.1.9:
kleur "^2.0.1"
sisteransi "^0.1.1"
prop-types@^15.6.1, prop-types@^15.6.2:
prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.6.1, prop-types@^15.6.2:
version "15.6.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.2.tgz#05d5ca77b4453e985d60fc7ff8c859094a497102"
dependencies:
@ -6702,6 +6767,13 @@ react-app-polyfill@^0.1.3:
raf "3.4.0"
whatwg-fetch "3.0.0"
react-css-grid@^2.0.0-0:
version "2.0.0-0"
resolved "https://registry.yarnpkg.com/react-css-grid/-/react-css-grid-2.0.0-0.tgz#df89242decc1f072425f154f254f176bb60500a7"
dependencies:
prop-types "^15.5.10"
styled-components "^2.1.2"
react-dev-utils@^6.1.1:
version "6.1.1"
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-6.1.1.tgz#a07e3e8923c4609d9f27e5af5207e3ca20724895"
@ -6734,7 +6806,6 @@ react-dev-utils@^6.1.1:
react-dom@^16.6.3:
version "16.6.3"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.6.3.tgz#8fa7ba6883c85211b8da2d0efeffc9d3825cccc0"
integrity sha512-8ugJWRCWLGXy+7PmNh8WJz3g1TaTUt1XyoIcFN+x0Zbkoz+KKdUyx1AQLYJdbFXjuF41Nmjn5+j//rxvhFjgSQ==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
@ -6845,7 +6916,6 @@ react-scripts@2.1.1:
react@^16.6.3:
version "16.6.3"
resolved "https://registry.yarnpkg.com/react/-/react-16.6.3.tgz#25d77c91911d6bbdd23db41e70fb094cc1e0871c"
integrity sha512-zCvmH2vbEolgKxtqXL2wmGCUxUyNheYn/C+PD1YAjfxHC54+MhdruyhO7QieQrYsYeTxrn93PM2y0jRH1zEExw==
dependencies:
loose-envify "^1.1.0"
object-assign "^4.1.1"
@ -7358,7 +7428,7 @@ set-value@^2.0.0:
is-plain-object "^2.0.3"
split-string "^3.0.1"
setimmediate@^1.0.4:
setimmediate@^1.0.4, setimmediate@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
@ -7741,6 +7811,19 @@ style-loader@0.23.0:
loader-utils "^1.1.0"
schema-utils "^0.4.5"
styled-components@^2.1.2:
version "2.4.1"
resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-2.4.1.tgz#663bd0485d4b6ab46f946210dc03d2398d1ade74"
dependencies:
buffer "^5.0.3"
css-to-react-native "^2.0.3"
fbjs "^0.8.9"
hoist-non-react-statics "^1.2.0"
is-plain-object "^2.0.1"
prop-types "^15.5.4"
stylis "^3.4.0"
supports-color "^3.2.3"
stylehacks@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.1.tgz#3186595d047ab0df813d213e51c8b94e0b9010f2"
@ -7749,11 +7832,15 @@ stylehacks@^4.0.0:
postcss "^7.0.0"
postcss-selector-parser "^3.0.0"
stylis@^3.4.0:
version "3.5.4"
resolved "https://registry.yarnpkg.com/stylis/-/stylis-3.5.4.tgz#f665f25f5e299cf3d64654ab949a57c768b73fbe"
supports-color@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
supports-color@^3.1.2:
supports-color@^3.1.2, supports-color@^3.2.3:
version "3.2.3"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6"
dependencies:
@ -7989,6 +8076,10 @@ typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
ua-parser-js@^0.7.18:
version "0.7.19"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b"
uglify-es@^3.3.4:
version "3.3.9"
resolved "https://registry.yarnpkg.com/uglify-es/-/uglify-es-3.3.9.tgz#0c1c4f0700bed8dbc124cdb304d2592ca203e677"
@ -8362,7 +8453,7 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5:
dependencies:
iconv-lite "0.4.24"
whatwg-fetch@3.0.0, whatwg-fetch@^3.0.0:
whatwg-fetch@3.0.0, whatwg-fetch@>=0.10.0, whatwg-fetch@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"