import React, { useState, useRef, useEffect } from 'react';
import { MentionsInput, Mention } from 'react-mentions';
import PropTypes from 'prop-types';
import { format } from 'timeago.js';
import { usePopup } from '../../utils/hooks/';
import { PageStore, MediaPageStore } from '../../utils/stores/';
import { PageActions, MediaPageActions } from '../../utils/actions/';
import { LinksContext, MemberContext, SiteContext } from '../../utils/contexts/';
import { PopupMain, UserThumbnail } from '../_shared';
import './Comments.scss';
const commentsText = {
single: 'comment',
uppercaseSingle: 'COMMENT',
ucfirstSingle: 'Comment',
ucfirstPlural: 'Comments',
submitCommentText: 'SUBMIT',
disabledCommentsMsg: 'Comments are disabled',
};
function CommentForm(props) {
const textareaRef = useRef(null);
const [value, setValue] = useState('');
const [madeChanges, setMadeChanges] = useState(false);
const [textareaFocused, setTextareaFocused] = useState(false);
const [textareaLineHeight, setTextareaLineHeight] = useState(-1);
const [userList, setUsersList] = useState('');
const [loginUrl] = useState(
!MemberContext._currentValue.is.anonymous
? null
: LinksContext._currentValue.signin +
'?next=/' +
window.location.href.replace(SiteContext._currentValue.url, '').replace(/^\//g, '')
);
function onFocus() {
setTextareaFocused(true);
}
function onBlur() {
setTextareaFocused(false);
}
function onUsersLoad()
{
const userList =[...MediaPageStore.get('users')];
const cleanList = []
userList.forEach(user => {
cleanList.push({id : user.username, display : user.name});
});
setUsersList(cleanList);
}
function onCommentSubmit() {
textareaRef.current.style.height = '';
const contentHeight = textareaRef.current.scrollHeight;
const contentLineHeight =
0 < textareaLineHeight ? textareaLineHeight : parseFloat(window.getComputedStyle(textareaRef.current).lineHeight);
setValue('');
setMadeChanges(false);
setTextareaLineHeight(contentLineHeight);
textareaRef.current.style.height =
Math.max(20, textareaLineHeight * Math.ceil(contentHeight / contentLineHeight)) + 'px';
}
function onCommentSubmitFail() {
setMadeChanges(false);
}
function onChangeWithMention(event, newValue, newPlainTextValue, mentions) {
textareaRef.current.style.height = '';
setValue(newValue);
setMadeChanges(true);
const contentHeight = textareaRef.current.scrollHeight;
const contentLineHeight =
0 < textareaLineHeight ? textareaLineHeight : parseFloat(window.getComputedStyle(textareaRef.current).lineHeight);
setTextareaLineHeight(contentLineHeight);
textareaRef.current.style.height =
Math.max(20, textareaLineHeight * Math.ceil(contentHeight / contentLineHeight)) + 'px';
}
function onChange(event) {
textareaRef.current.style.height = '';
const contentHeight = textareaRef.current.scrollHeight;
const contentLineHeight =
0 < textareaLineHeight ? textareaLineHeight : parseFloat(window.getComputedStyle(textareaRef.current).lineHeight);
setValue(textareaRef.current.value);
setMadeChanges(true);
setTextareaLineHeight(contentLineHeight);
textareaRef.current.style.height =
Math.max(20, textareaLineHeight * Math.ceil(contentHeight / contentLineHeight)) + 'px';
}
function submitComment() {
if (!madeChanges) {
return;
}
const val = value.trim();
if ('' !== val) {
MediaPageActions.submitComment(val);
}
}
useEffect(() => {
MediaPageStore.on('comment_submit', onCommentSubmit);
MediaPageStore.on('comment_submit_fail', onCommentSubmitFail);
if (MediaCMS.features.media.actions.comment_mention === true)
{
MediaPageStore.on('users_load', onUsersLoad);
}
return () => {
MediaPageStore.removeListener('comment_submit', onCommentSubmit);
MediaPageStore.removeListener('comment_submit_fail', onCommentSubmitFail);
if (MediaCMS.features.media.actions.comment_mention === true)
{
MediaPageStore.removeListener('users_load', onUsersLoad);
}
};
});
return !MemberContext._currentValue.is.anonymous ? (
{ MediaCMS.features.media.actions.comment_mention ?
:
}
) : (
);
}
CommentForm.propTypes = {
comment_type: PropTypes.oneOf(['new', 'reply']),
media_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
reply_comment_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
};
CommentForm.defaultProps = {
comment_type: 'new',
};
const ENABLED_COMMENTS_READ_MORE = false;
function CommentActions(props) {
const [popupContentRef, PopupContent, PopupTrigger] = usePopup();
function cancelCommentRemoval() {
popupContentRef.current.toggle();
}
function proceedCommentRemoval() {
popupContentRef.current.toggle();
MediaPageActions.deleteComment(props.comment_id);
}
return (
{/*
145
*/}
{/*
19
*/}
{/*
*/}
{MemberContext._currentValue.can.deleteComment ? (
{commentsText.ucfirstSingle} removal
You're willing to remove {commentsText.single} permanently?
) : null}
);
}
function Comment(props) {
const commentTextRef = useRef(null);
const commentTextInnerRef = useRef(null);
const [viewMoreContent, setViewMoreContent] = useState(!ENABLED_COMMENTS_READ_MORE || false);
const [enabledViewMoreContent, setEnabledViewMoreContent] = useState(false);
function onWindowResize() {
const newval = enabledViewMoreContent || commentTextInnerRef.offsetHeight > commentTextRef.offsetHeight;
setEnabledViewMoreContent(newval);
setViewMoreContent(newval || false);
}
function toggleMore() {
setViewMoreContent(!viewMoreContent);
}
useEffect(() => {
if (ENABLED_COMMENTS_READ_MORE) {
PageStore.on('window_resize', onWindowResize);
setEnabledViewMoreContent(commentTextInnerRef.offsetHeight > commentTextRef.offsetHeight);
}
return () => {
if (ENABLED_COMMENTS_READ_MORE) {
PageStore.removeListener('window_resize', onWindowResize);
}
};
}, []);
function parseComment(text) {
return { __html: text.replace(/\n/g, `
`) };
}
return (
{format(new Date(props.publish_date))}
{enabledViewMoreContent ? (
) : null}
{MemberContext._currentValue.can.deleteComment ?
: null}
);
}
Comment.propTypes = {
comment_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
media_id: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
text: PropTypes.string,
author_name: PropTypes.string,
author_link: PropTypes.string,
author_thumb: PropTypes.string,
publish_date: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
likes: PropTypes.number,
dislikes: PropTypes.number,
};
Comment.defaultProps = {
author_name: '',
author_link: '#',
publish_date: 0,
likes: 0,
dislikes: 0,
};
function displayCommentsRelatedAlert() {
// TODO: Improve this and move it into Media Page code.
var pageMainEl = document.querySelector('.page-main');
var noCommentDiv = pageMainEl.querySelector('.no-comment');
const postUploadMessage = PageStore.get('config-contents').uploader.postUploadMessage;
if ('' === postUploadMessage) {
if (noCommentDiv && 0 === comm.length) {
noCommentDiv.parentNode.removeChild(noCommentDiv);
}
} else if (0 === comm.length && 'unlisted' === MediaPageStore.get('media-data').state) {
if (-1 < LinksContext._currentValue.profile.media.indexOf(MediaPageStore.get('media-data').author_profile)) {
if (!noCommentDiv) {
const missingCommentariesUnlistedMsgElem = document.createElement('div');
missingCommentariesUnlistedMsgElem.setAttribute('role', 'alert');
missingCommentariesUnlistedMsgElem.setAttribute('class', 'alert info alert-dismissible no-comment');
missingCommentariesUnlistedMsgElem.innerHTML =
'' +
postUploadMessage;
if (pageMainEl.firstChild) {
pageMainEl.insertBefore(missingCommentariesUnlistedMsgElem, pageMainEl.firstChild);
} else {
pageMainEl.appendChild(missingCommentariesUnlistedMsgElem);
}
missingCommentariesUnlistedMsgElem.querySelector('button.close').addEventListener('click', function (ev) {
missingCommentariesUnlistedMsgElem.setAttribute('class', 'alert info alert-dismissible hiding');
setTimeout(function () {
missingCommentariesUnlistedMsgElem.parentNode.removeChild(missingCommentariesUnlistedMsgElem);
}, 400);
ev.preventDefault();
ev.stopPropagation();
return false;
});
}
}
} else if (noCommentDiv && 0 < comm.length) {
noCommentDiv.parentNode.removeChild(noCommentDiv);
}
}
const CommentsListHeader = ({ commentsLength }) => {
return (
<>
{!MemberContext._currentValue.can.readComment || MediaPageStore.get('media-data').enable_comments ? null : (
{commentsText.disabledCommentsMsg}
)}
{MemberContext._currentValue.can.readComment &&
(MediaPageStore.get('media-data').enable_comments || MemberContext._currentValue.can.editMedia) ? (
{commentsLength
? 1 < commentsLength
? commentsLength + ' ' + commentsText.ucfirstPlural
: commentsLength + ' ' + commentsText.ucfirstSingle
: MediaPageStore.get('media-data').enable_comments
? 'No ' + commentsText.single + ' yet'
: ''}
) : null}
>
);
};
export default function CommentsList(props) {
const [mediaId, setMediaId] = useState(MediaPageStore.get('media-id'));
const [comments, setComments] = useState(
MemberContext._currentValue.can.readComment ? MediaPageStore.get('media-comments') : []
);
const [displayComments, setDisplayComments] = useState(false);
function onCommentsLoad() {
const retrievedComments = [...MediaPageStore.get('media-comments')];
if (MediaCMS.features.media.actions.comment_mention === true)
{
retrievedComments.forEach(comment => {
comment.text = setMentions(comment.text);
});
}
retrievedComments.forEach(comment => {
comment.text = setTimestampAnchors(comment.text);
});
displayCommentsRelatedAlert();
setComments(retrievedComments);
}
function setMentions(text)
{
let sanitizedComment = text.split('@(_').join("");
return sanitizedComment.split('_]').join("");
}
function setTimestampAnchors(text)
{
function wrapTimestampWithAnchor(match, string)
{
let split = match.split(':'), s = 0, m = 1;
let searchParameters = new URLSearchParams(window.location.search);
while (split.length > 0)
{
s += m * parseInt(split.pop(), 10);
m *= 60;
}
searchParameters.set('t', s)
const wrapped = "" + match + "";
return wrapped;
}
const timeRegex = new RegExp('((\\d)?\\d:)?(\\d)?\\d:\\d\\d', 'g');
return text.replace(timeRegex , wrapTimestampWithAnchor);
}
function onCommentSubmit(commentId) {
onCommentsLoad();
// FIXME: Without delay creates conflict [ Uncaught Error: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. ].
setTimeout(() => PageActions.addNotification(commentsText.ucfirstSingle + ' added', 'commentSubmit'), 100);
}
function onCommentSubmitFail() {
// FIXME: Without delay creates conflict [ Uncaught Error: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. ].
setTimeout(
() => PageActions.addNotification(commentsText.ucfirstSingle + ' submition failed', 'commentSubmitFail'),
100
);
}
function onCommentDelete(commentId) {
onCommentsLoad();
// FIXME: Without delay creates conflict [ Uncaught Error: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. ].
setTimeout(() => PageActions.addNotification(commentsText.ucfirstSingle + ' removed', 'commentDelete'), 100);
}
function onCommentDeleteFail(commentId) {
// FIXME: Without delay creates conflict [ Uncaught Error: Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch. ].
setTimeout(
() => PageActions.addNotification(commentsText.ucfirstSingle + ' removal failed', 'commentDeleteFail'),
100
);
}
useEffect(() => {
setDisplayComments(
comments.length &&
MemberContext._currentValue.can.readComment &&
(MediaPageStore.get('media-data').enable_comments || MemberContext._currentValue.can.editMedia)
);
}, [comments]);
useEffect(() => {
MediaPageStore.on('comments_load', onCommentsLoad);
MediaPageStore.on('comment_submit', onCommentSubmit);
MediaPageStore.on('comment_submit_fail', onCommentSubmitFail);
MediaPageStore.on('comment_delete', onCommentDelete);
MediaPageStore.on('comment_delete_fail', onCommentDeleteFail);
return () => {
MediaPageStore.removeListener('comments_load', onCommentsLoad);
MediaPageStore.removeListener('comment_submit', onCommentSubmit);
MediaPageStore.removeListener('comment_submit_fail', onCommentSubmitFail);
MediaPageStore.removeListener('comment_delete', onCommentDelete);
MediaPageStore.removeListener('comment_delete_fail', onCommentDeleteFail);
};
}, []);
return (
{MediaPageStore.get('media-data').enable_comments ? : null}
{displayComments
? comments.map((c) => {
return (
);
})
: null}
);
}