diff --git a/webclient/src/containers/Login/Login.tsx b/webclient/src/containers/Login/Login.tsx
index 679ccdc0c..85c121be2 100644
--- a/webclient/src/containers/Login/Login.tsx
+++ b/webclient/src/containers/Login/Login.tsx
@@ -9,7 +9,7 @@ import Typography from '@material-ui/core/Typography';
import { AuthenticationService } from 'api';
-import { RegistrationDialog, RequestPasswordResetDialog, ResetPasswordDialog } from 'dialogs';
+import { RegistrationDialog, RequestPasswordResetDialog, ResetPasswordDialog, AccountActivationDialog } from 'dialogs';
import { LoginForm } from 'forms';
import { useReduxEffect, useFireOnce } from 'hooks';
import { Images } from 'images';
@@ -65,6 +65,7 @@ const Login = ({ state, description }: LoginProps) => {
passwordResetRequestDialog: false,
resetPasswordDialog: false,
registrationDialog: false,
+ activationDialog: false,
});
const [userToResetPassword, setUserToResetPassword] = useState(null);
@@ -77,6 +78,24 @@ const Login = ({ state, description }: LoginProps) => {
closeResetPasswordDialog();
}, ServerTypes.RESET_PASSWORD_SUCCESS, []);
+ useReduxEffect(() => {
+ closeActivateAccountDialog();
+ }, ServerTypes.ACCOUNT_ACTIVATION_SUCCESS, []);
+
+ useReduxEffect(() => {
+ closeRegistrationDialog();
+ openActivateAccountDialog();
+ }, ServerTypes.ACCOUNT_AWAITING_ACTIVATION, []);
+
+ useReduxEffect(({ options: { hashedPassword } }) => {
+ if (hostIdToRemember) {
+ HostDTO.get(hostIdToRemember).then(host => {
+ host.hashedPassword = hashedPassword;
+ host.save();
+ });
+ }
+ }, ServerTypes.LOGIN_SUCCESSFUL, [hostIdToRemember]);
+
const showDescription = () => {
return !isConnected && description?.length;
};
@@ -112,18 +131,7 @@ const Login = ({ state, description }: LoginProps) => {
AuthenticationService.login(options as WebSocketConnectOptions);
}, []);
- const [submitButtonDisabled, resetSubmitButton, handleLogin] = useFireOnce(onSubmitLogin)
-
- useReduxEffect(({ options: { hashedPassword } }) => {
- if (hostIdToRemember) {
- HostDTO.get(hostIdToRemember).then(host => {
- host.hashedPassword = hashedPassword;
- host.save();
- });
- }
- resetSubmitButton()
- }, ServerTypes.LOGIN_SUCCESSFUL, [hostIdToRemember]);
-
+ const [submitButtonDisabled, resetSubmitButton, handleLogin] = useFireOnce(onSubmitLogin);
const updateHost = ({ selectedHost, userName, hashedPassword, remember }) => {
HostDTO.get(selectedHost.id).then(hostDTO => {
@@ -165,6 +173,10 @@ const Login = ({ state, description }: LoginProps) => {
AuthenticationService.resetPassword({ userName, token, newPassword, host, port } as any);
};
+ const handleAccountActivationDialogSubmit = ({ token }) => {
+ AuthenticationService.activateAccount({ token } as any);
+ };
+
const skipTokenRequest = (userName) => {
setUserToResetPassword(userName);
@@ -198,6 +210,14 @@ const Login = ({ state, description }: LoginProps) => {
setDialogState(s => ({ ...s, registrationDialog: true }));
}
+ const closeActivateAccountDialog = () => {
+ setDialogState(s => ({ ...s, activationDialog: false }));
+ };
+
+ const openActivateAccountDialog = () => {
+ setDialogState(s => ({ ...s, activationDialog: true }));
+ };
+
return (
{ isConnected &&
}
@@ -296,6 +316,12 @@ const Login = ({ state, description }: LoginProps) => {
handleClose={closeResetPasswordDialog}
userName={userToResetPassword}
/>
+
+
);
}
diff --git a/webclient/src/dialogs/AccountActivationDialog/AccountActivationDialog.css b/webclient/src/dialogs/AccountActivationDialog/AccountActivationDialog.css
new file mode 100644
index 000000000..5175ab845
--- /dev/null
+++ b/webclient/src/dialogs/AccountActivationDialog/AccountActivationDialog.css
@@ -0,0 +1,13 @@
+.dialog-title {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.MuiDialogTitle-root.dialog-title {
+ padding-bottom: 0;
+}
+
+.content {
+ margin-bottom: 20px;
+}
\ No newline at end of file
diff --git a/webclient/src/dialogs/AccountActivationDialog/AccountActivationDialog.tsx b/webclient/src/dialogs/AccountActivationDialog/AccountActivationDialog.tsx
new file mode 100644
index 000000000..970a140f7
--- /dev/null
+++ b/webclient/src/dialogs/AccountActivationDialog/AccountActivationDialog.tsx
@@ -0,0 +1,41 @@
+import React from 'react';
+import Dialog from '@material-ui/core/Dialog';
+import DialogContent from '@material-ui/core/DialogContent';
+import DialogTitle from '@material-ui/core/DialogTitle';
+import IconButton from '@material-ui/core/IconButton';
+import CloseIcon from '@material-ui/icons/Close';
+import Typography from '@material-ui/core/Typography';
+
+import { AccountActivationForm } from 'forms';
+
+import './AccountActivationDialog.css';
+
+const AccountActivationDialog = ({ classes, handleClose, isOpen, onSubmit }: any) => {
+ const handleOnClose = () => {
+ handleClose();
+ }
+
+ return (
+
+ );
+};
+
+export default AccountActivationDialog;
diff --git a/webclient/src/dialogs/index.ts b/webclient/src/dialogs/index.ts
index d2af9ce70..5c5ace1c5 100644
--- a/webclient/src/dialogs/index.ts
+++ b/webclient/src/dialogs/index.ts
@@ -1,3 +1,4 @@
+export { default as AccountActivationDialog } from './AccountActivationDialog/AccountActivationDialog';
export { default as CardImportDialog } from './CardImportDialog/CardImportDialog';
export { default as KnownHostDialog } from './KnownHostDialog/KnownHostDialog';
export { default as RegistrationDialog } from './RegistrationDialog/RegistrationDialog';
diff --git a/webclient/src/forms/AccountActivationForm/AccountActivationForm.css b/webclient/src/forms/AccountActivationForm/AccountActivationForm.css
new file mode 100644
index 000000000..ffb4ecc77
--- /dev/null
+++ b/webclient/src/forms/AccountActivationForm/AccountActivationForm.css
@@ -0,0 +1,12 @@
+.AccountActivationForm {
+ width: 100%;
+ padding-bottom: 15px;
+}
+
+.AccountActivationForm-item {
+ margin-bottom: 20px;
+}
+
+.AccountActivationForm-submit {
+ width: 100%;
+}
diff --git a/webclient/src/forms/AccountActivationForm/AccountActivationForm.tsx b/webclient/src/forms/AccountActivationForm/AccountActivationForm.tsx
new file mode 100644
index 000000000..1a821adaf
--- /dev/null
+++ b/webclient/src/forms/AccountActivationForm/AccountActivationForm.tsx
@@ -0,0 +1,64 @@
+// eslint-disable-next-line
+import React, { useState } from "react";
+import { connect } from 'react-redux';
+import { Form, Field } from 'react-final-form';
+import { OnChange } from 'react-final-form-listeners';
+
+import Button from '@material-ui/core/Button';
+import Typography from '@material-ui/core/Typography';
+
+import { InputField, KnownHosts } from 'components';
+import { FormKey } from 'types';
+
+import './AccountActivationForm.css';
+import { useReduxEffect } from 'hooks';
+import { ServerTypes } from 'store';
+
+const AccountActivationForm = ({ onSubmit }) => {
+ const [errorMessage, setErrorMessage] = useState(false);
+
+ useReduxEffect(() => {
+ setErrorMessage(true);
+ }, ServerTypes.ACCOUNT_ACTIVATION_FAILED, []);
+
+ const handleOnSubmit = (form) => {
+ setErrorMessage(false);
+ onSubmit(form);
+ }
+
+ const validate = values => {
+ const errors: any = {};
+
+ if (!values.token) {
+ errors.token = 'Required';
+ }
+
+ return errors;
+ };
+
+ return (
+
+ );
+ }}
+
+ );
+};
+
+export default AccountActivationForm;
diff --git a/webclient/src/forms/index.ts b/webclient/src/forms/index.ts
index 34f36fe4e..1740694a2 100644
--- a/webclient/src/forms/index.ts
+++ b/webclient/src/forms/index.ts
@@ -1,3 +1,4 @@
+export { default as AccountActivationForm } from './AccountActivationForm/AccountActivationForm';
export { default as CardImportForm } from './CardImportForm/CardImportForm';
export { default as ConnectForm } from './ConnectForm/ConnectForm';
export { default as LoginForm } from './LoginForm/LoginForm';
diff --git a/webclient/src/store/server/server.actions.ts b/webclient/src/store/server/server.actions.ts
index 720e2ed8d..d1a3b2f45 100644
--- a/webclient/src/store/server/server.actions.ts
+++ b/webclient/src/store/server/server.actions.ts
@@ -93,6 +93,15 @@ export const Actions = {
type: Types.REGISTRATION_USERNAME_ERROR,
error
}),
+ accountAwaitingActivation: () => ({
+ type: Types.ACCOUNT_AWAITING_ACTIVATION,
+ }),
+ accountActivationSuccess: () => ({
+ type: Types.ACCOUNT_ACTIVATION_SUCCESS,
+ }),
+ accountActivationFailed: () => ({
+ type: Types.ACCOUNT_ACTIVATION_FAILED,
+ }),
resetPassword: () => ({
type: Types.RESET_PASSWORD_REQUESTED,
}),
diff --git a/webclient/src/store/server/server.dispatch.ts b/webclient/src/store/server/server.dispatch.ts
index 9db1e189c..a489edbc4 100644
--- a/webclient/src/store/server/server.dispatch.ts
+++ b/webclient/src/store/server/server.dispatch.ts
@@ -80,6 +80,15 @@ export const Dispatch = {
registrationUserNameError: (error) => {
store.dispatch(Actions.registrationUserNameError(error));
},
+ accountAwaitingActivation: () => {
+ store.dispatch(Actions.accountAwaitingActivation());
+ },
+ accountActivationSuccess: () => {
+ store.dispatch(Actions.accountActivationSuccess());
+ },
+ accountActivationFailed: () => {
+ store.dispatch(Actions.accountActivationFailed());
+ },
resetPassword: () => {
store.dispatch(Actions.resetPassword());
},
diff --git a/webclient/src/store/server/server.types.ts b/webclient/src/store/server/server.types.ts
index 4acd8108d..5767c3a8d 100644
--- a/webclient/src/store/server/server.types.ts
+++ b/webclient/src/store/server/server.types.ts
@@ -22,6 +22,9 @@ export const Types = {
REGISTRATION_EMAIL_ERROR: '[Server] Registration Email Error',
REGISTRATION_PASSWORD_ERROR: '[Server] Registration Password Error',
REGISTRATION_USERNAME_ERROR: '[Server] Registration Username Error',
+ ACCOUNT_AWAITING_ACTIVATION: '[Server] Account Awaiting Activation',
+ ACCOUNT_ACTIVATION_SUCCESS: '[Server] Account Activation Success',
+ ACCOUNT_ACTIVATION_FAILED: '[Server] Account Activation Failed',
RESET_PASSWORD_REQUESTED: '[Server] Reset Password Requested',
RESET_PASSWORD_FAILED: '[Server] Reset Password Failed',
RESET_PASSWORD_CHALLENGE: '[Server] Reset Password Challenge',
diff --git a/webclient/src/websocket/commands/SessionCommands.ts b/webclient/src/websocket/commands/SessionCommands.ts
index 00300fed9..354d908a0 100644
--- a/webclient/src/websocket/commands/SessionCommands.ts
+++ b/webclient/src/websocket/commands/SessionCommands.ts
@@ -137,7 +137,7 @@ export class SessionCommands {
webClient.protobuf.sendSessionCommand(sc, raw => {
switch (raw.responseCode) {
case webClient.protobuf.controller.Response.ResponseCode.RespOk: {
- const passwordSalt = raw['.Response_PasswordSalt.ext'].passwordSalt;
+ const passwordSalt = raw['.Response_PasswordSalt.ext']?.passwordSalt;
SessionCommands.login(passwordSalt);
break;
}
@@ -236,6 +236,7 @@ export class SessionCommands {
webClient.protobuf.sendSessionCommand(sc, raw => {
if (raw.responseCode === webClient.protobuf.controller.Response.ResponseCode.RespActivationAccepted) {
+ SessionPersistence.accountActivationSuccess();
SessionCommands.login();
} else {
SessionCommands.updateStatus(StatusEnum.DISCONNECTED, 'Account Activation Failed');
diff --git a/webclient/src/websocket/persistence/SessionPersistence.ts b/webclient/src/websocket/persistence/SessionPersistence.ts
index 237a4664d..78e385a28 100644
--- a/webclient/src/websocket/persistence/SessionPersistence.ts
+++ b/webclient/src/websocket/persistence/SessionPersistence.ts
@@ -78,11 +78,15 @@ export class SessionPersistence {
}
static accountAwaitingActivation() {
- console.log('Open Modal for Activation Code input');
+ ServerDispatch.accountAwaitingActivation();
+ }
+
+ static accountActivationSuccess() {
+ ServerDispatch.accountActivationSuccess();
}
static accountActivationFailed() {
- console.log('Account activation failed, show an action here');
+ ServerDispatch.accountActivationFailed();
}
static registrationRequiresEmail() {