1
0
Fork 0
mirror of https://github.com/muerwre/vault-frontend.git synced 2025-04-24 20:36:40 +07:00

added user profile popup

This commit is contained in:
Fedor Katurov 2021-09-22 17:54:51 +07:00
parent 43450dff54
commit 1242c04587
10 changed files with 162 additions and 57 deletions

View file

@ -31,10 +31,10 @@ const Comment: FC<IProps> = memo(
return (
<CommentWrapper
className={className}
is_empty={is_empty}
is_loading={is_loading}
isEmpty={is_empty}
isLoading={is_loading}
user={comment_group.user}
is_same={is_same}
isSame={is_same}
{...props}
>
<div className={styles.wrap}>

View file

@ -1,27 +1,59 @@
import React, { FC, useCallback } from 'react';
import { getURLFromString } from '~/utils/dom';
import { PRESETS } from '~/constants/urls';
import React, { FC, useCallback, useState } from 'react';
import { IUser } from '~/redux/auth/types';
import { Avatar } from '~/components/common/Avatar';
import { path } from 'ramda';
import { Manager, Popper, Reference } from 'react-popper';
import styles from './styles.module.scss';
import classNames from 'classnames';
import { openUserProfile } from '~/utils/user';
import { useRandomPhrase } from '~/constants/phrases';
interface Props {
url?: string;
username?: string;
size?: number;
user: IUser;
withDetails: boolean;
className?: string;
}
const CommentAvatar: FC<Props> = ({ url, username, size, className }) => {
const backgroundImage = !!url ? `url('${getURLFromString(url, PRESETS.avatar)}')` : undefined;
const onOpenProfile = useCallback(() => openUserProfile(username), [username]);
const modifiers = [
{
name: 'offset',
options: {
offset: [0, 10],
},
},
];
const CommentAvatar: FC<Props> = ({ user, withDetails, className }) => {
const [hovered, setHovered] = useState(false);
const randomPhrase = useRandomPhrase('USER_DESCRIPTION');
const onMouseOver = useCallback(() => setHovered(true), [setHovered]);
const onMouseOut = useCallback(() => setHovered(false), [setHovered]);
return (
<div
className={classNames(styles.avatar, className)}
style={{ backgroundImage }}
onClick={onOpenProfile}
<Manager>
<Reference>
{({ ref }) => (
<Avatar
url={path(['photo', 'url'], user)}
username={user.username}
className={className}
innerRef={ref}
onMouseOver={onMouseOver}
onMouseOut={onMouseOut}
/>
)}
</Reference>
{hovered && withDetails && (
<Popper placement="right" modifiers={modifiers}>
{({ style, ref }) => (
<div style={style} ref={ref} className={styles.popper}>
<h4 className={styles.username}>{user.fullname || user.username}</h4>
<div className={styles.description}>{user.description || randomPhrase}</div>
</div>
)}
</Popper>
)}
</Manager>
);
};

View file

@ -1,19 +1,33 @@
@import "~/styles/variables";
@import "~/styles/variables.scss";
.avatar {
@keyframes appear {
0% { opacity: 0 }
100% { opacity: 1 }
}
.popper {
@include outer_shadow;
width: $comment_height;
height: $comment_height;
background-color: transparentize(black, 0.9);
flex-shrink: 0;
overflow: hidden;
background-color: darken($content_bg, 4%);
padding: $gap;
box-sizing:border-box;
z-index: 4;
touch-action: none;
pointer-events: none;
border-radius: $radius;
background-position: center;
background-size: cover;
cursor: pointer;
animation: appear forwards 250ms;
}
img {
object-fit: cover;
.username {
font: $font_18_semibold;
line-height: 1.25em;
}
.description {
@include clamp(2, 12px);
font: $font_12_regular;
opacity: 0.5;
margin-top: 3px;
line-height: 1.25em;
}

View file

@ -0,0 +1,31 @@
import React, { FC, useCallback } from 'react';
import { getURLFromString } from '~/utils/dom';
import { PRESETS } from '~/constants/urls';
import styles from './styles.module.scss';
import classNames from 'classnames';
import { openUserProfile } from '~/utils/user';
import { DivProps } from '~/utils/types';
interface Props extends DivProps {
url?: string;
username?: string;
size?: number;
innerRef?: React.Ref<any>;
}
const Avatar: FC<Props> = ({ url, username, size, className, innerRef, ...rest }) => {
const backgroundImage = !!url ? `url('${getURLFromString(url, PRESETS.avatar)}')` : undefined;
const onOpenProfile = useCallback(() => openUserProfile(username), [username]);
return (
<div
{...rest}
className={classNames(styles.avatar, className)}
style={{ backgroundImage }}
onClick={onOpenProfile}
ref={innerRef}
/>
);
};
export { Avatar };

View file

@ -0,0 +1,19 @@
@import "~/styles/variables";
.avatar {
@include outer_shadow;
width: $comment_height;
height: $comment_height;
background-color: transparentize(black, 0.9);
flex-shrink: 0;
overflow: hidden;
border-radius: $radius;
background-position: center;
background-size: cover;
cursor: pointer;
img {
object-fit: cover;
}
}

View file

@ -1,37 +1,40 @@
import React, { FC, HTMLAttributes } from 'react';
import React, { FC } from 'react';
import classNames from 'classnames';
import styles from './styles.module.scss';
import { IUser } from '~/redux/auth/types';
import { path } from 'ramda';
import { CommentAvatar } from '~/components/comment/CommentAvatar';
import { Card } from '~/components/containers/Card';
import { DivProps } from '~/utils/types';
type IProps = HTMLAttributes<HTMLDivElement> & {
type IProps = DivProps & {
user: IUser;
is_empty?: boolean;
is_loading?: boolean;
is_same?: boolean;
isEmpty?: boolean;
isLoading?: boolean;
isSame?: boolean;
isForm?: boolean;
};
const CommentWrapper: FC<IProps> = ({
// photo,
children,
is_empty,
is_loading,
className,
is_same,
user,
className,
isEmpty,
isLoading,
isSame,
isForm,
children,
...props
}) => (
<div className={classNames(styles.wrap, className, { is_empty, is_loading, is_same })} {...props}>
<div
className={classNames(styles.wrap, className, {
is_empty: isEmpty,
is_loading: isLoading,
is_same: isSame,
})}
{...props}
>
<div className={styles.thumb}>
<CommentAvatar
url={path(['photo', 'url'], user)}
username={user.username}
className={styles.thumb_image}
/>
<CommentAvatar user={user} className={styles.thumb_image} withDetails={!isForm} />
<div className={styles.thumb_user}>~{path(['username'], user)}</div>
</div>

View file

@ -1,7 +1,7 @@
import React, { FC, useCallback } from 'react';
import { INode } from '~/redux/types';
import styles from './styles.module.scss';
import { CommentAvatar } from '~/components/comment/CommentAvatar';
import { Avatar } from '~/components/common/Avatar';
import { openUserProfile } from '~/utils/user';
import { useRandomPhrase } from '~/constants/phrases';
@ -24,7 +24,7 @@ const NodeAuthorBlock: FC<Props> = ({ node }) => {
return (
<div className={styles.block} onClick={onOpenProfile}>
<CommentAvatar username={username} url={photo?.url} className={styles.avatar} />
<Avatar username={username} url={photo?.url} className={styles.avatar} />
<div className={styles.info}>
<div className={styles.username}>{fullname || username}</div>

View file

@ -16,7 +16,7 @@ type IProps = ReturnType<typeof mapStateToProps> & {
const NodeCommentFormUnconnected: FC<IProps> = ({ user, isBefore, nodeId }) => {
return (
<CommentWrapper user={user}>
<CommentWrapper user={user} isForm>
<CommentForm nodeId={nodeId} />
</CommentWrapper>
);

View file

@ -5,7 +5,7 @@ import { INode } from '~/redux/types';
import { PRESETS, URLS } from '~/constants/urls';
import { RouteComponentProps, withRouter } from 'react-router';
import { getURL, stringToColour } from '~/utils/dom';
import { CommentAvatar } from '~/components/comment/CommentAvatar';
import { Avatar } from '~/components/common/Avatar';
type IProps = RouteComponentProps & {
item: Partial<INode>;
@ -69,7 +69,7 @@ const NodeRelatedItemUnconnected: FC<IProps> = memo(({ item, history }) => {
onClick={onClick}
ref={ref}
>
<CommentAvatar
<Avatar
username={item.title}
url={item.thumbnail}
className={classNames(styles.thumb, { [styles.is_loaded]: is_loaded })}

6
src/utils/types.ts Normal file
View file

@ -0,0 +1,6 @@
import React from 'react';
export type DivProps = React.DetailedHTMLProps<
React.HTMLAttributes<HTMLDivElement>,
HTMLDivElement
>;