mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
added follow support
This commit is contained in:
parent
7f28f854b1
commit
57aedb84d4
@ -6,6 +6,7 @@ import verbosity from 'core/libs/verbosity'
|
||||
|
||||
export default class LikeBtn extends React.Component {
|
||||
state = {
|
||||
hoveringCounter: false,
|
||||
liked: this.props.liked,
|
||||
count: this.props.count,
|
||||
clicked: false,
|
||||
@ -34,21 +35,31 @@ export default class LikeBtn extends React.Component {
|
||||
}
|
||||
}, 3000))
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
handleLeave() {
|
||||
if (this.state.hoveringCounter) {
|
||||
this.setState({hoveringCounter: false })
|
||||
}
|
||||
}
|
||||
|
||||
handleOver() {
|
||||
if (!this.state.hoveringCounter) {
|
||||
this.setState({hoveringCounter: true })
|
||||
}
|
||||
}
|
||||
|
||||
getDecoratorCount(count) {
|
||||
return `${core.abbreviateCount(new Number(count).toString())}`
|
||||
return <span>{this.state.hoveringCounter? `${count}` : core.abbreviateCount(new Number(count).toString())}</span>
|
||||
}
|
||||
|
||||
render() {
|
||||
const { liked, clicked, count } = this.state
|
||||
return (
|
||||
<div>
|
||||
<div onMouseLeave={() => this.handleLeave()} onMouseOver={() => this.handleOver()}>
|
||||
<button onClick={() => { this.handleClick() }} className={classnames(styles.like_button, { [styles.clickanim]: clicked })} >
|
||||
<div className={styles.like_wrapper}>
|
||||
<div
|
||||
@ -71,7 +82,7 @@ export default class LikeBtn extends React.Component {
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
<div className={styles.likesIndicator} >
|
||||
<div className={classnames(styles.likesIndicator, {[styles.hover]: this.state.hoveringCounter})} >
|
||||
<span className={classnames(styles.likeCounter, {
|
||||
[styles.active]: !clicked,
|
||||
[styles.past]: clicked,
|
||||
|
@ -16,6 +16,7 @@
|
||||
}
|
||||
|
||||
.likesIndicator{
|
||||
cursor: default;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
z-index: 12;
|
||||
@ -24,26 +25,32 @@
|
||||
background-color: #fff;
|
||||
border-radius: 0 12px 12px 0;
|
||||
color: #333;
|
||||
width: fit-content;
|
||||
width: 52px;
|
||||
height: fit-content;
|
||||
transition: all 150ms ease-in-out;
|
||||
transform: translate(30px, -4px);
|
||||
padding: 5px 14px;
|
||||
|
||||
&.hover{
|
||||
width: 90px;
|
||||
}
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.likeCounter {
|
||||
font-family: "Poppins", sans-serif;
|
||||
|
||||
opacity: 0;
|
||||
transform: perspective(100px) translateZ(10px);
|
||||
filter: blur(10px);
|
||||
letter-spacing: 0.1em;
|
||||
letter-spacing: 0;
|
||||
|
||||
&.active {
|
||||
opacity: 1;
|
||||
//transform: perspective(100px) translateZ(0px);
|
||||
filter: blur(0px);
|
||||
letter-spacing: 0.15em;
|
||||
|
||||
transition: opacity @blur-transition-duration linear, transform @blur-transition-duration linear, filter @blurFilter-transition-duration linear, letter-spacing @blur-transition-duration linear;
|
||||
}
|
||||
|
||||
@ -51,7 +58,7 @@
|
||||
opacity: 0;
|
||||
//transform: perspective(100px) translateZ(-10px);
|
||||
filter: blur(1px);
|
||||
letter-spacing: 0.2em;
|
||||
letter-spacing: 0.15em;
|
||||
transition: opacity @blur-transition-duration linear, transform @blur-transition-duration linear, filter @blurFilter-transition-duration linear, letter-spacing @blur-transition-duration linear;
|
||||
}
|
||||
}
|
||||
|
@ -32,6 +32,8 @@
|
||||
}
|
||||
|
||||
.ant-card {
|
||||
cursor: default;
|
||||
|
||||
border-radius: @post_card_general_border-rd;
|
||||
border: 0;
|
||||
border-top: 1px solid #4646460c;
|
||||
|
@ -88,8 +88,8 @@ export default {
|
||||
then(socket)
|
||||
}
|
||||
if (typeof(query) !== "undefined") {
|
||||
socket._emit(invoke, query.payload, (callback) =>{
|
||||
new Promise((resolve, reject) => resolve(query.callback(callback))).then(() => {
|
||||
socket._emit(invoke, query.payload, (...callbacks) =>{
|
||||
new Promise((resolve, reject) => resolve(query.callback(...callbacks)) ).then(() => {
|
||||
if (!persistent) {
|
||||
socket.remove()
|
||||
}
|
||||
|
@ -16,6 +16,24 @@ export default {
|
||||
state: {
|
||||
},
|
||||
effects: {
|
||||
*actions({callback, payload}, { call, select }) {
|
||||
dispatch({
|
||||
type: "socket/use",
|
||||
scope: "users",
|
||||
invoke: "actions",
|
||||
query: {
|
||||
payload: {
|
||||
from: payload.from,
|
||||
user_id: payload.user_id ?? state.app.session_uuid,
|
||||
username: payload.username ?? state.app.session_authframe["username"],
|
||||
userToken: state.app.session_token
|
||||
},
|
||||
callback: (callbackResponse) => {
|
||||
return callback(callbackResponse)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
*get({ callback, payload }, { call, put, select }) {
|
||||
const dispatch = yield select(state => state.app.dispatcher)
|
||||
const state = yield select(state => state)
|
||||
|
@ -1,8 +1,10 @@
|
||||
import React from 'react'
|
||||
import { pathMatchRegexp } from 'core'
|
||||
import { pathMatchRegexp, booleanFix, __legacy__objectToArray } from 'core'
|
||||
import HandleError from 'core/libs/errorhandler'
|
||||
import { Invalid } from 'components'
|
||||
import styles from './index.less'
|
||||
import GlobalBadges from 'globals/badges_list.json'
|
||||
import * as Icons from 'components/Icons'
|
||||
|
||||
import FollowButton from './components/follow'
|
||||
import Menu from './components/menu'
|
||||
@ -10,8 +12,9 @@ import { PostsFeed } from 'components'
|
||||
|
||||
import * as antd from 'antd'
|
||||
import { connect } from 'umi'
|
||||
import { verbosity } from '../../core/libs'
|
||||
|
||||
class UserLayout extends React.Component {
|
||||
export class UserLayout extends React.Component {
|
||||
state = {
|
||||
styleComponent: "UserLayout",
|
||||
userString: pathMatchRegexp('/@/:id', location.pathname)[1],
|
||||
@ -24,6 +27,22 @@ class UserLayout extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
handleClickFollow(user_id) {
|
||||
if (typeof (this.props.onFollow) !== "undefined") {
|
||||
this.updateFollow(!booleanFix(this.state.layoutData.is_following))
|
||||
|
||||
this.props.onFollow(user_id, (callback) => {
|
||||
this.updateFollow(callback)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
updateFollow(to) {
|
||||
let updated = this.state.layoutData
|
||||
updated.is_following = to
|
||||
this.setState({ layoutData: updated })
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { layoutData } = this.props
|
||||
if (layoutData) {
|
||||
@ -31,11 +50,61 @@ class UserLayout extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
renderUserBadges() {
|
||||
let { layoutData } = this.state
|
||||
if (typeof(layoutData.user_tags) == "undefined") {
|
||||
return null
|
||||
}
|
||||
let userTags = __legacy__objectToArray(layoutData.user_tags)
|
||||
let renderTags = []
|
||||
|
||||
if (!userTags) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
userTags = JSON.parse(userTags[0].value.badges)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
|
||||
if (!userTags) {
|
||||
return null
|
||||
}
|
||||
|
||||
__legacy__objectToArray(GlobalBadges).forEach(e => {
|
||||
if(userTags.includes(e.value.id)) {
|
||||
renderTags.push(e.value)
|
||||
}
|
||||
})
|
||||
|
||||
try {
|
||||
if (Array.isArray(userTags)) {
|
||||
return renderTags.map((element) => {
|
||||
return(
|
||||
<antd.Tooltip key={element.key ?? Math.random()} title={element.tip} >
|
||||
<antd.Tag icon={React.createElement(Icons[element.icon]) ?? null} key={element.key ?? Math.random()} color={element.color ?? "default"} >
|
||||
{element.title ?? "maybe"}
|
||||
</antd.Tag>
|
||||
</antd.Tooltip>
|
||||
)
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
return null
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
render() {
|
||||
const { styleComponent } = this.state
|
||||
const toStyles = e => styles[`${styleComponent}_${e}`]
|
||||
const { followers_count } = this.state.layoutData.details ?? { }
|
||||
const { followers_count } = this.state.layoutData.details ?? {}
|
||||
const isFollowed = booleanFix(this.state.layoutData.is_following)
|
||||
|
||||
if (!this.state.layoutData) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<div className={toStyles("wrapper")} >
|
||||
<div className={toStyles("cover")}>
|
||||
@ -46,8 +115,10 @@ class UserLayout extends React.Component {
|
||||
<div className={toStyles("avatar")}>
|
||||
<antd.Avatar shape="square" src={this.state.layoutData.avatar} />
|
||||
</div>
|
||||
|
||||
<div className={toStyles("title")}>
|
||||
<div style={{ display: "flex", alignItems: "center", marginBottom: "7px" }} >
|
||||
{/* {this.renderUserBadges()} */}
|
||||
</div>
|
||||
<antd.Tooltip title={`${followers_count ?? "Non-existent"} Followers`}>
|
||||
<h1>{this.state.userString}</h1>
|
||||
</antd.Tooltip>
|
||||
@ -60,19 +131,19 @@ class UserLayout extends React.Component {
|
||||
marginBottom: '5px',
|
||||
}}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: this.state.layoutData.about,
|
||||
__html: typeof(this.state.layoutData.about) == "string"? this.state.layoutData.about : null,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={toStyles("options")}>
|
||||
<div><FollowButton followed={this.state.layoutData.follow} /></div>
|
||||
<div><FollowButton onClick={() => { this.handleClickFollow(this.state.layoutData.user_id) }} followed={isFollowed} /></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div className={toStyles("content")}>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@ -90,6 +161,41 @@ export default class UserIndexer extends React.Component {
|
||||
|
||||
promiseState = async state => new Promise(resolve => this.setState(state, resolve));
|
||||
|
||||
handleClickFollow(user_id, callback) {
|
||||
if (this.props.app.session_valid) {
|
||||
const requestCallback = (callbackResponse) => {
|
||||
if (callbackResponse.code == 200) {
|
||||
const response = callbackResponse.response
|
||||
const result =( response.follow ?? false) == "followed" ? true : false
|
||||
if (typeof(response) !== "undefined") {
|
||||
return callback(result)
|
||||
}else{
|
||||
return false
|
||||
}
|
||||
}else{
|
||||
return callback(null)
|
||||
}
|
||||
}
|
||||
|
||||
this.props.dispatch({
|
||||
type: "socket/use",
|
||||
scope: "users",
|
||||
invoke: "actions",
|
||||
query: {
|
||||
payload: {
|
||||
userToken: this.props.app.session_token,
|
||||
action: "follow",
|
||||
user_id,
|
||||
},
|
||||
callback: requestCallback
|
||||
}
|
||||
})
|
||||
}else{
|
||||
verbosity(`Need auth`)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const matchRegexp = pathMatchRegexp('/@/:id', location.pathname)
|
||||
|
||||
@ -123,7 +229,7 @@ export default class UserIndexer extends React.Component {
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<UserLayout layoutData={this.state.layoutData} />
|
||||
<UserLayout onFollow={(...context) => {this.handleClickFollow(...context)}} layoutData={this.state.layoutData} />
|
||||
<PostsFeed from="user" fromID={this.state.layoutData.user_id} />
|
||||
</div>
|
||||
|
||||
|
@ -4,7 +4,7 @@ import classnames from 'classnames'
|
||||
|
||||
const FollowButton = (props) => {
|
||||
return (
|
||||
<a className={classnames(styles.button, {[styles.disabled]: !props.followed })}>
|
||||
<a onClick={props.onClick} className={classnames(styles.button, {[styles.disabled]: !props.followed })}>
|
||||
<span>{props.followed ? 'Following' : 'Follow'}</span>
|
||||
</a>
|
||||
)
|
||||
|
@ -82,10 +82,12 @@
|
||||
.ant-avatar {
|
||||
box-shadow: 13px 13px 17px 4px rgba(69, 69, 69, 0.151);
|
||||
border-radius: 7px;
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
|
||||
img {
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user