Compare commits

..

7 Commits

Author SHA1 Message Date
SaadYousaf
aa236f96d8 feat: add label for content that is awaiting review 2023-04-12 15:50:16 +05:00
ayesha waris
ac117c75de style: adds new icons and changes messages to accept reject review 2023-04-12 14:43:40 +05:00
SaadYousaf
37ee085253 feat: add action handlers for rejecting/accepting content flagged for review 2023-04-12 03:18:35 +05:00
Muhammad Adeel Tajamul
f13b34c6c7 fix: updated post actions dropdown design (#495)
Co-authored-by: adeel.tajamul <adeel.tajamul@arbisoft.com>
2023-04-07 14:30:54 +05:00
ayesha waris
1ba5b938c4 fix: typeset failed: Cannot read properties of undefined (#496) 2023-04-05 17:22:19 +05:00
sundasnoreen12
c1478dbb41 fix: fixed dynamic learner profile issue (#493)
* fix: fixed dynamic URL issue

* refactor: removed unused selectors

---------

Co-authored-by: sundasnoreen12 <sundasnoreen12@ggmail.com>
2023-04-05 16:21:25 +05:00
sundasnoreen12
5cc5156b2b test: added test cases of navigation bar (#487) (#489)
* test: added test cases of navigation bar

* test: added test case for navigation bar api

---------

Co-authored-by: ayesha waris <73840786+ayeshoali@users.noreply.github.com>
Co-authored-by: sundasnoreen12 <sundasnoreen12@ggmail.com>
2023-04-04 16:22:13 +05:00
15 changed files with 65 additions and 30 deletions

View File

@@ -24,7 +24,7 @@ function HTMLLoader({
function typeset(code) {
promise = promise.then(() => {
if (typeof window?.MathJax !== 'undefined') {
if (typeof window?.MathJax !== 'undefined' && typeof window?.MathJax.startup !== 'undefined') {
window.MathJax.startup.defaultPageReady().then((window.MathJax?.typesetPromise(code())));
}
return null;

View File

@@ -55,6 +55,8 @@ export const ContentActions = {
CHANGE_TOPIC: 'topic_id',
CHANGE_TYPE: 'type',
VOTE: 'voted',
ACCEPT_REVIEW: 'accept_review',
REJECT_REVIEW: 'reject_review',
};
/**

View File

@@ -75,7 +75,7 @@ function ActionsDropdown({
placement="bottom-end"
>
<div
className="bg-white p-1 shadow d-flex flex-column"
className="bg-white shadow d-flex flex-column"
data-testid="actions-dropdown-modal-popup"
>
{actions.map(action => (
@@ -91,9 +91,15 @@ function ActionsDropdown({
close();
handleActions(action.action);
}}
className="d-flex justify-content-start py-1.5 mr-4"
className="d-flex justify-content-start actions-dropdown-item"
>
<Icon src={action.icon} className="mr-1" /> {intl.formatMessage(action.label)}
<Icon
src={action.icon}
className="icon-size-24"
/>
<span className="font-weight-normal font-xl ml-2">
{intl.formatMessage(action.label)}
</span>
</Dropdown.Item>
</React.Fragment>
))}

View File

@@ -14,7 +14,3 @@ export const selectUsernameSearch = () => state => state.learners.usernameSearch
export const selectLearnerSorting = () => state => state.learners.sortedBy;
export const selectLearnerNextPage = () => state => state.learners.nextPage;
export const selectLearnerAvatar = author => state => (
state.learners.learnerProfiles[author]?.profileImage?.imageUrlLarge
);

View File

@@ -1,20 +1,15 @@
import React from 'react';
import { useSelector } from 'react-redux';
import { Avatar } from '@edx/paragon';
import { selectLearnerAvatar } from '../data/selectors';
import { learnerShape } from './proptypes';
function LearnerAvatar({ learner }) {
const learnerAvatar = useSelector(selectLearnerAvatar(learner.username));
return (
<div className="mr-3 mt-1">
<Avatar
size="sm"
alt={learner.username}
src={learnerAvatar}
style={{
height: '2rem',
width: '2rem',

View File

@@ -26,6 +26,16 @@ const messages = defineMessages({
defaultMessage: 'Unpin',
description: 'Action to unpin a post',
},
acceptReview: {
id: 'discussions.actions.reviewAccept',
defaultMessage: 'Accept',
description: 'Action to accept content flagged for review',
},
rejectReview: {
id: 'discussions.actions.reviewReject',
defaultMessage: 'Decline',
description: 'Action to reject content flagged for review',
},
deleteAction: {
id: 'discussions.actions.delete',
defaultMessage: 'Delete',

View File

@@ -1,7 +1,6 @@
import React from 'react';
import classNames from 'classnames';
import { useSelector } from 'react-redux';
import { injectIntl } from '@edx/frontend-platform/i18n';
import { Avatar } from '@edx/paragon';
@@ -9,13 +8,11 @@ import { Avatar } from '@edx/paragon';
import { AvatarOutlineAndLabelColors } from '../../../../data/constants';
import { AuthorLabel } from '../../../common';
import { useAlertBannerVisible } from '../../../data/hooks';
import { selectAuthorAvatars } from '../../../posts/data/selectors';
import { commentShape } from './proptypes';
function CommentHeader({
comment,
}) {
const authorAvatars = useSelector(selectAuthorAvatars(comment.author));
const colorClass = AvatarOutlineAndLabelColors[comment.authorLabel];
const hasAnyAlert = useAlertBannerVisible(comment);
@@ -28,7 +25,6 @@ function CommentHeader({
<Avatar
className={`border-0 ml-0.5 mr-2.5 ${colorClass ? `outline-${colorClass}` : 'outline-anonymous'}`}
alt={comment.author}
src={authorAvatars?.imageUrlSmall}
style={{
width: '32px',
height: '32px',

View File

@@ -1,7 +1,7 @@
import React, { useCallback, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { useDispatch } from 'react-redux';
import * as timeago from 'timeago.js';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
@@ -14,7 +14,6 @@ import {
} from '../../../common';
import timeLocale from '../../../common/time-locale';
import { useAlertBannerVisible } from '../../../data/hooks';
import { selectAuthorAvatars } from '../../../posts/data/selectors';
import { editComment, removeComment } from '../../data/thunks';
import messages from '../../messages';
import CommentEditor from './CommentEditor';
@@ -60,7 +59,6 @@ function Reply({
[ContentActions.REPORT]: () => handleAbusedFlag(),
}), [dispatch, handleAbusedFlag, reply.endorsed, reply.id, showDeleteConfirmation]);
const authorAvatars = useSelector(selectAuthorAvatars(reply.author));
const colorClass = AvatarOutlineAndLabelColors[reply.authorLabel];
const hasAnyAlert = useAlertBannerVisible(reply);
@@ -101,7 +99,6 @@ function Reply({
<Avatar
className={`ml-0.5 mt-0.5 border-0 ${colorClass ? `outline-${colorClass}` : 'outline-anonymous'}`}
alt={reply.author}
src={authorAvatars?.imageUrlSmall}
style={{
width: '32px',
height: '32px',

View File

@@ -151,6 +151,7 @@ export async function updateThread(threadId, {
pinned,
editReasonCode,
closeReasonCode,
reviewStatus,
} = {}) {
const url = `${getThreadsApiUrl()}${threadId}/`;
const patchData = snakeCaseObject({
@@ -166,6 +167,7 @@ export async function updateThread(threadId, {
pinned,
editReasonCode,
closeReasonCode,
reviewStatus,
});
const { data } = await getAuthenticatedHttpClient()
.patch(url, patchData, { headers: { 'Content-Type': 'application/merge-patch+json' } });

View File

@@ -44,7 +44,3 @@ export const selectThreadSorting = () => state => state.threads.sortedBy;
export const selectThreadFilters = () => state => state.threads.filters;
export const selectThreadNextPage = () => state => state.threads.nextPage;
export const selectAuthorAvatars = author => state => (
state.threads.avatars?.[author]?.profile.image
);

View File

@@ -239,6 +239,7 @@ export function createNewThread({
export function updateExistingThread(threadId, {
flagged, voted, read, topicId, type, title, content, following, closed, pinned, closeReasonCode, editReasonCode,
reviewStatus,
}) {
return async (dispatch) => {
try {
@@ -256,6 +257,7 @@ export function updateExistingThread(threadId, {
pinned,
editReasonCode,
closeReasonCode,
reviewStatus,
}));
const data = await updateThread(threadId, {
flagged,
@@ -270,6 +272,7 @@ export function updateExistingThread(threadId, {
pinned,
editReasonCode,
closeReasonCode,
reviewStatus,
});
dispatch(updateThreadSuccess(camelCaseObject(data)));
} catch (error) {

View File

@@ -44,6 +44,7 @@ function Post({
const userHasModerationPrivileges = useSelector(selectUserHasModerationPrivileges);
const displayPostFooter = post.following || post.voteCount || post.closed
|| (post.groupId && userHasModerationPrivileges);
const displayReviewContentLabel = post.reviewStatus === "PENDING";
const handleAbusedFlag = useCallback(() => {
if (post.abuseFlagged) {
@@ -85,6 +86,8 @@ function Post({
[ContentActions.COPY_LINK]: () => { navigator.clipboard.writeText(`${window.location.origin}/${courseId}/posts/${post.id}`); },
[ContentActions.PIN]: () => dispatch(updateExistingThread(post.id, { pinned: !post.pinned })),
[ContentActions.REPORT]: () => handleAbusedFlag(),
[ContentActions.ACCEPT_REVIEW]: () => dispatch(updateExistingThread(post.id, { reviewStatus: "ACCEPTED" })),
[ContentActions.REJECT_REVIEW]: () => dispatch(updateExistingThread(post.id, { reviewStatus: "REJECTED" })),
}), [
showDeleteConfirmation,
history,
@@ -144,6 +147,8 @@ function Post({
/>
<AlertBanner content={post} />
<PostHeader post={post} />
{displayReviewContentLabel && <p style= {{ background: "yellow" }}> This content is under review </p>}
<div className="d-flex mt-14px text-break font-style text-primary-500">
<HTMLLoader htmlNode={post.renderedBody} componentId="post" cssClassName="html-loader" testId={post.id} />
</div>

View File

@@ -2,7 +2,6 @@ import React, { useMemo } 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 { Avatar, Badge, Icon } from '@edx/paragon';
@@ -11,14 +10,12 @@ import { Issue, Question } from '../../../components/icons';
import { AvatarOutlineAndLabelColors, ThreadType } from '../../../data/constants';
import { AuthorLabel } from '../../common';
import { useAlertBannerVisible } from '../../data/hooks';
import { selectAuthorAvatars } from '../data/selectors';
import messages from './messages';
import { postShape } from './proptypes';
export function PostAvatar({
post, authorLabel, fromPostLink, read,
}) {
const authorAvatars = useSelector(selectAuthorAvatars(post.author));
const outlineColor = AvatarOutlineAndLabelColors[authorLabel];
const avatarSize = useMemo(() => {
@@ -63,7 +60,6 @@ export function PostAvatar({
width: avatarSize,
}}
alt={post.author}
src={authorAvatars?.imageUrlSmall}
/>
</div>
);

View File

@@ -5,7 +5,8 @@ import { generatePath, useRouteMatch } from 'react-router';
import { getConfig } from '@edx/frontend-platform';
import {
CheckCircle, CheckCircleOutline, Delete, Edit, Lock, LockOpen, Pin, Report, Verified, VerifiedOutline,
CheckCircle, CheckCircleOutline, Close, Delete, Done,
Edit, Lock, LockOpen, Pin, Report, Verified, VerifiedOutline,
} from '@edx/paragon/icons';
import { InsertLink } from '../components/icons';
@@ -173,6 +174,20 @@ export const ACTIONS_LIST = [
label: messages.deleteAction,
conditions: { canDelete: true },
},
{
id: 'accept-review',
action: ContentActions.ACCEPT_REVIEW,
icon: Done,
label: messages.acceptReview,
conditions: { reviewStatus: 'PENDING' },
},
{
id: 'reject-review',
action: ContentActions.REJECT_REVIEW,
icon: Close,
label: messages.rejectReview,
conditions: { reviewStatus: 'PENDING' },
},
];
export function useActions(content) {

View File

@@ -502,3 +502,19 @@ header {
padding-left: 1rem;
}
}
.icon-size-24 {
width: 1.5rem;
height: 1.5rem;
}
.actions-dropdown-item {
padding: 12px 16px;
height: 48px;
width: 304px;
}
.font-xl {
font-size: 18px;
line-height: 28px;
}