Auto-reconnect on server loss

This commit is contained in:
James Ketr 2025-09-01 15:08:52 -07:00
parent 450357db79
commit 2910789c86
2 changed files with 156 additions and 0 deletions

View File

@ -0,0 +1,112 @@
import React, { useState, useEffect } from 'react';
import {
Box,
Typography,
CircularProgress,
Paper,
LinearProgress
} from '@mui/material';
import { ReadyState } from 'react-use-websocket';
interface ConnectionStatusProps {
readyState: ReadyState;
reconnectAttempt?: number;
}
const ConnectionStatus: React.FC<ConnectionStatusProps> = ({
readyState,
reconnectAttempt = 0
}) => {
const [countdown, setCountdown] = useState(0);
// Start countdown when connection is closed and we're attempting to reconnect
useEffect(() => {
if (readyState === ReadyState.CLOSED && reconnectAttempt > 0) {
setCountdown(5);
const interval = setInterval(() => {
setCountdown(prev => {
if (prev <= 1) {
clearInterval(interval);
return 0;
}
return prev - 1;
});
}, 1000);
return () => clearInterval(interval);
}
}, [readyState, reconnectAttempt]);
const getConnectionStatusText = () => {
switch (readyState) {
case ReadyState.CONNECTING:
return reconnectAttempt > 0 ? `Reconnecting... (attempt ${reconnectAttempt})` : 'Connecting to server...';
case ReadyState.OPEN:
return 'Connected';
case ReadyState.CLOSING:
return 'Disconnecting...';
case ReadyState.CLOSED:
if (reconnectAttempt > 0 && countdown > 0) {
return `Connection lost. Retrying in ${countdown}s... (attempt ${reconnectAttempt})`;
}
return reconnectAttempt > 0 ? 'Reconnecting...' : 'Disconnected';
case ReadyState.UNINSTANTIATED:
return 'Initializing...';
default:
return 'Unknown connection state';
}
};
const getConnectionColor = () => {
switch (readyState) {
case ReadyState.OPEN:
return 'success.main';
case ReadyState.CONNECTING:
return 'info.main';
case ReadyState.CLOSED:
return 'error.main';
case ReadyState.CLOSING:
return 'warning.main';
default:
return 'text.secondary';
}
};
const shouldShowProgress = readyState === ReadyState.CONNECTING ||
(readyState === ReadyState.CLOSED && reconnectAttempt > 0);
return (
<Paper
sx={{
p: 2,
m: 2,
width: 'fit-content',
backgroundColor: readyState === ReadyState.CLOSED ? '#ffebee' : 'background.paper'
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
{shouldShowProgress && (
<CircularProgress size={20} />
)}
<Typography
variant="h6"
color={getConnectionColor()}
>
{getConnectionStatusText()}
</Typography>
</Box>
{readyState === ReadyState.CLOSED && reconnectAttempt > 0 && countdown > 0 && (
<Box sx={{ mt: 2, width: '100%' }}>
<LinearProgress
variant="determinate"
value={((5 - countdown) / 5) * 100}
sx={{ borderRadius: 1 }}
/>
</Box>
)}
</Paper>
);
};
export default ConnectionStatus;

View File

@ -0,0 +1,44 @@
import React from 'react';
import { Box, CircularProgress, Typography } from '@mui/material';
interface WebRTCStatusProps {
isNegotiating: boolean;
connectionState?: string;
}
const WebRTCStatus: React.FC<WebRTCStatusProps> = ({
isNegotiating,
connectionState
}) => {
if (!isNegotiating && connectionState !== 'connecting') {
return null;
}
return (
<Box
className="webrtc-status"
sx={{
position: 'absolute',
top: '50%',
left: '50%',
transform: 'translate(-50%, -50%)',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 1,
backgroundColor: 'rgba(0, 0, 0, 0.7)',
color: 'white',
padding: 2,
borderRadius: 2,
zIndex: 10
}}
>
<CircularProgress size={24} color="inherit" />
<Typography variant="caption">
{isNegotiating ? 'Negotiating WebRTC...' : 'Connecting...'}
</Typography>
</Box>
);
};
export default WebRTCStatus;