Merge branch 'master' of https://github.com/openedx/frontend-app-discussions into bilalqamar95/frontend-platform-upgrade
This commit is contained in:
5
Makefile
5
Makefile
@@ -66,13 +66,14 @@ pull_translations:
|
||||
rm -rf src/i18n/messages
|
||||
mkdir src/i18n/messages
|
||||
cd src/i18n/messages \
|
||||
&& atlas pull --filter=$(transifex_langs) \
|
||||
&& atlas pull $(ATLAS_OPTIONS) \
|
||||
translations/frontend-component-header/src/i18n/messages:frontend-component-header \
|
||||
translations/frontend-component-footer/src/i18n/messages:frontend-component-footer \
|
||||
translations/frontend-platform/src/i18n/messages:frontend-platform \
|
||||
translations/paragon/src/i18n/messages:paragon \
|
||||
translations/frontend-app-discussions/src/i18n/messages:frontend-app-discussions
|
||||
|
||||
$(intl_imports) frontend-component-header frontend-component-footer paragon frontend-app-discussions
|
||||
$(intl_imports) frontend-component-header frontend-component-footer frontend-platform paragon frontend-app-discussions
|
||||
endif
|
||||
|
||||
# This target is used by Travis.
|
||||
|
||||
@@ -5,10 +5,10 @@ module.exports = createConfig('jest', {
|
||||
// If you want to add config BEFORE jest loads, use setupFiles instead.
|
||||
setupFiles: ['<rootDir>/.env.test'],
|
||||
setupFilesAfterEnv: [
|
||||
'<rootDir>/src/setupTest.js',
|
||||
'<rootDir>/src/setupTest.jsx',
|
||||
],
|
||||
coveragePathIgnorePatterns: [
|
||||
'src/setupTest.js',
|
||||
'src/setupTest.jsx',
|
||||
'src/i18n',
|
||||
],
|
||||
});
|
||||
|
||||
9
package-lock.json
generated
9
package-lock.json
generated
@@ -13,6 +13,7 @@
|
||||
"@edx/frontend-component-footer": "12.7.0",
|
||||
"@edx/frontend-component-header": "4.11.0",
|
||||
"@edx/frontend-platform": "6.2.0",
|
||||
"@edx/openedx-atlas": "^0.6.0",
|
||||
"@edx/paragon": "21.5.6",
|
||||
"@reduxjs/toolkit": "1.9.7",
|
||||
"@tinymce/tinymce-react": "3.13.1",
|
||||
@@ -3567,6 +3568,14 @@
|
||||
"@newrelic/publish-sourcemap": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/openedx-atlas": {
|
||||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/openedx-atlas/-/openedx-atlas-0.6.0.tgz",
|
||||
"integrity": "sha512-wZO7hA4VJ/bXjaQNNR7KXGYyTCNs1mBJd3HwQK2EmOwFZYFNX6nzSAm9S7HCfi/kb1PCRpmp3wJt+v/Eu9BEQg==",
|
||||
"bin": {
|
||||
"atlas": "atlas"
|
||||
}
|
||||
},
|
||||
"node_modules/@edx/paragon": {
|
||||
"version": "21.5.6",
|
||||
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-21.5.6.tgz",
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"@edx/frontend-component-footer": "12.7.0",
|
||||
"@edx/frontend-component-header": "4.11.0",
|
||||
"@edx/frontend-platform": "6.2.0",
|
||||
"@edx/openedx-atlas": "^0.6.0",
|
||||
"@edx/paragon": "21.5.6",
|
||||
"@reduxjs/toolkit": "1.9.7",
|
||||
"@tinymce/tinymce-react": "3.13.1",
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable react/forbid-prop-types */
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
@@ -15,7 +14,7 @@ import {
|
||||
PostsStatusFilter, RequestStatus,
|
||||
ThreadOrdering, ThreadType,
|
||||
} from '../data/constants';
|
||||
import { selectCourseCohorts } from '../discussions/cohorts/data/selectors';
|
||||
import selectCourseCohorts from '../discussions/cohorts/data/selectors';
|
||||
import messages from '../discussions/posts/post-filter-bar/messages';
|
||||
import { ActionItem } from '../discussions/posts/post-filter-bar/PostFilterBar';
|
||||
|
||||
@@ -194,8 +193,16 @@ const FilterBar = ({
|
||||
|
||||
FilterBar.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
filters: PropTypes.array.isRequired,
|
||||
selectedFilters: PropTypes.object.isRequired,
|
||||
filters: PropTypes.arrayOf(PropTypes.shape({
|
||||
name: PropTypes.string,
|
||||
filters: PropTypes.arrayOf(PropTypes.string),
|
||||
})).isRequired,
|
||||
selectedFilters: PropTypes.shape({
|
||||
postType: ThreadType,
|
||||
status: PostsStatusFilter,
|
||||
orderBy: ThreadOrdering,
|
||||
cohort: PropTypes.string,
|
||||
}).isRequired,
|
||||
onFilterChange: PropTypes.func.isRequired,
|
||||
showCohortsFilter: PropTypes.bool,
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useDispatch, useSelector } from 'react-redux';
|
||||
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { fetchTab } from './data/thunks';
|
||||
import fetchTab from './data/thunks';
|
||||
import Tabs from './tabs/Tabs';
|
||||
import messages from './messages';
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { camelCaseObject } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { initializeMockApp } from '@edx/frontend-platform/testing';
|
||||
|
||||
import { initializeStore } from '../../../store';
|
||||
import { executeThunk } from '../../../test-utils';
|
||||
import executeThunk from '../../../test-utils';
|
||||
import { getCourseMetadataApiUrl } from './api';
|
||||
import { fetchTab } from './thunks';
|
||||
import fetchTab from './thunks';
|
||||
|
||||
import './__factories__';
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
const selectCourseTabs = state => state.courseTabs;
|
||||
|
||||
export const selectCourseTabs = state => state.courseTabs;
|
||||
export default selectCourseTabs;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
export const LOADING = 'loading';
|
||||
@@ -17,27 +16,39 @@ const slice = createSlice({
|
||||
org: null,
|
||||
},
|
||||
reducers: {
|
||||
fetchTabDenied: (state, { payload }) => {
|
||||
state.courseId = payload.courseId;
|
||||
state.courseStatus = DENIED;
|
||||
},
|
||||
fetchTabFailure: (state, { payload }) => {
|
||||
state.courseId = payload.courseId;
|
||||
state.courseStatus = FAILED;
|
||||
},
|
||||
fetchTabRequest: (state, { payload }) => {
|
||||
state.courseId = payload.courseId;
|
||||
state.courseStatus = LOADING;
|
||||
},
|
||||
fetchTabSuccess: (state, { payload }) => {
|
||||
state.courseId = payload.courseId;
|
||||
state.targetUserId = payload.targetUserId;
|
||||
state.tabs = payload.tabs;
|
||||
state.courseStatus = LOADED;
|
||||
state.courseTitle = payload.courseTitle;
|
||||
state.courseNumber = payload.courseNumber;
|
||||
state.org = payload.org;
|
||||
},
|
||||
fetchTabDenied: (state, { payload }) => (
|
||||
{
|
||||
...state,
|
||||
courseId: payload.courseId,
|
||||
courseStatus: DENIED,
|
||||
}
|
||||
),
|
||||
fetchTabFailure: (state, { payload }) => (
|
||||
{
|
||||
...state,
|
||||
courseId: payload.courseId,
|
||||
courseStatus: FAILED,
|
||||
}
|
||||
),
|
||||
fetchTabRequest: (state, { payload }) => (
|
||||
{
|
||||
...state,
|
||||
courseId: payload.courseId,
|
||||
courseStatus: LOADING,
|
||||
}
|
||||
),
|
||||
fetchTabSuccess: (state, { payload }) => (
|
||||
{
|
||||
...state,
|
||||
courseId: payload.courseId,
|
||||
targetUserId: payload.targetUserId,
|
||||
tabs: payload.tabs,
|
||||
courseStatus: LOADED,
|
||||
courseTitle: payload.courseTitle,
|
||||
courseNumber: payload.courseNumber,
|
||||
org: payload.org,
|
||||
}
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export, no-unused-expressions */
|
||||
import { logError } from '@edx/frontend-platform/logging';
|
||||
|
||||
import { getHttpErrorStatus } from '../../../discussions/utils';
|
||||
@@ -10,7 +9,7 @@ import {
|
||||
fetchTabSuccess,
|
||||
} from './slice';
|
||||
|
||||
export function fetchTab(courseId, rootSlug) {
|
||||
export default function fetchTab(courseId, rootSlug) {
|
||||
return async (dispatch) => {
|
||||
dispatch(fetchTabRequest({ courseId }));
|
||||
try {
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export { default as CourseTabsNavigation } from './CourseTabsNavigation';
|
||||
@@ -9,7 +9,7 @@ import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { Icon, SearchField } from '@edx/paragon';
|
||||
import { Search as SearchIcon } from '@edx/paragon/icons';
|
||||
|
||||
import { DiscussionContext } from '../discussions/common/context';
|
||||
import DiscussionContext from '../discussions/common/context';
|
||||
import { setUsernameSearch } from '../discussions/learners/data';
|
||||
import { setSearchQuery } from '../discussions/posts/data';
|
||||
import postsMessages from '../discussions/posts/post-actions-bar/messages';
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
export default function InsertLink() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
d="M3.9 12c0-1.71 1.39-3.1 3.1-3.1h4V7H7c-2.76 0-5 2.24-5 5s2.24 5 5 5h4v-1.9H7c-1.71 0-3.1-1.39-3.1-3.1zM8 13h8v-2H8v2zm9-6h-4v1.9h4c1.71 0 3.1 1.39 3.1 3.1s-1.39 3.1-3.1 3.1h-4V17h4c2.76 0 5-2.24 5-5s-2.24-5-5-5z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
export default function Issue() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="28"
|
||||
height="28"
|
||||
fill="none"
|
||||
viewBox="0 0 28 28"
|
||||
>
|
||||
<path
|
||||
fill="#F2F0EF"
|
||||
d="M0 14C0 6.268 6.268 0 14 0s14 6.268 14 14-6.268 14-14 14S0 21.732 0 14z"
|
||||
/>
|
||||
<path
|
||||
fill="#2D494E"
|
||||
d="M14 2.333C7.56 2.333 2.333 7.56 2.333 14c0 6.44 5.227 11.667 11.667 11.667 6.44 0 11.667-5.227 11.667-11.667C25.667 7.56 20.44 2.334 14 2.334z"
|
||||
/>
|
||||
<path
|
||||
fill="#fff"
|
||||
d="M12.833 22.167h2.334v-2.334h-2.334v2.334zM16.532 14.198l1.05-1.073a3.713 3.713 0 001.085-2.625A4.665 4.665 0 0014 5.833 4.665 4.665 0 009.333 10.5h2.334A2.34 2.34 0 0114 8.167a2.34 2.34 0 012.333 2.333c0 .642-.256 1.225-.688 1.645l-1.447 1.47a4.696 4.696 0 00-1.365 3.302v.583h2.334c0-1.75.525-2.45 1.365-3.302z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
export default function People() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16"
|
||||
height="16"
|
||||
fill="none"
|
||||
viewBox="0 0 16 16"
|
||||
>
|
||||
<path
|
||||
fill="#707070"
|
||||
d="M11.072 7.332a1.992 1.992 0 001.993-2 1.997 1.997 0 10-3.993 0c0 1.107.893 2 2 2zm-5.334 0a1.992 1.992 0 001.994-2 1.997 1.997 0 10-3.993 0c0 1.107.893 2 2 2zm0 1.333c-1.553 0-4.666.78-4.666 2.334v1.666h9.333V11c0-1.554-3.113-2.334-4.667-2.334zm5.334 0c-.194 0-.414.014-.647.034.773.56 1.313 1.313 1.313 2.3v1.666h4V11c0-1.554-3.113-2.334-4.666-2.334z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
export default function PushPin() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
d="M16 9V4H18V2H6V4H8V9C8 10.66 6.66 12 5 12V14H10.97V21L11.97 22L12.97 21V14H19V12C17.34 12 16 10.66 16 9Z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
export default function Question() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="28"
|
||||
height="28"
|
||||
fill="none"
|
||||
viewBox="0 0 28 28"
|
||||
>
|
||||
<path
|
||||
fill="#fff"
|
||||
d="M0 14.001c0-7.732 6.268-14 14-14s14 6.268 14 14-6.268 14-14 14-14-6.268-14-14z"
|
||||
/>
|
||||
<path
|
||||
fill="#2D494E"
|
||||
d="M14 2.334c-6.44 0-11.667 5.227-11.667 11.667 0 6.44 5.227 11.667 11.667 11.667 6.44 0 11.666-5.227 11.666-11.667 0-6.44-5.226-11.667-11.666-11.667z"
|
||||
/>
|
||||
<path
|
||||
fill="#fff"
|
||||
d="M12.833 22.168h2.333v-2.334h-2.333v2.334zM16.531 14.2l1.05-1.074a3.712 3.712 0 001.085-2.625A4.665 4.665 0 0014 5.834a4.665 4.665 0 00-4.667 4.667h2.333A2.34 2.34 0 0114 8.168a2.34 2.34 0 012.333 2.333c0 .642-.257 1.225-.688 1.645l-1.447 1.47a4.696 4.696 0 00-1.365 3.302v.583h2.333c0-1.75.525-2.45 1.365-3.302z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
export default function QuestionAnswer() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="21"
|
||||
height="20"
|
||||
fill="none"
|
||||
viewBox="0 0 21 20"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M18.737 5h-2.5v7.5H5.404V15h10l3.333 3.333V5zm-4.166 5.833V1.667H2.07v12.5l3.333-3.334h9.166z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
export default function QuestionAnswerOutline() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
fill="none"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12.6 3.267v6.067H4.08l-.512.512-.502.502v-7.08H12.6zm.867-1.733H2.198a.87.87 0 00-.867.867v12.134L4.8 11.068h8.668a.87.87 0 00.866-.867v-7.8a.87.87 0 00-.867-.867zM17.8 5h-1.733v7.8H4.799v1.734c0 .476.39.867.867.867H15.2l3.467 3.466v-13A.87.87 0 0017.8 5z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
export default function StarFilled() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="21"
|
||||
height="20"
|
||||
fill="none"
|
||||
viewBox="0 0 21 20"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M10.404 14.392l5.15 3.108-1.367-5.858 4.55-3.942-5.991-.508-2.342-5.525-2.342 5.525L2.07 7.7l4.55 3.942L5.254 17.5l5.15-3.108z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
export default function StarOutline() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
fill="none"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M18.737 7.7l-5.991-.517-2.342-5.516-2.342 5.525L2.07 7.7l4.55 3.942L5.254 17.5l5.15-3.108 5.15 3.108-1.359-5.858L18.737 7.7zm-8.333 5.133L7.27 14.725l.834-3.567-2.767-2.4 3.65-.316 1.417-3.359 1.425 3.367 3.65.317-2.767 2.4.834 3.566-3.142-1.9z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
export default function ThumbUpFilled() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="21"
|
||||
height="20"
|
||||
fill="none"
|
||||
viewBox="0 0 21 20"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12.212.833L6.237 6.817V17.5h10.258l3.075-7.167V6.667h-6.925l.934-4.484-1.367-1.35zM1.237 7.5H4.57v10H1.237v-10z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
import React from 'react';
|
||||
|
||||
// eslint-disable-next-line react/function-component-definition
|
||||
export default function ThumbUpOutline() {
|
||||
return (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="20"
|
||||
height="20"
|
||||
fill="none"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
fillRule="evenodd"
|
||||
d="M19.57 6.667v3.666L16.495 17.5H6.238V6.817L12.212.833l1.367 1.35-.934 4.484h6.925zm-11.666.841v8.325h7.492l2.508-5.841V8.333h-7.309l.925-4.45-3.616 3.625z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
<path fill="currentColor" d="M4.57 17.5H1.237v-10H4.57v10z" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
export { default as InsertLink } from './InsertLink';
|
||||
export { default as Issue } from './Issue';
|
||||
export { default as People } from './People';
|
||||
export { default as PushPin } from './PushPin';
|
||||
export { default as Question } from './Question';
|
||||
export { default as QuestionAnswer } from './QuestionAnswer';
|
||||
export { default as QuestionAnswerOutline } from './QuestionAnswerOutline';
|
||||
export { default as StarFilled } from './StarFilled';
|
||||
export { default as StarOutline } from './StarOutline';
|
||||
export { default as ThumbUpFilled } from './ThumbUpFilled';
|
||||
export { default as ThumbUpOutline } from './ThumbUpOutline';
|
||||
@@ -1,7 +1,5 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
|
||||
// Course Blocks API response for the demo course.
|
||||
export const getBlocksAPIResponse = (newProvider = false) => {
|
||||
const getBlocksAPIResponse = (newProvider = false) => {
|
||||
const response = {
|
||||
root: 'block-v1:edX+DemoX+Demo_Course+type@course+block@course',
|
||||
blocks: {
|
||||
@@ -936,3 +934,5 @@ export const getBlocksAPIResponse = (newProvider = false) => {
|
||||
}
|
||||
return response;
|
||||
};
|
||||
|
||||
export default getBlocksAPIResponse;
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
export * from './blocks';
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
|
||||
import { getApiBaseUrl } from './constants';
|
||||
|
||||
@@ -80,6 +80,7 @@ export const RequestStatus = {
|
||||
*/
|
||||
export const AvatarOutlineAndLabelColors = {
|
||||
Staff: 'staff-color',
|
||||
Moderator: 'TA-color',
|
||||
'Community TA': 'TA-color',
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { useState } from 'react';
|
||||
|
||||
import { useDispatch } from 'react-redux';
|
||||
@@ -15,7 +14,7 @@ import { useDispatch } from 'react-redux';
|
||||
*
|
||||
* @return {(boolean|(function(*=): Promise<void>)|*)[]}
|
||||
*/
|
||||
export function useDispatchWithState() {
|
||||
export default function useDispatchWithState() {
|
||||
const dispatch = useDispatch();
|
||||
const [isDispatching, setDispatching] = useState(false);
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { initializeMockApp } from '@edx/frontend-platform/testing';
|
||||
|
||||
import { initializeStore } from '../store';
|
||||
import { executeThunk } from '../test-utils';
|
||||
import { getBlocksAPIResponse } from './__factories__';
|
||||
import executeThunk from '../test-utils';
|
||||
import getBlocksAPIResponse from './__factories__/blocks';
|
||||
import { getBlocksAPIURL } from './api';
|
||||
import { RequestStatus } from './constants';
|
||||
import { fetchCourseBlocks } from './thunks';
|
||||
import fetchCourseBlocks from './thunks';
|
||||
|
||||
const blocksAPIURL = getBlocksAPIURL();
|
||||
const courseId = 'course-v1:edX+DemoX+Demo_Course';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
|
||||
import { selectDiscussionProvider, selectGroupAtSubsection } from '../discussions/data/selectors';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-param-reassign,import/prefer-default-export */
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
import { RequestStatus } from './constants';
|
||||
@@ -16,19 +15,33 @@ const blocksSlice = createSlice({
|
||||
blocks: {},
|
||||
},
|
||||
reducers: {
|
||||
fetchCourseBlocksRequest: (state) => {
|
||||
state.status = RequestStatus.IN_PROGRESS;
|
||||
},
|
||||
fetchCourseBlocksSuccess: (state, { payload }) => {
|
||||
state.status = RequestStatus.SUCCESSFUL;
|
||||
Object.assign(state, payload);
|
||||
},
|
||||
fetchCourseBlocksFailed: (state) => {
|
||||
state.status = RequestStatus.FAILED;
|
||||
},
|
||||
fetchCourseBlocksDenied: (state) => {
|
||||
state.status = RequestStatus.DENIED;
|
||||
},
|
||||
fetchCourseBlocksRequest: (state) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.IN_PROGRESS,
|
||||
}
|
||||
),
|
||||
fetchCourseBlocksSuccess: (state, { payload }) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.SUCCESSFUL,
|
||||
topics: payload.topics,
|
||||
chapters: payload.chapters,
|
||||
blocks: payload.blocks,
|
||||
}
|
||||
),
|
||||
fetchCourseBlocksFailed: (state) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.FAILED,
|
||||
}
|
||||
),
|
||||
fetchCourseBlocksDenied: (state) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.DENIED,
|
||||
}
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export, no-unused-expressions */
|
||||
import { camelCaseObject } from '@edx/frontend-platform';
|
||||
import { logError } from '@edx/frontend-platform/logging';
|
||||
|
||||
@@ -88,7 +87,7 @@ function normaliseCourseBlocks({
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchCourseBlocks(courseId, username) {
|
||||
export default function fetchCourseBlocks(courseId, username) {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
dispatch(fetchCourseBlocksRequest({ courseId }));
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { ensureConfig, getConfig, snakeCaseObject } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { initializeMockApp } from '@edx/frontend-platform/testing';
|
||||
|
||||
import { initializeStore } from '../../../store';
|
||||
import { executeThunk } from '../../../test-utils';
|
||||
import executeThunk from '../../../test-utils';
|
||||
import { getCohortsApiUrl } from './api';
|
||||
import { fetchCourseCohorts } from './thunks';
|
||||
import fetchCourseCohorts from './thunks';
|
||||
|
||||
import './__factories__';
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
const selectCourseCohorts = state => state.cohorts.cohorts;
|
||||
|
||||
export const selectCourseCohorts = state => state.cohorts.cohorts;
|
||||
export default selectCourseCohorts;
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-param-reassign,import/prefer-default-export */
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
import { RequestStatus } from '../../../data/constants';
|
||||
@@ -10,17 +9,26 @@ const cohortsSlice = createSlice({
|
||||
cohorts: [],
|
||||
},
|
||||
reducers: {
|
||||
fetchCohortsRequest: (state) => {
|
||||
state.status = RequestStatus.IN_PROGRESS;
|
||||
state.cohorts = [];
|
||||
},
|
||||
fetchCohortsSuccess: (state, { payload }) => {
|
||||
state.status = RequestStatus.SUCCESSFUL;
|
||||
state.cohorts = payload;
|
||||
},
|
||||
fetchCohortsFailed: (state) => {
|
||||
state.status = RequestStatus.FAILED;
|
||||
},
|
||||
fetchCohortsRequest: (state) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.IN_PROGRESS,
|
||||
cohorts: [],
|
||||
}
|
||||
),
|
||||
fetchCohortsSuccess: (state, { payload }) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.SUCCESSFUL,
|
||||
cohorts: payload,
|
||||
}
|
||||
),
|
||||
fetchCohortsFailed: (state) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.FAILED,
|
||||
}
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { camelCaseObject } from '@edx/frontend-platform';
|
||||
import { logError } from '@edx/frontend-platform/logging';
|
||||
|
||||
@@ -11,7 +10,7 @@ import {
|
||||
fetchCohortsSuccess,
|
||||
} from './slices';
|
||||
|
||||
export function fetchCourseCohorts(courseId) {
|
||||
export default function fetchCourseCohorts(courseId) {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
dispatch(fetchCohortsRequest());
|
||||
|
||||
@@ -12,13 +12,13 @@ import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { ContentActions } from '../../data/constants';
|
||||
import { initializeStore } from '../../store';
|
||||
import { executeThunk } from '../../test-utils';
|
||||
import executeThunk from '../../test-utils';
|
||||
import { getCourseConfigApiUrl } from '../data/api';
|
||||
import { fetchCourseConfig } from '../data/thunks';
|
||||
import fetchCourseConfig from '../data/thunks';
|
||||
import messages from '../messages';
|
||||
import { getCommentsApiUrl } from '../post-comments/data/api';
|
||||
import { addComment, fetchThreadComments } from '../post-comments/data/thunks';
|
||||
import { PostCommentsContext } from '../post-comments/postCommentsContext';
|
||||
import PostCommentsContext from '../post-comments/postCommentsContext';
|
||||
import { getThreadsApiUrl } from '../posts/data/api';
|
||||
import { fetchThread } from '../posts/data/thunks';
|
||||
import { ACTIONS_LIST } from '../utils';
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ThreadType } from '../../data/constants';
|
||||
import { initializeStore } from '../../store';
|
||||
import messages from '../post-comments/messages';
|
||||
import AlertBanner from './AlertBanner';
|
||||
import { DiscussionContext } from './context';
|
||||
import DiscussionContext from './context';
|
||||
|
||||
import '../post-comments/data/__factories__';
|
||||
import '../posts/data/__factories__';
|
||||
|
||||
@@ -7,11 +7,11 @@ import * as timeago from 'timeago.js';
|
||||
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { Icon, OverlayTrigger, Tooltip } from '@edx/paragon';
|
||||
import { Institution, School } from '@edx/paragon/icons';
|
||||
|
||||
import { Routes } from '../../data/constants';
|
||||
import messages from '../messages';
|
||||
import { DiscussionContext } from './context';
|
||||
import { getAuthorLabel } from '../utils';
|
||||
import DiscussionContext from './context';
|
||||
import timeLocale from './time-locale';
|
||||
|
||||
const AuthorLabel = ({
|
||||
@@ -26,25 +26,15 @@ const AuthorLabel = ({
|
||||
}) => {
|
||||
timeago.register('time-locale', timeLocale);
|
||||
const intl = useIntl();
|
||||
const { courseId } = useContext(DiscussionContext);
|
||||
let icon = null;
|
||||
let authorLabelMessage = null;
|
||||
|
||||
if (authorLabel === 'Staff') {
|
||||
icon = Institution;
|
||||
authorLabelMessage = intl.formatMessage(messages.authorLabelStaff);
|
||||
}
|
||||
|
||||
if (authorLabel === 'Community TA') {
|
||||
icon = School;
|
||||
authorLabelMessage = intl.formatMessage(messages.authorLabelTA);
|
||||
}
|
||||
const { courseId, enableInContextSidebar } = useContext(DiscussionContext);
|
||||
const { icon, authorLabelMessage } = useMemo(() => getAuthorLabel(intl, authorLabel), [authorLabel]);
|
||||
|
||||
const isRetiredUser = author ? author.startsWith('retired__user') : false;
|
||||
const showTextPrimary = !authorLabelMessage && !isRetiredUser && !alert;
|
||||
const className = classNames('d-flex align-items-center', { 'mb-0.5': !postOrComment }, labelColor);
|
||||
|
||||
const showUserNameAsLink = linkToProfile && author && author !== intl.formatMessage(messages.anonymous);
|
||||
const showUserNameAsLink = linkToProfile && author && author !== intl.formatMessage(messages.anonymous)
|
||||
&& !enableInContextSidebar;
|
||||
|
||||
const authorName = useMemo(() => (
|
||||
<span
|
||||
@@ -62,17 +52,15 @@ const AuthorLabel = ({
|
||||
const labelContents = useMemo(() => (
|
||||
<>
|
||||
<OverlayTrigger
|
||||
placement={authorToolTip ? 'top' : 'right'}
|
||||
overlay={(
|
||||
<Tooltip id={`endorsed-by-${author}-tooltip`}>
|
||||
{author}
|
||||
<Tooltip id={authorToolTip ? `endorsed-by-${author}-tooltip` : `${authorLabel}-label-tooltip`}>
|
||||
{authorToolTip ? author : authorLabel}
|
||||
</Tooltip>
|
||||
)}
|
||||
trigger={['hover', 'focus']}
|
||||
>
|
||||
<div className={classNames('d-flex flex-row align-items-center', {
|
||||
'disable-div': !authorToolTip,
|
||||
})}
|
||||
>
|
||||
<div className={classNames('d-flex flex-row align-items-center')}>
|
||||
<Icon
|
||||
style={{
|
||||
width: '1rem',
|
||||
|
||||
@@ -9,11 +9,11 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { initializeStore } from '../../store';
|
||||
import { executeThunk } from '../../test-utils';
|
||||
import executeThunk from '../../test-utils';
|
||||
import { getCourseConfigApiUrl } from '../data/api';
|
||||
import { fetchCourseConfig } from '../data/thunks';
|
||||
import fetchCourseConfig from '../data/thunks';
|
||||
import AuthorLabel from './AuthorLabel';
|
||||
import { DiscussionContext } from './context';
|
||||
import DiscussionContext from './context';
|
||||
|
||||
const courseId = 'course-v1:edX+DemoX+Demo_Course';
|
||||
const courseConfigApiUrl = getCourseConfigApiUrl();
|
||||
@@ -21,11 +21,11 @@ let store;
|
||||
let axiosMock;
|
||||
let container;
|
||||
|
||||
function renderComponent(author, authorLabel, linkToProfile, labelColor) {
|
||||
function renderComponent(author, authorLabel, linkToProfile, labelColor, enableInContextSidebar) {
|
||||
const wrapper = render(
|
||||
<IntlProvider locale="en">
|
||||
<AppProvider store={store}>
|
||||
<DiscussionContext.Provider value={{ courseId }}>
|
||||
<DiscussionContext.Provider value={{ courseId, enableInContextSidebar }}>
|
||||
<AuthorLabel
|
||||
author={author}
|
||||
authorLabel={authorLabel}
|
||||
@@ -62,6 +62,7 @@ describe('Author label', () => {
|
||||
describe.each([
|
||||
['anonymous', null, false, ''],
|
||||
['ta_user', 'Community TA', true, 'text-TA-color'],
|
||||
['moderator_user', 'Moderator', true, 'text-TA-color'],
|
||||
['retired__user', null, false, ''],
|
||||
['staff_user', 'Staff', true, 'text-staff-color'],
|
||||
['learner_user', null, false, ''],
|
||||
@@ -78,9 +79,9 @@ describe('Author label', () => {
|
||||
);
|
||||
|
||||
it(
|
||||
`it is "${!linkToProfile && 'not'}" clickable when linkToProfile is ${!!linkToProfile}`,
|
||||
`it is "${(!linkToProfile) && 'not'}" clickable when linkToProfile is ${!!linkToProfile} and enableInContextSidebar is false`,
|
||||
async () => {
|
||||
renderComponent(author, authorLabel, linkToProfile, labelColor);
|
||||
renderComponent(author, authorLabel, linkToProfile, labelColor, false);
|
||||
|
||||
if (linkToProfile) {
|
||||
expect(screen.queryByTestId('learner-posts-link')).toBeInTheDocument();
|
||||
@@ -90,6 +91,15 @@ describe('Author label', () => {
|
||||
},
|
||||
);
|
||||
|
||||
it(
|
||||
'it is not clickable when enableInContextSidebar is true',
|
||||
async () => {
|
||||
renderComponent(author, authorLabel, linkToProfile, labelColor, true);
|
||||
|
||||
expect(screen.queryByTestId('learner-posts-link')).not.toBeInTheDocument();
|
||||
},
|
||||
);
|
||||
|
||||
it(
|
||||
`it has "${!linkToProfile && 'not'}" label text and label color when linkToProfile is ${!!linkToProfile}`,
|
||||
async () => {
|
||||
@@ -97,7 +107,7 @@ describe('Author label', () => {
|
||||
const authorElement = container.querySelector('[role=heading]');
|
||||
const labelParentNode = authorElement.parentNode.parentNode;
|
||||
const labelElement = labelParentNode.lastChild.lastChild;
|
||||
const label = ['TA', 'Staff'].includes(labelElement.textContent) && labelElement.textContent;
|
||||
const label = ['CTA', 'TA', 'Staff'].includes(labelElement.textContent) && labelElement.textContent;
|
||||
|
||||
if (linkToProfile) {
|
||||
expect(labelParentNode).toHaveClass(labelColor);
|
||||
|
||||
@@ -9,7 +9,7 @@ import { CheckCircle, Verified } from '@edx/paragon/icons';
|
||||
|
||||
import { ThreadType } from '../../data/constants';
|
||||
import messages from '../post-comments/messages';
|
||||
import { PostCommentsContext } from '../post-comments/postCommentsContext';
|
||||
import PostCommentsContext from '../post-comments/postCommentsContext';
|
||||
import AuthorLabel from './AuthorLabel';
|
||||
import timeLocale from './time-locale';
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import { AppProvider } from '@edx/frontend-platform/react';
|
||||
import { ThreadType } from '../../data/constants';
|
||||
import { initializeStore } from '../../store';
|
||||
import messages from '../post-comments/messages';
|
||||
import { PostCommentsContext } from '../post-comments/postCommentsContext';
|
||||
import { DiscussionContext } from './context';
|
||||
import PostCommentsContext from '../post-comments/postCommentsContext';
|
||||
import DiscussionContext from './context';
|
||||
import EndorsedAlertBanner from './EndorsedAlertBanner';
|
||||
|
||||
import '../post-comments/data/__factories__';
|
||||
|
||||
@@ -7,14 +7,15 @@ import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import {
|
||||
Button, Icon, IconButton, OverlayTrigger, Tooltip,
|
||||
} from '@edx/paragon';
|
||||
|
||||
import {
|
||||
StarFilled, StarOutline, ThumbUpFilled, ThumbUpOutline,
|
||||
} from '../../components/icons';
|
||||
} from '@edx/paragon/icons';
|
||||
|
||||
import { ThreadType } from '../../data/constants';
|
||||
import { useUserPostingEnabled } from '../data/hooks';
|
||||
import { PostCommentsContext } from '../post-comments/postCommentsContext';
|
||||
import PostCommentsContext from '../post-comments/postCommentsContext';
|
||||
import ActionsDropdown from './ActionsDropdown';
|
||||
import { DiscussionContext } from './context';
|
||||
import DiscussionContext from './context';
|
||||
|
||||
const HoverCard = ({
|
||||
id,
|
||||
@@ -127,8 +128,22 @@ HoverCard.propTypes = {
|
||||
addResponseCommentButtonMessage: PropTypes.string.isRequired,
|
||||
onLike: PropTypes.func.isRequired,
|
||||
voted: PropTypes.bool.isRequired,
|
||||
// eslint-disable-next-line react/forbid-prop-types
|
||||
endorseIcons: PropTypes.objectOf(PropTypes.any),
|
||||
endorseIcons: PropTypes.objectOf(PropTypes.shape(
|
||||
{
|
||||
id: PropTypes.string,
|
||||
action: PropTypes.string,
|
||||
icon: PropTypes.element,
|
||||
label: {
|
||||
id: PropTypes.string,
|
||||
defaultMessage: PropTypes.string,
|
||||
description: PropTypes.string,
|
||||
},
|
||||
conditions: {
|
||||
endorsed: PropTypes.bool,
|
||||
postType: ThreadType,
|
||||
},
|
||||
},
|
||||
)),
|
||||
onFollow: PropTypes.func,
|
||||
following: PropTypes.bool,
|
||||
};
|
||||
|
||||
@@ -11,15 +11,15 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { initializeStore } from '../../store';
|
||||
import { executeThunk } from '../../test-utils';
|
||||
import executeThunk from '../../test-utils';
|
||||
import { getCourseConfigApiUrl } from '../data/api';
|
||||
import { fetchCourseConfig } from '../data/thunks';
|
||||
import fetchCourseConfig from '../data/thunks';
|
||||
import DiscussionContent from '../discussions-home/DiscussionContent';
|
||||
import { getCommentsApiUrl } from '../post-comments/data/api';
|
||||
import { fetchCommentResponses } from '../post-comments/data/thunks';
|
||||
import { getThreadsApiUrl } from '../posts/data/api';
|
||||
import { fetchThreads } from '../posts/data/thunks';
|
||||
import { DiscussionContext } from './context';
|
||||
import DiscussionContext from './context';
|
||||
|
||||
import '../posts/data/__factories__';
|
||||
import '../post-comments/data/__factories__';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import React from 'react';
|
||||
|
||||
export const DiscussionContext = React.createContext({
|
||||
const DiscussionContext = React.createContext({
|
||||
page: null,
|
||||
courseId: null,
|
||||
postId: null,
|
||||
@@ -10,3 +9,5 @@ export const DiscussionContext = React.createContext({
|
||||
category: null,
|
||||
learnerUsername: null,
|
||||
});
|
||||
|
||||
export default DiscussionContext;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
|
||||
import { ensureConfig, getConfig } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@ import { breakpoints, useWindowSize } from '@edx/paragon';
|
||||
|
||||
import { RequestStatus, Routes } from '../../data/constants';
|
||||
import { selectTopicsUnderCategory } from '../../data/selectors';
|
||||
import { fetchCourseBlocks } from '../../data/thunks';
|
||||
import { DiscussionContext } from '../common/context';
|
||||
import fetchCourseBlocks from '../../data/thunks';
|
||||
import DiscussionContext from '../common/context';
|
||||
import { clearRedirect } from '../posts/data';
|
||||
import { threadsLoadingStatus } from '../posts/data/selectors';
|
||||
import { selectTopics } from '../topics/data/selectors';
|
||||
import tourCheckpoints from '../tours/constants';
|
||||
import { selectTours } from '../tours/data/selectors';
|
||||
import selectTours from '../tours/data/selectors';
|
||||
import { updateTourShowStatus } from '../tours/data/thunks';
|
||||
import messages from '../tours/messages';
|
||||
import { discussionsPath } from '../utils';
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
selectUserIsGroupTa,
|
||||
selectUserIsStaff,
|
||||
} from './selectors';
|
||||
import { fetchCourseConfig } from './thunks';
|
||||
import fetchCourseConfig from './thunks';
|
||||
|
||||
export function useTotalTopicThreadCount() {
|
||||
const topics = useSelector(selectTopics);
|
||||
|
||||
@@ -8,11 +8,11 @@ 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 executeThunk from '../../test-utils';
|
||||
import DiscussionContext from '../common/context';
|
||||
import { getCourseConfigApiUrl } from './api';
|
||||
import { useCurrentDiscussionTopic, useUserPostingEnabled } from './hooks';
|
||||
import { fetchCourseConfig } from './thunks';
|
||||
import fetchCourseConfig from './thunks';
|
||||
|
||||
const courseId = 'course-v1:edX+TestX+Test_Course';
|
||||
const courseConfigApiUrl = getCourseConfigApiUrl();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { PostsStatusFilter, ThreadType } from '../../data/constants';
|
||||
|
||||
export const selectAnonymousPostingConfig = state => ({
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-param-reassign,import/prefer-default-export */
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
import { RequestStatus } from '../../data/constants';
|
||||
@@ -28,19 +27,29 @@ const configSlice = createSlice({
|
||||
enableInContext: false,
|
||||
},
|
||||
reducers: {
|
||||
fetchConfigRequest: (state) => {
|
||||
state.status = RequestStatus.IN_PROGRESS;
|
||||
},
|
||||
fetchConfigRequest: (state) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.IN_PROGRESS,
|
||||
}
|
||||
),
|
||||
fetchConfigSuccess: (state, { payload }) => {
|
||||
state.status = RequestStatus.SUCCESSFUL;
|
||||
Object.assign(state, payload);
|
||||
},
|
||||
fetchConfigFailed: (state) => {
|
||||
state.status = RequestStatus.FAILED;
|
||||
},
|
||||
fetchConfigDenied: (state) => {
|
||||
state.status = RequestStatus.DENIED;
|
||||
const newState = Object.assign(state, payload);
|
||||
newState.status = RequestStatus.SUCCESSFUL;
|
||||
return newState;
|
||||
},
|
||||
fetchConfigFailed: (state) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.FAILED,
|
||||
}
|
||||
),
|
||||
fetchConfigDenied: (state) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.DENIED,
|
||||
}
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { camelCaseObject } from '@edx/frontend-platform';
|
||||
import { logError } from '@edx/frontend-platform/logging';
|
||||
|
||||
@@ -19,7 +18,7 @@ import {
|
||||
* @param {string} courseId The course ID for the course to fetch config for.
|
||||
* @returns {(function(*): Promise<void>)|*}
|
||||
*/
|
||||
export function fetchCourseConfig(courseId) {
|
||||
export default function fetchCourseConfig(courseId) {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
let learnerSort = LearnersOrdering.BY_LAST_ACTIVITY;
|
||||
|
||||
@@ -13,7 +13,7 @@ import { useWindowSize } from '@edx/paragon';
|
||||
|
||||
import Spinner from '../../components/Spinner';
|
||||
import { RequestStatus, Routes as ROUTES } from '../../data/constants';
|
||||
import { DiscussionContext } from '../common/context';
|
||||
import DiscussionContext from '../common/context';
|
||||
import { useContainerSize, useIsOnDesktop, useIsOnXLDesktop } from '../data/hooks';
|
||||
import { selectConfigLoadingStatus, selectEnableInContext } from '../data/selectors';
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { initializeStore } from '../../store';
|
||||
import { DiscussionContext } from '../common/context';
|
||||
import DiscussionContext from '../common/context';
|
||||
import { fetchConfigSuccess } from '../data/slices';
|
||||
import { getThreadsApiUrl } from '../posts/data/api';
|
||||
import DiscussionSidebar from './DiscussionSidebar';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable react/jsx-no-constructed-context-values */
|
||||
import React, { lazy, Suspense, useRef } from 'react';
|
||||
import React, {
|
||||
lazy, Suspense, useMemo, useRef,
|
||||
} from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useSelector } from 'react-redux';
|
||||
@@ -10,14 +11,15 @@ import {
|
||||
import { LearningHeader as Header } from '@edx/frontend-component-header';
|
||||
|
||||
import { Spinner } from '../../components';
|
||||
import { selectCourseTabs } from '../../components/NavigationBar/data/selectors';
|
||||
import selectCourseTabs from '../../components/NavigationBar/data/selectors';
|
||||
import { ALL_ROUTES, DiscussionProvider, Routes as ROUTES } from '../../data/constants';
|
||||
import { DiscussionContext } from '../common/context';
|
||||
import DiscussionContext from '../common/context';
|
||||
import {
|
||||
useCourseDiscussionData, useIsOnDesktop, useRedirectToThread, useSidebarVisible,
|
||||
} from '../data/hooks';
|
||||
import { selectDiscussionProvider, selectEnableInContext } from '../data/selectors';
|
||||
import { EmptyLearners, EmptyPosts, EmptyTopics } from '../empty-posts';
|
||||
import { EmptyLearners, EmptyTopics } from '../empty-posts';
|
||||
import EmptyPosts from '../empty-posts/EmptyPosts';
|
||||
import { EmptyTopic as InContextEmptyTopics } from '../in-context-topics/components';
|
||||
import messages from '../messages';
|
||||
import { selectPostEditorVisible } from '../posts/data/selectors';
|
||||
@@ -60,18 +62,19 @@ const DiscussionsHome = () => {
|
||||
const displayContentArea = (postId || postEditorVisible || (learnerUsername && postId));
|
||||
if (displayContentArea) { displaySidebar = isOnDesktop; }
|
||||
|
||||
const discussionContextValue = useMemo(() => ({
|
||||
page,
|
||||
courseId,
|
||||
postId,
|
||||
topicId,
|
||||
enableInContextSidebar,
|
||||
category,
|
||||
learnerUsername,
|
||||
}));
|
||||
|
||||
return (
|
||||
<Suspense fallback={(<Spinner />)}>
|
||||
<DiscussionContext.Provider value={{
|
||||
page,
|
||||
courseId,
|
||||
postId,
|
||||
topicId,
|
||||
enableInContextSidebar,
|
||||
category,
|
||||
learnerUsername,
|
||||
}}
|
||||
>
|
||||
<DiscussionContext.Provider value={discussionContextValue}>
|
||||
{!enableInContextSidebar && (
|
||||
<Header courseOrg={org} courseNumber={courseNumber} courseTitle={courseTitle} />
|
||||
)}
|
||||
|
||||
@@ -13,18 +13,18 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { getCourseMetadataApiUrl } from '../../components/NavigationBar/data/api';
|
||||
import { fetchTab } from '../../components/NavigationBar/data/thunks';
|
||||
import fetchTab from '../../components/NavigationBar/data/thunks';
|
||||
import { getApiBaseUrl } from '../../data/constants';
|
||||
import { initializeStore } from '../../store';
|
||||
import { executeThunk } from '../../test-utils';
|
||||
import executeThunk from '../../test-utils';
|
||||
import { getCourseConfigApiUrl, getDiscussionsConfigUrl } from '../data/api';
|
||||
import { fetchCourseConfig } from '../data/thunks';
|
||||
import fetchCourseConfig from '../data/thunks';
|
||||
import { getCourseTopicsApiUrl } from '../in-context-topics/data/api';
|
||||
import { fetchCourseTopicsV3 } from '../in-context-topics/data/thunks';
|
||||
import fetchCourseTopicsV3 from '../in-context-topics/data/thunks';
|
||||
import navigationBarMessages from '../navigation/navigation-bar/messages';
|
||||
import { getThreadsApiUrl } from '../posts/data/api';
|
||||
import { fetchThreads } from '../posts/data/thunks';
|
||||
import { fetchCourseTopics } from '../topics/data/thunks';
|
||||
import fetchCourseTopics from '../topics/data/thunks';
|
||||
import DiscussionsHome from './DiscussionsHome';
|
||||
|
||||
import '../posts/data/__factories__/threads.factory';
|
||||
|
||||
@@ -5,7 +5,7 @@ import { initializeMockApp } from '@edx/frontend-platform';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { initializeStore } from '../../store';
|
||||
import { DiscussionContext } from '../common/context';
|
||||
import DiscussionContext from '../common/context';
|
||||
import { fetchConfigSuccess } from '../data/slices';
|
||||
import messages from '../messages';
|
||||
import DiscussionsRestrictionBanner from './DiscussionsRestrictionBanner';
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export { default as DiscussionsHome } from './DiscussionsHome';
|
||||
@@ -8,8 +8,8 @@ import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { useIsOnDesktop } from '../data/hooks';
|
||||
import { selectAreThreadsFiltered, selectPostThreadCount } from '../data/selectors';
|
||||
import messages from '../messages';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import { messages as postMessages, showPostEditor } from '../posts';
|
||||
import { showPostEditor } from '../posts/data';
|
||||
import postMessages from '../posts/post-actions-bar/messages';
|
||||
import EmptyPage from './EmptyPage';
|
||||
|
||||
const EmptyPosts = ({ subTitleMessage }) => {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { initializeStore } from '../../store';
|
||||
import { executeThunk } from '../../test-utils';
|
||||
import executeThunk from '../../test-utils';
|
||||
import messages from '../messages';
|
||||
import { getThreadsApiUrl } from '../posts/data/api';
|
||||
import { fetchThreads } from '../posts/data/thunks';
|
||||
|
||||
@@ -8,8 +8,8 @@ import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { useIsOnDesktop, useTotalTopicThreadCount } from '../data/hooks';
|
||||
import { selectTopicThreadCount } from '../data/selectors';
|
||||
import messages from '../messages';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
import { messages as postMessages, showPostEditor } from '../posts';
|
||||
import { showPostEditor } from '../posts/data';
|
||||
import postMessages from '../posts/post-actions-bar/messages';
|
||||
import EmptyPage from './EmptyPage';
|
||||
|
||||
const EmptyTopics = () => {
|
||||
|
||||
@@ -11,9 +11,9 @@ import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { getApiBaseUrl, Routes as ROUTES } from '../../data/constants';
|
||||
import { initializeStore } from '../../store';
|
||||
import { executeThunk } from '../../test-utils';
|
||||
import executeThunk from '../../test-utils';
|
||||
import messages from '../messages';
|
||||
import { fetchCourseTopics } from '../topics/data/thunks';
|
||||
import fetchCourseTopics from '../topics/data/thunks';
|
||||
import EmptyTopics from './EmptyTopics';
|
||||
|
||||
import '../topics/data/__factories__';
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
export { default as EmptyLearners } from './EmptyLearners';
|
||||
export { default as EmptyPage } from './EmptyPage';
|
||||
// eslint-disable-next-line import/no-cycle
|
||||
export { default as EmptyPosts } from './EmptyPosts';
|
||||
export { default as EmptyTopics } from './EmptyTopics';
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { Spinner } from '@edx/paragon';
|
||||
|
||||
import { RequestStatus, Routes } from '../../data/constants';
|
||||
import { DiscussionContext } from '../common/context';
|
||||
import DiscussionContext from '../common/context';
|
||||
import { selectDiscussionProvider } from '../data/selectors';
|
||||
import { selectTopicThreadsIds } from '../posts/data/selectors';
|
||||
import PostsList from '../posts/PostsList';
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
selectArchivedTopic, selectLoadingStatus, selectNonCoursewareTopics,
|
||||
selectSubsection, selectSubsectionUnits, selectUnits,
|
||||
} from './data/selectors';
|
||||
import { fetchCourseTopicsV3 } from './data/thunks';
|
||||
import fetchCourseTopicsV3 from './data/thunks';
|
||||
import { BackButton, NoResults } from './components';
|
||||
import messages from './messages';
|
||||
import { Topic } from './topic';
|
||||
|
||||
@@ -16,13 +16,13 @@ import { AppProvider } from '@edx/frontend-platform/react';
|
||||
import { PostActionsBar } from '../../components';
|
||||
import { Routes as ROUTES } from '../../data/constants';
|
||||
import { initializeStore } from '../../store';
|
||||
import { executeThunk } from '../../test-utils';
|
||||
import { DiscussionContext } from '../common/context';
|
||||
import executeThunk from '../../test-utils';
|
||||
import DiscussionContext from '../common/context';
|
||||
import { getThreadsApiUrl } from '../posts/data/api';
|
||||
import { fetchThreads } from '../posts/data/thunks';
|
||||
import { getCourseTopicsApiUrl } from './data/api';
|
||||
import { selectCoursewareTopics } from './data/selectors';
|
||||
import { fetchCourseTopicsV3 } from './data/thunks';
|
||||
import fetchCourseTopicsV3 from './data/thunks';
|
||||
import TopicPostsView from './TopicPostsView';
|
||||
import TopicsView from './TopicsView';
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Spinner } from '@edx/paragon';
|
||||
|
||||
import SearchInfo from '../../components/SearchInfo';
|
||||
import { RequestStatus } from '../../data/constants';
|
||||
import { DiscussionContext } from '../common/context';
|
||||
import DiscussionContext from '../common/context';
|
||||
import { selectAreThreadsFiltered, selectDiscussionProvider } from '../data/selectors';
|
||||
import { clearFilter, clearSort } from '../posts/data/slices';
|
||||
import NoResults from '../posts/NoResults';
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
selectNonCoursewareTopics, selectTopicFilter, selectTopics,
|
||||
} from './data/selectors';
|
||||
import { setFilter } from './data/slices';
|
||||
import { fetchCourseTopicsV3 } from './data/thunks';
|
||||
import fetchCourseTopicsV3 from './data/thunks';
|
||||
import { ArchivedBaseGroup, SectionBaseGroup, Topic } from './topic';
|
||||
|
||||
const TopicsList = () => {
|
||||
|
||||
@@ -15,11 +15,11 @@ 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 executeThunk from '../../test-utils';
|
||||
import DiscussionContext from '../common/context';
|
||||
import { getCourseTopicsApiUrl } from './data/api';
|
||||
import { selectCoursewareTopics, selectNonCoursewareTopics } from './data/selectors';
|
||||
import { fetchCourseTopicsV3 } from './data/thunks';
|
||||
import fetchCourseTopicsV3 from './data/thunks';
|
||||
import TopicPostsView from './TopicPostsView';
|
||||
import TopicsView from './TopicsView';
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useParams } from 'react-router-dom';
|
||||
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { DiscussionContext } from '../../common/context';
|
||||
import DiscussionContext from '../../common/context';
|
||||
import { useIsOnDesktop } from '../../data/hooks';
|
||||
import { selectPostThreadCount } from '../../data/selectors';
|
||||
import EmptyPage from '../../empty-posts/EmptyPage';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export { default as BackButton } from './BackButton';
|
||||
export { default as EmptyTopic } from './EmptyTopics';
|
||||
export { default as NoResults } from './NoResults';
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { camelCaseObject } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { initializeMockApp } from '@edx/frontend-platform/testing';
|
||||
|
||||
import { initializeStore } from '../../../store';
|
||||
import { executeThunk } from '../../../test-utils';
|
||||
import executeThunk from '../../../test-utils';
|
||||
import { getCourseTopicsApiUrl, getCourseTopicsV3 } from './api';
|
||||
import { fetchCourseTopicsV3 } from './thunks';
|
||||
import fetchCourseTopicsV3 from './thunks';
|
||||
|
||||
import './__factories__';
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { initializeMockApp } from '@edx/frontend-platform/testing';
|
||||
|
||||
import { initializeStore } from '../../../store';
|
||||
import { executeThunk } from '../../../test-utils';
|
||||
import executeThunk from '../../../test-utils';
|
||||
import { getCourseTopicsApiUrl } from './api';
|
||||
import { fetchCourseTopicsV3 } from './thunks';
|
||||
import fetchCourseTopicsV3 from './thunks';
|
||||
|
||||
import './__factories__';
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { initializeMockApp } from '@edx/frontend-platform/testing';
|
||||
|
||||
import { initializeStore } from '../../../store';
|
||||
import { executeThunk } from '../../../test-utils';
|
||||
import executeThunk from '../../../test-utils';
|
||||
import { getCourseTopicsApiUrl } from './api';
|
||||
import {
|
||||
selectArchivedTopic,
|
||||
@@ -20,7 +20,7 @@ import {
|
||||
selectTotalTopicsThreadsCount,
|
||||
selectUnits,
|
||||
} from './selectors';
|
||||
import { fetchCourseTopicsV3 } from './thunks';
|
||||
import fetchCourseTopicsV3 from './thunks';
|
||||
|
||||
import './__factories__';
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
|
||||
export const selectTopicFilter = state => state.inContextTopics.filter.trim().toLowerCase();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-param-reassign,import/prefer-default-export */
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
import { RequestStatus } from '../../../data/constants';
|
||||
@@ -16,27 +15,42 @@ const topicsSlice = createSlice({
|
||||
filter: '',
|
||||
},
|
||||
reducers: {
|
||||
fetchCourseTopicsRequest: (state) => {
|
||||
state.status = RequestStatus.IN_PROGRESS;
|
||||
},
|
||||
fetchCourseTopicsSuccess: (state, { payload }) => {
|
||||
state.status = RequestStatus.SUCCESSFUL;
|
||||
state.topics = payload.topics;
|
||||
state.coursewareTopics = payload.coursewareTopics;
|
||||
state.nonCoursewareTopics = payload.nonCoursewareTopics;
|
||||
state.nonCoursewareIds = payload.nonCoursewareIds;
|
||||
state.units = payload.units;
|
||||
state.archivedTopics = payload.archivedTopics;
|
||||
},
|
||||
fetchCourseTopicsFailed: (state) => {
|
||||
state.status = RequestStatus.FAILED;
|
||||
},
|
||||
fetchCourseTopicsDenied: (state) => {
|
||||
state.status = RequestStatus.DENIED;
|
||||
},
|
||||
setFilter: (state, { payload }) => {
|
||||
state.filter = payload;
|
||||
},
|
||||
fetchCourseTopicsRequest: (state) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.IN_PROGRESS,
|
||||
}
|
||||
),
|
||||
fetchCourseTopicsSuccess: (state, { payload }) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.SUCCESSFUL,
|
||||
topics: payload.topics,
|
||||
coursewareTopics: payload.coursewareTopics,
|
||||
nonCoursewareTopics: payload.nonCoursewareTopics,
|
||||
nonCoursewareIds: payload.nonCoursewareIds,
|
||||
units: payload.units,
|
||||
archivedTopics: payload.archivedTopics,
|
||||
}
|
||||
),
|
||||
fetchCourseTopicsFailed: (state) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.FAILED,
|
||||
}
|
||||
),
|
||||
fetchCourseTopicsDenied: (state) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.DENIED,
|
||||
}
|
||||
),
|
||||
setFilter: (state, { payload }) => (
|
||||
{
|
||||
...state,
|
||||
filter: payload,
|
||||
}
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { reduce } from 'lodash';
|
||||
|
||||
import { logError } from '@edx/frontend-platform/logging';
|
||||
@@ -53,7 +52,7 @@ function normalizeTopicsV3(topics) {
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchCourseTopicsV3(courseId) {
|
||||
export default function fetchCourseTopicsV3(courseId) {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
dispatch(fetchCourseTopicsRequest({ courseId }));
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export { default as TopicPostsView } from './TopicPostsView';
|
||||
export { default as TopicsView } from './TopicsView';
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { Icon, SearchField } from '@edx/paragon';
|
||||
import { Search as SearchIcon } from '@edx/paragon/icons';
|
||||
|
||||
import { DiscussionContext } from '../../common/context';
|
||||
import DiscussionContext from '../../common/context';
|
||||
import postsMessages from '../../posts/post-actions-bar/messages';
|
||||
import { setFilter as setTopicFilter } from '../data/slices';
|
||||
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export { default as TopicSearchBar } from './TopicSearchBar';
|
||||
export { default as TopicSearchResultBar } from './TopicSearchResultBar';
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
/* eslint-disable react/prop-types */
|
||||
/* eslint-disable no-unused-vars, react/forbid-prop-types */
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { Icon, OverlayTrigger, Tooltip } from '@edx/paragon';
|
||||
import { HelpOutline, PostOutline, Report } from '@edx/paragon/icons';
|
||||
|
||||
import TopicStats from '../../../components/TopicStats';
|
||||
import { Routes } from '../../../data/constants';
|
||||
import { selectUserHasModerationPrivileges, selectUserIsGroupTa } from '../../data/selectors';
|
||||
import { discussionsPath } from '../../utils';
|
||||
import messages from '../messages';
|
||||
|
||||
@@ -24,10 +18,6 @@ const Topic = ({
|
||||
}) => {
|
||||
const intl = useIntl();
|
||||
const { courseId } = useParams();
|
||||
const userHasModerationPrivileges = useSelector(selectUserHasModerationPrivileges);
|
||||
const userIsGroupTa = useSelector(selectUserIsGroupTa);
|
||||
const { inactiveFlags, activeFlags } = topic;
|
||||
const canSeeReportedStats = (activeFlags || inactiveFlags) && (userHasModerationPrivileges || userIsGroupTa);
|
||||
const isSelected = (id) => window.location.pathname.includes(id);
|
||||
const topicUrl = discussionsPath(Routes.TOPICS.TOPIC, {
|
||||
courseId,
|
||||
@@ -72,7 +62,8 @@ export const topicShape = PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
usage_key: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
thread_counts: PropTypes.shape({
|
||||
displayName: PropTypes.string,
|
||||
threadCounts: PropTypes.shape({
|
||||
discussions: PropTypes.number,
|
||||
questions: PropTypes.number,
|
||||
}),
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export { default as ArchivedBaseGroup } from './ArchivedBaseGroup';
|
||||
export { default as SectionBaseGroup } from './SectionBaseGroup';
|
||||
export { default as Topic } from './Topic';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export * from './discussions-home';
|
||||
export * from './post-comments';
|
||||
export { default as DiscussionsHome } from './discussions-home/DiscussionsHome';
|
||||
export { default as PostCommentsView } from './post-comments/PostCommentsView';
|
||||
export * from './posts';
|
||||
export * from './topics';
|
||||
export { default as TopicsView } from './topics/TopicsView';
|
||||
|
||||
@@ -16,9 +16,9 @@ import {
|
||||
RequestStatus,
|
||||
Routes,
|
||||
} from '../../data/constants';
|
||||
import { DiscussionContext } from '../common/context';
|
||||
import DiscussionContext from '../common/context';
|
||||
import { selectUserHasModerationPrivileges, selectUserIsStaff } from '../data/selectors';
|
||||
import { usePostList } from '../posts/data/hooks';
|
||||
import usePostList from '../posts/data/hooks';
|
||||
import {
|
||||
selectAllThreadsIds,
|
||||
selectThreadNextPage,
|
||||
|
||||
@@ -16,10 +16,10 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { initializeStore } from '../../store';
|
||||
import { executeThunk } from '../../test-utils';
|
||||
import executeThunk from '../../test-utils';
|
||||
import { getCohortsApiUrl } from '../cohorts/data/api';
|
||||
import { fetchCourseCohorts } from '../cohorts/data/thunks';
|
||||
import { DiscussionContext } from '../common/context';
|
||||
import fetchCourseCohorts from '../cohorts/data/thunks';
|
||||
import DiscussionContext from '../common/context';
|
||||
import { learnerPostsApiUrl } from './data/api';
|
||||
import { fetchUserPosts } from './data/thunks';
|
||||
import LearnerPostsView from './LearnerPostsView';
|
||||
|
||||
@@ -55,14 +55,14 @@ const LearnersView = () => {
|
||||
dispatch(setUsernameSearch(''));
|
||||
}, []);
|
||||
|
||||
const renderLearnersList = useMemo(() => (
|
||||
(
|
||||
courseConfigLoadingStatus === RequestStatus.SUCCESSFUL && learners.map((learner) => (
|
||||
const renderLearnersList = useMemo(() => {
|
||||
if (courseConfigLoadingStatus === RequestStatus.SUCCESSFUL) {
|
||||
return learners.map((learner) => (
|
||||
<LearnerCard learner={learner} key={learner.username} />
|
||||
))
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
) || <></>
|
||||
), [courseConfigLoadingStatus, learners]);
|
||||
));
|
||||
}
|
||||
return null;
|
||||
}, [courseConfigLoadingStatus, learners]);
|
||||
|
||||
return (
|
||||
<div className="d-flex flex-column border-right border-light-400">
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable default-param-last */
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
@@ -16,10 +15,10 @@ import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { PostActionsBar } from '../../components';
|
||||
import { initializeStore } from '../../store';
|
||||
import { executeThunk } from '../../test-utils';
|
||||
import { DiscussionContext } from '../common/context';
|
||||
import executeThunk from '../../test-utils';
|
||||
import DiscussionContext from '../common/context';
|
||||
import { getDiscussionsConfigUrl } from '../data/api';
|
||||
import { fetchCourseConfig } from '../data/thunks';
|
||||
import fetchCourseConfig from '../data/thunks';
|
||||
import { getUserProfileApiUrl, learnersApiUrl } from './data/api';
|
||||
import { fetchLearners } from './data/thunks';
|
||||
import LearnersView from './LearnersView';
|
||||
@@ -81,9 +80,9 @@ describe('LearnersView', () => {
|
||||
pageSize = 6,
|
||||
page = 1,
|
||||
username = ['learner-1', 'learner-2', 'learner-3'],
|
||||
searchText,
|
||||
activeFlags,
|
||||
inactiveFlags,
|
||||
searchText = null,
|
||||
activeFlags = null,
|
||||
inactiveFlags = null,
|
||||
) {
|
||||
Factory.resetAll();
|
||||
const learnersData = Factory.build('learnersResult', {}, {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import snakeCase from 'lodash/snakeCase';
|
||||
|
||||
import { ensureConfig, getConfig, snakeCaseObject } from '@edx/frontend-platform';
|
||||
|
||||
@@ -5,7 +5,7 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { initializeMockApp } from '@edx/frontend-platform/testing';
|
||||
|
||||
import { initializeStore } from '../../../store';
|
||||
import { executeThunk } from '../../../test-utils';
|
||||
import executeThunk from '../../../test-utils';
|
||||
import { setupLearnerMockResponse } from '../test-utils';
|
||||
import { setPostFilter, setSortedBy, setUsernameSearch } from './slices';
|
||||
import { fetchLearners } from './thunks';
|
||||
|
||||
@@ -5,7 +5,7 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
import { initializeMockApp } from '@edx/frontend-platform/testing';
|
||||
|
||||
import { initializeStore } from '../../../store';
|
||||
import { executeThunk } from '../../../test-utils';
|
||||
import executeThunk from '../../../test-utils';
|
||||
import { getUserProfileApiUrl, learnersApiUrl } from './api';
|
||||
import {
|
||||
learnersLoadingStatus,
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
|
||||
export const selectAllLearners = createSelector(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable no-param-reassign,import/prefer-default-export */
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
import {
|
||||
@@ -28,38 +27,63 @@ const learnersSlice = createSlice({
|
||||
usernameSearch: null,
|
||||
},
|
||||
reducers: {
|
||||
fetchLearnersSuccess: (state, { payload }) => {
|
||||
state.status = RequestStatus.SUCCESSFUL;
|
||||
state.pages[payload.page - 1] = payload.results;
|
||||
state.learnerProfiles = {
|
||||
...state.learnerProfiles,
|
||||
...(payload.learnerProfiles || {}),
|
||||
};
|
||||
state.nextPage = (payload.page < payload.pagination.numPages) ? payload.page + 1 : null;
|
||||
state.totalPages = payload.pagination.numPages;
|
||||
state.totalLearners = payload.pagination.count;
|
||||
},
|
||||
fetchLearnersFailed: (state) => {
|
||||
state.status = RequestStatus.FAILED;
|
||||
},
|
||||
fetchLearnersDenied: (state) => {
|
||||
state.status = RequestStatus.DENIED;
|
||||
},
|
||||
fetchLearnersRequest: (state) => {
|
||||
state.status = RequestStatus.IN_PROGRESS;
|
||||
},
|
||||
setSortedBy: (state, { payload }) => {
|
||||
state.pages = [];
|
||||
state.sortedBy = payload;
|
||||
},
|
||||
setUsernameSearch: (state, { payload }) => {
|
||||
state.usernameSearch = payload;
|
||||
state.pages = [];
|
||||
},
|
||||
setPostFilter: (state, { payload }) => {
|
||||
state.pages = [];
|
||||
state.postFilter = payload;
|
||||
},
|
||||
fetchLearnersSuccess: (state, { payload }) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.SUCCESSFUL,
|
||||
pages: [
|
||||
...state.pages.slice(0, payload.page - 1),
|
||||
payload.results,
|
||||
...state.pages.slice(payload.page),
|
||||
],
|
||||
learnerProfiles: {
|
||||
...state.learnerProfiles,
|
||||
...(payload.learnerProfiles || {}),
|
||||
},
|
||||
nextPage: payload.page < payload.pagination.numPages ? payload.page + 1 : null,
|
||||
totalPages: payload.pagination.numPages,
|
||||
totalLearners: payload.pagination.count,
|
||||
}
|
||||
),
|
||||
fetchLearnersFailed: (state) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.FAILED,
|
||||
}
|
||||
),
|
||||
fetchLearnersDenied: (state) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.DENIED,
|
||||
}
|
||||
),
|
||||
fetchLearnersRequest: (state) => (
|
||||
{
|
||||
...state,
|
||||
status: RequestStatus.IN_PROGRESS,
|
||||
}
|
||||
),
|
||||
setSortedBy: (state, { payload }) => (
|
||||
{
|
||||
...state,
|
||||
pages: [],
|
||||
sortedBy: payload,
|
||||
}
|
||||
),
|
||||
setUsernameSearch: (state, { payload }) => (
|
||||
{
|
||||
...state,
|
||||
usernameSearch: payload,
|
||||
pages: [],
|
||||
}
|
||||
),
|
||||
setPostFilter: (state, { payload }) => (
|
||||
{
|
||||
...state,
|
||||
pages: [],
|
||||
postFilter: payload,
|
||||
}
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
import { camelCaseObject, snakeCaseObject } from '@edx/frontend-platform';
|
||||
import { logError } from '@edx/frontend-platform/logging';
|
||||
|
||||
|
||||
@@ -1,3 +1,2 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export { default as LearnerPostsView } from './LearnerPostsView';
|
||||
export { default as LearnersView } from './LearnersView';
|
||||
|
||||
@@ -8,8 +8,8 @@ import { sendTrackEvent } from '@edx/frontend-platform/analytics';
|
||||
|
||||
import FilterBar from '../../../components/FilterBar';
|
||||
import { PostsStatusFilter, ThreadType } from '../../../data/constants';
|
||||
import { selectCourseCohorts } from '../../cohorts/data/selectors';
|
||||
import { fetchCourseCohorts } from '../../cohorts/data/thunks';
|
||||
import selectCourseCohorts from '../../cohorts/data/selectors';
|
||||
import fetchCourseCohorts from '../../cohorts/data/thunks';
|
||||
import { selectUserHasModerationPrivileges, selectUserIsGroupTa } from '../../data/selectors';
|
||||
import { setPostFilter } from '../data/slices';
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import { initializeMockApp } from '@edx/frontend-platform';
|
||||
import { AppProvider } from '@edx/frontend-platform/react';
|
||||
|
||||
import { initializeStore } from '../../../store';
|
||||
import { DiscussionContext } from '../../common/context';
|
||||
import DiscussionContext from '../../common/context';
|
||||
import LearnerPostFilterBar from './LearnerPostFilterBar';
|
||||
|
||||
let store;
|
||||
|
||||
@@ -3,11 +3,11 @@ import React, { useContext } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { Routes } from '../../../data/constants';
|
||||
import { DiscussionContext } from '../../common/context';
|
||||
import DiscussionContext from '../../common/context';
|
||||
import { discussionsPath } from '../../utils';
|
||||
import LearnerAvatar from './LearnerAvatar';
|
||||
import LearnerFooter from './LearnerFooter';
|
||||
import { learnerShape } from './proptypes';
|
||||
import learnerShape from './proptypes';
|
||||
|
||||
const LearnerCard = ({ learner }) => {
|
||||
const {
|
||||
|
||||
@@ -5,9 +5,10 @@ import { useSelector } from 'react-redux';
|
||||
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { Icon, OverlayTrigger, Tooltip } from '@edx/paragon';
|
||||
import { Edit, Report, ReportGmailerrorred } from '@edx/paragon/icons';
|
||||
import {
|
||||
Edit, QuestionAnswerOutline, Report, ReportGmailerrorred,
|
||||
} from '@edx/paragon/icons';
|
||||
|
||||
import { QuestionAnswerOutline } from '../../../components/icons';
|
||||
import { selectUserHasModerationPrivileges, selectUserIsGroupTa } from '../../data/selectors';
|
||||
import messages from '../messages';
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
/* eslint-disable import/prefer-default-export */
|
||||
export { default as LearnerCard } from './LearnerCard';
|
||||
export { default as LearnerFilterBar } from './LearnerFilterBar';
|
||||
export { default as LearnerFooter } from './LearnerFooter';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user