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",
|
"@types/react-dom": "^18.0.2",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-redux": "^8.0.1",
|
|
||||||
"react-scripts": "5.0.1",
|
"react-scripts": "5.0.1",
|
||||||
"react-use-websocket": "^4.3.1",
|
"react-use-websocket": "^4.3.1",
|
||||||
"sass": "^1.58.3",
|
"sass": "^1.58.3",
|
||||||
|
|
|
@ -1,15 +1,11 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
import { Provider } from 'react-redux';
|
|
||||||
import { store } from './app/store';
|
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
|
||||||
test('renders learn react link', () => {
|
test('renders learn react link', () => {
|
||||||
const { getByText } = render(
|
const { getByText } = render(
|
||||||
<Provider store={store}>
|
<App />
|
||||||
<App />
|
);
|
||||||
</Provider>
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(getByText(/learn/i)).toBeInTheDocument();
|
expect(getByText(/recupera stato/i)).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { RadioToggler } from './features/radio/Radio';
|
import { RadioToggler } from './Radio';
|
||||||
import { LogViewer } from './features/radio/LogViewer';
|
import { LogViewer } from './LogViewer';
|
||||||
import './App.css';
|
import './App.css';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
|
@ -1,21 +1,25 @@
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { useAppSelector, useAppDispatch } from '../../app/hooks';
|
import { requestLogs } from './radioAPI';
|
||||||
import { selectLogsLoading, selectLogs, requestLogsThunk } from './radioSlice';
|
|
||||||
import styles from './LogViewer.module.scss';
|
import styles from './LogViewer.module.scss';
|
||||||
|
|
||||||
export function LogViewer() {
|
export function LogViewer() {
|
||||||
const loading = useAppSelector(selectLogsLoading);
|
const [loading, setLoading] = useState(false);
|
||||||
const logs = useAppSelector(selectLogs);
|
const [logs, setLogs] = useState<string[] | undefined>(undefined);
|
||||||
const [hidden, setHidden] = useState(true);
|
const [hidden, setHidden] = useState(true);
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const onRequestLogsButtonClick = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
const logs = await requestLogs();
|
||||||
|
setLogs(logs);
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.logsContainer}>
|
<div className={styles.logsContainer}>
|
||||||
<div className={styles.buttonContainer}>
|
<div className={styles.buttonContainer}>
|
||||||
<button
|
<button
|
||||||
className={styles.button}
|
className={styles.button}
|
||||||
onClick={() => dispatch(requestLogsThunk())}
|
onClick={onRequestLogsButtonClick}
|
||||||
>Recupera stato</button>
|
>Recupera stato</button>
|
||||||
<button
|
<button
|
||||||
className={styles.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 React from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
import { Provider } from 'react-redux';
|
|
||||||
import { store } from './app/store';
|
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import './index.css';
|
import './index.css';
|
||||||
|
|
||||||
|
@ -10,8 +8,6 @@ const root = createRoot(container);
|
||||||
|
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<Provider store={store}>
|
<App />
|
||||||
<App />
|
|
||||||
</Provider>
|
|
||||||
</React.StrictMode>
|
</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 () => {
|
export const requestToStart = async () => {
|
||||||
console.log("[REST] request to start");
|
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"
|
resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
|
||||||
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
|
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"
|
version "7.20.13"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b"
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.13.tgz#7055ab8a7cff2b8f6058bf6ae45ff84ad2aded4b"
|
||||||
integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==
|
integrity sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==
|
||||||
|
@ -1940,14 +1940,6 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@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":
|
"@types/html-minifier-terser@^6.0.0":
|
||||||
version "6.1.0"
|
version "6.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
|
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"
|
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.3.tgz#a136f83b0758698df454e328759dbd3d44555311"
|
||||||
integrity sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==
|
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":
|
"@types/ws@^8.5.1":
|
||||||
version "8.5.4"
|
version "8.5.4"
|
||||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5"
|
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"
|
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
|
||||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
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:
|
hoopy@^0.1.4:
|
||||||
version "0.1.4"
|
version "0.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/hoopy/-/hoopy-0.1.4.tgz#609207d661100033a9a9402ad3dea677381c1b1d"
|
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"
|
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.11.tgz#92835de5841c5cf08ba00ddd2d677b6d17ff9adb"
|
||||||
integrity sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==
|
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"
|
version "16.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
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"
|
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
|
||||||
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
|
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:
|
react-refresh@^0.11.0:
|
||||||
version "0.11.0"
|
version "0.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.11.0.tgz#77198b944733f0f1f1a90e791de4541f9f074046"
|
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"
|
querystringify "^2.1.1"
|
||||||
requires-port "^1.0.0"
|
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:
|
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||||
|
|
Loading…
Reference in New Issue
Block a user