Refactor ui: drop redux in favor of state local to components
This commit is contained in:
parent
b5fba3dc5b
commit
3f4f1d5358
45
ui/.yarnclean
Normal file
45
ui/.yarnclean
Normal file
|
@ -0,0 +1,45 @@
|
|||
# test directories
|
||||
__tests__
|
||||
test
|
||||
tests
|
||||
powered-test
|
||||
|
||||
# asset directories
|
||||
docs
|
||||
doc
|
||||
website
|
||||
images
|
||||
assets
|
||||
|
||||
# examples
|
||||
example
|
||||
examples
|
||||
|
||||
# code coverage directories
|
||||
coverage
|
||||
.nyc_output
|
||||
|
||||
# build scripts
|
||||
Makefile
|
||||
Gulpfile.js
|
||||
Gruntfile.js
|
||||
|
||||
# configs
|
||||
appveyor.yml
|
||||
circle.yml
|
||||
codeship-services.yml
|
||||
codeship-steps.yml
|
||||
wercker.yml
|
||||
.tern-project
|
||||
.gitattributes
|
||||
.editorconfig
|
||||
.*ignore
|
||||
.eslintrc
|
||||
.jshintrc
|
||||
.flowconfig
|
||||
.documentup.json
|
||||
.yarn-metadata.json
|
||||
.travis.yml
|
||||
|
||||
# misc
|
||||
*.md
|
|
@ -13,7 +13,6 @@
|
|||
"@types/react-dom": "^18.0.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-redux": "^8.0.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"react-use-websocket": "^4.3.1",
|
||||
"sass": "^1.58.3",
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { Provider } from 'react-redux';
|
||||
import { store } from './app/store';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
const { getByText } = render(
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
);
|
||||
const { getByText } = render(
|
||||
<App />
|
||||
);
|
||||
|
||||
expect(getByText(/learn/i)).toBeInTheDocument();
|
||||
expect(getByText(/recupera stato/i)).toBeInTheDocument();
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
import { RadioToggler } from './features/radio/Radio';
|
||||
import { LogViewer } from './features/radio/LogViewer';
|
||||
import { RadioToggler } from './Radio';
|
||||
import { LogViewer } from './LogViewer';
|
||||
import './App.css';
|
||||
|
||||
function App() {
|
||||
|
|
|
@ -1,21 +1,25 @@
|
|||
import React, { useState } from 'react';
|
||||
import { useAppSelector, useAppDispatch } from '../../app/hooks';
|
||||
import { selectLogsLoading, selectLogs, requestLogsThunk } from './radioSlice';
|
||||
import { requestLogs } from './radioAPI';
|
||||
import styles from './LogViewer.module.scss';
|
||||
|
||||
export function LogViewer() {
|
||||
const loading = useAppSelector(selectLogsLoading);
|
||||
const logs = useAppSelector(selectLogs);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [logs, setLogs] = useState<string[] | undefined>(undefined);
|
||||
const [hidden, setHidden] = useState(true);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
const onRequestLogsButtonClick = async () => {
|
||||
setLoading(true);
|
||||
const logs = await requestLogs();
|
||||
setLogs(logs);
|
||||
setLoading(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.logsContainer}>
|
||||
<div className={styles.buttonContainer}>
|
||||
<button
|
||||
className={styles.button}
|
||||
onClick={() => dispatch(requestLogsThunk())}
|
||||
onClick={onRequestLogsButtonClick}
|
||||
>Recupera stato</button>
|
||||
<button
|
||||
className={styles.button}
|
99
ui/src/Radio.tsx
Normal file
99
ui/src/Radio.tsx
Normal file
|
@ -0,0 +1,99 @@
|
|||
import React, { useEffect, useState } from 'react';
|
||||
import useWebSocket from 'react-use-websocket';
|
||||
import { RadioState, ConnectionState } from './types';
|
||||
import {
|
||||
requestToStart,
|
||||
requestToStop,
|
||||
} from './radioAPI';
|
||||
import styles from './Radio.module.scss';
|
||||
|
||||
const getWSUrl = async () => {
|
||||
const proto = (window.location.protocol === "http:") ? "ws" : "wss"
|
||||
const wsUrl = process.env.WS_ADDR ?
|
||||
process.env.WS_ADDR :
|
||||
proto + "://" + window.location.host + window.location.pathname + "liveness";
|
||||
console.log("WS ADDR: %s", wsUrl);
|
||||
return wsUrl;
|
||||
};
|
||||
|
||||
export function RadioToggler() {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [radioState, setRadioState] = useState<RadioState|undefined>(undefined);
|
||||
const [connectionState, setConnectionState] = useState(ConnectionState.DISCONNECTED);
|
||||
|
||||
const { lastJsonMessage } = useWebSocket(getWSUrl,
|
||||
{
|
||||
onOpen: () => setConnectionState(ConnectionState.CONNECTED),
|
||||
onClose: () => setConnectionState(ConnectionState.DISCONNECTED),
|
||||
});
|
||||
|
||||
const onButtonClick = async () => {
|
||||
if (radioState === RadioState.STARTED) {
|
||||
setLoading(true);
|
||||
const stopped = await requestToStop();
|
||||
if (stopped) {
|
||||
setRadioState(RadioState.STOPPED);
|
||||
}
|
||||
setLoading(false);
|
||||
} else if (radioState === RadioState.STOPPED) {
|
||||
setLoading(true);
|
||||
const started = await requestToStart();
|
||||
if (started) {
|
||||
setRadioState(RadioState.STARTED);
|
||||
}
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const setRadioStateFromWS = (message: any) => {
|
||||
switch (message.status) {
|
||||
case "STARTED":
|
||||
setRadioState(RadioState.STARTED);
|
||||
break;
|
||||
case "STOPPED":
|
||||
setRadioState(RadioState.STOPPED);
|
||||
break;
|
||||
default:
|
||||
setRadioState(undefined);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (lastJsonMessage != null) {
|
||||
if (!loading && lastJsonMessage.hasOwnProperty('status')) {
|
||||
setRadioStateFromWS(lastJsonMessage);
|
||||
}
|
||||
}
|
||||
}, [lastJsonMessage]);
|
||||
|
||||
let buttonStatus = "";
|
||||
let circleStyle = styles.circleDisconnected;
|
||||
if (loading) {
|
||||
buttonStatus = "Attesa";
|
||||
circleStyle = styles.circleLoading;
|
||||
} else if (connectionState && connectionState !== ConnectionState.DISCONNECTED) {
|
||||
switch (radioState) {
|
||||
case RadioState.STARTED:
|
||||
buttonStatus = "ON AIR";
|
||||
circleStyle = styles.circleStarted;
|
||||
break;
|
||||
case RadioState.STOPPED:
|
||||
buttonStatus = "SPENTA";
|
||||
circleStyle = styles.circleStopped;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.buttonWrap} aria-label="toggle-radio-state">
|
||||
<div
|
||||
className={styles.clicker}
|
||||
onClick={onButtonClick}
|
||||
>{buttonStatus}</div>
|
||||
<div className={circleStyle}></div>
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
|
||||
import type { RootState, AppDispatch } from './store';
|
||||
|
||||
// Use throughout your app instead of plain `useDispatch` and `useSelector`
|
||||
export const useAppDispatch = () => useDispatch<AppDispatch>();
|
||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
|
@ -1,17 +0,0 @@
|
|||
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
|
||||
import radioReducer from '../features/radio/radioSlice';
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
globalState: radioReducer,
|
||||
},
|
||||
});
|
||||
|
||||
export type AppDispatch = typeof store.dispatch;
|
||||
export type RootState = ReturnType<typeof store.getState>;
|
||||
export type AppThunk<ReturnType = void> = ThunkAction<
|
||||
ReturnType,
|
||||
RootState,
|
||||
unknown,
|
||||
Action<string>
|
||||
>;
|
|
@ -1,79 +0,0 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import useWebSocket from 'react-use-websocket';
|
||||
|
||||
import { useAppSelector, useAppDispatch } from '../../app/hooks';
|
||||
import { RadioState, ConnectionState } from '../../app/types';
|
||||
import {
|
||||
toggleRadioState,
|
||||
selectRadioLoading,
|
||||
selectRadio,
|
||||
selectConnection,
|
||||
} from './radioSlice';
|
||||
import {
|
||||
getOnWSOpen,
|
||||
getOnWSClose,
|
||||
getOnWSMessage,
|
||||
} from './radioWS';
|
||||
import styles from './Radio.module.scss';
|
||||
|
||||
const getWSUrl = async () => {
|
||||
const proto = (window.location.protocol === "http:") ? "ws" : "wss"
|
||||
const wsUrl = process.env.WS_ADDR ?
|
||||
process.env.WS_ADDR :
|
||||
proto + "://" + window.location.host + window.location.pathname + "liveness";
|
||||
console.log("WS ADDR: %s", wsUrl);
|
||||
return wsUrl;
|
||||
};
|
||||
|
||||
export function RadioToggler() {
|
||||
const loading = useAppSelector(selectRadioLoading);
|
||||
const radioState = useAppSelector(selectRadio);
|
||||
const connectionState = useAppSelector(selectConnection);
|
||||
|
||||
const dispatch = useAppDispatch();
|
||||
|
||||
const { lastJsonMessage } = useWebSocket(getWSUrl,
|
||||
{
|
||||
onOpen: getOnWSOpen(dispatch),
|
||||
onClose: getOnWSClose(dispatch),
|
||||
});
|
||||
|
||||
const onWSMessage = getOnWSMessage(dispatch);
|
||||
useEffect(() => {
|
||||
if (lastJsonMessage != null) {
|
||||
if (!loading) {
|
||||
onWSMessage(lastJsonMessage);
|
||||
}
|
||||
}
|
||||
}, [lastJsonMessage]);
|
||||
|
||||
let buttonStatus = "";
|
||||
let circleStyle = styles.circleDisconnected;
|
||||
if (loading) {
|
||||
buttonStatus = "Attesa";
|
||||
circleStyle = styles.circleLoading;
|
||||
} else if (connectionState && connectionState !== ConnectionState.DISCONNECTED) {
|
||||
switch (radioState) {
|
||||
case RadioState.STARTED:
|
||||
buttonStatus = "ON AIR";
|
||||
circleStyle = styles.circleStarted;
|
||||
break;
|
||||
case RadioState.STOPPED:
|
||||
buttonStatus = "SPENTA";
|
||||
circleStyle = styles.circleStopped;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.buttonWrap} aria-label="toggle-radio-state">
|
||||
<div
|
||||
className={styles.clicker}
|
||||
onClick={() => dispatch(toggleRadioState())}
|
||||
>{buttonStatus}</div>
|
||||
<div className={circleStyle}></div>
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
import { RadioState, ConnectionState } from '../../app/types';
|
||||
import reducer, {
|
||||
AppState,
|
||||
toggleRadioLoading,
|
||||
setRadioStarted,
|
||||
setRadioStopped,
|
||||
toggleRadio,
|
||||
setConnected,
|
||||
setDisconnected,
|
||||
toggleConnection,
|
||||
selectRadioLoading,
|
||||
selectRadio,
|
||||
selectConnection,
|
||||
} from './radioSlice';
|
||||
|
||||
describe('radio reducer', () => {
|
||||
const initialState: AppState = {
|
||||
loading: { radio: false, logs: false },
|
||||
connection: ConnectionState.DISCONNECTED,
|
||||
};
|
||||
|
||||
const connectedStarted: AppState = {
|
||||
radio: RadioState.STARTED,
|
||||
loading: { radio: false, logs: false },
|
||||
connection: ConnectionState.CONNECTED,
|
||||
};
|
||||
|
||||
const connectedStopped: AppState = {
|
||||
radio: RadioState.STOPPED,
|
||||
loading: { radio: false, logs: false },
|
||||
connection: ConnectionState.CONNECTED,
|
||||
};
|
||||
|
||||
it('should handle initial state', () => {
|
||||
expect(reducer(undefined, { type: 'unknown' })).toEqual({
|
||||
loading: { radio: false, logs: false },
|
||||
connection: ConnectionState.DISCONNECTED,
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle set loading', () => {
|
||||
const actual = reducer(initialState, toggleRadioLoading());
|
||||
expect(actual.loading.radio).toEqual(true);
|
||||
});
|
||||
|
||||
it('should hande toggle radio started', () => {
|
||||
const actual = reducer(connectedStopped, toggleRadio());
|
||||
expect(actual.radio).toEqual(RadioState.STARTED);
|
||||
})
|
||||
|
||||
it('should hande toggle radio stopped', () => {
|
||||
const actual = reducer(connectedStarted, toggleRadio());
|
||||
expect(actual.radio).toEqual(RadioState.STOPPED);
|
||||
})
|
||||
|
||||
it('should hande set radio started', () => {
|
||||
const actual = reducer(connectedStopped, setRadioStarted());
|
||||
expect(actual.radio).toEqual(RadioState.STARTED);
|
||||
})
|
||||
|
||||
it('should hande set radio stopped', () => {
|
||||
const actual = reducer(connectedStarted, setRadioStopped());
|
||||
expect(actual.radio).toEqual(RadioState.STOPPED);
|
||||
})
|
||||
|
||||
it('should noop set radio started', () => {
|
||||
const actual = reducer(connectedStarted, setRadioStarted());
|
||||
expect(actual.radio).toEqual(RadioState.STARTED);
|
||||
})
|
||||
|
||||
it('should noop set radio stopped', () => {
|
||||
const actual = reducer(connectedStopped, setRadioStopped());
|
||||
expect(actual.radio).toEqual(RadioState.STOPPED);
|
||||
})
|
||||
});
|
|
@ -1,138 +0,0 @@
|
|||
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
|
||||
import { RootState, AppThunk } from '../../app/store';
|
||||
import { RadioState, ConnectionState } from '../../app/types';
|
||||
import { requestToStart, requestToStop, requestLogs } from './radioAPI';
|
||||
|
||||
export interface LoadingState {
|
||||
radio: boolean,
|
||||
logs: boolean,
|
||||
}
|
||||
|
||||
export interface AppState {
|
||||
radio?: RadioState,
|
||||
logs?: Array<string>,
|
||||
loading: LoadingState,
|
||||
connection: ConnectionState
|
||||
}
|
||||
|
||||
const initialState: AppState = {
|
||||
radio: undefined,
|
||||
loading: { radio: false, logs: false },
|
||||
connection: ConnectionState.DISCONNECTED,
|
||||
};
|
||||
|
||||
export const requestToStartThunk = createAsyncThunk(
|
||||
'radio/restStart',
|
||||
async () => {
|
||||
const success = await requestToStart();
|
||||
return success;
|
||||
}
|
||||
);
|
||||
|
||||
export const requestToStopThunk = createAsyncThunk(
|
||||
'radio/restStop',
|
||||
async () => {
|
||||
const success = await requestToStop();
|
||||
return success;
|
||||
}
|
||||
);
|
||||
|
||||
export const requestLogsThunk = createAsyncThunk(
|
||||
'radio/restStatus',
|
||||
async () => {
|
||||
return await requestLogs();
|
||||
}
|
||||
);
|
||||
|
||||
export const radioSlice = createSlice({
|
||||
name: 'radio',
|
||||
initialState,
|
||||
reducers: {
|
||||
toggleRadioLoading: (state) => { state.loading.radio = !state.loading.radio },
|
||||
toggleLogsLoading: (state) => { state.loading.logs = !state.loading.logs },
|
||||
setRadioStarted: (state) => { console.log("[Redux] STARTED"); state.radio = RadioState.STARTED },
|
||||
setRadioStopped: (state) => { console.log("[Redux] STOPPED"); state.radio = RadioState.STOPPED },
|
||||
unsetRadio: (state) => { state.radio = undefined },
|
||||
toggleRadio: (state) => {
|
||||
if (state.radio === RadioState.STARTED) {
|
||||
state.radio = RadioState.STOPPED
|
||||
} else if (state.radio === RadioState.STOPPED) {
|
||||
state.radio = RadioState.STARTED
|
||||
}
|
||||
},
|
||||
setConnected: (state) => { state.connection = ConnectionState.CONNECTED },
|
||||
setDisconnected: (state) => { state.connection = ConnectionState.DISCONNECTED },
|
||||
toggleConnection: (state) => {
|
||||
if (state.connection === ConnectionState.CONNECTED) {
|
||||
state.connection = ConnectionState.DISCONNECTED
|
||||
} else if (state.connection === ConnectionState.DISCONNECTED) {
|
||||
state.connection = ConnectionState.CONNECTED
|
||||
}
|
||||
},
|
||||
},
|
||||
extraReducers: builder => {
|
||||
builder
|
||||
.addCase(requestToStartThunk.pending, (state) => {
|
||||
state.loading.radio = true;
|
||||
return state;
|
||||
})
|
||||
.addCase(requestToStartThunk.fulfilled, (state, action) => {
|
||||
state.loading.radio = false;
|
||||
if (action.payload) {
|
||||
state.radio = RadioState.STARTED;
|
||||
}
|
||||
return state;
|
||||
})
|
||||
.addCase(requestToStopThunk.pending, (state, _) => {
|
||||
state.loading.radio = true;
|
||||
return state;
|
||||
})
|
||||
.addCase(requestToStopThunk.fulfilled, (state, action) => {
|
||||
state.loading.radio = false;
|
||||
if (action.payload) {
|
||||
state.radio = RadioState.STOPPED;
|
||||
}
|
||||
return state;
|
||||
})
|
||||
.addCase(requestLogsThunk.pending, (state, _) => {
|
||||
state.loading.logs = true;
|
||||
return state;
|
||||
})
|
||||
.addCase(requestLogsThunk.fulfilled, (state, action) => {
|
||||
state.loading.logs = false;
|
||||
state.logs = action.payload;
|
||||
return state;
|
||||
})
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
toggleRadioLoading,
|
||||
toggleLogsLoading,
|
||||
setRadioStarted,
|
||||
setRadioStopped,
|
||||
unsetRadio,
|
||||
toggleRadio,
|
||||
setConnected,
|
||||
setDisconnected,
|
||||
toggleConnection,
|
||||
} = radioSlice.actions;
|
||||
|
||||
export const selectRadioLoading = (state: RootState) => state.globalState.loading.radio;
|
||||
export const selectLogsLoading = (state: RootState) => state.globalState.loading.logs;
|
||||
export const selectRadio = (state: RootState) => state.globalState.radio;
|
||||
export const selectLogs = (state: RootState) => state.globalState.logs;
|
||||
export const selectConnection = (state: RootState) => state.globalState.connection;
|
||||
|
||||
export const toggleRadioState =
|
||||
(): AppThunk =>
|
||||
async (dispatch, getState) => {
|
||||
const currentRadioState = selectRadio(getState());
|
||||
if (currentRadioState === RadioState.STARTED) {
|
||||
dispatch(requestToStopThunk());
|
||||
} else if (currentRadioState === RadioState.STOPPED) {
|
||||
dispatch(requestToStartThunk());
|
||||
}
|
||||
};
|
||||
|
||||
export default radioSlice.reducer
|
|
@ -1,41 +0,0 @@
|
|||
import { AppDispatch } from '../../app/store';
|
||||
import {
|
||||
setRadioStarted,
|
||||
setRadioStopped,
|
||||
unsetRadio,
|
||||
setConnected,
|
||||
setDisconnected,
|
||||
} from './radioSlice';
|
||||
|
||||
|
||||
export const getOnWSOpen = (dispatch: AppDispatch) => {
|
||||
return () => {
|
||||
console.log("[WS] Opened");
|
||||
dispatch(setConnected());
|
||||
}
|
||||
};
|
||||
|
||||
export const getOnWSClose = (dispatch: AppDispatch) => {
|
||||
return () => {
|
||||
console.log("[WS] Closed")
|
||||
dispatch(setDisconnected());
|
||||
return true
|
||||
}
|
||||
};
|
||||
|
||||
export const getOnWSMessage = (dispatch: AppDispatch) => {
|
||||
return (message: any) => {
|
||||
console.log("[WS] Message: %s", JSON.stringify(message));
|
||||
|
||||
switch (message.status) {
|
||||
case "STARTED":
|
||||
dispatch(setRadioStarted());
|
||||
break;
|
||||
case "STOPPED":
|
||||
dispatch(setRadioStopped());
|
||||
break;
|
||||
default:
|
||||
dispatch(unsetRadio());
|
||||
}
|
||||
}
|
||||
};
|
|
@ -1,7 +1,5 @@
|
|||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { Provider } from 'react-redux';
|
||||
import { store } from './app/store';
|
||||
import App from './App';
|
||||
import './index.css';
|
||||
|
||||
|
@ -10,8 +8,6 @@ const root = createRoot(container);
|
|||
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<Provider store={store}>
|
||||
<App />
|
||||
</Provider>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 1.1 KiB |
|
@ -1,4 +1,4 @@
|
|||
import { RadioState } from '../../app/types';
|
||||
import { RadioState } from './types';
|
||||
|
||||
export const requestToStart = async () => {
|
||||
console.log("[REST] request to start");
|
41
ui/yarn.lock
41
ui/yarn.lock
|
@ -1034,7 +1034,7 @@
|
|||
resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
|
||||
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
|
||||
|
||||
"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.20.7", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||
"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.20.7", "@babel/runtime@^7.8.4", "@babel/runtime@^7.9.2":
|
||||
version "7.20.13"
|
||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b"
|
||||
integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==
|
||||
|
@ -1940,14 +1940,6 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@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/html-minifier-terser@^6.0.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
|
||||
|
@ -2127,11 +2119,6 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.3.tgz#a136f83b0758698df454e328759dbd3d44555311"
|
||||
integrity sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==
|
||||
|
||||
"@types/use-sync-external-store@^0.0.3":
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43"
|
||||
integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA==
|
||||
|
||||
"@types/ws@^8.5.1":
|
||||
version "8.5.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5"
|
||||
|
@ -4810,13 +4797,6 @@ he@^1.2.0:
|
|||
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||
|
||||
hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
|
||||
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"
|
||||
|
||||
hoopy@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d"
|
||||
|
@ -7515,7 +7495,7 @@ react-error-overlay@^6.0.11:
|
|||
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb"
|
||||
integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==
|
||||
|
||||
react-is@^16.13.1, react-is@^16.7.0:
|
||||
react-is@^16.13.1:
|
||||
version "16.13.1"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
@ -7530,18 +7510,6 @@ react-is@^18.0.0:
|
|||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
|
||||
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
|
||||
|
||||
react-redux@^8.0.1:
|
||||
version "8.0.5"
|
||||
resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-8.0.5.tgz#e5fb8331993a019b8aaf2e167a93d10af469c7bd"
|
||||
integrity sha512-Q2f6fCKxPFpkXt1qNRZdEDLlScsDWyrgSj0mliK59qU6W5gvBiKkdMEG2lJzhd1rCctf0hb6EtePPLZ2e0m1uw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.1"
|
||||
"@types/hoist-non-react-statics" "^3.3.1"
|
||||
"@types/use-sync-external-store" "^0.0.3"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
react-is "^18.0.0"
|
||||
use-sync-external-store "^1.0.0"
|
||||
|
||||
react-refresh@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
|
||||
|
@ -8781,11 +8749,6 @@ url-parse@^1.5.3:
|
|||
querystringify "^2.1.1"
|
||||
requires-port "^1.0.0"
|
||||
|
||||
use-sync-external-store@^1.0.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a"
|
||||
integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==
|
||||
|
||||
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
|
|
Loading…
Reference in New Issue
Block a user