Skip to content

Commit

Permalink
Handle the websocket connection for incoming message using a redux mi…
Browse files Browse the repository at this point in the history
…ddleware
  • Loading branch information
mfechner committed Jan 15, 2025
1 parent c73cbdf commit fc642af
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 48 deletions.
48 changes: 2 additions & 46 deletions ui/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import React, {useEffect} from 'react';
import React from 'react';
import {createHashRouter, RouterProvider} from 'react-router-dom';
import Applications from './application/Applications.tsx';
import Clients from './client/Clients.tsx';
import {checkAuthLoader} from './common/Auth.ts';
import Messages from './message/Messages.tsx';
import {WebSocketStore} from './message/WebSocketStore.ts';
import PluginsRootLayout from './pages/plugins.tsx';
import RootLayout from './pages/root';
import PluginDetailView from './plugin/PluginDetailView.tsx';
import Plugins from './plugin/Plugins.tsx';
import * as Notifications from './snack/browserNotification.ts';
import {useAppDispatch, useAppSelector} from './store';
import {messageActions} from './message/message-slice.ts';
import Login from './user/Login.tsx';
import Users from './user/Users.tsx';

Expand Down Expand Up @@ -71,46 +67,6 @@ const router = createHashRouter([
},
]);

const ws = new WebSocketStore();

const App = () => {
const dispatch = useAppDispatch();
const loggedIn = useAppSelector((state) => state.auth.loggedIn);
const reloadRequired = useAppSelector((state) => state.ui.reloadRequired);

useEffect(() => {
if (loggedIn) {
ws.listen((message) => {
dispatch(messageActions.loading(true));
dispatch(messageActions.add(message));
Notifications.notifyNewMessage(message);
if (message.priority >= 4) {
const src = 'static/notification.ogg';
const audio = new Audio(src);
audio.play();
}
});
window.onbeforeunload = () => {
ws.close();
};
} else {
ws.close();
}
}, [dispatch, loggedIn, reloadRequired]);


return (
<RouterProvider router={router} />
);
};
const App = () => (<RouterProvider router={router} />);

export default App;

/*
<Routes>
{authenticating ? (<Route path="/" element={<LoadingSpinner />} />) : null}
<Route path="/" element={<Messages />} />
<Route path="messages/:id" element={<Messages />} />
</Routes>
*/
40 changes: 39 additions & 1 deletion ui/src/message/WebSocketStore.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import {Middleware} from '@reduxjs/toolkit';
import {AxiosError} from 'axios';
import {getAuthToken} from '../common/Auth.ts';
import * as config from '../config';
import * as Notifications from '../snack/browserNotification.ts';
import {tryAuthenticate} from '../user/auth-actions.ts';
import {uiActions} from '../store/ui-slice.ts';
import {IMessage} from '../types';
import state from '../store/index.ts';
import state, {RootState} from '../store/index.ts';
import {messageActions} from './message-slice.ts';

export class WebSocketStore {
private wsActive = false;
Expand Down Expand Up @@ -57,3 +60,38 @@ export class WebSocketStore {

public close = () => this.ws?.close(1000, 'WebSocketStore#close');
}

const ws = new WebSocketStore();

export const handleWebsocketMiddleware: Middleware<{}, RootState> =
(store) => (next) => (action) => {
const prevLoginStatus = store.getState().auth.loggedIn;
const prevReloadRequired = store.getState().ui.reloadRequired;
const result = next(action);
const nextLoginStatus = store.getState().auth.loggedIn;
const nextReloadRequired = store.getState().ui.reloadRequired;

// Open websocket connection on login and if a reload is required
if ((action.type.startsWith('auth/login') && prevLoginStatus !== nextLoginStatus)
|| (action.type.startsWith('ui/setReloadRequired') && prevReloadRequired !== nextReloadRequired && nextReloadRequired)
) {
ws.listen((message) => {
store.dispatch(messageActions.loading(true));
store.dispatch(messageActions.add(message));
Notifications.notifyNewMessage(message);
if (message.priority >= 4) {
const src = 'static/notification.ogg';
const audio = new Audio(src);
audio.play();
}
});
window.onbeforeunload = () => {
ws.close();
};
}
// Close websocket if the user logs out
if (action.type.startsWith('auth/logout') && prevLoginStatus !== nextLoginStatus && !nextLoginStatus) {
ws.close();
}
return result;
};
3 changes: 2 additions & 1 deletion ui/src/store/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {configureStore} from '@reduxjs/toolkit';
import {useDispatch, useSelector} from 'react-redux';
import {handleWebsocketMiddleware} from '../message/WebSocketStore.ts';

import uiReducer from './ui-slice';
import authReducer from '../user/auth-slice';
Expand All @@ -21,7 +22,7 @@ const store = configureStore({
message: messageReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(connectionErrorMiddleware),
getDefaultMiddleware().concat(handleWebsocketMiddleware, connectionErrorMiddleware),
});

// Infer the `RootState` and `AppDispatch` types from the store itself
Expand Down

0 comments on commit fc642af

Please sign in to comment.