Skip to content

Commit

Permalink
✨ Users can now mention other users in Comments
Browse files Browse the repository at this point in the history
✨ Comments can now be liked
✨ Posts can be administrated (enable/disable, clear likes/comments, pin posts)
🐛 Knowledge Base Data Path is now being generated automatically
  • Loading branch information
BerkeAras committed Mar 18, 2022
1 parent aa0672f commit b6570c7
Show file tree
Hide file tree
Showing 12 changed files with 634 additions and 168 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"react-infinite-scroll-component": "^6.0.0",
"react-ionicons": "^4.0.5",
"react-markdown": "^7.1.1",
"react-mentions": "^4.3.1",
"react-newline-to-break": "^1.0.6",
"react-router-dom": "^6.2.1",
"react-scripts": "4.0.1",
Expand Down
191 changes: 132 additions & 59 deletions src/components/Feed/FeedCommentSection/index.jsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import React, { useRef } from 'react'
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'
import './style.scss'
import { Feed, Icon, Header, Loader, Button, Comment, Form } from 'semantic-ui-react'
import { MentionsInput, Mention } from 'react-mentions'
import { Header, Loader, Button, Comment, Form, Feed } from 'semantic-ui-react'
import PropTypes from 'prop-types'
import mentionStyles from './mentionStyles'
import {ThumbsUp} from 'react-feather'

import unknownAvatar from '../../../static/unknown.png'
import likeComment from '../../../utils/likeComment'

