add persistent mode to use & other changes

This commit is contained in:
srgooglo 2020-10-29 20:29:09 +01:00
parent 9a123ae190
commit bba876b02b
9 changed files with 267 additions and 238 deletions

View File

@ -1,5 +1,5 @@
import React from 'react'
import styles from './like.less'
import styles from './index.less'
import * as core from 'core'
import classnames from 'classnames'
@ -9,15 +9,22 @@ export default class LikeBtn extends React.Component {
count: this.props.count,
clicked: false,
}
handleClick(){
if (typeof(this.props.handleClick) !== "undefined") {
this.props.handleClick()
this.setState({ clicked: true })
setTimeout(() => {
this.setState({ clicked: false, liked: !this.state.liked })
}, 500)
}else{
return false
}
}
render() {
const { id } = this.props
const { count, liked, clicked } = this.state
const { liked, clicked } = this.state
return (
<div className={styles.btnWrapper}>
<button className={classnames(styles.like_button, {[styles.clickanim]: clicked })} >
<button onClick={() => { this.handleClick() }} className={classnames(styles.like_button, {[styles.clickanim]: clicked })} >
<div className={styles.like_wrapper}>
<div
className={classnames(
@ -39,15 +46,6 @@ export default class LikeBtn extends React.Component {
</svg>
</div>
</button>
<p
className={classnames(styles.likeCounter, {
[styles.active]: !clicked,
[styles.past]: clicked,
})}
>
{count}
</p>
</div>
)
}
}

View File

@ -12,38 +12,6 @@
box-sizing: border-box;
}
.btnWrapper {
display: flex;
}
.likeCounter {
font-family: "Poppins", sans-serif;
line-height: 70px;
margin: 0 0 0 10px;
opacity: 0;
transform: perspective(100px) translateZ(10px);
filter: blur(10px);
letter-spacing: 0.1em;
&.active {
opacity: 1;
transform: perspective(100px) translateZ(0px);
filter: blur(0px);
letter-spacing: 0.15em;
transition: opacity 1000ms linear, transform 1000ms linear, filter 400ms linear, letter-spacing 1000ms linear;
}
&.past {
opacity: 0;
transform: perspective(100px) translateZ(-10px);
filter: blur(10px);
letter-spacing: 0.2em;
transition: opacity 1000ms linear, transform 1000ms linear, filter 400ms linear, letter-spacing 1000ms linear;
}
}
.like_button {
--color-heart: #EA442B;
--easing: cubic-bezier(.7, 0, .3, 1);
@ -115,10 +83,10 @@
}
.like_wrapper {
display: grid;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
z-index: 5;
>* {
margin: auto;
@ -127,10 +95,9 @@
}
.heart {
width: .5em;
height: .5em;
width: 100%;
height: 100%;
display: block;
transform-origin: center 80%;
>path {
stroke: var(--color-heart);
@ -168,8 +135,9 @@
}
.ripple {
height: 1em;
width: 1em;
position: absolute;
height: 100%;
width: 100%;
border-radius: 50%;
overflow: hidden;
z-index: 1;

View File

@ -1,4 +1,4 @@
import React from 'react'
import React, { useLayoutEffect } from 'react'
import * as antd from 'antd'
import styles from './index.less'
import { MediaPlayer } from 'components'
@ -9,7 +9,7 @@ import classnames from 'classnames'
import settings from 'core/libs/settings'
import { router } from 'core/libs'
import LikeBtn from './components/like'
import LikeBtn from './components/like/index.js'
import { connect } from 'umi'
const { Meta } = antd.Card
@ -47,7 +47,7 @@ const contextMenuList = [
title: "Save screenshot",
icon: <Icons.Aperture />,
params: {
itemProps: {
itemProps: {
style: { color: "#40a9ff" }
},
onClick: (e) => {
@ -55,155 +55,166 @@ const contextMenuList = [
}
}
}
]
]
@connect(({ app }) => ({ app }))
export default class PostCard extends React.Component {
export default class PostCard extends React.PureComponent {
state = {
visibleMoreMenu: false,
payload: this.props.payload,
}
elementRef = React.createRef()
handleDispatchInvoke(key, payload) {
this.props.dispatch({
type: "app/ipcInvoke",
payload: { key: key, payload: payload }
})
}
goElementById(id){
document.getElementById(id).scrollIntoView({
behavior: "smooth",
block: "center",
inline: "center"
})
}
handleDispatchInvoke(key, payload) {
this.props.dispatch({
type: "app/ipcInvoke",
payload: { key: key, payload: payload }
})
}
toogleMoreMenu(){
this.setState({ visibleMoreMenu: !this.state.visibleMoreMenu })
}
goElementById(id) {
document.getElementById(id).scrollIntoView({
behavior: "smooth",
block: "center",
inline: "center"
})
}
renderReportedPost(){
if(this.state.ReportIgnore) return null
return (
<div className={styles.post_card_flaggedWarning}>
<Icons.FlagOutlined />
<h3>It seems that this post has been reported</h3>
<p>The content may be inappropriate or compromising</p>
<antd.Button
onClick={() => {
this.setState({ ReportIgnore: true })
}}
>
Ignore
toogleMoreMenu() {
this.setState({ visibleMoreMenu: !this.state.visibleMoreMenu })
}
renderReportedPost() {
if (this.state.ReportIgnore) return null
return (
<div className={styles.post_card_flaggedWarning}>
<Icons.FlagOutlined />
<h3>It seems that this post has been reported</h3>
<p>The content may be inappropriate or compromising</p>
<antd.Button
onClick={() => {
this.setState({ ReportIgnore: true })
}}
>
Ignore
</antd.Button>
</div>
)
}
</div>
)
}
renderPost(data){
const {
id,
post_time,
postText,
postFile,
publisher,
post_likes,
is_post_pinned,
is_liked,
post_comments,
get_post_comments
} = data || defaultPayload
return(
<>
{this.state.postReported? this.renderReportedPost() : null}
<div className={classnames(styles.post_include, {[styles.blur]: this.state.ReportIgnore? false : this.state.postReported })}>
<Meta
avatar={
<div className={styles.postAvatar}>
<antd.Avatar shape="square" size={50} src={publisher.avatar} />
</div>
}
title={
<div className={styles.post_card_title}>
<h4 onClick={() => router.goProfile(publisher.username)} className={styles.titleUser}>
@{publisher.username}
{core.booleanFix(publisher.verified)? (<Icon style={{ color: 'blue' }} component={Icons.verifiedBadge} />) : null}
{core.booleanFix(publisher.nsfw_flag)? (<antd.Tag style={{ margin: '0 0 0 13px' }} color="volcano" > NSFW </antd.Tag> ) : null}
</h4>
<div className={styles.PostTags}>
<div className={styles.MoreMenu}>
<antd.Dropdown onVisibleChange={this.handleVisibleChange} visible={this.state.visibleMoreMenu} trigger={['click']}>
<Icons.MoreOutlined key="actionMenu" />
</antd.Dropdown>
</div>
{core.booleanFix(is_post_pinned)? (<Icons.PushpinFilled />) : null}
</div>
</div>
}
description={<span className={styles.textAgo}>{post_time}</span>}
bordered="false"
/>
{postText ? (
<div className={styles.post_card_content}>
<h3 dangerouslySetInnerHTML={{ __html: postText }} />
</div>
) : null}
{postFile ? (
<div className={styles.post_card_file}>
<MediaPlayer file={postFile} />
</div>
) : null}
<div className={styles.ellipsisIcon}>
<Icons.EllipsisOutlined />
</div>
</div>
</>
)
}
componentDidMount(){
window.contextMenu.addEventListener(
{
priority: 100,
onEventRender: contextMenuList,
ref: this.elementRef.current,
props: { id: this.state.payload.id }
}
)
}
render() {
//
const actions = [
<LikeBtn count={this.state.payload.post_likes} liked={core.booleanFix(this.state.payload.is_liked)} />,
<Icons.Share2 />,
<antd.Badge dot={this.state.payload.post_comments > 0 ? true : false}>
<Icons.MessageSquare key="comments" />
</antd.Badge>,
]
return (
<div ref={this.elementRef} key={this.state.payload.id} id={this.state.payload.id} className={styles.post_card_wrapper}>
<antd.Card
className={settings("post_hidebar") ? null : styles.showMode}
onDoubleClick={() => null}
onClick={() => this.goElementById(this.state.payload.id)}
// onContextMenu={(e) => {
// e.stopPropagation()
// e.preventDefault()
// window.contextMenu.open({ xPos: e.clientX, yPos: e.clientY, renderList: contextMenuList, id: this.state.payload.id })
// }}
actions={actions}
hoverable
>
{this.renderPost(this.state.payload)}
</antd.Card>
renderContent(payload) {
return (
<div>
{payload.postText ? (
<div className={styles.post_card_content}>
<h3 dangerouslySetInnerHTML={{ __html: payload.postText }} />
</div>
)
) : null}
{payload.postFile ? (
<div className={styles.post_card_file}>
<MediaPlayer file={payload.postFile} />
</div>
) : null}
</div>
)
}
componentDidMount() {
window.contextMenu.addEventListener(
{
priority: 100,
onEventRender: contextMenuList,
ref: this.elementRef.current,
props: { id: this.state.payload.id }
}
)
}
handleLikeClick = (id) => {
if (typeof (this.props.handleActions)) {
this.props.handleActions("like", id, (callback) => {
let updated = this.state.payload
if (callback.code == 200) {
updated.is_liked = !this.state.payload.is_liked
updated.post_likes = callback.response.count ?? 0
this.setState({ payload: updated })
} else {
verbosity(`Api error response ${callback.code}`)
}
})
} else {
verbosity(`socket connection not available`)
}
}
render() {
const {
id,
post_time,
postText,
postFile,
publisher,
post_likes,
is_post_pinned,
is_liked,
post_comments,
get_post_comments
} = this.state.payload || defaultPayload
const actions = [
<LikeBtn handleClick={() => { this.handleLikeClick(id) }} count={post_likes} liked={core.booleanFix(is_liked)} />,
<antd.Badge dot={this.state.payload.post_comments > 0 ? true : false}>
<Icons.MessageSquare key="comments" />
</antd.Badge>,
]
return (
<div ref={this.elementRef} key={this.state.payload.id} id={this.state.payload.id} className={styles.post_card_wrapper}>
<antd.Card
className={settings("post_hidebar") ? null : styles.showMode}
onClick={() => this.goElementById(this.state.payload.id)}
actions={actions}
hoverable
>
{this.state.postReported ? this.renderReportedPost() : null}
<div className={classnames(styles.post_include, { [styles.blur]: this.state.ReportIgnore ? false : this.state.postReported })}>
<Meta
avatar={
<div className={styles.postAvatar}>
<antd.Avatar shape="square" size={50} src={publisher.avatar} />
</div>
}
title={
<div className={styles.post_card_title}>
<h4 onClick={() => router.goProfile(publisher.username)} className={styles.titleUser}>
@{publisher.username}
{core.booleanFix(publisher.verified) ? (<Icon style={{ color: 'blue' }} component={Icons.verifiedBadge} />) : null}
{core.booleanFix(publisher.nsfw_flag) ? (<antd.Tag style={{ margin: '0 0 0 13px' }} color="volcano" > NSFW </antd.Tag>) : null}
</h4>
<div className={styles.PostTags}>
<div className={styles.MoreMenu}>
<antd.Dropdown onVisibleChange={this.handleVisibleChange} visible={this.state.visibleMoreMenu} trigger={['click']}>
<Icons.MoreOutlined key="actionMenu" />
</antd.Dropdown>
</div>
{core.booleanFix(is_post_pinned) ? (<Icons.PushpinFilled />) : null}
</div>
</div>
}
description={<span className={styles.textAgo}>{post_time}</span>}
bordered="false"
/>
{this.renderContent(this.state.payload)}
<div className={styles.ellipsisIcon}>
<Icons.EllipsisOutlined />
</div>
<div className={styles.likesIndicator} >
{post_likes} likes
</div>
</div>
</antd.Card>
</div>
)
}
}

View File

@ -17,6 +17,20 @@
}
}
.likesIndicator{
margin: auto;
position: absolute;
bottom: 0;
left:0;
background-color: #242424;
border-radius: 4px;
color: #fff;
width: fit-content;
height: fit-content;
transform: translate(0, -15px);
padding: 5px 12px;
}
.post_card_wrapper {
user-select: none;
box-shadow: @post_card_wrapper_shadow;
@ -131,7 +145,6 @@
}
}
.post_card_title {
display: flex;

View File

@ -196,6 +196,7 @@ export function urlToBase64(url, callback) {
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', url);
xhr.setRequestHeader('Access-Control-Allow-Headers', 'Access-Control-Allow-Origin');
xhr.setRequestHeader('Access-Control-Allow-Origin', '*');
xhr.responseType = 'blob';
xhr.send();

View File

@ -61,12 +61,15 @@ export default {
*resetHeader({ }, { put }) {
yield put({ type: "createNodeSocket" })
},
*use({ scope, invoke, query }, { put, select }) {
*use({ scope, invoke, query, persistent, then }, { put, select }) {
const state = yield select(state => state)
if (!scope || !invoke || !query) {
if (!scope) {
verbosity(`some params is missing`)
return false
}
if (typeof(persistent) == "undefined" ) {
persistent = false
}
if (!state.socket.nodes[scope] && scope !== state.socket.headerNode) {
let opt = {
namespaceOrigin: `/${scope}`,
@ -81,11 +84,18 @@ export default {
connector: state.app.dispatcher,
payload: opt,
then: (socket) => {
socket._emit(invoke, query.payload, (callback) =>{
new Promise((resolve, reject) => resolve(query.callback(callback))).then(() => {
socket.remove()
if (typeof(then) !== "undefined") {
then(socket)
}
if (typeof(query) !== "undefined") {
socket._emit(invoke, query.payload, (callback) =>{
new Promise((resolve, reject) => resolve(query.callback(callback))).then(() => {
if (!persistent) {
socket.remove()
}
})
})
})
}
}
})
}else{

View File

@ -13,67 +13,95 @@ import styles from './index.less'
export default class Explore extends React.Component {
state = {
socket: null,
feed: null,
renderError: false
}
request(){
this.props.dispatch({
type: "socket/use",
scope: "posts",
invoke: "get",
query: {
payload: {
from: "feed",
userToken: this.props.app.session_token
},
callback: (data) => {
if (Array.isArray(data.response)) {
this.setState({ feed: data.response })
}else{
verbosity([`error gathering posts >`, data])
this.setState({ renderError: true })
}
fetchFeed() {
const { socket } = this.state
if (socket) {
const requestPayload = {
from: "feed",
userToken: this.props.app.session_token
}
const requestCallback = (data) => {
if (Array.isArray(data.response)) {
this.setState({ feed: data.response })
} else {
verbosity([`error gathering posts >`, data])
this.setState({ renderError: true })
}
}
})
socket._emit("get", requestPayload, requestCallback)
}
}
componentDidMount(){
if(this.props.app.session_valid){
this.request()
handlePostActions(action, post_id, callback) {
const { socket } = this.state
if (socket) {
const requestPayload = {
userToken: this.props.app.session_token,
post_id,
action
}
socket._emit("actions", requestPayload, (res) => callback(res))
}
}
componentDidMount() {
if (this.props.app.session_valid) {
this.props.dispatch({
type: "socket/use",
persistent: true,
scope: "posts",
then: (data) => {
this.setState({ socket: data })
this.fetchFeed()
}
})
}
}
componentWillUnmount() {
if (this.state.socket) {
this.state.socket.remove()
}
}
render() {
if(!this.props.app.session_valid){
if (!this.props.app.session_valid) {
return <Invalid type="SESSION_INVALID" />
}
if (!this.state.feed){
return (
<antd.Card bordered="false" >
<antd.Skeleton active />
</antd.Card>
)
if (!this.state.feed) {
return (
<antd.Card bordered="false" >
<antd.Skeleton active />
</antd.Card>
)
}
if (this.state.renderError){
if (this.state.renderError) {
return (
<Invalid type="SESSION_INVALID" />
)
}
return(
return (
<div className={styles.exploreWrapper}>
<List
//loadMore={loadMore}
dataSource={this.state.feed}
renderItem={item => (
<PostCard payload={item}/>
)}
//loadMore={loadMore}
dataSource={this.state.feed}
renderItem={item => (
<PostCard handleActions={(...context) => this.handlePostActions(...context)} payload={item} />
)}
/>
</div>
</div>
)
}
}

View File

@ -73,7 +73,6 @@ export default class BackgroundImage extends ThemeConfigurator {
<h4 style={{ color: this.schemeToRGB(this.state.accentColor) }}>Sample text</h4>
<p style={{ color: this.schemeToRGB(this.state.textColor) }}>Some text here</p>
<p style={{ color: this.schemeToRGB(this.state.accentColor) }}>Some text here</p>
</div>
<img style={{ opacity: this.state.model.opacity }} src={this.state.model.src} />
</div> : <h3 style={{ textAlign: 'center' }} > No Background </h3>}

View File

@ -26,6 +26,7 @@
.background_image_preview{
position: relative;
height: 50%;
border-radius: 8px;
img{
z-index: 9;