diff --git a/src/discussions/data/api.js b/src/discussions/data/api.js new file mode 100644 index 00000000..eb966727 --- /dev/null +++ b/src/discussions/data/api.js @@ -0,0 +1,22 @@ +/* eslint-disable import/prefer-default-export */ + +import { ensureConfig, getConfig } from '@edx/frontend-platform'; +import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; + +ensureConfig([ + 'LMS_BASE_URL', +], 'Posts API service'); + +const apiBaseUrl = getConfig().LMS_BASE_URL; + +export const courseConfigApiUrl = `${apiBaseUrl}/api/discussion/v1/courses/`; + +/** + * Get discussions course config + * @param {string} courseId + */ +export async function getDiscussionsConfig(courseId) { + const url = `${courseConfigApiUrl}${courseId}/`; + const { data } = await getAuthenticatedHttpClient().get(url); + return data; +} diff --git a/src/discussions/data/selectors.js b/src/discussions/data/selectors.js new file mode 100644 index 00000000..6284bb23 --- /dev/null +++ b/src/discussions/data/selectors.js @@ -0,0 +1,7 @@ +/* eslint-disable import/prefer-default-export */ + +export const selectAnonymousPostingConfig = state => ({ + allowAnonymous: state.config.allowAnonymous, + allowAnonymousToPeers: state.config.allowAnonymousToPeers, +} +); diff --git a/src/discussions/data/slices.js b/src/discussions/data/slices.js new file mode 100644 index 00000000..e573603e --- /dev/null +++ b/src/discussions/data/slices.js @@ -0,0 +1,38 @@ +/* eslint-disable no-param-reassign,import/prefer-default-export */ +import { createSlice } from '@reduxjs/toolkit'; + +import { RequestStatus } from '../../data/constants'; + +const configSlice = createSlice({ + name: 'config', + initialState: { + status: RequestStatus.IN_PROGRESS, + blackouts: [], + allowAnonymous: false, + allowAnonymousToPeers: false, + }, + reducers: { + 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; + }, + }, +}); + +export const { + fetchConfigDenied, + fetchConfigFailed, + fetchConfigRequest, + fetchConfigSuccess, +} = configSlice.actions; + +export const configReducer = configSlice.reducer; diff --git a/src/discussions/data/thunks.js b/src/discussions/data/thunks.js new file mode 100644 index 00000000..bec4a0d9 --- /dev/null +++ b/src/discussions/data/thunks.js @@ -0,0 +1,31 @@ +/* eslint-disable import/prefer-default-export */ +import { camelCaseObject } from '@edx/frontend-platform'; +import { logError } from '@edx/frontend-platform/logging'; + +import { getHttpErrorStatus } from '../utils'; +import { getDiscussionsConfig } from './api'; +import { + fetchConfigDenied, fetchConfigFailed, fetchConfigRequest, fetchConfigSuccess, +} from './slices'; + +/** + * Fetches the configuration data for the course + * @param {string} courseId The course ID for the course to fetch config for. + * @returns {(function(*): Promise)|*} + */ +export function fetchCourseConfig(courseId) { + return async (dispatch) => { + try { + dispatch(fetchConfigRequest()); + const data = await getDiscussionsConfig(courseId); + dispatch(fetchConfigSuccess(camelCaseObject(data))); + } catch (error) { + if (getHttpErrorStatus(error) === 403) { + dispatch(fetchConfigDenied()); + } else { + dispatch(fetchConfigFailed()); + } + logError(error); + } + }; +} diff --git a/src/discussions/discussions-home/DiscussionsHome.jsx b/src/discussions/discussions-home/DiscussionsHome.jsx index 312040a7..6ca79d62 100644 --- a/src/discussions/discussions-home/DiscussionsHome.jsx +++ b/src/discussions/discussions-home/DiscussionsHome.jsx @@ -9,6 +9,7 @@ import { PostActionsBar } from '../../components'; import { ALL_ROUTES, Routes } from '../../data/constants'; import { CommentsView } from '../comments'; import { DiscussionContext } from '../common/context'; +import { fetchCourseConfig } from '../data/thunks'; import { BreadcrumbMenu, NavigationBar } from '../navigation'; import { PostEditor, PostsView } from '../posts'; import { clearRedirect } from '../posts/data'; @@ -28,6 +29,9 @@ export default function DiscussionsHome() { }, } = useRouteMatch(ALL_ROUTES); const redirectToThread = useSelector(state => state.threads.redirectToThread); + useEffect(() => { + dispatch(fetchCourseConfig(courseId)); + }, [courseId]); useEffect(() => { // After posting a new thread we'd like to redirect users to it, the topic and post id are temporarily // stored in redirectToThread diff --git a/src/discussions/navigation/navigation-bar/NavigationBar.jsx b/src/discussions/navigation/navigation-bar/NavigationBar.jsx index 2840bed5..a189de23 100644 --- a/src/discussions/navigation/navigation-bar/NavigationBar.jsx +++ b/src/discussions/navigation/navigation-bar/NavigationBar.jsx @@ -30,7 +30,7 @@ function NavigationBar({ intl }) {