class CommentSection extends React.Component {
constructor(props) {
Expand All @@ -14,13 +18,17 @@ class CommentSection extends React.Component {
placeholder: 'Create a comment',
comments: [],
isPosting: false,
isLoading: false,
newCommentContent: '',
newCommentContentRaw: '',
}

this.handleSubmit = this.handleSubmit.bind(this)
this.loadComments = this.loadComments.bind(this)
}

loadComments() {
this.setState({ isLoading: true })
let tokenHeaders = new Headers()
tokenHeaders.append('Authorization', 'Bearer ' + localStorage.getItem('token'))

Expand All @@ -42,10 +50,10 @@ class CommentSection extends React.Component {
} else {
returnText = 'Create a comment'
}
that.setState({ placeholder: returnText })
that.setState({ placeholder: returnText, isLoading: false })

// Get Comments
that.setState({ comments: result })
that.setState({ comments: result, isLoading: false })
})
.catch((error) => {
location.href = '/'
Expand All @@ -56,32 +64,23 @@ class CommentSection extends React.Component {
this.loadComments()
}

handleSubmit(e) {
handleSubmit(e, postId) {
e.preventDefault()

let element

if (e.target.tagName.toLowerCase() == 'button') {
element = e.target.parentNode
} else {
element = e.target.parentNode.parentNode
}

let postContent = element.querySelector('textarea').value

let postContent = this.state.newCommentContentRaw
postContent = postContent.replace(/(?:\r\n|\r|\n)/g, '<br>')

if (postContent !== null && postContent.trim() !== '' && postContent.replaceAll('<br>', '').trim() !== '') {
this.setState({ isPosting: true })
this.setState({ isPosting: true, isLoading: true })

let myHeaders = new Headers()
myHeaders.append('Authorization', 'Bearer ' + localStorage.getItem('token'))
myHeaders.append('Content-Type', 'application/x-www-form-urlencoded')

let urlencoded = new URLSearchParams()
urlencoded.append('content', postContent)
urlencoded.append('post_id', element.querySelector('textarea').id.replace('comment_textarea_', ''))

element.querySelector('textarea').disabled = true
urlencoded.append('post_id', postId)

let requestOptions = {
method: 'POST',
Expand All @@ -93,14 +92,7 @@ class CommentSection extends React.Component {
fetch(process.env.REACT_APP_API_URL + '/api/content/createComment', requestOptions)
.then((response) => response.text())
.then((result) => {
if (result == '1') {
this.setState({ isPosting: false })
} else {
this.setState({ isPosting: false })
}

element.querySelector('textarea').disabled = false
element.querySelector('textarea').value = ''
this.setState({ isPosting: false, isLoading: false, newCommentContent: '' })
this.loadComments()
})
}
Expand Down Expand Up @@ -137,49 +129,130 @@ class CommentSection extends React.Component {
return value
}

fetchUsers(query, callback) {
if (!query) return

let tokenHeaders = new Headers()
tokenHeaders.append('Authorization', 'Bearer ' + localStorage.getItem('token'))

let requestOptions = {
method: 'GET',
headers: tokenHeaders,
redirect: 'follow',
}

// eslint-disable-next-line no-undef
fetch(process.env.REACT_APP_API_URL + '/api/search?query=' + query, requestOptions)
.then((response) => response.json())
.then((result) => {
let userResult = result[0];
return userResult.map(user => ({ display: user.name, id: user.id }))
})
.then(callback)
}

mentionStyles = {
list: {
backgroundColor: 'white',
border: '1px solid rgba(0,0,0,0.15)',
fontSize: 14,
},
item: {
padding: '5px 15px',
borderBottom: '1px solid rgba(0,0,0,0.15)',
'&focused': {
backgroundColor: '#cee4e5',
},
},
}

getLikes(likes) {
let returnStr = '0 Likes'

if (likes == 1) {
returnStr = '1 Like'
} else {
returnStr = likes + ' Likes'
}

return returnStr
}

render() {
return (
<Comment.Group className="comments-group" threaded>
<Header as="h3" dividing>
Comments
</Header>

{this.state.comments ? (
this.state.comments.length == 0 ? (
<span className="empty-comments">Sorry. We could not find any comments.</span>
) : (
this.state.comments.map((comment) => {
return (
<Comment key={comment.id}>
{comment.avatar == '' ? (
<Comment.Avatar href={'/app/user/' + comment.email} src={unknownAvatar} />
) : (
<Comment.Avatar href={'/app/user/' + comment.email} src={process.env.REACT_APP_API_URL + '/' + comment.avatar.replace('./', '')} />
)}
<Comment.Content>
<Comment.Author href={'/app/user/' + comment.email}>{comment.name}</Comment.Author>
<Comment.Metadata>
<span>{this.getDate(comment.created_at)}</span>
</Comment.Metadata>
<Comment.Text dangerouslySetInnerHTML={{ __html: this.decodeHTMLEntities(comment.comment_content) }}></Comment.Text>
</Comment.Content>
</Comment>
{this.state.isLoading ? (
<div style={{position:'relative',height:'130px'}}>
<Loader active>Loading Comments</Loader>
</div>
): (
<>
{this.state.comments ? (
this.state.comments.length == 0 ? (
<span className="empty-comments">Sorry. We could not find any comments.</span>
) : (
this.state.comments.map((comment) => {
return (
<Comment key={comment.id} id={"comment_" + comment.id}>
{comment.avatar == '' ? (
<Comment.Avatar href={'/app/user/' + comment.email} src={unknownAvatar} />
) : (
<Comment.Avatar href={'/app/user/' + comment.email} src={process.env.REACT_APP_API_URL + '/' + comment.avatar.replace('./', '')} />
)}
<Comment.Content>
<Comment.Author href={'/app/user/' + comment.email}>{comment.name}</Comment.Author>
<Comment.Metadata>
<span>{this.getDate(comment.created_at)}</span>
</Comment.Metadata>
<Comment.Text dangerouslySetInnerHTML={{ __html: this.decodeHTMLEntities(comment.comment_content) }}></Comment.Text>
<Comment.Actions>
<Feed.Like
href="#"
onClick={(e) => {
e.preventDefault()
likeComment(e)
}}
id={'comment_like_id_' + comment.id}
className={comment.hasLiked}
>
<ThumbsUp size={16} strokeWidth={2.5} />
<span>{this.getLikes(comment.likes)}</span>
</Feed.Like>
</Comment.Actions>
</Comment.Content>
</Comment>
)
})
)
})
)
) : (
<Navigate to="/" />
) : (
<Navigate to="/" />
)}

<Form onSubmit={(e) => {this.handleSubmit(e, this.props.postId)}} reply>
<br />
<MentionsInput className='MentionsInputComment' style={{minHeight:'60px',marginBottom:'10px'}} disabled={this.state.isLoading} value={this.state.newCommentContent} onChange={(event, newValue, newPlainTextValue, mentions) => {this.setState({newCommentContent:event.target.value,newCommentContentRaw:newValue})}}>
<Mention
displayTransform={(id, name) => `@${name}`}
trigger="@"
data={this.fetchUsers}
style={mentionStyles}
appendSpaceOnAdd
/>
</MentionsInput>

{this.state.isPosting === true ? (
<Button size="small" compact content="Add Reply" loading labelPosition="left" icon="edit" primary />
) : (
<Button size="small" compact content="Add Reply" onClick={(e) => {this.handleSubmit(e, this.props.postId)}} labelPosition="left" icon="edit" primary />
)}
</Form>
</>
)}

<Form onSubmit={this.handleSubmit} reply>
<Form.TextArea id={'comment_textarea_' + this.props.postId} placeholder={this.state.placeholder} />

{this.state.isPosting === true ? (
<Button size="small" compact content="Add Reply" loading labelPosition="left" icon="edit" primary />
) : (
<Button size="small" compact content="Add Reply" onClick={this.handleSubmit} labelPosition="left" icon="edit" primary />
)}
</Form>
</Comment.Group>
)
}
Expand Down
Empty file.
86 changes: 81 additions & 5 deletions src/components/Feed/FeedCommentSection/style.scss
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
.ui.comments .reply.form textarea {
height: 5em !important;
margin-top: 20px;
}

.comments-group {
.avatar {
margin-top: 0 !important;
Expand All @@ -15,6 +10,14 @@
}
}

.MentionsInputComment__input {
min-height: unset !important;
max-height: unset !important;
line-height: unset !important;
font-family: "PT Sans", sans-serif !important;
font-size: 16px !important;
}

.empty-comments {
display: inline-block;
width: 100%;
Expand All @@ -25,3 +28,76 @@
font-style: italic;
letter-spacing: 0.3px;
}

.MentionsInputComment--singleLine .MentionsInputComment__control {
display: inline-block;
width: 130px;
}

.MentionsInputComment--singleLine .MentionsInputComment__highlighter {
padding: 1px;
border: 2px inset transparent;
}

.MentionsInputComment--singleLine .MentionsInputComment__input {
padding: 1px;
border: 2px inset;
}

.MentionsInputComment--multiLine .MentionsInputComment__control {
font-size: 16px;
font-family: "PT Sans", sans-serif !important;
}

.MentionsInputComment--multiLine .MentionsInputComment__highlighter {
border: 1px solid transparent;
padding: 9px;
min-height: 63px;
padding: .78571429em 1em !important;
}

.MentionsInputComment--multiLine .MentionsInputComment__highlighter strong {
font-weight: inherit;
background: #c5d0d652;
border-bottom: 2px solid #2185d0;
padding: 1px;
margin-left: -1px;
}

.MentionsInputComment--multiLine .MentionsInputComment__input {
border: 1px solid silver;
padding: 9px;
outline: 0;
}

.MentionsInputComment__suggestions {
margin-top: 0 !important;
top: 100% !important;
left: 0% !important;
bottom: 0% !important;
width: 100% !important;
}

.MentionsInputComment__suggestions__list {
background-color: white;
border: 1px solid rgba(0, 0, 0, 0.15);
font-size: 16px;
}

.MentionsInputComment__suggestions__item {
padding: 10px 15px;
border-bottom: 1px solid #d4d4d5;
}

.MentionsInputComment__suggestions__item--focused {
background-color: #eeeeee;
}

.MentionsInputComment__mention {
position: relative;
z-index: 1;
color: blue;
text-shadow: 1px 1px 1px white, 1px -1px 1px white, -1px 1px 1px white, -1px -1px 1px white;
text-decoration: underline;
pointer-events: none;
}
Loading

0 comments on commit b6570c7

Please sign in to comment.