diff --git a/src/help-urls/data/api.js b/src/help-urls/data/api.js new file mode 100644 index 000000000..3243f5be3 --- /dev/null +++ b/src/help-urls/data/api.js @@ -0,0 +1,9 @@ +/* eslint-disable import/prefer-default-export */ +import { camelCaseObject, getConfig } from '@edx/frontend-platform'; +import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; + +export async function getHelpUrls() { + const { data } = await getAuthenticatedHttpClient() + .get(`${getConfig().STUDIO_BASE_URL}/api/contentstore/v1/help_urls`); + return camelCaseObject(data); +} diff --git a/src/help-urls/data/selectors.js b/src/help-urls/data/selectors.js new file mode 100644 index 000000000..cd88d4e72 --- /dev/null +++ b/src/help-urls/data/selectors.js @@ -0,0 +1,13 @@ +export const selectHelpUrlsByNames = (names) => (state) => { + const urlsDictionary = {}; + + names.forEach(name => { + urlsDictionary[name] = state.helpUrls.pages[name] || null; + }); + + return urlsDictionary; +}; + +export const getPages = (state) => state.helpUrls.pages; + +export const getLoadingHelpUrlsStatus = (state) => state.helpUrls.loadingHelpUrlsStatus; diff --git a/src/help-urls/data/slice.js b/src/help-urls/data/slice.js new file mode 100644 index 000000000..01c8821e5 --- /dev/null +++ b/src/help-urls/data/slice.js @@ -0,0 +1,27 @@ +/* eslint-disable no-param-reassign */ +import { createSlice } from '@reduxjs/toolkit'; + +const initialState = { + loadingHelpUrlsStatus: '', + pages: {}, +}; + +const slice = createSlice({ + name: 'helpUrls', + initialState, + reducers: { + updatePages: (state, { payload }) => { + state.pages = payload; + }, + updateLoadingHelpUrlsStatus: (state, { payload }) => { + state.loadingHelpUrlsStatus = payload.status; + }, + }, +}); + +export const { + updatePages, + updateLoadingHelpUrlsStatus, +} = slice.actions; + +export const { reducer } = slice; diff --git a/src/help-urls/data/thunks.js b/src/help-urls/data/thunks.js new file mode 100644 index 000000000..439c1cd1d --- /dev/null +++ b/src/help-urls/data/thunks.js @@ -0,0 +1,24 @@ +import { RequestStatus } from '../../data/constants'; + +import { getHelpUrls } from './api'; +import { updateLoadingHelpUrlsStatus, updatePages } from './slice'; + +/* eslint-disable import/prefer-default-export */ +export function fetchHelpUrls() { + return async (dispatch) => { + dispatch(updateLoadingHelpUrlsStatus({ status: RequestStatus.IN_PROGRESS })); + + try { + const urls = await getHelpUrls(); + + dispatch(updatePages(urls)); + + dispatch(updateLoadingHelpUrlsStatus({ status: RequestStatus.SUCCESSFUL })); + return true; + } catch (error) { + dispatch(updateLoadingHelpUrlsStatus({ status: RequestStatus.FAILED })); + + return false; + } + }; +} diff --git a/src/help-urls/hooks.jsx b/src/help-urls/hooks.jsx new file mode 100644 index 000000000..666f2b877 --- /dev/null +++ b/src/help-urls/hooks.jsx @@ -0,0 +1,22 @@ +import { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { isEmpty } from 'lodash'; + +import { fetchHelpUrls } from './data/thunks'; +import { getPages, selectHelpUrlsByNames } from './data/selectors'; + +const useHelpUrls = (tokenNames) => { + const dispatch = useDispatch(); + const helpTokens = useSelector(selectHelpUrlsByNames(tokenNames)); + const pages = useSelector(getPages); + + useEffect(() => { + if (isEmpty(pages)) { + dispatch(fetchHelpUrls()); + } + }, []); + + return helpTokens; +}; +/* eslint-disable-next-line import/prefer-default-export */ +export { useHelpUrls }; diff --git a/src/store.js b/src/store.js index 515751cbf..5b538134a 100644 --- a/src/store.js +++ b/src/store.js @@ -13,6 +13,7 @@ import { reducer as filesReducer } from './files-and-uploads/data/slice'; import { reducer as courseTeamReducer } from './course-team/data/slice'; import { reducer as CourseUpdatesReducer } from './course-updates/data/slice'; import { reducer as processingNotificationReducer } from './generic/processing-notification/data/slice'; +import { reducer as helpUrlsReducer } from './help-urls/data/slice'; export default function initializeStore(preloadedState = undefined) { return configureStore({ @@ -30,6 +31,7 @@ export default function initializeStore(preloadedState = undefined) { courseTeam: courseTeamReducer, courseUpdates: CourseUpdatesReducer, processingNotification: processingNotificationReducer, + helpUrls: helpUrlsReducer, }, preloadedState, });