diff --git a/packages/concordia-app/public/locales/en/translation.json b/packages/concordia-app/public/locales/en/translation.json
index 443ce20..61d79a1 100644
--- a/packages/concordia-app/public/locales/en/translation.json
+++ b/packages/concordia-app/public/locales/en/translation.json
@@ -3,17 +3,28 @@
"board.sub.header.no.topics.guest": "Sign up and be the first to post.",
"board.sub.header.no.topics.user": "Be the first to post.",
"custom.loading.tab.pane.default.generic.message": "Magic in the background",
+ "edit.information.modal.form.cancel.button": "Cancel",
+ "edit.information.modal.form.error.invalid.profile.picture.url.message": "The profile picture URL provided is not valid.",
+ "edit.information.modal.form.error.message.header": "Form contains errors",
+ "edit.information.modal.form.location.field.label": "Location",
+ "edit.information.modal.form.location.field.placeholder": "Location",
+ "edit.information.modal.form.profile.picture.field.label": "Profile picture URL",
+ "edit.information.modal.form.profile.picture.field.placeholder": "URL",
+ "edit.information.modal.form.submit.button": "Submit",
+ "edit.information.modal.title": "Edit profile information",
"post.create.form.send.button": "Post",
"post.form.content.field.placeholder": "Message",
"post.form.subject.field.placeholder": "Subject",
"post.list.row.post.id": "#{{id}}",
"profile.general.tab.address.row.title": "Account address:",
+ "profile.general.tab.edit.info.button.title": "Edit information",
"profile.general.tab.location.row.not.set": "Not set",
"profile.general.tab.location.row.title": "Location:",
"profile.general.tab.number.of.posts.row.title": "Number of posts:",
"profile.general.tab.number.of.topics.row.title": "Number of topics created:",
"profile.general.tab.posts.db.address.row.title": "PostsDB:",
"profile.general.tab.registration.date.row.title": "Member since:",
+ "profile.general.tab.save.info.button.title": "Save information",
"profile.general.tab.title": "General",
"profile.general.tab.topics.db.address.row.title": "TopicsDB:",
"profile.general.tab.user.db.address.row.title": "UserDB:",
@@ -39,10 +50,7 @@
"register.form.sign.up.step.button.submit": "Sign Up",
"register.form.sign.up.step.description": "Create a Concordia account",
"register.form.sign.up.step.error.message.header": "Form contains errors",
- "register.form.sign.up.step.error.username.taken.message": "The username {{username}} is already taken.",
"register.form.sign.up.step.title": "Sign Up",
- "register.form.sign.up.step.username.field.label": "Username",
- "register.form.sign.up.step.username.field.placeholder": "Username",
"register.p.account.address": "Account address:",
"topbar.button.create.topic": "Create topic",
"topbar.button.profile": "Profile",
@@ -54,5 +62,9 @@
"topic.create.form.subject.field.placeholder": "Subject",
"topic.list.row.author": "by {{author}}",
"topic.list.row.number.of.replies": "{{numberOfReplies}} replies",
- "topic.list.row.topic.id": "#{{id}}"
-}
+ "topic.list.row.topic.id": "#{{id}}",
+ "username.selector.error.username.empty.message": "Username is required",
+ "username.selector.error.username.taken.message": "The username {{username}} is already taken.",
+ "username.selector.username.field.label": "Username",
+ "username.selector.username.field.placeholder": "Username"
+}
\ No newline at end of file
diff --git a/packages/concordia-app/src/assets/images/metamask_logo.svg b/packages/concordia-app/src/assets/images/metamask_logo.svg
new file mode 100644
index 0000000..a6cffef
--- /dev/null
+++ b/packages/concordia-app/src/assets/images/metamask_logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/packages/concordia-app/src/components/InitializationScreen/CustomLoader/index.jsx b/packages/concordia-app/src/components/InitializationScreen/CustomLoader/index.jsx
index 99bcedf..32ea822 100644
--- a/packages/concordia-app/src/components/InitializationScreen/CustomLoader/index.jsx
+++ b/packages/concordia-app/src/components/InitializationScreen/CustomLoader/index.jsx
@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import { Container, Progress } from 'semantic-ui-react';
// Images
+import metamaskLogo from '../../../assets/images/metamask_logo.svg';
import ethereumLogo from '../../../assets/images/ethereum_logo.svg';
import ipfsLogo from '../../../assets/images/ipfs_logo.svg';
import orbitdbLogo from '../../../assets/images/orbitdb_logo.svg';
@@ -20,7 +21,10 @@ const LoadingComponent = (props) => {
let imageSrc; let imageAlt; let listItems; let indicating; let
error;
- if (imageType === 'ethereum') {
+ if (imageType === 'metamask') {
+ imageSrc = metamaskLogo;
+ imageAlt = 'metamask_logo';
+ } else if (imageType === 'ethereum') {
imageSrc = ethereumLogo;
imageAlt = 'ethereum_logo';
} else if (imageType === 'ipfs') {
diff --git a/packages/concordia-app/src/components/InitializationScreen/index.jsx b/packages/concordia-app/src/components/InitializationScreen/index.jsx
index 0e46ac3..7a2dc5a 100644
--- a/packages/concordia-app/src/components/InitializationScreen/index.jsx
+++ b/packages/concordia-app/src/components/InitializationScreen/index.jsx
@@ -25,9 +25,9 @@ const InitializationLoader = ({ children }) => {
MetaMask, ' first.']}
- imageType="ethereum"
+ imageType="metamask"
progress={10}
- progressType="indicating"
+ progressType="error"
/>
);
}
diff --git a/packages/concordia-app/src/components/UsernameSelector.jsx b/packages/concordia-app/src/components/UsernameSelector.jsx
new file mode 100644
index 0000000..530cd7a
--- /dev/null
+++ b/packages/concordia-app/src/components/UsernameSelector.jsx
@@ -0,0 +1,104 @@
+import React, { useCallback, useEffect, useMemo } from 'react';
+import {
+ Form, Input,
+} from 'semantic-ui-react';
+import throttle from 'lodash/throttle';
+import { useTranslation } from 'react-i18next';
+import { useSelector } from 'react-redux';
+import PropTypes from 'prop-types';
+import { drizzle } from '../redux/store';
+import { FORUM_CONTRACT } from '../constants/contracts/ContractNames';
+
+const { contracts: { [FORUM_CONTRACT]: { methods: { isUserNameTaken } } } } = drizzle;
+
+const UsernameSelector = (props) => {
+ const {
+ initialUsername, username, onChangeCallback, onErrorChangeCallback,
+ } = props;
+ const isUserNameTakenResults = useSelector((state) => state.contracts[FORUM_CONTRACT].isUserNameTaken);
+ const { t } = useTranslation();
+
+ useEffect(() => {
+ if (username.length > 0) {
+ const checkedUsernames = Object
+ .values(isUserNameTakenResults)
+ .map((callCompleted) => ({
+ checkedUsername: callCompleted.args[0],
+ isTaken: callCompleted.value,
+ }));
+
+ const checkedUsername = checkedUsernames
+ .find((callCompleted) => callCompleted.checkedUsername === username);
+
+ if (checkedUsername && checkedUsername.isTaken && username !== initialUsername) {
+ onErrorChangeCallback({
+ usernameChecked: true,
+ error: true,
+ errorMessage: t('username.selector.error.username.taken.message', { username }),
+ });
+ } else {
+ onErrorChangeCallback({
+ usernameChecked: checkedUsername !== undefined,
+ error: false,
+ errorMessage: null,
+ });
+ }
+
+ return;
+ }
+
+ // Username input is empty
+ if (initialUsername && initialUsername !== '') {
+ onErrorChangeCallback({
+ usernameChecked: true,
+ error: true,
+ errorMessage: t('username.selector.error.username.empty.message'),
+ });
+ } else {
+ onErrorChangeCallback({
+ usernameChecked: true,
+ error: false,
+ errorMessage: null,
+ });
+ }
+ }, [initialUsername, isUserNameTakenResults, onErrorChangeCallback, t, username, username.length]);
+
+ const checkUsernameTaken = useMemo(() => throttle(
+ (usernameToCheck) => {
+ isUserNameTaken.cacheCall(usernameToCheck);
+ }, 200,
+ ), []);
+
+ const handleInputChange = useCallback((event, { value }) => {
+ onChangeCallback(value);
+
+ if (value.length > 0) {
+ checkUsernameTaken(value);
+ }
+ }, [checkUsernameTaken, onChangeCallback]);
+
+ return (
+
+
+
+
+ );
+};
+
+UsernameSelector.propTypes = {
+ initialUsername: PropTypes.string,
+ username: PropTypes.string.isRequired,
+ onChangeCallback: PropTypes.func.isRequired,
+ onErrorChangeCallback: PropTypes.func.isRequired,
+};
+
+export default UsernameSelector;
diff --git a/packages/concordia-app/src/views/Profile/GeneralTab/EditInformationModal/index.jsx b/packages/concordia-app/src/views/Profile/GeneralTab/EditInformationModal/index.jsx
new file mode 100644
index 0000000..1290c7e
--- /dev/null
+++ b/packages/concordia-app/src/views/Profile/GeneralTab/EditInformationModal/index.jsx
@@ -0,0 +1,235 @@
+import React, {
+ useCallback, useEffect, useMemo, useState,
+} from 'react';
+import {
+ Button, Form, Icon, Image, Input, Message, Modal,
+} from 'semantic-ui-react';
+import PropTypes from 'prop-types';
+import { useTranslation } from 'react-i18next';
+import checkUrlValid from '../../../../utils/urlUtils';
+import { USER_LOCATION, USER_PROFILE_PICTURE } from '../../../../constants/orbit/UserDatabaseKeys';
+import { USER_DATABASE } from '../../../../constants/orbit/OrbitDatabases';
+import { breeze, drizzle } from '../../../../redux/store';
+import UsernameSelector from '../../../../components/UsernameSelector';
+import { FORUM_CONTRACT } from '../../../../constants/contracts/ContractNames';
+
+const { orbit: { stores } } = breeze;
+const { contracts: { [FORUM_CONTRACT]: { methods: { updateUsername } } } } = drizzle;
+
+const EditInformationModal = (props) => {
+ const {
+ initialUsername, initialAuthorAvatar, initialUserLocation, open, onSubmit, onCancel,
+ } = props;
+ const [usernameInput, setUsernameInput] = useState(initialUsername);
+ const [usernameChecked, setUsernameChecked] = useState(true);
+ const [profilePictureInput, setProfilePictureInput] = useState('');
+ const [profilePictureUrlValid, setProfilePictureUrlValid] = useState(true);
+ const [locationInput, setLocationInput] = useState('');
+ const [error, setError] = useState(false);
+ const [errorMessages, setErrorMessages] = useState([]);
+ const [usernameError, setUsernameError] = useState(false);
+ const [usernameErrorMessage, setUsernameErrorMessage] = useState('');
+ const { t } = useTranslation();
+
+ useEffect(() => {
+ setLocationInput(initialUserLocation || '');
+ }, [initialUserLocation]);
+
+ useEffect(() => {
+ setProfilePictureInput(initialAuthorAvatar || '');
+ setProfilePictureUrlValid(initialAuthorAvatar ? checkUrlValid(initialAuthorAvatar) : true);
+ }, [initialAuthorAvatar]);
+
+ useEffect(() => {
+ let formHasError = false;
+ const formErrors = [];
+
+ if (!profilePictureUrlValid) {
+ formHasError = true;
+ formErrors.push(t('edit.information.modal.form.error.invalid.profile.picture.url.message'));
+ }
+
+ setError(formHasError);
+ setErrorMessages(formErrors);
+ }, [profilePictureUrlValid, t]);
+
+ const handleUsernameChange = (modifiedUsername) => {
+ setUsernameInput(modifiedUsername);
+ };
+
+ const handleUsernameErrorChange = useCallback(({
+ usernameChecked: isUsernameChecked,
+ error: hasUsernameError,
+ errorMessage,
+ }) => {
+ setUsernameChecked(isUsernameChecked);
+
+ if (hasUsernameError) {
+ setUsernameError(true);
+ setUsernameErrorMessage(errorMessage);
+ } else {
+ setUsernameError(false);
+ }
+ }, []);
+
+ const handleInputChange = useCallback((event, { name, value }) => {
+ if (name === 'profilePictureInput') {
+ setProfilePictureInput(value);
+
+ if (value.length > 0) {
+ setProfilePictureUrlValid(checkUrlValid(value));
+ } else {
+ setProfilePictureUrlValid(true);
+ }
+ }
+
+ if (name === 'locationInput') {
+ setLocationInput(value);
+ }
+ }, []);
+
+ const profilePicture = useMemo(() => (profilePictureInput.length > 0 && profilePictureUrlValid
+ ? ()
+ : ()
+ ), [profilePictureInput, profilePictureUrlValid]);
+
+ const handleSubmit = useCallback(() => {
+ const keyValuesToStore = [];
+
+ keyValuesToStore.push({
+ key: USER_PROFILE_PICTURE,
+ value: profilePictureInput,
+ });
+
+ keyValuesToStore.push({
+ key: USER_LOCATION,
+ value: locationInput,
+ });
+
+ const userDb = Object.values(stores).find((store) => store.dbname === USER_DATABASE);
+
+ const promiseArray = keyValuesToStore
+ .map((keyValueToStore) => {
+ if (keyValueToStore.value !== '') {
+ return userDb
+ .put(keyValueToStore.key, keyValueToStore.value, { pin: true });
+ }
+
+ return userDb.del(keyValueToStore.key);
+ });
+
+ Promise
+ .all(promiseArray)
+ .then(() => {
+ // TODO: display a message
+ })
+ .catch((reason) => {
+ console.log(reason);
+ });
+
+ if (usernameInput !== initialUsername) {
+ updateUsername.cacheSend(usernameInput);
+ }
+
+ onSubmit();
+ }, [initialUsername, locationInput, onSubmit, profilePictureInput, usernameInput]);
+
+ return useMemo(() => (
+
+ {t('edit.information.modal.title')}
+
+ {profilePicture}
+
+
+
+
+
+
+
+
+
+
+ {error === true && (
+ errorMessages
+ .map((errorMessage) => (
+
+ ))
+ )}
+ {usernameError === true && (
+
+ )}
+
+
+
+
+
+
+
+ ), [
+ error, errorMessages, handleInputChange, handleSubmit, handleUsernameErrorChange, initialUsername, locationInput,
+ onCancel, open, profilePicture, profilePictureInput, t, usernameChecked, usernameError, usernameErrorMessage,
+ usernameInput,
+ ]);
+};
+
+EditInformationModal.defaultProps = {
+ open: false,
+};
+
+EditInformationModal.propTypes = {
+ profileAddress: PropTypes.string.isRequired,
+ initialUsername: PropTypes.string.isRequired,
+ initialAuthorAvatar: PropTypes.string,
+ initialUserLocation: PropTypes.string,
+ open: PropTypes.bool,
+ onSubmit: PropTypes.func.isRequired,
+ onCancel: PropTypes.func.isRequired,
+};
+
+export default EditInformationModal;
diff --git a/packages/concordia-app/src/views/Profile/GeneralTab/index.jsx b/packages/concordia-app/src/views/Profile/GeneralTab/index.jsx
index 6dddc44..9e1b974 100644
--- a/packages/concordia-app/src/views/Profile/GeneralTab/index.jsx
+++ b/packages/concordia-app/src/views/Profile/GeneralTab/index.jsx
@@ -1,6 +1,6 @@
import React, { useEffect, useMemo, useState } from 'react';
import {
- Icon, Image, Placeholder, Table,
+ Button, Icon, Image, Placeholder, Table,
} from 'semantic-ui-react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
@@ -11,17 +11,21 @@ import { FETCH_USER_DATABASE } from '../../../redux/actions/peerDbReplicationAct
import { breeze } from '../../../redux/store';
import { USER_LOCATION, USER_PROFILE_PICTURE } from '../../../constants/orbit/UserDatabaseKeys';
import './styles.css';
+import EditInformationModal from './EditInformationModal';
const { orbit } = breeze;
const GeneralTab = (props) => {
const {
- profileAddress, username, numberOfTopics, numberOfPosts, userRegistrationTimestamp,
+ profileAddress, username, numberOfTopics, numberOfPosts, userRegistrationTimestamp, isSelf,
} = props;
const [userInfoOrbitAddress, setUserInfoOrbitAddress] = useState(null);
const [userTopicsOrbitAddress, setUserTopicsOrbitAddress] = useState(null);
const [userPostsOrbitAddress, setUserPostsOrbitAddress] = useState(null);
- const [profileMeta, setProfileMeta] = useState(null);
+ const [profileMetadataFetched, setProfileMetadataFetched] = useState(false);
+ const [userAvatarUrl, setUserAvatarUrl] = useState(null);
+ const [userLocation, setUserLocation] = useState(null);
+ const [editingProfileInformation, setEditingProfileInformation] = useState(false);
const users = useSelector((state) => state.orbitData.users);
const dispatch = useDispatch();
const { t } = useTranslation();
@@ -45,7 +49,9 @@ const GeneralTab = (props) => {
.find((user) => user.id === userOrbitAddress);
if (userFound) {
- setProfileMeta(userFound);
+ setProfileMetadataFetched(true);
+ setUserAvatarUrl(userFound[USER_PROFILE_PICTURE]);
+ setUserLocation(userFound[USER_LOCATION]);
} else {
dispatch({
type: FETCH_USER_DATABASE,
@@ -60,13 +66,13 @@ const GeneralTab = (props) => {
}
}, [dispatch, profileAddress, users]);
- const authorAvatar = useMemo(() => (profileMeta !== null && profileMeta[USER_PROFILE_PICTURE]
+ const authorAvatar = useMemo(() => (profileMetadataFetched && userAvatarUrl !== null
? (
)
: (
@@ -75,91 +81,143 @@ const GeneralTab = (props) => {
size="massive"
inverted
color="black"
- verticalAlign="middle"
/>
- )), [profileMeta]);
+ )), [profileMetadataFetched, userAvatarUrl]);
- const userLocation = useMemo(() => {
- if (profileMeta === null) {
+ const userLocationCell = useMemo(() => {
+ if (!profileMetadataFetched) {
return (
);
- } if (profileMeta[USER_LOCATION] === undefined) {
+ }
+
+ if (!userLocation) {
return {t('profile.general.tab.location.row.not.set')};
}
- return profileMeta[USER_LOCATION];
- }, [profileMeta, t]);
+
+ return userLocation;
+ }, [profileMetadataFetched, t, userLocation]);
+
+ const handleEditInfoClick = () => {
+ setEditingProfileInformation(true);
+ };
+
+ const closeEditInformationModal = () => {
+ setEditingProfileInformation(false);
+ };
+
+ const editInformationModal = useMemo(() => profileMetadataFetched && (
+
+ ), [editingProfileInformation, profileAddress, profileMetadataFetched, userAvatarUrl, userLocation, username]);
return useMemo(() => (
-
-
-
- {authorAvatar}
-
-
- {t('profile.general.tab.username.row.title')}
- {username}
-
-
- {t('profile.general.tab.address.row.title')}
- {profileAddress}
-
-
- {t('profile.general.tab.user.db.address.row.title')}
-
- {userInfoOrbitAddress || ()}
-
-
-
- {t('profile.general.tab.topics.db.address.row.title')}
-
- {userTopicsOrbitAddress || ()}
-
-
-
- {t('profile.general.tab.posts.db.address.row.title')}
-
- {userPostsOrbitAddress || ()}
-
-
-
- {t('profile.general.tab.number.of.topics.row.title')}
-
- {numberOfTopics}
-
-
-
- {t('profile.general.tab.number.of.posts.row.title')}
-
- {numberOfPosts}
-
-
-
- {t('profile.general.tab.location.row.title')}
-
- {userLocation}
-
-
-
- {t('profile.general.tab.registration.date.row.title')}
-
- {new Date(userRegistrationTimestamp * 1000).toLocaleString()}
-
-
-
-
+ <>
+
+
+
+ {authorAvatar}
+
+
+ {t('profile.general.tab.username.row.title')}
+ {username}
+
+
+ {t('profile.general.tab.address.row.title')}
+ {profileAddress}
+
+
+ {t('profile.general.tab.user.db.address.row.title')}
+
+ {userInfoOrbitAddress || ()}
+
+
+
+ {t('profile.general.tab.topics.db.address.row.title')}
+
+ {userTopicsOrbitAddress || ()}
+
+
+
+ {t('profile.general.tab.posts.db.address.row.title')}
+
+ {userPostsOrbitAddress || ()}
+
+
+
+ {t('profile.general.tab.number.of.topics.row.title')}
+
+ {numberOfTopics}
+
+
+
+ {t('profile.general.tab.number.of.posts.row.title')}
+
+ {numberOfPosts}
+
+
+
+ {t('profile.general.tab.location.row.title')}
+
+ {userLocationCell}
+
+
+
+ {t('profile.general.tab.registration.date.row.title')}
+
+ {new Date(userRegistrationTimestamp * 1000).toLocaleString()}
+
+
+
+
+ {isSelf && (
+
+
+
+
+
+
+
+ )}
+
+ {isSelf && editInformationModal}
+ >
), [
- authorAvatar, numberOfPosts, numberOfTopics, profileAddress, profileMeta, t, userInfoOrbitAddress,
- userPostsOrbitAddress, userRegistrationTimestamp, userTopicsOrbitAddress, username,
+ authorAvatar, editInformationModal, isSelf, numberOfPosts, numberOfTopics, profileAddress, profileMetadataFetched,
+ t, userInfoOrbitAddress, userLocationCell, userPostsOrbitAddress, userRegistrationTimestamp, userTopicsOrbitAddress,
+ username,
]);
};
+GeneralTab.defaultProps = {
+ isSelf: false,
+};
+
GeneralTab.propTypes = {
profileAddress: PropTypes.string.isRequired,
username: PropTypes.string.isRequired,
numberOfTopics: PropTypes.number.isRequired,
numberOfPosts: PropTypes.number.isRequired,
userRegistrationTimestamp: PropTypes.string.isRequired,
+ isSelf: PropTypes.bool,
};
export default GeneralTab;
diff --git a/packages/concordia-app/src/views/Profile/index.jsx b/packages/concordia-app/src/views/Profile/index.jsx
index 4d3e1bd..2126536 100644
--- a/packages/concordia-app/src/views/Profile/index.jsx
+++ b/packages/concordia-app/src/views/Profile/index.jsx
@@ -69,8 +69,11 @@ const Profile = () => {
numberOfTopics={userTopicIds.length}
numberOfPosts={userPostIds.length}
userRegistrationTimestamp={userRegistrationTimestamp}
+ isSelf={profileAddress === self.address}
/>
- )), [loading, profileAddress, userPostIds.length, userRegistrationTimestamp, userTopicIds.length, username]);
+ )), [
+ loading, profileAddress, self.address, userPostIds.length, userRegistrationTimestamp, userTopicIds.length, username,
+ ]);
const topicsTab = useMemo(() => (userTopicIds.length > 0
? ()
diff --git a/packages/concordia-app/src/views/Register/SignUpStep/index.jsx b/packages/concordia-app/src/views/Register/SignUpStep/index.jsx
index 6d2da68..7e8cb8b 100644
--- a/packages/concordia-app/src/views/Register/SignUpStep/index.jsx
+++ b/packages/concordia-app/src/views/Register/SignUpStep/index.jsx
@@ -1,10 +1,7 @@
-import React, {
- useCallback, useEffect, useMemo, useState,
-} from 'react';
+import React, { useCallback, useEffect, useState } from 'react';
import {
- Button, Card, Form, Input, Message,
+ Button, Card, Form, Message,
} from 'semantic-ui-react';
-import throttle from 'lodash/throttle';
import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { useHistory } from 'react-router';
@@ -12,18 +9,17 @@ import PropTypes from 'prop-types';
import { drizzle } from '../../../redux/store';
import { TRANSACTION_ERROR, TRANSACTION_SUCCESS } from '../../../constants/TransactionStatus';
import { FORUM_CONTRACT } from '../../../constants/contracts/ContractNames';
+import UsernameSelector from '../../../components/UsernameSelector';
-const { contracts: { [FORUM_CONTRACT]: { methods: { isUserNameTaken, signUp } } } } = drizzle;
+const { contracts: { [FORUM_CONTRACT]: { methods: { signUp } } } } = drizzle;
const SignUpStep = (props) => {
const { pushNextStep, account } = props;
const user = useSelector((state) => state.user);
- const isUserNameTakenResults = useSelector((state) => state.contracts[FORUM_CONTRACT].isUserNameTaken);
const transactionStack = useSelector((state) => state.transactionStack);
const transactions = useSelector((state) => state.transactions);
const [usernameInput, setUsernameInput] = useState('');
const [usernameIsChecked, setUsernameIsChecked] = useState(true);
- const [usernameIsTaken, setUsernameIsTaken] = useState(true);
const [error, setError] = useState(false);
const [errorMessage, setErrorMessage] = useState('');
const [signingUp, setSigningUp] = useState(false);
@@ -32,31 +28,6 @@ const SignUpStep = (props) => {
const history = useHistory();
const { t } = useTranslation();
- useEffect(() => {
- if (usernameInput.length > 0) {
- const checkedUsernames = Object
- .values(isUserNameTakenResults)
- .map((callCompleted) => ({
- checkedUsername: callCompleted.args[0],
- isTaken: callCompleted.value,
- }));
-
- const checkedUsername = checkedUsernames
- .find((callCompleted) => callCompleted.checkedUsername === usernameInput);
-
- setUsernameIsChecked(checkedUsername !== undefined);
-
- if (checkedUsername && checkedUsername.isTaken) {
- setUsernameIsTaken(true);
- setError(true);
- setErrorMessage(t('register.form.sign.up.step.error.username.taken.message', { username: usernameInput }));
- } else {
- setUsernameIsTaken(false);
- setError(false);
- }
- }
- }, [isUserNameTakenResults, t, usernameInput]);
-
useEffect(() => {
if (signingUp && transactionStack && transactionStack[registerCacheSendStackId]
&& transactions[transactionStack[registerCacheSendStackId]]) {
@@ -69,19 +40,24 @@ const SignUpStep = (props) => {
}
}, [pushNextStep, registerCacheSendStackId, signingUp, transactionStack, transactions]);
- const checkUsernameTaken = useMemo(() => throttle(
- (username) => {
- isUserNameTaken.cacheCall(username);
- }, 200,
- ), []);
+ const handleUsernameChange = useCallback((modifiedUsername) => {
+ setUsernameInput(modifiedUsername);
+ }, []);
- const handleInputChange = useCallback((event, { value }) => {
- setUsernameInput(value);
+ const handleUsernameErrorChange = useCallback(({
+ usernameChecked: isUsernameChecked,
+ error: hasUsernameError,
+ errorMessage: usernameErrorMessage,
+ }) => {
+ setUsernameIsChecked(isUsernameChecked);
- if (value.length > 0) {
- checkUsernameTaken(value);
+ if (hasUsernameError) {
+ setError(true);
+ setErrorMessage(usernameErrorMessage);
+ } else {
+ setError(false);
}
- }, [checkUsernameTaken]);
+ }, []);
const handleSubmit = useCallback(() => {
if (user.hasSignedUp) {
@@ -99,19 +75,11 @@ const SignUpStep = (props) => {
-
-
-
+
@@ -130,7 +98,7 @@ const SignUpStep = (props) => {
floated="right"
content={t('register.form.sign.up.step.button.submit')}
onClick={handleSubmit}
- disabled={usernameIsTaken || signingUp}
+ disabled={error || signingUp || usernameInput.length === 0}
loading={!usernameIsChecked}
/>