mirror of
https://github.com/muerwre/vault-frontend.git
synced 2025-04-24 20:36:40 +07:00
made sample profile page
This commit is contained in:
parent
b43fb35b47
commit
a808045a7d
17 changed files with 118 additions and 179 deletions
|
@ -3,7 +3,7 @@ import { useCommentFormFormik } from '~/utils/hooks/useCommentFormFormik';
|
|||
import { FormikProvider } from 'formik';
|
||||
import { LocalCommentFormTextarea } from '~/components/comment/LocalCommentFormTextarea';
|
||||
import { Button } from '~/components/input/Button';
|
||||
import { FileUploaderProvider, useFileUploader } from '~/utils/hooks/fileUploader';
|
||||
import { FileUploaderProvider, useFileUploader } from '~/utils/hooks/useFileUploader';
|
||||
import { UPLOAD_SUBJECTS, UPLOAD_TARGETS } from '~/redux/uploads/constants';
|
||||
import { CommentFormAttachButtons } from '~/components/comment/CommentFormAttachButtons';
|
||||
import { CommentFormFormatButtons } from '~/components/comment/CommentFormFormatButtons';
|
||||
|
|
|
@ -7,7 +7,7 @@ import { SortEnd } from 'react-sortable-hoc';
|
|||
import { moveArrItem } from '~/utils/fn';
|
||||
import { useDropZone } from '~/utils/hooks';
|
||||
import { COMMENT_FILE_TYPES, UPLOAD_TYPES } from '~/redux/uploads/constants';
|
||||
import { useFileUploaderContext } from '~/utils/hooks/fileUploader';
|
||||
import { useFileUploaderContext } from '~/utils/hooks/useFileUploader';
|
||||
|
||||
const CommentFormAttaches: FC = () => {
|
||||
const uploader = useFileUploaderContext();
|
||||
|
|
|
@ -10,11 +10,20 @@ interface Props extends DivProps {
|
|||
url?: string;
|
||||
username?: string;
|
||||
size?: number;
|
||||
preset?: typeof PRESETS[keyof typeof PRESETS];
|
||||
innerRef?: React.Ref<any>;
|
||||
}
|
||||
|
||||
const Avatar: FC<Props> = ({ url, username, size, className, innerRef, ...rest }) => {
|
||||
const backgroundImage = !!url ? `url('${getURLFromString(url, PRESETS.avatar)}')` : undefined;
|
||||
const Avatar: FC<Props> = ({
|
||||
url,
|
||||
username,
|
||||
size,
|
||||
className,
|
||||
innerRef,
|
||||
preset = PRESETS.avatar,
|
||||
...rest
|
||||
}) => {
|
||||
const backgroundImage = !!url ? `url('${getURLFromString(url, preset)}')` : undefined;
|
||||
const onOpenProfile = useCallback(() => openUserProfile(username), [username]);
|
||||
|
||||
return (
|
||||
|
|
|
@ -11,7 +11,7 @@ import { NodeEditorProps } from '~/redux/node/types';
|
|||
import { useNodeImages } from '~/utils/hooks/node/useNodeImages';
|
||||
import { useNodeAudios } from '~/utils/hooks/node/useNodeAudios';
|
||||
import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik';
|
||||
import { useFileUploaderContext } from '~/utils/hooks/fileUploader';
|
||||
import { useFileUploaderContext } from '~/utils/hooks/useFileUploader';
|
||||
|
||||
type IProps = NodeEditorProps;
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ import styles from './styles.module.scss';
|
|||
import { Icon } from '~/components/input/Icon';
|
||||
import { UPLOAD_TYPES } from '~/redux/uploads/constants';
|
||||
import { IEditorComponentProps } from '~/redux/node/types';
|
||||
import { useFileUploaderContext } from '~/utils/hooks/fileUploader';
|
||||
import { useFileUploaderContext } from '~/utils/hooks/useFileUploader';
|
||||
import { getFileType } from '~/utils/uploader';
|
||||
import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik';
|
||||
import { Button } from '~/components/input/Button';
|
||||
|
|
|
@ -11,7 +11,7 @@ import { getURL } from '~/utils/dom';
|
|||
import { Icon } from '~/components/input/Icon';
|
||||
import { PRESETS } from '~/constants/urls';
|
||||
import { IEditorComponentProps } from '~/redux/node/types';
|
||||
import { useFileUploader, useFileUploaderContext } from '~/utils/hooks/fileUploader';
|
||||
import { useFileUploader, useFileUploaderContext } from '~/utils/hooks/useFileUploader';
|
||||
import { useNodeFormContext } from '~/utils/hooks/useNodeFormFormik';
|
||||
import { getFileType } from '~/utils/uploader';
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { selectUploads } from '~/redux/uploads/selectors';
|
|||
import { ImageGrid } from '~/components/editors/ImageGrid';
|
||||
import styles from './styles.module.scss';
|
||||
import { NodeEditorProps } from '~/redux/node/types';
|
||||
import { useFileUploaderContext } from '~/utils/hooks/fileUploader';
|
||||
import { useFileUploaderContext } from '~/utils/hooks/useFileUploader';
|
||||
|
||||
type IProps = NodeEditorProps;
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { CoverBackdrop } from '~/components/containers/CoverBackdrop';
|
|||
import { prop } from 'ramda';
|
||||
import { useNodeFormFormik } from '~/utils/hooks/useNodeFormFormik';
|
||||
import { EditorButtons } from '~/components/editors/EditorButtons';
|
||||
import { FileUploaderProvider, useFileUploader } from '~/utils/hooks/fileUploader';
|
||||
import { FileUploaderProvider, useFileUploader } from '~/utils/hooks/useFileUploader';
|
||||
import { UPLOAD_SUBJECTS, UPLOAD_TARGETS } from '~/redux/uploads/constants';
|
||||
import { FormikProvider } from 'formik';
|
||||
import { INode } from '~/redux/types';
|
||||
|
|
|
@ -9,6 +9,9 @@ import classNames from 'classnames';
|
|||
|
||||
import styles from './styles.module.scss';
|
||||
import markdown from '~/styles/common/markdown.module.scss';
|
||||
import { ProfileAvatar } from '~/containers/profile/ProfileAvatar';
|
||||
import { Avatar } from '~/components/common/Avatar';
|
||||
import { Markdown } from '~/components/containers/Markdown';
|
||||
|
||||
interface IProps {
|
||||
profile: IAuthState['profile'];
|
||||
|
@ -16,17 +19,15 @@ interface IProps {
|
|||
}
|
||||
|
||||
const ProfilePageLeft: FC<IProps> = ({ username, profile }) => {
|
||||
const thumb = useMemo(() => {
|
||||
if (!profile || !profile.user || !profile.user.photo) return '';
|
||||
|
||||
return getURL(profile.user.photo, PRESETS.small_hero);
|
||||
}, [profile]);
|
||||
|
||||
return (
|
||||
<div className={styles.wrap}>
|
||||
<div className={styles.avatar} style={{ backgroundImage: `url('${thumb}')` }} />
|
||||
<Avatar
|
||||
username={username}
|
||||
url={profile.user?.photo?.url}
|
||||
className={styles.avatar}
|
||||
preset={PRESETS['600']}
|
||||
/>
|
||||
|
||||
<div className={styles.region_wrap}>
|
||||
<div className={styles.region}>
|
||||
<div className={styles.name}>
|
||||
{profile.is_loading ? <Placeholder /> : profile?.user?.fullname}
|
||||
|
@ -35,30 +36,13 @@ const ProfilePageLeft: FC<IProps> = ({ username, profile }) => {
|
|||
<div className={styles.username}>
|
||||
{profile.is_loading ? <Placeholder /> : `~${profile?.user?.username}`}
|
||||
</div>
|
||||
|
||||
<div className={styles.menu}>
|
||||
<Link to={`${URLS.PROFILE_PAGE(username)}/`}>
|
||||
<Icon icon="profile" size={20} />
|
||||
Профиль
|
||||
</Link>
|
||||
|
||||
<Link to={`${URLS.PROFILE_PAGE(username)}/settings`}>
|
||||
<Icon icon="settings" size={20} />
|
||||
Настройки
|
||||
</Link>
|
||||
|
||||
<Link to={`${URLS.PROFILE_PAGE(username)}/messages`}>
|
||||
<Icon icon="messages" size={20} />
|
||||
Сообщения
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{profile && profile.user && profile.user.description && false && (
|
||||
<div className={classNames(styles.description, markdown.wrapper)}>
|
||||
{formatText(profile?.user?.description || '')}
|
||||
</div>
|
||||
{profile && profile.user && profile.user.description && (
|
||||
<Markdown
|
||||
className={styles.description}
|
||||
dangerouslySetInnerHTML={{ __html: formatText(profile.user.description) }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,92 +1,51 @@
|
|||
@import "src/styles/variables";
|
||||
|
||||
.wrap {
|
||||
@include outer_shadow;
|
||||
|
||||
padding: $gap $gap $gap * 2;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: stretch;
|
||||
flex-direction: column;
|
||||
background: $comment_bg;
|
||||
height: 100%;
|
||||
border-radius: $radius;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 100%;
|
||||
padding-bottom: 75%;
|
||||
border-radius: 0 $radius 0 0;
|
||||
background: 50% 50% no-repeat;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.region_wrap {
|
||||
width: 100%;
|
||||
// padding: 0 10px;
|
||||
position: relative;
|
||||
margin-top: -$radius;
|
||||
box-sizing: border-box;
|
||||
height: 0;
|
||||
padding-bottom: 100%;
|
||||
margin-bottom: $gap * 2;
|
||||
}
|
||||
|
||||
.region {
|
||||
// background: $content_bg;
|
||||
background: darken($content_bg, 2%);
|
||||
width: 100%;
|
||||
border-radius: 0 $radius $radius 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.name {
|
||||
font: $font_24_semibold;
|
||||
color: white;
|
||||
padding: $gap $gap 0 $gap;
|
||||
text-transform: uppercase;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.username {
|
||||
font: $font_14_semibold;
|
||||
padding: 0 $gap $gap $gap;
|
||||
font: $font_14_regular;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
color: transparentize(white, 0.5);
|
||||
margin-top: $gap / 2;
|
||||
}
|
||||
|
||||
.menu {
|
||||
padding: $gap 0 $gap 0;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
display: none;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
.description {
|
||||
padding: $gap;
|
||||
box-sizing: border-box;
|
||||
// background: darken($content_bg, 2%);
|
||||
background: darken($content_bg, 4%);
|
||||
// margin: 0 $gap;
|
||||
border-radius: 0 0 $radius $radius;
|
||||
@include clamp(3, 21px * 3);
|
||||
line-height: 21px;
|
||||
font: $font_14_regular;
|
||||
margin-top: $gap * 3;
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -9,66 +9,15 @@
|
|||
|
||||
$cols: $content_width / $cell;
|
||||
|
||||
@mixin fluid {
|
||||
@media(min-width: $content_width) {
|
||||
.fluid & {
|
||||
@content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: $content_width;
|
||||
width: 100%;
|
||||
|
||||
&.fluid {
|
||||
padding: 0 $gap;
|
||||
box-sizing: border-box;
|
||||
max-width: none;
|
||||
}
|
||||
}
|
||||
|
||||
.grid {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
|
||||
grid-template-columns: repeat(auto-fit, minmax($cell - 5, 1fr));
|
||||
grid-template-rows: 50vh $cell;
|
||||
grid-auto-rows: $cell;
|
||||
|
||||
grid-auto-flow: row dense;
|
||||
grid-column-gap: $gap;
|
||||
grid-row-gap: $gap;
|
||||
|
||||
@include fluid {
|
||||
grid-template-columns: repeat(auto-fit, minmax($fluid_cell - 5, 1fr));
|
||||
grid-template-rows: $fluid_cell;
|
||||
grid-auto-rows: $fluid_cell;
|
||||
}
|
||||
|
||||
@media (max-width: ($cell + 10) * 3) {
|
||||
grid-template-columns: repeat(auto-fill, minmax($fluid_cell - 20, 1fr));
|
||||
grid-auto-rows: $fluid_cell;
|
||||
grid-template-rows: calc(50vw - 10px) $fluid_cell;
|
||||
}
|
||||
|
||||
@media (max-width: $cell_tablet) {
|
||||
grid-template-rows: calc(66vw - 10px) auto $fluid_cell;
|
||||
}
|
||||
|
||||
@media (max-width: $cell_mobile) {
|
||||
// rework stamp, so it will be shown as smaller one on mobiles
|
||||
grid-template-columns: repeat(auto-fill, minmax(calc(50vw - 20px), 1fr));
|
||||
grid-template-rows: calc(80vw - 10px) auto 50vw;
|
||||
grid-auto-rows: 50vw;
|
||||
}
|
||||
|
||||
@media (max-width: ($fluid_cell + 5) * 1.5 + 20) {
|
||||
grid-template-columns: repeat(auto-fill, minmax(calc(50vw - 20px), 1fr));
|
||||
grid-template-rows: calc(80vw - 10px) auto 50vw;
|
||||
grid-auto-rows: 50vw;
|
||||
}
|
||||
@include flow_grid;
|
||||
}
|
||||
|
||||
.pad_last {
|
||||
|
@ -86,15 +35,6 @@ $cols: $content_width / $cell;
|
|||
align-items: center;
|
||||
justify-content: center;
|
||||
font: $font_24_semibold;
|
||||
|
||||
@include fluid {
|
||||
grid-row-end: span 2;
|
||||
grid-column-end: span 4;
|
||||
|
||||
@media(max-width: $content_width) {
|
||||
grid-column-end: -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.stamp {
|
||||
|
|
|
@ -4,9 +4,13 @@ import { Route, RouteComponentProps, Switch } from 'react-router';
|
|||
import { useDispatch } from 'react-redux';
|
||||
import { authLoadProfile } from '~/redux/auth/actions';
|
||||
import { useShallowSelect } from '~/utils/hooks/useShallowSelect';
|
||||
import { selectAuthProfile } from '~/redux/auth/selectors';
|
||||
import { selectAuthProfile, selectUser } from '~/redux/auth/selectors';
|
||||
import { ProfilePageLeft } from '~/containers/profile/ProfilePageLeft';
|
||||
import { Container } from '~/containers/main/Container';
|
||||
import { FlowGrid } from '~/components/flow/FlowGrid';
|
||||
import { FlowLayout } from '~/layouts/FlowLayout';
|
||||
import { Sticky } from '~/components/containers/Sticky';
|
||||
import { selectFlow } from '~/redux/flow/selectors';
|
||||
|
||||
type Props = RouteComponentProps<{ username: string }> & {};
|
||||
|
||||
|
@ -15,6 +19,9 @@ const ProfileLayout: FC<Props> = ({
|
|||
params: { username },
|
||||
},
|
||||
}) => {
|
||||
const { nodes } = useShallowSelect(selectFlow);
|
||||
const user = useShallowSelect(selectUser);
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -25,7 +32,15 @@ const ProfileLayout: FC<Props> = ({
|
|||
|
||||
return (
|
||||
<Container className={styles.wrap}>
|
||||
<div className={styles.left}>
|
||||
<Sticky>
|
||||
<ProfilePageLeft profile={profile} username={username} />
|
||||
</Sticky>
|
||||
</div>
|
||||
|
||||
<div className={styles.grid}>
|
||||
<FlowGrid nodes={nodes} user={user} onChangeCellView={console.log} />
|
||||
</div>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,20 +1,16 @@
|
|||
@import "src/styles/variables";
|
||||
|
||||
.wrap {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
justify-content: stretch;
|
||||
border-radius: $radius;
|
||||
display: grid;
|
||||
grid-template-columns: $cell auto;
|
||||
grid-column-gap: $gap;
|
||||
}
|
||||
|
||||
.grid {
|
||||
@include flow_grid;
|
||||
}
|
||||
.left {
|
||||
flex: 1;
|
||||
background: darken($content_bg, 2%);
|
||||
border-radius: 0 $radius $radius 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 4;
|
||||
}
|
||||
|
|
|
@ -268,3 +268,39 @@ $sidebar_border: transparentize(white, 0.95);
|
|||
$color: mix($wisegreen, $content_bg, 30%);
|
||||
background: linear-gradient(170deg, lighten($color, 10%), $color);
|
||||
}
|
||||
|
||||
@mixin flow_grid {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
display: grid;
|
||||
|
||||
grid-template-columns: repeat(auto-fit, minmax($cell - 5, 1fr));
|
||||
grid-auto-rows: $cell;
|
||||
|
||||
grid-auto-flow: row dense;
|
||||
grid-column-gap: $gap;
|
||||
grid-row-gap: $gap;
|
||||
|
||||
@media (max-width: ($cell + 10) * 3) {
|
||||
grid-template-columns: repeat(auto-fill, minmax($fluid_cell - 20, 1fr));
|
||||
grid-auto-rows: $fluid_cell;
|
||||
grid-template-rows: calc(50vw - 10px) $fluid_cell;
|
||||
}
|
||||
|
||||
@media (max-width: $cell_tablet) {
|
||||
grid-template-rows: calc(66vw - 10px) auto $fluid_cell;
|
||||
}
|
||||
|
||||
@media (max-width: $cell_mobile) {
|
||||
// rework stamp, so it will be shown as smaller one on mobiles
|
||||
grid-template-columns: repeat(auto-fill, minmax(calc(50vw - 20px), 1fr));
|
||||
grid-template-rows: calc(80vw - 10px) auto 50vw;
|
||||
grid-auto-rows: 50vw;
|
||||
}
|
||||
|
||||
@media (max-width: ($fluid_cell + 5) * 1.5 + 20) {
|
||||
grid-template-columns: repeat(auto-fill, minmax(calc(50vw - 20px), 1fr));
|
||||
grid-template-rows: calc(80vw - 10px) auto 50vw;
|
||||
grid-auto-rows: 50vw;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { IComment, INode } from '~/redux/types';
|
|||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { FormikHelpers, useFormik, useFormikContext } from 'formik';
|
||||
import { array, object, string } from 'yup';
|
||||
import { FileUploader } from '~/utils/hooks/fileUploader';
|
||||
import { FileUploader } from '~/utils/hooks/useFileUploader';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { nodePostLocalComment } from '~/redux/node/actions';
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { IComment, INode } from '~/redux/types';
|
||||
import { FileUploader } from '~/utils/hooks/fileUploader';
|
||||
import { FileUploader } from '~/utils/hooks/useFileUploader';
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import { FormikHelpers, useFormik, useFormikContext } from 'formik';
|
||||
import { object, string } from 'yup';
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue