Compare commits

..

1 Commits

Author SHA1 Message Date
adeel.tajamul
5f734afbee feat: added recent sort in learners area 2022-11-08 11:15:25 +05:00
66 changed files with 510 additions and 1269 deletions

View File

@@ -8,4 +8,5 @@ openedx-release:
# The openedx-release key is described in OEP-10:
# https://open-edx-proposals.readthedocs.io/en/latest/oep-0010-proc-openedx-releases.html
# The FAQ might also be helpful: https://openedx.atlassian.net/wiki/spaces/COMM/pages/1331268879/Open+edX+Release+FAQ
maybe: true # Delete this "maybe" line when you have decided about Open edX inclusion.
ref: master

View File

@@ -101,7 +101,7 @@ function FilterBar({
<span className="text-primary-700 pr-4">
{intl.formatMessage(messages.sortFilterStatus, {
own: false,
type: selectedFilters.postType,
type: selectedFilters.type,
sort: selectedFilters.orderBy,
status: selectedFilters.status,
cohortType: selectedCohort?.name ? 'group' : 'all',

View File

@@ -32,7 +32,6 @@ import 'tinymce/plugins/lists';
import 'tinymce/plugins/emoticons';
import 'tinymce/plugins/emoticons/js/emojis';
import 'tinymce/plugins/charmap';
import 'tinymce/plugins/paste';
/* eslint import/no-webpack-loader-syntax: off */
// eslint-disable-next-line import/no-unresolved
import edxBrandCss from '!!raw-loader!sass-loader!../index.scss';
@@ -101,13 +100,12 @@ export default function TinyMCEEditor(props) {
skin: false,
menubar: false,
branding: false,
paste_data_images: false,
contextmenu: false,
browser_spellcheck: true,
a11y_advanced_options: true,
autosave_interval: '1s',
autosave_restore_when_empty: false,
plugins: 'autoresize autosave codesample link lists image imagetools code emoticons charmap paste',
plugins: 'autoresize autosave codesample link lists image imagetools code emoticons charmap',
toolbar: 'undo redo'
+ ' | formatselect | bold italic underline'
+ ' | link blockquote openedx_code image'

View File

@@ -90,6 +90,16 @@ export const ThreadOrdering = {
BY_VOTE_COUNT: 'voteCount',
};
/**
* Enum for thread view status filtering.
* @readonly
* @enum {string}
*/
export const ThreadViewStatus = {
UNREAD: 'unread',
UNANSWERED: 'unanswered',
};
/**
* Enum for filtering posts by status.
* @readonly

View File

@@ -1,28 +1,18 @@
import React, { useContext, useEffect, useMemo } from 'react';
import React, { useEffect, useMemo } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import { useParams } from 'react-router';
import { useHistory, useLocation } from 'react-router-dom';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
Button, Icon, IconButton, Spinner,
} from '@edx/paragon';
import { ArrowBack } from '@edx/paragon/icons';
import { Button, Spinner } from '@edx/paragon';
import {
EndorsementStatus, PostsPages, ThreadType,
} from '../../data/constants';
import { EndorsementStatus, ThreadType } from '../../data/constants';
import { useDispatchWithState } from '../../data/hooks';
import { DiscussionContext } from '../common/context';
import { useIsOnDesktop } from '../data/hooks';
import { EmptyPage } from '../empty-posts';
import { Post } from '../posts';
import { selectThread } from '../posts/data/selectors';
import { fetchThread, markThreadAsRead } from '../posts/data/thunks';
import { discussionsPath, filterPosts } from '../utils';
import { filterPosts } from '../utils';
import { selectThreadComments, selectThreadCurrentPage, selectThreadHasMorePages } from './data/selectors';
import { fetchThreadComments } from './data/thunks';
import { Comment, ResponseEditor } from './comment';
@@ -134,74 +124,23 @@ DiscussionCommentsView.propTypes = {
};
function CommentsView({ intl }) {
const [isLoading, submitDispatch] = useDispatchWithState();
const { postId } = useParams();
const thread = usePost(postId);
const history = useHistory();
const location = useLocation();
const isOnDesktop = useIsOnDesktop();
const {
courseId, learnerUsername, category, topicId, page, inContext,
} = useContext(DiscussionContext);
useEffect(() => {
if (!thread) { submitDispatch(fetchThread(postId, courseId, true)); }
}, [postId]);
const dispatch = useDispatch();
if (!thread) {
if (!isLoading) {
return (
<EmptyPage title={intl.formatMessage(messages.noThreadFound)} />
);
}
dispatch(fetchThread(postId, true));
return (
<Spinner animation="border" variant="primary" data-testid="loading-indicator" />
);
}
return (
<>
{!isOnDesktop && (
inContext ? (
<>
<div className="px-4 py-1.5 bg-white">
<Button
variant="plain"
className="px-0 font-weight-light text-primary-500"
iconBefore={ArrowBack}
onClick={() => history.push(discussionsPath(PostsPages[page], {
courseId, learnerUsername, category, topicId,
})(location))}
size="sm"
>
{intl.formatMessage(messages.backAlt)}
</Button>
</div>
<div className="border-bottom border-light-400" />
</>
) : (
<IconButton
src={ArrowBack}
iconAs={Icon}
style={{ padding: '18px' }}
size="inline"
className="ml-4 mt-4"
onClick={() => history.push(discussionsPath(PostsPages[page], {
courseId, learnerUsername, category, topicId,
})(location))}
alt={intl.formatMessage(messages.backAlt)}
/>
)
)}
<div className={classNames('discussion-comments d-flex flex-column card', {
'm-4 p-4.5': !inContext,
'p-4 rounded-0 border-0 mb-4': inContext,
})}
>
<div className="discussion-comments d-flex flex-column m-4 p-4.5 card">
<Post post={thread} />
{!thread.closed && <ResponseEditor postId={postId} /> }
</div>
{thread.type === ThreadType.DISCUSSION && (
{thread.type === ThreadType.DISCUSSION
&& (
<DiscussionCommentsView
postId={postId}
intl={intl}
@@ -209,7 +148,7 @@ function CommentsView({ intl }) {
endorsed={EndorsementStatus.DISCUSSION}
isClosed={thread.closed}
/>
)}
)}
{thread.type === ThreadType.QUESTION && (
<>
<DiscussionCommentsView

View File

@@ -102,7 +102,7 @@ function renderComponent(postId) {
}
describe('CommentsView', () => {
beforeEach(() => {
beforeEach(async () => {
initializeMockApp({
authenticatedUser: {
userId: 3,
@@ -147,7 +147,7 @@ describe('CommentsView', () => {
)];
});
executeThunk(fetchThreads(courseId), store.dispatch, store.getState);
await executeThunk(fetchThreads(courseId), store.dispatch, store.getState);
mockAxiosReturnPagedComments();
mockAxiosReturnPagedCommentsResponses();
});
@@ -381,7 +381,6 @@ describe('CommentsView', () => {
});
expect(testLocation.pathname).toBe(`/${courseId}/posts/${discussionPostId}/edit`);
});
it('should allow pinning the post', async () => {
renderComponent(discussionPostId);
await act(async () => {
@@ -395,7 +394,6 @@ describe('CommentsView', () => {
});
assertLastUpdateData({ pinned: false });
});
it('should allow reporting the post', async () => {
renderComponent(discussionPostId);
await act(async () => {
@@ -407,11 +405,6 @@ describe('CommentsView', () => {
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /report/i }));
});
expect(screen.queryByRole('dialog', { name: /Report \w+/i, exact: false })).toBeInTheDocument();
await act(async () => {
fireEvent.click(screen.queryByRole('button', { name: /Confirm/i }));
});
expect(screen.queryByRole('dialog', { name: /Report \w+/i, exact: false })).not.toBeInTheDocument();
assertLastUpdateData({ abuse_flagged: true });
});
@@ -430,8 +423,12 @@ describe('CommentsView', () => {
expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject({ voted: true });
});
it('handles endorsing comments', async () => {
it.each([
['endorsing comments', 'Endorse', { endorsed: true }],
['reporting comments', 'Report', { abuse_flagged: true }],
])('handles %s', async (label, buttonLabel, patchData) => {
renderComponent(discussionPostId);
// Wait for the content to load
await screen.findByText('comment number 7', { exact: false });
@@ -441,45 +438,20 @@ describe('CommentsView', () => {
await act(async () => {
fireEvent.click(actionButtons[1]);
});
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /Endorse/i }));
fireEvent.click(screen.getByRole('button', { name: buttonLabel }));
});
expect(axiosMock.history.patch).toHaveLength(2);
expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject({ endorsed: true });
});
it('handles reporting comments', async () => {
renderComponent(discussionPostId);
// Wait for the content to load
await screen.findByText('comment number 7', { exact: false });
// There should be three buttons, one for the post, the second for the
// comment and the third for a response to that comment
const actionButtons = screen.queryAllByRole('button', { name: /actions menu/i });
await act(async () => {
fireEvent.click(actionButtons[1]);
});
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /Report/i }));
});
expect(screen.queryByRole('dialog', { name: /Report \w+/i, exact: false })).toBeInTheDocument();
await act(async () => {
fireEvent.click(screen.queryByRole('button', { name: /Confirm/i }));
});
expect(screen.queryByRole('dialog', { name: /Report \w+/i, exact: false })).not.toBeInTheDocument();
expect(axiosMock.history.patch).toHaveLength(2);
expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject({ abuse_flagged: true });
expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject(patchData);
});
});
describe('for discussion thread', () => {
const findLoadMoreCommentsButton = () => screen.findByTestId('load-more-comments');
it('shown post not found when post id does not belong to course', async () => {
it('shown spinner when post isn\'t loaded', async () => {
renderComponent('unloaded-id');
expect(await screen.findByText('Thread not found', { exact: true }))
expect(await screen.findByTestId('loading-indicator'))
.toBeInTheDocument();
});
@@ -652,8 +624,12 @@ describe('CommentsView', () => {
expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject({ voted: true });
});
it('handles endorsing comments', async () => {
it.each([
['endorsing comments', 'Endorse', { endorsed: true }],
['reporting comments', 'Report', { abuse_flagged: true }],
])('handles %s', async (label, buttonLabel, patchData) => {
renderComponent(discussionPostId);
// Wait for the content to load
await screen.findByText('comment number 7', { exact: false });
@@ -663,36 +639,11 @@ describe('CommentsView', () => {
await act(async () => {
fireEvent.click(actionButtons[1]);
});
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /Endorse/i }));
fireEvent.click(screen.getByRole('button', { name: buttonLabel }));
});
expect(axiosMock.history.patch).toHaveLength(2);
expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject({ endorsed: true });
});
it('handles reporting comments', async () => {
renderComponent(discussionPostId);
// Wait for the content to load
await screen.findByText('comment number 7', { exact: false });
// There should be three buttons, one for the post, the second for the
// comment and the third for a response to that comment
const actionButtons = screen.queryAllByRole('button', { name: /actions menu/i });
await act(async () => {
fireEvent.click(actionButtons[1]);
});
await act(async () => {
fireEvent.click(screen.getByRole('button', { name: /Report/i }));
});
expect(screen.queryByRole('dialog', { name: /Report \w+/i, exact: false })).toBeInTheDocument();
await act(async () => {
fireEvent.click(screen.queryByRole('button', { name: /Confirm/i }));
});
expect(screen.queryByRole('dialog', { name: /Report \w+/i, exact: false })).not.toBeInTheDocument();
expect(axiosMock.history.patch).toHaveLength(2);
expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject({ abuse_flagged: true });
expect(JSON.parse(axiosMock.history.patch[1].data)).toMatchObject(patchData);
});
});

View File

@@ -1,4 +1,4 @@
import React, { useContext, useEffect, useState } from 'react';
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
@@ -9,10 +9,10 @@ import { Button, useToggle } from '@edx/paragon';
import HTMLLoader from '../../../components/HTMLLoader';
import { ContentActions } from '../../../data/constants';
import { AlertBanner, Confirmation, EndorsedAlertBanner } from '../../common';
import { DiscussionContext } from '../../common/context';
import { useUserCanAddThreadInBlackoutDate } from '../../data/hooks';
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';
@@ -35,14 +35,11 @@ function Comment({
const inlineReplies = useSelector(selectCommentResponses(comment.id));
const [isEditing, setEditing] = useState(false);
const [isDeleting, showDeleteConfirmation, hideDeleteConfirmation] = useToggle(false);
const [isReporting, showReportConfirmation, hideReportConfirmation] = useToggle(false);
const [isReplying, setReplying] = useState(false);
const hasMorePages = useSelector(selectCommentHasMorePages(comment.id));
const currentPage = useSelector(selectCommentCurrentPage(comment.id));
const userCanAddThreadInBlackoutDate = useUserCanAddThreadInBlackoutDate();
const {
courseId,
} = useContext(DiscussionContext);
const blackoutDateRange = useSelector(selectBlackoutDate);
useEffect(() => {
// If the comment has a parent comment, it won't have any children, so don't fetch them.
if (hasChildren && !currentPage && showFullThread) {
@@ -50,32 +47,14 @@ function Comment({
}
}, [comment.id]);
const handleAbusedFlag = () => {
if (comment.abuseFlagged) {
dispatch(editComment(comment.id, { flagged: !comment.abuseFlagged }));
} else {
showReportConfirmation();
}
};
const handleDeleteConfirmation = () => {
dispatch(removeComment(comment.id));
hideDeleteConfirmation();
};
const handleReportConfirmation = () => {
dispatch(editComment(comment.id, { flagged: !comment.abuseFlagged }));
hideReportConfirmation();
};
const actionHandlers = {
[ContentActions.EDIT_CONTENT]: () => setEditing(true),
[ContentActions.ENDORSE]: async () => {
await dispatch(editComment(comment.id, { endorsed: !comment.endorsed }, ContentActions.ENDORSE));
await dispatch(fetchThread(comment.threadId, courseId));
await dispatch(fetchThread(comment.threadId));
},
[ContentActions.DELETE]: showDeleteConfirmation,
[ContentActions.REPORT]: () => handleAbusedFlag(),
[ContentActions.REPORT]: () => dispatch(editComment(comment.id, { flagged: !comment.abuseFlagged })),
};
const handleLoadMoreComments = () => (
@@ -85,25 +64,16 @@ function Comment({
return (
<div className={classNames({ 'py-2 my-3': showFullThread })}>
<div className="d-flex flex-column card" data-testid={`comment-${comment.id}`} role="listitem">
<Confirmation
<DeleteConfirmation
isOpen={isDeleting}
title={intl.formatMessage(messages.deleteResponseTitle)}
description={intl.formatMessage(messages.deleteResponseDescription)}
onClose={hideDeleteConfirmation}
comfirmAction={handleDeleteConfirmation}
closeButtonVaraint="tertiary"
confirmButtonText={intl.formatMessage(messages.deleteConfirmationDelete)}
onDelete={() => {
dispatch(removeComment(comment.id));
hideDeleteConfirmation();
}}
/>
{!comment.abuseFlagged && (
<Confirmation
isOpen={isReporting}
title={intl.formatMessage(messages.reportResponseTitle)}
description={intl.formatMessage(messages.reportResponseDescription)}
onClose={hideReportConfirmation}
comfirmAction={handleReportConfirmation}
confirmButtonVariant="danger"
/>
)}
<EndorsedAlertBanner postType={postType} content={comment} />
<div className="d-flex flex-column p-4.5">
<AlertBanner content={comment} />
@@ -157,18 +127,18 @@ function Comment({
/>
) : (
<>
{!isClosedPost && userCanAddThreadInBlackoutDate
{(!isClosedPost && !inBlackoutDateRange(blackoutDateRange))
&& (
<Button
className="d-flex flex-grow mt-3 py-2 font-size-14"
variant="outline-primary"
style={{
lineHeight: '20px',
}}
onClick={() => setReplying(true)}
>
{intl.formatMessage(messages.addComment)}
</Button>
<Button
className="d-flex flex-grow mt-3 py-2 font-size-14"
variant="outline-primary"
style={{
lineHeight: '20px',
}}
onClick={() => setReplying(true)}
>
{intl.formatMessage(messages.addComment)}
</Button>
)}
</>

View File

@@ -5,17 +5,14 @@ import classNames from 'classnames';
import { useSelector } from 'react-redux';
import { injectIntl } from '@edx/frontend-platform/i18n';
import { logError } from '@edx/frontend-platform/logging';
import {
Avatar, Icon,
} from '@edx/paragon';
import { Avatar, Icon } from '@edx/paragon';
import { CheckCircle, Verified } from '@edx/paragon/icons';
import { AvatarOutlineAndLabelColors, EndorsementStatus, ThreadType } from '../../../data/constants';
import { AvatarOutlineAndLabelColors, ThreadType } from '../../../data/constants';
import { AuthorLabel } from '../../common';
import ActionsDropdown from '../../common/ActionsDropdown';
import { useAlertBannerVisible } from '../../data/hooks';
import { selectAuthorAvatars } from '../../posts/data/selectors';
import { useActions } from '../../utils';
import { commentShape } from './proptypes';
function CommentHeader({
@@ -27,20 +24,6 @@ function CommentHeader({
const colorClass = AvatarOutlineAndLabelColors[comment.authorLabel];
const hasAnyAlert = useAlertBannerVisible(comment);
const actions = useActions({
...comment,
postType,
});
const actionIcons = actions.find(({ action }) => action === EndorsementStatus.ENDORSED);
const handleIcons = (action) => {
const actionFunction = actionHandlers[action];
if (actionFunction) {
actionFunction();
} else {
logError(`Unknown or unimplemented action ${action}`);
}
};
return (
<div className={classNames('d-flex flex-row justify-content-between', {
'mt-2': hasAnyAlert,
@@ -64,23 +47,11 @@ function CommentHeader({
/>
</div>
<div className="d-flex align-items-center">
{actionIcons && (
<span className="btn-icon btn-icon-sm mr-1 align-items-center">
<Icon
data-testid="check-icon"
onClick={
() => {
handleIcons(actionIcons.action);
}
}
src={actionIcons.icon}
className={['endorse', 'unendorse'].includes(actionIcons.id) ? 'text-dark-500' : 'text-success-500'}
size="sm"
/>
{comment.endorsed && (postType === 'question'
? <Icon src={CheckCircle} className="text-success" data-testid="check-icon" />
: <Icon src={Verified} className="text-dark-500" data-testid="verified-icon" />)}
</span>
)}
<ActionsDropdown
commentOrPost={{
...comment,

View File

@@ -30,7 +30,7 @@ const mockComment = {
author: 'abc123',
authorLabel: 'ABC 123',
endorsed: true,
editableFields: ['endorsed'],
editableFields: [],
};
describe('Comment Header', () => {
@@ -48,7 +48,7 @@ describe('Comment Header', () => {
it('should render verified icon for endorsed discussion posts', () => {
renderComponent(mockComment, 'discussion', {});
expect(screen.queryAllByTestId('check-icon')).toHaveLength(1);
expect(screen.queryAllByTestId('verified-icon')).toHaveLength(1);
});
it('should render check icon for endorsed question posts', () => {
renderComponent(mockComment, 'question', {});

View File

@@ -10,7 +10,7 @@ import { Avatar, useToggle } from '@edx/paragon';
import HTMLLoader from '../../../components/HTMLLoader';
import { AvatarOutlineAndLabelColors, ContentActions } from '../../../data/constants';
import {
ActionsDropdown, AlertBanner, AuthorLabel, Confirmation,
ActionsDropdown, AlertBanner, AuthorLabel, DeleteConfirmation,
} from '../../common';
import timeLocale from '../../common/time-locale';
import { useAlertBannerVisible } from '../../data/hooks';
@@ -29,26 +29,6 @@ function Reply({
const dispatch = useDispatch();
const [isEditing, setEditing] = useState(false);
const [isDeleting, showDeleteConfirmation, hideDeleteConfirmation] = useToggle(false);
const [isReporting, showReportConfirmation, hideReportConfirmation] = useToggle(false);
const handleAbusedFlag = () => {
if (reply.abuseFlagged) {
dispatch(editComment(reply.id, { flagged: !reply.abuseFlagged }));
} else {
showReportConfirmation();
}
};
const handleDeleteConfirmation = () => {
dispatch(removeComment(reply.id));
hideDeleteConfirmation();
};
const handleReportConfirmation = () => {
dispatch(editComment(reply.id, { flagged: !reply.abuseFlagged }));
hideReportConfirmation();
};
const actionHandlers = {
[ContentActions.EDIT_CONTENT]: () => setEditing(true),
[ContentActions.ENDORSE]: () => dispatch(editComment(
@@ -57,7 +37,7 @@ function Reply({
ContentActions.ENDORSE,
)),
[ContentActions.DELETE]: showDeleteConfirmation,
[ContentActions.REPORT]: () => handleAbusedFlag(),
[ContentActions.REPORT]: () => dispatch(editComment(reply.id, { flagged: !reply.abuseFlagged })),
};
const authorAvatars = useSelector(selectAuthorAvatars(reply.author));
const colorClass = AvatarOutlineAndLabelColors[reply.authorLabel];
@@ -65,25 +45,17 @@ function Reply({
return (
<div className="d-flex flex-column mt-4.5" data-testid={`reply-${reply.id}`} role="listitem">
<Confirmation
<DeleteConfirmation
isOpen={isDeleting}
title={intl.formatMessage(messages.deleteCommentTitle)}
description={intl.formatMessage(messages.deleteCommentDescription)}
onClose={hideDeleteConfirmation}
comfirmAction={handleDeleteConfirmation}
closeButtonVaraint="tertiary"
confirmButtonText={intl.formatMessage(messages.deleteConfirmationDelete)}
onDelete={() => {
dispatch(removeComment(reply.id));
hideDeleteConfirmation();
}}
/>
{!reply.abuseFlagged && (
<Confirmation
isOpen={isReporting}
title={intl.formatMessage(messages.reportCommentTitle)}
description={intl.formatMessage(messages.reportCommentDescription)}
onClose={hideReportConfirmation}
comfirmAction={handleReportConfirmation}
confirmButtonVariant="danger"
/>
)}
{hasAnyAlert && (
<div className="d-flex">
<div className="d-flex invisible">

View File

@@ -1,13 +1,14 @@
import React, { useContext, useEffect, useState } from 'react';
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 { DiscussionContext } from '../../common/context';
import { useUserCanAddThreadInBlackoutDate } from '../../data/hooks';
import { selectBlackoutDate } from '../../data/selectors';
import { inBlackoutDateRange } from '../../utils';
import messages from '../messages';
import CommentEditor from './CommentEditor';
@@ -16,14 +17,14 @@ function ResponseEditor({
intl,
addWrappingDiv,
}) {
const { inContext } = useContext(DiscussionContext);
const [addingResponse, setAddingResponse] = useState(false);
const userCanAddThreadInBlackoutDate = useUserCanAddThreadInBlackoutDate();
useEffect(() => {
setAddingResponse(false);
}, [postId]);
const blackoutDateRange = useSelector(selectBlackoutDate);
return addingResponse
? (
<div className={classNames({ 'bg-white p-4 mb-4 rounded': addWrappingDiv })}>
@@ -34,11 +35,11 @@ function ResponseEditor({
/>
</div>
)
: userCanAddThreadInBlackoutDate && (
: !inBlackoutDateRange(blackoutDateRange) && (
<div className={classNames({ 'mb-4': addWrappingDiv }, 'actions d-flex')}>
<Button
variant="primary"
className={classNames('px-2.5 py-2 font-size-14', { 'w-100': inContext })}
className="px-2.5 py-2 font-size-14"
onClick={() => setAddingResponse(true)}
style={{
lineHeight: '20px',

View File

@@ -16,11 +16,6 @@ const messages = defineMessages({
defaultMessage: 'Content reported for staff to review',
description: 'Alert banner over comment that has been reported for abuse',
},
backAlt: {
id: 'discussions.actions.back.alt',
defaultMessage: 'Back to list',
description: 'Back to Posts list button text',
},
responseCount: {
id: 'discussions.comments.comment.responseCount',
defaultMessage: `{num, plural,
@@ -148,31 +143,6 @@ const messages = defineMessages({
defaultMessage: 'Are you sure you want to permanently delete this comment?',
description: 'Text displayed in confirmation dialog when deleting a comment',
},
deleteConfirmationDelete: {
id: 'discussions.delete.confirmation.button.delete',
defaultMessage: 'Delete',
description: 'Delete button shown on delete confirmation dialog',
},
reportResponseTitle: {
id: 'discussions.editor.response.response.title',
defaultMessage: 'Report inappropriate content?',
description: 'Title of confirmation dialog shown when reporting a response',
},
reportResponseDescription: {
id: 'discussions.editor.response.description',
defaultMessage: 'The discussion moderation team will review this content and take appropriate action.',
description: 'Text displayed in confirmation dialog when deleting a response',
},
reportCommentTitle: {
id: 'discussions.editor.report.comment.title',
defaultMessage: 'Report inappropriate content?',
description: 'Title of confirmation dialog shown when reporting a comment',
},
reportCommentDescription: {
id: 'discussions.editor.report.comment.description',
defaultMessage: 'The discussion moderation team will review this content and take appropriate action.',
description: 'Text displayed in confirmation dialog when deleting a response',
},
editReasonCode: {
id: 'discussions.editor.comments.editReasonCode',
defaultMessage: 'Reason for editing',
@@ -207,11 +177,6 @@ const messages = defineMessages({
defaultMessage: '{time} ago',
description: 'Time text for endorse banner',
},
noThreadFound: {
id: 'discussion.thread.notFound',
defaultMessage: 'Thread not found',
description: 'message to show on screen if the request thread is not found in course',
},
});
export default messages;

View File

@@ -1,4 +1,4 @@
import React, { useContext, useState } from 'react';
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { useSelector } from 'react-redux';
@@ -16,7 +16,6 @@ import { selectBlackoutDate } from '../data/selectors';
import messages from '../messages';
import { postShape } from '../posts/post/proptypes';
import { inBlackoutDateRange, useActions } from '../utils';
import { DiscussionContext } from './context';
function ActionsDropdown({
intl,
@@ -27,7 +26,6 @@ function ActionsDropdown({
const [isOpen, open, close] = useToggle(false);
const [target, setTarget] = useState(null);
const actions = useActions(commentOrPost);
const { inContext } = useContext(DiscussionContext);
const handleActions = (action) => {
const actionFunction = actionHandlers[action];
if (actionFunction) {
@@ -52,39 +50,37 @@ function ActionsDropdown({
size="sm"
ref={setTarget}
/>
<div className="actions-dropdown">
<ModalPopup
onClose={close}
positionRef={target}
isOpen={isOpen}
placement={inContext ? 'left' : 'auto-start'}
<ModalPopup
onClose={close}
positionRef={target}
isOpen={isOpen}
placement="auto-start"
>
<div
className="bg-white p-1 shadow d-flex flex-column"
data-testid="actions-dropdown-modal-popup"
>
<div
className="bg-white p-1 shadow d-flex flex-column"
data-testid="actions-dropdown-modal-popup"
>
{actions.map(action => (
<React.Fragment key={action.id}>
{(action.action === ContentActions.DELETE)
{actions.map(action => (
<React.Fragment key={action.id}>
{action.action === ContentActions.DELETE
&& <Dropdown.Divider />}
<Dropdown.Item
as={Button}
variant="tertiary"
size="inline"
onClick={() => {
close();
handleActions(action.action);
}}
className="d-flex justify-content-start py-1.5 mr-4"
>
<Icon src={action.icon} className="mr-1" /> {intl.formatMessage(action.label)}
</Dropdown.Item>
</React.Fragment>
))}
</div>
</ModalPopup>
</div>
<Dropdown.Item
as={Button}
variant="tertiary"
size="inline"
onClick={() => {
close();
handleActions(action.action);
}}
className="d-flex justify-content-start py-1.5 mr-4"
>
<Icon src={action.icon} className="mr-1" /> {intl.formatMessage(action.label)}
</Dropdown.Item>
</React.Fragment>
))}
</div>
</ModalPopup>
</>
);
}

View File

@@ -6,7 +6,7 @@ import { useSelector } from 'react-redux';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Alert } from '@edx/paragon';
import { Report } from '@edx/paragon/icons';
import { Error } from '@edx/paragon/icons';
import { commentShape } from '../comments/comment/proptypes';
import messages from '../comments/messages';
@@ -28,7 +28,7 @@ function AlertBanner({
return (
<>
{canSeeReportedBanner && (
<Alert icon={Report} variant="danger" className="px-3 mb-2 py-10px shadow-none flex-fill">
<Alert icon={Error} variant="danger" className="px-3 mb-2 py-10px shadow-none flex-fill">
{intl.formatMessage(messages.abuseFlaggedMessage)}
</Alert>
)}

View File

@@ -20,7 +20,6 @@ function AuthorLabel({
authorLabel,
linkToProfile,
labelColor,
alert,
}) {
const location = useLocation();
const { courseId } = useContext(DiscussionContext);
@@ -47,8 +46,8 @@ function AuthorLabel({
<div className={className}>
<span
className={classNames('mr-1 font-size-14 font-style-normal font-family-inter font-weight-500', {
'text-primary-500': !authorLabelMessage && !isRetiredUser,
'text-gray-700': isRetiredUser,
'text-primary-500': !authorLabelMessage && !isRetiredUser && !alert,
})}
role="heading"
aria-level="2"
@@ -66,9 +65,8 @@ function AuthorLabel({
)}
{authorLabelMessage && (
<span
className={classNames('mr-1 font-size-14 font-style-normal font-family-inter font-weight-500', {
'text-primary-500': !authorLabelMessage && !isRetiredUser && !alert,
'text-gray-700': isRetiredUser,
className={classNames('mr-3 font-size-14 font-style-normal font-family-inter font-weight-500', {
'text-primary-500': !authorLabelMessage,
})}
style={{ marginLeft: '2px' }}
>
@@ -99,14 +97,12 @@ AuthorLabel.propTypes = {
authorLabel: PropTypes.string,
linkToProfile: PropTypes.bool,
labelColor: PropTypes.string,
alert: PropTypes.bool,
};
AuthorLabel.defaultProps = {
linkToProfile: false,
authorLabel: null,
labelColor: '',
alert: false,
};
export default injectIntl(AuthorLabel);

View File

@@ -6,16 +6,13 @@ import { ActionRow, Button, ModalDialog } from '@edx/paragon';
import messages from '../messages';
function Confirmation({
function DeleteConfirmation({
intl,
isOpen,
title,
description,
onClose,
comfirmAction,
closeButtonVaraint,
confirmButtonVariant,
confirmButtonText,
onDelete,
}) {
return (
<ModalDialog title={title} isOpen={isOpen} hasCloseButton={false} onClose={onClose} zIndex={5000}>
@@ -29,11 +26,11 @@ function Confirmation({
</ModalDialog.Body>
<ModalDialog.Footer>
<ActionRow>
<ModalDialog.CloseButton variant={closeButtonVaraint}>
{intl.formatMessage(messages.confirmationCancel)}
<ModalDialog.CloseButton variant="tertiary">
{intl.formatMessage(messages.deleteConfirmationCancel)}
</ModalDialog.CloseButton>
<Button variant={confirmButtonVariant} onClick={comfirmAction}>
{ confirmButtonText || intl.formatMessage(messages.confirmationConfirm)}
<Button variant="primary" onClick={onDelete}>
{intl.formatMessage(messages.deleteConfirmationDelete)}
</Button>
</ActionRow>
</ModalDialog.Footer>
@@ -41,22 +38,13 @@ function Confirmation({
);
}
Confirmation.propTypes = {
DeleteConfirmation.propTypes = {
intl: intlShape.isRequired,
isOpen: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
comfirmAction: PropTypes.func.isRequired,
onDelete: PropTypes.func.isRequired,
title: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
closeButtonVaraint: PropTypes.string,
confirmButtonVariant: PropTypes.string,
confirmButtonText: PropTypes.string,
};
Confirmation.defaultProps = {
closeButtonVaraint: 'default',
confirmButtonVariant: 'primary',
confirmButtonText: '',
};
export default injectIntl(Confirmation);
export default injectIntl(DeleteConfirmation);

View File

@@ -39,19 +39,14 @@ function EndorsedAlertBanner({
)}
</strong>
<span className="d-flex align-items-center mr-1 flex-wrap">
<span className="mr-1">
<span className="mr-2">
{intl.formatMessage(
isQuestion
? messages.answeredLabel
: messages.endorsedLabel,
)}
</span>
<AuthorLabel
author={content.endorsedBy}
authorLabel={content.endorsedByLabel}
linkToProfile
alert={content.endorsed}
/>
<AuthorLabel author={content.endorsedBy} authorLabel={content.endorsedByLabel} linkToProfile />
{intl.formatMessage(messages.time, { time: timeago.format(content.endorsedAt, 'time-locale') })}
</span>
</div>

View File

@@ -1,5 +1,5 @@
export { default as ActionsDropdown } from './ActionsDropdown';
export { default as AlertBanner } from './AlertBanner';
export { default as AuthorLabel } from './AuthorLabel';
export { default as Confirmation } from './Confirmation';
export { default as DeleteConfirmation } from './DeleteConfirmation';
export { default as EndorsedAlertBanner } from './EndorsedAlertBanner';

View File

@@ -10,26 +10,20 @@ import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { AppContext } from '@edx/frontend-platform/react';
import { breakpoints, useWindowSize } from '@edx/paragon';
import { RequestStatus, Routes } from '../../data/constants';
import { Routes } from '../../data/constants';
import { selectTopicsUnderCategory } from '../../data/selectors';
import { fetchCourseBlocks } from '../../data/thunks';
import { DiscussionContext } from '../common/context';
import { clearRedirect } from '../posts/data';
import { threadsLoadingStatus } from '../posts/data/selectors';
import { selectTopics, topicsLoadingStatus } from '../topics/data/selectors';
import { selectTopics } from '../topics/data/selectors';
import { fetchCourseTopics } from '../topics/data/thunks';
import { discussionsPath, inBlackoutDateRange } from '../utils';
import { discussionsPath } from '../utils';
import {
selectAreThreadsFiltered,
selectBlackoutDate,
selectIsCourseAdmin,
selectIsCourseStaff,
selectLearnersTabEnabled,
selectAreThreadsFiltered, selectLearnersTabEnabled,
selectModerationSettings,
selectPostThreadCount,
selectUserHasModerationPrivileges,
selectUserIsGroupTa,
selectUserIsStaff,
} from './selectors';
import { fetchCourseConfig } from './thunks';
@@ -49,27 +43,14 @@ export function useTotalTopicThreadCount() {
export const useSidebarVisible = () => {
const isFiltered = useSelector(selectAreThreadsFiltered);
const totalThreads = useSelector(selectPostThreadCount);
const threadsCallStatus = useSelector(threadsLoadingStatus);
const isViewingSpecificTopic = useRouteMatch(Routes.TOPICS.TOPIC);
const isViewingTopics = useRouteMatch(Routes.TOPICS.ALL);
const isViewingTopics = useRouteMatch(Routes.TOPICS.PATH);
const isViewingLearners = useRouteMatch(Routes.LEARNERS.PATH);
const topicsLoading = useSelector(topicsLoadingStatus);
if (
isViewingSpecificTopic
&& isViewingSpecificTopic.isExact
&& totalThreads > 0
&& topicsLoading === RequestStatus.SUCCESSFUL
&& threadsCallStatus === RequestStatus.SUCCESSFUL
) {
return false;
}
if (isFiltered) {
return true;
}
if ((isViewingTopics && isViewingTopics.isExact) || isViewingLearners) {
if (isViewingTopics || isViewingLearners) {
return true;
}
@@ -91,7 +72,7 @@ export function useCourseDiscussionData(courseId) {
}, [courseId]);
}
export function useRedirectToThread(courseId, inContext) {
export function useRedirectToThread(courseId) {
const dispatch = useDispatch();
const redirectToThread = useSelector(
(state) => state.threads.redirectToThread,
@@ -104,10 +85,9 @@ export function useRedirectToThread(courseId, inContext) {
// stored in redirectToThread
if (redirectToThread) {
dispatch(clearRedirect());
const newLocation = discussionsPath(Routes.COMMENTS.PAGES[inContext ? 'topics' : 'my-posts'], {
const newLocation = discussionsPath(Routes.COMMENTS.PAGES['my-posts'], {
courseId,
postId: redirectToThread.threadId,
topicId: redirectToThread.topicId,
})(location);
history.push(newLocation);
}
@@ -115,8 +95,7 @@ export function useRedirectToThread(courseId, inContext) {
}
export function useIsOnDesktop() {
const windowSize = useWindowSize();
return windowSize.width >= breakpoints.medium.minWidth;
return window.outerWidth >= breakpoints.large.minWidth;
}
export function useIsOnXLDesktop() {
@@ -193,16 +172,3 @@ export const useCurrentDiscussionTopic = () => {
}
return null;
};
export const useUserCanAddThreadInBlackoutDate = () => {
const blackoutDateRange = useSelector(selectBlackoutDate);
const isUserAdmin = useSelector(selectUserIsStaff);
const userHasModerationPrivilages = useSelector(selectUserHasModerationPrivileges);
const isUserGroupTA = useSelector(selectUserIsGroupTa);
const isCourseAdmin = useSelector(selectIsCourseAdmin);
const isCourseStaff = useSelector(selectIsCourseStaff);
const isInBlackoutDateRange = inBlackoutDateRange(blackoutDateRange);
return (!(isInBlackoutDateRange)
|| (isUserAdmin || userHasModerationPrivilages || isUserGroupTA || isCourseAdmin || isCourseStaff));
};

View File

@@ -1,32 +1,15 @@
import { render } from '@testing-library/react';
import MockAdapter from 'axios-mock-adapter';
import { IntlProvider } from 'react-intl';
import { Factory } from 'rosie';
import { initializeMockApp } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { AppProvider } from '@edx/frontend-platform/react';
import { initializeStore } from '../../store';
import { executeThunk } from '../../test-utils';
import { DiscussionContext } from '../common/context';
import { courseConfigApiUrl } from './api';
import { useCurrentDiscussionTopic, useUserCanAddThreadInBlackoutDate } from './hooks';
import { fetchCourseConfig } from './thunks';
import { useCurrentDiscussionTopic } from './hooks';
const courseId = 'course-v1:edX+TestX+Test_Course';
let store;
let axiosMock;
const generateApiResponse = (blackouts = [], isCourseAdmin = false) => ({
blackouts,
hasModerationPrivileges: false,
isGroupTa: false,
isCourseAdmin,
isCourseStaff: false,
isUserAdmin: false,
});
initializeMockApp();
describe('Hooks', () => {
describe('useCurrentDiscussionTopic', () => {
function ComponentWithHook() {
@@ -56,7 +39,6 @@ describe('Hooks', () => {
}
beforeEach(() => {
initializeMockApp();
store = initializeStore({
blocks: {
blocks: {
@@ -100,75 +82,4 @@ describe('Hooks', () => {
expect(queryByText('null')).toBeInTheDocument();
});
});
describe('useUserCanAddThreadInBlackoutDate', () => {
function ComponentWithHook() {
const userCanAddThreadInBlackoutDate = useUserCanAddThreadInBlackoutDate();
return (
<div>
{String(userCanAddThreadInBlackoutDate)}
</div>
);
}
function renderComponent() {
return render(
<IntlProvider locale="en">
<AppProvider store={store}>
<ComponentWithHook />
</AppProvider>
</IntlProvider>,
);
}
describe('User can add Thread in blackoutdates ', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
Factory.resetAll();
store = initializeStore();
});
test('when blackoutdates are not active and Role is Learner return true', async () => {
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/`)
.reply(200, generateApiResponse([], false));
await executeThunk(fetchCourseConfig(courseId), store.dispatch, store.getState);
const { queryByText } = renderComponent();
expect(queryByText('true')).toBeInTheDocument();
});
test('when blackoutdates are not active and Role is not Learner return true', async () => {
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/`)
.reply(200, generateApiResponse([], true));
await executeThunk(fetchCourseConfig(courseId), store.dispatch, store.getState);
const { queryByText } = renderComponent();
expect(queryByText('true')).toBeInTheDocument();
});
test('when blackoutdates are active and Role is Learner return false', async () => {
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/`)
.reply(200, generateApiResponse([{
start: '2022-11-25T00:00:00Z',
end: '2050-11-25T23:59:00Z',
}], false));
await executeThunk(fetchCourseConfig(courseId), store.dispatch, store.getState);
const { queryByText } = renderComponent();
expect(queryByText('false')).toBeInTheDocument();
});
test('when blackoutdates are active and Role is not Learner return true', async () => {
axiosMock.onGet(`${courseConfigApiUrl}${courseId}/`)
.reply(200, generateApiResponse([
{ start: '2022-11-25T00:00:00Z', end: '2050-11-25T23:59:00Z' }], true));
const { queryByText } = renderComponent();
expect(queryByText('true')).toBeInTheDocument();
});
});
});
});

View File

@@ -24,10 +24,6 @@ export const selectBlackoutDate = state => state.config.blackouts;
export const selectGroupAtSubsection = state => state.config.groupAtSubsection;
export const selectIsCourseAdmin = state => state.config.isCourseAdmin;
export const selectIsCourseStaff = state => state.config.isCourseStaff;
export const selectModerationSettings = state => ({
postCloseReasons: state.config.postCloseReasons,
editReasons: state.config.editReasons,

View File

@@ -14,8 +14,6 @@ const configSlice = createSlice({
groupAtSubsection: false,
hasModerationPrivileges: false,
isGroupTa: false,
isCourseAdmin: false,
isCourseStaff: false,
isUserAdmin: false,
learnersTabEnabled: false,
settings: {

View File

@@ -1,20 +1,46 @@
import React from 'react';
import React, { useContext } from 'react';
import { useSelector } from 'react-redux';
import { Route, Switch } from 'react-router';
import { useHistory, useLocation } from 'react-router-dom';
import { injectIntl } from '@edx/frontend-platform/i18n';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Icon, IconButton } from '@edx/paragon';
import { ArrowBack } from '@edx/paragon/icons';
import { Routes } from '../../data/constants';
import { PostsPages, Routes } from '../../data/constants';
import { CommentsView } from '../comments';
import { DiscussionContext } from '../common/context';
import { useIsOnDesktop } from '../data/hooks';
import messages from '../messages';
import { PostEditor } from '../posts';
import { discussionsPath } from '../utils';
function DiscussionContent() {
function DiscussionContent({ intl }) {
const location = useLocation();
const history = useHistory();
const postEditorVisible = useSelector((state) => state.threads.postEditorVisible);
const isOnDesktop = useIsOnDesktop();
const {
courseId, learnerUsername, category, topicId, page,
} = useContext(DiscussionContext);
return (
<div className="d-flex bg-light-400 flex-column w-75 w-xs-100 w-xl-75 align-items-center">
<div className="d-flex flex-column w-100">
{!isOnDesktop && (
<IconButton
src={ArrowBack}
iconAs={Icon}
style={{ padding: '18px' }}
size="inline"
className="ml-4 mt-4"
onClick={() => history.push(discussionsPath(PostsPages[page], {
courseId, learnerUsername, category, topicId,
})(location))}
alt={intl.formatMessage(messages.backAlt)}
/>
)}
{postEditorVisible ? (
<Route path={Routes.POSTS.NEW_POST}>
<PostEditor />
@@ -34,4 +60,8 @@ function DiscussionContent() {
);
}
DiscussionContent.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(DiscussionContent);

View File

@@ -1,4 +1,4 @@
import React, { useContext, useEffect, useRef } from 'react';
import React, { useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
@@ -10,7 +10,6 @@ import {
import { useWindowSize } from '@edx/paragon';
import { RequestStatus, Routes } from '../../data/constants';
import { DiscussionContext } from '../common/context';
import {
useContainerSize, useIsOnDesktop, useIsOnXLDesktop, useShowLearnersTab,
} from '../data/hooks';
@@ -28,28 +27,26 @@ export default function DiscussionSidebar({ displaySidebar, postActionBarRef })
const sidebarRef = useRef(null);
const postActionBarHeight = useContainerSize(postActionBarRef);
const { height: windowHeight } = useWindowSize();
const { inContext } = useContext(DiscussionContext);
useEffect(() => {
if (sidebarRef && postActionBarHeight && !inContext) {
if (sidebarRef && postActionBarHeight) {
if (isOnDesktop) {
sidebarRef.current.style.maxHeight = `${windowHeight - postActionBarHeight}px`;
}
sidebarRef.current.style.minHeight = `${windowHeight - postActionBarHeight}px`;
sidebarRef.current.style.top = `${postActionBarHeight}px`;
}
}, [sidebarRef, postActionBarHeight, inContext]);
}, [sidebarRef, postActionBarHeight]);
return (
<div
ref={sidebarRef}
className={classNames('flex-column position-sticky', {
className={classNames('flex-column min-content-height position-sticky', {
'd-none': !displaySidebar,
'd-flex overflow-auto': displaySidebar,
'w-100': !isOnDesktop,
'sidebar-desktop-width': isOnDesktop && !isOnXLDesktop,
'w-25 sidebar-XL-width': isOnXLDesktop,
'min-content-height': !inContext,
})}
data-testid="sidebar"
>
@@ -60,11 +57,12 @@ export default function DiscussionSidebar({ displaySidebar, postActionBarRef })
/>
<Route path={Routes.TOPICS.PATH} component={TopicsView} />
{redirectToLearnersTab && (
<Route path={Routes.LEARNERS.POSTS} component={LearnerPostsView} />
<Route path={Routes.LEARNERS.POSTS} component={LearnerPostsView} />
)}
{redirectToLearnersTab && (
<Route path={Routes.LEARNERS.PATH} component={LearnersView} />
<Route path={Routes.LEARNERS.PATH} component={LearnersView} />
)}
{configStatus === RequestStatus.SUCCESSFUL && (
<Redirect
from={Routes.DISCUSSIONS.PATH}

View File

@@ -1,6 +1,5 @@
import React, { useEffect, useRef } from 'react';
import classNames from 'classnames';
import { useSelector } from 'react-redux';
import {
Route, Switch, useLocation, useRouteMatch,
@@ -56,7 +55,9 @@ export default function DiscussionsHome() {
const isOnDesktop = useIsOnDesktop();
const { courseNumber, courseTitle, org } = useSelector((state) => state.courseTabs);
const { courseNumber, courseTitle, org } = useSelector(
(state) => state.courseTabs,
);
if (displayContentArea) {
// If the window is larger than a particular size, show the sidebar for navigating between posts/topics.
// However, for smaller screens or embeds, only show the sidebar if the content area isn't displayed.
@@ -65,7 +66,7 @@ export default function DiscussionsHome() {
const provider = useSelector(selectDiscussionProvider);
useCourseDiscussionData(courseId);
useRedirectToThread(courseId, inContext);
useRedirectToThread(courseId);
useEffect(() => {
if (path && path !== 'undefined') {
postMessageToParent('discussions.navigate', { path });
@@ -83,50 +84,45 @@ export default function DiscussionsHome() {
learnerUsername,
}}
>
{!inContext && <Header courseOrg={org} courseNumber={courseNumber} courseTitle={courseTitle} />}
<Header courseOrg={org} courseNumber={courseNumber} courseTitle={courseTitle} />
<main className="container-fluid d-flex flex-column p-0 w-100" id="main" tabIndex="-1">
{!inContext && <CourseTabsNavigation activeTab="discussion" courseId={courseId} />}
<div
className={classNames('header-action-bar', { 'shadow-none border-light-300 border-bottom': inContext })}
ref={postActionBarRef}
>
<CourseTabsNavigation activeTab="discussion" courseId={courseId} />
<div className="header-action-bar" ref={postActionBarRef}>
<div
className={classNames('d-flex flex-row justify-content-between navbar fixed-top', {
'pl-4 pr-2.5 py-1.5': inContext,
})}
className="d-flex flex-row justify-content-between navbar fixed-top"
>
{!inContext && <Route path={Routes.DISCUSSIONS.PATH} component={NavigationBar} />}
{!inContext && (
<Route path={Routes.DISCUSSIONS.PATH} component={NavigationBar} />
)}
<PostActionsBar inContext={inContext} />
</div>
{isFeedbackBannerVisible && <InformationBanner />}
<BlackoutInformationBanner />
</div>
{!inContext && (
<Route
path={[Routes.POSTS.PATH, Routes.TOPICS.CATEGORY]}
component={provider === DiscussionProvider.LEGACY ? LegacyBreadcrumbMenu : BreadcrumbMenu}
/>
)}
<Route
path={[Routes.POSTS.PATH, Routes.TOPICS.CATEGORY]}
component={provider === DiscussionProvider.LEGACY ? LegacyBreadcrumbMenu : BreadcrumbMenu}
/>
<div className="d-flex flex-row">
<DiscussionSidebar displaySidebar={displaySidebar} postActionBarRef={postActionBarRef} />
{displayContentArea && <DiscussionContent />}
{!displayContentArea && (
<Switch>
<Route path={Routes.TOPICS.PATH} component={EmptyTopics} />
<Route
path={Routes.POSTS.MY_POSTS}
render={routeProps => <EmptyPosts {...routeProps} subTitleMessage={messages.emptyMyPosts} />}
/>
<Route
path={[Routes.POSTS.PATH, Routes.POSTS.ALL_POSTS, Routes.LEARNERS.POSTS]}
render={routeProps => <EmptyPosts {...routeProps} subTitleMessage={messages.emptyAllPosts} />}
/>
{isRedirectToLearners && <Route path={Routes.LEARNERS.PATH} component={EmptyLearners} /> }
</Switch>
<Switch>
<Route path={Routes.TOPICS.PATH} component={EmptyTopics} />
<Route
path={Routes.POSTS.MY_POSTS}
render={routeProps => <EmptyPosts {...routeProps} subTitleMessage={messages.emptyMyPosts} />}
/>
<Route
path={[Routes.POSTS.PATH, Routes.POSTS.ALL_POSTS, Routes.LEARNERS.POSTS]}
render={routeProps => <EmptyPosts {...routeProps} subTitleMessage={messages.emptyAllPosts} />}
/>
{isRedirectToLearners && <Route path={Routes.LEARNERS.PATH} component={EmptyLearners} /> }
</Switch>
)}
</div>
</main>
{!inContext && <Footer />}
<Footer />
</DiscussionContext.Provider>
);
}

View File

@@ -2,6 +2,7 @@ import React, {
useCallback, useContext, useEffect, useMemo,
} from 'react';
import snakeCase from 'lodash.snakecase';
import capitalize from 'lodash/capitalize';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory, useLocation } from 'react-router-dom';
@@ -13,8 +14,10 @@ import {
import { ArrowBack } from '@edx/paragon/icons';
import {
PostsStatusFilter,
RequestStatus,
Routes,
ThreadType,
} from '../../data/constants';
import { DiscussionContext } from '../common/context';
import { selectUserHasModerationPrivileges, selectUserIsStaff } from '../data/selectors';
@@ -43,23 +46,46 @@ function LearnerPostsView({ intl }) {
const nextPage = useSelector(selectThreadNextPage());
const userHasModerationPrivileges = useSelector(selectUserHasModerationPrivileges);
const userIsStaff = useSelector(selectUserIsStaff);
const loadMorePosts = (pageNum = undefined) => {
const params = {
author: username,
page: pageNum,
filters: postFilter,
orderBy: postFilter.orderBy,
countFlagged: (userHasModerationPrivileges || userIsStaff) || undefined,
};
dispatch(fetchUserPosts(courseId, params));
const countFlagged = userHasModerationPrivileges || userIsStaff;
const params = {
orderBy: snakeCase(postFilter.orderBy),
username,
page: 1,
};
if (postFilter.type !== ThreadType.ALL) {
params.threadType = postFilter.type;
}
if (postFilter.status !== PostsStatusFilter.ALL) {
const statusMapping = {
statusUnread: 'unread',
statusReported: 'flagged',
statusUnanswered: 'unanswered',
statusUnresponded: 'unresponded',
};
params.status = statusMapping[postFilter.status];
}
if (postFilter.cohort !== '') {
params.groupId = postFilter.cohort;
}
if (countFlagged) {
params.countFlagged = countFlagged;
}
useEffect(() => {
dispatch(fetchUserPosts(courseId, params));
}, [courseId, username]);
useEffect(() => {
dispatch(clearPostsPages());
loadMorePosts();
}, [courseId, postFilter, username]);
dispatch(fetchUserPosts(courseId, params));
}, [postFilter]);
const loadMorePosts = () => (
dispatch(fetchUserPosts(courseId, {
...params,
page: nextPage,
}))
);
const checkIsSelected = (id) => window.location.pathname.includes(id);
const pinnedPosts = useMemo(() => filterPosts(posts, 'pinned'), [posts]);
@@ -95,7 +121,6 @@ function LearnerPostsView({ intl }) {
</div>
<div className="bg-light-400 border border-light-300" />
<LearnerPostFilterBar />
<div className="border-bottom border-light-400" />
<div className="list-group list-group-flush">
{postInstances(pinnedPosts)}
{postInstances(unpinnedPosts)}
@@ -106,7 +131,7 @@ function LearnerPostsView({ intl }) {
</div>
) : (
nextPage && loadingStatus === RequestStatus.SUCCESSFUL && (
<Button onClick={() => loadMorePosts(nextPage)} variant="primary" size="md">
<Button onClick={() => loadMorePosts()} variant="primary" size="md">
{intl.formatMessage(messages.loadMore)}
</Button>
)

View File

@@ -1,6 +1,4 @@
/* eslint-disable import/prefer-default-export */
import snakeCase from 'lodash/snakeCase';
import { ensureConfig, getConfig, snakeCaseObject } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
@@ -39,50 +37,18 @@ export async function getUserProfiles(usernames) {
* Get the posts by a specific user in a course's discussions
*
* @param {string} courseId Course ID of the course
* @param {string} author
* @param {string} username Username of the user
* @param {number} page
* @param {number} pageSize
* @param {string} textSearch A search string to match.
* @param {ThreadOrdering} orderBy The results wil be sorted on this basis.
* @param {boolean} following If true, only threads followed by the current user will be returned.
* @param {boolean} flagged If true, only threads that have been reported will be returned.
* @param {string} threadType Can be 'discussion' or 'question'.
* @param {ThreadViewStatus} view Set to "unread" on "unanswered" to filter to only those statuses.
* @param {boolean} countFlagged If true, abuseFlaggedCount will be available.
* @param {number} cohort
* @returns API Response object in the format
* {
* results: [array of posts],
* pagination: {count, num_pages, next, previous}
* }
*/
export async function getUserPosts(courseId, {
page,
pageSize,
textSearch,
orderBy,
status,
author,
threadType,
countFlagged,
cohort,
} = {}) {
export async function getUserPosts(courseId, params) {
const learnerPostsApiUrl = `${coursesApiUrl}${courseId}/learner/`;
const params = snakeCaseObject({
page,
pageSize,
textSearch,
threadType,
orderBy: orderBy && snakeCase(orderBy),
status,
requestedFields: 'profile_image',
username: author,
countFlagged,
groupId: cohort,
});
const snakeCaseParams = snakeCaseObject(params);
const { data } = await getAuthenticatedHttpClient()
.get(learnerPostsApiUrl, { params });
.get(learnerPostsApiUrl, { params: snakeCaseParams });
return data;
}

View File

@@ -20,7 +20,7 @@ const learnersSlice = createSlice({
totalLearners: null,
sortedBy: LearnersOrdering.BY_LAST_ACTIVITY,
postFilter: {
postType: ThreadType.ALL,
type: ThreadType.ALL,
status: PostsStatusFilter.ALL,
orderBy: ThreadOrdering.BY_LAST_ACTIVITY,
cohort: '',

View File

@@ -2,9 +2,6 @@
import { camelCaseObject, snakeCaseObject } from '@edx/frontend-platform';
import { logError } from '@edx/frontend-platform/logging';
import {
PostsStatusFilter, ThreadType,
} from '../../../data/constants';
import {
fetchLearnerThreadsRequest,
fetchThreadsDenied,
@@ -70,48 +67,17 @@ export function fetchLearners(courseId, {
* @param page
* @returns a promise that will update the state with the learner's posts
*/
export function fetchUserPosts(courseId, {
orderBy,
filters = {},
page = 1,
author = null,
countFlagged,
} = {}) {
const options = {
orderBy,
page,
author,
countFlagged,
};
if (filters.status === PostsStatusFilter.UNREAD) {
options.status = 'unread';
}
if (filters.status === PostsStatusFilter.UNANSWERED) {
options.status = 'unanswered';
}
if (filters.status === PostsStatusFilter.REPORTED) {
options.status = 'flagged';
}
if (filters.status === PostsStatusFilter.UNRESPONDED) {
options.status = 'unresponded';
}
if (filters.postType !== ThreadType.ALL) {
options.threadType = filters.postType;
}
if (filters.search) {
options.textSearch = filters.search;
}
if (filters.cohort) {
options.cohort = filters.cohort;
}
export function fetchUserPosts(courseId, params) {
return async (dispatch) => {
try {
dispatch(fetchLearnerThreadsRequest({ courseId, author }));
const data = await getUserPosts(courseId, options);
dispatch(fetchLearnerThreadsRequest({ courseId, author: params?.username }));
const data = await getUserPosts(courseId, params);
const normalisedData = normaliseThreads(camelCaseObject(data));
dispatch(fetchThreadsSuccess({ ...normalisedData, page, author }));
dispatch(fetchThreadsSuccess({
...normalisedData,
page: params.page,
author: params.username,
}));
} catch (error) {
if (getHttpErrorStatus(error) === 403) {
dispatch(fetchThreadsDenied());

View File

@@ -4,8 +4,6 @@ import { isEmpty } from 'lodash';
import { useDispatch, useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import FilterBar from '../../../components/FilterBar';
import { selectCourseCohorts } from '../../cohorts/data/selectors';
import { fetchCourseCohorts } from '../../cohorts/data/thunks';
@@ -22,7 +20,7 @@ function LearnerPostFilterBar() {
const filtersToShow = [
{
name: 'postType',
name: 'type',
filters: ['type-all', 'type-discussions', 'type-questions'],
},
{
@@ -41,20 +39,12 @@ function LearnerPostFilterBar() {
const handleFilterChange = (event) => {
const { name, value } = event.currentTarget;
const filterContentEventProperties = {
statusFilter: postFilter.status,
threadTypeFilter: postFilter.postType,
sortFilter: postFilter.orderBy,
cohortFilter: postFilter.cohort,
triggeredBy: name,
};
if (name === 'postType') {
if (postFilter.postType !== value) {
if (name === 'type') {
if (postFilter.type !== value) {
dispatch(setPostFilter({
...postFilter,
postType: value,
type: value,
}));
filterContentEventProperties.threadTypeFilter = value;
}
} else if (name === 'status') {
if (postFilter.status !== value) {
@@ -62,7 +52,6 @@ function LearnerPostFilterBar() {
...postFilter,
status: value,
}));
filterContentEventProperties.statusFilter = value;
}
} else if (name === 'orderBy') {
if (postFilter.orderBy !== value) {
@@ -70,7 +59,6 @@ function LearnerPostFilterBar() {
...postFilter,
orderBy: value,
}));
filterContentEventProperties.sortFilter = value;
}
} else if (name === 'cohort') {
if (postFilter.cohort !== value) {
@@ -78,10 +66,8 @@ function LearnerPostFilterBar() {
...postFilter,
cohort: value,
}));
filterContentEventProperties.cohortFilter = value;
}
}
sendTrackEvent('edx.forum.filter.content', filterContentEventProperties);
};
useEffect(() => {

View File

@@ -4,7 +4,6 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Collapsible, Form, Icon } from '@edx/paragon';
import { Check, Tune } from '@edx/paragon/icons';
@@ -59,12 +58,6 @@ function LearnerFilterBar({
if (name === 'sort') {
dispatch(setSortedBy(value));
sendTrackEvent(
'edx.forum.sort.user',
{
sort: value,
},
);
}
};

View File

@@ -45,7 +45,7 @@ const messages = defineMessages({
},
sortFilterStatus: {
id: 'discussions.learner.sortFilterStatus',
defaultMessage: `All learners sorted by {sort, select,
defaultMessage: `All learners by {sort, select,
flagged {reported activity}
activity {most activity}
other {{sort}}

View File

@@ -6,6 +6,11 @@ const messages = defineMessages({
defaultMessage: 'Actions menu',
description: 'Alt-text for dropdown button for actions related to a post or comment',
},
backAlt: {
id: 'discussions.actions.back.alt',
defaultMessage: 'Back',
description: 'Text on button for back to posts list',
},
copyLink: {
id: 'discussions.actions.copylink',
defaultMessage: 'Copy link',
@@ -31,11 +36,6 @@ const messages = defineMessages({
defaultMessage: 'Delete',
description: 'Action to delete a post or comment',
},
confirmationConfirm: {
id: 'discussions.confirmation.button.confirm',
defaultMessage: 'Confirm',
description: 'Confirm button shown on confirmation dialog',
},
closeAction: {
id: 'discussions.actions.close',
defaultMessage: 'Close',
@@ -68,7 +68,7 @@ const messages = defineMessages({
},
markAnsweredAction: {
id: 'discussions.actions.markAnswered',
defaultMessage: 'Mark as answered',
defaultMessage: 'Mark as Answered',
description: 'Action to mark a comment as answering a post',
},
unmarkAnsweredAction: {
@@ -76,11 +76,16 @@ const messages = defineMessages({
defaultMessage: 'Unmark as answered',
description: 'Action to unmark a comment as answering a post',
},
confirmationCancel: {
id: 'discussions.modal.confirmation.button.cancel',
deleteConfirmationCancel: {
id: 'discussions.delete.confirmation.button.cancel',
defaultMessage: 'Cancel',
description: 'Cancel button shown on delete confirmation dialog',
},
deleteConfirmationDelete: {
id: 'discussions.delete.confirmation.button.delete',
defaultMessage: 'Delete',
description: 'Delete button shown on delete confirmation dialog',
},
emptyAllTopics: {
id: 'discussions.empty.allTopics',
defaultMessage:

View File

@@ -12,7 +12,6 @@ import { Button, Spinner } from '@edx/paragon';
import { RequestStatus } from '../../data/constants';
import { DiscussionContext } from '../common/context';
import { selectconfigLoadingStatus, selectUserHasModerationPrivileges, selectUserIsStaff } from '../data/selectors';
import { fetchUserPosts } from '../learners/data/thunks';
import messages from '../messages';
import { filterPosts } from '../utils';
import {
@@ -22,9 +21,7 @@ import { fetchThreads } from './data/thunks';
import NoResults from './NoResults';
import { PostLink } from './post';
function PostsList({
posts, topics, intl, isTopicTab,
}) {
function PostsList({ posts, topics, intl }) {
const dispatch = useDispatch();
const {
courseId,
@@ -40,33 +37,22 @@ function PostsList({
const userIsStaff = useSelector(selectUserIsStaff);
const configStatus = useSelector(selectconfigLoadingStatus);
const loadThreads = (topicIds, pageNum = undefined, isFilterChanged = false) => {
const params = {
const loadThreads = (topicIds, pageNum = undefined) => (
dispatch(fetchThreads(courseId, {
topicIds,
orderBy,
filters,
page: pageNum,
author: showOwnPosts ? authenticatedUser.username : null,
countFlagged: (userHasModerationPrivileges || userIsStaff) || undefined,
topicIds,
isFilterChanged,
};
if (showOwnPosts) {
dispatch(fetchUserPosts(courseId, params));
} else {
dispatch(fetchThreads(courseId, params));
}
};
countFlagged: userHasModerationPrivileges || userIsStaff,
}))
);
useEffect(() => {
if (topics !== undefined && configStatus === RequestStatus.SUCCESSFUL) {
loadThreads(topics);
}
}, [courseId, filters, orderBy, page, JSON.stringify(topics), configStatus]);
useEffect(() => {
if (isTopicTab) { loadThreads(topics, 1, true); }
}, [filters]);
}, [courseId, orderBy, filters, page, JSON.stringify(topics), configStatus]);
const checkIsSelected = (id) => window.location.pathname.includes(id);
const pinnedPosts = useMemo(() => filterPosts(posts, 'pinned'), [posts]);
@@ -110,14 +96,12 @@ PostsList.propTypes = {
id: PropTypes.string.isRequired,
})),
topics: PropTypes.arrayOf(PropTypes.string),
isTopicTab: PropTypes.bool,
intl: intlShape.isRequired,
};
PostsList.defaultProps = {
posts: [],
topics: undefined,
isTopicTab: false,
};
export default injectIntl(PostsList);

View File

@@ -21,7 +21,7 @@ function AllPostsList() {
function TopicPostsList({ topicId }) {
const posts = useSelector(selectTopicThreads([topicId]));
return <PostsList posts={posts} topics={[topicId]} isTopicTab />;
return <PostsList posts={posts} topics={[topicId]} />;
}
TopicPostsList.propTypes = {

View File

@@ -18,7 +18,6 @@ import { initializeStore } from '../../store';
import { getCohortsApiUrl } from '../cohorts/data/api';
import { DiscussionContext } from '../common/context';
import { fetchConfigSuccess } from '../data/slices';
import { coursesApiUrl } from '../learners/data/api';
import { threadsApiUrl } from './data/api';
import { PostsView } from './index';
@@ -28,7 +27,6 @@ import '../cohorts/data/__factories__';
const courseId = 'course-v1:edX+TestX+Test_Course';
let store;
let axiosMock;
const username = 'abc123';
async function renderComponent({
postId, topicId, category, myPosts, inContext = false,
@@ -83,7 +81,7 @@ describe('PostsView', () => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username,
username: 'abc123',
administrator: true,
roles: [],
},
@@ -112,6 +110,7 @@ describe('PostsView', () => {
config: { hasModerationPrivileges: true },
...data,
};
// console.log(storeData);
store = initializeStore(storeData);
store.dispatch(fetchConfigSuccess({}));
}
@@ -127,21 +126,9 @@ describe('PostsView', () => {
test('displays a list of user posts', async () => {
setupStore();
axiosMock.onGet(`${coursesApiUrl}${courseId}/learner/`, { username, count_flagged: true })
.reply(() => {
const threadAttrs = { previewBody: 'thread preview body', author: username };
return [200, Factory.build('threadsResult', {}, {
topicId: undefined,
count: threadCount,
threadAttrs,
pageSize: 6,
})];
});
await act(async () => {
await renderComponent({ myPosts: true });
});
expect(screen.getAllByText('abc123')).toHaveLength(threadCount);
});
@@ -198,7 +185,7 @@ describe('PostsView', () => {
await renderComponent();
});
dropDownButton = screen.getByRole('button', {
name: /all posts sorted by recent activity/i,
name: /all posts by recent activity/i,
});
await act(async () => {
fireEvent.click(dropDownButton);
@@ -210,7 +197,7 @@ describe('PostsView', () => {
// 5 status filters: any, unread, following, reported, unanswered
// 3 sort: activity, comments, likes
// 2 cohort: all groups, 1 api mock response cohort
expect(screen.queryAllByRole('radio')).toHaveLength(14);
expect(screen.queryAllByRole('radio')).toHaveLength(13);
});
test('test that the cohorts filter works', async () => {
@@ -219,7 +206,7 @@ describe('PostsView', () => {
});
dropDownButton = screen.getByRole('button', {
name: /All posts in Cohort 1 sorted by recent activity/i,
name: /All posts in Cohort 1 by recent activity/i,
});
expect(dropDownButton).toBeInTheDocument();

View File

@@ -71,8 +71,8 @@ export async function getThreads(
* @param {string} threadId
* @returns {Promise<{}>}
*/
export async function getThread(threadId, courseId) {
const params = { requested_fields: 'profile_image', course_id: courseId };
export async function getThread(threadId) {
const params = { requested_fields: 'profile_image' };
const url = `${threadsApiUrl}${threadId}/`;
const { data } = await getAuthenticatedHttpClient().get(url, { params });
return data;

View File

@@ -79,13 +79,7 @@ const threadsSlice = createSlice({
}
state.status = RequestStatus.SUCCESSFUL;
state.threadsById = { ...state.threadsById, ...payload.threadsById };
// filter
if (payload.isFilterChanged) {
state.threadsInTopic = { ...payload.threadsInTopic };
} else {
state.threadsInTopic = mergeThreadsInTopics(state.threadsInTopic, payload.threadsInTopic);
}
state.threadsInTopic = mergeThreadsInTopics(state.threadsInTopic, payload.threadsInTopic);
state.avatars = { ...state.avatars, ...payload.avatars };
state.nextPage = (payload.page < payload.pagination.numPages) ? payload.page + 1 : null;
state.totalPages = payload.pagination.numPages;

View File

@@ -102,7 +102,6 @@ export function fetchThreads(courseId, {
author = null,
filters = {},
page = 1,
isFilterChanged,
countFlagged,
} = {}) {
const options = {
@@ -121,9 +120,6 @@ export function fetchThreads(courseId, {
if (filters.status === PostsStatusFilter.UNANSWERED) {
options.view = 'unanswered';
}
if (filters.status === PostsStatusFilter.UNRESPONDED) {
options.view = 'unresponded';
}
if (filters.status === PostsStatusFilter.REPORTED) {
options.flagged = true;
}
@@ -142,7 +138,7 @@ export function fetchThreads(courseId, {
const data = await getThreads(courseId, options);
const normalisedData = normaliseThreads(camelCaseObject(data), topicIds);
dispatch(fetchThreadsSuccess({
...normalisedData, page, author, textSearchRewrite: data.text_search_rewrite, isFilterChanged,
...normalisedData, page, author, textSearchRewrite: data.text_search_rewrite,
}));
} catch (error) {
if (getHttpErrorStatus(error) === 403) {
@@ -155,18 +151,18 @@ export function fetchThreads(courseId, {
};
}
export function fetchThread(threadId, courseId, isDirectLinkPost = false) {
export function fetchThread(threadId, isDirectLinkPost = false) {
return async (dispatch) => {
try {
dispatch(fetchThreadRequest({ threadId }));
const data = await getThread(threadId, courseId);
const data = await getThread(threadId);
if (isDirectLinkPost) {
dispatch(fetchThreadByDirectLinkSuccess({ ...normaliseThreads(camelCaseObject(data)), page: 1 }));
} else {
dispatch(fetchThreadSuccess(normaliseThreads(camelCaseObject(data))));
}
} catch (error) {
if (getHttpErrorStatus(error) === 403 || getHttpErrorStatus(error) === 404) {
if (getHttpErrorStatus(error) === 403) {
dispatch(fetchThreadDenied());
} else {
dispatch(fetchThreadFailed());

View File

@@ -1,7 +1,6 @@
import React 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';
@@ -11,10 +10,8 @@ import {
import { Close } from '@edx/paragon/icons';
import Search from '../../../components/Search';
import { RequestStatus } from '../../../data/constants';
import { useUserCanAddThreadInBlackoutDate } from '../../data/hooks';
import { selectconfigLoadingStatus } from '../../data/selectors';
import { postMessageToParent } from '../../utils';
import { selectBlackoutDate } from '../../data/selectors';
import { inBlackoutDateRange, postMessageToParent } from '../../utils';
import { showPostEditor } from '../data';
import messages from './messages';
@@ -25,38 +22,38 @@ function PostActionsBar({
inContext,
}) {
const dispatch = useDispatch();
const loadingStatus = useSelector(selectconfigLoadingStatus);
const userCanAddThreadInBlackoutDate = useUserCanAddThreadInBlackoutDate();
const handleCloseInContext = () => {
postMessageToParent('learning.events.sidebar.close');
};
const blackoutDateRange = useSelector(selectBlackoutDate);
return (
<div className={classNames('d-flex justify-content-end flex-grow-1', { 'py-1': !inContext })}>
{!inContext && <Search />}
<div className="d-flex justify-content-end py-1 flex-grow-1">
{!inContext && (
<>
<Search />
<div className="border-right border-light-400 mx-3" />
</>
)}
{inContext && (
<h4 className="d-flex flex-grow-1 font-weight-bold my-0 py-0 align-self-center">
{intl.formatMessage(messages.title)}
</h4>
)}
{loadingStatus === RequestStatus.SUCCESSFUL && userCanAddThreadInBlackoutDate
&& (
<>
{!inContext && <div className="border-right border-light-400 mx-3" />}
{
!inBlackoutDateRange(blackoutDateRange) && (
<Button
variant={inContext ? 'plain' : 'brand'}
className={classNames('my-0', { 'p-0': inContext })}
className="my-0"
onClick={() => dispatch(showPostEditor())}
size={inContext ? 'md' : 'sm'}
size="sm"
>
{intl.formatMessage(messages.addAPost)}
</Button>
</>
)}
)
}
{inContext && (
<>
<div className="border-right border-light-300 mr-2 ml-3.5 my-2" />
<div className="border-right mr-3 ml-4" />
<IconButton
src={Close}
iconAs={Icon}
@@ -65,6 +62,7 @@ function PostActionsBar({
/>
</>
)}
</div>
);
}

View File

@@ -23,7 +23,6 @@ import PostPreviewPane from '../../../components/PostPreviewPane';
import { useDispatchWithState } from '../../../data/hooks';
import { selectCourseCohorts } from '../../cohorts/data/selectors';
import { fetchCourseCohorts } from '../../cohorts/data/thunks';
import { DiscussionContext } from '../../common/context';
import { useCurrentDiscussionTopic } from '../../data/hooks';
import {
selectAnonymousPostingConfig,
@@ -33,7 +32,6 @@ import {
selectUserIsGroupTa,
selectUserIsStaff,
} from '../../data/selectors';
import { EmptyPage } from '../../empty-posts';
import { selectCoursewareTopics, selectNonCoursewareIds, selectNonCoursewareTopics } from '../../topics/data/selectors';
import {
discussionsPath, formikCompatibleHandler, isFormikFieldInvalid, useCommentsPagePath,
@@ -95,6 +93,7 @@ function PostEditor({
postId,
} = useParams();
const topicId = useCurrentDiscussionTopic();
const nonCoursewareTopics = useSelector(selectNonCoursewareTopics);
const nonCoursewareIds = useSelector(selectNonCoursewareIds);
const coursewareTopics = useSelector(selectCoursewareTopics);
@@ -106,7 +105,6 @@ function PostEditor({
const { allowAnonymous, allowAnonymousToPeers } = useSelector(selectAnonymousPostingConfig);
const { reasonCodesEnabled, editReasons } = useSelector(selectModerationSettings);
const userIsStaff = useSelector(selectUserIsStaff);
const { category, inContext } = useContext(DiscussionContext);
const canDisplayEditReason = (reasonCodesEnabled && editExisting
&& (userHasModerationPrivileges || userIsGroupTa || userIsStaff)
@@ -150,7 +148,6 @@ function PostEditor({
topicId,
postId,
learnerUsername: post?.author,
category,
})(location);
history.push(newLocation);
}
@@ -194,25 +191,16 @@ function PostEditor({
dispatch(fetchCourseCohorts(courseId));
}
if (editExisting) {
dispatchSubmit(fetchThread(postId, courseId));
dispatch(fetchThread(postId));
}
}, [courseId, editExisting]);
if (editExisting && !post) {
if (submitting) {
return (
<div className="m-4 card p-4 align-items-center">
<Spinner animation="border" variant="primary" />
</div>
);
}
if (!submitting) {
return (
<EmptyPage
title={intl.formatMessage(messages.noThreadFound)}
/>
);
}
return (
<div className="m-4 card p-4 align-items-center">
<Spinner animation="border" variant="primary" />
</div>
);
}
const validationSchema = Yup.object().shape({
@@ -296,20 +284,15 @@ function PostEditor({
onBlur={handleBlur}
aria-describedby="topicAreaInput"
floatingLabel={intl.formatMessage(messages.topicArea)}
disabled={inContext}
>
{nonCoursewareTopics.map(topic => (
<option
key={topic.id}
value={topic.id}
>{topic.name || intl.formatMessage(messages.unnamedSubTopics)}
</option>
<option key={topic.id} value={topic.id}>{topic.name}</option>
))}
{coursewareTopics.map(categoryObj => (
<optgroup label={categoryObj.name || intl.formatMessage(messages.unnamedTopics)} key={categoryObj.id}>
{categoryObj.topics.map(subtopic => (
{coursewareTopics.map(category => (
<optgroup label={category.name} key={category.id}>
{category.topics.map(subtopic => (
<option key={subtopic.id} value={subtopic.id}>
{subtopic.name || intl.formatMessage(messages.unnamedSubTopics)}
{subtopic.name}
</option>
))}
</optgroup>

View File

@@ -16,7 +16,6 @@ import { API_BASE_URL, Routes } from '../../../data/constants';
import { initializeStore } from '../../../store';
import { executeThunk } from '../../../test-utils';
import { getCohortsApiUrl } from '../../cohorts/data/api';
import { DiscussionContext } from '../../common/context';
import { fetchCourseTopics } from '../../topics/data/thunks';
import { threadsApiUrl } from '../data/api';
import { fetchThread } from '../data/thunks';
@@ -37,15 +36,11 @@ async function renderComponent(editExisting = false, location = `/${courseId}/po
await render(
<IntlProvider locale="en">
<AppProvider store={store}>
<DiscussionContext.Provider
value={{ courseId, category: null }}
>
<MemoryRouter initialEntries={[location]}>
<Route path={path}>
<PostEditor editExisting={editExisting} />
</Route>
</MemoryRouter>
</DiscussionContext.Provider>
<MemoryRouter initialEntries={[location]}>
<Route path={path}>
<PostEditor editExisting={editExisting} />
</Route>
</MemoryRouter>
</AppProvider>
</IntlProvider>,
);

View File

@@ -121,21 +121,6 @@ const messages = defineMessages({
defaultMessage: 'Actions menu',
description: 'Button to see actions for a post or comment',
},
unnamedTopics: {
id: 'discussions.topic.noName.label',
defaultMessage: 'Unnamed category',
description: 'display string for topics with missing names',
},
unnamedSubTopics: {
id: 'discussions.subtopic.noName.label',
defaultMessage: 'Unnamed subcategory',
description: 'display string for topics with missing names',
},
noThreadFound: {
id: 'discussion.thread.notFound',
defaultMessage: 'Thread not found',
description: 'message to show on screen if the request thread is not found in course',
},
});
export default messages;

View File

@@ -1,6 +1,4 @@
import React, {
useContext, useEffect, useMemo, useState,
} from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
@@ -8,7 +6,6 @@ import { capitalize, isEmpty, toString } from 'lodash';
import { useDispatch, useSelector } from 'react-redux';
import { useParams } from 'react-router-dom';
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import {
Collapsible, Form, Icon, Spinner,
@@ -21,7 +18,6 @@ import {
} from '../../../data/constants';
import { selectCourseCohorts } from '../../cohorts/data/selectors';
import { fetchCourseCohorts } from '../../cohorts/data/thunks';
import { DiscussionContext } from '../../common/context';
import { selectUserHasModerationPrivileges, selectUserIsGroupTa } from '../../data/selectors';
import {
setCohortFilter, setPostsTypeFilter, setSortedBy, setStatusFilter,
@@ -64,7 +60,6 @@ function PostFilterBar({
}) {
const dispatch = useDispatch();
const { courseId } = useParams();
const { page } = useContext(DiscussionContext);
const userHasModerationPrivileges = useSelector(selectUserHasModerationPrivileges);
const userIsGroupTa = useSelector(selectUserIsGroupTa);
const currentSorting = useSelector(selectThreadSorting());
@@ -85,13 +80,6 @@ function PostFilterBar({
name,
value,
} = event.currentTarget;
const filterContentEventProperties = {
statusFilter: currentStatus,
threadTypeFilter: currentType,
sortFilter: currentSorting,
cohortFilter: selectedCohort,
triggeredBy: name,
};
if (name === 'type') {
dispatch(setPostsTypeFilter(value));
if (
@@ -100,7 +88,6 @@ function PostFilterBar({
// You can't filter discussions by unanswered
dispatch(setStatusFilter(PostsStatusFilter.ALL));
}
filterContentEventProperties.threadTypeFilter = value;
}
if (name === 'status') {
dispatch(setStatusFilter(value));
@@ -108,21 +95,13 @@ function PostFilterBar({
// You can't filter discussions by unanswered so switch type to questions
dispatch(setPostsTypeFilter(ThreadType.QUESTION));
}
if (value === PostsStatusFilter.UNRESPONDED && currentType !== ThreadType.DISCUSSION) {
// You can't filter questions by not responded so switch type to discussion
dispatch(setPostsTypeFilter(ThreadType.DISCUSSION));
}
filterContentEventProperties.statusFilter = value;
}
if (name === 'sort') {
dispatch(setSortedBy(value));
filterContentEventProperties.sortFilter = value;
}
if (name === 'cohort') {
dispatch(setCohortFilter(value));
filterContentEventProperties.cohortFilter = value;
}
sendTrackEvent('edx.forum.filter.content', filterContentEventProperties);
};
useEffect(() => {
@@ -201,14 +180,12 @@ function PostFilterBar({
value={PostsStatusFilter.UNREAD}
selected={currentFilters.status}
/>
{page !== 'my-posts' && (
<ActionItem
id="status-following"
label={intl.formatMessage(messages.filterFollowing)}
value={PostsStatusFilter.FOLLOWING}
selected={currentFilters.status}
/>
)}
<ActionItem
id="status-following"
label={intl.formatMessage(messages.filterFollowing)}
value={PostsStatusFilter.FOLLOWING}
selected={currentFilters.status}
/>
{(userHasModerationPrivileges || userIsGroupTa) && (
<ActionItem
id="status-reported"
@@ -223,12 +200,6 @@ function PostFilterBar({
value={PostsStatusFilter.UNANSWERED}
selected={currentFilters.status}
/>
<ActionItem
id="status-unresponded"
label={intl.formatMessage(messages.filterUnresponded)}
value={PostsStatusFilter.UNRESPONDED}
selected={currentFilters.status}
/>
</Form.RadioSet>
<Form.RadioSet
name="sort"

View File

@@ -48,7 +48,7 @@ const messages = defineMessages({
},
filterUnresponded: {
id: 'discussions.posts.status.filter.unresponded',
defaultMessage: 'Not responded',
defaultMessage: 'Unresponded',
description: 'Option in dropdown to filter to unresponded posts',
},
myPosts: {
@@ -109,10 +109,10 @@ const messages = defineMessages({
all {}
group {in {cohort}}
other {{cohortType}}
} sorted by {sort, select,
} by {sort, select,
lastActivityAt {recent activity}
commentCount {most activity}
voteCount {most likes}
voteCount {most votes}
other {{sort}}
}`,
description: 'Status message showing current sorting and filtering status',

View File

@@ -50,7 +50,8 @@ function ClosePostReasonModal({
isOpen={isOpen}
onClose={onCancel}
hasCloseButton={false}
zIndex={5000}
isFullscreenOnMobile
isFullscreenScroll
>
<ModalDialog.Header>
<ModalDialog.Title>

View File

@@ -1,7 +1,6 @@
import React, { useContext } from 'react';
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory, useLocation } from 'react-router-dom';
@@ -11,8 +10,7 @@ import { Hyperlink, useToggle } from '@edx/paragon';
import HTMLLoader from '../../../components/HTMLLoader';
import { ContentActions } from '../../../data/constants';
import { selectorForUnitSubsection, selectTopicContext } from '../../../data/selectors';
import { AlertBanner, Confirmation } from '../../common';
import { DiscussionContext } from '../../common/context';
import { AlertBanner, DeleteConfirmation } from '../../common';
import { selectModerationSettings } from '../../data/selectors';
import { selectTopic } from '../../topics/data/selectors';
import { removeThread, updateExistingThread } from '../data/thunks';
@@ -36,29 +34,7 @@ function Post({
const topicContext = useSelector(selectTopicContext(post.topicId));
const { reasonCodesEnabled } = useSelector(selectModerationSettings);
const [isDeleting, showDeleteConfirmation, hideDeleteConfirmation] = useToggle(false);
const [isReporting, showReportConfirmation, hideReportConfirmation] = useToggle(false);
const [isClosing, showClosePostModal, hideClosePostModal] = useToggle(false);
const handleAbusedFlag = () => {
if (post.abuseFlagged) {
dispatch(updateExistingThread(post.id, { flagged: !post.abuseFlagged }));
} else {
showReportConfirmation();
}
};
const handleDeleteConfirmation = () => {
dispatch(removeThread(post.id));
history.push('.');
hideDeleteConfirmation();
};
const handleReportConfirmation = () => {
dispatch(updateExistingThread(post.id, { flagged: !post.abuseFlagged }));
hideReportConfirmation();
};
const { inContext } = useContext(DiscussionContext);
const actionHandlers = {
[ContentActions.EDIT_CONTENT]: () => history.push({
...location,
@@ -76,7 +52,7 @@ 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.REPORT]: () => dispatch(updateExistingThread(post.id, { flagged: !post.abuseFlagged })),
};
const getTopicCategoryName = topicData => (
@@ -85,50 +61,30 @@ function Post({
return (
<div className="d-flex flex-column w-100 mw-100" data-testid={`post-${post.id}`}>
<Confirmation
<DeleteConfirmation
isOpen={isDeleting}
title={intl.formatMessage(messages.deletePostTitle)}
description={intl.formatMessage(messages.deletePostDescription)}
onClose={hideDeleteConfirmation}
comfirmAction={handleDeleteConfirmation}
closeButtonVaraint="tertiary"
confirmButtonText={intl.formatMessage(messages.deleteConfirmationDelete)}
onDelete={() => {
dispatch(removeThread(post.id));
history.push('.');
hideDeleteConfirmation();
}}
/>
{!post.abuseFlagged && (
<Confirmation
isOpen={isReporting}
title={intl.formatMessage(messages.reportPostTitle)}
description={intl.formatMessage(messages.reportPostDescription)}
onClose={hideReportConfirmation}
comfirmAction={handleReportConfirmation}
confirmButtonVariant="danger"
/>
)}
<AlertBanner content={post} />
<PostHeader post={post} actionHandlers={actionHandlers} />
<div className="d-flex mt-4 mb-2 text-break font-style-normal text-primary-500">
<HTMLLoader htmlNode={post.renderedBody} id="post" />
</div>
{topicContext && topic && (
<div className={classNames('border px-3 rounded mb-4 border-light-400 align-self-start py-2.5',
{ 'w-100': inContext })}
>
<div className="border px-3 rounded mb-4 border-light-400 align-self-start py-2.5">
<span className="text-gray-500">{intl.formatMessage(messages.relatedTo)}{' '}</span>
<Hyperlink
destination={topicContext.unitLink}
target="_top"
>
{inContext
? (
<>
<span className="w-auto">{topicContext.chapterName}</span>
<span className="mx-1">/</span>
<span className="w-auto">{topicContext.verticalName}</span>
<span className="mx-1">/</span>
<span className="w-auto">{topicContext.unitName}</span>
</>
)
: `${getTopicCategoryName(topic)} / ${topic.name}`}
{`${getTopicCategoryName(topic)} / ${topic.name}`}
</Hyperlink>
</div>
)}

View File

@@ -93,14 +93,14 @@ function PostFooter({
</span>
</OverlayTrigger>
<span
className="text-gray-700 mx-1.5 font-weight-500"
className="text-light-700 mx-1.5 font-weight-500"
style={{ fontSize: '16px' }}
>
·
</span>
</>
)}
<span title={post.createdAt} className="text-gray-700">
<span title={post.createdAt} className="text-gray-500">
{timeago.format(post.createdAt, 'time-locale')}
</span>
{!preview && post.closed

View File

@@ -120,7 +120,7 @@ function PostHeader({
</div>
{!preview
&& (
<div className="ml-auto d-flex">
<div className="ml-auto d-flex align-items-center">
<ActionsDropdown commentOrPost={post} actionHandlers={actionHandlers} />
</div>
)}

View File

@@ -1,4 +1,3 @@
/* eslint-disable react/no-unknown-property */
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
@@ -6,8 +5,7 @@ import classNames from 'classnames';
import { Link } from 'react-router-dom';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Badge, Icon, Truncate } from '@edx/paragon';
import { CheckCircle } from '@edx/paragon/icons';
import { Badge, Icon } from '@edx/paragon';
import { PushPin } from '../../../components/icons';
import { AvatarOutlineAndLabelColors, Routes, ThreadType } from '../../../data/constants';
@@ -56,7 +54,7 @@ function PostLink({
}
to={linkUrl}
onClick={() => isSelected(post.id)}
style={{ lineHeight: '22px' }}
style={{ lineHeight: '21px' }}
aria-current={isSelected(post.id) ? 'page' : undefined}
role="option"
tabIndex={(isSelected(post.id) || idx === 0) ? 0 : -1}
@@ -75,28 +73,20 @@ function PostLink({
<div className="d-flex flex-column flex-fill" style={{ minWidth: 0 }}>
<div className="d-flex flex-column justify-content-start mw-100 flex-fill">
<div className="d-flex align-items-center pb-0 mb-0 flex-fill font-weight-500">
<Truncate lines={1} className="mr-1.5" whiteSpace>
<span
class={
classNames('font-weight-500 font-size-14 text-primary-500 font-style-normal font-family-inter align-bottom',
{ 'font-weight-bolder': !read })
}
>
{post.title}
</span>
<span class="align-bottom"> </span>
<span
class="text-gray-700 font-weight-normal font-size-14 font-style-normal font-family-inter align-bottom"
>
{isPostPreviewAvailable(post.previewBody)
? post.previewBody
: intl.formatMessage(messages.postWithoutPreview)}
</span>
</Truncate>
<div
className={
classNames('text-truncate font-weight-500 font-size-14 text-primary-500 font-style-normal font-family-inter',
{ 'font-weight-bolder': !read })
}
>
{post.title}
</div>
{showAnsweredBadge && (
<Icon src={CheckCircle} className="text-success font-weight-500 ml-auto badge-padding" data-testid="check-icon">
<span className="sr-only">{' '}answered</span>
</Icon>
<Badge variant="success" className="font-weight-500 ml-auto badge-padding">
{intl.formatMessage(messages.answered)}
<span className="sr-only">{' '}answered</span>
</Badge>
)}
{canSeeReportedBadge && (
@@ -123,7 +113,17 @@ function PostLink({
authorLabel={post.authorLabel}
labelColor={authorLabelColor && `text-${authorLabelColor}`}
/>
<PostFooter post={post} preview intl={intl} showNewCountLabel={read} />
<div
className="text-truncate text-primary-500 font-weight-normal font-size-14 font-style-normal font-family-inter"
style={{ maxHeight: '1.5rem' }}
>
{isPostPreviewAvailable(post.previewBody)
? post.previewBody
: intl.formatMessage(messages.postWithoutPreview)}
</div>
<div className="mt-1">
<PostFooter post={post} preview intl={intl} showNewCountLabel={read} />
</div>
</div>
</div>
{!showDivider && post.pinned && <div className="pt-1 bg-light-500 border-top border-light-700" />}

View File

@@ -71,21 +71,6 @@ const messages = defineMessages({
id: 'discussions.editor.delete.post.description',
defaultMessage: 'Are you sure you want to permanently delete this post?',
},
deleteConfirmationDelete: {
id: 'discussions.post.delete.confirmation.button.delete',
defaultMessage: 'Delete',
description: 'Delete button shown on delete confirmation dialog',
},
reportPostTitle: {
id: 'discussions.editor.report.post.title',
defaultMessage: 'Report inappropriate content?',
description: 'Title of confirmation dialog shown when reporting a post',
},
reportPostDescription: {
id: 'discussions.editor.report.post.description',
defaultMessage: 'The discussion moderation team will review this content and take appropriate action.',
description: 'Text displayed in confirmation dialog when deleting a post',
},
closePostModalTitle: {
id: 'discussions.post.closePostModal.title',
defaultMessage: 'Close post',

View File

@@ -45,7 +45,6 @@ export const selectNonCoursewareTopics = state => state.topics.nonCoursewareIds.
export const selectTopic = topicId => state => state.topics.topics[topicId];
export const selectTopicsById = topicIds => state => topicIds.map(topicId => state.topics.topics[topicId])
.filter(Boolean);
export const selectTopicsById = topicIds => state => topicIds.map(topicId => state.topics.topics[topicId]);
export const topicsLoadingStatus = state => state.topics.status;

View File

@@ -61,12 +61,7 @@ const messages = defineMessages({
},
unnamedTopicCategories: {
id: 'discussions.topics.unnamed.label',
defaultMessage: 'Unnamed category',
description: 'Text to display in place of topic name if topic name is empty',
},
unnamedTopicSubCategories: {
id: 'discussions.subtopics.unnamed.label',
defaultMessage: 'Unnamed subcategory',
defaultMessage: 'Unnamed Topic',
description: 'Text to display in place of topic name if topic name is empty',
},
});

View File

@@ -51,7 +51,7 @@ function Topic({
<div className="d-flex flex-column flex-fill" style={{ minWidth: 0 }}>
<div className="d-flex flex-column justify-content-start mw-100 flex-fill">
<div className="topic-name text-truncate">
{topic.name || intl.formatMessage(messages.unnamedTopicSubCategories)}
{topic.name}
</div>
</div>
<div className="d-flex align-items-center mt-2.5" style={{ marginBottom: '2px' }}>

View File

@@ -5,9 +5,7 @@ import { generatePath, useRouteMatch } from 'react-router';
import { getConfig } from '@edx/frontend-platform';
import {
CheckCircle,
CheckCircleOutline,
Delete, Edit, Pin, QuestionAnswer, Report, Verified, VerifiedOutline,
Delete, Edit, Pin, QuestionAnswer, Report, VerifiedBadge,
} from '@edx/paragon/icons';
import { InsertLink } from '../components/icons';
@@ -103,7 +101,7 @@ export const ACTIONS_LIST = [
{
id: 'endorse',
action: ContentActions.ENDORSE,
icon: VerifiedOutline,
icon: VerifiedBadge,
label: messages.endorseAction,
conditions: {
endorsed: false,
@@ -113,7 +111,7 @@ export const ACTIONS_LIST = [
{
id: 'unendorse',
action: ContentActions.ENDORSE,
icon: Verified,
icon: VerifiedBadge,
label: messages.unendorseAction,
conditions: {
endorsed: true,
@@ -123,7 +121,7 @@ export const ACTIONS_LIST = [
{
id: 'answer',
action: ContentActions.ENDORSE,
icon: CheckCircleOutline,
icon: VerifiedBadge,
label: messages.markAnsweredAction,
conditions: {
endorsed: false,
@@ -133,7 +131,7 @@ export const ACTIONS_LIST = [
{
id: 'unanswer',
action: ContentActions.ENDORSE,
icon: CheckCircle,
icon: VerifiedBadge,
label: messages.unmarkAnsweredAction,
conditions: {
endorsed: true,
@@ -185,7 +183,6 @@ export function useActions(content) {
.every(condition => condition === true)
: true
);
return ACTIONS_LIST.filter(
({
action,

View File

@@ -4,7 +4,6 @@
"discussions.comments.comment.addResponse": "Add a response",
"discussions.comments.comment.addComment": "Add a comment",
"discussions.comments.comment.abuseFlaggedMessage": "Content reported for staff to review",
"discussions.actions.back.alt": "Back to list",
"discussions.comments.comment.responseCount": "{num, plural,\n =0 {No responses}\n one {Showing # response}\n other {Showing # responses}\n }",
"discussions.comments.comment.endorsedResponseCount": "{num, plural,\n =0 {No endorsed responses}\n one {Showing # endorsed response}\n other {Showing # endorsed responses}\n }",
"discussions.comments.comment.loadMoreComments": "Load more comments",
@@ -28,11 +27,6 @@
"discussions.editor.delete.response.description": "Are you sure you want to permanently delete this response?",
"discussions.editor.delete.comment.title": "Delete comment",
"discussions.editor.delete.comment.description": "Are you sure you want to permanently delete this comment?",
"discussions.delete.confirmation.button.delete": "Delete",
"discussions.editor.response.response.title": "Report inappropriate content?",
"discussions.editor.response.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.editor.report.comment.title": "Report inappropriate content?",
"discussions.editor.report.comment.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.editor.comments.editReasonCode": "Reason for editing",
"discussions.editor.posts.editReasonCode.error": "Select reason for editing",
"discussions.comment.comments.editedBy": "Edited by",
@@ -40,7 +34,6 @@
"discussions.post.closedBy": "Post closed by",
"discussion.comment.repliesHeading": "{count} replies for the response added",
"discussion.comment.time": "{time} ago",
"discussion.thread.notFound": "Thread not found",
"discussions.learner.reported": "{reported} reported",
"discussions.learner.previouslyReported": "{previouslyReported} previously reported",
"discussions.learner.lastLogin": "Last active {lastActiveTime}",
@@ -49,23 +42,23 @@
"discussions.learner.activityForLearner": "Activity for {username}",
"discussions.learner.mostActivity": "Most activity",
"discussions.learner.reportedActivity": "Reported activity",
"discussions.learner.recentActivity": "Recent activity",
"discussions.learner.sortFilterStatus": "All learners sorted by {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussions.learner.sortFilterStatus": "All learners by {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussion.learner.allActivity": "All activity",
"discussion.learner.posts": "Posts",
"discussions.actions.button.alt": "Actions menu",
"discussions.actions.back.alt": "Back",
"discussions.actions.copylink": "Copy link",
"discussions.actions.unpin": "Unpin",
"discussions.confirmation.button.confirm": "Confirm",
"discussions.actions.close": "Close",
"discussions.actions.reopen": "Reopen",
"discussions.actions.report": "Report",
"discussions.actions.unreport": "Unreport",
"discussions.actions.endorse": "Endorse",
"discussions.actions.unendorse": "Unendorse",
"discussions.actions.markAnswered": "Mark as answered",
"discussions.actions.markAnswered": "Mark as Answered",
"discussions.actions.unMarkAnswered": "Unmark as answered",
"discussions.modal.confirmation.button.cancel": "Cancel",
"discussions.delete.confirmation.button.cancel": "Cancel",
"discussions.delete.confirmation.button.delete": "Delete",
"discussions.empty.allTopics": "All discussion activity for these topics will show up here.",
"discussions.empty.allPosts": "All discussion activity for your course will show up here.",
"discussions.empty.myPosts": "Posts you've interacted with will show up here.",
@@ -87,9 +80,6 @@
"discussion.banner.learnMore": "Learn more",
"discussion.banner.shareFeedback": "Share feedback",
"discussion.blackoutBanner.information": "Blackout dates are currently active. Posting in discussions is unavailable at this time.",
"discussions.editor.image.warning.message": "Images having width or height greater than 999px will not be visible when the post, response or comment is viewed using in-line course discussions",
"discussions.editor.image.warning.title": "Warning!",
"discussions.editor.image.warning.dismiss": "Ok",
"discussions.navigation.breadcrumbMenu.allTopics": "Topics",
"discussions.navigation.breadcrumbMenu.showAll": "Show all",
"discussions.navigation.navigationBar.allPosts": "All posts",
@@ -129,8 +119,6 @@
"discussions.post.editor.anonymousToPeersPost": "Post anonymously to peers",
"discussions.editor.posts.editReasonCode": "Reason for editing",
"discussions.editor.posts.showPreview.button": "Show Preview",
"discussions.topic.noName.label": "Unnamed category",
"discussions.subtopic.noName.label": "Unnamed subcategory",
"discussions.posts.filter.showALl": "Show all",
"discussions.posts.filter.discussions": "Discussions",
"discussions.posts.filter.questions": "Questions",
@@ -140,7 +128,7 @@
"discussions.posts.status.filter.following": "Following",
"discussions.posts.status.filter.reported": "Reported",
"discussions.posts.status.filter.unanswered": "Unanswered",
"discussions.posts.status.filter.unresponded": "Not responded",
"discussions.posts.status.filter.unresponded": "Unresponded",
"discussions.posts.filter.myPosts": "My posts",
"discussions.posts.filter.myDiscussions": "My discussions",
"discussions.posts.filter.myQuestions": "My questions",
@@ -148,7 +136,7 @@
"discussions.posts.sort.lastActivity": "Recent activity",
"discussions.posts.sort.commentCount": "Most activity",
"discussions.posts.sort.voteCount": "Most likes",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } sorted by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most likes}\n other {{sort}}\n }",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most votes}\n other {{sort}}\n }",
"discussions.post.author.anonymous": "anonymous",
"discussions.post.lastResponse": "Last response {time}",
"discussions.post.postedOn": "Posted {time} by {author} {authorLabel}",
@@ -164,9 +152,6 @@
"discussions.post.relatedTo": "Related to",
"discussions.editor.delete.post.title": "Delete post",
"discussions.editor.delete.post.description": "Are you sure you want to permanently delete this post?",
"discussions.post.delete.confirmation.button.delete": "Delete",
"discussions.editor.report.post.title": "Report inappropriate content?",
"discussions.editor.report.post.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.post.closePostModal.title": "Close post",
"discussions.post.closePostModal.text": "Enter a reason for closing this post. This will only be displayed to other moderators.",
"discussions.post.closePostModal.reasonCodeInput": "Reason",
@@ -186,6 +171,5 @@
"discussions.topics.sort.courseStructure": "Course Structure",
"discussions.topics.find.label": "Search topics",
"discussions.topics.archived.label": "Archived",
"discussions.topics.unnamed.label": "Unnamed category",
"discussions.subtopics.unnamed.label": "Unnamed subcategory"
"discussions.topics.unnamed.label": "Unnamed Topic"
}

View File

@@ -4,7 +4,6 @@
"discussions.comments.comment.addResponse": "Add a response",
"discussions.comments.comment.addComment": "Add a comment",
"discussions.comments.comment.abuseFlaggedMessage": "Content reported for staff to review",
"discussions.actions.back.alt": "Back to list",
"discussions.comments.comment.responseCount": "{num, plural,\n =0 {No responses}\n one {Showing # response}\n other {Showing # responses}\n }",
"discussions.comments.comment.endorsedResponseCount": "{num, plural,\n =0 {No endorsed responses}\n one {Showing # endorsed response}\n other {Showing # endorsed responses}\n }",
"discussions.comments.comment.loadMoreComments": "Load more comments",
@@ -28,11 +27,6 @@
"discussions.editor.delete.response.description": "Are you sure you want to permanently delete this response?",
"discussions.editor.delete.comment.title": "Delete comment",
"discussions.editor.delete.comment.description": "Are you sure you want to permanently delete this comment?",
"discussions.delete.confirmation.button.delete": "Delete",
"discussions.editor.response.response.title": "Report inappropriate content?",
"discussions.editor.response.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.editor.report.comment.title": "Report inappropriate content?",
"discussions.editor.report.comment.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.editor.comments.editReasonCode": "Reason for editing",
"discussions.editor.posts.editReasonCode.error": "Select reason for editing",
"discussions.comment.comments.editedBy": "Edited by",
@@ -40,7 +34,6 @@
"discussions.post.closedBy": "Post closed by",
"discussion.comment.repliesHeading": "{count} replies for the response added",
"discussion.comment.time": "{time} ago",
"discussion.thread.notFound": "Thread not found",
"discussions.learner.reported": "{reported} reported",
"discussions.learner.previouslyReported": "{previouslyReported} previously reported",
"discussions.learner.lastLogin": "Last active {lastActiveTime}",
@@ -49,23 +42,23 @@
"discussions.learner.activityForLearner": "Activity for {username}",
"discussions.learner.mostActivity": "Most activity",
"discussions.learner.reportedActivity": "Reported activity",
"discussions.learner.recentActivity": "Recent activity",
"discussions.learner.sortFilterStatus": "All learners sorted by {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussions.learner.sortFilterStatus": "All learners by {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussion.learner.allActivity": "All activity",
"discussion.learner.posts": "Posts",
"discussions.actions.button.alt": "Actions menu",
"discussions.actions.back.alt": "Back",
"discussions.actions.copylink": "Copy link",
"discussions.actions.unpin": "Unpin",
"discussions.confirmation.button.confirm": "Confirm",
"discussions.actions.close": "Close",
"discussions.actions.reopen": "Reopen",
"discussions.actions.report": "Report",
"discussions.actions.unreport": "Unreport",
"discussions.actions.endorse": "Endorse",
"discussions.actions.unendorse": "Unendorse",
"discussions.actions.markAnswered": "Mark as answered",
"discussions.actions.markAnswered": "Mark as Answered",
"discussions.actions.unMarkAnswered": "Unmark as answered",
"discussions.modal.confirmation.button.cancel": "Cancel",
"discussions.delete.confirmation.button.cancel": "Cancel",
"discussions.delete.confirmation.button.delete": "Delete",
"discussions.empty.allTopics": "All discussion activity for these topics will show up here.",
"discussions.empty.allPosts": "All discussion activity for your course will show up here.",
"discussions.empty.myPosts": "Posts you've interacted with will show up here.",
@@ -87,9 +80,6 @@
"discussion.banner.learnMore": "Learn more",
"discussion.banner.shareFeedback": "Share feedback",
"discussion.blackoutBanner.information": "Blackout dates are currently active. Posting in discussions is unavailable at this time.",
"discussions.editor.image.warning.message": "Images having width or height greater than 999px will not be visible when the post, response or comment is viewed using in-line course discussions",
"discussions.editor.image.warning.title": "Warning!",
"discussions.editor.image.warning.dismiss": "Ok",
"discussions.navigation.breadcrumbMenu.allTopics": "Topics",
"discussions.navigation.breadcrumbMenu.showAll": "Show all",
"discussions.navigation.navigationBar.allPosts": "All posts",
@@ -129,8 +119,6 @@
"discussions.post.editor.anonymousToPeersPost": "Post anonymously to peers",
"discussions.editor.posts.editReasonCode": "Reason for editing",
"discussions.editor.posts.showPreview.button": "Show Preview",
"discussions.topic.noName.label": "Unnamed category",
"discussions.subtopic.noName.label": "Unnamed subcategory",
"discussions.posts.filter.showALl": "Show all",
"discussions.posts.filter.discussions": "Discussions",
"discussions.posts.filter.questions": "Questions",
@@ -140,7 +128,7 @@
"discussions.posts.status.filter.following": "Following",
"discussions.posts.status.filter.reported": "Reported",
"discussions.posts.status.filter.unanswered": "Unanswered",
"discussions.posts.status.filter.unresponded": "Not responded",
"discussions.posts.status.filter.unresponded": "Unresponded",
"discussions.posts.filter.myPosts": "My posts",
"discussions.posts.filter.myDiscussions": "My discussions",
"discussions.posts.filter.myQuestions": "My questions",
@@ -148,7 +136,7 @@
"discussions.posts.sort.lastActivity": "Recent activity",
"discussions.posts.sort.commentCount": "Most activity",
"discussions.posts.sort.voteCount": "Most likes",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } sorted by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most likes}\n other {{sort}}\n }",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most votes}\n other {{sort}}\n }",
"discussions.post.author.anonymous": "anonymous",
"discussions.post.lastResponse": "Last response {time}",
"discussions.post.postedOn": "Posted {time} by {author} {authorLabel}",
@@ -164,9 +152,6 @@
"discussions.post.relatedTo": "Related to",
"discussions.editor.delete.post.title": "Delete post",
"discussions.editor.delete.post.description": "Are you sure you want to permanently delete this post?",
"discussions.post.delete.confirmation.button.delete": "Delete",
"discussions.editor.report.post.title": "Report inappropriate content?",
"discussions.editor.report.post.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.post.closePostModal.title": "Close post",
"discussions.post.closePostModal.text": "Enter a reason for closing this post. This will only be displayed to other moderators.",
"discussions.post.closePostModal.reasonCodeInput": "Reason",
@@ -186,6 +171,5 @@
"discussions.topics.sort.courseStructure": "Course Structure",
"discussions.topics.find.label": "Search topics",
"discussions.topics.archived.label": "Archived",
"discussions.topics.unnamed.label": "Unnamed category",
"discussions.subtopics.unnamed.label": "Unnamed subcategory"
"discussions.topics.unnamed.label": "Unnamed Topic"
}

View File

@@ -4,7 +4,6 @@
"discussions.comments.comment.addResponse": "Agregar una respuesta",
"discussions.comments.comment.addComment": "Agregar un comentario",
"discussions.comments.comment.abuseFlaggedMessage": "Contenido informado para que el personal lo revise",
"discussions.actions.back.alt": "Back to list",
"discussions.comments.comment.responseCount": "{num, plural, =0 {No responses} one {Showing # response} other {Showing # responses} }",
"discussions.comments.comment.endorsedResponseCount": "{num, plural, =0 {Sin respuestas respaldadas} one {Mostrando # respuesta respaldada} other {Mostrando # respuestas respaldadas} }",
"discussions.comments.comment.loadMoreComments": "Cargar más comentarios",
@@ -28,11 +27,6 @@
"discussions.editor.delete.response.description": "¿Está seguro de que desea eliminar esta respuesta de forma permanente?",
"discussions.editor.delete.comment.title": "Eliminar comentario",
"discussions.editor.delete.comment.description": "¿Estás seguro de que quieres eliminar este comentario de forma permanente?",
"discussions.delete.confirmation.button.delete": "Borrar",
"discussions.editor.response.response.title": "Report inappropriate content?",
"discussions.editor.response.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.editor.report.comment.title": "Report inappropriate content?",
"discussions.editor.report.comment.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.editor.comments.editReasonCode": "Razón de la edición",
"discussions.editor.posts.editReasonCode.error": "Seleccione el motivo de la edición",
"discussions.comment.comments.editedBy": "Editado por",
@@ -40,7 +34,6 @@
"discussions.post.closedBy": "Publicación cerrada por",
"discussion.comment.repliesHeading": "{count} respuestas para la respuesta añadida",
"discussion.comment.time": "hace {time}",
"discussion.thread.notFound": "Thread not found",
"discussions.learner.reported": "{reported} informado",
"discussions.learner.previouslyReported": "{previouslyReported} informado anteriormente",
"discussions.learner.lastLogin": "Último activo {lastActiveTime}",
@@ -49,23 +42,23 @@
"discussions.learner.activityForLearner": "Actividad para {username}",
"discussions.learner.mostActivity": "La mayoría de la actividad",
"discussions.learner.reportedActivity": "Actividad reportada",
"discussions.learner.recentActivity": "Recent activity",
"discussions.learner.sortFilterStatus": "All learners sorted by {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussions.learner.sortFilterStatus": "Todos los alumnos por {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussion.learner.allActivity": "Toda la actividad",
"discussion.learner.posts": "Publicaciones",
"discussions.actions.button.alt": "Menú de acciones",
"discussions.actions.back.alt": "Volver atrás",
"discussions.actions.copylink": "Copiar link",
"discussions.actions.unpin": "Desmarcar",
"discussions.confirmation.button.confirm": "Confirm",
"discussions.actions.close": "Cerrar",
"discussions.actions.reopen": "Reabrir",
"discussions.actions.report": "Informar",
"discussions.actions.unreport": "Dejar de denunciar",
"discussions.actions.endorse": "Validar",
"discussions.actions.unendorse": "Invalidar",
"discussions.actions.markAnswered": "Mark as answered",
"discussions.actions.markAnswered": "Marcar como respondida",
"discussions.actions.unMarkAnswered": "Desmarcar como respondida",
"discussions.modal.confirmation.button.cancel": "Cancel",
"discussions.delete.confirmation.button.cancel": "Cancelar",
"discussions.delete.confirmation.button.delete": "Borrar",
"discussions.empty.allTopics": "Toda la actividad de debate de estos temas se mostrará aquí.",
"discussions.empty.allPosts": "Toda la actividad de debate de su curso se mostrará aquí.",
"discussions.empty.myPosts": "Las publicaciones con las que has interactuado se mostrarán aquí.",
@@ -87,9 +80,6 @@
"discussion.banner.learnMore": "Aprender más",
"discussion.banner.shareFeedback": "Compartir comentarios",
"discussion.blackoutBanner.information": "Blackout dates are currently active. Posting in discussions is unavailable at this time.",
"discussions.editor.image.warning.message": "Images having width or height greater than 999px will not be visible when the post, response or comment is viewed using in-line course discussions",
"discussions.editor.image.warning.title": "Warning!",
"discussions.editor.image.warning.dismiss": "Ok",
"discussions.navigation.breadcrumbMenu.allTopics": "Temas",
"discussions.navigation.breadcrumbMenu.showAll": "Mostrar todo",
"discussions.navigation.navigationBar.allPosts": "Todos los mensajes",
@@ -129,8 +119,6 @@
"discussions.post.editor.anonymousToPeersPost": "Publicar de forma anónima para tus compañeros",
"discussions.editor.posts.editReasonCode": "Motivo de la edición",
"discussions.editor.posts.showPreview.button": "Mostrar vista previa",
"discussions.topic.noName.label": "Unnamed category",
"discussions.subtopic.noName.label": "Unnamed subcategory",
"discussions.posts.filter.showALl": "Mostrar todo",
"discussions.posts.filter.discussions": "Debates\n",
"discussions.posts.filter.questions": "Preguntas",
@@ -140,7 +128,7 @@
"discussions.posts.status.filter.following": "Siguiendo",
"discussions.posts.status.filter.reported": "Informado",
"discussions.posts.status.filter.unanswered": "Sin responder",
"discussions.posts.status.filter.unresponded": "Not responded",
"discussions.posts.status.filter.unresponded": "Unresponded",
"discussions.posts.filter.myPosts": "Mis publicaciones",
"discussions.posts.filter.myDiscussions": "Mis debates",
"discussions.posts.filter.myQuestions": "Mis preguntas",
@@ -148,7 +136,7 @@
"discussions.posts.sort.lastActivity": "Actividad reciente",
"discussions.posts.sort.commentCount": "La mayoría de la actividad",
"discussions.posts.sort.voteCount": "La mayoría me gusta",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } sorted by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most likes}\n other {{sort}}\n }",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most votes}\n other {{sort}}\n }",
"discussions.post.author.anonymous": "anónimo",
"discussions.post.lastResponse": "Última respuesta {time}",
"discussions.post.postedOn": "Publicado {time} por {author} {authorLabel}",
@@ -161,12 +149,9 @@
"discussions.post.removeLike": "Dejar de gustar",
"discussions.post.viewActivity": "Ver actividad",
"discussions.post.closed": "Publicación cerrada por respuestas y comentarios.",
"discussions.post.relatedTo": "Related to",
"discussions.post.relatedTo": "Relacionado con",
"discussions.editor.delete.post.title": "Eliminar mensaje",
"discussions.editor.delete.post.description": "¿Seguro que quieres eliminar esta publicación de forma permanente?",
"discussions.post.delete.confirmation.button.delete": "Delete",
"discussions.editor.report.post.title": "Report inappropriate content?",
"discussions.editor.report.post.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.post.closePostModal.title": "Cerrar publicación",
"discussions.post.closePostModal.text": "Escribe un motivo para cerrar esta publicación. Esto solo se mostrará a otros moderadores.",
"discussions.post.closePostModal.reasonCodeInput": "Motivo",
@@ -186,6 +171,5 @@
"discussions.topics.sort.courseStructure": "Estructura del curso",
"discussions.topics.find.label": "Buscar temas",
"discussions.topics.archived.label": "Archivado",
"discussions.topics.unnamed.label": "Unnamed category",
"discussions.subtopics.unnamed.label": "Unnamed subcategory"
"discussions.topics.unnamed.label": "Tema sin nombre"
}

View File

@@ -4,7 +4,6 @@
"discussions.comments.comment.addResponse": "Ajouter une réponse",
"discussions.comments.comment.addComment": "Ajouter un commentaire",
"discussions.comments.comment.abuseFlaggedMessage": "Contenu signalé au personnel pour examen",
"discussions.actions.back.alt": "Back to list",
"discussions.comments.comment.responseCount": "{num, plural,\n =0 {No responses}\n one {Showing # response}\n other {Showing # responses}\n }",
"discussions.comments.comment.endorsedResponseCount": "{num, plural,\n =0 {No endorsed responses}\n one {Showing # endorsed response}\n other {Showing # endorsed responses}\n }",
"discussions.comments.comment.loadMoreComments": "Charger plus de commentaires",
@@ -28,11 +27,6 @@
"discussions.editor.delete.response.description": "Are you sure you want to permanently delete this response?",
"discussions.editor.delete.comment.title": "Delete comment",
"discussions.editor.delete.comment.description": "Are you sure you want to permanently delete this comment?",
"discussions.delete.confirmation.button.delete": "Delete",
"discussions.editor.response.response.title": "Report inappropriate content?",
"discussions.editor.response.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.editor.report.comment.title": "Report inappropriate content?",
"discussions.editor.report.comment.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.editor.comments.editReasonCode": "Reason for editing",
"discussions.editor.posts.editReasonCode.error": "Select reason for editing",
"discussions.comment.comments.editedBy": "Édité par",
@@ -40,7 +34,6 @@
"discussions.post.closedBy": "Message fermé par",
"discussion.comment.repliesHeading": "{count} réponses pour la réponse ajoutée",
"discussion.comment.time": "il y a {time}",
"discussion.thread.notFound": "Thread not found",
"discussions.learner.reported": "{reported} signalé",
"discussions.learner.previouslyReported": "{previouslyReported} previously reported",
"discussions.learner.lastLogin": "Last active {lastActiveTime}",
@@ -49,23 +42,23 @@
"discussions.learner.activityForLearner": "Activité pour {username}",
"discussions.learner.mostActivity": "Most activity",
"discussions.learner.reportedActivity": "Reported activity",
"discussions.learner.recentActivity": "Recent activity",
"discussions.learner.sortFilterStatus": "All learners sorted by {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussions.learner.sortFilterStatus": "All learners by {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussion.learner.allActivity": "All activity",
"discussion.learner.posts": "Posts",
"discussions.actions.button.alt": "Actions menu",
"discussions.actions.back.alt": "Back",
"discussions.actions.copylink": "Copy link",
"discussions.actions.unpin": "Unpin",
"discussions.confirmation.button.confirm": "Confirm",
"discussions.actions.close": "Close",
"discussions.actions.reopen": "Reopen",
"discussions.actions.report": "Report",
"discussions.actions.unreport": "Unreport",
"discussions.actions.endorse": "Endorse",
"discussions.actions.unendorse": "Unendorse",
"discussions.actions.markAnswered": "Mark as answered",
"discussions.actions.markAnswered": "Mark as Answered",
"discussions.actions.unMarkAnswered": "Unmark as answered",
"discussions.modal.confirmation.button.cancel": "Cancel",
"discussions.delete.confirmation.button.cancel": "Cancel",
"discussions.delete.confirmation.button.delete": "Delete",
"discussions.empty.allTopics": "All discussion activity for these topics will show up here.",
"discussions.empty.allPosts": "All discussion activity for your course will show up here.",
"discussions.empty.myPosts": "Posts you've interacted with will show up here.",
@@ -87,9 +80,6 @@
"discussion.banner.learnMore": "Learn more",
"discussion.banner.shareFeedback": "Share feedback",
"discussion.blackoutBanner.information": "Blackout dates are currently active. Posting in discussions is unavailable at this time.",
"discussions.editor.image.warning.message": "Images having width or height greater than 999px will not be visible when the post, response or comment is viewed using in-line course discussions",
"discussions.editor.image.warning.title": "Warning!",
"discussions.editor.image.warning.dismiss": "Ok",
"discussions.navigation.breadcrumbMenu.allTopics": "Topics",
"discussions.navigation.breadcrumbMenu.showAll": "Show all",
"discussions.navigation.navigationBar.allPosts": "All posts",
@@ -129,8 +119,6 @@
"discussions.post.editor.anonymousToPeersPost": "Post anonymously to peers",
"discussions.editor.posts.editReasonCode": "Reason for editing",
"discussions.editor.posts.showPreview.button": "Show Preview",
"discussions.topic.noName.label": "Unnamed category",
"discussions.subtopic.noName.label": "Unnamed subcategory",
"discussions.posts.filter.showALl": "Show all",
"discussions.posts.filter.discussions": "Discussions",
"discussions.posts.filter.questions": "Questions",
@@ -140,7 +128,7 @@
"discussions.posts.status.filter.following": "Following",
"discussions.posts.status.filter.reported": "Reported",
"discussions.posts.status.filter.unanswered": "Unanswered",
"discussions.posts.status.filter.unresponded": "Not responded",
"discussions.posts.status.filter.unresponded": "Unresponded",
"discussions.posts.filter.myPosts": "My posts",
"discussions.posts.filter.myDiscussions": "My discussions",
"discussions.posts.filter.myQuestions": "My questions",
@@ -148,7 +136,7 @@
"discussions.posts.sort.lastActivity": "Recent activity",
"discussions.posts.sort.commentCount": "Most activity",
"discussions.posts.sort.voteCount": "Most likes",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } sorted by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most likes}\n other {{sort}}\n }",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most votes}\n other {{sort}}\n }",
"discussions.post.author.anonymous": "anonymous",
"discussions.post.lastResponse": "Last response {time}",
"discussions.post.postedOn": "Posted {time} by {author} {authorLabel}",
@@ -164,9 +152,6 @@
"discussions.post.relatedTo": "Related to",
"discussions.editor.delete.post.title": "Delete post",
"discussions.editor.delete.post.description": "Are you sure you want to permanently delete this post?",
"discussions.post.delete.confirmation.button.delete": "Delete",
"discussions.editor.report.post.title": "Report inappropriate content?",
"discussions.editor.report.post.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.post.closePostModal.title": "Close post",
"discussions.post.closePostModal.text": "Enter a reason for closing this post. This will only be displayed to other moderators.",
"discussions.post.closePostModal.reasonCodeInput": "Reason",
@@ -186,6 +171,5 @@
"discussions.topics.sort.courseStructure": "Course Structure",
"discussions.topics.find.label": "Search topics",
"discussions.topics.archived.label": "Archived",
"discussions.topics.unnamed.label": "Unnamed category",
"discussions.subtopics.unnamed.label": "Unnamed subcategory"
"discussions.topics.unnamed.label": "Unnamed Topic"
}

View File

@@ -4,7 +4,6 @@
"discussions.comments.comment.addResponse": "Ajouter une réponse",
"discussions.comments.comment.addComment": "Ajouter un commentaire",
"discussions.comments.comment.abuseFlaggedMessage": "Contenu signalé au personnel pour examen",
"discussions.actions.back.alt": "Retour à la liste",
"discussions.comments.comment.responseCount": "{num, plural,\n =0 {Aucune réponse}\n one {Affiche # réponse}\n other {Affiche # réponses}\n }",
"discussions.comments.comment.endorsedResponseCount": "{num, plural,\n =0 {No endorsed responses}\n one {Showing # endorsed response}\n other {Showing # endorsed responses}\n }",
"discussions.comments.comment.loadMoreComments": "Charger plus de commentaires",
@@ -28,11 +27,6 @@
"discussions.editor.delete.response.description": "Êtes-vous sûr de vouloir supprimer définitivement cette réponse ?",
"discussions.editor.delete.comment.title": "Supprimer le commentaire",
"discussions.editor.delete.comment.description": "Êtes-vous sûr de vouloir supprimer définitivement ce commentaire ?",
"discussions.delete.confirmation.button.delete": "Supprimer",
"discussions.editor.response.response.title": "Signaler un contenu inapproprié ?",
"discussions.editor.response.description": "L'équipe de modération de la discussion examinera ce contenu et prendra les mesures appropriées.",
"discussions.editor.report.comment.title": "Signaler un contenu inapproprié ?",
"discussions.editor.report.comment.description": "L'équipe de modération de la discussion examinera ce contenu et prendra les mesures appropriées.",
"discussions.editor.comments.editReasonCode": "Raison de la modification",
"discussions.editor.posts.editReasonCode.error": "Sélectionnez la raison de la modification",
"discussions.comment.comments.editedBy": "Édité par",
@@ -40,7 +34,6 @@
"discussions.post.closedBy": "Message fermé par",
"discussion.comment.repliesHeading": "{count} réponses pour la réponse ajoutée",
"discussion.comment.time": "il y a {time}",
"discussion.thread.notFound": "Sujet introuvable",
"discussions.learner.reported": "{reported} signalé",
"discussions.learner.previouslyReported": "{previouslyReported} signalé précédemment",
"discussions.learner.lastLogin": "Dernier actif {lastActiveTime}",
@@ -49,14 +42,13 @@
"discussions.learner.activityForLearner": "Activité pour {username}",
"discussions.learner.mostActivity": "La plupart des activités",
"discussions.learner.reportedActivity": "Activité signalée",
"discussions.learner.recentActivity": "Activité récente",
"discussions.learner.sortFilterStatus": "Tous les apprenants triés pas {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussions.learner.sortFilterStatus": "Tous les apprenants par {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussion.learner.allActivity": "Toutes les activités",
"discussion.learner.posts": "Posts",
"discussions.actions.button.alt": "Menu Actions",
"discussions.actions.back.alt": "Retour",
"discussions.actions.copylink": "Copier le lien",
"discussions.actions.unpin": "Détacher",
"discussions.confirmation.button.confirm": "Confirmer",
"discussions.actions.close": "Fermer",
"discussions.actions.reopen": "Rouvrir",
"discussions.actions.report": "Signaler",
@@ -65,7 +57,8 @@
"discussions.actions.unendorse": "Ne plus approuver",
"discussions.actions.markAnswered": "Marquer comme répondu",
"discussions.actions.unMarkAnswered": "Décocher comme répondu",
"discussions.modal.confirmation.button.cancel": "Annuler",
"discussions.delete.confirmation.button.cancel": "Annuler",
"discussions.delete.confirmation.button.delete": "Supprimer",
"discussions.empty.allTopics": "Toutes les activités de discussion pour ces sujets apparaîtront ici.",
"discussions.empty.allPosts": "Toutes les activités de discussion pour votre cours s'afficheront ici.",
"discussions.empty.myPosts": "Les publications avec lesquelles vous avez interagi s'afficheront ici.",
@@ -87,9 +80,6 @@
"discussion.banner.learnMore": "En savoir plus",
"discussion.banner.shareFeedback": "Partager vos commentaires",
"discussion.blackoutBanner.information": "Les dates d'interdiction sont actuellement actives. La publication dans les discussions n'est pas disponible pour le moment.",
"discussions.editor.image.warning.message": "Les images dont la largeur ou la hauteur est supérieure à 999 pixels ne seront pas visibles lorsque la publication, la réponse ou le commentaire est affiché à l'aide de discussions de cours en ligne",
"discussions.editor.image.warning.title": "Avertissement!",
"discussions.editor.image.warning.dismiss": "Ok",
"discussions.navigation.breadcrumbMenu.allTopics": "Sujets",
"discussions.navigation.breadcrumbMenu.showAll": "Tout afficher",
"discussions.navigation.navigationBar.allPosts": "Tous les messages",
@@ -129,8 +119,6 @@
"discussions.post.editor.anonymousToPeersPost": "Publiez anonymement à vos pairs",
"discussions.editor.posts.editReasonCode": "Raison de la modification",
"discussions.editor.posts.showPreview.button": "Afficher l'aperçu",
"discussions.topic.noName.label": "Catégorie sans nom",
"discussions.subtopic.noName.label": "Sous-catégorie sans nom",
"discussions.posts.filter.showALl": "Tout afficher",
"discussions.posts.filter.discussions": "Discussions",
"discussions.posts.filter.questions": "Questions",
@@ -140,7 +128,7 @@
"discussions.posts.status.filter.following": "Suivi",
"discussions.posts.status.filter.reported": "Reporté",
"discussions.posts.status.filter.unanswered": "Non répondu",
"discussions.posts.status.filter.unresponded": "Pas répondu",
"discussions.posts.status.filter.unresponded": "Unresponded",
"discussions.posts.filter.myPosts": "Mes messages",
"discussions.posts.filter.myDiscussions": "Mes discussions",
"discussions.posts.filter.myQuestions": "Mes questions",
@@ -148,7 +136,7 @@
"discussions.posts.sort.lastActivity": "Activité récente",
"discussions.posts.sort.commentCount": "La plupart des activités",
"discussions.posts.sort.voteCount": "La plupart des aimés",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } sorted by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most likes}\n other {{sort}}\n }",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most votes}\n other {{sort}}\n }",
"discussions.post.author.anonymous": "anonyme",
"discussions.post.lastResponse": "Dernière réponse {time}",
"discussions.post.postedOn": "Publié {time} par {author} {authorLabel}",
@@ -164,9 +152,6 @@
"discussions.post.relatedTo": "Relative à",
"discussions.editor.delete.post.title": "Supprimer le message",
"discussions.editor.delete.post.description": "Êtes-vous sûr de vouloir supprimer définitivement ce message ?",
"discussions.post.delete.confirmation.button.delete": "Supprimer",
"discussions.editor.report.post.title": "Signaler un contenu inapproprié ?",
"discussions.editor.report.post.description": "L'équipe de modération de la discussion examinera ce contenu et prendra les mesures appropriées.",
"discussions.post.closePostModal.title": "Fermer le message",
"discussions.post.closePostModal.text": "Entrez une raison pour fermer ce message. Cela ne sera affiché qu'aux autres modérateurs.",
"discussions.post.closePostModal.reasonCodeInput": "Raison",
@@ -186,6 +171,5 @@
"discussions.topics.sort.courseStructure": "Structure du cours",
"discussions.topics.find.label": "Rechercher des sujets",
"discussions.topics.archived.label": "Archivé",
"discussions.topics.unnamed.label": "Catégorie sans nom",
"discussions.subtopics.unnamed.label": "Sous-catégorie sans nom"
"discussions.topics.unnamed.label": "Sujet sans nom"
}

View File

@@ -4,7 +4,6 @@
"discussions.comments.comment.addResponse": "Add a response",
"discussions.comments.comment.addComment": "Add a comment",
"discussions.comments.comment.abuseFlaggedMessage": "Content reported for staff to review",
"discussions.actions.back.alt": "Back to list",
"discussions.comments.comment.responseCount": "{num, plural,\n =0 {No responses}\n one {Showing # response}\n other {Showing # responses}\n }",
"discussions.comments.comment.endorsedResponseCount": "{num, plural,\n =0 {No endorsed responses}\n one {Showing # endorsed response}\n other {Showing # endorsed responses}\n }",
"discussions.comments.comment.loadMoreComments": "Load more comments",
@@ -28,11 +27,6 @@
"discussions.editor.delete.response.description": "Are you sure you want to permanently delete this response?",
"discussions.editor.delete.comment.title": "Delete comment",
"discussions.editor.delete.comment.description": "Are you sure you want to permanently delete this comment?",
"discussions.delete.confirmation.button.delete": "Delete",
"discussions.editor.response.response.title": "Report inappropriate content?",
"discussions.editor.response.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.editor.report.comment.title": "Report inappropriate content?",
"discussions.editor.report.comment.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.editor.comments.editReasonCode": "Reason for editing",
"discussions.editor.posts.editReasonCode.error": "Select reason for editing",
"discussions.comment.comments.editedBy": "Edited by",
@@ -40,7 +34,6 @@
"discussions.post.closedBy": "Post closed by",
"discussion.comment.repliesHeading": "{count} replies for the response added",
"discussion.comment.time": "{time} ago",
"discussion.thread.notFound": "Thread not found",
"discussions.learner.reported": "{reported} reported",
"discussions.learner.previouslyReported": "{previouslyReported} previously reported",
"discussions.learner.lastLogin": "Last active {lastActiveTime}",
@@ -49,23 +42,23 @@
"discussions.learner.activityForLearner": "Activity for {username}",
"discussions.learner.mostActivity": "Most activity",
"discussions.learner.reportedActivity": "Reported activity",
"discussions.learner.recentActivity": "Recent activity",
"discussions.learner.sortFilterStatus": "All learners sorted by {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussions.learner.sortFilterStatus": "All learners by {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussion.learner.allActivity": "All activity",
"discussion.learner.posts": "Posts",
"discussions.actions.button.alt": "Actions menu",
"discussions.actions.back.alt": "Back",
"discussions.actions.copylink": "Copy link",
"discussions.actions.unpin": "Unpin",
"discussions.confirmation.button.confirm": "Confirm",
"discussions.actions.close": "Close",
"discussions.actions.reopen": "Reopen",
"discussions.actions.report": "Report",
"discussions.actions.unreport": "Unreport",
"discussions.actions.endorse": "Endorse",
"discussions.actions.unendorse": "Unendorse",
"discussions.actions.markAnswered": "Mark as answered",
"discussions.actions.markAnswered": "Mark as Answered",
"discussions.actions.unMarkAnswered": "Unmark as answered",
"discussions.modal.confirmation.button.cancel": "Cancel",
"discussions.delete.confirmation.button.cancel": "Cancel",
"discussions.delete.confirmation.button.delete": "Delete",
"discussions.empty.allTopics": "All discussion activity for these topics will show up here.",
"discussions.empty.allPosts": "All discussion activity for your course will show up here.",
"discussions.empty.myPosts": "Posts you've interacted with will show up here.",
@@ -87,9 +80,6 @@
"discussion.banner.learnMore": "Learn more",
"discussion.banner.shareFeedback": "Share feedback",
"discussion.blackoutBanner.information": "Blackout dates are currently active. Posting in discussions is unavailable at this time.",
"discussions.editor.image.warning.message": "Images having width or height greater than 999px will not be visible when the post, response or comment is viewed using in-line course discussions",
"discussions.editor.image.warning.title": "Warning!",
"discussions.editor.image.warning.dismiss": "Ok",
"discussions.navigation.breadcrumbMenu.allTopics": "Topics",
"discussions.navigation.breadcrumbMenu.showAll": "Show all",
"discussions.navigation.navigationBar.allPosts": "All posts",
@@ -129,8 +119,6 @@
"discussions.post.editor.anonymousToPeersPost": "Post anonymously to peers",
"discussions.editor.posts.editReasonCode": "Reason for editing",
"discussions.editor.posts.showPreview.button": "Show Preview",
"discussions.topic.noName.label": "Unnamed category",
"discussions.subtopic.noName.label": "Unnamed subcategory",
"discussions.posts.filter.showALl": "Show all",
"discussions.posts.filter.discussions": "Discussions",
"discussions.posts.filter.questions": "Questions",
@@ -140,7 +128,7 @@
"discussions.posts.status.filter.following": "Following",
"discussions.posts.status.filter.reported": "Reported",
"discussions.posts.status.filter.unanswered": "Unanswered",
"discussions.posts.status.filter.unresponded": "Not responded",
"discussions.posts.status.filter.unresponded": "Unresponded",
"discussions.posts.filter.myPosts": "My posts",
"discussions.posts.filter.myDiscussions": "My discussions",
"discussions.posts.filter.myQuestions": "My questions",
@@ -148,7 +136,7 @@
"discussions.posts.sort.lastActivity": "Recent activity",
"discussions.posts.sort.commentCount": "Most activity",
"discussions.posts.sort.voteCount": "Most likes",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } sorted by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most likes}\n other {{sort}}\n }",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most votes}\n other {{sort}}\n }",
"discussions.post.author.anonymous": "anonymous",
"discussions.post.lastResponse": "Last response {time}",
"discussions.post.postedOn": "Posted {time} by {author} {authorLabel}",
@@ -164,9 +152,6 @@
"discussions.post.relatedTo": "Related to",
"discussions.editor.delete.post.title": "Delete post",
"discussions.editor.delete.post.description": "Are you sure you want to permanently delete this post?",
"discussions.post.delete.confirmation.button.delete": "Delete",
"discussions.editor.report.post.title": "Report inappropriate content?",
"discussions.editor.report.post.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.post.closePostModal.title": "Close post",
"discussions.post.closePostModal.text": "Enter a reason for closing this post. This will only be displayed to other moderators.",
"discussions.post.closePostModal.reasonCodeInput": "Reason",
@@ -186,6 +171,5 @@
"discussions.topics.sort.courseStructure": "Course Structure",
"discussions.topics.find.label": "Search topics",
"discussions.topics.archived.label": "Archived",
"discussions.topics.unnamed.label": "Unnamed category",
"discussions.subtopics.unnamed.label": "Unnamed subcategory"
"discussions.topics.unnamed.label": "Unnamed Topic"
}

View File

@@ -4,7 +4,6 @@
"discussions.comments.comment.addResponse": "Aggiungi una risposta",
"discussions.comments.comment.addComment": "Aggiungi un commento",
"discussions.comments.comment.abuseFlaggedMessage": "Contenuto segnalato per la revisione da parte del personale",
"discussions.actions.back.alt": "Back to list",
"discussions.comments.comment.responseCount": "{num, plural, =0 {Nessuna risposta} one {Mostra # risposte} other {Mostra # risposte} }",
"discussions.comments.comment.endorsedResponseCount": "{num, plural, =0 {Nessuna risposta approvata} one {Mostra # risposta approvata} other {Mostra # risposte approvate} }",
"discussions.comments.comment.loadMoreComments": "Carica più commenti",
@@ -28,11 +27,6 @@
"discussions.editor.delete.response.description": "Sei sicuro di voler eliminare definitivamente questa risposta?",
"discussions.editor.delete.comment.title": "Elimina commento",
"discussions.editor.delete.comment.description": "Sei sicuro di voler eliminare definitivamente questo commento?",
"discussions.delete.confirmation.button.delete": "Cancella",
"discussions.editor.response.response.title": "Report inappropriate content?",
"discussions.editor.response.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.editor.report.comment.title": "Report inappropriate content?",
"discussions.editor.report.comment.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.editor.comments.editReasonCode": "Motivo della modifica",
"discussions.editor.posts.editReasonCode.error": "Seleziona il motivo per la modifica",
"discussions.comment.comments.editedBy": "A cura di",
@@ -40,7 +34,6 @@
"discussions.post.closedBy": "Post chiuso da",
"discussion.comment.repliesHeading": "{count} risposte per la risposta aggiunta",
"discussion.comment.time": "{time} fa",
"discussion.thread.notFound": "Thread not found",
"discussions.learner.reported": "{reported} segnalato",
"discussions.learner.previouslyReported": "{previouslyReported} segnalato in precedenza",
"discussions.learner.lastLogin": "Ultimo attivo {lastActiveTime}",
@@ -49,23 +42,23 @@
"discussions.learner.activityForLearner": "Attività per {username}",
"discussions.learner.mostActivity": "La maggior parte delle attività",
"discussions.learner.reportedActivity": "Attività segnalata",
"discussions.learner.recentActivity": "Recent activity",
"discussions.learner.sortFilterStatus": "All learners sorted by {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussions.learner.sortFilterStatus": "Tutti gli studenti di {sort, select, flagged {attività segnalata} activity {più attività} other {{sort}} }",
"discussion.learner.allActivity": "All activity",
"discussion.learner.posts": "Posts",
"discussions.actions.button.alt": "Menù Azioni",
"discussions.actions.back.alt": "Indietro",
"discussions.actions.copylink": "Copia link",
"discussions.actions.unpin": "Sblocca ",
"discussions.confirmation.button.confirm": "Confirm",
"discussions.actions.close": "Chiudi",
"discussions.actions.reopen": "Riaprire",
"discussions.actions.report": "Segnala",
"discussions.actions.unreport": "Annulla segnalazione",
"discussions.actions.endorse": "Promuovi ",
"discussions.actions.unendorse": "Annulla promozione ",
"discussions.actions.markAnswered": "Mark as answered",
"discussions.actions.markAnswered": "Segna come risposta",
"discussions.actions.unMarkAnswered": "Deseleziona come risposta",
"discussions.modal.confirmation.button.cancel": "Cancel",
"discussions.delete.confirmation.button.cancel": "Annulla",
"discussions.delete.confirmation.button.delete": "Cancella",
"discussions.empty.allTopics": "Tutte le attività di discussione per questi argomenti verranno visualizzate qui.",
"discussions.empty.allPosts": "Tutte le attività di discussione per il tuo corso verranno visualizzate qui.",
"discussions.empty.myPosts": "I post con cui hai interagito verranno visualizzati qui.",
@@ -87,9 +80,6 @@
"discussion.banner.learnMore": "Per saperne di più",
"discussion.banner.shareFeedback": "Condividi feedback",
"discussion.blackoutBanner.information": "Blackout dates are currently active. Posting in discussions is unavailable at this time.",
"discussions.editor.image.warning.message": "Images having width or height greater than 999px will not be visible when the post, response or comment is viewed using in-line course discussions",
"discussions.editor.image.warning.title": "Warning!",
"discussions.editor.image.warning.dismiss": "Ok",
"discussions.navigation.breadcrumbMenu.allTopics": "Argomenti",
"discussions.navigation.breadcrumbMenu.showAll": "Mostra tutto",
"discussions.navigation.navigationBar.allPosts": "Tutti i post",
@@ -129,8 +119,6 @@
"discussions.post.editor.anonymousToPeersPost": "Pubblica in modo anonimo ai colleghi",
"discussions.editor.posts.editReasonCode": "Motivo della modifica",
"discussions.editor.posts.showPreview.button": "Anteprima dello spettacolo",
"discussions.topic.noName.label": "Unnamed category",
"discussions.subtopic.noName.label": "Unnamed subcategory",
"discussions.posts.filter.showALl": "Mostra tutto",
"discussions.posts.filter.discussions": "Discussioni",
"discussions.posts.filter.questions": "Domande",
@@ -140,7 +128,7 @@
"discussions.posts.status.filter.following": "Seguente",
"discussions.posts.status.filter.reported": "Segnalato ",
"discussions.posts.status.filter.unanswered": "Senza Risposta",
"discussions.posts.status.filter.unresponded": "Not responded",
"discussions.posts.status.filter.unresponded": "Unresponded",
"discussions.posts.filter.myPosts": "I miei post",
"discussions.posts.filter.myDiscussions": "Le mie discussioni",
"discussions.posts.filter.myQuestions": "Le mie domande",
@@ -148,7 +136,7 @@
"discussions.posts.sort.lastActivity": "Attività Recente",
"discussions.posts.sort.commentCount": "La maggior parte delle attività",
"discussions.posts.sort.voteCount": "La maggior parte dei Mi piace",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } sorted by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most likes}\n other {{sort}}\n }",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most votes}\n other {{sort}}\n }",
"discussions.post.author.anonymous": "anonimo",
"discussions.post.lastResponse": "Ultima risposta {time}",
"discussions.post.postedOn": "Inserito {time} da {author} {authorLabel}",
@@ -161,12 +149,9 @@
"discussions.post.removeLike": "A differenza di",
"discussions.post.viewActivity": "Visualizza attività",
"discussions.post.closed": "Post chiuso per risposte e commenti",
"discussions.post.relatedTo": "Related to",
"discussions.post.relatedTo": "Relativo a",
"discussions.editor.delete.post.title": "Elimina messaggio",
"discussions.editor.delete.post.description": "Sei sicuro di voler eliminare definitivamente questo post?",
"discussions.post.delete.confirmation.button.delete": "Delete",
"discussions.editor.report.post.title": "Report inappropriate content?",
"discussions.editor.report.post.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.post.closePostModal.title": "Chiudi messaggio",
"discussions.post.closePostModal.text": "Inserisci un motivo per chiudere questo post. Questo verrà mostrato solo agli altri moderatori.",
"discussions.post.closePostModal.reasonCodeInput": "Motivo ",
@@ -186,6 +171,5 @@
"discussions.topics.sort.courseStructure": "Struttura Corso",
"discussions.topics.find.label": "Search topics",
"discussions.topics.archived.label": "Archiviati",
"discussions.topics.unnamed.label": "Unnamed category",
"discussions.subtopics.unnamed.label": "Unnamed subcategory"
"discussions.topics.unnamed.label": "Unnamed Topic"
}

View File

@@ -4,7 +4,6 @@
"discussions.comments.comment.addResponse": "Dodaj odpowiedź",
"discussions.comments.comment.addComment": "Add a comment",
"discussions.comments.comment.abuseFlaggedMessage": "Content reported for staff to review",
"discussions.actions.back.alt": "Back to list",
"discussions.comments.comment.responseCount": "{num, plural,\n=0 {No responses}\none {Showing # response}\nother {Showing # responses}\n}",
"discussions.comments.comment.endorsedResponseCount": "{num, plural,\n=0 {No endorsed responses}\none {Showing # endorsed response}\nother {Showing # endorsed responses}\n}",
"discussions.comments.comment.loadMoreComments": "Załaduj więcej komentarzy",
@@ -28,11 +27,6 @@
"discussions.editor.delete.response.description": "Czy na pewno chcesz trwale usunąć tę odpowiedź?",
"discussions.editor.delete.comment.title": "Usuń komentarz",
"discussions.editor.delete.comment.description": "Czy na pewno chcesz trwale usunąć ten komentarz?",
"discussions.delete.confirmation.button.delete": "Delete",
"discussions.editor.response.response.title": "Report inappropriate content?",
"discussions.editor.response.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.editor.report.comment.title": "Report inappropriate content?",
"discussions.editor.report.comment.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.editor.comments.editReasonCode": "Reason for editing",
"discussions.editor.posts.editReasonCode.error": "Wybierz powód edycji",
"discussions.comment.comments.editedBy": "Edytowany przez",
@@ -40,7 +34,6 @@
"discussions.post.closedBy": "Post zamknięty przez",
"discussion.comment.repliesHeading": "{count} replies for the response added",
"discussion.comment.time": "{time} ago",
"discussion.thread.notFound": "Thread not found",
"discussions.learner.reported": "{reported} zgłoszone",
"discussions.learner.previouslyReported": "{previouslyReported} previously reported",
"discussions.learner.lastLogin": "Ostatnia aktywność {lastActiveTime}",
@@ -49,23 +42,23 @@
"discussions.learner.activityForLearner": "Aktywność dla {username}",
"discussions.learner.mostActivity": "Największa aktywność",
"discussions.learner.reportedActivity": "Zgłoszona aktywność",
"discussions.learner.recentActivity": "Recent activity",
"discussions.learner.sortFilterStatus": "All learners sorted by {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussions.learner.sortFilterStatus": "All learners by {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussion.learner.allActivity": "All activity",
"discussion.learner.posts": "Posts",
"discussions.actions.button.alt": "Menu czynności",
"discussions.actions.back.alt": "Back",
"discussions.actions.copylink": "Copy link",
"discussions.actions.unpin": "Unpin",
"discussions.confirmation.button.confirm": "Confirm",
"discussions.actions.close": "Close",
"discussions.actions.reopen": "Otwórz ponownie",
"discussions.actions.report": "Report",
"discussions.actions.unreport": "Unreport",
"discussions.actions.endorse": "Endorse",
"discussions.actions.unendorse": "Unendorse",
"discussions.actions.markAnswered": "Mark as answered",
"discussions.actions.markAnswered": "Oznacz jako odpowiedziane",
"discussions.actions.unMarkAnswered": "Nie oznaczaj jako odpowiedziane",
"discussions.modal.confirmation.button.cancel": "Cancel",
"discussions.delete.confirmation.button.cancel": "Cancel",
"discussions.delete.confirmation.button.delete": "Delete",
"discussions.empty.allTopics": "Wszystkie dyskusje dotyczące tych tematów pojawią się tutaj.",
"discussions.empty.allPosts": "All discussion activity for your course will show up here.",
"discussions.empty.myPosts": "Posts you've interacted with will show up here.",
@@ -87,9 +80,6 @@
"discussion.banner.learnMore": "Learn more",
"discussion.banner.shareFeedback": "Share feedback",
"discussion.blackoutBanner.information": "Blackout dates are currently active. Posting in discussions is unavailable at this time.",
"discussions.editor.image.warning.message": "Images having width or height greater than 999px will not be visible when the post, response or comment is viewed using in-line course discussions",
"discussions.editor.image.warning.title": "Warning!",
"discussions.editor.image.warning.dismiss": "Ok",
"discussions.navigation.breadcrumbMenu.allTopics": "Topics",
"discussions.navigation.breadcrumbMenu.showAll": "Show all",
"discussions.navigation.navigationBar.allPosts": "Wszystkie posty",
@@ -129,8 +119,6 @@
"discussions.post.editor.anonymousToPeersPost": "Post anonymously to peers",
"discussions.editor.posts.editReasonCode": "Powód edycji",
"discussions.editor.posts.showPreview.button": "Show Preview",
"discussions.topic.noName.label": "Unnamed category",
"discussions.subtopic.noName.label": "Unnamed subcategory",
"discussions.posts.filter.showALl": "Show all",
"discussions.posts.filter.discussions": "Discussions",
"discussions.posts.filter.questions": "Pytania",
@@ -140,7 +128,7 @@
"discussions.posts.status.filter.following": "Following",
"discussions.posts.status.filter.reported": "Reported",
"discussions.posts.status.filter.unanswered": "Unanswered",
"discussions.posts.status.filter.unresponded": "Not responded",
"discussions.posts.status.filter.unresponded": "Unresponded",
"discussions.posts.filter.myPosts": "Moje posty",
"discussions.posts.filter.myDiscussions": "Moje dyskusje",
"discussions.posts.filter.myQuestions": "Moje pytania",
@@ -148,7 +136,7 @@
"discussions.posts.sort.lastActivity": "Ostatnia aktywność",
"discussions.posts.sort.commentCount": "Most activity",
"discussions.posts.sort.voteCount": "Most likes",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } sorted by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most likes}\n other {{sort}}\n }",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most votes}\n other {{sort}}\n }",
"discussions.post.author.anonymous": "anonymous",
"discussions.post.lastResponse": "Ostatnia odpowiedź {time}",
"discussions.post.postedOn": "Posted {time} by {author} {authorLabel}",
@@ -164,9 +152,6 @@
"discussions.post.relatedTo": "Related to",
"discussions.editor.delete.post.title": "Usuń post",
"discussions.editor.delete.post.description": "Czy na pewno chcesz trwale usunąć ten wpis?",
"discussions.post.delete.confirmation.button.delete": "Delete",
"discussions.editor.report.post.title": "Report inappropriate content?",
"discussions.editor.report.post.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.post.closePostModal.title": "Close post",
"discussions.post.closePostModal.text": "Enter a reason for closing this post. This will only be displayed to other moderators.",
"discussions.post.closePostModal.reasonCodeInput": "Reason",
@@ -186,6 +171,5 @@
"discussions.topics.sort.courseStructure": "Course Structure",
"discussions.topics.find.label": "Search topics",
"discussions.topics.archived.label": "Archived",
"discussions.topics.unnamed.label": "Unnamed category",
"discussions.subtopics.unnamed.label": "Unnamed subcategory"
"discussions.topics.unnamed.label": "Unnamed Topic"
}

View File

@@ -4,18 +4,17 @@
"discussions.comments.comment.addResponse": "Bir cevap ekle",
"discussions.comments.comment.addComment": "Bir yorum ekle",
"discussions.comments.comment.abuseFlaggedMessage": "Personelin incelemesi için bildirilen içerik",
"discussions.actions.back.alt": "Listeye dön",
"discussions.comments.comment.responseCount": "{num, plural,\n =0 {No responses}\n one {Showing # response}\n other {Showing # responses}\n }",
"discussions.comments.comment.endorsedResponseCount": "{num, plural,\n =0 {No endorsed responses}\n one {Showing # endorsed response}\n other {Showing # endorsed responses}\n }",
"discussions.comments.comment.loadMoreComments": "Daha fazla yorum yükle",
"discussions.comments.comment.loadMoreResponses": "Daha fazla yanıt yükle",
"discussions.comments.comment.visibility": "This post is visible to {group, select,\n null {Everyone}\n other {{group}}\n }.",
"discussions.comments.comment.postedTime": "{postType, select,\n discussion {Discussion}\n question {Question}\n other {{postType}}\n } posted {relativeTime} by",
"discussions.comments.comment.commentTime": "{relativeTime} önce gönderildi",
"discussions.comments.comment.commentTime": "Posted {relativeTime}",
"discussions.comments.comment.answer": "Cevap",
"discussions.comments.comment.answeredlabel": "Marked as answered by",
"discussions.comments.comment.endorsed": "Doğrulandı",
"discussions.comments.comment.endorsedlabel": "Doğrulayan",
"discussions.comments.comment.endorsed": "Endorsed",
"discussions.comments.comment.endorsedlabel": "Endorsed by",
"discussions.actions.label": "Eylemler menüsü",
"discussions.actions.edit": "Düzenle",
"discussions.actions.pin": "İşaretle",
@@ -28,19 +27,13 @@
"discussions.editor.delete.response.description": "Bu yanıtı kalıcı olarak silmek istediğinizden emin misiniz?",
"discussions.editor.delete.comment.title": "Yorumu sil",
"discussions.editor.delete.comment.description": "Bu yorumu kalıcı olarak silmek istediğinizden emin misiniz?",
"discussions.delete.confirmation.button.delete": "Sil",
"discussions.editor.response.response.title": "Uygunsuz içerik mi raporlayacaksınız?",
"discussions.editor.response.description": "Tartışma yöneticileri bu içeriği inceleyecek ve uygun işlemi yapacaktır.",
"discussions.editor.report.comment.title": "Uygunsuz içerik mi raporlayacaksınız?",
"discussions.editor.report.comment.description": "Tartışma yöneticileri bu içeriği inceleyecek ve uygun işlemi yapacaktır.",
"discussions.editor.comments.editReasonCode": "Düzenleme nedeni",
"discussions.editor.posts.editReasonCode.error": "Düzenleme nedenini seçin",
"discussions.comment.comments.editedBy": "Düzenleyen",
"discussions.comment.comments.reason": "Gerekçe",
"discussions.post.closedBy": "Gönderiyi kapatan ",
"discussion.comment.repliesHeading": "Eklenen yanıt için {count} yanıt",
"discussion.comment.time": "{time} önce",
"discussion.thread.notFound": "Tartışma zinciri bulunamadı",
"discussions.post.closedBy": "Post closed by",
"discussion.comment.repliesHeading": "{count} replies for the response added",
"discussion.comment.time": "{time} ago",
"discussions.learner.reported": "{reported} rapor edildi",
"discussions.learner.previouslyReported": "{previouslyReported} daha önce rapor edildi",
"discussions.learner.lastLogin": "Son etkinlik {lastActiveTime}",
@@ -49,14 +42,13 @@
"discussions.learner.activityForLearner": "{username} için etkinlik",
"discussions.learner.mostActivity": "En çok etkinlik",
"discussions.learner.reportedActivity": "Rapor edilen etkinlik",
"discussions.learner.recentActivity": "Son etkinlik",
"discussions.learner.sortFilterStatus": "All learners sorted by {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussion.learner.allActivity": "Tüm etkinlikler",
"discussions.learner.sortFilterStatus": "All learners by {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussion.learner.allActivity": "All activity",
"discussion.learner.posts": "Gönderiler",
"discussions.actions.button.alt": "Eylemler menüsü",
"discussions.actions.copylink": "Bağlantıyı kopyala",
"discussions.actions.back.alt": "Geri",
"discussions.actions.copylink": "Copy link",
"discussions.actions.unpin": "İşareti kaldır",
"discussions.confirmation.button.confirm": "Onayla",
"discussions.actions.close": "Kapat",
"discussions.actions.reopen": "Yeniden aç",
"discussions.actions.report": "Raporla",
@@ -65,7 +57,8 @@
"discussions.actions.unendorse": "Destekleme",
"discussions.actions.markAnswered": "Cevaplandı olarak işaretle",
"discussions.actions.unMarkAnswered": "Cevaplandı olarak işaretini kaldır",
"discussions.modal.confirmation.button.cancel": "İptal",
"discussions.delete.confirmation.button.cancel": "İptal",
"discussions.delete.confirmation.button.delete": "Sil",
"discussions.empty.allTopics": "Bu konularla ilgili tüm tartışma etkinlikleri burada gösterilecektir.",
"discussions.empty.allPosts": "Dersiniz için tüm tartışma etkinlikleri burada gösterilecektir.",
"discussions.empty.myPosts": "Etkileşimde bulunduğunuz gönderiler burada gösterilecektir.",
@@ -74,22 +67,19 @@
"discussions.empty.noPostSelected": "Gönderi seçilmedi",
"discussions.empty.noTopicSelected": "Konu seçilmedi",
"discussions.sidebar.noResultsFound": "Sonuç bulunamadı",
"discussions.sidebar.differentKeywords": "Farklı anahtar kelimelerle aramayı dene",
"discussions.sidebar.differentKeywords": "Try searching different keywords",
"discussions.sidebar.removeKeywords": "Farklı anahtar kelimeler aramayı veya bazı filtreleri kaldırmayı deneyin",
"discussions.sidebar.removeKeywordsOnly": "Farklı anahtar kelimelerle aramayı dene",
"discussions.sidebar.removeKeywordsOnly": "Try searching different keywords",
"discussions.sidebar.removeFilters": "Bazı filtreleri kaldırmayı deneyin",
"discussions.empty.iconAlt": "Boş",
"discussions.authors.label.staff": "Personel",
"discussions.authors.label.ta": "TA",
"discussions.learner.loadMostPosts": "Daha fazla ileti yükle",
"discussions.post.anonymous.author": "anonim",
"discussion.banner.welcomeMessage": "🎉 Yeni ve geliştirilmiş tartışma deneyimine hoş geldiniz!",
"discussion.banner.welcomeMessage": "🎉 Welcome to the new and improved discussions experience!",
"discussion.banner.learnMore": "Daha fazlasını öğren",
"discussion.banner.shareFeedback": "Geri bildirim paylaş",
"discussion.banner.shareFeedback": "Share feedback",
"discussion.blackoutBanner.information": "Blackout dates are currently active. Posting in discussions is unavailable at this time.",
"discussions.editor.image.warning.message": "Images having width or height greater than 999px will not be visible when the post, response or comment is viewed using in-line course discussions",
"discussions.editor.image.warning.title": "Uyarı!",
"discussions.editor.image.warning.dismiss": "Tamam",
"discussions.navigation.breadcrumbMenu.allTopics": "Konular",
"discussions.navigation.breadcrumbMenu.showAll": "Tümünü göster",
"discussions.navigation.navigationBar.allPosts": "Tüm iletiler",
@@ -102,7 +92,7 @@
"discussions.actionBar.searchInfo": "\"{text}\" için {count} sonuç gösteriliyor",
"discussions.actionBar.searchRewriteInfo": "No results found for \"{searchString}\". Showing {count} results for \"{textSearchRewrite}\".",
"discussions.actionBar.searchInfoSearching": "Aranıyor...",
"discussions.actionBar.clearSearch": "Sonuçları temizle",
"discussions.actionBar.clearSearch": "Clear results",
"discussion.posts.actionBar.add": "Bir ileti ekle",
"discussion.posts.actionBar.close": "Kapat",
"discussions.post.editor.type": "Gönderi türü",
@@ -115,13 +105,13 @@
"discussions.post.editor.discussionType": "Forum",
"discussions.post.editor.discussionDescription": "Fikirleri paylaşın ve sohbetler başlatın",
"discussions.post.editor.topicArea": "Başlık alanı",
"discussions.post.editor.topicAreaDescription": "Başkalarının bulmasına yardımcı olmak için gönderinizi ilgili bir konuya ekleyin.",
"discussions.post.editor.topicAreaDescription": "Add your post to a relevant topic to help others find it.",
"discussions.post.editor.cohortVisibility": "Kohort görünürlüğü",
"discussions.post.editor.cohortVisibilityAllLearners": "Tüm öğrenciler",
"discussions.post.editor.title": "İleti başlığı",
"discussions.post.editor.title": "Post title",
"discussions.post.editor.titleDescription": "Katılıma teşvik etmek için, açık ve tanıtıcı bir başlık ekleyin.",
"discussions.post.editor.title.error": "Gönderi başlığı boş olamaz.",
"discussions.post.editor.content.error": "Gönderi içeriği boş olamaz.",
"discussions.post.editor.title.error": "Post title cannot be empty.",
"discussions.post.editor.content.error": "Post content cannot be empty.",
"discussions.post.editor.questionText": "Sorunuz veya fikriniz (gerekli)",
"discussions.post.editor.preview": "Önizleme",
"discussions.post.editor.followPost": "Bu iletiyi takip edin",
@@ -129,8 +119,6 @@
"discussions.post.editor.anonymousToPeersPost": "Akranlarına anonim olarak gönder",
"discussions.editor.posts.editReasonCode": "Düzenleme nedeni",
"discussions.editor.posts.showPreview.button": "Önizlemeyi Göster",
"discussions.topic.noName.label": "İsimsiz kategori",
"discussions.subtopic.noName.label": "İsimsiz alt kategori",
"discussions.posts.filter.showALl": "Tümünü göster",
"discussions.posts.filter.discussions": "Forumlar",
"discussions.posts.filter.questions": "Sorular",
@@ -140,7 +128,7 @@
"discussions.posts.status.filter.following": "Takip ediliyor",
"discussions.posts.status.filter.reported": "Rapor edildi",
"discussions.posts.status.filter.unanswered": "Cevaplanmamış",
"discussions.posts.status.filter.unresponded": "Yanıtlanmamış",
"discussions.posts.status.filter.unresponded": "Unresponded",
"discussions.posts.filter.myPosts": "Gönderilerim",
"discussions.posts.filter.myDiscussions": "Tartışmalarım",
"discussions.posts.filter.myQuestions": "Sorularım",
@@ -148,25 +136,22 @@
"discussions.posts.sort.lastActivity": "Son etkinlik",
"discussions.posts.sort.commentCount": "En çok etkinlik",
"discussions.posts.sort.voteCount": "En çok beğenilenler",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } sorted by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most likes}\n other {{sort}}\n }",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most votes}\n other {{sort}}\n }",
"discussions.post.author.anonymous": "anonim",
"discussions.post.lastResponse": "Son yanıt {time}",
"discussions.post.postedOn": "{author} {authorLabel} tarafından {time} önce gönderildi",
"discussions.post.postedOn": "Posted {time} by {author} {authorLabel}",
"discussions.post.contentReported": "Rapor edildi",
"discussions.post.following": "Takip ediliyor",
"discussions.post.follow": "Takip et",
"discussions.post.answered": "Yanıtlandı",
"discussions.post.unFollow": "Takibi bırak",
"discussions.post.like": "Beğen",
"discussions.post.removeLike": "Beğenmeme",
"discussions.post.removeLike": "Unlike",
"discussions.post.viewActivity": "Etkinliği görüntüle",
"discussions.post.closed": "Yanıtlar ve yorumlar için gönderi kapatıldı",
"discussions.post.relatedTo": "Bunun ile ilgili",
"discussions.post.relatedTo": "Related to",
"discussions.editor.delete.post.title": "Gönderiyi sil",
"discussions.editor.delete.post.description": "Bu gönderiyi kalıcı olarak silmek istediğinizden emin misiniz?",
"discussions.post.delete.confirmation.button.delete": "Sil",
"discussions.editor.report.post.title": "Uygunsuz içerik mi raporlayacaksınız?",
"discussions.editor.report.post.description": "Tartışma yöneticileri bu içeriği inceleyecek ve uygun işlemi yapacaktır.",
"discussions.post.closePostModal.title": "Gönderiyi kapat",
"discussions.post.closePostModal.text": "Bu gönderiyi kapatmak için bir neden girin. Bu sadece diğer moderatörlere gösterilecektir.",
"discussions.post.closePostModal.reasonCodeInput": "Gerekçe",
@@ -178,14 +163,13 @@
"discussions.post.postWithoutPreview": "Önizleme yok",
"discussions.topics.discussions": "{count, plural,\n =0 {Discussion}\n one {# Discussion}\n other {# Discussions}\n }",
"discussions.topics.questions": "{count, plural,\n =0 {Question}\n one {# Question}\n other {# Questions}\n }",
"discussions.topics.reported": "{reported} rapor edildi",
"discussions.topics.previouslyReported": "{previouslyReported} ileti rapor edildi",
"discussions.topics.reported": "{reported} reported",
"discussions.topics.previouslyReported": "{previouslyReported} previously reported",
"discussions.topics.sort.message": "{sortBy} ölçütüne göre sıralandı",
"discussions.topics.sort.lastActivity": "Son etkinlik",
"discussions.topics.sort.commentCount": "En çok etkinlik",
"discussions.topics.sort.courseStructure": "Ders Yapısı",
"discussions.topics.find.label": "Konuları ara",
"discussions.topics.find.label": "Search topics",
"discussions.topics.archived.label": "Arşivlenmiş",
"discussions.topics.unnamed.label": "İsimsiz kategori",
"discussions.subtopics.unnamed.label": "İsimsiz alt kategori"
"discussions.topics.unnamed.label": "Unnamed Topic"
}

View File

@@ -4,7 +4,6 @@
"discussions.comments.comment.addResponse": "Add a response",
"discussions.comments.comment.addComment": "Add a comment",
"discussions.comments.comment.abuseFlaggedMessage": "Content reported for staff to review",
"discussions.actions.back.alt": "Back to list",
"discussions.comments.comment.responseCount": "{num, plural,\n =0 {No responses}\n one {Showing # response}\n other {Showing # responses}\n }",
"discussions.comments.comment.endorsedResponseCount": "{num, plural,\n =0 {No endorsed responses}\n one {Showing # endorsed response}\n other {Showing # endorsed responses}\n }",
"discussions.comments.comment.loadMoreComments": "Load more comments",
@@ -28,11 +27,6 @@
"discussions.editor.delete.response.description": "Are you sure you want to permanently delete this response?",
"discussions.editor.delete.comment.title": "Delete comment",
"discussions.editor.delete.comment.description": "Are you sure you want to permanently delete this comment?",
"discussions.delete.confirmation.button.delete": "Delete",
"discussions.editor.response.response.title": "Report inappropriate content?",
"discussions.editor.response.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.editor.report.comment.title": "Report inappropriate content?",
"discussions.editor.report.comment.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.editor.comments.editReasonCode": "Reason for editing",
"discussions.editor.posts.editReasonCode.error": "Select reason for editing",
"discussions.comment.comments.editedBy": "Edited by",
@@ -40,7 +34,6 @@
"discussions.post.closedBy": "Post closed by",
"discussion.comment.repliesHeading": "{count} replies for the response added",
"discussion.comment.time": "{time} ago",
"discussion.thread.notFound": "Thread not found",
"discussions.learner.reported": "{reported} reported",
"discussions.learner.previouslyReported": "{previouslyReported} previously reported",
"discussions.learner.lastLogin": "Last active {lastActiveTime}",
@@ -49,23 +42,23 @@
"discussions.learner.activityForLearner": "Activity for {username}",
"discussions.learner.mostActivity": "Most activity",
"discussions.learner.reportedActivity": "Reported activity",
"discussions.learner.recentActivity": "Recent activity",
"discussions.learner.sortFilterStatus": "All learners sorted by {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussions.learner.sortFilterStatus": "All learners by {sort, select,\n flagged {reported activity}\n activity {most activity}\n other {{sort}}\n }",
"discussion.learner.allActivity": "All activity",
"discussion.learner.posts": "Posts",
"discussions.actions.button.alt": "Actions menu",
"discussions.actions.back.alt": "Back",
"discussions.actions.copylink": "Copy link",
"discussions.actions.unpin": "Unpin",
"discussions.confirmation.button.confirm": "Confirm",
"discussions.actions.close": "Close",
"discussions.actions.reopen": "Reopen",
"discussions.actions.report": "Report",
"discussions.actions.unreport": "Unreport",
"discussions.actions.endorse": "Endorse",
"discussions.actions.unendorse": "Unendorse",
"discussions.actions.markAnswered": "Mark as answered",
"discussions.actions.markAnswered": "Mark as Answered",
"discussions.actions.unMarkAnswered": "Unmark as answered",
"discussions.modal.confirmation.button.cancel": "Cancel",
"discussions.delete.confirmation.button.cancel": "Cancel",
"discussions.delete.confirmation.button.delete": "Delete",
"discussions.empty.allTopics": "All discussion activity for these topics will show up here.",
"discussions.empty.allPosts": "All discussion activity for your course will show up here.",
"discussions.empty.myPosts": "Posts you've interacted with will show up here.",
@@ -87,9 +80,6 @@
"discussion.banner.learnMore": "Learn more",
"discussion.banner.shareFeedback": "Share feedback",
"discussion.blackoutBanner.information": "Blackout dates are currently active. Posting in discussions is unavailable at this time.",
"discussions.editor.image.warning.message": "Images having width or height greater than 999px will not be visible when the post, response or comment is viewed using in-line course discussions",
"discussions.editor.image.warning.title": "Warning!",
"discussions.editor.image.warning.dismiss": "Ok",
"discussions.navigation.breadcrumbMenu.allTopics": "Topics",
"discussions.navigation.breadcrumbMenu.showAll": "Show all",
"discussions.navigation.navigationBar.allPosts": "All posts",
@@ -129,8 +119,6 @@
"discussions.post.editor.anonymousToPeersPost": "Post anonymously to peers",
"discussions.editor.posts.editReasonCode": "Reason for editing",
"discussions.editor.posts.showPreview.button": "Show Preview",
"discussions.topic.noName.label": "Unnamed category",
"discussions.subtopic.noName.label": "Unnamed subcategory",
"discussions.posts.filter.showALl": "Show all",
"discussions.posts.filter.discussions": "Discussions",
"discussions.posts.filter.questions": "Questions",
@@ -140,7 +128,7 @@
"discussions.posts.status.filter.following": "Following",
"discussions.posts.status.filter.reported": "Reported",
"discussions.posts.status.filter.unanswered": "Unanswered",
"discussions.posts.status.filter.unresponded": "Not responded",
"discussions.posts.status.filter.unresponded": "Unresponded",
"discussions.posts.filter.myPosts": "My posts",
"discussions.posts.filter.myDiscussions": "My discussions",
"discussions.posts.filter.myQuestions": "My questions",
@@ -148,7 +136,7 @@
"discussions.posts.sort.lastActivity": "Recent activity",
"discussions.posts.sort.commentCount": "Most activity",
"discussions.posts.sort.voteCount": "Most likes",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } sorted by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most likes}\n other {{sort}}\n }",
"discussions.posts.sort-filter.sortFilterStatus": "{own, select,\n false {All}\n true {Own}\n other {{own}}\n } {status, select,\n statusAll {}\n statusUnread {unread}\n statusFollowing {followed}\n statusReported {reported}\n statusUnanswered {unanswered}\n statusUnresponded {unresponded}\n other {{status}}\n } {type, select,\n discussion {discussions}\n question {questions}\n all {posts}\n other {{type}}\n } {cohortType, select,\n all {}\n group {in {cohort}}\n other {{cohortType}}\n } by {sort, select,\n lastActivityAt {recent activity}\n commentCount {most activity}\n voteCount {most votes}\n other {{sort}}\n }",
"discussions.post.author.anonymous": "anonymous",
"discussions.post.lastResponse": "Last response {time}",
"discussions.post.postedOn": "Posted {time} by {author} {authorLabel}",
@@ -164,9 +152,6 @@
"discussions.post.relatedTo": "Related to",
"discussions.editor.delete.post.title": "Delete post",
"discussions.editor.delete.post.description": "Are you sure you want to permanently delete this post?",
"discussions.post.delete.confirmation.button.delete": "Delete",
"discussions.editor.report.post.title": "Report inappropriate content?",
"discussions.editor.report.post.description": "The discussion moderation team will review this content and take appropriate action.",
"discussions.post.closePostModal.title": "Close post",
"discussions.post.closePostModal.text": "Enter a reason for closing this post. This will only be displayed to other moderators.",
"discussions.post.closePostModal.reasonCodeInput": "Reason",
@@ -186,6 +171,5 @@
"discussions.topics.sort.courseStructure": "Course Structure",
"discussions.topics.find.label": "Search topics",
"discussions.topics.archived.label": "Archived",
"discussions.topics.unnamed.label": "Unnamed category",
"discussions.subtopics.unnamed.label": "Unnamed subcategory"
"discussions.topics.unnamed.label": "Unnamed Topic"
}

View File

@@ -118,7 +118,7 @@ $fa-font-path: "~font-awesome/fonts";
header {
.user-dropdown {
z-index: 1;
z-index: 2005;
}
.logo {
margin-right: 1rem;
@@ -227,7 +227,7 @@ header {
.header-action-bar {
background-color: #fff;
z-index: 1;
z-index: 2002;
box-shadow: 0px 2px 4px rgb(0 0 0 / 15%), 0px 2px 8px rgb(0 0 0 / 15%);
position: sticky;
top: 0;
@@ -237,28 +237,14 @@ header {
z-index: 1;
}
.actions-dropdown {
z-index: 0;
}
.discussion-topic-group:last-of-type .divider{
display: none;
}
.zindex-5000 {
z-index: 5000;
z-index: 5000;
}
#paragon-portal-root .pgn__modal-layer {
z-index: 5000 !important;
}
#iconbutton-tooltip-top {
z-index: 0;
}
@media only screen and (max-width: 767px) {
body:not(.tox-force-desktop) .tox .tox-dialog {
align-self: center;
}
}
z-index: 1500 !important;
}