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

show like button to guests

This commit is contained in:
Fedor Katurov 2023-10-30 17:37:31 +06:00
parent 8e40cf9885
commit 7e20975cb1
7 changed files with 149 additions and 112 deletions

View file

@ -0,0 +1,38 @@
import React, { FC } from 'react';
import classNames from 'classnames';
import { Icon } from '~/components/input/Icon';
import styles from './styles.module.scss';
interface NodeLikeButtonProps {
active: boolean;
count?: number;
className?: string;
onClick: () => void;
}
const NodeLikeButton: FC<NodeLikeButtonProps> = ({
className,
active,
count,
onClick,
}) => (
<div
className={classNames(styles.like, className, {
[styles.is_liked]: active,
})}
>
{active ? (
<Icon icon="heart_full" size={24} onClick={onClick} />
) : (
<Icon icon="heart" size={24} onClick={onClick} />
)}
{!!count && count > 0 && <div className={styles.count}>{count}</div>}
</div>
);
export { NodeLikeButton };

View file

@ -0,0 +1,72 @@
@import 'src/styles/variables';
@keyframes pulse {
0% {
transform: scale(1);
}
45% {
transform: scale(1);
}
60% {
transform: scale(1.4);
}
75% {
transform: scale(1);
}
90% {
transform: scale(1.4);
}
100% {
transform: scale(1);
}
}
.like {
transition: fill, stroke 0.25s;
will-change: transform;
position: relative;
flex: 0 0 32px;
fill: currentColor;
&.is_liked {
opacity: 1;
svg {
fill: $color_like;
}
.like_count {
color: $color_like;
}
}
&:hover {
fill: $color_like;
animation: pulse 0.75s infinite;
.count {
opacity: 0;
}
}
}
.count {
position: absolute;
font: $font_12_bold;
left: 16px;
bottom: 0;
opacity: 1;
transition: opacity 0.25s, color 0.25s;
background: $content_bg_dark;
padding: 0 3px;
border-radius: 10px;
z-index: 3;
color: $gray_50;
pointer-events: none;
touch-action: none;
}

View file

@ -1,14 +1,14 @@
import React, { memo, VFC } from 'react'; import { memo, VFC } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { Authorized } from '~/components/containers/Authorized';
import { Icon } from '~/components/input/Icon';
import { SeparatedMenu } from '~/components/menu/SeparatedMenu'; import { SeparatedMenu } from '~/components/menu/SeparatedMenu';
import { NodeEditMenu } from '~/components/node/NodeEditMenu'; import { NodeEditMenu } from '~/components/node/NodeEditMenu';
import { Placeholder } from '~/components/placeholders/Placeholder'; import { Placeholder } from '~/components/placeholders/Placeholder';
import { getPrettyDate } from '~/utils/dom'; import { getPrettyDate } from '~/utils/dom';
import { NodeLikeButton } from '../NodeLikeButton';
import styles from './styles.module.scss'; import styles from './styles.module.scss';
interface IProps { interface IProps {
@ -77,39 +77,28 @@ const NodeTitle: VFC<IProps> = memo(
)} )}
</div> </div>
<Authorized> <SeparatedMenu className={styles.buttons}>
<SeparatedMenu className={styles.buttons}> {canEdit && (
{canEdit && ( <NodeEditMenu
<NodeEditMenu className={styles.button}
className={styles.button} canStar={canStar}
canStar={canStar} isHeroic={isHeroic}
isHeroic={isHeroic} isLocked={isLocked}
isLocked={isLocked} onStar={onStar}
onStar={onStar} onLock={onLock}
onLock={onLock} onEdit={onEdit}
onEdit={onEdit} />
/> )}
)}
{canLike && ( {canLike && (
<div <NodeLikeButton
className={classNames(styles.button, styles.like, { active={isLiked}
[styles.is_liked]: isLiked, count={likeCount}
})} onClick={onLike}
> className={styles.button}
{isLiked ? ( />
<Icon icon="heart_full" size={24} onClick={onLike} /> )}
) : ( </SeparatedMenu>
<Icon icon="heart" size={24} onClick={onLike} />
)}
{!!likeCount && likeCount > 0 && (
<div className={styles.like_count}>{likeCount}</div>
)}
</div>
)}
</SeparatedMenu>
</Authorized>
</div> </div>
</div> </div>
); );

View file

