That's a spicy meatball mammamia

This commit is contained in:
Hamcha 2020-09-29 10:52:39 +02:00
parent 1ec48eaf65
commit efb127dedd
Signed by: hamcha
GPG Key ID: 41467804B19A3315
29 changed files with 536 additions and 34 deletions

33
.eslintrc.js Normal file
View File

@ -0,0 +1,33 @@
module.exports = {
env: {
browser: true,
es2020: true,
},
extends: [
"airbnb-base",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
],
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint", "prettier", "react-hooks"],
rules: {
"prettier/prettier": "error",
"one-var": "off", // WHO CARES?????
"no-use-before-define": "off", // YOU ARE WRONG
"import/extensions": "off",
"import/no-unresolved": "off",
"no-param-reassign": "off",
"no-alert": "off",
"no-console": "off",
"func-names": "off",
"dot-notation": ["error", { allowPattern: "^[A-Z][a-z]+$" }],
"@typescript-eslint/ban-ts-comment": [
"error",
{
"ts-expect-error": "allow-with-description",
},
],
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
},
};

4
.prettierrc Normal file
View File

@ -0,0 +1,4 @@
{
"singleQuote": false,
"endOfLine": "auto"
}

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -5,16 +5,22 @@
"license": "MIT",
"dependencies": {
"@inlet/react-pixi": "^5.1.4",
"@reduxjs/toolkit": "^1.4.0",
"@types/howler": "^2.2.1",
"@types/react": "^16.9.49",
"@types/react-dom": "^16.9.8",
"@types/react-redux": "^7.1.9",
"animejs": "^3.2.0",
"eslint-plugin-react-hooks": "^4.1.2",
"hotkeys-js": "^3.8.1",
"howler": "^2.2.0",
"parcel-bundler": "^1.12.4",
"pixi.js": "^5.3.3",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-redux": "^7.2.1",
"redux": "^4.0.5",
"redux-devtools-extension": "^2.13.8",
"typescript": "^4.0.3"
},
"scripts": {

View File

29
src/game/lib/loader.tsx Normal file
View File

@ -0,0 +1,29 @@
import { Loader, ILoaderOptions } from "pixi.js";
import { useEffect, useState } from "react";
export type ResourceList = Record<
string,
string | { name: string; options: ILoaderOptions }
>;
export default function useLoader(resources: ResourceList) {
const loader = new Loader();
const [state, setState] = useState({ loaded: false, resources: null });
useEffect(() => {
for (const res in resources) {
const param = resources[res];
if (typeof param === "string") {
loader.add(res, param);
} else {
loader.add(res, param.name, param.options);
}
}
loader.load((loader, resources) => {
setState({ loaded: true, resources });
});
}, []);
return state;
}

32
src/game/scenes/Game.tsx Normal file
View File

@ -0,0 +1,32 @@
import React, { Fragment } from "react";
import useLoader from "~game/lib/Loader";
import InGameSpaceBG from "./backgrounds/InGameSpaceBG";
// Resources, this will make TS go crazy!
//@ts-expect-error Image resource
import NoiseSpaceFine from "~/../assets/images/noise/space_fine.png";
//@ts-expect-error Image resource
import NoiseSpaceSparse from "~/../assets/images/noise/space_sparse.png";
export default function Game() {
const { loaded, resources } = useLoader({
bg_fine: NoiseSpaceFine,
bg_sparse: NoiseSpaceSparse,
});
if (!loaded) {
return <Fragment></Fragment>;
}
return (
<Fragment>
<InGameSpaceBG
textures={{
noise_fine: resources.bg_fine,
noise_sparse: resources.bg_sparse,
}}
/>
</Fragment>
);
}

View File

@ -0,0 +1,5 @@
import React, { Fragment } from "react";
export default function LoadingScreen() {
return <Fragment></Fragment>;
}

View File

@ -0,0 +1,35 @@
import React, { Fragment } from "react";
import useLoader from "~game/lib/Loader";
import InGameSpaceBG from "./backgrounds/InGameSpaceBG";
// Resources, this will make TS go crazy!
//@ts-expect-error Image resource
import NoiseSpaceFine from "~/../assets/images/noise/space_fine.png";
//@ts-expect-error Image resource
import NoiseSpaceSparse from "~/../assets/images/noise/space_sparse.png";
import LoadingScreen from "./LoadingScreen";
import useUILayer from "~ui/utils/useLayer";
export default function MapEditor() {
const { loaded, resources } = useLoader({
bg_fine: NoiseSpaceFine,
bg_sparse: NoiseSpaceSparse,
});
const id = useUILayer({ type: "MapEditor" }, loaded);
if (!loaded) {
return <LoadingScreen />;
}
return (
<Fragment>
<InGameSpaceBG
textures={{
noise_fine: resources.bg_fine,
noise_sparse: resources.bg_sparse,
}}
/>
</Fragment>
);
}

View File

@ -0,0 +1,10 @@
import React, { Fragment } from "react";
import MenuBackground from "./backgrounds/MenuBackground";
export default function TitleScreen() {
return (
<Fragment>
<MenuBackground />
</Fragment>
);
}

View File

@ -0,0 +1,30 @@
import React, { useEffect } from "react";
import { Sprite, useApp, useTick } from "@inlet/react-pixi";
import InGameSpace, { SpaceTextures } from "~graphics/filters/InGameSpace";
export interface InGameSpaceBGProps {
textures: SpaceTextures;
}
export default function InGameSpaceBG({ textures }: InGameSpaceBGProps) {
const app = useApp();
const scale = 1;
const menubg = new InGameSpace(app.renderer, scale, textures);
useTick((delta) => {
menubg.update(delta);
menubg.render();
});
useEffect(() => {
const handleResize = () => {
menubg.rescale(scale);
};
app.renderer.on("resize", handleResize);
return () => {
app.renderer.off("resize", handleResize);
};
});
return <Sprite texture={menubg.texture} scale={{ x: scale, y: scale }} />;
}

View File

@ -1,5 +1,5 @@
import * as React from "react";
import MenuBackgroundFilter from "../graphics/filters/MenuBackground";
import React, { useEffect } from "react";
import MenuBackgroundFilter from "~graphics/filters/MenuBackground";
import { Sprite, useApp, useTick } from "@inlet/react-pixi";
export default function () {
@ -12,8 +12,14 @@ export default function () {
menubg.render();
});
window.addEventListener("resize", () => {
menubg.rescale(scale);
useEffect(() => {
const handleResize = () => {
menubg.rescale(scale);
};
app.renderer.on("resize", handleResize);
return () => {
app.renderer.off("resize", handleResize);
};
});
return <Sprite texture={menubg.texture} scale={{ x: scale, y: scale }} />;

View File

@ -0,0 +1,49 @@
import { Filter, Renderer, Texture, WRAP_MODES } from "pixi.js";
import RTTShader from "../utils/RTTShader";
//@ts-expect-error Frag file
import filter_src from "./frag/InGameSpace.frag";
export interface SpaceTextures {
noise_sparse;
noise_fine;
}
export default class InGameSpaceFilter extends RTTShader {
private elapsed: number;
private filter: Filter;
constructor(renderer: Renderer, scale: number, textures: SpaceTextures) {
const noise_sparse = textures.noise_sparse.texture as Texture;
const noise_fine = textures.noise_fine.texture as Texture;
noise_sparse.baseTexture.wrapMode = WRAP_MODES.REPEAT;
noise_fine.baseTexture.wrapMode = WRAP_MODES.REPEAT;
const filter = new Filter(null, filter_src, {
time: 0,
ires: [renderer.width / scale, renderer.height / scale],
noise_sparse,
noise_fine,
scroll_speed: 0,
warp_boost: 0,
warp_opacity: 0,
});
super(renderer, filter, scale);
this.elapsed = 0;
this.filter = filter;
}
rescale(scale: number) {
super.rescale(scale);
this.filter.uniforms.ires = [
this.renderer.width / scale,
this.renderer.height / scale,
];
}
update(delta: number) {
this.filter.uniforms.time = this.elapsed;
this.elapsed += delta / 60;
}
}

View File

@ -1,4 +1,4 @@
import * as PIXI from "pixi.js";
import { Filter, Renderer } from "pixi.js";
import RTTShader from "../utils/RTTShader";
//@ts-expect-error Frag file
@ -6,10 +6,10 @@ import filter_src from "./frag/MenuBackground.frag";
export default class MenuBackgroundFilter extends RTTShader {
private elapsed: number;
private filter: PIXI.Filter;
private filter: Filter;
constructor(renderer: PIXI.Renderer, scale: number) {
const filter = new PIXI.Filter(null, filter_src, {
constructor(renderer: Renderer, scale: number) {
const filter = new Filter(null, filter_src, {
iTime: 0,
iResolution: [renderer.width / scale, renderer.height / scale],
});

View File

@ -3,26 +3,26 @@ precision mediump float;
uniform sampler2D noise_sparse;
uniform sampler2D noise_fine;
uniform vec2 iResolution;
uniform vec2 ires;
uniform float scroll_speed;
uniform float warp_boost;
uniform float warp_opacity;
uniform float time;
const float noise_sparse_scale = 0.0007;
const float noise_fine_scale = 0.003;
const float noise_fine_scale = 0.004;
const float warp_clouds_scale = 0.0003;
const vec2 scroll_dir = vec2(1,0);
const vec2 blink_scroll = vec2(50.0, 0);
const float PI = 3.1415;
void main() {
vec2 uv=gl_FragCoord.xy/iResolution.xy;
vec2 uv=gl_FragCoord.xy/ires.xy;
float scroll = scroll_speed * time;
float sparse = texture2D(noise_sparse, (gl_FragCoord.xy + scroll_dir * scroll)*noise_sparse_scale).r;
sparse = ((sparse * sparse) - 0.2) * 1.2;
float fine = texture2D(noise_fine, (gl_FragCoord.xy + scroll_dir * scroll)*noise_fine_scale).r;
fine = fine - 0.6;
fine = fine - 0.62;
if (fine > 0.) {
fine = fine * 10.0;
}

View File

@ -1,17 +1,17 @@
import * as PIXI from "pixi.js";
import { RenderTexture, Container, Renderer, Filter } from "pixi.js";
export default class RTTShader {
readonly texture: PIXI.RenderTexture;
readonly container: PIXI.Container;
readonly renderer: PIXI.Renderer;
readonly texture: RenderTexture;
readonly container: Container;
readonly renderer: Renderer;
constructor(renderer: PIXI.Renderer, filter: PIXI.Filter, scale: number = 2) {
constructor(renderer: Renderer, filter: Filter, scale: number = 2) {
this.renderer = renderer;
this.texture = PIXI.RenderTexture.create({
this.texture = RenderTexture.create({
width: this.renderer.width / scale,
height: this.renderer.height / scale,
});
this.container = new PIXI.Container();
this.container = new Container();
this.container.filterArea = this.texture.frame;
this.container.filters = [filter];
}

View File

@ -1,8 +1,12 @@
import * as React from "react";
import * as PIXI from "pixi.js";
import React from "react";
import ReactDOM from "react-dom";
import { Stage, useApp } from "@inlet/react-pixi";
import MenuBackground from "./game/MenuBackground";
import { Provider } from "react-redux";
import { Stage } from "@inlet/react-pixi";
import { configureStore } from "@reduxjs/toolkit";
import UI from "~ui/UI";
import reducer from "~store/reducer";
import MapEditor from "~game/scenes/MapEditor";
PIXI.settings.SCALE_MODE = PIXI.SCALE_MODES.NEAREST;
@ -13,14 +17,18 @@ const mounted = (app: PIXI.Application) => {
app.queueResize();
};
const store = configureStore({ reducer });
ReactDOM.render(
<React.Fragment>
<Stage options={{ resolution: 1, resizeTo: window }} onMount={mounted}>
<MenuBackground />
<Provider store={store}>
<MapEditor />
</Provider>
</Stage>
<div id="ui">
<p>Hello!</p>
</div>
<Provider store={store}>
<UI />
</Provider>
</React.Fragment>,
document.getElementById("app")
);

6
src/store/reducer.ts Normal file
View File

@ -0,0 +1,6 @@
import { combineReducers } from "@reduxjs/toolkit";
import uiReducer from "./ui/reducer";
const reducer = combineReducers({ ui: uiReducer });
export default reducer;

5
src/store/state.ts Normal file
View File

@ -0,0 +1,5 @@
import UIStore from "./ui/state";
export interface GameStore {
ui: UIStore;
}

56
src/store/ui/reducer.ts Normal file
View File

@ -0,0 +1,56 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { UILayer } from "~ui/UI";
import UIStore from "./state";
const initialState: UIStore = {
layers: [],
};
export interface AddLayerPayload {
id: string;
data: UILayer;
}
export interface LayerActionPayload {
id: string;
}
const uiSlice = createSlice({
name: "ui",
initialState,
reducers: {
addLayer(state, action: PayloadAction<AddLayerPayload>) {
state.layers.push({ ...action.payload, visible: false });
},
removeLayer(state, action: PayloadAction<LayerActionPayload>) {
const index = state.layers.findIndex(
(layer) => layer.id == action.payload.id
);
if (index < 0) {
return;
}
state.layers.splice(index, 1);
},
showLayer(state, action: PayloadAction<LayerActionPayload>) {
const index = state.layers.findIndex(
(layer) => layer.id == action.payload.id
);
if (index < 0) {
return;
}
state.layers[index].visible = true;
},
hideLayer(state, action: PayloadAction<LayerActionPayload>) {
const index = state.layers.findIndex(
(layer) => layer.id == action.payload.id
);
if (index < 0) {
return;
}
state.layers[index].visible = false;
},
},
});
export const { addLayer, removeLayer, showLayer, hideLayer } = uiSlice.actions;
export default uiSlice.reducer;

5
src/store/ui/state.ts Normal file
View File

@ -0,0 +1,5 @@
import { LayerList } from "~ui/UI";
export default interface UIStore {
layers: LayerList;
}

View File

@ -1,4 +0,0 @@
import * as React from "react";
import ReactDOM from "react-dom";
ReactDOM.render(<p>Hello</p>, document.getElementById("ui"));

13
src/ui/MapEditor.tsx Normal file
View File

@ -0,0 +1,13 @@
import React from "react";
export interface MapEditorLayer {
type: "MapEditor";
}
export default function MapEditor() {
return (
<div>
<p>HEY HEY</p>
</div>
);
}

View File

@ -1 +0,0 @@
import * as React from "react";

28
src/ui/UI.tsx Normal file
View File

@ -0,0 +1,28 @@
import React from "react";
import { useSelector } from "react-redux";
import { GameStore } from "~store/state";
import MapEditor, { MapEditorLayer } from "./MapEditor";
export type UILayer = MapEditorLayer;
export type LayerList = { id: string; data: UILayer; visible: boolean }[];
function renderLayer(data: UILayer) {
switch (data.type) {
case "MapEditor":
return <MapEditor />;
default:
throw new Error("unknown or invalid ui layer");
}
}
export default function UI() {
const layers = useSelector<GameStore, LayerList>((state) => state.ui.layers);
return (
<div id="ui">
{layers.map(({ id, data }) => (
<div key={id}>{renderLayer(data)}</div>
))}
</div>
);
}

32
src/ui/utils/useLayer.ts Normal file
View File

@ -0,0 +1,32 @@
import { useEffect, useState } from "react";
import { useDispatch } from "react-redux";
import { UILayer } from "~ui/UI";
import { addLayer, removeLayer, showLayer, hideLayer } from "~store/ui/reducer";
export default function useUILayer(data: UILayer, show: boolean) {
const dispatch = useDispatch();
const id = data.type + "-" + Math.random().toString(32).slice(2);
const [visible, setVisible] = useState(show);
useEffect(() => {
dispatch(addLayer({ id, data }));
return () => {
dispatch(removeLayer({ id }));
};
}, []);
useEffect(() => {
if (visible) {
dispatch(showLayer({ id }));
} else {
dispatch(hideLayer({ id }));
}
}, [visible]);
if (show != visible) {
setVisible(show);
}
return id;
}

13
tsconfig.json Normal file
View File

@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "ES2020",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"jsx": "react",
"baseUrl": ".",
"paths": {
"~*": ["./src/*"]
}
}
}

View File

@ -837,7 +837,7 @@
"@babel/types" "^7.4.4"
esutils "^2.0.2"
"@babel/runtime@^7.4.4", "@babel/runtime@^7.8.4":
"@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4":
version "7.11.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.11.2.tgz#f549c13c754cc40b87644b9fa9f09a6a95fe0736"
integrity sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==
@ -1263,6 +1263,24 @@
eventemitter3 "^3.1.0"
url "^0.11.0"
"@reduxjs/toolkit@^1.4.0":
version "1.4.0"
resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.4.0.tgz#ee2e2384cc3d1d76780d844b9c2da3580d32710d"
integrity sha512-hkxQwVx4BNVRsYdxjNF6cAseRmtrkpSlcgJRr3kLUcHPIAMZAmMJkXmHh/eUEGTMqPzsYpJLM7NN2w9fxQDuGw==
dependencies:
immer "^7.0.3"
redux "^4.0.0"
redux-thunk "^2.3.0"
reselect "^4.0.0"
"@types/hoist-non-react-statics@^3.3.0":
version "3.3.1"
resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f"
integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==
dependencies:
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
"@types/howler@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@types/howler/-/howler-2.2.1.tgz#c95314724a8e970b1b29a731861975f39987b9ca"
@ -1285,6 +1303,16 @@
dependencies:
"@types/react" "*"
"@types/react-redux@^7.1.9":
version "7.1.9"
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.9.tgz#280c13565c9f13ceb727ec21e767abe0e9b4aec3"
integrity sha512-mpC0jqxhP4mhmOl3P4ipRsgTgbNofMRXJb08Ms6gekViLj61v1hOZEKWDCyWsdONr6EjEA6ZHXC446wdywDe0w==
dependencies:
"@types/hoist-non-react-statics" "^3.3.0"
"@types/react" "*"
hoist-non-react-statics "^3.3.0"
redux "^4.0.0"
"@types/react@*", "@types/react@^16.9.49":
version "16.9.49"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.49.tgz#09db021cf8089aba0cdb12a49f8021a69cce4872"
@ -2586,6 +2614,11 @@ escodegen@~1.9.0:
optionalDependencies:
source-map "~0.6.1"
eslint-plugin-react-hooks@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.1.2.tgz#2eb53731d11c95826ef7a7272303eabb5c9a271e"
integrity sha512-ykUeqkGyUGgwTtk78C0o8UG2fzwmgJ0qxBGPp2WqRKsTwcLuVf01kTDRAtOsd4u6whX2XOC8749n2vPydP82fg==
esprima@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633"
@ -3101,6 +3134,13 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
hoist-non-react-statics@^3.3.0:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
dependencies:
react-is "^16.7.0"
hotkeys-js@^3.8.1:
version "3.8.1"
resolved "https://registry.yarnpkg.com/hotkeys-js/-/hotkeys-js-3.8.1.tgz#fa7051f73bf1dc92a8b8d580a40b247f91966376"
@ -3206,6 +3246,11 @@ ieee754@^1.1.4:
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84"
integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==
immer@^7.0.3:
version "7.0.9"
resolved "https://registry.yarnpkg.com/immer/-/immer-7.0.9.tgz#28e7552c21d39dd76feccd2b800b7bc86ee4a62e"
integrity sha512-Vs/gxoM4DqNAYR7pugIxi0Xc8XAun/uy7AQu4fLLqaTBHxjOP9pJ266Q9MWA/ly4z6rAFZbvViOtihxUZ7O28A==
import-fresh@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546"
@ -4733,7 +4778,7 @@ process@^0.11.10:
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
prop-types@^15.6.2:
prop-types@^15.6.2, prop-types@^15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@ -4843,7 +4888,7 @@ react-dom@^16.13.1:
prop-types "^15.6.2"
scheduler "^0.19.1"
react-is@^16.8.1:
react-is@^16.7.0, react-is@^16.8.1, react-is@^16.9.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@ -4858,6 +4903,17 @@ react-reconciler@0.25.1:
prop-types "^15.6.2"
scheduler "^0.19.1"
react-redux@^7.2.1:
version "7.2.1"
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.2.1.tgz#8dedf784901014db2feca1ab633864dee68ad985"
integrity sha512-T+VfD/bvgGTUA74iW9d2i5THrDQWbweXP0AVNI8tNd1Rk5ch1rnMiJkDD67ejw7YBKM4+REvcvqRuWJb7BLuEg==
dependencies:
"@babel/runtime" "^7.5.5"
hoist-non-react-statics "^3.3.0"
loose-envify "^1.4.0"
prop-types "^15.7.2"
react-is "^16.9.0"
react@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"
@ -4915,6 +4971,24 @@ readdirp@~3.4.0:
dependencies:
picomatch "^2.2.1"
redux-devtools-extension@^2.13.8:
version "2.13.8"
resolved "https://registry.yarnpkg.com/redux-devtools-extension/-/redux-devtools-extension-2.13.8.tgz#37b982688626e5e4993ff87220c9bbb7cd2d96e1"
integrity sha512-8qlpooP2QqPtZHQZRhx3x3OP5skEV1py/zUdMY28WNAocbafxdG2tRD1MWE7sp8obGMNYuLWanhhQ7EQvT1FBg==
redux-thunk@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==
redux@^4.0.0, redux@^4.0.5:
version "4.0.5"
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f"
integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==
dependencies:
loose-envify "^1.4.0"
symbol-observable "^1.2.0"
regenerate-unicode-properties@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec"
@ -5033,6 +5107,11 @@ request@^2.88.0:
tunnel-agent "^0.6.0"
uuid "^3.3.2"
reselect@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7"
integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==
resolve-from@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748"
@ -5521,6 +5600,11 @@ svgo@^1.0.0, svgo@^1.3.2:
unquote "~1.1.1"
util.promisify "~1.0.0"
symbol-observable@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
symbol-tree@^3.2.2:
version "3.2.4"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"