mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-14 04:54:16 +00:00
302 lines
8.1 KiB
JavaScript
Executable File
302 lines
8.1 KiB
JavaScript
Executable File
import React from "react"
|
|
import * as antd from "antd"
|
|
import { Icons } from "components/Icons"
|
|
import { PostCard, LoadMore } from "components"
|
|
import { ViewportList } from "react-viewport-list"
|
|
|
|
import PostModel from "models/post"
|
|
|
|
import "./index.less"
|
|
|
|
const LoadingComponent = () => {
|
|
// FIXME: Im not sure why but, using <antd.Skeleton> will cause a memory leak of DOM Nodes when using IntersectionObserver
|
|
//return <antd.Skeleton active />
|
|
|
|
return <p><Icons.LoadingOutlined spin className="loadingIcon" />Loading more ...</p>
|
|
}
|
|
|
|
const NoResultComponent = () => {
|
|
return <antd.Result
|
|
status="info"
|
|
title="This is the end"
|
|
subTitle="We dont have more posts for you"
|
|
/>
|
|
}
|
|
|
|
export class PostsListsComponent extends React.Component {
|
|
state = {
|
|
openPost: null,
|
|
|
|
loading: false,
|
|
initialLoading: true,
|
|
|
|
realtimeUpdates: true,
|
|
|
|
hasMore: true,
|
|
list: this.props.list ?? [],
|
|
}
|
|
|
|
viewRef = this.props.innerRef
|
|
|
|
timelineWsEvents = {
|
|
"post.new": (data) => {
|
|
console.log("New post => ", data)
|
|
|
|
if (!this.state.realtimeUpdates) {
|
|
return
|
|
}
|
|
|
|
this.setState({
|
|
list: [data, ...this.state.list],
|
|
})
|
|
},
|
|
"post.delete": (id) => {
|
|
console.log("Deleted post => ", id)
|
|
|
|
this.setState({
|
|
list: this.state.list.filter((post) => {
|
|
return post._id !== id
|
|
}),
|
|
})
|
|
}
|
|
}
|
|
|
|
handleLoad = async (fn) => {
|
|
this.setState({
|
|
loading: true,
|
|
})
|
|
|
|
let payload = {
|
|
trim: this.state.list.length,
|
|
}
|
|
|
|
if (this.props.loadFromModelProps) {
|
|
payload = {
|
|
...payload,
|
|
...this.props.loadFromModelProps,
|
|
}
|
|
}
|
|
|
|
const result = await fn(payload).catch((err) => {
|
|
console.error(err)
|
|
|
|
app.message.error("Failed to load more posts")
|
|
|
|
return null
|
|
})
|
|
|
|
if (result) {
|
|
if (result.length === 0) {
|
|
return this.setState({
|
|
hasMore: false,
|
|
})
|
|
}
|
|
|
|
this.setState({
|
|
list: [...this.state.list, ...result],
|
|
})
|
|
}
|
|
|
|
this.setState({
|
|
loading: false,
|
|
})
|
|
}
|
|
|
|
addPost = (post) => {
|
|
this.setState({
|
|
list: [post, ...this.state.list],
|
|
})
|
|
}
|
|
|
|
removePost = (id) => {
|
|
this.setState({
|
|
list: this.state.list.filter((post) => {
|
|
return post._id !== id
|
|
}),
|
|
})
|
|
}
|
|
|
|
_hacks = {
|
|
addPost: this.addPost,
|
|
removePost: this.removePost,
|
|
addRandomPost: () => {
|
|
this.addPost({
|
|
_id: Math.random().toString(36).substring(7),
|
|
message: `Random post ${Math.random().toString(36).substring(7)}`,
|
|
user: {
|
|
_id: Math.random().toString(36).substring(7),
|
|
username: "random user",
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
onResumeRealtimeUpdates = async () => {
|
|
// fetch new posts
|
|
await this.handleLoad(this.props.loadFromModel)
|
|
|
|
this.setState({
|
|
realtimeUpdates: true,
|
|
})
|
|
}
|
|
|
|
onScrollList = (e) => {
|
|
const { scrollTop, scrollHeight, clientHeight } = e.target
|
|
|
|
if (scrollTop + clientHeight >= scrollHeight - 100) {
|
|
this.setState({
|
|
realtimeUpdates: false,
|
|
})
|
|
}
|
|
}
|
|
|
|
componentDidMount = async () => {
|
|
if (typeof this.props.loadFromModel === "function") {
|
|
await this.handleLoad(this.props.loadFromModel)
|
|
}
|
|
|
|
this.setState({
|
|
initialLoading: false,
|
|
})
|
|
|
|
if (this.props.watchTimeline) {
|
|
Object.entries(this.timelineWsEvents).forEach(([event, callback]) => {
|
|
app.cores.api.listenEvent(event, callback)
|
|
})
|
|
}
|
|
|
|
//console.log("PostsList mounted", this.viewRef)
|
|
|
|
if (this.viewRef) {
|
|
// handle when the user is scrolling a bit down, disable ws events
|
|
this.viewRef.current.addEventListener("scroll", this.onScrollList)
|
|
}
|
|
|
|
window._hacks = this._hacks
|
|
}
|
|
|
|
componentWillUnmount = async () => {
|
|
if (this.props.watchTimeline) {
|
|
Object.entries(this.timelineWsEvents).forEach(([event, callback]) => {
|
|
app.cores.api.unlistenEvent(event, callback)
|
|
})
|
|
}
|
|
|
|
if (this.viewRef) {
|
|
this.viewRef.current.removeEventListener("scroll", this.onScrollList)
|
|
}
|
|
|
|
window._hacks = null
|
|
}
|
|
|
|
componentDidUpdate = async (prevProps) => {
|
|
if (prevProps.list !== this.props.list) {
|
|
this.setState({
|
|
list: this.props.list,
|
|
})
|
|
}
|
|
}
|
|
|
|
onLikePost = async (data) => {
|
|
let result = await PostModel.toogleLike({ post_id: data._id }).catch(() => {
|
|
antd.message.error("Failed to like post")
|
|
|
|
return false
|
|
})
|
|
|
|
return result
|
|
}
|
|
|
|
onSavePost = async (data) => {
|
|
let result = await PostModel.toogleSave({ post_id: data._id }).catch(() => {
|
|
antd.message.error("Failed to save post")
|
|
|
|
return false
|
|
})
|
|
|
|
return result
|
|
}
|
|
|
|
onDeletePost = async (data) => {
|
|
antd.Modal.confirm({
|
|
title: "Are you sure you want to delete this post?",
|
|
content: "This action is irreversible",
|
|
okText: "Yes",
|
|
okType: "danger",
|
|
cancelText: "No",
|
|
onOk: async () => {
|
|
await PostModel.deletePost({ post_id: data._id }).catch(() => {
|
|
antd.message.error("Failed to delete post")
|
|
})
|
|
},
|
|
})
|
|
}
|
|
|
|
onToogleOpen = (to, data) => {
|
|
if (typeof this.props.onOpenPost === "function") {
|
|
this.props.onOpenPost(to, data)
|
|
}
|
|
}
|
|
|
|
onLoadMore = async () => {
|
|
if (typeof this.props.onLoadMore === "function") {
|
|
return this.handleLoad(this.props.onLoadMore)
|
|
}
|
|
}
|
|
|
|
render() {
|
|
if (this.state.initialLoading) {
|
|
return <antd.Skeleton active />
|
|
}
|
|
|
|
if (this.state.list.length === 0) {
|
|
if (typeof this.props.emptyListRender === "function") {
|
|
return React.createElement(this.props.emptyListRender)
|
|
}
|
|
|
|
return <div className="no_more_posts">
|
|
<antd.Empty />
|
|
<h1>Whoa, nothing on here...</h1>
|
|
</div>
|
|
}
|
|
|
|
return <LoadMore
|
|
className="postList"
|
|
loadingComponent={LoadingComponent}
|
|
noResultComponent={NoResultComponent}
|
|
hasMore={this.state.hasMore}
|
|
fetching={this.state.loading}
|
|
onBottom={this.onLoadMore}
|
|
>
|
|
{
|
|
!this.state.realtimeUpdates && <div className="realtime_updates_disabled">
|
|
<antd.Alert
|
|
message="Realtime updates disabled"
|
|
description="You are scrolling down, realtime updates are disabled to improve performance"
|
|
type="warning"
|
|
showIcon
|
|
/>
|
|
</div>
|
|
}
|
|
<ViewportList
|
|
viewportRef={this.viewRef}
|
|
items={this.state.list}
|
|
>
|
|
{
|
|
(item) => <PostCard
|
|
key={item._id}
|
|
data={item}
|
|
events={{
|
|
onClickLike: this.onLikePost,
|
|
onClickSave: this.onSavePost,
|
|
onClickDelete: this.onDeletePost,
|
|
onClickEdit: this.onEditPost,
|
|
}}
|
|
/>
|
|
}
|
|
</ViewportList>
|
|
</LoadMore>
|
|
}
|
|
}
|
|
|
|
export default React.forwardRef((props, ref) => <PostsListsComponent innerRef={ref} {...props} />) |