Skip to content

Commit

Permalink
feat: comments pre-moderation
Browse files Browse the repository at this point in the history
  • Loading branch information
Tomasz Puch committed Sep 13, 2021
1 parent 09ad9f2 commit 3429711
Show file tree
Hide file tree
Showing 17 changed files with 306 additions and 33 deletions.
19 changes: 17 additions & 2 deletions admin/src/components/Item/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import CardItem from './CardItem';
import ItemFooter from '../ItemFooter';
import ItemHeader from '../ItemHeader';
import useDataManager from '../../hooks/useDataManager';
import { APPROVAL_STATUS } from '../../utils/constants';

const Item = ({
id,
Expand All @@ -26,6 +27,9 @@ const Item = ({
createdAt,
updatedAt,
relatedContentTypes,
onApproveCommentClick,
onRejectCommentClick,
approvalStatus,
}) => {
const { push } = useHistory();
const { getSearchParams } = useDataManager();
Expand All @@ -43,13 +47,17 @@ const Item = ({
};

const isAbuseReported = !isEmpty(reports);
const isItemHeaderDisplayed = blocked || blockedThread || isNew || removed || isAbuseReported;
const isPending = approvalStatus === APPROVAL_STATUS.PENDING;
const isItemHeaderDisplayed = blocked || blockedThread || isNew || removed || isAbuseReported || isPending;
const headerProps = {
blocked,
blockedThread,
isNew,
isAbuseReported,
isRemoved: removed,
onApproveCommentClick,
onRejectCommentClick,
approvalStatus,
};

const footerProps = {
Expand All @@ -67,7 +75,7 @@ const Item = ({
onClick={onClick}
active={id === parsedId}
>
{ isItemHeaderDisplayed && (<ItemHeader { ...headerProps } />) }
{isItemHeaderDisplayed && (<ItemHeader { ...headerProps } />) }
<p>{content}</p>
<ItemFooter {...footerProps} />
</CardItem>
Expand All @@ -87,6 +95,13 @@ Item.propTypes = {
blocked: PropTypes.bool,
blockedThread: PropTypes.bool,
isNew: PropTypes.bool,
approvalStatus: PropTypes.oneOf([
APPROVAL_STATUS.APPROVED,
APPROVAL_STATUS.PENDING,
APPROVAL_STATUS
]),
onApproveCommentClick: PropTypes.func,
onRejectCommentClick: PropTypes.func,
};

export default Item;
30 changes: 23 additions & 7 deletions admin/src/components/ItemDetails/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import ItemModeration from '../ItemModeration';
import ItemHeader from '../ItemHeader';
import AbuseReportsPopUp from '../AbuseReportsPopUp';
import pluginId from '../../pluginId';
import { APPROVAL_STATUS } from "../../utils/constants";

const ItemDetails = ({
id,
Expand All @@ -36,6 +37,9 @@ const ItemDetails = ({
onBlockClick,
onBlockThreadClick,
onAbuseReportResolve,
onApproveCommentClick,
onRejectCommentClick,
approvalStatus,
}) => {
const [showPopUp, setPopUpVisibility] = useState(false);

Expand All @@ -58,14 +62,15 @@ const ItemDetails = ({
};
const hasThreads = (threadsCount !== undefined) && (threadsCount > 0);
const isAbuseReported = !isEmpty(reports);
const isItemHeaderDisplayed = blocked || blockedThread || removed || isAbuseReported;
const isPending = approvalStatus === APPROVAL_STATUS.PENDING;
const isItemHeaderDisplayed = blocked || blockedThread || removed || isAbuseReported || isPending;
const footerProps = {
authorName,
authorUser,
related: isArray(related) ? first(related) : related,
created_at: created_at || createdAt,
updated_at: updated_at || updatedAt,
isDelailedView: true,
isDetailedView: true,
};
const headerProps = {
active,
Expand All @@ -74,15 +79,19 @@ const ItemDetails = ({
isRemoved: removed,
abuseReports: reports || [],
isAbuseReported: !isEmpty(reports),
isDelailedView: true,
isDetailedView: true,
onReportsClick: onPopUpOpen,
approvalStatus,
};
const moderationProps = {
id,
blocked,
blockedThread,
onBlockClick,
onBlockThreadClick,
onApproveCommentClick,
onRejectCommentClick,
approvalStatus,
};
const reportsPopUpProps = {
blocked,
Expand All @@ -105,20 +114,20 @@ const ItemDetails = ({
clickable={clickable}
root={root}
active={active}>
{ isItemHeaderDisplayed && (<ItemHeader { ...headerProps } />) }
{isItemHeaderDisplayed && (<ItemHeader { ...headerProps } />) }
<p>{content}</p>
<ItemFooter {...footerProps} />
</CardItem>
{ hasThreads && (<CardLevelCounter>
{hasThreads && (<CardLevelCounter>
<FormattedMessage id={`${pluginId}.list.item.threads.count`} values={{ count: threadsCount }}/>
<CardLevelCounterLink onClick={onClick}>
<FormattedMessage id={`${pluginId}.list.item.threads.drilldown`} />
<FontAwesomeIcon icon={faArrowRight} />
</CardLevelCounterLink>
</CardLevelCounter>
)}
{ active && !removed && (<ItemModeration { ...moderationProps } />) }
{ (!isEmpty(reports) && active) && (
{active && !removed && (<ItemModeration { ...moderationProps } />)}
{(!isEmpty(reports) && active) && (
<AbuseReportsPopUp
{...reportsPopUpProps}
/>
Expand Down Expand Up @@ -146,6 +155,13 @@ ItemDetails.propTypes = {
onBlockClick: PropTypes.func,
onBlockThreadClick: PropTypes.func,
onAbuseReportResolve: PropTypes.func,
approvalStatus: PropTypes.oneOf([
APPROVAL_STATUS.APPROVED,
APPROVAL_STATUS.PENDING,
APPROVAL_STATUS
]),
onApproveCommentClick: PropTypes.func,
onRejectCommentClick: PropTypes.func,
};

export default ItemDetails;
13 changes: 13 additions & 0 deletions admin/src/components/ItemHeader/CardHeaderPending.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import styled from 'styled-components';

import { colors } from 'strapi-helper-plugin';

import CardHeaderBlocked from './CardHeaderBlocked';

const CardHeaderPending = styled(CardHeaderBlocked)`
color: ${colors.leftMenu.blue};
background: ${colors.leftMenu.lightBlue};
`;

export default CardHeaderPending;
43 changes: 30 additions & 13 deletions admin/src/components/ItemHeader/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,34 @@ import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { useGlobalContext, CheckPermissions } from 'strapi-helper-plugin';
import { faLock, faStream, faAsterisk, faFire } from '@fortawesome/free-solid-svg-icons';
import { faLock, faStream, faAsterisk, faFire, faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
import Wrapper from './Wrapper';
import CardHeaderBlocked from './CardHeaderBlocked';
import CardHeaderPending from './CardHeaderPending';
import pluginId from '../../pluginId';
import CardHeaderIndicatorsContainer from './CardHeaderIndicatorsContainer';
import CardHeaderIndicatorBlue from './CardHeaderIndicatorBlue';
import CardHeaderIndicatorRed from './CardHeaderIndicatorRed';
import CardHeaderReports from './CardHeaderReports';
import pluginPermissions from '../../permissions';
import { APPROVAL_STATUS } from "../../utils/constants";

const ItemHeader = ({ active, isDelailedView, blocked, blockedThread, isNew, isAbuseReported, isRemoved, abuseReports, onReportsClick }) => {
const ItemHeader = ({ active, isDetailedView, blocked, blockedThread, isNew, isAbuseReported, isRemoved, abuseReports, onReportsClick, approvalStatus }) => {
const { formatMessage } = useGlobalContext();
const isBlocked = blocked || blockedThread;
const hasAnyIndicators = isNew || (isAbuseReported && !isDelailedView);
const hasAnyIndicators = isNew || (isAbuseReported && !isDetailedView);

return (
<Wrapper hasMargin={isBlocked || isRemoved || (isAbuseReported && isDelailedView)}>
{ isBlocked && (
<Wrapper hasMargin={isBlocked || isRemoved || (isAbuseReported && isDetailedView)}>
{isBlocked && (
<CheckPermissions permissions={pluginPermissions.moderate}>
<CardHeaderBlocked>
<FontAwesomeIcon icon={faLock} />
{blockedThread && <FontAwesomeIcon icon={faStream} />}
<FormattedMessage id={`${pluginId}.list.item.header.blocked${blockedThread ? '.thread' : ''}`} />
</CardHeaderBlocked>
</CheckPermissions>
) }
)}
{
isRemoved && (
<CheckPermissions permissions={pluginPermissions.moderate}>
Expand All @@ -40,8 +42,18 @@ const ItemHeader = ({ active, isDelailedView, blocked, blockedThread, isNew, isA
</CheckPermissions>
)
}
{ (isDelailedView && active) && (<>
{ isAbuseReported && (
{
approvalStatus === APPROVAL_STATUS.PENDING && (
<CheckPermissions permissions={pluginPermissions.moderate}>
<CardHeaderPending>
<FontAwesomeIcon icon={faExclamationCircle} />
<FormattedMessage id={`${pluginId}.list.item.header.pending`} />
</CardHeaderPending>
</CheckPermissions>
)
}
{(isDetailedView && active) && (<>
{isAbuseReported && (
<CheckPermissions permissions={pluginPermissions.moderateReports}>
<CardHeaderReports href="#abuse-reports" onClick={onReportsClick}>
<FontAwesomeIcon icon={faFire} />
Expand All @@ -51,13 +63,13 @@ const ItemHeader = ({ active, isDelailedView, blocked, blockedThread, isNew, isA
)}
</>)}
{hasAnyIndicators && <CardHeaderIndicatorsContainer>
{ isNew && (<CardHeaderIndicatorBlue title={formatMessage({ id: `${pluginId}.list.item.indication.new`})}>
{isNew && (<CardHeaderIndicatorBlue title={formatMessage({ id: `${pluginId}.list.item.indication.new`})}>
<FontAwesomeIcon icon={faAsterisk} />
</CardHeaderIndicatorBlue>) }
</CardHeaderIndicatorBlue>)}
<CheckPermissions permissions={pluginPermissions.moderateReports}>
{ isAbuseReported && (<CardHeaderIndicatorRed title={formatMessage({ id: `${pluginId}.list.item.indication.abuse`})}>
{isAbuseReported && (<CardHeaderIndicatorRed title={formatMessage({ id: `${pluginId}.list.item.indication.abuse`})}>
<FontAwesomeIcon icon={faFire} />
</CardHeaderIndicatorRed>) }
</CardHeaderIndicatorRed>)}
</CheckPermissions>
</CardHeaderIndicatorsContainer>
}
Expand All @@ -67,14 +79,19 @@ const ItemHeader = ({ active, isDelailedView, blocked, blockedThread, isNew, isA

ItemHeader.propTypes = {
active: PropTypes.bool,
isDelailedView: PropTypes.bool,
isDetailedView: PropTypes.bool,
blocked: PropTypes.bool,
blockedThread: PropTypes.bool,
isNew: PropTypes.bool,
removed: PropTypes.bool,
isAbuseReported: PropTypes.bool,
abuseReports: PropTypes.array,
onReportsClick: PropTypes.func,
approvalStatus: PropTypes.oneOf([
APPROVAL_STATUS.APPROVED,
APPROVAL_STATUS.PENDING,
APPROVAL_STATUS
])
};

export default ItemHeader;
47 changes: 42 additions & 5 deletions admin/src/components/ItemModeration/index.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,51 @@
import React from 'react';
import React, { useMemo } from 'react';
import PropTypes from 'prop-types';
import { useGlobalContext, CheckPermissions } from 'strapi-helper-plugin';
import { Button } from '@buffetjs/core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { faComment, faCommentSlash, faComments } from '@fortawesome/free-solid-svg-icons';
import { faComment, faCommentSlash, faComments, faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
import Wrapper from './Wrapper';
import pluginId from '../../pluginId';
import pluginPermissions from '../../permissions';
import { APPROVAL_STATUS } from '../../utils/constants';

const ItemModeration = ({ id, blocked, blockedThread, onBlockClick, onBlockThreadClick }) => {

const ItemModeration = ({ id, blocked, blockedThread, onBlockClick, onBlockThreadClick, onApproveCommentClick, onRejectCommentClick, approvalStatus }) => {
const { formatMessage } = useGlobalContext();
const canBeApproved =
approvalStatus === APPROVAL_STATUS.PENDING ||
approvalStatus === APPROVAL_STATUS.REJECTED;
const approvalButtonProps = useMemo(
() =>
canBeApproved
? {
onClick: onApproveCommentClick.bind(null, id),
color: 'primary',
label: formatMessage({
id: `${pluginId}.list.item.moderation.button.comment.approval.approve`,
}),
}
: {
onClick: onRejectCommentClick.bind(null, id),
color: 'delete',
label: formatMessage({
id: `${pluginId}.list.item.moderation.button.comment.approval.reject`,
}),
},
[canBeApproved, id],
);

return (
<CheckPermissions permissions={pluginPermissions.moderate}>
<Wrapper>
{ !blockedThread && (
{approvalStatus ? (
<CheckPermissions permissions={pluginPermissions.moderateComments}>
<Button
{...approvalButtonProps}
icon={<FontAwesomeIcon icon={faExclamationCircle} />}
/>
</CheckPermissions>
) : null}
{!blockedThread && (
<CheckPermissions permissions={pluginPermissions.moderateComments}>
<Button
onClick={e => onBlockClick(id)}
Expand All @@ -42,6 +72,13 @@ ItemModeration.propTypes = {
blockedThread: PropTypes.bool,
onBlockClick: PropTypes.func.isRequired,
onBlockThreadClick: PropTypes.func.isRequired,
approvalStatus: PropTypes.oneOf([
APPROVAL_STATUS.APPROVED,
APPROVAL_STATUS.PENDING,
APPROVAL_STATUS
]),
onApproveCommentClick: PropTypes.func,
onRejectCommentClick: PropTypes.func,
};

export default ItemModeration;
4 changes: 4 additions & 0 deletions admin/src/containers/DataManagerProvider/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,7 @@ export const BLOCK_COMMENT_SUCCESS = 'BLOCK_COMMENT_SUCCESS';
export const BLOCK_COMMENT_THREAD_SUCCESS = 'BLOCK_COMMENT_THREAD_SUCCESS';
export const RESOLVE_ABUSE_REPORT = 'RESOLVE_ABUSE_REPORT';
export const RESOLVE_ABUSE_REPORT_SUCCESS = 'RESOLVE_ABUSE_REPORT_SUCCESS';
export const APPROVE_COMMENT = 'APPROVE_COMMENT';
export const REJECT_COMMENT = 'REJECT_COMMENT';
export const APPROVE_COMMENT_SUCCESS = 'APPROVE_COMMENT_SUCCESS';
export const REJECT_COMMENT_SUCCESS = 'REJECT_COMMENT_SUCCESS';
Loading

0 comments on commit 3429711

Please sign in to comment.