test connection UI (#4596)
* test connection UI * cleanup Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
This commit is contained in:
parent
00a2a8ab71
commit
0ff59e6d1e
11 changed files with 169 additions and 22 deletions
|
@ -6,6 +6,10 @@ export default class AuthenticationService {
|
||||||
SessionCommands.connect(options, WebSocketConnectReason.LOGIN);
|
SessionCommands.connect(options, WebSocketConnectReason.LOGIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static testConnection(options: WebSocketConnectOptions): void {
|
||||||
|
SessionCommands.connect(options, WebSocketConnectReason.TEST_CONNECTION);
|
||||||
|
}
|
||||||
|
|
||||||
static register(options: WebSocketConnectOptions): void {
|
static register(options: WebSocketConnectOptions): void {
|
||||||
SessionCommands.connect(options, WebSocketConnectReason.REGISTER);
|
SessionCommands.connect(options, WebSocketConnectReason.REGISTER);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,23 @@
|
||||||
.KnownHosts {
|
.KnownHosts {
|
||||||
|
}
|
||||||
|
|
||||||
|
.KnownHosts-form {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.KnownHosts-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.KnownHosts-item__wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.KnownHosts-item__label {
|
.KnownHosts-item__label {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
@ -16,6 +31,16 @@
|
||||||
font-size: .9em;
|
font-size: .9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.KnownHosts-item__status {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.KnownHosts-item__status svg {
|
||||||
|
display: none;
|
||||||
|
margin-left: -5px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.KnownHosts-validation {
|
.KnownHosts-validation {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
@ -33,6 +58,11 @@
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.KnownHosts .MuiSelect-selectMenu .KnownHosts-item__status,
|
||||||
|
.KnownHosts .MuiSelect-selectMenu .KnownHosts-item__status svg {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.Mui-selected .KnownHosts-item__label svg {
|
.Mui-selected .KnownHosts-item__label svg {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,8 @@ import { Select, MenuItem } from '@material-ui/core';
|
||||||
import Button from '@material-ui/core/Button';
|
import Button from '@material-ui/core/Button';
|
||||||
import FormControl from '@material-ui/core/FormControl';
|
import FormControl from '@material-ui/core/FormControl';
|
||||||
import IconButton from '@material-ui/core/IconButton';
|
import IconButton from '@material-ui/core/IconButton';
|
||||||
|
import WifiTetheringIcon from '@material-ui/icons/WifiTethering';
|
||||||
|
import PortableWifiOffIcon from '@material-ui/icons/PortableWifiOff';
|
||||||
import InputLabel from '@material-ui/core/InputLabel';
|
import InputLabel from '@material-ui/core/InputLabel';
|
||||||
import { makeStyles } from '@material-ui/core/styles';
|
import { makeStyles } from '@material-ui/core/styles';
|
||||||
import Check from '@material-ui/icons/Check';
|
import Check from '@material-ui/icons/Check';
|
||||||
|
@ -11,13 +13,22 @@ import AddIcon from '@material-ui/icons/Add';
|
||||||
import EditRoundedIcon from '@material-ui/icons/Edit';
|
import EditRoundedIcon from '@material-ui/icons/Edit';
|
||||||
import ErrorOutlinedIcon from '@material-ui/icons/ErrorOutlined';
|
import ErrorOutlinedIcon from '@material-ui/icons/ErrorOutlined';
|
||||||
|
|
||||||
|
import { AuthenticationService } from 'api';
|
||||||
import { KnownHostDialog } from 'dialogs';
|
import { KnownHostDialog } from 'dialogs';
|
||||||
|
import { useReduxEffect } from 'hooks';
|
||||||
import { HostDTO } from 'services';
|
import { HostDTO } from 'services';
|
||||||
|
import { ServerTypes } from 'store';
|
||||||
import { DefaultHosts, Host, getHostPort } from 'types';
|
import { DefaultHosts, Host, getHostPort } from 'types';
|
||||||
import Toast from 'components/Toast/Toast';
|
import Toast from 'components/Toast/Toast';
|
||||||
|
|
||||||
import './KnownHosts.css';
|
import './KnownHosts.css';
|
||||||
|
|
||||||
|
enum TestConnection {
|
||||||
|
TESTING = 'testing',
|
||||||
|
FAILED = 'failed',
|
||||||
|
SUCCESS = 'success',
|
||||||
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
root: {
|
root: {
|
||||||
'& .KnownHosts-error': {
|
'& .KnownHosts-error': {
|
||||||
|
@ -26,6 +37,18 @@ const useStyles = makeStyles(theme => ({
|
||||||
|
|
||||||
'& .KnownHosts-warning': {
|
'& .KnownHosts-warning': {
|
||||||
color: theme.palette.warning.main
|
color: theme.palette.warning.main
|
||||||
|
},
|
||||||
|
|
||||||
|
'& .KnownHosts-item': {
|
||||||
|
[`& .${TestConnection.TESTING}`]: {
|
||||||
|
color: theme.palette.warning.main
|
||||||
|
},
|
||||||
|
[`& .${TestConnection.FAILED}`]: {
|
||||||
|
color: theme.palette.error.main
|
||||||
|
},
|
||||||
|
[`& .${TestConnection.SUCCESS}`]: {
|
||||||
|
color: theme.palette.success.main
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
@ -46,9 +69,11 @@ const KnownHosts = (props) => {
|
||||||
edit: null,
|
edit: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [showCreateToast, setShowCreateToast] = useState(false)
|
const [testingConnection, setTestingConnection] = useState<TestConnection>(null);
|
||||||
const [showDeleteToast, setShowDeleteToast] = useState(false)
|
|
||||||
const [showEditToast, setShowEditToast] = useState(false)
|
const [showCreateToast, setShowCreateToast] = useState(false);
|
||||||
|
const [showDeleteToast, setShowDeleteToast] = useState(false);
|
||||||
|
const [showEditToast, setShowEditToast] = useState(false);
|
||||||
|
|
||||||
const loadKnownHosts = useCallback(async () => {
|
const loadKnownHosts = useCallback(async () => {
|
||||||
const hosts = await HostDTO.getAll();
|
const hosts = await HostDTO.getAll();
|
||||||
|
@ -77,6 +102,14 @@ const KnownHosts = (props) => {
|
||||||
}
|
}
|
||||||
}, [hostsState, onChange]);
|
}, [hostsState, onChange]);
|
||||||
|
|
||||||
|
useReduxEffect(() => {
|
||||||
|
setTestingConnection(TestConnection.SUCCESS);
|
||||||
|
}, ServerTypes.TEST_CONNECTION_SUCCESSFUL, []);
|
||||||
|
|
||||||
|
useReduxEffect(() => {
|
||||||
|
setTestingConnection(TestConnection.FAILED);
|
||||||
|
}, ServerTypes.TEST_CONNECTION_FAILED, []);
|
||||||
|
|
||||||
const selectHost = (selectedHost) => {
|
const selectHost = (selectedHost) => {
|
||||||
setHostsState(s => ({ ...s, selectedHost }));
|
setHostsState(s => ({ ...s, selectedHost }));
|
||||||
};
|
};
|
||||||
|
@ -135,6 +168,8 @@ const KnownHosts = (props) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateLastSelectedHost = (hostId): Promise<any[]> => {
|
const updateLastSelectedHost = (hostId): Promise<any[]> => {
|
||||||
|
testConnection();
|
||||||
|
|
||||||
return HostDTO.getAll().then(hosts =>
|
return HostDTO.getAll().then(hosts =>
|
||||||
hosts.map(async host => {
|
hosts.map(async host => {
|
||||||
if (host.id === hostId) {
|
if (host.id === hostId) {
|
||||||
|
@ -152,20 +187,27 @@ const KnownHosts = (props) => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const testConnection = () => {
|
||||||
|
setTestingConnection(TestConnection.TESTING);
|
||||||
|
|
||||||
|
const options = { ...getHostPort(hostsState.selectedHost) };
|
||||||
|
AuthenticationService.testConnection(options);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className={'KnownHosts ' + classes.root}>
|
||||||
<FormControl variant='outlined' className={'KnownHosts ' + classes.root}>
|
<FormControl variant='outlined' className='KnownHosts-form'>
|
||||||
{ touched && (
|
{ touched && (
|
||||||
<div className="KnownHosts-validation">
|
<div className='KnownHosts-validation'>
|
||||||
{
|
{
|
||||||
(error &&
|
(error &&
|
||||||
<div className="KnownHosts-error">
|
<div className='KnownHosts-error'>
|
||||||
{error}
|
{error}
|
||||||
<ErrorOutlinedIcon style={{ fontSize: 'small', fontWeight: 'bold' }} />
|
<ErrorOutlinedIcon style={{ fontSize: 'small', fontWeight: 'bold' }} />
|
||||||
</div>
|
</div>
|
||||||
) ||
|
) ||
|
||||||
|
|
||||||
(warning && <div className="KnownHosts-warning">{warning}</div>)
|
(warning && <div className='KnownHosts-warning'>{warning}</div>)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
) }
|
) }
|
||||||
|
@ -189,19 +231,33 @@ const KnownHosts = (props) => {
|
||||||
|
|
||||||
{
|
{
|
||||||
hostsState.hosts.map((host, index) => (
|
hostsState.hosts.map((host, index) => (
|
||||||
<MenuItem className='KnownHosts-item' value={host} key={index}>
|
<MenuItem value={host} key={index}>
|
||||||
<div className='KnownHosts-item__label'>
|
<div className='KnownHosts-item'>
|
||||||
<Check />
|
<div className='KnownHosts-item__wrapper'>
|
||||||
<span>{host.name} ({ getHostPort(hostsState.hosts[index]).host }:{getHostPort(hostsState.hosts[index]).port})</span>
|
<div className='KnownHosts-item__status'>
|
||||||
</div>
|
<div className={testingConnection}>
|
||||||
|
{
|
||||||
|
testingConnection === TestConnection.FAILED
|
||||||
|
? <PortableWifiOffIcon fontSize="small" />
|
||||||
|
: <WifiTetheringIcon fontSize="small" />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
{ host.editable && (
|
<div className='KnownHosts-item__label'>
|
||||||
<IconButton className='KnownHosts-item__edit' size='small' color='primary' onClick={(e) => {
|
<Check />
|
||||||
openEditKnownHostDialog(hostsState.hosts[index]);
|
<span>{host.name} ({ getHostPort(hostsState.hosts[index]).host }:{getHostPort(hostsState.hosts[index]).port})</span>
|
||||||
}}>
|
</div>
|
||||||
<EditRoundedIcon fontSize='small' />
|
</div>
|
||||||
</IconButton>
|
|
||||||
) }
|
{ host.editable && (
|
||||||
|
<IconButton className='KnownHosts-item__edit' size='small' color='primary' onClick={(e) => {
|
||||||
|
openEditKnownHostDialog(hostsState.hosts[index]);
|
||||||
|
}}>
|
||||||
|
<EditRoundedIcon fontSize='small' />
|
||||||
|
</IconButton>
|
||||||
|
) }
|
||||||
|
</div>
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,12 @@ export const Actions = {
|
||||||
connectionFailed: () => ({
|
connectionFailed: () => ({
|
||||||
type: Types.CONNECTION_FAILED,
|
type: Types.CONNECTION_FAILED,
|
||||||
}),
|
}),
|
||||||
|
testConnectionSuccessful: () => ({
|
||||||
|
type: Types.TEST_CONNECTION_SUCCESSFUL,
|
||||||
|
}),
|
||||||
|
testConnectionFailed: () => ({
|
||||||
|
type: Types.TEST_CONNECTION_FAILED,
|
||||||
|
}),
|
||||||
serverMessage: message => ({
|
serverMessage: message => ({
|
||||||
type: Types.SERVER_MESSAGE,
|
type: Types.SERVER_MESSAGE,
|
||||||
message
|
message
|
||||||
|
|
|
@ -22,6 +22,12 @@ export const Dispatch = {
|
||||||
connectionFailed: () => {
|
connectionFailed: () => {
|
||||||
store.dispatch(Actions.connectionFailed());
|
store.dispatch(Actions.connectionFailed());
|
||||||
},
|
},
|
||||||
|
testConnectionSuccessful: () => {
|
||||||
|
store.dispatch(Actions.testConnectionSuccessful());
|
||||||
|
},
|
||||||
|
testConnectionFailed: () => {
|
||||||
|
store.dispatch(Actions.testConnectionFailed());
|
||||||
|
},
|
||||||
updateBuddyList: buddyList => {
|
updateBuddyList: buddyList => {
|
||||||
store.dispatch(Actions.updateBuddyList(buddyList));
|
store.dispatch(Actions.updateBuddyList(buddyList));
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,6 +5,8 @@ export const Types = {
|
||||||
LOGIN_FAILED: '[Server] Login Failed',
|
LOGIN_FAILED: '[Server] Login Failed',
|
||||||
CONNECTION_CLOSED: '[Server] Connection Closed',
|
CONNECTION_CLOSED: '[Server] Connection Closed',
|
||||||
CONNECTION_FAILED: '[Server] Connection Failed',
|
CONNECTION_FAILED: '[Server] Connection Failed',
|
||||||
|
TEST_CONNECTION_SUCCESSFUL: '[Server] Test Connection Successful',
|
||||||
|
TEST_CONNECTION_FAILED: '[Server] Test Connection Failed',
|
||||||
SERVER_MESSAGE: '[Server] Server Message',
|
SERVER_MESSAGE: '[Server] Server Message',
|
||||||
UPDATE_BUDDY_LIST: '[Server] Update Buddy List',
|
UPDATE_BUDDY_LIST: '[Server] Update Buddy List',
|
||||||
ADD_TO_BUDDY_LIST: '[Server] Add to Buddy List',
|
ADD_TO_BUDDY_LIST: '[Server] Add to Buddy List',
|
||||||
|
|
|
@ -35,7 +35,8 @@ export enum WebSocketConnectReason {
|
||||||
ACTIVATE_ACCOUNT,
|
ACTIVATE_ACCOUNT,
|
||||||
PASSWORD_RESET_REQUEST,
|
PASSWORD_RESET_REQUEST,
|
||||||
PASSWORD_RESET_CHALLENGE,
|
PASSWORD_RESET_CHALLENGE,
|
||||||
PASSWORD_RESET
|
PASSWORD_RESET,
|
||||||
|
TEST_CONNECTION,
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Host {
|
export class Host {
|
||||||
|
|
|
@ -57,7 +57,11 @@ export class WebClient {
|
||||||
public connect(options: WebSocketConnectOptions) {
|
public connect(options: WebSocketConnectOptions) {
|
||||||
this.connectionAttemptMade = true;
|
this.connectionAttemptMade = true;
|
||||||
this.options = options;
|
this.options = options;
|
||||||
this.socket.connect(this.options);
|
this.socket.connect(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
public testConnect(options: WebSocketConnectOptions) {
|
||||||
|
this.socket.testConnect(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public disconnect() {
|
public disconnect() {
|
||||||
|
|
|
@ -25,6 +25,9 @@ export class SessionCommands {
|
||||||
case WebSocketConnectReason.PASSWORD_RESET:
|
case WebSocketConnectReason.PASSWORD_RESET:
|
||||||
SessionCommands.updateStatus(StatusEnum.CONNECTING, 'Connecting...');
|
SessionCommands.updateStatus(StatusEnum.CONNECTING, 'Connecting...');
|
||||||
break;
|
break;
|
||||||
|
case WebSocketConnectReason.TEST_CONNECTION:
|
||||||
|
webClient.testConnect({ ...options });
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
SessionCommands.updateStatus(StatusEnum.DISCONNECTED, 'Unknown Connection Attempt: ' + reason);
|
SessionCommands.updateStatus(StatusEnum.DISCONNECTED, 'Unknown Connection Attempt: ' + reason);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -29,6 +29,14 @@ export class SessionPersistence {
|
||||||
ServerDispatch.connectionFailed();
|
ServerDispatch.connectionFailed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static testConnectionSuccessful() {
|
||||||
|
ServerDispatch.testConnectionSuccessful();
|
||||||
|
}
|
||||||
|
|
||||||
|
static testConnectionFailed() {
|
||||||
|
ServerDispatch.testConnectionFailed();
|
||||||
|
}
|
||||||
|
|
||||||
static updateBuddyList(buddyList) {
|
static updateBuddyList(buddyList) {
|
||||||
ServerDispatch.updateBuddyList(buddyList);
|
ServerDispatch.updateBuddyList(buddyList);
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,16 @@ export class WebSocketService {
|
||||||
this.socket = this.createWebSocket(`${protocol}://${host}:${port}`);
|
this.socket = this.createWebSocket(`${protocol}://${host}:${port}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public testConnect(options: WebSocketConnectOptions, protocol: string = 'wss'): void {
|
||||||
|
if (window.location.hostname === 'localhost') {
|
||||||
|
protocol = 'ws';
|
||||||
|
}
|
||||||
|
|
||||||
|
const { host, port } = options;
|
||||||
|
|
||||||
|
this.testWebSocket(`${protocol}://${host}:${port}`);
|
||||||
|
}
|
||||||
|
|
||||||
public disconnect(): void {
|
public disconnect(): void {
|
||||||
if (this.socket) {
|
if (this.socket) {
|
||||||
this.socket.close();
|
this.socket.close();
|
||||||
|
@ -92,4 +102,21 @@ export class WebSocketService {
|
||||||
|
|
||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private testWebSocket(url: string): void {
|
||||||
|
const socket = new WebSocket(url);
|
||||||
|
socket.binaryType = 'arraybuffer';
|
||||||
|
|
||||||
|
const connectionTimer = setTimeout(() => socket.close(), this.webClient.clientOptions.keepalive);
|
||||||
|
|
||||||
|
socket.onopen = () => {
|
||||||
|
clearTimeout(connectionTimer);
|
||||||
|
SessionPersistence.testConnectionSuccessful();
|
||||||
|
socket.close();
|
||||||
|
};
|
||||||
|
|
||||||
|
socket.onerror = () => {
|
||||||
|
SessionPersistence.testConnectionFailed();
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue