diff --git a/Intl/localizationData/en.js b/Intl/localizationData/en.js index 79b481d3c..5b4b8ed71 100644 --- a/Intl/localizationData/en.js +++ b/Intl/localizationData/en.js @@ -3,12 +3,16 @@ export default { messages: { siteTitle: 'MERN Starter Blog', addPost: 'Add Post', + addComment: 'Add comment', + singleComment: 'Comment', + confirmDeleteComment: 'Are you sure you want to delete this comment?', switchLanguage: 'Switch Language', twitterMessage: 'We are on Twitter', by: 'By', deletePost: 'Delete Post', createNewPost: 'Create new post', authorName: 'Author\'s Name', + commentContent: 'Write your comment', postTitle: 'Post Title', postContent: 'Post Content', submit: 'Submit', diff --git a/Intl/localizationData/fr.js b/Intl/localizationData/fr.js index 7e5b81b3f..87e3cb833 100644 --- a/Intl/localizationData/fr.js +++ b/Intl/localizationData/fr.js @@ -3,12 +3,16 @@ export default { messages: { siteTitle: 'MERN blog de démarrage', addPost: 'Ajouter Poster', + addComment: 'Ajouter un commentaire', + singleComment: 'Сommentaire', + confirmDeleteComment: 'Étes-vous sûr de vouloir supprimer ce commentaire?', switchLanguage: 'Changer de langue', twitterMessage: 'Nous sommes sur Twitter', by: 'Par', deletePost: 'Supprimer le message', createNewPost: 'Créer un nouveau message', authorName: 'Nom de l\'auteur', + commentContent: 'Écrivez votre commentaire', postTitle: 'Titre de l\'article', postContent: 'Contenu après', submit: 'Soumettre', diff --git a/client/modules/Comment/AddCommentWidget/AddCommentWidget.css b/client/modules/Comment/AddCommentWidget/AddCommentWidget.css new file mode 100644 index 000000000..07da87e69 --- /dev/null +++ b/client/modules/Comment/AddCommentWidget/AddCommentWidget.css @@ -0,0 +1,31 @@ +.comment-form { + margin-top: 5rem; +} +.comment-submit-button { + display: inline-block; + padding: 8px 16px; + font-size: 18px; + color: #FFF; + background: #03A9F4; + text-decoration: none; + border-radius: 4px; + cursor: pointer; +} +.comment-form-title { + font-size: 16px; + font-weight: 700; + margin-bottom: 16px; + color: #757575; +} +.comment-form-field { + width: 100%; + margin-bottom: 16px; + font-family: 'Lato', sans-serif; + font-size: 16px; + line-height: normal; + padding: 12px 16px; + border-radius: 4px; + border: 1px solid #ddd; + outline: none; + color: #212121; +} \ No newline at end of file diff --git a/client/modules/Comment/AddCommentWidget/AddCommentWidget.jsx b/client/modules/Comment/AddCommentWidget/AddCommentWidget.jsx new file mode 100644 index 000000000..af9490d06 --- /dev/null +++ b/client/modules/Comment/AddCommentWidget/AddCommentWidget.jsx @@ -0,0 +1,61 @@ +import React, { useRef } from 'react'; +import PropTypes from 'prop-types'; +import { connect } from 'react-redux'; +import { injectIntl, intlShape, FormattedMessage } from 'react-intl'; + +// Import Style +import styles from './AddCommentWidget.css'; + +// Import components +import CommentList from '../CommentList'; + +// Import actions +import { addComment } from '../CommentActions'; + +// Import selectors +import { getComments } from '../CommentReducer'; + +const AddCommentWidget = (props) => { + const authorRef = useRef(); + const contentRef = useRef(); + + const addNewComment = () => { + authorRef.current.focus(); + contentRef.current.focus(); + + if (authorRef.current.value && contentRef.current.value) { + props.addComment(authorRef.current.value, contentRef.current.value); + authorRef.current.value = contentRef.current.value = ''; + } + }; + + const onKeyDownHandler = (e) => { + if (e.keyCode === 13) { + addNewComment(); + } + }; + + return ( +
+ +

+ + +

addNewComment()} className={styles['comment-submit-button']} href="#">

+
+ ); +}; + +AddCommentWidget.propTypes = { + intl: intlShape.isRequired, + comments: PropTypes.object.isRequired, + addComment: PropTypes.func.isRequired, +}; + +const mapStateToProps = (state) => { + return { + comments: getComments(state), + }; +}; + +export default injectIntl(connect(mapStateToProps, { addComment })(AddCommentWidget)); diff --git a/client/modules/Comment/CommentActions.js b/client/modules/Comment/CommentActions.js new file mode 100644 index 000000000..21b62c31a --- /dev/null +++ b/client/modules/Comment/CommentActions.js @@ -0,0 +1,29 @@ +// Export Constants +export const ADD_COMMENT = 'ADD_COMMENT'; +export const EDIT_COMMENT = 'EDIT_COMMENT'; +export const DELETE_COMMENT = 'DELETE_COMMENT'; + +// Export Actions +export const addComment = (author, content) => { + return { + type: ADD_COMMENT, + author, + content, + }; +}; + +export const editComment = (author, content, cid) => { + return { + type: EDIT_COMMENT, + author, + content, + cid, + }; +}; + +export const deleteComment = (cid) => { + return { + type: DELETE_COMMENT, + cid, + }; +}; diff --git a/client/modules/Comment/CommentList.jsx b/client/modules/Comment/CommentList.jsx new file mode 100644 index 000000000..ff8697e7c --- /dev/null +++ b/client/modules/Comment/CommentList.jsx @@ -0,0 +1,30 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +// Import Components +import CommentListItem from './CommentListItem/CommentListItem'; + +const CommentList = (props) => { + return ( +
+ { + props.comments.map(comment => ( + + )) + } +
+ ); +}; +//onDelete={() => props.handleDeletePost(post.cuid)} +CommentList.propTypes = { + comments: PropTypes.arrayOf(PropTypes.shape({ + cid: PropTypes.number.isRequired, + author: PropTypes.string.isRequired, + content: PropTypes.string.isRequired, + })).isRequired, +}; + +export default CommentList; diff --git a/client/modules/Comment/CommentListItem/CommentListItem.css b/client/modules/Comment/CommentListItem/CommentListItem.css new file mode 100644 index 000000000..3f425d231 --- /dev/null +++ b/client/modules/Comment/CommentListItem/CommentListItem.css @@ -0,0 +1,68 @@ +.comment-item-container { + margin: 1rem 0 1rem 0; + background-color: whitesmoke; + padding: 1rem; + border-radius: 10px; + position: relative; +} + +.author-name { + display: flex; + margin: 0.5rem 0 0 0.5rem; + font-family: 'Lato', sans-serif; +} +.comment-content { + margin: 0 0 0 0.5rem; + font-family: 'Lato', sans-serif; + font-size: 14px; + color: #424242; +} +.comment-delete-button { + position: absolute; + right: 0; + margin: 0.2rem 1rem 0 0; + top: 0; + font-size: 26px; + cursor: pointer; +} +.comment-delete-button:hover { + color: red; +} +.comment-edit-button { + position: absolute; + right: 0; + top: 0; + margin: 0.5rem 2.5rem 0 0; + font-size: 18px; + cursor: pointer; +} +.comment-edit-button:hover { + color: green; +} +.edit-form-field { + width: 100%; + margin-bottom: 5px; + margin-top: 16px; + font-family: 'Lato', sans-serif; + font-size: 16px; + line-height: normal; + padding: 12px 16px; + border-radius: 4px; + border: 1px solid #ddd; + outline: none; + color: #212121; +} +.edit-button { + margin: 0.2rem 1rem 0 0; + font-size: 14px; + border-radius: 5px; + cursor: pointer; + padding: 7px; + outline: none !important; + border: none; + background-color: rgb(209, 209, 209); + width: 70px; +} +.edit-button:hover { + background-color: white; +} \ No newline at end of file diff --git a/client/modules/Comment/CommentListItem/CommentListItem.jsx b/client/modules/Comment/CommentListItem/CommentListItem.jsx new file mode 100644 index 000000000..8d1844919 --- /dev/null +++ b/client/modules/Comment/CommentListItem/CommentListItem.jsx @@ -0,0 +1,82 @@ +import React, { useState, useRef } from 'react'; +import PropTypes from 'prop-types'; +import { intlShape, injectIntl, FormattedMessage } from 'react-intl'; +import { connect } from 'react-redux'; + + +// Import Style +import styles from './CommentListItem.css'; + +// Import selectors +import { getComment } from '../CommentReducer'; + +// Import actions +import { editComment, deleteComment } from '../CommentActions'; + +const CommentListItem = (props) => { + const [edit, setEdit] = useState(false); + + const editAuthorRef = useRef(); + const editContentRef = useRef(); + + const onClickSaveButton = () => { + editAuthorRef.current.focus(); + editContentRef.current.focus(); + + if (editAuthorRef.current.value && editContentRef.current.value) { + props.editComment(editAuthorRef.current.value, editContentRef.current.value, props.comment.cid); + setEdit(false); + } + }; + + const onClickOnDeleteButton = () => { + if (confirm(props.intl.messages.confirmDeleteComment)) { // eslint-disable-line + props.deleteComment(props.comment.cid); + } + }; + + return ( +
+ { + !edit ? +
+

#{props.comment.cid}

+

+ +   +

{props.comment.author}

+

+

{props.comment.content}

+

onClickOnDeleteButton()}className={styles['comment-delete-button']}>×

+

setEdit(true)} className={styles['comment-edit-button']}>✎

+
: +
+

#{props.comment.cid}

+ +