@ -59,77 +59,6 @@
min-width: 0; min-width: 0;
} }
@keyframes pulse {
0% {
transform: scale(1);
}
45% {
transform: scale(1);
}
60% {
transform: scale(1.4);
}
75% {
transform: scale(1);
}
90% {
transform: scale(1.4);
}
100% {
transform: scale(1);
}
}
.like {
transition: fill, stroke 0.25s;
will-change: transform;
position: relative;
flex: 0 0 32px;
fill: currentColor;
&.is_liked {
opacity: 1;
svg {
fill: $color_like;
}
.like_count {
color: $color_like;
}
}
&:hover {
fill: $color_like;
animation: pulse 0.75s infinite;
.like_count {
opacity: 0;
}
}
}
.like_count {
position: absolute;
font: $font_12_bold;
left: 16px;
bottom: 0;
opacity: 1;
transition: opacity 0.25s, color 0.25s;
background: $content_bg_dark;
padding: 0 3px;
border-radius: 10px;
z-index: 3;
color: $gray_50;
pointer-events: none;
touch-action: none;
}
.buttons { .buttons {
margin-top: 12px; margin-top: 12px;
margin-right: $gap; margin-right: $gap;

View file

@ -11,6 +11,7 @@ import { TestDialog } from '~/containers/dialogs/TestDialog';
export enum Dialog { export enum Dialog {
Login = 'Login', Login = 'Login',
Register = 'Register',
LoginSocialRegister = 'LoginSocialRegister', LoginSocialRegister = 'LoginSocialRegister',
Loading = 'Loading', Loading = 'Loading',
RestoreRequest = 'RestoreRequest', RestoreRequest = 'RestoreRequest',
@ -24,6 +25,7 @@ export enum Dialog {
export const DIALOG_CONTENT = { export const DIALOG_CONTENT = {
[Dialog.Login]: LoginDialog, [Dialog.Login]: LoginDialog,
[Dialog.Register]: LoginDialog, // TODO: make inviting dialog
[Dialog.LoginSocialRegister]: LoginSocialRegisterDialog, [Dialog.LoginSocialRegister]: LoginSocialRegisterDialog,
[Dialog.Loading]: LoadingDialog, [Dialog.Loading]: LoadingDialog,
[Dialog.Test]: TestDialog, [Dialog.Test]: TestDialog,

View file

@ -20,19 +20,19 @@ export interface Props {
const NodeCommentForm: FC<Props> = observer(({ saveComment }) => { const NodeCommentForm: FC<Props> = observer(({ saveComment }) => {
const { user, isUser } = useAuth(); const { user, isUser } = useAuth();
const showLoginDialog = useShowModal(Dialog.Login); const showRegisterDialog = useShowModal(Dialog.Register);
const uploader = useUploader(UploadSubject.Comment, UploadTarget.Comments); const uploader = useUploader(UploadSubject.Comment, UploadTarget.Comments);
const onCommentSave = useCallback( const onCommentSave = useCallback(
async (comment: IComment) => { async (comment: IComment) => {
if (!isUser) { if (!isUser) {
showLoginDialog({}); showRegisterDialog({});
return; return;
} }
return saveComment(comment); return saveComment(comment);
}, },
[isUser, showLoginDialog, saveComment], [isUser, showRegisterDialog, saveComment],
); );
return ( return (

View file

@ -1,4 +1,4 @@
import React from 'react'; import React, { useCallback } from 'react';
import { observer } from 'mobx-react-lite'; import { observer } from 'mobx-react-lite';
@ -7,9 +7,12 @@ import { ScrollHelperBottom } from '~/components/common/ScrollHelperBottom';
import { Card } from '~/components/containers/Card'; import { Card } from '~/components/containers/Card';
import { Footer } from '~/components/main/Footer'; import { Footer } from '~/components/main/Footer';
import { NodeTitle } from '~/components/node/NodeTitle'; import { NodeTitle } from '~/components/node/NodeTitle';
import { Dialog } from '~/constants/modal';
import { Container } from '~/containers/main/Container'; import { Container } from '~/containers/main/Container';
import { SubmitBarRouter } from '~/containers/main/SubmitBarRouter'; import { SubmitBarRouter } from '~/containers/main/SubmitBarRouter';
import { NodeBottomBlock } from '~/containers/node/NodeBottomBlock'; import { NodeBottomBlock } from '~/containers/node/NodeBottomBlock';
import { useAuth } from '~/hooks/auth/useAuth';
import { useShowModal } from '~/hooks/modal/useShowModal';
import { useNodeActions } from '~/hooks/node/useNodeActions'; import { useNodeActions } from '~/hooks/node/useNodeActions';
import { useNodeBlocks } from '~/hooks/node/useNodeBlocks'; import { useNodeBlocks } from '~/hooks/node/useNodeBlocks';
import { useNodeCoverImage } from '~/hooks/node/useNodeCoverImage'; import { useNodeCoverImage } from '~/hooks/node/useNodeCoverImage';
@ -19,6 +22,8 @@ import { useNodeContext } from '~/utils/context/NodeContextProvider';
import styles from './styles.module.scss'; import styles from './styles.module.scss';
const NodeLayout = observer(() => { const NodeLayout = observer(() => {
const { isUser } = useAuth();
const showRegisterDialog = useShowModal(Dialog.Register);
const { node, isLoading, update } = useNodeContext(); const { node, isLoading, update } = useNodeContext();
const { head, block } = useNodeBlocks(node, isLoading); const { head, block } = useNodeBlocks(node, isLoading);
const [canEdit, canLike, canStar] = useNodePermissions(node); const [canEdit, canLike, canStar] = useNodePermissions(node);
@ -26,6 +31,8 @@ const NodeLayout = observer(() => {
useNodeCoverImage(node); useNodeCoverImage(node);
const onUnauthorizedLike = useCallback(() => showRegisterDialog({}), []);
return ( return (
<div className={styles.wrap}> <div className={styles.wrap}>
{head} {head}
@ -46,9 +53,9 @@ const NodeLayout = observer(() => {
isLoading={isLoading} isLoading={isLoading}
createdAt={node.created_at || ''} createdAt={node.created_at || ''}
canEdit={canEdit} canEdit={canEdit}
canLike={canLike} canLike={canLike || !isUser}
canStar={canStar} canStar={canStar}
onLike={onLike} onLike={isUser ? onLike : onUnauthorizedLike}
onStar={onStar} onStar={onStar}
onLock={onLock} onLock={onLock}
onEdit={onEdit} onEdit={onEdit}