feat: added blackout dates implementation (#271)
* feat: added blackout dates implementation Co-authored-by: Mehak Nasir <mehaknasir94@gmail.com>
This commit is contained in:
@@ -10,7 +10,9 @@ import { Button, useToggle } from '@edx/paragon';
|
||||
import HTMLLoader from '../../../components/HTMLLoader';
|
||||
import { ContentActions } from '../../../data/constants';
|
||||
import { AlertBanner, DeleteConfirmation, EndorsedAlertBanner } from '../../common';
|
||||
import { selectBlackoutDate } from '../../data/selectors';
|
||||
import { fetchThread } from '../../posts/data/thunks';
|
||||
import { inBlackoutDateRange } from '../../utils';
|
||||
import CommentIcons from '../comment-icons/CommentIcons';
|
||||
import { selectCommentCurrentPage, selectCommentHasMorePages, selectCommentResponses } from '../data/selectors';
|
||||
import { editComment, fetchCommentResponses, removeComment } from '../data/thunks';
|
||||
@@ -36,6 +38,7 @@ function Comment({
|
||||
const [isReplying, setReplying] = useState(false);
|
||||
const hasMorePages = useSelector(selectCommentHasMorePages(comment.id));
|
||||
const currentPage = useSelector(selectCommentCurrentPage(comment.id));
|
||||
const blackoutDateRange = useSelector(selectBlackoutDate);
|
||||
|
||||
useEffect(() => {
|
||||
// If the comment has a parent comment, it won't have any children, so don't fetch them.
|
||||
@@ -124,13 +127,18 @@ function Comment({
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
{!isClosedPost
|
||||
{(!isClosedPost && !inBlackoutDateRange(blackoutDateRange))
|
||||
&& (
|
||||
<Button className="d-flex flex-grow mt-4.5" variant="outline-primary" onClick={() => setReplying(true)}>
|
||||
{intl.formatMessage(messages.addComment)}
|
||||
</Button>
|
||||
<Button
|
||||
className="d-flex flex-grow mt-4.5"
|
||||
variant="outline-primary"
|
||||
onClick={() => setReplying(true)}
|
||||
>
|
||||
{intl.formatMessage(messages.addComment)}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -2,10 +2,13 @@ import React, { useEffect, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { Button } from '@edx/paragon';
|
||||
|
||||
import { selectBlackoutDate } from '../../data/selectors';
|
||||
import { inBlackoutDateRange } from '../../utils';
|
||||
import messages from '../messages';
|
||||
import CommentEditor from './CommentEditor';
|
||||
|
||||
@@ -20,6 +23,8 @@ function ResponseEditor({
|
||||
setAddingResponse(false);
|
||||
}, [postId]);
|
||||
|
||||
const blackoutDateRange = useSelector(selectBlackoutDate);
|
||||
|
||||
return addingResponse
|
||||
? (
|
||||
<div className={classNames({ 'bg-white p-4 mb-4 rounded': addWrappingDiv })}>
|
||||
@@ -29,7 +34,8 @@ function ResponseEditor({
|
||||
onCloseEditor={() => setAddingResponse(false)}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
)
|
||||
: !inBlackoutDateRange(blackoutDateRange) && (
|
||||
<div className={classNames({ 'mb-4': addWrappingDiv }, 'actions d-flex')}>
|
||||
<Button variant="primary" className="px-2.5 py-2" onClick={() => setAddingResponse(true)}>
|
||||
{intl.formatMessage(messages.addResponse)}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import React, { useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import { logError } from '@edx/frontend-platform/logging';
|
||||
import {
|
||||
@@ -10,9 +12,10 @@ import { MoreHoriz } from '@edx/paragon/icons';
|
||||
|
||||
import { ContentActions } from '../../data/constants';
|
||||
import { commentShape } from '../comments/comment/proptypes';
|
||||
import { selectBlackoutDate } from '../data/selectors';
|
||||
import messages from '../messages';
|
||||
import { postShape } from '../posts/post/proptypes';
|
||||
import { useActions } from '../utils';
|
||||
import { inBlackoutDateRange, useActions } from '../utils';
|
||||
|
||||
function ActionsDropdown({
|
||||
intl,
|
||||
@@ -31,6 +34,11 @@ function ActionsDropdown({
|
||||
logError(`Unknown or unimplemented action ${action}`);
|
||||
}
|
||||
};
|
||||
const blackoutDateRange = useSelector(selectBlackoutDate);
|
||||
// Find and remove edit action if in blackout date range.
|
||||
if (inBlackoutDateRange(blackoutDateRange)) {
|
||||
actions.splice(actions.findIndex(action => action.id === 'edit'), 1);
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<IconButton
|
||||
|
||||
@@ -9,6 +9,7 @@ import { camelCaseObject, initializeMockApp, snakeCaseObject } from '@edx/fronte
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { ContentActions } from '../../data/constants';
|
||||
import { initializeStore } from '../../store';
|
||||
import messages from '../messages';
|
||||
import { ACTIONS_LIST } from '../utils';
|
||||
import ActionsDropdown from './ActionsDropdown';
|
||||
@@ -126,6 +127,7 @@ describe('ActionsDropdown', () => {
|
||||
roles: [],
|
||||
},
|
||||
});
|
||||
store = initializeStore();
|
||||
});
|
||||
|
||||
it.each(buildTestContent())('can open drop down if enabled', async (commentOrPost) => {
|
||||
|
||||
@@ -18,6 +18,8 @@ export const selectLearnersTabEnabled = state => state.config.learnersTabEnabled
|
||||
|
||||
export const selectDivisionSettings = state => state.config.settings;
|
||||
|
||||
export const selectBlackoutDate = state => state.config.blackouts;
|
||||
|
||||
export const selectModerationSettings = state => ({
|
||||
postCloseReasons: state.config.postCloseReasons,
|
||||
editReasons: state.config.editReasons,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import {
|
||||
@@ -10,7 +10,8 @@ import {
|
||||
import { Close } from '@edx/paragon/icons';
|
||||
|
||||
import Search from '../../../components/Search';
|
||||
import { postMessageToParent } from '../../utils';
|
||||
import { selectBlackoutDate } from '../../data/selectors';
|
||||
import { inBlackoutDateRange, postMessageToParent } from '../../utils';
|
||||
import { showPostEditor } from '../data';
|
||||
import messages from './messages';
|
||||
|
||||
@@ -24,6 +25,7 @@ function PostActionsBar({
|
||||
const handleCloseInContext = () => {
|
||||
postMessageToParent('learning.events.sidebar.close');
|
||||
};
|
||||
const blackoutDateRange = useSelector(selectBlackoutDate);
|
||||
return (
|
||||
<div className="d-flex justify-content-end py-1 flex-grow-1">
|
||||
{!inContext && (
|
||||
@@ -37,15 +39,18 @@ function PostActionsBar({
|
||||
{intl.formatMessage(messages.title)}
|
||||
</h4>
|
||||
)}
|
||||
|
||||
<Button
|
||||
variant={inContext ? 'plain' : 'brand'}
|
||||
className="my-0"
|
||||
onClick={() => dispatch(showPostEditor())}
|
||||
size="sm"
|
||||
>
|
||||
{intl.formatMessage(messages.addAPost)}
|
||||
</Button>
|
||||
{
|
||||
!inBlackoutDateRange(blackoutDateRange) && (
|
||||
<Button
|
||||
variant={inContext ? 'plain' : 'brand'}
|
||||
className="my-0"
|
||||
onClick={() => dispatch(showPostEditor())}
|
||||
size="sm"
|
||||
>
|
||||
{intl.formatMessage(messages.addAPost)}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
{inContext && (
|
||||
<>
|
||||
<div className="border-right mr-3 ml-4" />
|
||||
|
||||
@@ -229,10 +229,10 @@ export function postMessageToParent(type, payload = {}) {
|
||||
export const isPostPreviewAvailable = (htmlNode) => {
|
||||
const containsImage = htmlNode.match(/(<img((?:\\.|.)*)>)/);
|
||||
const isLatex = htmlNode.match(/(\${1,2})((?:\\.|.)*)/)
|
||||
|| htmlNode.match(/(\[mathjax](.+?))+/)
|
||||
|| htmlNode.match(/(\[mathjaxinline](.+?))+/)
|
||||
|| htmlNode.match(/(\\\[(.+?))+/)
|
||||
|| htmlNode.match(/(\\\((.+?))+/);
|
||||
|| htmlNode.match(/(\[mathjax](.+?))+/)
|
||||
|| htmlNode.match(/(\[mathjaxinline](.+?))+/)
|
||||
|| htmlNode.match(/(\\\[(.+?))+/)
|
||||
|| htmlNode.match(/(\\\((.+?))+/);
|
||||
|
||||
if (containsImage || isLatex || htmlNode === '') {
|
||||
return false;
|
||||
@@ -253,3 +253,25 @@ export const filterPosts = (posts, filterBy, sortBy = 'createdAt', order = 'desc
|
||||
post => (filterBy.startsWith('un') ? !post[filterBy.slice(2)] : post[filterBy]),
|
||||
), [sortBy], [order],
|
||||
);
|
||||
|
||||
/**
|
||||
* Helper function to make a check if date is in given range
|
||||
* @param {Date} date this date to be checked in range
|
||||
* @param {Date} start start date
|
||||
* @param {Date} end end date
|
||||
*/
|
||||
export function dateInDateRange(date, start, end) {
|
||||
return date >= start && date <= end;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to make a check if date is in given range
|
||||
* @param {array} blackoutDateRanges start date
|
||||
* @return Boolean
|
||||
*/
|
||||
export function inBlackoutDateRange(blackoutDateRanges) {
|
||||
const now = new Date();
|
||||
return blackoutDateRanges.some(
|
||||
(blackoutDateRange) => dateInDateRange(now, new Date(blackoutDateRange.start), new Date(blackoutDateRange.end)),
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user