mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 18:44:16 +00:00
added like button logic
This commit is contained in:
parent
f495d802d2
commit
1259735498
@ -1,10 +1,54 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
import { Icons } from "components/Icons"
|
import { Icons } from "components/Icons"
|
||||||
|
import classnames from "classnames"
|
||||||
import moment from "moment"
|
import moment from "moment"
|
||||||
|
|
||||||
|
import { User } from "models"
|
||||||
|
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
|
function LikeButton(props) {
|
||||||
|
const [liked, setLiked] = React.useState(props.defaultLiked ?? false)
|
||||||
|
|
||||||
|
const handleClick = async () => {
|
||||||
|
let to = !liked
|
||||||
|
|
||||||
|
if (typeof props.onClick === "function") {
|
||||||
|
const result = await props.onClick(to)
|
||||||
|
if (typeof result === "boolean") {
|
||||||
|
to = result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setLiked(to)
|
||||||
|
}
|
||||||
|
|
||||||
|
return <button
|
||||||
|
className={classnames("likeButton", { ["clicked"]: liked })}
|
||||||
|
onClick={handleClick}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={classnames(
|
||||||
|
"ripple",
|
||||||
|
{ ["clicked"]: liked }
|
||||||
|
)}
|
||||||
|
></div>
|
||||||
|
<svg
|
||||||
|
className={classnames(
|
||||||
|
"heart",
|
||||||
|
{ ["empty"]: !liked },
|
||||||
|
{ ["clicked"]: liked },
|
||||||
|
)}
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path d="M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z"></path>
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
|
||||||
function PostHeader({ postData }) {
|
function PostHeader({ postData }) {
|
||||||
const [timeAgo, setTimeAgo] = React.useState(0)
|
const [timeAgo, setTimeAgo] = React.useState(0)
|
||||||
|
|
||||||
@ -50,9 +94,9 @@ function PostContent({ message }) {
|
|||||||
|
|
||||||
function PostActions(props) {
|
function PostActions(props) {
|
||||||
return <div className="actions">
|
return <div className="actions">
|
||||||
<div className="action" id="likes" onClick={props.onClickLike}>
|
<div className="action" id="likes">
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<Icons.Heart />
|
<LikeButton defaultLiked={props.defaultLiked} onClick={props.onClickLike} />
|
||||||
</div>
|
</div>
|
||||||
<div className="value">
|
<div className="value">
|
||||||
{String(props.likes)}
|
{String(props.likes)}
|
||||||
@ -80,7 +124,56 @@ function PostActions(props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default class PostCard extends React.Component {
|
export default class PostCard extends React.Component {
|
||||||
|
state = {
|
||||||
|
loading: true,
|
||||||
|
selfId: null,
|
||||||
|
data: this.props.data,
|
||||||
|
}
|
||||||
|
|
||||||
|
api = window.app.request
|
||||||
|
|
||||||
|
componentDidMount = async () => {
|
||||||
|
const selfId = await User.selfUserId()
|
||||||
|
|
||||||
|
window.app.ws.listen(`like.post.${this.props.data._id}`, async (data) => {
|
||||||
|
await this.setState({ data })
|
||||||
|
})
|
||||||
|
window.app.ws.listen(`unlike.post.${this.props.data._id}`, async (data) => {
|
||||||
|
await this.setState({ data })
|
||||||
|
})
|
||||||
|
|
||||||
|
await this.setState({
|
||||||
|
selfId,
|
||||||
|
likes: this.props.data.likes,
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onClickLike = async (to) => {
|
||||||
|
let result = false
|
||||||
|
|
||||||
|
if (to) {
|
||||||
|
const apiResult = await await this.api.put.like({ post_id: this.props.data._id })
|
||||||
|
result = apiResult.success
|
||||||
|
} else {
|
||||||
|
const apiResult = await await this.api.put.unlike({ post_id: this.props.data._id })
|
||||||
|
result = apiResult.success
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
hasLiked = () => {
|
||||||
|
return this.props.data.likes.some(user_id => user_id === this.state.selfId)
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
const defaultLiked = this.hasLiked()
|
||||||
|
|
||||||
|
if (this.state.loading) {
|
||||||
|
return <antd.Skeleton active />
|
||||||
|
}
|
||||||
|
|
||||||
return <div className="postCard">
|
return <div className="postCard">
|
||||||
<div className="wrapper">
|
<div className="wrapper">
|
||||||
<PostHeader
|
<PostHeader
|
||||||
@ -92,8 +185,10 @@ export default class PostCard extends React.Component {
|
|||||||
</div>
|
</div>
|
||||||
<div className="actionsWrapper">
|
<div className="actionsWrapper">
|
||||||
<PostActions
|
<PostActions
|
||||||
likes={this.props.data.likes.length}
|
onClickLike={this.onClickLike}
|
||||||
comments={this.props.data.comments.length}
|
defaultLiked={defaultLiked}
|
||||||
|
likes={this.state.data.likes.length}
|
||||||
|
comments={this.state.data.comments.length}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -190,4 +190,157 @@
|
|||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@color-heart : #EA442B;
|
||||||
|
@likeAnimationDuration : .5s;
|
||||||
|
@likeAnimationEasing : cubic-bezier(.7, 0, .3, 1);
|
||||||
|
|
||||||
|
.likeButton {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
.ripple,
|
||||||
|
.ripple:before,
|
||||||
|
.ripple:after {
|
||||||
|
position : relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
font-size : 40px;
|
||||||
|
border : none;
|
||||||
|
border-radius: 50%;
|
||||||
|
width : 1em;
|
||||||
|
height : 1em;
|
||||||
|
padding : 0;
|
||||||
|
margin : 0;
|
||||||
|
outline : none;
|
||||||
|
z-index : 2;
|
||||||
|
transition : transform @likeAnimationDuration @likeAnimationEasing;
|
||||||
|
cursor : pointer;
|
||||||
|
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
z-index : -1;
|
||||||
|
content : '';
|
||||||
|
position : absolute;
|
||||||
|
top : 0;
|
||||||
|
left : 0;
|
||||||
|
width : 100%;
|
||||||
|
height : 100%;
|
||||||
|
border-radius: inherit;
|
||||||
|
transition : inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content : '';
|
||||||
|
position : absolute;
|
||||||
|
top : 0;
|
||||||
|
left : 0;
|
||||||
|
width : 100%;
|
||||||
|
height : 100%;
|
||||||
|
border-radius : inherit;
|
||||||
|
z-index : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.heart {
|
||||||
|
position: relative;
|
||||||
|
>path {
|
||||||
|
stroke : var(--primaryColor);
|
||||||
|
stroke-width: 2;
|
||||||
|
transition : fill @likeAnimationDuration @likeAnimationEasing;
|
||||||
|
fill : var(--primaryColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.empty {
|
||||||
|
>path {
|
||||||
|
stroke : var(--primaryColor);
|
||||||
|
stroke-width: 2;
|
||||||
|
transition : fill @likeAnimationDuration @likeAnimationEasing;
|
||||||
|
fill : transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.clicked {
|
||||||
|
animation: heart-bounce @likeAnimationDuration @likeAnimationEasing;
|
||||||
|
|
||||||
|
@keyframes heart-bounce {
|
||||||
|
40% {
|
||||||
|
transform: scale(0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
0%,
|
||||||
|
80%,
|
||||||
|
100% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ripple {
|
||||||
|
position: absolute;
|
||||||
|
height : 1em;
|
||||||
|
width : 1em;
|
||||||
|
border-radius: 50%;
|
||||||
|
overflow : hidden;
|
||||||
|
z-index : 1;
|
||||||
|
|
||||||
|
&:before {
|
||||||
|
content : '';
|
||||||
|
position : absolute;
|
||||||
|
top : 0;
|
||||||
|
left : 0;
|
||||||
|
width : 100%;
|
||||||
|
height : 100%;
|
||||||
|
border : .4em solid var(--primaryColor);
|
||||||
|
border-radius: inherit;
|
||||||
|
transform : scale(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.clicked {
|
||||||
|
&:before {
|
||||||
|
animation: ripple-out @likeAnimationDuration @likeAnimationEasing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes ripple-out {
|
||||||
|
from {
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
transform: scale(5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes depress {
|
||||||
|
|
||||||
|
from,
|
||||||
|
to {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: translateY(5%) scale(0.9);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes depress-shadow {
|
||||||
|
|
||||||
|
from,
|
||||||
|
to {
|
||||||
|
transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
transform: scale(0.5);
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user