mirror of
https://github.com/Cockatrice/Cockatrice.git
synced 2026-04-27 07:48:01 -07:00
Webatrice: KnownHosts component (#4456)
* refactor dexie services for future schema updates Co-authored-by: Jeremy Letto <jeremy.letto@datasite.com>
This commit is contained in:
parent
37879c4255
commit
6ce346af4a
54 changed files with 1381 additions and 1291 deletions
21
webclient/src/forms/KnownHostForm/KnownHostForm.css
Normal file
21
webclient/src/forms/KnownHostForm/KnownHostForm.css
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
.KnownHostForm {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.KnownHostForm-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.KnownHostForm-submit {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.KnownHostForm-actions {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 5px 0 10px;
|
||||
color: red;
|
||||
}
|
||||
83
webclient/src/forms/KnownHostForm/KnownHostForm.tsx
Normal file
83
webclient/src/forms/KnownHostForm/KnownHostForm.tsx
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
// eslint-disable-next-line
|
||||
import React, { useState } from "react";
|
||||
import { connect } from 'react-redux';
|
||||
import { Form, Field } from 'react-final-form'
|
||||
|
||||
import Button from '@material-ui/core/Button';
|
||||
import AnchorLink from '@material-ui/core/Link';
|
||||
|
||||
import { InputField } from 'components';
|
||||
|
||||
import './KnownHostForm.css';
|
||||
|
||||
function KnownHostForm({ host, onRemove, onSubmit }) {
|
||||
const [confirmDelete, setConfirmDelete] = useState(false);
|
||||
|
||||
return (
|
||||
<Form
|
||||
initialValues={{
|
||||
id: host?.id,
|
||||
name: host?.name,
|
||||
host: host?.host,
|
||||
port: host?.port,
|
||||
}}
|
||||
onSubmit={onSubmit}
|
||||
validate={values => {
|
||||
const errors: any = {};
|
||||
|
||||
if (!values.name) {
|
||||
errors.name = 'Required'
|
||||
}
|
||||
|
||||
if (!values.host) {
|
||||
errors.host = 'Required'
|
||||
}
|
||||
|
||||
if (!values.port) {
|
||||
errors.port = 'Required'
|
||||
}
|
||||
|
||||
if (Object.keys(errors).length) {
|
||||
return errors;
|
||||
}
|
||||
}}
|
||||
>
|
||||
{({ handleSubmit }) => (
|
||||
<form className="KnownHostForm" onSubmit={handleSubmit}>
|
||||
<div className="KnownHostForm-item">
|
||||
<Field label="Host Name" name="name" component={InputField} />
|
||||
</div>
|
||||
<div className="KnownHostForm-item">
|
||||
<Field label="Host Address" name="host" component={InputField} />
|
||||
</div>
|
||||
<div className="KnownHostForm-item">
|
||||
<Field label="Port" name="port" type="number" component={InputField} />
|
||||
</div>
|
||||
|
||||
<Button className="KnownHostForm-submit" color="primary" variant="contained" type="submit">
|
||||
{host ? 'Save Changes' : 'Add Host' }
|
||||
</Button>
|
||||
|
||||
<div className="KnownHostForm-actions">
|
||||
<div className="KnownHostForm-actions__delete">
|
||||
{ host && (
|
||||
<Button color="inherit" onClick={() => !confirmDelete ? setConfirmDelete(true) : onRemove(host)}>
|
||||
{ !confirmDelete ? 'Delete' : 'Are you sure?' }
|
||||
</Button>
|
||||
) }
|
||||
</div>
|
||||
<AnchorLink href='https://github.com/Cockatrice/Cockatrice/wiki/Public-Servers' target='_blank'>
|
||||
Need help adding a new host?
|
||||
</AnchorLink>
|
||||
</div>
|
||||
</form>
|
||||
) }
|
||||
</Form>
|
||||
);
|
||||
}
|
||||
|
||||
const mapStateToProps = () => ({
|
||||
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(KnownHostForm);
|
||||
|
|
@ -1,46 +1,130 @@
|
|||
// eslint-disable-next-line
|
||||
import React from "react";
|
||||
import React, { Component, useCallback, useEffect, useState, useRef } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Form, Field, reduxForm, change } from 'redux-form'
|
||||
import { Form, Field, reduxForm, change, FormSubmitHandler } from 'redux-form'
|
||||
|
||||
import Button from '@material-ui/core/Button';
|
||||
|
||||
import { InputField, KnownHosts } from 'components';
|
||||
// import { ServerDispatch } from "store";
|
||||
import { FormKey } from 'types';
|
||||
import { AuthenticationService } from 'api';
|
||||
import { CheckboxField, InputField, KnownHosts } from 'components';
|
||||
import { useAutoConnect } from 'hooks';
|
||||
import { HostDTO, SettingDTO } from 'services';
|
||||
import { FormKey, APP_USER } from 'types';
|
||||
|
||||
import './LoginForm.css';
|
||||
|
||||
const LoginForm = (props) => {
|
||||
const { dispatch, handleSubmit } = props;
|
||||
const PASSWORD_LABEL = 'Password';
|
||||
const STORED_PASSWORD_LABEL = '* SAVED *';
|
||||
|
||||
const LoginForm: any = ({ dispatch, form, submit, handleSubmit }: LoginFormProps) => {
|
||||
const password: any = useRef();
|
||||
const [host, setHost] = useState(null);
|
||||
const [remember, setRemember] = useState(false);
|
||||
const [passwordLabel, setPasswordLabel] = useState(PASSWORD_LABEL);
|
||||
const [hasStoredPassword, useStoredPassword] = useState(false);
|
||||
|
||||
const [autoConnect, setAutoConnect] = useAutoConnect(() => {
|
||||
dispatch(change(form, 'autoConnect', autoConnect));
|
||||
|
||||
if (autoConnect && !remember) {
|
||||
setRemember(true);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
SettingDTO.get(APP_USER).then((userSetting: SettingDTO) => {
|
||||
if (userSetting?.autoConnect && !AuthenticationService.connectionAttemptMade()) {
|
||||
HostDTO.getAll().then(hosts => {
|
||||
let lastSelectedHost = hosts.find(({ lastSelected }) => lastSelected);
|
||||
|
||||
if (lastSelectedHost?.remember && lastSelectedHost?.hashedPassword) {
|
||||
dispatch(change(form, 'selectedHost', lastSelectedHost));
|
||||
dispatch(change(form, 'userName', lastSelectedHost.userName));
|
||||
dispatch(change(form, 'remember', true));
|
||||
setPasswordLabel(STORED_PASSWORD_LABEL);
|
||||
dispatch(submit);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [submit, dispatch, form]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(change(form, 'remember', remember));
|
||||
|
||||
if (!remember) {
|
||||
setAutoConnect(false);
|
||||
}
|
||||
|
||||
if (!remember) {
|
||||
useStoredPassword(false);
|
||||
setPasswordLabel(PASSWORD_LABEL);
|
||||
} else if (host?.hashedPassword) {
|
||||
useStoredPassword(true);
|
||||
setPasswordLabel(STORED_PASSWORD_LABEL);
|
||||
}
|
||||
}, [remember, dispatch, form]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!host) {
|
||||
return
|
||||
}
|
||||
|
||||
dispatch(change(form, 'userName', host.userName));
|
||||
dispatch(change(form, 'password', ''));
|
||||
|
||||
setRemember(host.remember);
|
||||
setAutoConnect(host.remember && autoConnect);
|
||||
|
||||
if (host.remember && host.hashedPassword) {
|
||||
// TODO: check if this causes a double render (maybe try combined state)
|
||||
// try deriving useStoredPassword
|
||||
useStoredPassword(true);
|
||||
setPasswordLabel(STORED_PASSWORD_LABEL);
|
||||
} else {
|
||||
useStoredPassword(false);
|
||||
setPasswordLabel(PASSWORD_LABEL);
|
||||
}
|
||||
}, [host, dispatch, form]);
|
||||
|
||||
const onRememberChange = event => setRemember(event.target.checked);
|
||||
const onAutoConnectChange = event => setAutoConnect(event.target.checked);
|
||||
const onHostChange = h => setHost(h);
|
||||
|
||||
const forgotPassword = () => {
|
||||
console.log('Show recover password dialog, then AuthService.forgotPasswordRequest');
|
||||
};
|
||||
|
||||
const onHostChange = ({ host, port }) => {
|
||||
dispatch(change(FormKey.LOGIN, 'host', host));
|
||||
dispatch(change(FormKey.LOGIN, 'port', port));
|
||||
}
|
||||
|
||||
return (
|
||||
<Form className="loginForm" onSubmit={handleSubmit}>
|
||||
<div className="loginForm-items">
|
||||
<div className="loginForm-item">
|
||||
<Field label="Username" name="user" component={InputField} autoComplete="username" />
|
||||
<Form className='loginForm' onSubmit={handleSubmit}>
|
||||
<div className='loginForm-items'>
|
||||
<div className='loginForm-item'>
|
||||
<Field label='Username' name='userName' component={InputField} autoComplete='off' />
|
||||
</div>
|
||||
<div className="loginForm-item">
|
||||
<Field label="Password" name="pass" type="password" component={InputField} autoComplete="current-password" />
|
||||
<div className='loginForm-item'>
|
||||
<Field
|
||||
label={passwordLabel}
|
||||
ref={password}
|
||||
onFocus={() => setPasswordLabel(PASSWORD_LABEL)}
|
||||
onBlur={() => !password.current.value && hasStoredPassword && setPasswordLabel(STORED_PASSWORD_LABEL)}
|
||||
name='password'
|
||||
type='password'
|
||||
component={InputField}
|
||||
autoComplete='new-password'
|
||||
/>
|
||||
</div>
|
||||
<div className="loginForm-actions">
|
||||
<span>Remember Me</span>
|
||||
<Button color="primary" onClick={forgotPassword}>Forgot Password</Button>
|
||||
<div className='loginForm-actions'>
|
||||
<Field label='Save Password' name='remember' component={CheckboxField} onChange={onRememberChange} />
|
||||
<Button color='primary' onClick={forgotPassword}>Forgot Password</Button>
|
||||
</div>
|
||||
<div className="loginForm-item">
|
||||
<KnownHosts onChange={onHostChange} />
|
||||
<div className='loginForm-item'>
|
||||
<Field name='selectedHost' component={KnownHosts} onChange={onHostChange} />
|
||||
</div>
|
||||
<div className='loginForm-actions'>
|
||||
<Field label='Auto Connect' name='autoConnect' component={CheckboxField} onChange={onAutoConnectChange} />
|
||||
</div>
|
||||
</div>
|
||||
<Button className="loginForm-submit rounded tall" color="primary" variant="contained" type="submit">
|
||||
<Button className='loginForm-submit rounded tall' color='primary' variant='contained' type='submit'>
|
||||
Login
|
||||
</Button>
|
||||
</Form>
|
||||
|
|
@ -55,27 +139,28 @@ const propsMap = {
|
|||
if (!values.user) {
|
||||
errors.user = 'Required';
|
||||
}
|
||||
if (!values.pass) {
|
||||
errors.pass = 'Required';
|
||||
|
||||
if (!values.password && !values.selectedHost?.hashedPassword) {
|
||||
errors.password = 'Required';
|
||||
}
|
||||
if (!values.host) {
|
||||
errors.host = 'Required';
|
||||
}
|
||||
if (!values.port) {
|
||||
errors.port = 'Required';
|
||||
|
||||
if (!values.selectedHost) {
|
||||
errors.selectedHost = 'Required';
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
};
|
||||
|
||||
const mapStateToProps = () => ({
|
||||
initialValues: {
|
||||
// host: "mtg.tetrarch.co/servatrice",
|
||||
// port: "443"
|
||||
// host: "server.cockatrice.us",
|
||||
// port: "4748"
|
||||
}
|
||||
interface LoginFormProps {
|
||||
form: string;
|
||||
dispatch: Function;
|
||||
submit: Function;
|
||||
handleSubmit: FormSubmitHandler;
|
||||
}
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(reduxForm(propsMap)(LoginForm));
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// eslint-disable-next-line
|
||||
import React, { Component } from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Form, Field, reduxForm, change } from 'redux-form'
|
||||
|
||||
|
|
@ -13,15 +13,16 @@ import './RegisterForm.css';
|
|||
const RegisterForm = (props) => {
|
||||
const { dispatch, handleSubmit } = props;
|
||||
|
||||
const onHostChange = ({ host, port }) => {
|
||||
const onHostChange: any = ({ host, port }) => {
|
||||
dispatch(change(FormKey.REGISTER, 'host', host));
|
||||
dispatch(change(FormKey.REGISTER, 'port', port));
|
||||
}
|
||||
|
||||
return (
|
||||
<Form className="registerForm row" onSubmit={handleSubmit} autoComplete="off">
|
||||
<div className="leftRegisterForm column" >
|
||||
<div className="registerForm-item">
|
||||
<KnownHosts onChange={onHostChange} />
|
||||
<Field name="selectedHost" component={KnownHosts} onChange={onHostChange} />
|
||||
{ /* Padding is off */ }
|
||||
</div>
|
||||
<div className="registerForm-item">
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const RequestPasswordResetForm = (props) => {
|
|||
const [errorMessage, setErrorMessage] = useState(false);
|
||||
const [isMFA, setIsMFA] = useState(false);
|
||||
|
||||
const onHostChange = ({ host, port }) => {
|
||||
const onHostChange: any = ({ host, port }) => {
|
||||
dispatch(change(FormKey.RESET_PASSWORD_REQUEST, 'host', host));
|
||||
dispatch(change(FormKey.RESET_PASSWORD_REQUEST, 'port', port));
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@ const RequestPasswordResetForm = (props) => {
|
|||
</div>
|
||||
) : null}
|
||||
<div className="RequestPasswordResetForm-item">
|
||||
<KnownHosts onChange={onHostChange} />
|
||||
<Field name='selectedHost' component={KnownHosts} onChange={onHostChange} />
|
||||
</div>
|
||||
</div>
|
||||
<Button className="RequestPasswordResetForm-submit rounded tall" color="primary" variant="contained" type="submit">
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const ResetPasswordForm = (props) => {
|
|||
const [errorMessage, setErrorMessage] = useState(false);
|
||||
|
||||
|
||||
const onHostChange = ({ host, port }) => {
|
||||
const onHostChange: any = ({ host, port }) => {
|
||||
dispatch(change(FormKey.RESET_PASSWORD, 'host', host));
|
||||
dispatch(change(FormKey.RESET_PASSWORD, 'port', port));
|
||||
}
|
||||
|
|
@ -47,7 +47,7 @@ const ResetPasswordForm = (props) => {
|
|||
<Field label="Password Again" name="passwordAgain" component={InputField} />
|
||||
</div>
|
||||
<div className="ResetPasswordForm-item">
|
||||
<KnownHosts onChange={onHostChange} />
|
||||
<Field name='selectedHost' component={KnownHosts} onChange={onHostChange} />
|
||||
</div>
|
||||
</div>
|
||||
<Button className="ResetPasswordForm-submit rounded tall" color="primary" variant="contained" type="submit">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
export { default as CardImportForm } from './CardImportForm/CardImportForm';
|
||||
export { default as ConnectForm } from './ConnectForm/ConnectForm';
|
||||
export { default as LoginForm } from './LoginForm/LoginForm';
|
||||
export { default as KnownHostForm } from './KnownHostForm/KnownHostForm';
|
||||
export { default as RegisterForm } from './RegisterForm/RegisterForm';
|
||||
export { default as SearchForm } from './SearchForm/SearchForm';
|
||||
export { default as RequestPasswordResetForm } from './RequestPasswordResetForm/RequestPasswordResetForm';
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue