Compare commits

...

14 Commits

Author SHA1 Message Date
edX requirements bot
007972f61a feat: Add package-lock file version check 2022-04-29 08:47:26 -04:00
Muhammad Adeel Tajamul
4fa31fedbb Merge pull request #150 from openedx/inf-132
fix: post summary size fix
2022-04-29 17:31:13 +05:00
adeel.tajamul
3719e08321 fix: post summary size fix 2022-04-29 16:54:02 +05:00
Muhammad Adeel Tajamul
e5886c0e04 Merge pull request #151 from openedx/inf-181
fix: loading symbol remains after successful
2022-04-29 07:42:05 +05:00
adeel.tajamul
786c278a2d fix: loading symbol remains after successful 2022-04-28 15:22:14 +05:00
Abdurrahman Asad
e247bf859a fix: reported filter is missing for discussion moderator roles (#148)
fix: reported filter is missing for discussion moderator roles
2022-04-27 16:40:44 +05:00
Awais Ansari
3be40852eb fix: make the post, comment, response content images responsive (#147) 2022-04-25 15:12:51 +05:00
Awais Ansari
847a3b25ec fix: display comment actions alert in single row (#146) 2022-04-25 12:35:31 +05:00
Abdurrahman Asad
e2407e53e3 fix: related to field not shown on posts (#145) 2022-04-22 21:00:40 +05:00
Muhammad Adeel Tajamul
a550cfd30b Merge pull request #144 from openedx/inf-158
fix: post summary pinned bar
2022-04-22 15:42:56 +05:00
Muhammad Adeel Tajamul
af670ec1ab Merge pull request #143 from openedx/aansari/INF-156
fix: add validation for edit reason code dropdown
2022-04-22 11:15:00 +05:00
adeel.tajamul
4b7145dccd fix: post summary pinned bar 2022-04-22 08:46:44 +05:00
Awais Ansari
11f98d32a1 refactor: structure the edit reason code condition 2022-04-21 23:53:56 +05:00
Awais Ansari
bea2390b4d fix: add validation for edit reason code dropdown 2022-04-21 23:26:14 +05:00
26 changed files with 314 additions and 123 deletions

View File

@@ -0,0 +1,13 @@
#check package-lock file version
name: Lockfile Version check
on:
push:
branches:
- master
pull_request:
jobs:
version-check:
uses: edx/.github/.github/workflows/lockfileversion-check.yml@master

View File

@@ -0,0 +1,16 @@
import * as React from 'react';
import { _extends } from './common';
export default function QuestionAnswer(props) {
return /* #__PURE__ */React.createElement('svg', _extends({
width: 20,
height: 20,
viewBox: '0 0 20 20',
fill: 'none',
xmlns: 'http://www.w3.org/2000/svg',
}, props), /* #__PURE__ */React.createElement('path', {
d: 'M16.7371 4.00002H14.2371V11.5H3.4038V14H13.4038L16.7371 17.3334V4.00002ZM12.5705 9.83335V0.666687H0.0704651V13.1667L3.4038 9.83335H12.5705Z',
fill: 'currentColor',
}));
}

View File

@@ -0,0 +1,16 @@
import * as React from 'react';
import { _extends } from './common';
export default function QuestionAnswerOutline(props) {
return /* #__PURE__ */React.createElement('svg', _extends({
width: 20,
height: 20,
viewBox: '0 0 20 20',
fill: 'none',
xmlns: 'http://www.w3.org/2000/svg',
}, props), /* #__PURE__ */React.createElement('path', {
d: 'M 16.7371 4 H 14.2371 V 11.5 H 3.4038 V 14 H 13.4038 L 16.7371 17.3334 V 4 Z M 12.5705 9.8333 V 0.6667 H 0.0705 V 13.1667 L 3.4038 9.8333 H 12.5705 Z M 11.465 8.618 H 1.038 V 1.683 H 11.465Z',
fill: 'currentColor',
}));
}

View File

@@ -0,0 +1,16 @@
import * as React from 'react';
import { _extends } from './common';
export default function StarFilled(props) {
return /* #__PURE__ */React.createElement('svg', _extends({
width: 20,
height: 20,
viewBox: '0 0 20 20',
fill: 'none',
xmlns: 'http://www.w3.org/2000/svg',
}, props), /* #__PURE__ */React.createElement('path', {
d: 'M8.4038 13.3917L13.5538 16.5L12.1871 10.6417L16.7371 6.70002L10.7455 6.19169L8.4038 0.666687L6.06213 6.19169L0.0704651 6.70002L4.62047 10.6417L3.2538 16.5L8.4038 13.3917Z',
fill: 'currentColor',
}));
}

View File

@@ -0,0 +1,16 @@
import * as React from 'react';
import { _extends } from './common';
export default function StarOutline(props) {
return /* #__PURE__ */React.createElement('svg', _extends({
width: 20,
height: 20,
viewBox: '0 0 20 20',
fill: 'none',
xmlns: 'http://www.w3.org/2000/svg',
}, props), /* #__PURE__ */React.createElement('path', {
d: 'M16.7371 6.69999L10.7455 6.18332L8.4038 0.666656L6.06213 6.19166L0.0704651 6.69999L4.62047 10.6417L3.2538 16.5L8.4038 13.3917L13.5538 16.5L12.1955 10.6417L16.7371 6.69999ZM8.4038 11.8333L5.27047 13.725L6.1038 10.1583L3.33713 7.75832L6.98713 7.44166L8.4038 4.08332L9.8288 7.44999L13.4788 7.76666L10.7121 10.1667L11.5455 13.7333L8.4038 11.8333Z',
fill: 'currentColor',
}));
}

View File

@@ -0,0 +1,16 @@
import * as React from 'react';
import { _extends } from './common';
export default function ThumbUpFilled(props) {
return /* #__PURE__ */React.createElement('svg', _extends({
width: 20,
height: 20,
viewBox: '0 0 20 20',
fill: 'none',
xmlns: 'http://www.w3.org/2000/svg',
}, props), /* #__PURE__ */React.createElement('path', {
d: 'M11.2122 0.833344L5.23715 6.81668V17.5H15.4955L18.5705 10.3333V6.66668H11.6455L12.5788 2.18334L11.2122 0.833344ZM0.237152 7.50001H3.57049V17.5H0.237152V7.50001Z',
fill: 'currentColor',
}));
}

View File

@@ -0,0 +1,16 @@
import * as React from 'react';
import { _extends } from './common';
export default function ThumbUpOutline(props) {
return /* #__PURE__ */React.createElement('svg', _extends({
width: 20,
height: 20,
viewBox: '0 0 20 20',
fill: 'none',
xmlns: 'http://www.w3.org/2000/svg',
}, props), /* #__PURE__ */React.createElement('path', {
d: 'M18.5705 6.66668V10.3333L15.4955 17.5L5.23715 17.5L5.23715 6.81668L11.2122 0.833344L12.5788 2.18334L11.6455 6.66668L18.5705 6.66668ZM6.90382 7.50834L6.90382 15.8333L14.3955 15.8333L16.9038 9.99168V8.33334L9.59548 8.33334L10.5205 3.88334L6.90382 7.50834Z M3.57049 17.5H0.237152L0.237152 7.50001H3.57049L3.57049 17.5Z',
fill: 'currentColor',
}));
}

View File

@@ -0,0 +1,21 @@
/* eslint-disable no-underscore-dangle */
/* eslint-disable no-func-assign */
/* eslint-disable prefer-object-spread */
/* eslint-disable prefer-rest-params */
/* eslint-disable no-param-reassign */
/* eslint-disable no-restricted-syntax */
// eslint-disable-next-line import/prefer-default-export
export function _extends() {
_extends = Object.assign || function (target) {
for (let i = 1; i < arguments.length; i++) {
const source = arguments[i];
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
return _extends.apply(this, arguments);
}

View File

@@ -0,0 +1,6 @@
export { default as QuestionAnswer } from './QuestionAnswer';
export { default as QuestionAnswerOutline } from './QuestionAnswerOutline';
export { default as StarFilled } from './StarFilled';
export { default as StarOutline } from './StarOutline';
export { default as ThumbUpFilled } from './ThumbUpFilled';
export { default as ThumbUpOutline } from './ThumbUpOutline';

View File

@@ -58,19 +58,21 @@ function normaliseCourseBlocks({
} else {
blocks[verticalId].children?.forEach(discussionId => {
const discussion = camelCaseObject(blocks[discussionId]);
const { topicId } = discussion.studentViewData;
blockData[discussionId] = discussion;
// Add this topic id to the list of topics for the current chapter, sequential, and vertical
chapterData.topics.push(topicId);
blockData[sequentialId].topics.push(topicId);
blockData[verticalId].topics.push(topicId);
// Store the topic's context in the course in a map
topics[topicId] = {
chapterName: blockData[chapterId].displayName,
verticalName: blockData[sequentialId].displayName,
unitName: blockData[verticalId].displayName,
unitLink: blockData[verticalId].lmsWebUrl,
};
const { topicId } = discussion.studentViewData || {};
if (topicId) {
blockData[discussionId] = discussion;
// Add this topic id to the list of topics for the current chapter, sequential, and vertical
chapterData.topics.push(topicId);
blockData[sequentialId].topics.push(topicId);
blockData[verticalId].topics.push(topicId);
// Store the topic's context in the course in a map
topics[topicId] = {
chapterName: blockData[chapterId].displayName,
verticalName: blockData[sequentialId].displayName,
unitName: blockData[verticalId].displayName,
unitLink: blockData[verticalId].lmsWebUrl,
};
}
});
}
});

View File

@@ -74,7 +74,7 @@ function Comment({
<CommentEditor comment={comment} onCloseEditor={() => setEditing(false)} />
)
// eslint-disable-next-line react/no-danger
: <div className="comment-body px-2" dangerouslySetInnerHTML={{ __html: comment.renderedBody }} />}
: <div className="comment-body px-2" id="comment" dangerouslySetInnerHTML={{ __html: comment.renderedBody }} />}
<CommentIcons
comment={comment}
following={comment.following}

View File

@@ -10,6 +10,7 @@ import { AppContext } from '@edx/frontend-platform/react';
import { Button, Form, StatefulButton } from '@edx/paragon';
import { TinyMCEEditor } from '../../../components';
import FormikErrorFeedback from '../../../components/FormikErrorFeedback';
import { useDispatchWithState } from '../../../data/hooks';
import { selectModerationSettings, selectUserIsPrivileged } from '../../data/selectors';
import { formikCompatibleHandler, isFormikFieldInvalid } from '../../utils';
@@ -22,14 +23,38 @@ function CommentEditor({
onCloseEditor,
edit,
}) {
const editorRef = useRef(null);
const { authenticatedUser } = useContext(AppContext);
const userIsPrivileged = useSelector(selectUserIsPrivileged);
const { reasonCodesEnabled, editReasons } = useSelector(selectModerationSettings);
const [submitting, dispatch] = useDispatchWithState();
const editorRef = useRef(null);
const canDisplayEditReason = (reasonCodesEnabled && userIsPrivileged
&& edit && comment.author !== authenticatedUser.username
);
const editReasonCodeValidation = canDisplayEditReason && {
editReasonCode: Yup.string().required(intl.formatMessage(messages.editReasonCodeError)),
};
const validationSchema = Yup.object().shape({
comment: Yup.string()
.required(),
...editReasonCodeValidation,
});
const initialValues = {
comment: comment.rawBody,
editReasonCode: comment?.lastEdit?.reasonCode || '',
};
const saveUpdatedComment = async (values) => {
if (comment.id) {
await dispatch(editComment(comment.id, values));
const payload = {
...values,
editReasonCode: values.editReasonCode || undefined,
};
await dispatch(editComment(comment.id, payload));
} else {
await dispatch(addComment(values.comment, comment.threadId, comment.parentId));
}
@@ -42,17 +67,11 @@ function CommentEditor({
// The editorId is used to autosave contents to localstorage. This format means that the autosave is scoped to
// the current comment id, or the current comment parent or the curren thread.
const editorId = `comment-editor-${comment.id || comment.parentId || comment.threadId}`;
return (
<Formik
initialValues={{ comment: comment.rawBody }}
validationSchema={Yup.object()
.shape({
comment: Yup.string()
.required(),
editReasonCode: Yup.string()
.nullable()
.default(undefined),
})}
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={saveUpdatedComment}
>
{({
@@ -64,12 +83,13 @@ function CommentEditor({
handleChange,
}) => (
<Form onSubmit={handleSubmit}>
{(reasonCodesEnabled
&& userIsPrivileged
&& comment.author !== authenticatedUser.username
&& edit
) && (
<Form.Group>
{canDisplayEditReason && (
<Form.Group
isInvalid={isFormikFieldInvalid('editReasonCode', {
errors,
touched,
})}
>
<Form.Control
name="editReasonCode"
className="mt-2"
@@ -88,6 +108,7 @@ function CommentEditor({
<option key={code} value={code}>{label}</option>
))}
</Form.Control>
<FormikErrorFeedback name="editReasonCode" />
</Form.Group>
)}
<TinyMCEEditor
@@ -142,6 +163,7 @@ CommentEditor.propTypes = {
parentId: PropTypes.string,
rawBody: PropTypes.string,
author: PropTypes.string,
lastEdit: PropTypes.object,
}).isRequired,
onCloseEditor: PropTypes.func.isRequired,
intl: intlShape.isRequired,

View File

@@ -45,11 +45,17 @@ function Reply({
hideDeleteConfirmation();
}}
/>
<div className="d-flex flex-fill ml-6">
<AlertBanner postType={null} content={reply} intl={intl} />
</div>
<div className="d-flex">
<div className="d-flex">
<div className="d-flex mx-3 invisible">
<Avatar className="m-2" />
</div>
<div className="w-100">
<AlertBanner postType={null} content={reply} intl={intl} />
</div>
</div>
<div className="d-flex">
<div className="d-flex m-3">
<Avatar
className={`m-2 ${colorClass && `border-${colorClass}`}`}
@@ -72,9 +78,8 @@ function Reply({
{isEditing
? <CommentEditor comment={reply} onCloseEditor={() => setEditing(false)} />
// eslint-disable-next-line react/no-danger
: <div dangerouslySetInnerHTML={{ __html: reply.renderedBody }} />}
: <div id="reply" dangerouslySetInnerHTML={{ __html: reply.renderedBody }} />}
</div>
</div>
<div className="text-gray-500 align-self-end mt-2" title={reply.createdAt}>
{timeago.format(reply.createdAt, intl.locale)}

View File

@@ -26,7 +26,7 @@ const messages = defineMessages({
},
endorsedResponseCount: {
id: 'discussions.comments.comment.endorsedResponseCount',
defaultMessage: `{num, plural,
defaultMessage: `{num, plural,
=0 {No endorsed responses}
one {Showing # endorsed response}
other {Showing # endorsed responses}
@@ -147,6 +147,11 @@ const messages = defineMessages({
defaultMessage: 'Reason for editing',
description: 'Label for field visible to moderators that allows them to select a reason for editing another user\'s response',
},
editReasonCodeError: {
id: 'discussions.editor.posts.editReasonCode.error',
defaultMessage: 'Select reason for editing',
description: 'Error message visible to moderators when they submit the post/response/comment without select reason for editing',
},
editedBy: {
id: 'discussions.comment.comments.editedBy',
defaultMessage: 'Edited by',

View File

@@ -56,7 +56,7 @@ function AlertBanner({
</Alert>
)}
{content.abuseFlagged && (
<Alert icon={Error} variant="danger" className="p-3 m-0 shadow-none mb-1 flex-fill">
<Alert icon={Error} variant="danger" className="p-3 m-0 shadow-none my-1 flex-fill">
{intl.formatMessage(messages.abuseFlaggedMessage)}
</Alert>
)}

View File

@@ -64,6 +64,7 @@ const learnersSlice = createSlice({
nextPage: (payload.page < payload.pagination.numPages) ? payload.page + 1 : null,
totalPages: payload.pagination.numPages,
};
state.status = RequestStatus.SUCCESSFUL;
},
fetchUserCommentsDenied: (state) => {
state.status = RequestStatus.DENIED;

View File

@@ -58,7 +58,7 @@ function PostsList({ posts, topics }) {
// Add a spacing after the group of pinned posts
return (
<React.Fragment key={post.id}>
<div className="p-1 bg-light-300" />
<div className="p-1 bg-light-400" />
<PostLink post={post} key={post.id} />
</React.Fragment>
);

View File

@@ -85,6 +85,7 @@ describe('PostsView', () => {
store = initializeStore({
blocks: { blocks: { 'test-usage-key': { topics: ['some-topic-2', 'some-topic-0'] } } },
config: { userIsPrivileged: true },
});
Factory.resetAll();
axiosMock = new MockAdapter(getAuthenticatedHttpClient());

View File

@@ -2,6 +2,7 @@ import React, { useContext, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import { Formik } from 'formik';
import { isEmpty } from 'lodash';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory, useLocation, useParams } from 'react-router-dom';
import * as Yup from 'yup';
@@ -83,14 +84,21 @@ function PostEditor({
const nonCoursewareTopics = useSelector(selectNonCoursewareTopics);
const nonCoursewareIds = useSelector(selectNonCoursewareIds);
const coursewareTopics = useSelector(selectCoursewareTopics);
const {
allowAnonymous,
allowAnonymousToPeers,
} = useSelector(selectAnonymousPostingConfig);
const cohorts = useSelector(selectCourseCohorts);
const post = useSelector(selectThread(postId));
const userIsPrivileged = useSelector(selectUserIsPrivileged);
const settings = useSelector(selectDivisionSettings);
const { allowAnonymous, allowAnonymousToPeers } = useSelector(selectAnonymousPostingConfig);
const { reasonCodesEnabled, editReasons } = useSelector(selectModerationSettings);
const canDisplayEditReason = (reasonCodesEnabled && editExisting
&& userIsPrivileged && post.author !== authenticatedUser.username
);
const editReasonCodeValidation = canDisplayEditReason && {
editReasonCode: Yup.string().required(intl.formatMessage(messages.editReasonCodeError)),
};
const canSelectCohort = (tId) => {
// If the user isn't privileged, they can't edit the cohort.
// If the topic is being edited the cohort can't be changed.
@@ -164,60 +172,48 @@ function PostEditor({
</div>
);
}
let initialValues = {
postType: 'discussion',
topic: topicId || nonCoursewareTopics?.[0]?.id,
title: '',
comment: '',
follow: true,
anonymous: false,
anonymousToPeers: false,
const initialValues = {
postType: post?.type || 'discussion',
topic: post?.topicId || topicId || nonCoursewareTopics?.[0]?.id,
title: post?.title || '',
comment: post?.rawBody || '',
follow: isEmpty(post?.following) ? true : post?.following,
anonymous: allowAnonymous ? false : undefined,
anonymousToPeers: allowAnonymousToPeers ? false : undefined,
editReasonCode: post?.lastEdit?.reasonCode || '',
};
if (editExisting) {
initialValues = {
postType: post.type,
topic: post.topicId,
title: post.title,
comment: post.rawBody,
follow: (post.following === null || post.following === undefined) ? true : post.following,
anonymous: allowAnonymous ? false : undefined,
anonymousToPeers: allowAnonymousToPeers ? false : undefined,
};
}
const validationSchema = Yup.object().shape({
postType: Yup.mixed()
.oneOf(['discussion', 'question']),
topic: Yup.string()
.required(),
title: Yup.string()
.required(intl.formatMessage(messages.titleError)),
comment: Yup.string()
.required(intl.formatMessage(messages.commentError)),
follow: Yup.bool()
.default(true),
anonymous: Yup.bool()
.default(false)
.nullable(),
anonymousToPeers: Yup.bool()
.default(false)
.nullable(),
cohort: Yup.string()
.nullable()
.default(null),
...editReasonCodeValidation,
});
const postEditorId = `post-editor-${editExisting ? postId : 'new'}`;
const { reasonCodesEnabled, editReasons } = useSelector(selectModerationSettings);
return (
<Formik
enableReinitialize
initialValues={initialValues}
validationSchema={Yup.object()
.shape({
postType: Yup.mixed()
.oneOf(['discussion', 'question']),
topic: Yup.string()
.required(),
title: Yup.string()
.required(intl.formatMessage(messages.titleError)),
comment: Yup.string()
.required(intl.formatMessage(messages.commentError)),
follow: Yup.bool()
.default(true),
anonymous: Yup.bool()
.default(false)
.nullable(),
anonymousToPeers: Yup.bool()
.default(false)
.nullable(),
cohort: Yup.string()
.nullable()
.default(null),
editReasonCode: Yup.string()
.nullable()
.default(null),
})}
validationSchema={validationSchema}
onSubmit={submitForm}
>{
({
@@ -304,7 +300,7 @@ function PostEditor({
<div className="border-bottom my-1" />
<div className="d-flex flex-row py-2 mt-4 justify-content-between">
<Form.Group
className="d-flex flex-fill"
className="w-100"
isInvalid={isFormikFieldInvalid('title', {
errors,
touched,
@@ -321,27 +317,31 @@ function PostEditor({
/>
<FormikErrorFeedback name="title" />
</Form.Group>
{(reasonCodesEnabled
&& editExisting
&& userIsPrivileged
&& post.author !== authenticatedUser.username) && (
<Form.Group className="d-flex flex-fill">
<Form.Control
name="editReasonCode"
className="ml-4"
as="select"
value={values.editReasonCode}
onChange={handleChange}
onBlur={handleBlur}
aria-describedby="editReasonCodeInput"
floatingLabel={intl.formatMessage(messages.editReasonCode)}
>
<option key="empty" value="">---</option>
{editReasons.map(({ code, label }) => (
<option key={code} value={code}>{label}</option>
))}
</Form.Control>
</Form.Group>
{canDisplayEditReason && (
<Form.Group
className="w-100"
isInvalid={isFormikFieldInvalid('editReasonCode', {
errors,
touched,
})}
>
<Form.Control
name="editReasonCode"
className="m-0"
as="select"
value={values.editReasonCode}
onChange={handleChange}
onBlur={handleBlur}
aria-describedby="editReasonCodeInput"
floatingLabel={intl.formatMessage(messages.editReasonCode)}
>
<option key="empty" value="">---</option>
{editReasons.map(({ code, label }) => (
<option key={code} value={code}>{label}</option>
))}
</Form.Control>
<FormikErrorFeedback name="editReasonCode" />
</Form.Group>
)}
</div>
<div className="py-2">

View File

@@ -106,6 +106,11 @@ const messages = defineMessages({
defaultMessage: 'Reason for editing',
description: 'Label for field visible to moderators that allows them to select a reason for editing another user\'s post',
},
editReasonCodeError: {
id: 'discussions.editor.posts.editReasonCode.error',
defaultMessage: 'Select reason for editing',
description: 'Error message visible to moderators when they submit the post/response/comment without select reason for editing',
},
});
export default messages;

View File

@@ -1,17 +1,17 @@
import React, { useContext, useState } from 'react';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { AppContext } from '@edx/frontend-platform/react';
import { Collapsible, Form, Icon } from '@edx/paragon';
import { Check, Sort } from '@edx/paragon/icons';
import {
PostsStatusFilter, ThreadOrdering, ThreadType,
} from '../../../data/constants';
import { selectUserIsPrivileged } from '../../data/selectors';
import { setPostsTypeFilter, setSortedBy, setStatusFilter } from '../data';
import { selectThreadFilters, selectThreadSorting } from '../data/selectors';
import messages from './messages';
@@ -44,8 +44,8 @@ function PostFilterBar({
filterSelfPosts,
intl,
}) {
const { authenticatedUser } = useContext(AppContext);
const dispatch = useDispatch();
const userIsPrivileged = useSelector(selectUserIsPrivileged);
const currentSorting = useSelector(selectThreadSorting());
const currentFilters = useSelector(selectThreadFilters());
const [isOpen, setOpen] = useState(false);
@@ -147,7 +147,7 @@ function PostFilterBar({
value={PostsStatusFilter.FOLLOWING}
selected={currentFilters.status}
/>
{authenticatedUser.administrator
{userIsPrivileged
&& (
<ActionItem
id="status-reported"

View File

@@ -5,8 +5,8 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
Icon, IconButton, OverlayTrigger, Tooltip,
} from '@edx/paragon';
import { ThumbUpFilled, ThumbUpOutline } from '@edx/paragon/icons';
import { ThumbUpFilled, ThumbUpOutline } from '../../../components/icons';
import messages from './messages';
function LikeButton({
@@ -34,7 +34,7 @@ function LikeButton({
>
<IconButton
onClick={handleClick}
className="p-3 mr-2"
className="p-3 mr-2 mt-1"
alt="Like"
iconAs={Icon}
size="inline"

View File

@@ -75,7 +75,7 @@ function Post({
<PostHeader post={post} actionHandlers={actionHandlers} />
<div className="d-flex my-2 text-break">
{/* eslint-disable-next-line react/no-danger */}
<div dangerouslySetInnerHTML={{ __html: post.renderedBody }} />
<div id="post" dangerouslySetInnerHTML={{ __html: post.renderedBody }} />
</div>
{topicContext && topic && (
<div className="border p-3 rounded mb-3 mt-2 align-self-start">

View File

@@ -9,9 +9,15 @@ import {
Badge, Icon, IconButton, OverlayTrigger, Tooltip,
} from '@edx/paragon';
import {
Locked, People, QuestionAnswer, QuestionAnswerOutline, StarFilled, StarOutline,
Locked, People,
} from '@edx/paragon/icons';
import {
QuestionAnswer,
QuestionAnswerOutline,
StarFilled,
StarOutline,
} from '../../../components/icons';
import { updateExistingThread } from '../data/thunks';
import LikeButton from './LikeButton';
import messages from './messages';
@@ -45,14 +51,14 @@ function PostFooter({
alt="Follow"
iconAs={Icon}
size="inline"
className="mx-2.5 my-0"
className="mx-2.5 my-0 mt-1.5"
src={post.following ? StarFilled : StarOutline}
/>
</OverlayTrigger>
{preview && post.commentCount > 1
&& (
<>
<Icon src={post.unreadCommentCount ? QuestionAnswer : QuestionAnswerOutline} className="mx-2 my-0" />
<Icon src={post.unreadCommentCount ? QuestionAnswer : QuestionAnswerOutline} className="mx-2 my-0 mt-2" />
<span style={{ minWidth: '2rem' }}>
{post.commentCount}
</span>

View File

@@ -47,7 +47,7 @@ function PostLink({
</div>
)}
<div
className={classNames('d-flex flex-row flex-fill mw-100 p-1 border-primary-500', { 'bg-light-300': post.read })}
className={classNames('d-flex flex-row flex-fill mw-100 p-2.5 border-primary-500', { 'bg-light-300': post.read })}
style={post.id === postId ? {
borderRightWidth: '4px',
borderRightStyle: 'solid',

View File

@@ -5,3 +5,11 @@
$fa-font-path: "~font-awesome/fonts";
@import "~font-awesome/scss/font-awesome";
#post, #comment, #reply {
img {
height: auto;
max-width: 100%;
}
}