mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-10 02:54:15 +00:00
commit
c6953bdc6a
@ -12,6 +12,7 @@
|
|||||||
"variants": {
|
"variants": {
|
||||||
"light": {
|
"light": {
|
||||||
"appColor": "#ff6064",
|
"appColor": "#ff6064",
|
||||||
|
"text-color": "#000000",
|
||||||
"layoutBackgroundColor": "255, 255, 255",
|
"layoutBackgroundColor": "255, 255, 255",
|
||||||
"background-color-primary": "#ffffff",
|
"background-color-primary": "#ffffff",
|
||||||
"background-color-primary2": "#f0f0f0",
|
"background-color-primary2": "#f0f0f0",
|
||||||
|
13
packages/app/src/components/CommentCreator/index.jsx
Normal file
13
packages/app/src/components/CommentCreator/index.jsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import React from "react"
|
||||||
|
import * as antd from "antd"
|
||||||
|
|
||||||
|
import "./index.less"
|
||||||
|
|
||||||
|
export default (props) => {
|
||||||
|
return <div className="commentCreator">
|
||||||
|
<antd.Input.TextArea
|
||||||
|
placeholder="Write a comment..."
|
||||||
|
autoSize={{ minRows: 2, maxRows: 5 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
22
packages/app/src/components/CommentCreator/index.less
Normal file
22
packages/app/src/components/CommentCreator/index.less
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
.commentCreator {
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
align-items: right;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.ant-input {
|
||||||
|
background-color: var(--background-color-accent) !important;
|
||||||
|
|
||||||
|
border: none !important;
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ant-btn {
|
||||||
|
width: fit-content;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
86
packages/app/src/components/CommentsCard/index.jsx
Normal file
86
packages/app/src/components/CommentsCard/index.jsx
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import React from "react"
|
||||||
|
import * as antd from "antd"
|
||||||
|
import moment from "moment"
|
||||||
|
|
||||||
|
import { CommentCreator } from "components"
|
||||||
|
|
||||||
|
import "./index.less"
|
||||||
|
|
||||||
|
export default (props) => {
|
||||||
|
const [postData, setPostData] = React.useState(null)
|
||||||
|
const [comments, setComments] = React.useState(null)
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
setPostData(null)
|
||||||
|
setComments(null)
|
||||||
|
|
||||||
|
// fetch post data
|
||||||
|
const postDataResult = await window.app.api.request("main", "get", `post`, undefined, {
|
||||||
|
post_id: props.post_id
|
||||||
|
}).catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
|
||||||
|
antd.message.error("Failed to fetch post data")
|
||||||
|
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!postDataResult) return
|
||||||
|
|
||||||
|
setPostData(postDataResult)
|
||||||
|
|
||||||
|
// fetch comments
|
||||||
|
const commentsResult = await window.app.api.customRequest("main", {
|
||||||
|
method: "get",
|
||||||
|
url: `/post/${props.post_id}/comments`,
|
||||||
|
}).catch((err) => {
|
||||||
|
console.log(err)
|
||||||
|
|
||||||
|
antd.message.error("Failed to fetch comments")
|
||||||
|
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log(commentsResult)
|
||||||
|
|
||||||
|
if (!commentsResult) return
|
||||||
|
|
||||||
|
setComments(commentsResult.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
fetchData()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const renderComments = () => {
|
||||||
|
return comments.map((comment) => {
|
||||||
|
return <div className="comment" id={comment._id}>
|
||||||
|
<div className="header">
|
||||||
|
<div className="avatar">
|
||||||
|
<antd.Avatar src={comment.user.avatar} />
|
||||||
|
</div>
|
||||||
|
<div className="username">
|
||||||
|
{comment.user.username}
|
||||||
|
</div>
|
||||||
|
<div className="timeAgo">
|
||||||
|
{moment(comment.createdAt).fromNow()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="content">
|
||||||
|
{comment.message}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!comments) {
|
||||||
|
return <antd.Skeleton active />
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="comments">
|
||||||
|
{renderComments()}
|
||||||
|
<div className="commentCreatorWrapper">
|
||||||
|
<CommentCreator />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
71
packages/app/src/components/CommentsCard/index.less
Normal file
71
packages/app/src/components/CommentsCard/index.less
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
.comments {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
.comment {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
background-color: var(--background-color-accent);
|
||||||
|
|
||||||
|
padding: 10px;
|
||||||
|
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border-radius: 50%;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.username {
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeAgo {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
font-size: 14px;
|
||||||
|
color: var(--text-color);
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.commentCreatorWrapper {
|
||||||
|
position: sticky;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
padding: 30px 5px;
|
||||||
|
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
background-color: rgba(var(--background-color-accent), 0.6);
|
||||||
|
}
|
||||||
|
}
|
@ -349,6 +349,16 @@ export const PostCard = React.memo(({
|
|||||||
return events.onDoubleClick(data)
|
return events.onDoubleClick(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (fullmode) {
|
||||||
|
app.eventBus.emit("style.compactMode", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
app.eventBus.emit("style.compactMode", false)
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
// first listen to post changes
|
// first listen to post changes
|
||||||
window.app.api.namespaces["main"].listenEvent(`post.dataUpdate.${data._id}`, onDataUpdate)
|
window.app.api.namespaces["main"].listenEvent(`post.dataUpdate.${data._id}`, onDataUpdate)
|
||||||
@ -397,29 +407,36 @@ export const PostCard = React.memo(({
|
|||||||
isLiked={hasLiked}
|
isLiked={hasLiked}
|
||||||
likes={likes.length}
|
likes={likes.length}
|
||||||
comments={comments.length}
|
comments={comments.length}
|
||||||
|
fullmode={fullmode}
|
||||||
/>
|
/>
|
||||||
<PostContent
|
<PostContent
|
||||||
data={data}
|
data={data}
|
||||||
autoCarrousel={autoCarrousel}
|
autoCarrousel={autoCarrousel}
|
||||||
|
fullmode={fullmode}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="actionsIndicatorWrapper">
|
{!fullmode &&
|
||||||
<div className="actionsIndicator">
|
<div className="actionsIndicatorWrapper">
|
||||||
<Icons.MoreHorizontal />
|
<div className="actionsIndicator">
|
||||||
|
<Icons.MoreHorizontal />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
<div className="actionsWrapper">
|
{!fullmode &&
|
||||||
<PostActions
|
<div className="actionsWrapper">
|
||||||
isSelf={selfId === data.user_id}
|
<PostActions
|
||||||
defaultLiked={hasLiked}
|
isSelf={selfId === data.user_id}
|
||||||
defaultSaved={hasSaved}
|
defaultLiked={hasLiked}
|
||||||
onClickLike={onClickLike}
|
defaultSaved={hasSaved}
|
||||||
onClickSave={onClickSave}
|
onClickLike={onClickLike}
|
||||||
actions={{
|
onClickSave={onClickSave}
|
||||||
delete: onClickDelete,
|
actions={{
|
||||||
}}
|
delete: onClickDelete,
|
||||||
/>
|
}}
|
||||||
</div>
|
fullmode={fullmode}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -33,6 +33,51 @@
|
|||||||
|
|
||||||
&.fullmode {
|
&.fullmode {
|
||||||
max-width: none;
|
max-width: none;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
background-color: transparent;
|
||||||
|
|
||||||
|
filter: none;
|
||||||
|
|
||||||
|
.actionsIndicatorWrapper {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.actionsWrapper {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.content {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.additions {
|
||||||
|
height: fit-content;
|
||||||
|
max-height: 80vh;
|
||||||
|
|
||||||
|
.addition {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
img,
|
||||||
|
video {
|
||||||
|
width: fit-content;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
align-self: center;
|
||||||
|
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapper {
|
.wrapper {
|
||||||
|
@ -40,6 +40,10 @@ export { default as PostsFeed } from "./PostsFeed"
|
|||||||
export { default as PostCard } from "./PostCard"
|
export { default as PostCard } from "./PostCard"
|
||||||
export { default as PostCreator } from "./PostCreator"
|
export { default as PostCreator } from "./PostCreator"
|
||||||
|
|
||||||
|
// COMMENTS
|
||||||
|
export { default as CommentsCard } from "./CommentsCard"
|
||||||
|
export { default as CommentCreator } from "./CommentCreator"
|
||||||
|
|
||||||
// USERS
|
// USERS
|
||||||
export { default as FollowersList } from "./FollowersList"
|
export { default as FollowersList } from "./FollowersList"
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import React from "react"
|
import React from "react"
|
||||||
import * as antd from "antd"
|
import * as antd from "antd"
|
||||||
|
|
||||||
import { PostCard } from "components"
|
import { PostCard, CommentsCard } from "components"
|
||||||
|
|
||||||
import "./index.less"
|
import "./index.less"
|
||||||
|
|
||||||
@ -28,6 +28,11 @@ export default (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return <div className="fullPost">
|
return <div className="fullPost">
|
||||||
<PostCard data={data} fullmode />
|
<div className="postWrapper">
|
||||||
|
<PostCard data={data} fullmode />
|
||||||
|
</div>
|
||||||
|
<div className="commentsWrapper">
|
||||||
|
<CommentsCard post_id={data._id} />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
@ -1,3 +1,28 @@
|
|||||||
.fullPost {
|
.fullPost {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
|
||||||
|
width: 100%;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.postWrapper {
|
||||||
|
height: 100vh;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.commentsWrapper {
|
||||||
|
max-width: 600px;
|
||||||
|
min-width: 400px;
|
||||||
|
|
||||||
|
width: 40%;
|
||||||
|
height: 100vh;
|
||||||
|
|
||||||
|
overflow: scroll;
|
||||||
|
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
}
|
}
|
@ -32,6 +32,7 @@ export default class Server {
|
|||||||
controllers.PostsController,
|
controllers.PostsController,
|
||||||
controllers.StreamingController,
|
controllers.StreamingController,
|
||||||
controllers.BadgesController,
|
controllers.BadgesController,
|
||||||
|
controllers.CommentsController,
|
||||||
]
|
]
|
||||||
|
|
||||||
middlewares = middlewares
|
middlewares = middlewares
|
||||||
|
@ -2,15 +2,70 @@ import { Controller } from "linebridge/dist/server"
|
|||||||
import { User, Post, Comment } from "../../models"
|
import { User, Post, Comment } from "../../models"
|
||||||
import { Schematized } from "../../lib"
|
import { Schematized } from "../../lib"
|
||||||
|
|
||||||
|
import getComments from "./methods/getComments"
|
||||||
|
|
||||||
export default class CommentsController extends Controller {
|
export default class CommentsController extends Controller {
|
||||||
static refName = "CommentsController"
|
static refName = "CommentsController"
|
||||||
|
|
||||||
get = {
|
get = {
|
||||||
|
"/comments": {
|
||||||
|
fn: Schematized({
|
||||||
|
required: ["targetId"],
|
||||||
|
select: ["targetId"],
|
||||||
|
}, async (req, res) => {
|
||||||
|
|
||||||
|
})
|
||||||
|
},
|
||||||
|
"/post/:post_id/comments": {
|
||||||
|
fn: async (req, res) => {
|
||||||
|
const { post_id } = req.params
|
||||||
|
|
||||||
|
let comments = await Comment.find({ parent_id: post_id }).catch(err => {
|
||||||
|
res.status(500).json({ message: err.message })
|
||||||
|
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if (comments) {
|
||||||
|
// fullfill comments with user data
|
||||||
|
comments = await Promise.all(comments.map(async comment => {
|
||||||
|
const user = await User.findById(comment.user_id)
|
||||||
|
|
||||||
|
return {
|
||||||
|
...comment.toObject(),
|
||||||
|
user: user.toObject(),
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
return res.json(comments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
post = {
|
post = {
|
||||||
|
"/post/:post_id/comment": {
|
||||||
|
middlewares: ["withAuthentication"],
|
||||||
|
fn: Schematized({
|
||||||
|
required: ["message"],
|
||||||
|
select: ["message"],
|
||||||
|
}, async (req, res) => {
|
||||||
|
const { post_id } = req.params
|
||||||
|
const { message } = req.selection
|
||||||
|
|
||||||
|
const comment = new Comment({
|
||||||
|
user_id: req.user._id.toString(),
|
||||||
|
parent_id: post_id,
|
||||||
|
message: message,
|
||||||
|
})
|
||||||
|
|
||||||
|
await comment.save()
|
||||||
|
|
||||||
|
if (comment) {
|
||||||
|
return res.json(comment)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
put = {
|
put = {
|
||||||
|
@ -0,0 +1,11 @@
|
|||||||
|
import { User, Post, Comment } from "../../../models"
|
||||||
|
|
||||||
|
export default (payload) => {
|
||||||
|
const { parent_id, _id } = payload
|
||||||
|
|
||||||
|
if (typeof _id !== "undefined") {
|
||||||
|
return Comment.findById(_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Comment.find({ parent_id })
|
||||||
|
}
|
@ -7,3 +7,4 @@ export { default as PublicController } from "./PublicController"
|
|||||||
export { default as PostsController } from "./PostsController"
|
export { default as PostsController } from "./PostsController"
|
||||||
export { default as StreamingController } from "./StreamingController"
|
export { default as StreamingController } from "./StreamingController"
|
||||||
export { default as BadgesController } from "./BadgesController"
|
export { default as BadgesController } from "./BadgesController"
|
||||||
|
export { default as CommentsController } from "./CommentsController"
|
@ -1,7 +1,7 @@
|
|||||||
export default {
|
export default {
|
||||||
user_id: { type: String, required: true },
|
user_id: { type: String, required: true },
|
||||||
parent_id: { type: String, required: true },
|
parent_id: { type: String, required: true },
|
||||||
content: { type: String, required: true },
|
message: { type: String, required: true },
|
||||||
created_at: { type: Date, default: Date.now },
|
created_at: { type: Date, default: Date.now },
|
||||||
liked: { type: Array, default: [] },
|
//liked: { type: Array, default: [] },
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user