2023-03-14 19:51:51 +00:00

232 lines
6.9 KiB
JavaScript
Executable File

import React from "react"
import * as antd from "antd"
import classnames from "classnames"
import Plyr from "plyr-react"
import { CommentsCard } from "components"
import { Icons } from "components/Icons"
import { processString } from "utils"
import PostHeader from "./components/header"
import PostActions from "./components/actions"
import PostAttachments from "./components/attachments"
import "./index.less"
const messageRegexs = [
{
regex: /https?:\/\/(?:www\.)?youtube\.com\/watch\?v=([a-zA-Z0-9_-]{11})(&[a-zA-Z0-9_-]+=[a-zA-Z0-9_-]+)*/g,
fn: (key, result) => {
return <Plyr source={{
type: "video",
sources: [{
src: result[1],
provider: "youtube",
}],
}} />
}
},
{
regex: /(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|www\.[a-zA-Z0-9][a-zA-Z0-9-]+[a-zA-Z0-9]\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9]+\.[^\s]{2,}|www\.[a-zA-Z0-9]+\.[^\s]{2,})/gi,
fn: (key, result) => {
return <a key={key} href={result[1]} target="_blank" rel="noopener noreferrer">{result[1]}</a>
}
},
{
regex: /(@[a-zA-Z0-9_]+)/gi,
fn: (key, result) => {
return <a key={key} onClick={() => window.app.setLocation(`/@${result[1].substr(1)}`)}>{result[1]}</a>
}
},
]
export default class PostCard extends React.PureComponent {
state = {
countLikes: this.props.data.countLikes ?? 0,
countComments: this.props.data.countComments ?? 0,
hasLiked: this.props.data.isLiked ?? false,
hasSaved: this.props.data.isSaved ?? false,
open: this.props.defaultOpened ?? false,
isNsfw: this.props.data.flags?.includes("nsfw") ?? false,
nsfwAccepted: false,
}
onClickDelete = async () => {
if (typeof this.props.events.onClickDelete !== "function") {
console.warn("onClickDelete event is not a function")
return
}
return await this.props.events.onClickDelete(this.props.data)
}
onClickLike = async () => {
if (typeof this.props.events.onClickLike !== "function") {
console.warn("onClickLike event is not a function")
return
}
return await this.props.events.onClickLike(this.props.data)
}
onClickSave = async () => {
if (typeof this.props.events.onClickSave !== "function") {
console.warn("onClickSave event is not a function")
return
}
return await this.props.events.onClickSave(this.props.data)
}
onClickEdit = async () => {
if (typeof this.props.events.onClickEdit !== "function") {
console.warn("onClickEdit event is not a function")
return
}
return await this.props.events.onClickEdit(this.props.data)
}
onDoubleClick = async () => {
this.handleOpen()
}
onClickComments = async () => {
this.handleOpen()
}
handleOpen = (to) => {
if (typeof to === "undefined") {
to = !this.state.open
}
if (typeof this.props.events?.onToogleOpen === "function") {
this.props.events?.onToogleOpen(to, this.props.data)
}
this.setState({
open: to,
})
//app.controls.openPostViewer(this.props.data)
}
onLikesUpdate = (data) => {
console.log("onLikesUpdate", data)
if (data.to) {
this.setState({
countLikes: this.state.countLikes + 1,
})
} else {
this.setState({
countLikes: this.state.countLikes - 1,
})
}
}
componentDidMount = async () => {
// first listen to post changes
app.cores.api.listenEvent(`post.${this.props.data._id}.likes.update`, this.onLikesUpdate)
}
componentWillUnmount = () => {
// remove the listener
app.cores.api.unlistenEvent(`post.${this.props.data._id}.likes.update`, this.onLikesUpdate)
}
componentDidCatch = (error, info) => {
console.error(error)
return <div className="postCard error">
<h1>
<Icons.AlertTriangle />
<span>Cannot render this post</span>
<span>
Maybe this version of the app is outdated or is not supported yet
</span>
</h1>
</div>
}
render() {
return <div
key={this.props.index}
id={this.props.data._id}
className={classnames(
"postCard",
{
["open"]: this.state.open,
}
)}
style={this.props.style}
context-menu={"postCard-context"}
user-id={this.props.data.user_id}
>
<PostHeader
postData={this.props.data}
onDoubleClick={this.onDoubleClick}
/>
<div
id="post_content"
className={classnames(
"post_content",
{
["nsfw"]: this.state.isNsfw && !this.state.nsfwAccepted,
}
)}
>
{
this.state.isNsfw && !this.state.nsfwAccepted &&
<div className="nsfw_alert">
<h2>
This post may contain sensitive content.
</h2>
<antd.Button onClick={() => this.setState({ nsfwAccepted: true })}>
Show anyways
</antd.Button>
</div>
}
<div className="message">
{
processString(messageRegexs)(this.props.data.message ?? "")
}
</div>
{
this.props.data.attachments && this.props.data.attachments.length > 0 && <PostAttachments
attachments={this.props.data.attachments}
/>
}
</div>
<PostActions
user_id={this.props.data.user_id}
likesCount={this.state.countLikes}
commentsCount={this.state.countComments}
defaultLiked={this.state.hasLiked}
defaultSaved={this.state.hasSaved}
actions={{
onClickLike: this.onClickLike,
onClickEdit: this.onClickEdit,
onClickDelete: this.onClickDelete,
onClickSave: this.onClickSave,
onClickComments: this.onClickComments,
}}
/>
<CommentsCard
post_id={this.props.data._id}
visible={this.state.open}
/>
</div>
}
}