use like logic

This commit is contained in:
srgooglo 2022-03-02 20:34:17 +01:00
parent 914655ecd6
commit e7561c06c8
2 changed files with 148 additions and 272 deletions

View File

@ -1,62 +1,19 @@
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 { LikeButton } from "components"
import moment from "moment" import moment from "moment"
import classnames from "classnames"
import { User } from "models" import { User } from "models"
import "./index.less" import "./index.less"
function LikeButton(props) { function PostHeader(props) {
const [liked, setLiked] = React.useState(props.defaultLiked ?? false)
const [clicked, setCliked] = React.useState(false)
const handleClick = async () => {
let to = !liked
setCliked(to)
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"]: clicked }
)}
></div>
<svg
className={classnames(
"heart",
{ ["empty"]: !liked },
{ ["clicked"]: clicked },
)}
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 }) {
const [timeAgo, setTimeAgo] = React.useState(0) const [timeAgo, setTimeAgo] = React.useState(0)
const updateTimeAgo = () => { const updateTimeAgo = () => {
setTimeAgo(moment(postData.created_at ?? "").fromNow()) setTimeAgo(moment(props.postData.created_at ?? "").fromNow())
} }
React.useEffect(() => { React.useEffect(() => {
@ -69,21 +26,31 @@ function PostHeader({ postData }) {
return () => { return () => {
clearInterval(interval) clearInterval(interval)
} }
}, [postData.created_at]) }, [props.postData.created_at])
return <div className="userInfo"> return <div className="postHeader">
<div className="avatar"> <div className="userInfo">
<antd.Avatar src={postData.user?.avatar} /> <div className="avatar">
</div> <antd.Avatar src={props.postData.user?.avatar} />
<div className="info">
<div>
<h1>
{postData.user?.fullName ?? `@${postData.user?.username}`}
</h1>
</div> </div>
<div className="info">
<div>
<h1>
{props.postData.user?.fullName ?? `@${props.postData.user?.username}`}
</h1>
</div>
<div> <div>
{timeAgo} {timeAgo}
</div>
</div>
</div>
<div className="postHeaderActions">
<div className="item" onClick={props.onClickLike}>
{props.isLiked && <Icons.Heart id="likeIndicator" />}
</div>
<div className="item" onClick={props.onClickSave}>
<Icons.Bookmark />
</div> </div>
</div> </div>
</div> </div>
@ -91,8 +58,8 @@ function PostHeader({ postData }) {
function PostContent({ message }) { function PostContent({ message }) {
return <div className="content"> return <div className="content">
{message} {message}
</div> </div>
} }
function PostActions(props) { function PostActions(props) {
@ -147,7 +114,6 @@ export default class PostCard extends React.Component {
await this.setState({ await this.setState({
selfId, selfId,
likes: this.props.data.likes,
loading: false loading: false
}) })
} }
@ -166,30 +132,49 @@ export default class PostCard extends React.Component {
return result return result
} }
onClickSave = async () => {
// TODO: save post
}
hasLiked = () => { hasLiked = () => {
return this.props.data.likes.some(user_id => user_id === this.state.selfId) return this.state.data.likes.some(user_id => user_id === this.state.selfId)
}
isSelf = () => {
return this.state.selfId === this.state.data.user._id
} }
render() { render() {
const defaultLiked = this.hasLiked() const hasLiked = this.hasLiked()
if (this.state.loading) { if (this.state.loading) {
return <antd.Skeleton active /> return <antd.Skeleton active />
} }
return <div id={this.props.data._id} key={this.props.data._id} className="postCard"> return <div
id={this.props.data._id}
key={this.props.data._id}
className={classnames("postCard", { ["liked"]: hasLiked })}
>
<div className="wrapper"> <div className="wrapper">
<PostHeader <PostHeader
postData={this.props.data} postData={this.props.data}
isLiked={hasLiked}
onClickLike={() => this.onClickLike(false)}
onClickSave={this.onClickSave}
/> />
<PostContent <PostContent
message={this.props.data.message} message={this.props.data.message}
/> />
</div> </div>
<div className="actionsIndicator">
<Icons.MoreHorizontal />
</div>
<div className="actionsWrapper"> <div className="actionsWrapper">
<PostActions <PostActions
onClickLike={this.onClickLike} onClickLike={this.onClickLike}
defaultLiked={defaultLiked} defaultLiked={hasLiked}
isSelf={this.isSelf()}
likes={this.state.data.likes.length} likes={this.state.data.likes.length}
comments={this.state.data.comments.length} comments={this.state.data.comments.length}
/> />

View File

@ -3,7 +3,7 @@
flex-direction: column; flex-direction: column;
width : 100%; width : 100%;
max-width: 40vw; max-width: 600px;
filter: drop-shadow(3px 3px 2px #c5c5c5); filter: drop-shadow(3px 3px 2px #c5c5c5);
@ -12,6 +12,29 @@
transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out;
outline-width: 1px;
outline-style: solid;
outline-color: transparent;
&.liked {
filter: drop-shadow(0px 0px 2px var(--primaryColor));
//outline-color: ;
}
.actionsIndicator {
display : flex;
flex-direction : row;
align-items : center;
justify-content: center;
width : 100%;
margin-bottom: 5px;
font-size: 18px;
color : var(--background-color-contrast);
}
.wrapper { .wrapper {
display : inline-flex; display : inline-flex;
flex-direction: column; flex-direction: column;
@ -22,35 +45,56 @@
transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out;
.userInfo { .postHeader {
display : inline-flex; display : inline-flex;
flex-direction: row; flex-direction : row;
align-items : center; justify-content: space-between;
margin-bottom: 15px; .userInfo {
display : inline-flex;
flex-direction: row;
align-items : center;
>div { margin-bottom: 15px;
margin-right: 10px;
}
.info {
display : inline-flex;
flex-direction : column;
align-items : center;
justify-content: start;
text-align: start;
width: fit-content;
h1 {
margin : 0;
font-family: "DM Mono", monospace;
align-self : start;
}
>div { >div {
align-self: start; margin-right: 10px;
}
.info {
display : inline-flex;
flex-direction : column;
align-items : center;
justify-content: start;
text-align: start;
width: fit-content;
h1 {
margin : 0;
font-family: "DM Mono", monospace;
align-self : start;
}
>div {
align-self: start;
}
}
}
.postHeaderActions {
display : inline-flex;
flex-direction: row;
font-size : 16px;
.item {
cursor: pointer;
}
#likeIndicator {
color: var(--primaryColor);
fill : var(--primaryColor);
} }
} }
} }
@ -60,9 +104,10 @@
flex-direction: column; flex-direction: column;
align-items : flex-start; align-items : flex-start;
background-color: var(--background-color-primary); //background-color: var(--background-color-primary);
padding : 10px;
border-radius : 8px; padding : 0 10px 10px 10px;
border-radius: 8px;
font-size : 14px; font-size : 14px;
font-family: "Poppins", sans-serif; font-family: "Poppins", sans-serif;
@ -112,39 +157,38 @@
transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out;
#likes {
transition: all 0.2s ease-in-out;
color: var(--primaryColor) !important;
svg {
transition: all 0.2s ease-in-out;
color : var(--background-color-contrast) !important;
}
}
#likes:hover {
svg {
color: var(--primaryColor) !important;
}
}
.action { .action {
cursor : pointer; display : inline-flex;
flex-direction: column;
transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out;
.icon {
cursor : pointer;
transition: all 0.2s ease-in-out;
svg {
transition: all 0.2s ease-in-out;
}
}
.value { .value {
position: absolute;
bottom : 0;
font-size : 14px;
font-family: "DM Mono", monospace;
transform : translate(0, 50%);
transition: all 0.2s ease-in-out; transition: all 0.2s ease-in-out;
width : 0;
opacity : 0;
} }
} }
.action:hover { .action:hover {
.value { .icon {
margin-left: 3px; svg {
width : 20px; color: var(--primaryColor) !important;
opacity : 1; }
} }
} }
@ -190,157 +234,4 @@
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);
}
} }