diff --git a/src/components/main/Notifications/index.tsx b/src/components/main/Notifications/index.tsx index cf56bb81..6f65f41b 100644 --- a/src/components/main/Notifications/index.tsx +++ b/src/components/main/Notifications/index.tsx @@ -46,10 +46,7 @@ const NotificationsUnconnected: FC = ({ return; } - return authOpenProfile( - (notification as IMessageNotification).content.from!.username, - 'messages' - ); + return authOpenProfile((notification as IMessageNotification).content.from!.username); default: return; } diff --git a/src/components/main/UserButton/index.tsx b/src/components/main/UserButton/index.tsx index 7bd617af..b4e31d51 100644 --- a/src/components/main/UserButton/index.tsx +++ b/src/components/main/UserButton/index.tsx @@ -16,12 +16,12 @@ interface IProps { const UserButton: FC = ({ user: { username, photo }, authOpenProfile, onLogout }) => { const onProfileOpen = useCallback(() => { if (!username) return; - authOpenProfile(username, 'profile'); + authOpenProfile(username); }, [authOpenProfile, username]); const onSettingsOpen = useCallback(() => { if (!username) return; - authOpenProfile(username, 'settings'); + authOpenProfile(username); }, [authOpenProfile, username]); return ( diff --git a/src/components/profile/ProfileAvatar/index.tsx b/src/components/profile/ProfileAvatar/index.tsx new file mode 100644 index 00000000..b5a3ed79 --- /dev/null +++ b/src/components/profile/ProfileAvatar/index.tsx @@ -0,0 +1,45 @@ +import React, { ChangeEvent, FC, useCallback } from 'react'; +import styles from './styles.module.scss'; +import { getURL } from '~/utils/dom'; +import { PRESETS } from '~/constants/urls'; +import { Icon } from '~/components/input/Icon'; +import { IFile } from '~/redux/types'; + +export interface ProfileAvatarProps { + canEdit: boolean; + photo?: IFile; + onChangePhoto: (file: File) => void; +} + +const ProfileAvatar: FC = ({ photo, onChangePhoto, canEdit }) => { + const onInputChange = useCallback( + async (event: ChangeEvent) => { + if (!event.target.files?.length) { + return; + } + + onChangePhoto(event.target.files[0]); + }, + [onChangePhoto] + ); + + const backgroundImage = photo ? `url("${getURL(photo, PRESETS.avatar)}")` : undefined; + + return ( +
+ {canEdit && } + {canEdit && ( +
+ +
+ )} +
+ ); +}; + +export { ProfileAvatar }; diff --git a/src/containers/profile/ProfileAvatar/styles.module.scss b/src/components/profile/ProfileAvatar/styles.module.scss similarity index 100% rename from src/containers/profile/ProfileAvatar/styles.module.scss rename to src/components/profile/ProfileAvatar/styles.module.scss diff --git a/src/components/profile/ProfileDescription/index.tsx b/src/components/profile/ProfileDescription/index.tsx index e083c47a..683ccadb 100644 --- a/src/components/profile/ProfileDescription/index.tsx +++ b/src/components/profile/ProfileDescription/index.tsx @@ -1,39 +1,33 @@ import React, { FC } from 'react'; import { formatText } from '~/utils/dom'; import styles from './styles.module.scss'; -import { connect } from 'react-redux'; -import { selectAuthProfile } from '~/redux/auth/selectors'; import { ProfileLoader } from '~/containers/profile/ProfileLoader'; import { Group } from '~/components/containers/Group'; import markdown from '~/styles/common/markdown.module.scss'; import classNames from 'classnames'; +import { useProfileContext } from '~/utils/providers/ProfileProvider'; -const mapStateToProps = state => ({ - profile: selectAuthProfile(state), -}); +const ProfileDescription: FC = () => { + const { profile, isLoading } = useProfileContext(); -type IProps = ReturnType & {}; - -const ProfileDescriptionUnconnected: FC = ({ profile: { user, is_loading } }) => { - if (is_loading) return ; + if (isLoading) return ; return (
- {!!user?.description && ( + {!!profile?.description && ( )} - {!user?.description && ( + + {!profile?.description && (
- {user?.fullname || user?.username} пока ничего не рассказал о себе + {profile?.fullname || profile?.username} пока ничего не рассказал о себе
)}
); }; -const ProfileDescription = connect(mapStateToProps)(ProfileDescriptionUnconnected); - export { ProfileDescription }; diff --git a/src/components/profile/ProfileSettings/index.tsx b/src/components/profile/ProfileSettings/index.tsx index b7d27c95..4f54e3bb 100644 --- a/src/components/profile/ProfileSettings/index.tsx +++ b/src/components/profile/ProfileSettings/index.tsx @@ -1,35 +1,25 @@ import React, { FC, useCallback, useEffect, useState } from 'react'; import styles from './styles.module.scss'; -import { connect } from 'react-redux'; -import { selectAuthProfile, selectAuthUser } from '~/redux/auth/selectors'; import { Textarea } from '~/components/input/Textarea'; import { Button } from '~/components/input/Button'; import { Group } from '~/components/containers/Group'; import { Filler } from '~/components/containers/Filler'; import { InputText } from '~/components/input/InputText'; -import * as AUTH_ACTIONS from '~/redux/auth/actions'; import { ERROR_LITERAL } from '~/constants/errors'; import { ProfileAccounts } from '~/components/profile/ProfileAccounts'; import classNames from 'classnames'; +import { useUser } from '~/hooks/user/userUser'; +import { useProfileContext } from '~/utils/providers/ProfileProvider'; +import { showErrorToast } from '~/utils/errors/showToast'; +import { getValidationErrors } from '~/utils/errors/getValidationErrors'; +import { showToastSuccess } from '~/utils/toast'; +import { getRandomPhrase } from '~/constants/phrases'; -const mapStateToProps = state => ({ - user: selectAuthUser(state), - profile: selectAuthProfile(state), -}); +const ProfileSettings: FC = () => { + const [errors, setErrors] = useState>({}); + const user = useUser(); + const { updateProfile } = useProfileContext(); -const mapDispatchToProps = { - authPatchUser: AUTH_ACTIONS.authPatchUser, - authSetProfile: AUTH_ACTIONS.authSetProfile, -}; - -type IProps = ReturnType & typeof mapDispatchToProps & {}; - -const ProfileSettingsUnconnected: FC = ({ - user, - profile: { patch_errors, socials }, - authPatchUser, - authSetProfile, -}) => { const [password, setPassword] = useState(''); const [new_password, setNewPassword] = useState(''); @@ -39,28 +29,40 @@ const ProfileSettingsUnconnected: FC = ({ data, setData, ]); + const setEmail = useCallback(email => setData({ ...data, email }), [data, setData]); const setUsername = useCallback(username => setData({ ...data, username }), [data, setData]); const setFullname = useCallback(fullname => setData({ ...data, fullname }), [data, setData]); const onSubmit = useCallback( - event => { - event.preventDefault(); + async event => { + try { + event.preventDefault(); - const fields = { - ...data, - password: password.length > 0 && password ? password : undefined, - new_password: new_password.length > 0 && new_password ? new_password : undefined, - }; + const fields = { + ...data, + password: password.length > 0 && password ? password : undefined, + new_password: new_password.length > 0 && new_password ? new_password : undefined, + }; - authPatchUser(fields); + await updateProfile(fields); + + showToastSuccess(getRandomPhrase('SUCCESS')); + } catch (error) { + showErrorToast(error); + + const validationErrors = getValidationErrors(error); + if (validationErrors) { + setErrors(validationErrors); + } + } }, - [data, password, new_password, authPatchUser] + [data, password, new_password, updateProfile] ); useEffect(() => { - authSetProfile({ patch_errors: {} }); - }, [password, new_password, data, authSetProfile]); + setErrors({}); + }, [password, new_password, data]); return (
@@ -74,7 +76,7 @@ const ProfileSettingsUnconnected: FC = ({ value={data.fullname} handler={setFullname} title="Полное имя" - error={patch_errors.fullname && ERROR_LITERAL[patch_errors.fullname]} + error={errors.fullname && ERROR_LITERAL[errors.fullname]} />