diff --git a/src/components/flow/FlowGrid/index.tsx b/src/components/flow/FlowGrid/index.tsx index 064a92d3..d5f51ff5 100644 --- a/src/components/flow/FlowGrid/index.tsx +++ b/src/components/flow/FlowGrid/index.tsx @@ -30,6 +30,7 @@ export const FlowGrid: FC = ({
+
diff --git a/src/constants/urls.ts b/src/constants/urls.ts index 28813cd3..db07a4ce 100644 --- a/src/constants/urls.ts +++ b/src/constants/urls.ts @@ -14,7 +14,7 @@ export const URLS = { }, NODE_URL: (id: number | string) => `/post${id}`, PROFILE: (username: string) => `/~${username}`, - PROFILE_PAGE: `/profile`, + PROFILE_PAGE: (username: string) => `/profile/${username}`, }; export const PRESETS = { diff --git a/src/containers/App.tsx b/src/containers/App.tsx index c534a844..060b9e6e 100644 --- a/src/containers/App.tsx +++ b/src/containers/App.tsx @@ -44,7 +44,7 @@ const Component: FC = ({ modal: { is_shown } }) => { - + diff --git a/src/containers/dialogs/ProfileDialog/index.tsx b/src/containers/dialogs/ProfileDialog/index.tsx index 5479c53b..57956dca 100644 --- a/src/containers/dialogs/ProfileDialog/index.tsx +++ b/src/containers/dialogs/ProfileDialog/index.tsx @@ -1,4 +1,4 @@ -import React, { FC, useState, useCallback } from 'react'; +import React, { FC, useCallback } from 'react'; import { BetterScrollDialog } from '../BetterScrollDialog'; import { ProfileInfo } from '~/containers/profile/ProfileInfo'; import { IDialogProps } from '~/redux/types'; diff --git a/src/containers/profile/ProfilePage/index.tsx b/src/containers/profile/ProfilePage/index.tsx index 38edd5d1..0a34d020 100644 --- a/src/containers/profile/ProfilePage/index.tsx +++ b/src/containers/profile/ProfilePage/index.tsx @@ -1,10 +1,53 @@ -import React, { FC } from 'react'; +import React, { FC, useEffect } from 'react'; import styles from './styles.scss'; +import { ProfilePageLeft } from '../ProfilePageLeft'; +import { Switch, Route, RouteComponentProps } from 'react-router'; +import { IState } from '~/redux/store'; +import { selectAuthProfile } from '~/redux/auth/selectors'; +import { connect } from 'react-redux'; +import * as AUTH_ACTIONS from '~/redux/auth/actions'; -interface IProps {} +const mapStateToProps = (state: IState) => ({ + profile: selectAuthProfile(state), +}); -const ProfilePage: FC = ({}) => { - return
PROFILE
; +const mapDispatchToProps = { + authLoadProfile: AUTH_ACTIONS.authLoadProfile, }; +type Props = ReturnType & + typeof mapDispatchToProps & + RouteComponentProps<{ username: string }> & {}; + +const ProfilePageUnconnected: FC = ({ + profile, + authLoadProfile, + match: { + params: { username }, + }, +}) => { + useEffect(() => { + authLoadProfile(username); + }, [username]); + + return ( +
+
+ +
+
+ +
DEFAULT
} /> +
TAB
} /> +
+
+
+ ); +}; + +const ProfilePage = connect( + mapStateToProps, + mapDispatchToProps +)(ProfilePageUnconnected); + export { ProfilePage }; diff --git a/src/containers/profile/ProfilePage/styles.scss b/src/containers/profile/ProfilePage/styles.scss index 2a7a9055..7587fa51 100644 --- a/src/containers/profile/ProfilePage/styles.scss +++ b/src/containers/profile/ProfilePage/styles.scss @@ -2,6 +2,19 @@ flex: 1; background: $content_bg; display: flex; - align-items: center; - justify-content: center; + align-items: stretch; + justify-content: stretch; + box-shadow: $node_shadow; + border-radius: $radius; +} + +.left { + flex: 1; + background: darken($content_bg, 2%); + border-radius: $radius 0 0 $radius; + box-sizing: border-box; +} + +.right { + flex: 3; } diff --git a/src/containers/profile/ProfilePageLeft/index.tsx b/src/containers/profile/ProfilePageLeft/index.tsx new file mode 100644 index 00000000..aaa640ae --- /dev/null +++ b/src/containers/profile/ProfilePageLeft/index.tsx @@ -0,0 +1,58 @@ +import React, { FC, useMemo } from 'react'; +import styles from './styles.scss'; +import { IAuthState } from '~/redux/auth/types'; +import { getURL } from '~/utils/dom'; +import { PRESETS, URLS } from '~/constants/urls'; +import { Placeholder } from '~/components/placeholders/Placeholder'; +import { Link } from 'react-router-dom'; +import { Icon } from '~/components/input/Icon'; + +interface IProps { + profile: IAuthState['profile']; + username: string; +} + +const ProfilePageLeft: FC = ({ username, profile }) => { + const thumb = useMemo(() => { + if (!profile || !profile.user || !profile.user.photo) return ''; + + return getURL(profile.user.photo, PRESETS.small_hero); + }, [profile]); + + return ( +
+
+ +
+
+
+ {profile.is_loading ? : profile.user.fullname} +
+ +
+ {profile.is_loading ? : `~${profile.user.username}`} +
+ +
+ + + Профиль + + + + + Настройки + + + + + Сообщения + +
+
+
+
+ ); +}; + +export { ProfilePageLeft }; diff --git a/src/containers/profile/ProfilePageLeft/styles.scss b/src/containers/profile/ProfilePageLeft/styles.scss new file mode 100644 index 00000000..ab403266 --- /dev/null +++ b/src/containers/profile/ProfilePageLeft/styles.scss @@ -0,0 +1,79 @@ +.wrap { + display: flex; + align-items: flex-start; + justify-content: stretch; + flex-direction: column; +} + +.avatar { + width: 100%; + padding-bottom: 50%; + border-radius: $radius 0 0 0; + background: 50% 50% no-repeat; + background-size: cover; +} + +.region_wrap { + width: 100%; + padding: 0 10px; + position: relative; + top: -$radius; + box-sizing: border-box; +} + +.region { + background: $content_bg; + width: 100%; + border-radius: $radius; + box-shadow: rgba(0, 0, 0, 0.3) 0 2px; +} + +.name { + font: $font_24_semibold; + color: white; + padding: $gap $gap 0 $gap; + text-transform: uppercase; + width: 100%; + box-sizing: border-box; +} + +.username { + font: $font_14_semibold; + padding: 0 $gap $gap $gap; + box-sizing: border-box; + width: 100%; + color: transparentize(white, 0.5); +} + +.menu { + padding: $gap 0 $gap 0; + display: flex; + align-items: stretch; + width: 100%; + flex-direction: column; + box-sizing: border-box; + + a { + width: 100%; + color: inherit; + text-decoration: none; + text-transform: uppercase; + font: $font_18_semibold; + padding: $gap $gap; + display: flex; + align-items: center; + justify-content: flex-start; + opacity: 0.5; + box-sizing: border-box; + transition: opacity 0.25s; + + &:hover { + opacity: 1; + } + } + + svg { + margin-right: $gap; + fill: currentColor; + } +} diff --git a/src/redux/auth/actions.ts b/src/redux/auth/actions.ts index 3f1f501c..c84b7b39 100644 --- a/src/redux/auth/actions.ts +++ b/src/redux/auth/actions.ts @@ -44,6 +44,11 @@ export const authOpenProfile = (username: string, tab?: IAuthState['profile']['t tab, }); +export const authLoadProfile = (username: string) => ({ + type: AUTH_USER_ACTIONS.LOAD_PROFILE, + username, +}); + export const authSetProfile = (profile: Partial) => ({ type: AUTH_USER_ACTIONS.SET_PROFILE, profile, diff --git a/src/redux/auth/constants.ts b/src/redux/auth/constants.ts index bd320a82..e1b0980f 100644 --- a/src/redux/auth/constants.ts +++ b/src/redux/auth/constants.ts @@ -11,6 +11,7 @@ export const AUTH_USER_ACTIONS = { GOT_AUTH_POST_MESSAGE: 'GOT_POST_MESSAGE', OPEN_PROFILE: 'OPEN_PROFILE', + LOAD_PROFILE: 'LOAD_PROFILE', SET_PROFILE: 'SET_PROFILE', GET_MESSAGES: 'GET_MESSAGES', SEND_MESSAGE: 'SEND_MESSAGE', diff --git a/src/redux/auth/sagas.ts b/src/redux/auth/sagas.ts index 9df41cb8..8b9e3a79 100644 --- a/src/redux/auth/sagas.ts +++ b/src/redux/auth/sagas.ts @@ -18,6 +18,7 @@ import { authSetRestore, authRequestRestoreCode, authRestorePassword, + authLoadProfile, } from '~/redux/auth/actions'; import { apiUserLogin, @@ -127,9 +128,8 @@ function* logoutSaga() { ); } -function* openProfile({ username, tab = 'profile' }: ReturnType) { - yield put(modalShowDialog(DIALOGS.PROFILE)); - yield put(authSetProfile({ is_loading: true, tab })); +function* loadProfile({ username }: ReturnType) { + yield put(authSetProfile({ is_loading: true })); const { error, @@ -137,10 +137,22 @@ function* openProfile({ username, tab = 'profile' }: ReturnType) { + yield put(modalShowDialog(DIALOGS.PROFILE)); + yield put(authSetProfile({ tab })); + + const success: boolean = yield call(loadProfile, authLoadProfile(username)); + + if (!success) { + return yield put(modalSetShown(false)); + } } function* getMessages({ username }: ReturnType) { @@ -354,6 +366,7 @@ function* authSaga() { yield takeLatest(AUTH_USER_ACTIONS.SEND_LOGIN_REQUEST, sendLoginRequestSaga); yield takeLatest(AUTH_USER_ACTIONS.GOT_AUTH_POST_MESSAGE, gotPostMessageSaga); yield takeLatest(AUTH_USER_ACTIONS.OPEN_PROFILE, openProfile); + yield takeLatest(AUTH_USER_ACTIONS.LOAD_PROFILE, loadProfile); yield takeLatest(AUTH_USER_ACTIONS.GET_MESSAGES, getMessages); yield takeLatest(AUTH_USER_ACTIONS.SEND_MESSAGE, sendMessage); yield takeLatest(AUTH_USER_ACTIONS.SET_LAST_SEEN_MESSAGES, setLastSeenMessages); diff --git a/src/sprites/Sprites.tsx b/src/sprites/Sprites.tsx index 9d8e9a01..e418f696 100644 --- a/src/sprites/Sprites.tsx +++ b/src/sprites/Sprites.tsx @@ -207,6 +207,16 @@ const Sprites: FC<{}> = () => ( + + + + + + + + + +