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:
parent
8e40cf9885
commit
7e20975cb1
7 changed files with 149 additions and 112 deletions
38
src/components/node/NodeLikeButton/index.tsx
Normal file
38
src/components/node/NodeLikeButton/index.tsx
Normal 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 };
|
72
src/components/node/NodeLikeButton/styles.module.scss
Normal file
72
src/components/node/NodeLikeButton/styles.module.scss
Normal 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;
|
||||
}
|
|
@ -1,14 +1,14 @@
|
|||
import React, { memo, VFC } from 'react';
|
||||
import { memo, VFC } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { Authorized } from '~/components/containers/Authorized';
|
||||
import { Icon } from '~/components/input/Icon';
|
||||
import { SeparatedMenu } from '~/components/menu/SeparatedMenu';
|
||||
import { NodeEditMenu } from '~/components/node/NodeEditMenu';
|
||||
import { Placeholder } from '~/components/placeholders/Placeholder';
|
||||
import { getPrettyDate } from '~/utils/dom';
|
||||
|
||||
import { NodeLikeButton } from '../NodeLikeButton';
|
||||
|
||||
import styles from './styles.module.scss';
|
||||
|
||||
interface IProps {
|
||||
|
@ -77,39 +77,28 @@ const NodeTitle: VFC<IProps> = memo(
|
|||
)}
|
||||
</div>
|
||||
|
||||
<Authorized>
|
||||
<SeparatedMenu className={styles.buttons}>
|
||||
{canEdit && (
|
||||
<NodeEditMenu
|
||||
className={styles.button}
|
||||
canStar={canStar}
|
||||
isHeroic={isHeroic}
|
||||
isLocked={isLocked}
|
||||
onStar={onStar}
|
||||
onLock={onLock}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
)}
|
||||
<SeparatedMenu className={styles.buttons}>
|
||||
{canEdit && (
|
||||
<NodeEditMenu
|
||||
className={styles.button}
|
||||
canStar={canStar}
|
||||
isHeroic={isHeroic}
|
||||
isLocked={isLocked}
|
||||
onStar={onStar}
|
||||
onLock={onLock}
|
||||
onEdit={onEdit}
|
||||
/>
|
||||
)}
|
||||
|
||||
{canLike && (
|
||||
<div
|
||||
className={classNames(styles.button, styles.like, {
|
||||
[styles.is_liked]: isLiked,
|
||||
})}
|
||||
>
|
||||
{isLiked ? (
|
||||
<Icon icon="heart_full" size={24} onClick={onLike} />
|
||||
) : (
|
||||
<Icon icon="heart" size={24} onClick={onLike} />
|
||||
)}
|
||||
|
||||
{!!likeCount && likeCount > 0 && (
|
||||
<div className={styles.like_count}>{likeCount}</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</SeparatedMenu>
|
||||
</Authorized>
|
||||
{canLike && (
|
||||
<NodeLikeButton
|
||||
active={isLiked}
|
||||
count={likeCount}
|
||||
onClick={onLike}
|
||||
className={styles.button}
|
||||
/>
|
||||
)}
|
||||
</SeparatedMenu>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -59,77 +59,6 @@
|
|||
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 {
|
||||
margin-top: 12px;
|
||||
margin-right: $gap;
|
||||
|
|
|
@ -11,6 +11,7 @@ import { TestDialog } from '~/containers/dialogs/TestDialog';
|
|||
|
||||
export enum Dialog {
|
||||
Login = 'Login',
|
||||
Register = 'Register',
|
||||
LoginSocialRegister = 'LoginSocialRegister',
|
||||
Loading = 'Loading',
|
||||
RestoreRequest = 'RestoreRequest',
|
||||
|
@ -24,6 +25,7 @@ export enum Dialog {
|
|||
|
||||
export const DIALOG_CONTENT = {
|
||||
[Dialog.Login]: LoginDialog,
|
||||
[Dialog.Register]: LoginDialog, // TODO: make inviting dialog
|
||||
[Dialog.LoginSocialRegister]: LoginSocialRegisterDialog,
|
||||
[Dialog.Loading]: LoadingDialog,
|
||||
[Dialog.Test]: TestDialog,
|
||||
|
|
|
@ -20,19 +20,19 @@ export interface Props {
|
|||
|
||||
const NodeCommentForm: FC<Props> = observer(({ saveComment }) => {
|
||||
const { user, isUser } = useAuth();
|
||||
const showLoginDialog = useShowModal(Dialog.Login);
|
||||
const showRegisterDialog = useShowModal(Dialog.Register);
|
||||
|
||||
const uploader = useUploader(UploadSubject.Comment, UploadTarget.Comments);
|
||||
const onCommentSave = useCallback(
|
||||
async (comment: IComment) => {
|
||||
if (!isUser) {
|
||||
showLoginDialog({});
|
||||
showRegisterDialog({});
|
||||
return;
|
||||
}
|
||||
|
||||
return saveComment(comment);
|
||||
},
|
||||
[isUser, showLoginDialog, saveComment],
|
||||
[isUser, showRegisterDialog, saveComment],
|
||||
);
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react';
|
||||
import React, { useCallback } from 'react';
|
||||
|
||||
import { observer } from 'mobx-react-lite';
|
||||
|
||||
|
@ -7,9 +7,12 @@ import { ScrollHelperBottom } from '~/components/common/ScrollHelperBottom';
|
|||
import { Card } from '~/components/containers/Card';
|
||||
import { Footer } from '~/components/main/Footer';
|
||||
import { NodeTitle } from '~/components/node/NodeTitle';
|
||||
import { Dialog } from '~/constants/modal';
|
||||
import { Container } from '~/containers/main/Container';
|
||||
import { SubmitBarRouter } from '~/containers/main/SubmitBarRouter';
|
||||
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 { useNodeBlocks } from '~/hooks/node/useNodeBlocks';
|
||||
import { useNodeCoverImage } from '~/hooks/node/useNodeCoverImage';
|
||||
|
@ -19,6 +22,8 @@ import { useNodeContext } from '~/utils/context/NodeContextProvider';
|
|||
import styles from './styles.module.scss';
|
||||
|
||||
const NodeLayout = observer(() => {
|
||||
const { isUser } = useAuth();
|
||||
const showRegisterDialog = useShowModal(Dialog.Register);
|
||||
const { node, isLoading, update } = useNodeContext();
|
||||
const { head, block } = useNodeBlocks(node, isLoading);
|
||||
const [canEdit, canLike, canStar] = useNodePermissions(node);
|
||||
|
@ -26,6 +31,8 @@ const NodeLayout = observer(() => {
|
|||
|
||||
useNodeCoverImage(node);
|
||||
|
||||
const onUnauthorizedLike = useCallback(() => showRegisterDialog({}), []);
|
||||
|
||||
return (
|
||||
<div className={styles.wrap}>
|
||||
{head}
|
||||
|
@ -46,9 +53,9 @@ const NodeLayout = observer(() => {
|
|||
isLoading={isLoading}
|
||||
createdAt={node.created_at || ''}
|
||||
canEdit={canEdit}
|
||||
canLike={canLike}
|
||||
canLike={canLike || !isUser}
|
||||
canStar={canStar}
|
||||
onLike={onLike}
|
||||
onLike={isUser ? onLike : onUnauthorizedLike}
|
||||
onStar={onStar}
|
||||
onLock={onLock}
|
||||
onEdit={onEdit}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue