mirror of
https://github.com/ragestudio/comty.git
synced 2025-07-08 16:54:15 +00:00
Refactor PostsList to functional component and update post loading logic
This commit is contained in:
parent
79fbb74f32
commit
f79db03a1b
@ -4,58 +4,55 @@ import classnames from "classnames"
|
|||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
export default React.forwardRef((props, ref) => {
|
export default React.forwardRef((props, ref) => {
|
||||||
const {
|
const {
|
||||||
className,
|
className,
|
||||||
children,
|
children,
|
||||||
hasMore,
|
hasMore = false,
|
||||||
loadingComponent,
|
loadingComponent,
|
||||||
noResultComponent,
|
contentProps = {},
|
||||||
contentProps = {},
|
} = props
|
||||||
} = props
|
|
||||||
|
|
||||||
let observer = null
|
let observer = null
|
||||||
|
|
||||||
const insideViewportCb = (entries) => {
|
const insideViewportCb = (entries) => {
|
||||||
const { fetching, onBottom } = props
|
const { fetching, onBottom } = props
|
||||||
|
|
||||||
entries.forEach(element => {
|
entries.forEach((element) => {
|
||||||
if (element.intersectionRatio > 0 && !fetching) {
|
if (element.intersectionRatio > 0 && !fetching) {
|
||||||
onBottom()
|
onBottom()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
try {
|
try {
|
||||||
const node = document.getElementById("bottom")
|
const node = document.getElementById("bottom")
|
||||||
|
|
||||||
observer = new IntersectionObserver(insideViewportCb)
|
observer = new IntersectionObserver(insideViewportCb)
|
||||||
observer.observe(node)
|
observer.observe(node)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log("err in finding node", err)
|
console.log("err in finding node", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
observer.disconnect()
|
observer.disconnect()
|
||||||
observer = null
|
observer = null
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return <div
|
return (
|
||||||
ref={ref}
|
<div ref={ref} className={classnames(className)} {...contentProps}>
|
||||||
className={classnames(className)}
|
{children}
|
||||||
{...contentProps}
|
|
||||||
>
|
|
||||||
{children}
|
|
||||||
|
|
||||||
<div style={{ clear: "both" }} />
|
<div style={{ clear: "both" }} />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id="bottom"
|
id="bottom"
|
||||||
className="bottom"
|
className="bottom"
|
||||||
style={{ display: hasMore ? "block" : "none" }}
|
style={{ display: hasMore ? "block" : "none" }}
|
||||||
>
|
>
|
||||||
{loadingComponent && React.createElement(loadingComponent)}
|
{loadingComponent && React.createElement(loadingComponent)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
)
|
||||||
})
|
})
|
@ -1,5 +1,6 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
|
import lodash from "lodash"
|
||||||
import { AnimatePresence } from "motion/react"
|
import { AnimatePresence } from "motion/react"
|
||||||
import { Icons } from "@components/Icons"
|
import { Icons } from "@components/Icons"
|
||||||
|
|
||||||
@ -23,353 +24,8 @@ const LoadingComponent = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const NoResultComponent = () => {
|
const PostActions = {
|
||||||
return (
|
onClickLike: async (data) => {
|
||||||
<antd.Empty
|
|
||||||
description="No more post here"
|
|
||||||
style={{
|
|
||||||
width: "100%",
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const typeToComponent = {
|
|
||||||
post: (args) => <PostCard {...args} />,
|
|
||||||
//"playlist": (args) => <PlaylistTimelineEntry {...args} />,
|
|
||||||
}
|
|
||||||
|
|
||||||
const Entry = React.memo((props) => {
|
|
||||||
const { data } = props
|
|
||||||
|
|
||||||
return React.createElement(
|
|
||||||
typeToComponent[data.type ?? "post"] ?? PostCard,
|
|
||||||
{
|
|
||||||
key: data._id,
|
|
||||||
data: data,
|
|
||||||
disableReplyTag: props.disableReplyTag,
|
|
||||||
disableHasReplies: props.disableHasReplies,
|
|
||||||
events: {
|
|
||||||
onClickLike: props.onLikePost,
|
|
||||||
onClickSave: props.onSavePost,
|
|
||||||
onClickDelete: props.onDeletePost,
|
|
||||||
onClickEdit: props.onEditPost,
|
|
||||||
onClickReply: props.onReplyPost,
|
|
||||||
onDoubleClick: props.onDoubleClick,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
const PostList = React.forwardRef((props, ref) => {
|
|
||||||
return (
|
|
||||||
<LoadMore
|
|
||||||
ref={ref}
|
|
||||||
className="post-list"
|
|
||||||
loadingComponent={LoadingComponent}
|
|
||||||
noResultComponent={NoResultComponent}
|
|
||||||
hasMore={props.hasMore}
|
|
||||||
fetching={props.loading}
|
|
||||||
onBottom={props.onLoadMore}
|
|
||||||
>
|
|
||||||
{!props.realtimeUpdates && !app.isMobile && (
|
|
||||||
<div className="resume_btn_wrapper">
|
|
||||||
<antd.Button
|
|
||||||
type="primary"
|
|
||||||
shape="round"
|
|
||||||
onClick={props.onResumeRealtimeUpdates}
|
|
||||||
loading={props.resumingLoading}
|
|
||||||
icon={<Icons.FiSyncOutlined />}
|
|
||||||
>
|
|
||||||
Resume
|
|
||||||
</antd.Button>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<AnimatePresence>
|
|
||||||
{props.list.map((data) => {
|
|
||||||
return <Entry key={data._id} data={data} {...props} />
|
|
||||||
})}
|
|
||||||
</AnimatePresence>
|
|
||||||
</LoadMore>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
export class PostsListsComponent extends React.Component {
|
|
||||||
state = {
|
|
||||||
openPost: null,
|
|
||||||
|
|
||||||
loading: false,
|
|
||||||
resumingLoading: false,
|
|
||||||
initialLoading: true,
|
|
||||||
scrollingToTop: false,
|
|
||||||
|
|
||||||
topVisible: true,
|
|
||||||
|
|
||||||
realtimeUpdates: true,
|
|
||||||
|
|
||||||
hasMore: true,
|
|
||||||
list: this.props.list ?? [],
|
|
||||||
pageCount: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
parentRef = this.props.innerRef
|
|
||||||
listRef = React.createRef()
|
|
||||||
|
|
||||||
timelineWsEvents = {
|
|
||||||
"post:new": (data) => {
|
|
||||||
console.log("[WS] Recived a post >", data)
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
list: [data, ...this.state.list],
|
|
||||||
})
|
|
||||||
},
|
|
||||||
"post:delete": (id) => {
|
|
||||||
console.log("[WS] Received a post delete >", id)
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
list: this.state.list.filter((post) => {
|
|
||||||
return post._id !== id
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
"post:update": (data) => {
|
|
||||||
console.log("[WS] Received a post update >", data)
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
list: this.state.list.map((post) => {
|
|
||||||
if (post._id === data._id) {
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
return post
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
handleLoad = async (fn, params = {}) => {
|
|
||||||
if (this.state.loading === true) {
|
|
||||||
console.warn(`Please wait to load the post before load more`)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
loading: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
let payload = {
|
|
||||||
page: this.state.pageCount,
|
|
||||||
limit: app.cores.settings.get("feed_max_fetch"),
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.replace) {
|
|
||||||
this.setState({
|
|
||||||
list: result,
|
|
||||||
pageCount: 0,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.setState({
|
|
||||||
list: [...this.state.list, ...result],
|
|
||||||
pageCount: this.state.pageCount + 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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: () => {
|
|
||||||
const randomId = Math.random().toString(36).substring(7)
|
|
||||||
|
|
||||||
this.addPost({
|
|
||||||
_id: randomId,
|
|
||||||
message: `Random post ${randomId}`,
|
|
||||||
user: {
|
|
||||||
_id: randomId,
|
|
||||||
username: "random user",
|
|
||||||
avatar: `https://api.dicebear.com/7.x/thumbs/svg?seed=${randomId}`,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
},
|
|
||||||
listRef: this.listRef,
|
|
||||||
}
|
|
||||||
|
|
||||||
onResumeRealtimeUpdates = async () => {
|
|
||||||
console.log("Resuming realtime updates")
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
resumingLoading: true,
|
|
||||||
scrollingToTop: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.listRef.current.scrollTo({
|
|
||||||
top: 0,
|
|
||||||
behavior: "smooth",
|
|
||||||
})
|
|
||||||
|
|
||||||
// reload posts
|
|
||||||
await this.handleLoad(this.props.loadFromModel, {
|
|
||||||
replace: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
realtimeUpdates: true,
|
|
||||||
resumingLoading: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onScrollList = (e) => {
|
|
||||||
const { scrollTop } = e.target
|
|
||||||
|
|
||||||
if (this.state.scrollingToTop && scrollTop === 0) {
|
|
||||||
this.setState({
|
|
||||||
scrollingToTop: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scrollTop > 200) {
|
|
||||||
if (this.state.topVisible) {
|
|
||||||
this.setState({
|
|
||||||
topVisible: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (typeof this.props.onTopVisibility === "function") {
|
|
||||||
this.props.onTopVisibility(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
!this.props.realtime ||
|
|
||||||
this.state.resumingLoading ||
|
|
||||||
this.state.scrollingToTop
|
|
||||||
) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
realtimeUpdates: false,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
if (!this.state.topVisible) {
|
|
||||||
this.setState({
|
|
||||||
topVisible: true,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (typeof this.props.onTopVisibility === "function") {
|
|
||||||
this.props.onTopVisibility(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if (this.props.realtime || !this.state.realtimeUpdates && !this.state.resumingLoading && scrollTop < 5) {
|
|
||||||
// this.onResumeRealtimeUpdates()
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount = async () => {
|
|
||||||
if (typeof this.props.loadFromModel === "function") {
|
|
||||||
await this.handleLoad(this.props.loadFromModel)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
initialLoading: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
if (this.props.realtime) {
|
|
||||||
for (const [event, handler] of Object.entries(
|
|
||||||
this.timelineWsEvents,
|
|
||||||
)) {
|
|
||||||
app.cores.api.listenEvent(event, handler, "posts")
|
|
||||||
}
|
|
||||||
|
|
||||||
app.cores.api.joinTopic(
|
|
||||||
"posts",
|
|
||||||
this.props.customTopic ?? "realtime:feed",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.listRef && this.listRef.current) {
|
|
||||||
this.listRef.current.addEventListener("scroll", this.onScrollList)
|
|
||||||
}
|
|
||||||
|
|
||||||
window._hacks = this._hacks
|
|
||||||
}
|
|
||||||
|
|
||||||
componentWillUnmount = async () => {
|
|
||||||
if (this.props.realtime) {
|
|
||||||
for (const [event, handler] of Object.entries(
|
|
||||||
this.timelineWsEvents,
|
|
||||||
)) {
|
|
||||||
app.cores.api.unlistenEvent(event, handler, "posts")
|
|
||||||
}
|
|
||||||
|
|
||||||
app.cores.api.leaveTopic(
|
|
||||||
"posts",
|
|
||||||
this.props.customTopic ?? "realtime:feed",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.listRef && this.listRef.current) {
|
|
||||||
this.listRef.current.removeEventListener(
|
|
||||||
"scroll",
|
|
||||||
this.onScrollList,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
window._hacks = null
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate = async (prevProps, prevState) => {
|
|
||||||
if (prevProps.list !== this.props.list) {
|
|
||||||
this.setState({
|
|
||||||
list: this.props.list,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onLikePost = async (data) => {
|
|
||||||
let result = await PostModel.toggleLike({ post_id: data._id }).catch(
|
let result = await PostModel.toggleLike({ post_id: data._id }).catch(
|
||||||
() => {
|
() => {
|
||||||
antd.message.error("Failed to like post")
|
antd.message.error("Failed to like post")
|
||||||
@ -379,9 +35,8 @@ export class PostsListsComponent extends React.Component {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
},
|
||||||
|
onClickSave: async (data) => {
|
||||||
onSavePost = async (data) => {
|
|
||||||
let result = await PostModel.toggleSave({ post_id: data._id }).catch(
|
let result = await PostModel.toggleSave({ post_id: data._id }).catch(
|
||||||
() => {
|
() => {
|
||||||
antd.message.error("Failed to save post")
|
antd.message.error("Failed to save post")
|
||||||
@ -391,25 +46,8 @@ export class PostsListsComponent extends React.Component {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
},
|
||||||
|
onClickDelete: async (data) => {
|
||||||
onEditPost = (data) => {
|
|
||||||
app.controls.openPostCreator({
|
|
||||||
edit_post: data._id,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onReplyPost = (data) => {
|
|
||||||
app.controls.openPostCreator({
|
|
||||||
reply_to: data._id,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
onDoubleClickPost = (data) => {
|
|
||||||
app.navigation.goToPost(data._id)
|
|
||||||
}
|
|
||||||
|
|
||||||
onDeletePost = async (data) => {
|
|
||||||
antd.Modal.confirm({
|
antd.Modal.confirm({
|
||||||
title: "Are you sure you want to delete this post?",
|
title: "Are you sure you want to delete this post?",
|
||||||
content: "This action is irreversible",
|
content: "This action is irreversible",
|
||||||
@ -422,74 +60,218 @@ export class PostsListsComponent extends React.Component {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
},
|
||||||
|
onClickEdit: async (data) => {
|
||||||
|
app.controls.openPostCreator({
|
||||||
|
edit_post: data._id,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onClickReply: async (data) => {
|
||||||
|
app.controls.openPostCreator({
|
||||||
|
reply_to: data._id,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onDoubleClick: async (data) => {
|
||||||
|
app.navigation.goToPost(data._id)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
ontoggleOpen = (to, data) => {
|
const Entry = (props) => {
|
||||||
if (typeof this.props.onOpenPost === "function") {
|
const { data } = props
|
||||||
this.props.onOpenPost(to, data)
|
|
||||||
|
return (
|
||||||
|
<PostCard
|
||||||
|
key={data._id}
|
||||||
|
data={data}
|
||||||
|
disableReplyTag={props.disableReplyTag}
|
||||||
|
disableHasReplies={props.disableHasReplies}
|
||||||
|
events={PostActions}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const PostList = React.forwardRef((props, ref) => {
|
||||||
|
return (
|
||||||
|
<LoadMore
|
||||||
|
ref={ref}
|
||||||
|
className="post-list"
|
||||||
|
loadingComponent={LoadingComponent}
|
||||||
|
hasMore={props.hasMore}
|
||||||
|
onBottom={props.onLoadMore}
|
||||||
|
>
|
||||||
|
<AnimatePresence>
|
||||||
|
{props.list.map((data) => {
|
||||||
|
return <Entry key={data._id} data={data} {...props} />
|
||||||
|
})}
|
||||||
|
</AnimatePresence>
|
||||||
|
</LoadMore>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const PostsListsComponent = (props) => {
|
||||||
|
const [list, setList] = React.useState([])
|
||||||
|
const [hasMore, setHasMore] = React.useState(true)
|
||||||
|
|
||||||
|
// Refs
|
||||||
|
const firstLoad = React.useRef(true)
|
||||||
|
const loading = React.useRef(false)
|
||||||
|
const page = React.useRef(0)
|
||||||
|
const listRef = React.useRef(null)
|
||||||
|
const loadModelPropsRef = React.useRef({})
|
||||||
|
|
||||||
|
const timelineWsEvents = React.useRef({
|
||||||
|
"post:new": (data) => {
|
||||||
|
console.debug("post:new", data)
|
||||||
|
|
||||||
|
setList((prev) => {
|
||||||
|
return [data, ...prev]
|
||||||
|
})
|
||||||
|
},
|
||||||
|
"post:delete": (data) => {
|
||||||
|
console.debug("post:delete", data)
|
||||||
|
|
||||||
|
setList((prev) => {
|
||||||
|
return prev.filter((item) => {
|
||||||
|
return item._id !== data._id
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
"post:update": (data) => {
|
||||||
|
console.debug("post:update", data)
|
||||||
|
|
||||||
|
setList((prev) => {
|
||||||
|
return prev.map((item) => {
|
||||||
|
if (item._id === data._id) {
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
return item
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
// Logic
|
||||||
|
async function handleLoad(fn, params = {}) {
|
||||||
|
if (loading.current === true) {
|
||||||
|
console.warn(`Please wait to load the post before load more`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.current = true
|
||||||
|
|
||||||
|
let payload = {
|
||||||
|
page: page.current,
|
||||||
|
limit: app.cores.settings.get("feed_max_fetch"),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadModelPropsRef.current) {
|
||||||
|
payload = {
|
||||||
|
...payload,
|
||||||
|
...loadModelPropsRef.current,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await fn(payload).catch((err) => {
|
||||||
|
console.error(err)
|
||||||
|
app.message.error("Failed to load more posts")
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
loading.current = false
|
||||||
|
firstLoad.current = false
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
setHasMore(result.has_more)
|
||||||
|
|
||||||
|
if (result.items?.length > 0) {
|
||||||
|
if (params.replace) {
|
||||||
|
setList(result.items)
|
||||||
|
page.current = 0
|
||||||
|
} else {
|
||||||
|
setList((prev) => {
|
||||||
|
return [...prev, ...result.items]
|
||||||
|
})
|
||||||
|
page.current = page.current + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onLoadMore = async () => {
|
const onLoadMore = React.useCallback(() => {
|
||||||
if (typeof this.props.onLoadMore === "function") {
|
if (typeof props.onLoadMore === "function") {
|
||||||
return this.handleLoad(this.props.onLoadMore)
|
return handleLoad(props.onLoadMore)
|
||||||
} else if (this.props.loadFromModel) {
|
} else if (props.loadFromModel) {
|
||||||
return this.handleLoad(this.props.loadFromModel)
|
return handleLoad(props.loadFromModel)
|
||||||
}
|
}
|
||||||
}
|
}, [props])
|
||||||
|
|
||||||
render() {
|
React.useEffect(() => {
|
||||||
if (this.state.initialLoading) {
|
if (
|
||||||
return <antd.Skeleton active />
|
!lodash.isEqual(props.loadFromModelProps, loadModelPropsRef.current)
|
||||||
|
) {
|
||||||
|
loadModelPropsRef.current = props.loadFromModelProps
|
||||||
|
|
||||||
|
page.current = 0
|
||||||
|
loading.current = false
|
||||||
|
|
||||||
|
setHasMore(true)
|
||||||
|
setList([])
|
||||||
|
handleLoad(props.loadFromModel)
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
props.loadFromModel,
|
||||||
|
props.loadFromModelProps,
|
||||||
|
firstLoad.current === false,
|
||||||
|
])
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (props.loadFromModelProps) {
|
||||||
|
loadModelPropsRef.current = props.loadFromModelProps
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.state.list.length === 0) {
|
if (typeof props.loadFromModel === "function") {
|
||||||
if (typeof this.props.emptyListRender === "function") {
|
handleLoad(props.loadFromModel)
|
||||||
return React.createElement(this.props.emptyListRender)
|
}
|
||||||
|
|
||||||
|
if (props.realtime) {
|
||||||
|
for (const [event, handler] of Object.entries(
|
||||||
|
timelineWsEvents.current,
|
||||||
|
)) {
|
||||||
|
app.cores.api.listenEvent(event, handler, "posts")
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
app.cores.api.joinTopic(
|
||||||
<div className="no_more_posts">
|
"posts",
|
||||||
<antd.Empty />
|
props.customTopic ?? "realtime:feed",
|
||||||
<h1>Whoa, nothing on here...</h1>
|
|
||||||
</div>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const PostListProps = {
|
return () => {
|
||||||
list: this.state.list,
|
if (props.realtime) {
|
||||||
|
for (const [event, handler] of Object.entries(
|
||||||
|
timelineWsEvents.current,
|
||||||
|
)) {
|
||||||
|
app.cores.api.unlistenEvent(event, handler, "posts")
|
||||||
|
}
|
||||||
|
|
||||||
disableReplyTag: this.props.disableReplyTag,
|
app.cores.api.leaveTopic(
|
||||||
disableHasReplies: this.props.disableHasReplies,
|
"posts",
|
||||||
|
props.customTopic ?? "realtime:feed",
|
||||||
onLikePost: this.onLikePost,
|
)
|
||||||
onSavePost: this.onSavePost,
|
}
|
||||||
onDeletePost: this.onDeletePost,
|
|
||||||
onEditPost: this.onEditPost,
|
|
||||||
onReplyPost: this.onReplyPost,
|
|
||||||
onDoubleClick: this.onDoubleClickPost,
|
|
||||||
|
|
||||||
onLoadMore: this.onLoadMore,
|
|
||||||
hasMore: this.state.hasMore,
|
|
||||||
loading: this.state.loading,
|
|
||||||
|
|
||||||
realtimeUpdates: this.state.realtimeUpdates,
|
|
||||||
resumingLoading: this.state.resumingLoading,
|
|
||||||
onResumeRealtimeUpdates: this.onResumeRealtimeUpdates,
|
|
||||||
}
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
if (app.isMobile) {
|
return (
|
||||||
return <PostList ref={this.listRef} {...PostListProps} />
|
<div className="post-list_wrapper">
|
||||||
}
|
<PostList
|
||||||
|
ref={listRef}
|
||||||
return (
|
list={list}
|
||||||
<div className="post-list_wrapper">
|
hasMore={hasMore}
|
||||||
<PostList ref={this.listRef} {...PostListProps} />
|
onLoadMore={onLoadMore}
|
||||||
</div>
|
/>
|
||||||
)
|
</div>
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default React.forwardRef((props, ref) => (
|
export default PostsListsComponent
|
||||||
<PostsListsComponent innerRef={ref} {...props} />
|
|
||||||
))
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import { Result } from "antd"
|
import { Result } from "antd"
|
||||||
|
|
||||||
|
import { useNavigation } from "react-router"
|
||||||
|
|
||||||
import PostsList from "@components/PostsList"
|
import PostsList from "@components/PostsList"
|
||||||
import { Icons } from "@components/Icons"
|
import { Icons } from "@components/Icons"
|
||||||
|
|
||||||
@ -14,18 +16,17 @@ const emptyListRender = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class UserPosts extends React.Component {
|
const UserPosts = (props) => {
|
||||||
render() {
|
return (
|
||||||
console.log(this.props.state)
|
<PostsList
|
||||||
return (
|
onTopVisibility={props.onTopVisibility}
|
||||||
<PostsList
|
emptyListRender={emptyListRender}
|
||||||
onTopVisibility={this.props.onTopVisibility}
|
loadFromModel={PostModel.getUserPosts}
|
||||||
emptyListRender={emptyListRender}
|
loadFromModelProps={{
|
||||||
loadFromModel={PostModel.getUserPosts}
|
user_id: props.state.user._id,
|
||||||
loadFromModelProps={{
|
}}
|
||||||
user_id: this.props.state.user._id,
|
/>
|
||||||
}}
|
)
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default UserPosts
|
||||||
|
@ -19,6 +19,7 @@ export default async (payload = {}) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let posts = []
|
let posts = []
|
||||||
|
let total_posts = 0
|
||||||
|
|
||||||
if (post_id) {
|
if (post_id) {
|
||||||
try {
|
try {
|
||||||
@ -33,6 +34,8 @@ export default async (payload = {}) => {
|
|||||||
.sort(sort)
|
.sort(sort)
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
.skip(limit * page)
|
.skip(limit * page)
|
||||||
|
|
||||||
|
total_posts = await Post.countDocuments({ ...query })
|
||||||
}
|
}
|
||||||
|
|
||||||
// fullfill data
|
// fullfill data
|
||||||
@ -50,5 +53,9 @@ export default async (payload = {}) => {
|
|||||||
return posts[0]
|
return posts[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
return posts
|
return {
|
||||||
|
items: posts,
|
||||||
|
total_items: total_posts,
|
||||||
|
has_more: total_posts > limit * page + 1,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,11 +42,9 @@ export default async (payload = {}) => {
|
|||||||
|
|
||||||
// broadcast post to all users
|
// broadcast post to all users
|
||||||
if (post.visibility === "public") {
|
if (post.visibility === "public") {
|
||||||
global.websockets.senders.toTopic(
|
global.websockets.senders.toTopic("realtime:feed", "post:delete", {
|
||||||
"realtime:feed",
|
_id: post_id,
|
||||||
"post:delete",
|
})
|
||||||
post_id,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (post.visibility === "private") {
|
if (post.visibility === "private") {
|
||||||
@ -55,7 +53,9 @@ export default async (payload = {}) => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
for (const userSocket of userSockets) {
|
for (const userSocket of userSockets) {
|
||||||
userSocket.emit(`post:delete`, post_id)
|
userSocket.emit(`post:delete`, {
|
||||||
|
_id: post_id,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user