import React, { useEffect, useRef, useState } from 'react'; import { useDispatch, useSelector, connect } from 'react-redux'; import { Snackbar } from '@mui/material'; import { useNavigate } from 'react-router-dom'; import { positionsActions, devicesActions, sessionActions } from './store'; import { useEffectAsync } from './reactHelper'; import { useTranslation } from './common/components/LocalizationProvider'; import { prefixString } from './common/util/stringUtils'; import { snackBarDurationLongMs } from './common/util/duration'; import usePersistedState from './common/util/usePersistedState'; import alarm from './resources/alarm.mp3'; const SocketController = () => { const dispatch = useDispatch(); const navigate = useNavigate(); const t = useTranslation(); const authenticated = useSelector((state) => !!state.session.user); const devices = useSelector((state) => state.devices.items); const socketRef = useRef(); const [events, setEvents] = useState([]); const [notifications, setNotifications] = useState([]); const [soundEvents] = usePersistedState('soundEvents', []); const [soundAlarms] = usePersistedState('soundAlarms', ['sos']); const connectSocket = () => { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const socket = new WebSocket(`${protocol}//${window.location.host}/api/socket`); socketRef.current = socket; socket.onopen = () => { dispatch(sessionActions.updateSocket(true)); }; socket.onerror = async () => { dispatch(sessionActions.updateSocket(false)); const devicesResponse = await fetch('/api/devices'); if (devicesResponse.ok) { dispatch(devicesActions.update(await devicesResponse.json())); } const positionsResponse = await fetch('/api/positions', { headers: { 'Content-Type': 'application/json' }, }); if (positionsResponse.ok) { dispatch(positionsActions.update(await positionsResponse.json())); } setTimeout(() => connectSocket(), 60000); }; socket.onmessage = (event) => { const data = JSON.parse(event.data); if (data.devices) { dispatch(devicesActions.update(data.devices)); } if (data.positions) { dispatch(positionsActions.update(data.positions)); } if (data.events) { setEvents(data.events); } }; }; useEffectAsync(async () => { const response = await fetch('/api/server'); if (response.ok) { dispatch(sessionActions.updateServer(await response.json())); } else { throw Error(await response.text()); } }, []); useEffectAsync(async () => { if (authenticated) { const response = await fetch('/api/devices'); if (response.ok) { dispatch(devicesActions.refresh(await response.json())); } else { throw Error(await response.text()); } connectSocket(); return () => { const socket = socketRef.current; if (socket) { socket.close(); } }; } const response = await fetch('/api/session'); if (response.ok) { dispatch(sessionActions.updateUser(await response.json())); } else { navigate('/login'); } return null; }, [authenticated]); useEffect(() => { setNotifications(events.map((event) => ({ id: event.id, message: `${devices[event.deviceId]?.name}: ${t(prefixString('event', event.type))}`, show: true, }))); }, [events, devices, t]); useEffect(() => { events.forEach((event) => { if (soundEvents.includes(event.type) || (event.type === 'alarm' && soundAlarms.includes(event.attributes.alarm))) { new Audio(alarm).play(); } }); }, [events, soundEvents, soundAlarms]); return ( <> {notifications.map((notification) => ( setEvents(events.filter((e) => e.id !== notification.id))} /> ))} ); }; export default connect()(SocketController);