working-ish
This commit is contained in:
parent
8952f9da4a
commit
9b60cd178f
13 changed files with 1127 additions and 46 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -21,3 +21,5 @@
|
|||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
.env.local
|
17
package.json
17
package.json
|
@ -3,13 +3,26 @@
|
|||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "^1.0.4",
|
||||
"@types/jest": "24.0.22",
|
||||
"@types/lodash": "^4.14.149",
|
||||
"@types/node": "12.12.7",
|
||||
"@types/qs": "^6.9.0",
|
||||
"@types/react": "16.9.11",
|
||||
"@types/react-beautiful-dnd": "^11.0.3",
|
||||
"@types/react-dom": "16.9.4",
|
||||
"react": "^16.11.0",
|
||||
"react-dom": "^16.11.0",
|
||||
"@types/react-redux": "^7.1.5",
|
||||
"@types/webpack-env": "^1.14.1",
|
||||
"lodash": "^4.17.15",
|
||||
"qs": "^6.9.1",
|
||||
"react": "^0.0.0-experimental-38dd17ab9",
|
||||
"react-beautiful-dnd": "^12.1.1",
|
||||
"react-dnd": "^9.4.0",
|
||||
"react-dnd-html5-backend": "^9.4.0",
|
||||
"react-dom": "^0.0.0-experimental-38dd17ab9",
|
||||
"react-redux": "^7.1.3",
|
||||
"react-scripts": "3.2.0",
|
||||
"redux": "^4.0.4",
|
||||
"typescript": "3.7.2"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
42
src/App.css
42
src/App.css
|
@ -20,3 +20,45 @@
|
|||
.App-link {
|
||||
color: #09d3ac;
|
||||
}
|
||||
|
||||
.sp-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.sp {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.sp-col {
|
||||
display: block;
|
||||
flex: 1;
|
||||
border: 1px solid black;
|
||||
height: calc(100% - 60px);
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.sp-track {
|
||||
display: block;
|
||||
/* overflow-x: hidden; */
|
||||
/* height: 1.6em; */
|
||||
margin: 0.2em 0;
|
||||
/* overflow: hidden; */
|
||||
/* height: 1.3em; */
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sp-track:hover {
|
||||
background-color: #78acf1;
|
||||
}
|
||||
|
||||
html, body, #root {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
62
src/App.tsx
62
src/App.tsx
|
@ -1,26 +1,44 @@
|
|||
import React from 'react';
|
||||
import logo from './logo.svg';
|
||||
import './App.css';
|
||||
import React, { useReducer, useState, Suspense } from "react";
|
||||
import qs from "qs";
|
||||
import "./App.css";
|
||||
import Showplanner from "./showplanner";
|
||||
|
||||
const App: React.FC = () => {
|
||||
return (
|
||||
<div className="App">
|
||||
<header className="App-header">
|
||||
<img src={logo} className="App-logo" alt="logo" />
|
||||
<p>
|
||||
Edit <code>src/App.tsx</code> and save to reload.
|
||||
</p>
|
||||
<a
|
||||
className="App-link"
|
||||
href="https://reactjs.org"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Learn React
|
||||
</a>
|
||||
</header>
|
||||
</div>
|
||||
);
|
||||
const forceReducer = (state: boolean) => !state;
|
||||
function useForceUpdate() {
|
||||
const [_, action] = useReducer(forceReducer, false);
|
||||
return () => action(null);
|
||||
}
|
||||
|
||||
const App: React.FC = () => {
|
||||
const [inputVal, setInputVal] = useState("");
|
||||
const force = useForceUpdate();
|
||||
|
||||
function cont() {
|
||||
window.location.search = `?timeslot_id=${inputVal}`;
|
||||
force();
|
||||
}
|
||||
|
||||
const q = qs.parse(window.location.search, { ignoreQueryPrefix: true });
|
||||
if ("timeslot_id" in q) {
|
||||
return (
|
||||
<Suspense fallback={<div>Loading...</div>}>
|
||||
<Showplanner timeslotId={q.timeslot_id} />
|
||||
</Suspense>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
<h1>Welcome to showplanner2</h1>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="enter a timeslot id"
|
||||
value={inputVal}
|
||||
onChange={e => setInputVal(e.target.value)}
|
||||
/>
|
||||
<button onClick={cont}>Continue</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default App;
|
||||
|
|
176
src/api.ts
Normal file
176
src/api.ts
Normal file
|
@ -0,0 +1,176 @@
|
|||
import qs from "qs";
|
||||
import { convertModelToFormData, urlEncode } from "./lib/utils";
|
||||
|
||||
const MYRADIO_BASE_URL =
|
||||
process.env.REACT_APP_MYRADIO_BASE || "https://ury.org.uk/api/v2";
|
||||
const MYRADIO_API_KEY = process.env.REACT_APP_MYRADIO_KEY!;
|
||||
|
||||
class ApiException extends Error {}
|
||||
|
||||
export async function myradioApiRequest(
|
||||
endpoint: string,
|
||||
method: "GET" | "POST" | "PUT",
|
||||
params: any
|
||||
): Promise<any> {
|
||||
let req = null;
|
||||
if (method === "GET") {
|
||||
req = fetch(
|
||||
MYRADIO_BASE_URL +
|
||||
endpoint +
|
||||
qs.stringify(
|
||||
{
|
||||
...params,
|
||||
api_key: MYRADIO_API_KEY
|
||||
},
|
||||
{ addQueryPrefix: true }
|
||||
)
|
||||
);
|
||||
} else {
|
||||
const body = JSON.stringify(params);
|
||||
console.log(body);
|
||||
req = fetch(MYRADIO_BASE_URL + endpoint + "?api_key=" + MYRADIO_API_KEY, {
|
||||
method,
|
||||
body,
|
||||
headers: {
|
||||
"Content-Type": "application/json; charset=UTF-8"
|
||||
}
|
||||
});
|
||||
}
|
||||
const json = await (await req).json();
|
||||
if (json.status === "OK") {
|
||||
return json.payload;
|
||||
} else {
|
||||
console.error(json.payload);
|
||||
throw new ApiException("Request failed!");
|
||||
}
|
||||
}
|
||||
|
||||
interface Album {
|
||||
title: string;
|
||||
recordid: number;
|
||||
artist: string;
|
||||
cdid: number | null;
|
||||
date_added: string;
|
||||
date_released: string;
|
||||
// TODO
|
||||
}
|
||||
|
||||
interface TimeslotItemBase {
|
||||
timeslotitemid: string;
|
||||
channel: number;
|
||||
weight: number;
|
||||
title: string;
|
||||
length: string;
|
||||
trackid: number;
|
||||
}
|
||||
|
||||
interface TimeslotItemCentral {
|
||||
type: "central";
|
||||
artist: string;
|
||||
intro: number;
|
||||
clean: boolean;
|
||||
digitised: boolean;
|
||||
album: Album;
|
||||
}
|
||||
|
||||
interface TimeslotItemAux {
|
||||
type: "aux";
|
||||
summary: string;
|
||||
recordid: string;
|
||||
auxid: string;
|
||||
}
|
||||
|
||||
export type TimeslotItem = TimeslotItemBase &
|
||||
(TimeslotItemCentral | TimeslotItemAux);
|
||||
|
||||
export type Showplan = TimeslotItem[][];
|
||||
|
||||
export function getShowplan(showId: number): Promise<Showplan> {
|
||||
return myradioApiRequest(
|
||||
`/timeslot/${showId.toString(10)}/showplan`,
|
||||
"GET",
|
||||
{}
|
||||
).then(res => {
|
||||
console.log(res);
|
||||
return Object.keys(res).map(x => res[x]);
|
||||
});
|
||||
}
|
||||
|
||||
function wrapPromise<T, TArgs>(factory: (...args: TArgs[]) => Promise<T>) {
|
||||
let status = "pending";
|
||||
let result: T;
|
||||
let suspender: Promise<void>;
|
||||
return {
|
||||
read(...args: TArgs[]) {
|
||||
if (!(suspender instanceof Promise)) {
|
||||
suspender = factory(...args).then(
|
||||
r => {
|
||||
status = "success";
|
||||
result = r;
|
||||
},
|
||||
e => {
|
||||
status = "error";
|
||||
result = e;
|
||||
}
|
||||
);
|
||||
}
|
||||
if (status === "pending") {
|
||||
throw suspender;
|
||||
} else if (status === "error") {
|
||||
throw result;
|
||||
} else if (status === "success") {
|
||||
return result;
|
||||
} else {
|
||||
throw new Error("Can't happen.");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export interface Track {
|
||||
title: string;
|
||||
artist: string;
|
||||
album: Album;
|
||||
trackid: number;
|
||||
length: string;
|
||||
intro: number;
|
||||
clean: boolean;
|
||||
digitised: boolean;
|
||||
}
|
||||
|
||||
export const showPlanResource = wrapPromise<Showplan, number>(getShowplan);
|
||||
|
||||
export function searchForTracks(
|
||||
artist: string,
|
||||
title: string
|
||||
): Promise<Array<Track>> {
|
||||
return myradioApiRequest("/track/search", "GET", {
|
||||
artist,
|
||||
title,
|
||||
limit: 100,
|
||||
digitised: true
|
||||
});
|
||||
}
|
||||
|
||||
export type UpdateOp = {
|
||||
op: "MoveItem";
|
||||
timeslotitemid: string;
|
||||
oldchannel: number;
|
||||
oldweight: number;
|
||||
channel: number;
|
||||
weight: number;
|
||||
};
|
||||
|
||||
interface OpResult {
|
||||
status: boolean;
|
||||
timeslotitemid?: string;
|
||||
}
|
||||
|
||||
export function updateShowplan(
|
||||
timeslotid: number,
|
||||
ops: UpdateOp[]
|
||||
): Promise<OpResult[]> {
|
||||
return myradioApiRequest(`/timeslot/${timeslotid}/updateshowplan`, "PUT", {
|
||||
set: ops
|
||||
});
|
||||
}
|
|
@ -1,10 +1,21 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import * as serviceWorker from './serviceWorker';
|
||||
import React from "react";
|
||||
import ReactDOM from "react-dom";
|
||||
import "./index.css";
|
||||
import App from "./App";
|
||||
import * as serviceWorker from "./serviceWorker";
|
||||
|
||||
ReactDOM.render(<App />, document.getElementById('root'));
|
||||
import store from "./store";
|
||||
import { Provider } from "react-redux";
|
||||
|
||||
function render() {
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>,
|
||||
document.getElementById("root")
|
||||
);
|
||||
}
|
||||
render();
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
|
|
35
src/lib/useDebounce.ts
Normal file
35
src/lib/useDebounce.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
|
||||
// Our hook
|
||||
export default function useDebounce<T>(value: T, delay: number) {
|
||||
// State and setters for debounced value
|
||||
const [debouncedValue, setDebouncedValue] = useState(value);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
// Set debouncedValue to value (passed in) after the specified delay
|
||||
const handler = setTimeout(() => {
|
||||
setDebouncedValue(value);
|
||||
}, delay);
|
||||
|
||||
// Return a cleanup function that will be called every time ...
|
||||
// ... useEffect is re-called. useEffect will only be re-called ...
|
||||
// ... if value changes (see the inputs array below).
|
||||
// This is how we prevent debouncedValue from changing if value is ...
|
||||
// ... changed within the delay period. Timeout gets cleared and restarted.
|
||||
// To put it in context, if the user is typing within our app's ...
|
||||
// ... search box, we don't want the debouncedValue to update until ...
|
||||
// ... they've stopped typing for more than 500ms.
|
||||
return () => {
|
||||
clearTimeout(handler);
|
||||
};
|
||||
},
|
||||
// Only re-call effect if value changes
|
||||
// You could also add the "delay" var to inputs array if you ...
|
||||
// ... need to be able to change that dynamically.
|
||||
[value]
|
||||
);
|
||||
|
||||
return debouncedValue;
|
||||
}
|
||||
|
38
src/lib/utils.ts
Normal file
38
src/lib/utils.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
export function convertModelToFormData(
|
||||
model: any,
|
||||
form: FormData | null = null,
|
||||
namespace = ""
|
||||
): FormData {
|
||||
let formData = form || new FormData();
|
||||
let formKey;
|
||||
|
||||
for (let propertyName in model) {
|
||||
if (!model.hasOwnProperty(propertyName) || !model[propertyName]) continue;
|
||||
let formKey = namespace ? `${namespace}[${propertyName}]` : propertyName;
|
||||
if (model[propertyName] instanceof Date)
|
||||
formData.append(formKey, model[propertyName].toISOString());
|
||||
else if (model[propertyName] instanceof Array) {
|
||||
model[propertyName].forEach((element: any, index: number) => {
|
||||
const tempFormKey = `${formKey}[${index}]`;
|
||||
convertModelToFormData(element, formData, tempFormKey);
|
||||
});
|
||||
} else if (
|
||||
typeof model[propertyName] === "object" &&
|
||||
!(model[propertyName] instanceof File)
|
||||
)
|
||||
convertModelToFormData(model[propertyName], formData, formKey);
|
||||
else formData.append(formKey, model[propertyName].toString());
|
||||
}
|
||||
return formData;
|
||||
}
|
||||
|
||||
export function urlEncode(element: any, key?: string, list?: any[]) {
|
||||
list = list || [];
|
||||
if (typeof element == "object") {
|
||||
for (var idx in element)
|
||||
urlEncode(element[idx], key ? key + "[" + idx + "]" : idx, list);
|
||||
} else {
|
||||
list.push(key + "=" + encodeURIComponent(element));
|
||||
}
|
||||
return list.join("&");
|
||||
}
|
11
src/rootReducer.ts
Normal file
11
src/rootReducer.ts
Normal file
|
@ -0,0 +1,11 @@
|
|||
import { combineReducers } from "@reduxjs/toolkit";
|
||||
|
||||
import ShowplanReducer from "./showplanner/state";
|
||||
|
||||
const rootReducer = combineReducers({
|
||||
showplan: ShowplanReducer
|
||||
});
|
||||
|
||||
export type RootState = ReturnType<typeof rootReducer>;
|
||||
|
||||
export default rootReducer;
|
208
src/showplanner/index.tsx
Normal file
208
src/showplanner/index.tsx
Normal file
|
@ -0,0 +1,208 @@
|
|||
import React, { useState, useReducer, useRef, useEffect } from "react";
|
||||
import { DndProvider, useDrag, useDrop } from "react-dnd";
|
||||
import HTML5Backend from "react-dnd-html5-backend";
|
||||
|
||||
import {
|
||||
showPlanResource,
|
||||
Showplan,
|
||||
TimeslotItem,
|
||||
Track,
|
||||
searchForTracks
|
||||
} from "../api";
|
||||
import { XYCoord } from "dnd-core";
|
||||
import {
|
||||
Droppable,
|
||||
DragDropContext,
|
||||
Draggable,
|
||||
DropResult,
|
||||
ResponderProvided
|
||||
} from "react-beautiful-dnd";
|
||||
import useDebounce from "../lib/useDebounce";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { RootState } from "../rootReducer";
|
||||
import { Plan, PlanItem, getShowplan, itemId, moveItem } from "./state";
|
||||
|
||||
const CML_CACHE: { [recordid_trackid: string]: Track } = {};
|
||||
|
||||
function Item({ item: x, index }: { item: PlanItem | Track; index: number }) {
|
||||
const id = itemId(x);
|
||||
return (
|
||||
<Draggable draggableId={id} index={index} isDragDisabled={"ghostid" in x}>
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
ref={provided.innerRef}
|
||||
key={id}
|
||||
className="sp-track"
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
>
|
||||
{x.title}
|
||||
{"artist" in x && " - " + x.artist}
|
||||
<code>
|
||||
{itemId(x)} {"channel" in x && x.channel + "/" + x.weight}
|
||||
</code>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
}
|
||||
|
||||
function Column({ id, data }: { id: number; data: PlanItem[] }) {
|
||||
return (
|
||||
<Droppable droppableId={id.toString(10)}>
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
className="sp-col"
|
||||
ref={provided.innerRef}
|
||||
{...provided.droppableProps}
|
||||
>
|
||||
{typeof data[id] === "undefined"
|
||||
? null
|
||||
: data
|
||||
.filter(x => x.channel === id)
|
||||
.sort((a, b) => a.weight - b.weight)
|
||||
.map((x, index) => (
|
||||
<Item key={itemId(x)} item={x} index={index} />
|
||||
))}
|
||||
{provided.placeholder}
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
);
|
||||
}
|
||||
|
||||
function CentralMusicLibrary() {
|
||||
const [track, setTrack] = useState("");
|
||||
const debouncedTrack = useDebounce(track, 1000);
|
||||
const [items, setItems] = useState<Track[]>([]);
|
||||
useEffect(() => {
|
||||
if (debouncedTrack === "") {
|
||||
return;
|
||||
}
|
||||
searchForTracks("", track).then(tracks => {
|
||||
tracks.forEach(track => {
|
||||
const id =
|
||||
track.album.recordid.toString(10) + "-" + track.trackid.toString(10);
|
||||
if (!(id in CML_CACHE)) {
|
||||
CML_CACHE[id] = track;
|
||||
}
|
||||
});
|
||||
setItems(tracks);
|
||||
});
|
||||
}, [debouncedTrack]);
|
||||
return (
|
||||
<>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Filter by track..."
|
||||
value={track}
|
||||
onChange={e => setTrack(e.target.value)}
|
||||
/>
|
||||
<Droppable droppableId="$CML">
|
||||
{(provided, snapshot) => (
|
||||
<div ref={provided.innerRef} {...provided.droppableProps}>
|
||||
{items.map((item, index) => (
|
||||
<Item key={item.trackid} item={item} index={index} />
|
||||
))}
|
||||
{provided.placeholder}
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function LibraryColumn() {
|
||||
const [sauce, setSauce] = useState("None");
|
||||
return (
|
||||
<div className="sp-col">
|
||||
<select
|
||||
style={{ width: "100%" }}
|
||||
value={sauce}
|
||||
onChange={e => setSauce(e.target.value)}
|
||||
>
|
||||
<option value={"None"} disabled>
|
||||
Choose a library
|
||||
</option>
|
||||
<option value={"CentralMusicLibrary"}>Central Music Library</option>
|
||||
</select>
|
||||
{sauce === "CentralMusicLibrary" && <CentralMusicLibrary />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const Showplanner: React.FC<{ timeslotId: number }> = function({ timeslotId }) {
|
||||
const {
|
||||
plan: showplan,
|
||||
planLoadError,
|
||||
planLoading,
|
||||
planSaveError,
|
||||
planSaving
|
||||
} = useSelector((state: RootState) => state.showplan);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getShowplan(timeslotId));
|
||||
}, [timeslotId]);
|
||||
|
||||
async function onDragEnd(result: DropResult, provider: ResponderProvided) {
|
||||
if (result.destination!.droppableId[0] === "$") {
|
||||
// pseudo-channel
|
||||
return;
|
||||
}
|
||||
if (result.draggableId[0] === "T") {
|
||||
// this is a track from the CML
|
||||
//TODO
|
||||
} else {
|
||||
// this is a normal move (ghosts aren't draggable)
|
||||
dispatch(
|
||||
moveItem(timeslotId, result.draggableId, [
|
||||
parseInt(result.destination!.droppableId, 10),
|
||||
result.destination!.index
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
if (showplan === null) {
|
||||
return (
|
||||
<div className="sp-container">
|
||||
<h1>Show Planner</h1>
|
||||
{planLoading && (
|
||||
<b>Your plan is loading, please wait just a second...</b>
|
||||
)}
|
||||
{planLoadError !== null && (
|
||||
<>
|
||||
<b>Plan load failed!</b> Please tell Comp that something broke.
|
||||
<p>
|
||||
<code>{planLoadError}</code>
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className="sp-container">
|
||||
<h1>Show Planner</h1>
|
||||
<div className="sp-status">
|
||||
{planSaving && <em>Plan saving...</em>}
|
||||
{planSaveError && (
|
||||
<b>
|
||||
Catastrophe! <code>{planSaveError}</code>
|
||||
</b>
|
||||
)}
|
||||
</div>
|
||||
<div className="sp">
|
||||
<DragDropContext onDragEnd={onDragEnd}>
|
||||
<Column id={0} data={showplan} />
|
||||
<Column id={1} data={showplan} />
|
||||
<Column id={2} data={showplan} />
|
||||
<LibraryColumn />
|
||||
</DragDropContext>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Showplanner;
|
273
src/showplanner/state.ts
Normal file
273
src/showplanner/state.ts
Normal file
|
@ -0,0 +1,273 @@
|
|||
import { TimeslotItem, Track, Showplan } from "../api";
|
||||
import * as api from "../api";
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import { AppThunk } from "../store";
|
||||
import { cloneDeep } from "lodash";
|
||||
|
||||
export interface ItemGhost {
|
||||
title: string;
|
||||
ghostid: string;
|
||||
channel: number;
|
||||
weight: number;
|
||||
}
|
||||
|
||||
export type PlanItem = TimeslotItem | ItemGhost;
|
||||
|
||||
export type Plan = PlanItem[][];
|
||||
|
||||
export function itemId(item: PlanItem | Track) {
|
||||
if ("timeslotitemid" in item) {
|
||||
return item.timeslotitemid;
|
||||
}
|
||||
if ("ghostid" in item) {
|
||||
return "G" + item.ghostid;
|
||||
}
|
||||
if ("trackid" in item) {
|
||||
return "T" + item.album.recordid + "-" + item.trackid;
|
||||
}
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
interface ShowplanState {
|
||||
planLoading: boolean;
|
||||
planLoadError: string | null;
|
||||
plan: null | PlanItem[];
|
||||
planSaving: boolean;
|
||||
planSaveError: string | null;
|
||||
}
|
||||
|
||||
const initialState: ShowplanState = {
|
||||
planLoading: true,
|
||||
planLoadError: null,
|
||||
plan: null,
|
||||
planSaving: false,
|
||||
planSaveError: null
|
||||
};
|
||||
|
||||
const showplan = createSlice({
|
||||
name: "showplan",
|
||||
initialState,
|
||||
reducers: {
|
||||
getShowplanStarting(state, action) {
|
||||
state.planLoadError = null;
|
||||
state.planLoading = true;
|
||||
},
|
||||
getShowplanSuccess(state, action: PayloadAction<PlanItem[]>) {
|
||||
state.plan = action.payload;
|
||||
state.planLoading = false;
|
||||
},
|
||||
getShowplanError(state, action: PayloadAction<string>) {
|
||||
state.planLoading = false;
|
||||
state.planLoadError = action.payload;
|
||||
},
|
||||
setPlanSaving(state, action: PayloadAction<boolean>) {
|
||||
state.planSaving = action.payload;
|
||||
},
|
||||
planSaveError(state, action: PayloadAction<string>) {
|
||||
state.planSaving = false;
|
||||
state.planSaveError = action.payload;
|
||||
},
|
||||
applyOps(state, action: PayloadAction<api.UpdateOp[]>) {
|
||||
if (!state.plan) {
|
||||
return;
|
||||
}
|
||||
action.payload.forEach(op => {
|
||||
switch (op.op) {
|
||||
case "MoveItem":
|
||||
const item = state.plan!.find(
|
||||
x => itemId(x) === op.timeslotitemid
|
||||
)!;
|
||||
item.channel = op.channel;
|
||||
item.weight = op.weight;
|
||||
break;
|
||||
default:
|
||||
throw new Error();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default showplan.reducer;
|
||||
|
||||
export const moveItem = (
|
||||
timeslotid: number,
|
||||
itemid: string,
|
||||
to: [number, number]
|
||||
): AppThunk => async (dispatch, getState) => {
|
||||
// Make a copy of the plan, because we are about to engage in FUCKERY.
|
||||
const plan = cloneDeep(getState().showplan.plan!);
|
||||
const itemToMove = plan.find(x => itemId(x) === itemid)!;
|
||||
if (itemToMove.channel === to[0] && itemToMove.weight === to[1]) {
|
||||
return;
|
||||
}
|
||||
console.log(
|
||||
`Moving item ${itemId(itemToMove)} from ${itemToMove.channel}x${
|
||||
itemToMove.weight
|
||||
} to ${to[0]}x${to[1]}`
|
||||
);
|
||||
dispatch(showplan.actions.setPlanSaving(true));
|
||||
|
||||
const oldChannel = itemToMove.channel;
|
||||
const oldWeight = itemToMove.weight;
|
||||
const [newChannel, newWeight] = to;
|
||||
|
||||
const inc: string[] = [];
|
||||
const dec: string[] = [];
|
||||
|
||||
if (oldChannel === newChannel) {
|
||||
// Moving around in the same channel
|
||||
const itemChan = plan
|
||||
.filter(x => x.channel === oldChannel)
|
||||
.sort((a, b) => a.weight - b.weight);
|
||||
if (oldWeight < newWeight) {
|
||||
// moved the item down (incremented) - everything in between needs decrementing
|
||||
for (let i = oldWeight + 1; i <= newWeight; i++) {
|
||||
dec.push(itemId(itemChan[i]));
|
||||
itemChan[i].weight -= 1;
|
||||
}
|
||||
} else {
|
||||
// moved the item up (decremented) - everything in between needs incrementing
|
||||
for (let i = newWeight; i < oldWeight; i++) {
|
||||
inc.push(itemId(itemChan[i]));
|
||||
itemChan[i].weight += 1;
|
||||
}
|
||||
}
|
||||
itemToMove.channel = newChannel;
|
||||
itemToMove.weight = newWeight;
|
||||
} else {
|
||||
// Moving between channels
|
||||
// So here's the plan.
|
||||
// We're going to temporarily remove the item we're actually moving from the plan
|
||||
// This is because its position becomes nondeterministic when we move it around.
|
||||
plan.splice(plan.indexOf(itemToMove), 1);
|
||||
itemToMove.channel = newChannel;
|
||||
itemToMove.weight = newWeight;
|
||||
|
||||
// First, decrement everything between the old weight and the end of the old channel
|
||||
// (inclusive of old weight, because we've removed the item)
|
||||
const oldChannelData = plan
|
||||
.filter(x => x.channel === oldChannel)
|
||||
.sort((a, b) => a.weight - b.weight);
|
||||
for (let i = oldWeight; i < oldChannelData.length; i++) {
|
||||
const movingItem = oldChannelData[i];
|
||||
movingItem.weight -= 1;
|
||||
dec.push(itemId(movingItem));
|
||||
}
|
||||
|
||||
// Then, increment everything between the new weight and the end of the new channel
|
||||
// (again, inclusive)
|
||||
const newChannelData = plan
|
||||
.filter(x => x.channel === newChannel)
|
||||
.sort((a, b) => a.weight - b.weight);
|
||||
for (let i = newWeight; i < newChannelData.length; i++) {
|
||||
const movingItem = newChannelData[i];
|
||||
movingItem.weight += 1;
|
||||
inc.push(itemId(movingItem));
|
||||
}
|
||||
}
|
||||
|
||||
const ops: api.UpdateOp[] = [];
|
||||
console.log("Inc, dec:", inc, dec);
|
||||
|
||||
inc.forEach(id => {
|
||||
const item = plan.find(x => itemId(x) === id)!;
|
||||
ops.push({
|
||||
op: "MoveItem",
|
||||
timeslotitemid: itemId(item),
|
||||
oldchannel: item.channel,
|
||||
oldweight: item.weight - 1,
|
||||
channel: item.channel,
|
||||
weight: item.weight
|
||||
});
|
||||
});
|
||||
|
||||
dec.forEach(id => {
|
||||
const item = plan.find(x => itemId(x) === id)!;
|
||||
ops.push({
|
||||
op: "MoveItem",
|
||||
timeslotitemid: itemId(item),
|
||||
oldchannel: item.channel,
|
||||
oldweight: item.weight + 1,
|
||||
channel: item.channel,
|
||||
weight: item.weight
|
||||
});
|
||||
});
|
||||
|
||||
// Then, and only then, put the item in its new place
|
||||
console.log("Moving over");
|
||||
ops.push({
|
||||
op: "MoveItem",
|
||||
timeslotitemid: (itemToMove as TimeslotItem).timeslotitemid,
|
||||
oldchannel: oldChannel,
|
||||
oldweight: oldWeight,
|
||||
channel: newChannel,
|
||||
weight: newWeight
|
||||
});
|
||||
|
||||
console.log(
|
||||
"TL;DR of opset is\r\n" +
|
||||
ops
|
||||
.map(x => `${x.oldchannel}x${x.oldweight} -> ${x.channel}x${x.weight}`)
|
||||
.join(";\r\n")
|
||||
);
|
||||
|
||||
dispatch(showplan.actions.applyOps(ops));
|
||||
const result = await api.updateShowplan(timeslotid, ops);
|
||||
if (!result.every(x => x.status)) {
|
||||
dispatch(showplan.actions.planSaveError("Server says no!"));
|
||||
} else {
|
||||
dispatch(showplan.actions.setPlanSaving(false));
|
||||
}
|
||||
};
|
||||
|
||||
export const getShowplan = (timeslotId: number): AppThunk => async dispatch => {
|
||||
dispatch(showplan.actions.getShowplanStarting());
|
||||
try {
|
||||
const plan = await api.getShowplan(timeslotId);
|
||||
// Sanity check
|
||||
const ops: api.UpdateOp[] = [];
|
||||
|
||||
for (let colIndex = 0; colIndex < plan.length; colIndex++) {
|
||||
// Sort the column
|
||||
plan[colIndex] = plan[colIndex].sort((a, b) => {
|
||||
const weightRes = a.weight - b.weight;
|
||||
if (weightRes !== 0) {
|
||||
return weightRes;
|
||||
}
|
||||
return parseInt(a.timeslotitemid, 10) - parseInt(b.timeslotitemid, 10);
|
||||
});
|
||||
// If anything is out of place, budge it over
|
||||
const col = plan[colIndex];
|
||||
for (let itemIndex = 0; itemIndex < col.length; itemIndex++) {
|
||||
const item = col[itemIndex];
|
||||
if (item.weight !== itemIndex) {
|
||||
// arse.
|
||||
ops.push({
|
||||
op: "MoveItem",
|
||||
timeslotitemid: item.timeslotitemid,
|
||||
oldchannel: colIndex,
|
||||
channel: colIndex,
|
||||
oldweight: item.weight,
|
||||
weight: itemIndex
|
||||
});
|
||||
plan[colIndex][itemIndex].weight = itemIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ops.length > 0) {
|
||||
console.log("Repairing showplan", ops);
|
||||
const updateResult = await api.updateShowplan(timeslotId, ops);
|
||||
if (!updateResult.every(x => x.status)) {
|
||||
console.error("Repair failed!");
|
||||
dispatch(showplan.actions.getShowplanError("Repair failed!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
dispatch(showplan.actions.getShowplanSuccess(plan.flat(2)));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
dispatch(showplan.actions.getShowplanError(e.toString()));
|
||||
}
|
||||
};
|
18
src/store.ts
Normal file
18
src/store.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import rootReducer, { RootState } from "./rootReducer";
|
||||
import { configureStore, Action } from "@reduxjs/toolkit";
|
||||
import { ThunkAction } from "redux-thunk";
|
||||
|
||||
const store = configureStore({
|
||||
reducer: rootReducer
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV === "development" && module.hot) {
|
||||
module.hot.accept("./rootReducer", () => {
|
||||
const newRootReducer = require("./rootReducer").default;
|
||||
store.replaceReducer(newRootReducer);
|
||||
});
|
||||
}
|
||||
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
export type AppThunk = ThunkAction<void, RootState, null, Action<string>>;
|
||||
export default store;
|
268
yarn.lock
268
yarn.lock
|
@ -839,6 +839,14 @@
|
|||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
"@babel/plugin-transform-typescript" "^7.6.0"
|
||||
|
||||
"@babel/runtime-corejs2@^7.6.3":
|
||||
version "7.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.7.2.tgz#5a8c4e2f8688ce58adc9eb1d8320b6e7341f96ce"
|
||||
integrity sha512-GfVnHchOBvIMsweQ13l4jd9lT4brkevnavnVOej5g2y7PpTRY+R4pcQlCjWMZoUla5rMLFzaS/Ll2s59cB1TqQ==
|
||||
dependencies:
|
||||
core-js "^2.6.5"
|
||||
regenerator-runtime "^0.13.2"
|
||||
|
||||
"@babel/runtime@7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.6.0.tgz#4fc1d642a9fd0299754e8b5de62c631cf5568205"
|
||||
|
@ -853,6 +861,13 @@
|
|||
dependencies:
|
||||
regenerator-runtime "^0.13.2"
|
||||
|
||||
"@babel/runtime@^7.5.5":
|
||||
version "7.7.2"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.7.2.tgz#111a78002a5c25fc8e3361bedc9529c696b85a6a"
|
||||
integrity sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.2"
|
||||
|
||||
"@babel/template@^7.1.0", "@babel/template@^7.4.0", "@babel/template@^7.4.4", "@babel/template@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.6.0.tgz#7f0159c7f5012230dad64cca42ec9bdb5c9536e6"
|
||||
|
@ -1097,6 +1112,18 @@
|
|||
resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-1.1.3.tgz#2b5a3ab3f918cca48a8c754c08168e3f03eba61b"
|
||||
integrity sha512-shAmDyaQC4H92APFoIaVDHCx5bStIocgvbwQyxPRrbUY20V1EYTbSDchWbuwlMG3V17cprZhA6+78JfB+3DTPw==
|
||||
|
||||
"@reduxjs/toolkit@^1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.0.4.tgz#cec88446a22a98b48808af7ab19a58aa93e6f5f9"
|
||||
integrity sha512-nyCZ9/CpnMXFZ//0wm1mNPSEl0J0bCghY2qeHM8zuubaBBMBr6KsIaLLms1jThbOJ1O+Ej0Tl11z5naE9czfzA==
|
||||
dependencies:
|
||||
immer "^4.0.1"
|
||||
redux "^4.0.0"
|
||||
redux-devtools-extension "^2.13.8"
|
||||
redux-immutable-state-invariant "^2.1.0"
|
||||
redux-thunk "^2.3.0"
|
||||
reselect "^4.0.0"
|
||||
|
||||
"@svgr/babel-plugin-add-jsx-attribute@^4.2.0":
|
||||
version "4.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz#dadcb6218503532d6884b210e7f3c502caaa44b1"
|
||||
|
@ -1200,6 +1227,11 @@
|
|||
"@svgr/plugin-svgo" "^4.3.1"
|
||||
loader-utils "^1.2.3"
|
||||
|
||||
"@types/asap@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/asap/-/asap-2.0.0.tgz#d529e9608c83499a62ae08c871c5e62271aa2963"
|
||||
integrity sha512-upIS0Gt9Mc8eEpCbYMZ1K8rhNosfKUtimNcINce+zLwJF5UpM3Vv7yz3S5l/1IX+DxTa8lTkUjqynvjRXyJzsg==
|
||||
|
||||
"@types/babel__core@^7.1.0":
|
||||
version "7.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.3.tgz#e441ea7df63cd080dfcd02ab199e6d16a735fc30"
|
||||
|
@ -1238,6 +1270,19 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
|
||||
integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==
|
||||
|
||||
"@types/hoist-non-react-statics@^3.3.0", "@types/hoist-non-react-statics@^3.3.1":
|
||||
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/invariant@^2.2.30":
|
||||
version "2.2.30"
|
||||
resolved "https://registry.yarnpkg.com/@types/invariant/-/invariant-2.2.30.tgz#20efa342807606ada5483731a8137cb1561e5fe9"
|
||||
integrity sha512-98fB+yo7imSD2F7PF7GIpELNgtLNgo5wjivu0W5V4jx+KVVJxo6p/qN4zdzSTBWy4/sN3pPyXwnhRSD28QX+ag==
|
||||
|
||||
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
|
||||
|
@ -1275,6 +1320,11 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.3.tgz#bdfd69d61e464dcc81b25159c270d75a73c1a636"
|
||||
integrity sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==
|
||||
|
||||
"@types/lodash@^4.14.149":
|
||||
version "4.14.149"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.149.tgz#1342d63d948c6062838fbf961012f74d4e638440"
|
||||
integrity sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==
|
||||
|
||||
"@types/node@12.12.7":
|
||||
version "12.12.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.7.tgz#01e4ea724d9e3bd50d90c11fd5980ba317d8fa11"
|
||||
|
@ -1290,6 +1340,18 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
|
||||
integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
|
||||
|
||||
"@types/qs@^6.9.0":
|
||||
version "6.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.0.tgz#2a5fa918786d07d3725726f7f650527e1cfeaffd"
|
||||
integrity sha512-c4zji5CjWv1tJxIZkz1oUtGcdOlsH3aza28Nqmm+uNDWBRHoMsjooBEN4czZp1V3iXPihE/VRUOBqg+4Xq0W4g==
|
||||
|
||||
"@types/react-beautiful-dnd@^11.0.3":
|
||||
version "11.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-beautiful-dnd/-/react-beautiful-dnd-11.0.3.tgz#51d9f37942dd18cc4aa10da98a5c883664e7ee46"
|
||||
integrity sha512-7ZbT/7mNJu+uRrUGdTQ1hAINtqg909L4NHrXyspV42fvVgBgda6ysiBzoDUMENmQ/RlRJdpyrcp8Dtd/77bp9Q==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-dom@16.9.4":
|
||||
version "16.9.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.4.tgz#0b58df09a60961dcb77f62d4f1832427513420df"
|
||||
|
@ -1297,6 +1359,16 @@
|
|||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-redux@^7.1.5":
|
||||
version "7.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/react-redux/-/react-redux-7.1.5.tgz#c7a528d538969250347aa53c52241051cf886bd3"
|
||||
integrity sha512-ZoNGQMDxh5ENY7PzU7MVonxDzS1l/EWiy8nUhDqxFqUZn4ovboCyvk4Djf68x6COb7vhGTKjyjxHxtFdAA5sUA==
|
||||
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.11":
|
||||
version "16.9.11"
|
||||
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.11.tgz#70e0b7ad79058a7842f25ccf2999807076ada120"
|
||||
|
@ -1305,11 +1377,21 @@
|
|||
"@types/prop-types" "*"
|
||||
csstype "^2.2.0"
|
||||
|
||||
"@types/shallowequal@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/shallowequal/-/shallowequal-1.1.1.tgz#aad262bb3f2b1257d94c71d545268d592575c9b1"
|
||||
integrity sha512-Lhni3aX80zbpdxRuWhnuYPm8j8UQaa571lHP/xI4W+7BAFhSIhRReXnqjEgT/XzPoXZTJkCqstFMJ8CZTK6IlQ==
|
||||
|
||||
"@types/stack-utils@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-1.0.1.tgz#0a851d3bd96498fa25c33ab7278ed3bd65f06c3e"
|
||||
integrity sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==
|
||||
|
||||
"@types/webpack-env@^1.14.1":
|
||||
version "1.14.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/webpack-env/-/webpack-env-1.14.1.tgz#0d8a53f308f017c53a5ddc3d07f4d6fa76b790d7"
|
||||
integrity sha512-0Ki9jAAhKDSuLDXOIMADg54Hu60SuBTEsWaJGGy5cV+SSUQ63J2a+RrYYGrErzz39fXzTibhKrAQJAb8M7PNcA==
|
||||
|
||||
"@types/yargs-parser@*":
|
||||
version "13.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-13.1.0.tgz#c563aa192f39350a1d18da36c5a8da382bbd8228"
|
||||
|
@ -1760,7 +1842,7 @@ arrify@^1.0.1:
|
|||
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
|
||||
integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
|
||||
|
||||
asap@~2.0.6:
|
||||
asap@^2.0.6, asap@~2.0.6:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
|
||||
|
@ -2760,6 +2842,11 @@ core-js@^2.4.0:
|
|||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2"
|
||||
integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==
|
||||
|
||||
core-js@^2.6.5:
|
||||
version "2.6.10"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.10.tgz#8a5b8391f8cc7013da703411ce5b585706300d7f"
|
||||
integrity sha512-I39t74+4t+zau64EN1fE5v2W31Adtc/REhzWN+gWRRXg6WH5qAsZm62DHpQ1+Yhe4047T55jvzz7MUqF/dBBlA==
|
||||
|
||||
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"
|
||||
|
@ -2841,6 +2928,13 @@ css-blank-pseudo@^0.1.4:
|
|||
dependencies:
|
||||
postcss "^7.0.5"
|
||||
|
||||
css-box-model@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/css-box-model/-/css-box-model-1.2.0.tgz#3a26377b4162b3200d2ede4b064ec5b6a75186d0"
|
||||
integrity sha512-lri0br+jSNV0kkkiGEp9y9y3Njq2PmpqbeGWRFQJuZteZzY9iC9GZhQ8Y4WpPwM/2YocjHePxy14igJY7YKzkA==
|
||||
dependencies:
|
||||
tiny-invariant "^1.0.6"
|
||||
|
||||
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"
|
||||
|
@ -3275,6 +3369,17 @@ dir-glob@2.0.0:
|
|||
arrify "^1.0.1"
|
||||
path-type "^3.0.0"
|
||||
|
||||
dnd-core@^9.4.0:
|
||||
version "9.4.0"
|
||||
resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-9.4.0.tgz#ccf605d36887f18cdde8fd5576ca3145d2e69fa8"
|
||||
integrity sha512-Kg+8VwU8s7TgdR/BUYGUHrvFiS+5ePMZ0Q0XD7p+cFVJvgKqykBaeQDuaziuauFMPm8QxtnUy8Pncey9flXW3Q==
|
||||
dependencies:
|
||||
"@types/asap" "^2.0.0"
|
||||
"@types/invariant" "^2.2.30"
|
||||
asap "^2.0.6"
|
||||
invariant "^2.2.4"
|
||||
redux "^4.0.4"
|
||||
|
||||
dns-equal@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d"
|
||||
|
@ -4533,6 +4638,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.0"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.0.tgz#b09178f0122184fb95acf525daaecb4d8f45958b"
|
||||
integrity sha512-0XsbTXxgiaCDYDIWFcwkmerZPSwywfUqYmwT4jzewKTQSWoE6FCMoUVOeBJWK3E/CrWbxRG3m5GzY4lnIwGRBA==
|
||||
dependencies:
|
||||
react-is "^16.7.0"
|
||||
|
||||
hosted-git-info@^2.1.4:
|
||||
version "2.8.4"
|
||||
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.4.tgz#44119abaf4bc64692a16ace34700fed9c03e2546"
|
||||
|
@ -4745,6 +4857,11 @@ immer@1.10.0:
|
|||
resolved "https://registry.yarnpkg.com/immer/-/immer-1.10.0.tgz#bad67605ba9c810275d91e1c2a47d4582e98286d"
|
||||
integrity sha512-O3sR1/opvCDGLEVcvrGTMtLac8GJ5IwZC4puPrLuRj3l7ICKvkmA0vGuU9OW8mV9WIBRnaxp5GJh9IEAaNOoYg==
|
||||
|
||||
immer@^4.0.1:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-4.0.2.tgz#9ff0fcdf88e06f92618a5978ceecb5884e633559"
|
||||
integrity sha512-Q/tm+yKqnKy4RIBmmtISBlhXuSDrB69e9EKTYiIenIKQkXBQir43w+kN/eGiax3wt1J0O1b2fYcNqLSbEcXA7w==
|
||||
|
||||
import-cwd@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
|
||||
|
@ -4872,7 +4989,7 @@ internal-ip@^4.2.0:
|
|||
default-gateway "^4.2.0"
|
||||
ipaddr.js "^1.9.0"
|
||||
|
||||
invariant@^2.2.2, invariant@^2.2.4:
|
||||
invariant@^2.1.0, invariant@^2.2.2, invariant@^2.2.4:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
|
||||
integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==
|
||||
|
@ -5744,7 +5861,7 @@ json-stable-stringify@^1.0.1:
|
|||
dependencies:
|
||||
jsonify "~0.0.0"
|
||||
|
||||
json-stringify-safe@~5.0.1:
|
||||
json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
||||
integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=
|
||||
|
@ -6081,6 +6198,11 @@ mem@^4.0.0:
|
|||
mimic-fn "^2.0.0"
|
||||
p-is-promise "^2.0.0"
|
||||
|
||||
memoize-one@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/memoize-one/-/memoize-one-5.1.1.tgz#047b6e3199b508eaec03504de71229b8eb1d75c0"
|
||||
integrity sha512-HKeeBpWvqiVJD57ZUAsJNm71eHTykffzcLZVYWiVfQeI1rJtuEaS7hQiEpWfVVk18donPwJEcFKIkCmPJNOhHA==
|
||||
|
||||
memory-fs@^0.4.0, memory-fs@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552"
|
||||
|
@ -7921,6 +8043,11 @@ qs@6.7.0:
|
|||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"
|
||||
integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==
|
||||
|
||||
qs@^6.9.1:
|
||||
version "6.9.1"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.1.tgz#20082c65cb78223635ab1a9eaca8875a29bf8ec9"
|
||||
integrity sha512-Cxm7/SS/y/Z3MHWSxXb8lIFqgqBowP5JMlTUFyJN88y0SGQhVmZnqFK/PeuMX9LzUyWsqqhNxIyg0jlzq946yA==
|
||||
|
||||
qs@~6.5.2:
|
||||
version "6.5.2"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
|
||||
|
@ -7949,6 +8076,11 @@ querystringify@^2.1.1:
|
|||
resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.1.1.tgz#60e5a5fd64a7f8bfa4d2ab2ed6fdf4c85bad154e"
|
||||
integrity sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==
|
||||
|
||||
raf-schd@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/raf-schd/-/raf-schd-4.0.2.tgz#bd44c708188f2e84c810bf55fcea9231bcaed8a0"
|
||||
integrity sha512-VhlMZmGy6A6hrkJWHLNTGl5gtgMUm+xfGza6wbwnE914yeQ5Ybm18vgM734RZhMgfw4tacUrWseGZlpUrrakEQ==
|
||||
|
||||
raf@3.4.1:
|
||||
version "3.4.1"
|
||||
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
|
||||
|
@ -8008,6 +8140,19 @@ react-app-polyfill@^1.0.4:
|
|||
regenerator-runtime "0.13.3"
|
||||
whatwg-fetch "3.0.0"
|
||||
|
||||
react-beautiful-dnd@^12.1.1:
|
||||
version "12.1.1"
|
||||
resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-12.1.1.tgz#810f9b9d94f667b15b253793e853d016a0f3f07c"
|
||||
integrity sha512-w/mpIXMEXowc53PCEnMoFyAEYFgxMfygMK5msLo5ifJ2/CiSACLov9A79EomnPF7zno3N207QGXsraBxAJnyrw==
|
||||
dependencies:
|
||||
"@babel/runtime-corejs2" "^7.6.3"
|
||||
css-box-model "^1.2.0"
|
||||
memoize-one "^5.1.1"
|
||||
raf-schd "^4.0.2"
|
||||
react-redux "^7.1.1"
|
||||
redux "^4.0.4"
|
||||
use-memo-one "^1.1.1"
|
||||
|
||||
react-dev-utils@^9.1.0:
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-9.1.0.tgz#3ad2bb8848a32319d760d0a84c56c14bdaae5e81"
|
||||
|
@ -8039,26 +8184,66 @@ react-dev-utils@^9.1.0:
|
|||
strip-ansi "5.2.0"
|
||||
text-table "0.2.0"
|
||||
|
||||
react-dom@16.11.0:
|
||||
version "16.11.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.11.0.tgz#7e7c4a5a85a569d565c2462f5d345da2dd849af5"
|
||||
integrity sha512-nrRyIUE1e7j8PaXSPtyRKtz+2y9ubW/ghNgqKFHHAHaeP0fpF5uXR+sq8IMRHC+ZUxw7W9NyCDTBtwWxvkb0iA==
|
||||
react-dnd-html5-backend@^9.4.0:
|
||||
version "9.4.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-9.4.0.tgz#5b1d192f57d103298657cde1fe0eabdbf2726311"
|
||||
integrity sha512-gehPwLp505F6RoFkQiDX7Q4mbpbyfyT0TbIoZop/m4vkBw6yUE/QLrnxBQdNpDPSwL/9XkZxxd/PrbeMCQ+WrQ==
|
||||
dependencies:
|
||||
dnd-core "^9.4.0"
|
||||
|
||||
react-dnd@^9.4.0:
|
||||
version "9.4.0"
|
||||
resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-9.4.0.tgz#eec87035c6360fb33a44932326b3369af011a41c"
|
||||
integrity sha512-jnLF8qKowCKTqSddfCiLx5+sb+HxO1qgdiAgbBeL8yuo5tRYNtKxZYn7+wVwNoyZuWEuM1Gw/Wsdhr+yb2RELQ==
|
||||
dependencies:
|
||||
"@types/hoist-non-react-statics" "^3.3.1"
|
||||
"@types/shallowequal" "^1.1.1"
|
||||
dnd-core "^9.4.0"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
shallowequal "^1.1.0"
|
||||
|
||||
react-dom@^0.0.0-experimental-38dd17ab9:
|
||||
version "0.0.0-experimental-38dd17ab9"
|
||||
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-0.0.0-experimental-38dd17ab9.tgz#74e040bb234bf8df4ea129814e6f099ccd31639e"
|
||||
integrity sha512-PemrIDeg/5hhkU0Ota8vWPm9noyrQ2qQTpPFS2EAyNz3Nb499dM/+STaczkVgLmfoiwd8zQdHWM7Q7ijHyT56A==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
prop-types "^15.6.2"
|
||||
scheduler "^0.17.0"
|
||||
scheduler "0.0.0-experimental-38dd17ab9"
|
||||
|
||||
react-error-overlay@^6.0.3:
|
||||
version "6.0.3"
|
||||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.3.tgz#c378c4b0a21e88b2e159a3e62b2f531fd63bf60d"
|
||||
integrity sha512-bOUvMWFQVk5oz8Ded9Xb7WVdEi3QGLC8tH7HmYP0Fdp4Bn3qw0tRFmr5TW6mvahzvmrK4a6bqWGfCevBflP+Xw==
|
||||
|
||||
react-is@^16.7.0:
|
||||
version "16.11.0"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.11.0.tgz#b85dfecd48ad1ce469ff558a882ca8e8313928fa"
|
||||
integrity sha512-gbBVYR2p8mnriqAwWx9LbuUrShnAuSCNnuPGyc7GJrMVQtPDAh8iLpv7FRuMPFb56KkaVZIYSz1PrjI9q0QPCw==
|
||||
|
||||
react-is@^16.8.1, react-is@^16.8.4:
|
||||
version "16.10.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.10.1.tgz#0612786bf19df406502d935494f0450b40b8294f"
|
||||
integrity sha512-BXUMf9sIOPXXZWqr7+c5SeOKJykyVr2u0UDzEf4LNGc6taGkQe1A9DFD07umCIXz45RLr9oAAwZbAJ0Pkknfaw==
|
||||
|
||||
react-is@^16.9.0:
|
||||
version "16.12.0"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"
|
||||
integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==
|
||||
|
||||
react-redux@^7.1.1, react-redux@^7.1.3:
|
||||
version "7.1.3"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-7.1.3.tgz#717a3d7bbe3a1b2d535c94885ce04cdc5a33fc79"
|
||||
integrity sha512-uI1wca+ECG9RoVkWQFF4jDMqmaw0/qnvaSvOoL/GA4dNxf6LoV8sUAcNDvE5NWKs4hFpn0t6wswNQnY3f7HT3w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
hoist-non-react-statics "^3.3.0"
|
||||
invariant "^2.2.4"
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.7.2"
|
||||
react-is "^16.9.0"
|
||||
|
||||
react-scripts@3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/react-scripts/-/react-scripts-3.2.0.tgz#58ccd6b4ffa27f1b4d2986cbdcaa916660e9e33c"
|
||||
|
@ -8120,10 +8305,10 @@ react-scripts@3.2.0:
|
|||
optionalDependencies:
|
||||
fsevents "2.0.7"
|
||||
|
||||
react@16.11.0:
|
||||
version "16.11.0"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-16.11.0.tgz#d294545fe62299ccee83363599bf904e4a07fdbb"
|
||||
integrity sha512-M5Y8yITaLmU0ynd0r1Yvfq98Rmll6q8AxaEe88c8e7LxO8fZ2cNgmFt0aGAS9wzf1Ao32NKXtCl+/tVVtkxq6g==
|
||||
react@^0.0.0-experimental-38dd17ab9:
|
||||
version "0.0.0-experimental-38dd17ab9"
|
||||
resolved "https://registry.yarnpkg.com/react/-/react-0.0.0-experimental-38dd17ab9.tgz#d2cc104de47fa58abb4297f27bcc54854e20581a"
|
||||
integrity sha512-rPEXj4mppahrDR+jpDd/sYAkRiwhgTvxtehM5kNDPtTnzz6RChflHZ9U8inQpVdX/nkcnO6r92JyBxJaR+6tVg==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
@ -8208,6 +8393,32 @@ recursive-readdir@2.2.2:
|
|||
dependencies:
|
||||
minimatch "3.0.4"
|
||||
|
||||
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-immutable-state-invariant@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/redux-immutable-state-invariant/-/redux-immutable-state-invariant-2.1.0.tgz#308fd3cc7415a0e7f11f51ec997b6379c7055ce1"
|
||||
integrity sha512-3czbDKs35FwiBRsx/3KabUk5zSOoTXC+cgVofGkpBNv3jQcqIe5JrHcF5AmVt7B/4hyJ8MijBIpCJ8cife6yJg==
|
||||
dependencies:
|
||||
invariant "^2.1.0"
|
||||
json-stringify-safe "^5.0.1"
|
||||
|
||||
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.4:
|
||||
version "4.0.4"
|
||||
resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.4.tgz#4ee1aeb164b63d6a1bcc57ae4aa0b6e6fa7a3796"
|
||||
integrity sha512-vKv4WdiJxOWKxK0yRoaK3Y4pxxB0ilzVx6dszU2W8wLxlb2yikRph4iV/ymtdJ6ZxpBLFbyrxklnT5yBbQSl3Q==
|
||||
dependencies:
|
||||
loose-envify "^1.4.0"
|
||||
symbol-observable "^1.2.0"
|
||||
|
||||
regenerate-unicode-properties@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e"
|
||||
|
@ -8379,6 +8590,11 @@ requires-port@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
|
||||
|
||||
reselect@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7"
|
||||
integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==
|
||||
|
||||
resolve-cwd@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
|
||||
|
@ -8573,10 +8789,10 @@ saxes@^3.1.9:
|
|||
dependencies:
|
||||
xmlchars "^2.1.1"
|
||||
|
||||
scheduler@^0.17.0:
|
||||
version "0.17.0"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.17.0.tgz#7c9c673e4ec781fac853927916d1c426b6f3ddfe"
|
||||
integrity sha512-7rro8Io3tnCPuY4la/NuI5F2yfESpnfZyT6TtkXnSWVkcu0BCDJ+8gk5ozUaFaxpIyNuWAPXrH0yFcSi28fnDA==
|
||||
scheduler@0.0.0-experimental-38dd17ab9:
|
||||
version "0.0.0-experimental-38dd17ab9"
|
||||
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.0.0-experimental-38dd17ab9.tgz#59e1ab1df3a92e270ad0c94b474284185740b01d"
|
||||
integrity sha512-F2OVE+lm5Pxv5DlxB/c9xs0qBYBIUexxpJEi7yjic2aW1iiP4FrYnJNS9D4mWDHvwV0H5spDEURW5resrgsY+g==
|
||||
dependencies:
|
||||
loose-envify "^1.1.0"
|
||||
object-assign "^4.1.1"
|
||||
|
@ -8722,6 +8938,11 @@ shallow-clone@^3.0.0:
|
|||
dependencies:
|
||||
kind-of "^6.0.2"
|
||||
|
||||
shallowequal@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/shallowequal/-/shallowequal-1.1.0.tgz#188d521de95b9087404fd4dcb68b13df0ae4e7f8"
|
||||
integrity sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==
|
||||
|
||||
shebang-command@^1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
|
||||
|
@ -9231,6 +9452,11 @@ svgo@^1.0.0, svgo@^1.2.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"
|
||||
|
@ -9338,6 +9564,11 @@ timsort@^0.3.0:
|
|||
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
|
||||
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
|
||||
|
||||
tiny-invariant@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/tiny-invariant/-/tiny-invariant-1.0.6.tgz#b3f9b38835e36a41c843a3b0907a5a7b3755de73"
|
||||
integrity sha512-FOyLWWVjG+aC0UqG76V53yAWdXfH8bO6FNmyZOuUrzDzK8DI3/JRY25UD7+g49JWM1LXwymsKERB+DzI0dTEQA==
|
||||
|
||||
tmp@^0.0.33:
|
||||
version "0.0.33"
|
||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||
|
@ -9625,6 +9856,11 @@ url@^0.11.0:
|
|||
punycode "1.3.2"
|
||||
querystring "0.2.0"
|
||||
|
||||
use-memo-one@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.1.tgz#39e6f08fe27e422a7d7b234b5f9056af313bd22c"
|
||||
integrity sha512-oFfsyun+bP7RX8X2AskHNTxu+R3QdE/RC5IefMbqptmACAA/gfol1KDD5KRzPsGMa62sWxGZw+Ui43u6x4ddoQ==
|
||||
|
||||
use@^3.1.0:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f"
|
||||
|
|
Loading…
Reference in a new issue