diff --git a/packages/app/src/components/PostCard/index.jsx b/packages/app/src/components/PostCard/index.jsx index df77ec97..2ad8d00b 100644 --- a/packages/app/src/components/PostCard/index.jsx +++ b/packages/app/src/components/PostCard/index.jsx @@ -1,10 +1,54 @@ import React from "react" import * as antd from "antd" import { Icons } from "components/Icons" +import classnames from "classnames" import moment from "moment" +import { User } from "models" + 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 +} + function PostHeader({ postData }) { const [timeAgo, setTimeAgo] = React.useState(0) @@ -50,9 +94,9 @@ function PostContent({ message }) { function PostActions(props) { return
-
+
- +
{String(props.likes)} @@ -80,7 +124,56 @@ function PostActions(props) { } 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() { + const defaultLiked = this.hasLiked() + + if (this.state.loading) { + return + } + return
diff --git a/packages/app/src/components/PostCard/index.less b/packages/app/src/components/PostCard/index.less index 0388ad76..4f9d4059 100644 --- a/packages/app/src/components/PostCard/index.less +++ b/packages/app/src/components/PostCard/index.less @@ -190,4 +190,157 @@ to { 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); + } } \ No newline at end of file