mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-11 03:24:16 +00:00
improve component layout
This commit is contained in:
parent
38e88b48a7
commit
63ec7daa7a
@ -1,5 +1,6 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
|
import classnames from "classnames"
|
||||||
import moment from "moment"
|
import moment from "moment"
|
||||||
|
|
||||||
import { Icons } from "components/Icons"
|
import { Icons } from "components/Icons"
|
||||||
@ -48,11 +49,10 @@ const CommentCard = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default (props) => {
|
export default (props) => {
|
||||||
|
const [visible, setVisible] = React.useState(props.visible ?? true)
|
||||||
const [comments, setComments] = React.useState(null)
|
const [comments, setComments] = React.useState(null)
|
||||||
|
|
||||||
const fetchData = async () => {
|
const fetchData = async () => {
|
||||||
setComments(null)
|
|
||||||
|
|
||||||
// fetch comments
|
// fetch comments
|
||||||
const commentsResult = await PostModel.getPostComments({
|
const commentsResult = await PostModel.getPostComments({
|
||||||
post_id: props.post_id
|
post_id: props.post_id
|
||||||
@ -123,9 +123,19 @@ export default (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
fetchData()
|
setVisible(props.visible)
|
||||||
listenEvents()
|
}, [props.visible])
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (visible) {
|
||||||
|
fetchData()
|
||||||
|
listenEvents()
|
||||||
|
} else {
|
||||||
|
unlistenEvents()
|
||||||
|
}
|
||||||
|
}, [visible])
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
unlistenEvents()
|
unlistenEvents()
|
||||||
}
|
}
|
||||||
@ -149,17 +159,23 @@ export default (props) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!comments) {
|
return <div
|
||||||
return <antd.Skeleton active />
|
id="comments"
|
||||||
}
|
className={classnames(
|
||||||
|
"comments",
|
||||||
return <div className="comments">
|
{
|
||||||
|
"hidden": !visible
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
>
|
||||||
<div className="header">
|
<div className="header">
|
||||||
<h3>
|
<h3>
|
||||||
<Icons.MessageSquare /> Comments
|
<Icons.MessageSquare /> Comments
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{renderComments()}
|
{renderComments()}
|
||||||
|
|
||||||
<div className="commentCreatorWrapper">
|
<div className="commentCreatorWrapper">
|
||||||
<CommentCreator
|
<CommentCreator
|
||||||
onSubmit={handleCommentSubmit}
|
onSubmit={handleCommentSubmit}
|
||||||
|
@ -16,6 +16,18 @@
|
|||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.hidden {
|
||||||
|
height: 0px;
|
||||||
|
padding: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
transition: all 150ms ease-in-out;
|
||||||
|
|
||||||
.comment {
|
.comment {
|
||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -11,7 +11,7 @@ export default (props) => {
|
|||||||
<Button
|
<Button
|
||||||
type="ghost"
|
type="ghost"
|
||||||
shape="circle"
|
shape="circle"
|
||||||
onClick={props.onClickComments}
|
onClick={props.onClick}
|
||||||
icon={<Icons.MessageCircle />}
|
icon={<Icons.MessageCircle />}
|
||||||
/>
|
/>
|
||||||
{
|
{
|
||||||
|
@ -8,16 +8,36 @@ import CommentsButton from "./commentsButton"
|
|||||||
|
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
|
const SelfActionsItems = [
|
||||||
|
{
|
||||||
|
key: "onClickEdit",
|
||||||
|
label: <>
|
||||||
|
<Icons.Edit />
|
||||||
|
<span>Edit</span>
|
||||||
|
</>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "onClickDelete",
|
||||||
|
label: <>
|
||||||
|
<Icons.Trash />
|
||||||
|
<span>Delete</span>
|
||||||
|
</>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "divider",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
const MoreActionsItems = [
|
const MoreActionsItems = [
|
||||||
{
|
{
|
||||||
key: "repost",
|
key: "onClickRepost",
|
||||||
label: <>
|
label: <>
|
||||||
<Icons.Repeat />
|
<Icons.Repeat />
|
||||||
<span>Repost</span>
|
<span>Repost</span>
|
||||||
</>,
|
</>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "share",
|
key: "onClickShare",
|
||||||
label: <>
|
label: <>
|
||||||
<Icons.Share />
|
<Icons.Share />
|
||||||
<span>Share</span>
|
<span>Share</span>
|
||||||
@ -27,7 +47,7 @@ const MoreActionsItems = [
|
|||||||
type: "divider",
|
type: "divider",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "report",
|
key: "onClickReport",
|
||||||
label: <>
|
label: <>
|
||||||
<Icons.AlertTriangle />
|
<Icons.AlertTriangle />
|
||||||
<span>Report</span>
|
<span>Report</span>
|
||||||
@ -36,33 +56,68 @@ const MoreActionsItems = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export default (props) => {
|
export default (props) => {
|
||||||
|
const [isSelf, setIsSelf] = React.useState(false)
|
||||||
|
|
||||||
|
const {
|
||||||
|
onClickLike,
|
||||||
|
onClickSave,
|
||||||
|
onClickComments,
|
||||||
|
} = props.actions ?? {}
|
||||||
|
|
||||||
|
const genItems = () => {
|
||||||
|
let items = MoreActionsItems
|
||||||
|
|
||||||
|
if (isSelf) {
|
||||||
|
items = [...SelfActionsItems, ...items]
|
||||||
|
}
|
||||||
|
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDropdownClickItem = (e) => {
|
||||||
|
if (typeof props.actions[e.key] === "function") {
|
||||||
|
props.actions[e.key]()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return <div className="post_actions_wrapper">
|
return <div className="post_actions_wrapper">
|
||||||
<div className="actions">
|
<div className="actions">
|
||||||
<div className="action" id="likes">
|
<div className="action" id="likes">
|
||||||
<LikeButton
|
<LikeButton
|
||||||
defaultLiked={props.defaultLiked}
|
defaultLiked={props.defaultLiked}
|
||||||
onClick={props.onClickLike}
|
|
||||||
count={props.likesCount}
|
count={props.likesCount}
|
||||||
|
onClick={onClickLike}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="action" id="save">
|
<div className="action" id="save">
|
||||||
<SaveButton
|
<SaveButton
|
||||||
defaultActive={props.defaultSaved}
|
defaultActive={props.defaultSaved}
|
||||||
onClick={props.onClickSave}
|
onClick={onClickSave}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="action" id="comments">
|
<div className="action" id="comments">
|
||||||
<CommentsButton
|
<CommentsButton
|
||||||
onClickComments={props.onClickComments}
|
|
||||||
count={props.commentsCount}
|
count={props.commentsCount}
|
||||||
|
onClick={onClickComments}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="action" id="more">
|
<div className="action" id="more">
|
||||||
<Dropdown
|
<Dropdown
|
||||||
menu={{
|
menu={{
|
||||||
items: MoreActionsItems
|
items: genItems(),
|
||||||
|
onClick: handleDropdownClickItem,
|
||||||
}}
|
}}
|
||||||
trigger={["click"]}
|
trigger={["click"]}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
if (open && props.user_id) {
|
||||||
|
const isSelf = app.cores.permissions.checkUserIdIsSelf(props.user_id)
|
||||||
|
|
||||||
|
setIsSelf(isSelf)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
overlayStyle={{
|
||||||
|
minWidth: "200px",
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<div className="icon">
|
<div className="icon">
|
||||||
<Icons.MoreHorizontal />
|
<Icons.MoreHorizontal />
|
||||||
|
@ -124,7 +124,7 @@ const Attachment = React.memo((props) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export default (props) => {
|
export default React.memo((props) => {
|
||||||
const carouselRef = React.useRef(null)
|
const carouselRef = React.useRef(null)
|
||||||
const [attachmentIndex, setAttachmentIndex] = React.useState(0)
|
const [attachmentIndex, setAttachmentIndex] = React.useState(0)
|
||||||
|
|
||||||
@ -174,9 +174,11 @@ export default (props) => {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Attachment index={index} attachment={attachment} />
|
return <React.Fragment key={index}>
|
||||||
|
<Attachment index={index} attachment={attachment} />
|
||||||
|
</React.Fragment>
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
</Carousel>
|
</Carousel>
|
||||||
</div>
|
</div>
|
||||||
}
|
})
|
@ -1,75 +0,0 @@
|
|||||||
import React from "react"
|
|
||||||
import * as antd from "antd"
|
|
||||||
import Plyr from "plyr-react"
|
|
||||||
import classnames from "classnames"
|
|
||||||
|
|
||||||
import { processString } from "utils"
|
|
||||||
|
|
||||||
import "./index.less"
|
|
||||||
|
|
||||||
export default (props) => {
|
|
||||||
let { message } = props.data
|
|
||||||
|
|
||||||
const [nsfwAccepted, setNsfwAccepted] = React.useState(false)
|
|
||||||
|
|
||||||
// parse message
|
|
||||||
const regexs = [
|
|
||||||
{
|
|
||||||
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>
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
message = processString(regexs)(message)
|
|
||||||
|
|
||||||
return <div
|
|
||||||
className={
|
|
||||||
classnames(
|
|
||||||
"post_content",
|
|
||||||
{
|
|
||||||
["nsfw"]: props.nsfw && !nsfwAccepted
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{props.nsfw && !nsfwAccepted &&
|
|
||||||
<div className="nsfw_alert">
|
|
||||||
<h2>
|
|
||||||
This post may contain sensitive content.
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<antd.Button onClick={() => setNsfwAccepted(true)}>
|
|
||||||
Show anyways
|
|
||||||
</antd.Button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div className="message">
|
|
||||||
{message}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{
|
|
||||||
props.children
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
}
|
|
@ -1,81 +0,0 @@
|
|||||||
.post_content {
|
|
||||||
position: relative;
|
|
||||||
display: inline-flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
|
|
||||||
padding: 0 10px 10px 10px;
|
|
||||||
border-radius: 8px;
|
|
||||||
|
|
||||||
color: rgba(var(--background-color-contrast));
|
|
||||||
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
transition: all 0.2s ease-in-out;
|
|
||||||
|
|
||||||
z-index: 190;
|
|
||||||
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6,
|
|
||||||
p,
|
|
||||||
span {
|
|
||||||
color: var(--text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
&.nsfw {
|
|
||||||
.message {
|
|
||||||
filter: blur(10px);
|
|
||||||
}
|
|
||||||
|
|
||||||
.post_attachments {
|
|
||||||
filter: blur(25px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.nsfw_alert {
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
padding: 10px 0;
|
|
||||||
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
|
|
||||||
// -webkit-backdrop-filter: blur(25px);
|
|
||||||
// backdrop-filter: blur(25px);
|
|
||||||
//background-color: rgba(0, 0, 0, 0.5);
|
|
||||||
|
|
||||||
border-radius: 8px;
|
|
||||||
|
|
||||||
z-index: 200;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
color: #fff;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: 400;
|
|
||||||
|
|
||||||
color: var(--background-color-contrast);
|
|
||||||
|
|
||||||
word-break: break-all;
|
|
||||||
user-select: text;
|
|
||||||
}
|
|
||||||
|
|
||||||
>div {
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +1,48 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
import classnames from "classnames"
|
import classnames from "classnames"
|
||||||
|
import Plyr from "plyr-react"
|
||||||
|
|
||||||
import { CommentsCard } from "components"
|
import { CommentsCard } from "components"
|
||||||
import { Icons } from "components/Icons"
|
import { Icons } from "components/Icons"
|
||||||
|
|
||||||
|
import { processString } from "utils"
|
||||||
|
|
||||||
import PostHeader from "./components/header"
|
import PostHeader from "./components/header"
|
||||||
import PostContent from "./components/content"
|
|
||||||
import PostActions from "./components/actions"
|
import PostActions from "./components/actions"
|
||||||
import PostAttachments from "./components/attachments"
|
import PostAttachments from "./components/attachments"
|
||||||
|
|
||||||
import "./index.less"
|
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 {
|
export default class PostCard extends React.PureComponent {
|
||||||
state = {
|
state = {
|
||||||
loading: true,
|
|
||||||
data: this.props.data ?? {},
|
|
||||||
|
|
||||||
countLikes: this.props.data.countLikes ?? 0,
|
countLikes: this.props.data.countLikes ?? 0,
|
||||||
countComments: this.props.data.countComments ?? 0,
|
countComments: this.props.data.countComments ?? 0,
|
||||||
|
|
||||||
@ -24,6 +50,9 @@ export default class PostCard extends React.PureComponent {
|
|||||||
hasSaved: this.props.data.isSaved ?? false,
|
hasSaved: this.props.data.isSaved ?? false,
|
||||||
|
|
||||||
open: this.props.defaultOpened ?? false,
|
open: this.props.defaultOpened ?? false,
|
||||||
|
|
||||||
|
isNsfw: this.props.data.flags?.includes("nsfw") ?? false,
|
||||||
|
nsfwAccepted: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickDelete = async () => {
|
onClickDelete = async () => {
|
||||||
@ -32,7 +61,7 @@ export default class PostCard extends React.PureComponent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.props.events.onClickDelete(this.state.data)
|
return await this.props.events.onClickDelete(this.props.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickLike = async () => {
|
onClickLike = async () => {
|
||||||
@ -41,7 +70,7 @@ export default class PostCard extends React.PureComponent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.props.events.onClickLike(this.state.data)
|
return await this.props.events.onClickLike(this.props.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickSave = async () => {
|
onClickSave = async () => {
|
||||||
@ -50,7 +79,7 @@ export default class PostCard extends React.PureComponent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.props.events.onClickSave(this.state.data)
|
return await this.props.events.onClickSave(this.props.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
onClickEdit = async () => {
|
onClickEdit = async () => {
|
||||||
@ -59,7 +88,7 @@ export default class PostCard extends React.PureComponent {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return await this.props.events.onClickEdit(this.state.data)
|
return await this.props.events.onClickEdit(this.props.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
onDoubleClick = async () => {
|
onDoubleClick = async () => {
|
||||||
@ -76,17 +105,19 @@ export default class PostCard extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (typeof this.props.events?.onToogleOpen === "function") {
|
if (typeof this.props.events?.onToogleOpen === "function") {
|
||||||
this.props.events?.onToogleOpen(to, this.state.data)
|
this.props.events?.onToogleOpen(to, this.props.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
open: to,
|
open: to,
|
||||||
})
|
})
|
||||||
|
|
||||||
//app.controls.openPostViewer(this.state.data)
|
//app.controls.openPostViewer(this.props.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
onLikesUpdate = (data) => {
|
onLikesUpdate = (data) => {
|
||||||
|
console.log("onLikesUpdate", data)
|
||||||
|
|
||||||
if (data.to) {
|
if (data.to) {
|
||||||
this.setState({
|
this.setState({
|
||||||
countLikes: this.state.countLikes + 1,
|
countLikes: this.state.countLikes + 1,
|
||||||
@ -99,24 +130,13 @@ export default class PostCard extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount = async () => {
|
componentDidMount = async () => {
|
||||||
app.eventBus.on(`post.${this.state.data._id}.delete`, this.onClickDelete)
|
|
||||||
app.eventBus.on(`post.${this.state.data._id}.update`, this.onClickEdit)
|
|
||||||
|
|
||||||
// first listen to post changes
|
// first listen to post changes
|
||||||
app.cores.api.listenEvent(`post.${this.state.data._id}.likes.update`, this.onLikesUpdate)
|
app.cores.api.listenEvent(`post.${this.props.data._id}.likes.update`, this.onLikesUpdate)
|
||||||
|
|
||||||
this.setState({
|
|
||||||
isSelf: app.cores.permissions.checkUserIdIsSelf(this.state.data.user_id),
|
|
||||||
loading: false,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount = () => {
|
componentWillUnmount = () => {
|
||||||
app.eventBus.off(`post.${this.state.data._id}.delete`, this.onClickDelete)
|
|
||||||
app.eventBus.off(`post.${this.state.data._id}.update`, this.onClickEdit)
|
|
||||||
|
|
||||||
// remove the listener
|
// remove the listener
|
||||||
app.cores.api.unlistenEvent(`post.${this.state.data._id}.likes.update`, this.onLikesUpdate)
|
app.cores.api.unlistenEvent(`post.${this.props.data._id}.likes.update`, this.onLikesUpdate)
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidCatch = (error, info) => {
|
componentDidCatch = (error, info) => {
|
||||||
@ -133,62 +153,81 @@ export default class PostCard extends React.PureComponent {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
render = () => {
|
render() {
|
||||||
if (this.state.loading) {
|
|
||||||
return <div
|
|
||||||
key={this.state.data.key ?? this.state.data._id}
|
|
||||||
id={this.state.data._id}
|
|
||||||
className="postCard"
|
|
||||||
>
|
|
||||||
<antd.Skeleton active avatar />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div
|
return <div
|
||||||
key={this.props.index ?? this.state.data._id}
|
key={this.props.index}
|
||||||
id={this.state.data._id}
|
id={this.props.data._id}
|
||||||
className={classnames(
|
className={classnames(
|
||||||
"postCard",
|
"postCard",
|
||||||
{
|
{
|
||||||
["open"]: this.state.open,
|
["open"]: this.state.open,
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
|
style={this.props.style}
|
||||||
context-menu={"postCard-context"}
|
context-menu={"postCard-context"}
|
||||||
user-id={this.state.data.user_id}
|
user-id={this.props.data.user_id}
|
||||||
self-post={this.state.isSelf.toString()}
|
|
||||||
>
|
>
|
||||||
<div className="wrapper">
|
<div className="wrapper">
|
||||||
<PostHeader
|
<PostHeader
|
||||||
postData={this.state.data}
|
postData={this.props.data}
|
||||||
isLiked={this.state.hasLiked}
|
|
||||||
onDoubleClick={this.onDoubleClick}
|
onDoubleClick={this.onDoubleClick}
|
||||||
/>
|
/>
|
||||||
<PostContent
|
<div
|
||||||
data={this.state.data}
|
id="post_content"
|
||||||
nsfw={this.state.data.flags && this.state.data.flags.includes("nsfw")}
|
className={classnames(
|
||||||
onDoubleClick={this.onDoubleClick}
|
"post_content",
|
||||||
|
{
|
||||||
|
["nsfw"]: this.state.isNsfw && !this.state.nsfwAccepted,
|
||||||
|
}
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
{this.state.data.attachments && this.state.data.attachments.length > 0 && <PostAttachments
|
{
|
||||||
attachments={this.state.data.attachments}
|
this.state.isNsfw && !this.state.nsfwAccepted &&
|
||||||
/>}
|
<div className="nsfw_alert">
|
||||||
</PostContent>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<PostActions
|
<PostActions
|
||||||
|
user_id={this.props.data.user_id}
|
||||||
likesCount={this.state.countLikes}
|
likesCount={this.state.countLikes}
|
||||||
commentsCount={this.state.countComments}
|
commentsCount={this.state.countComments}
|
||||||
defaultLiked={this.state.hasLiked}
|
defaultLiked={this.state.hasLiked}
|
||||||
defaultSaved={this.state.hasSaved}
|
defaultSaved={this.state.hasSaved}
|
||||||
onClickLike={this.onClickLike}
|
|
||||||
onClickSave={this.onClickSave}
|
|
||||||
onClickComments={this.onClickComments}
|
|
||||||
actions={{
|
actions={{
|
||||||
delete: this.onClickDelete,
|
onClickLike: this.onClickLike,
|
||||||
|
onClickEdit: this.onClickEdit,
|
||||||
|
onClickDelete: this.onClickDelete,
|
||||||
|
onClickSave: this.onClickSave,
|
||||||
|
onClickComments: this.onClickComments,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{
|
<CommentsCard
|
||||||
this.state.open && <CommentsCard post_id={this.state.data._id} />
|
post_id={this.props.data._id}
|
||||||
}
|
visible={this.state.open}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,19 +1,22 @@
|
|||||||
.postCard {
|
.postCard {
|
||||||
display: inline-flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
width: 100%;
|
width: 35vw;
|
||||||
|
min-width: 300px;
|
||||||
max-width: 600px;
|
max-width: 600px;
|
||||||
|
|
||||||
background-color: var(--background-color-accent);
|
background-color: var(--background-color-accent);
|
||||||
|
|
||||||
transition: all 0.2s ease-in-out;
|
transition: all 150ms ease-in-out;
|
||||||
|
|
||||||
padding: 17px;
|
padding: 17px;
|
||||||
|
|
||||||
&.open {
|
border-bottom: 2px solid var(--border-color);
|
||||||
height: 100%;
|
|
||||||
}
|
padding-bottom: 10px;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
@ -29,9 +32,87 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
border-bottom: 2px solid var(--border-color);
|
.post_content {
|
||||||
|
position: relative;
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
|
||||||
padding-bottom: 10px;
|
padding: 0 10px 10px 10px;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
color: rgba(var(--background-color-contrast));
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
transition: all 0.2s ease-in-out;
|
||||||
|
|
||||||
|
z-index: 190;
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6,
|
||||||
|
p,
|
||||||
|
span {
|
||||||
|
color: var(--text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.nsfw {
|
||||||
|
.message {
|
||||||
|
filter: blur(10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.post_attachments {
|
||||||
|
filter: blur(25px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.nsfw_alert {
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
padding: 10px 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
// -webkit-backdrop-filter: blur(25px);
|
||||||
|
// backdrop-filter: blur(25px);
|
||||||
|
//background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
z-index: 200;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
color: var(--background-color-contrast);
|
||||||
|
|
||||||
|
word-break: break-all;
|
||||||
|
user-select: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
>div {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
&:first-child {
|
&:first-child {
|
||||||
border-top-left-radius: 8px;
|
border-top-left-radius: 8px;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user