Skip to content

Commit d0751c9

Browse files
committed
refactor: use single mutation for unit settings
1 parent bbfabd3 commit d0751c9

6 files changed

Lines changed: 26 additions & 140 deletions

File tree

src/course-outline/data/api.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { camelCaseObject, getConfig } from '@edx/frontend-platform';
22
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
3+
import { PUBLISH_TYPES } from '@src/course-unit/constants';
34
import { XBlock } from '@src/data/types';
45
import {
56
CourseOutline,
@@ -271,13 +272,14 @@ export async function configureCourseSubsection(variables: ConfigureSubsectionDa
271272
export async function configureCourseUnit(variables: ConfigureUnitData): Promise<object> {
272273
const { data } = await getAuthenticatedHttpClient()
273274
.post(getCourseItemApiUrl(variables.unitId), {
274-
publish: 'republish',
275-
metadata: {
276-
// The backend expects metadata.visible_to_staff_only to either true or null
277-
visible_to_staff_only: variables.isVisibleToStaffOnly ? true : null,
278-
group_access: variables.groupAccess,
279-
discussion_enabled: variables.discussionEnabled,
280-
},
275+
publish: variables.groupAccess ? null : variables.type,
276+
...(variables.type === PUBLISH_TYPES.republish ? {
277+
metadata: {
278+
visible_to_staff_only: variables.isVisibleToStaffOnly ? true : null,
279+
discussion_enabled: variables.discussionEnabled,
280+
...(variables.groupAccess != null && { group_access: variables.groupAccess }),
281+
},
282+
} : {}),
281283
});
282284

283285
return data;

src/course-outline/data/types.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { XBlock, XBlockActions } from '@src/data/types';
2+
import { PUBLISH_TYPES } from '@src/course-unit/constants';
23

34
export interface CourseStructure {
45
highlightsEnabledForMessaging: boolean,
@@ -117,10 +118,11 @@ export interface ConfigureSubsectionData {
117118
}
118119

119120
export interface ConfigureUnitData {
120-
unitId: string,
121-
isVisibleToStaffOnly: boolean,
122-
groupAccess: object,
123-
discussionEnabled: boolean,
121+
unitId: string;
122+
isVisibleToStaffOnly: boolean;
123+
type: keyof typeof PUBLISH_TYPES;
124+
groupAccess: Record<string, any> | null,
125+
discussionEnabled: boolean;
124126
}
125127

126128
export type StaticFileNotices = {

src/course-unit/data/api.ts

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { camelCaseObject, getConfig } from '@edx/frontend-platform';
22
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
33

4-
import { PUBLISH_TYPES } from '../constants';
54
import { CourseContainerChildrenData, CourseOutlineData, MoveInfoData } from './types';
65
import { isUnitImportedFromLib, normalizeCourseSectionVerticalData, updateXBlockBlockIdToId } from './utils';
76

@@ -41,40 +40,6 @@ export async function getVerticalData(unitId: string): Promise<object> {
4140
return courseSectionVerticalData;
4241
}
4342

44-
/**
45-
* Handles the visibility and data of a course unit, such as publishing, resetting to default values,
46-
* and toggling visibility to students.
47-
*/
48-
export async function handleCourseUnitVisibilityAndData({
49-
unitId,
50-
type,
51-
isVisible,
52-
isDiscussionEnabled,
53-
groupAccess,
54-
}: {
55-
unitId: string,
56-
type: string, // The action type (e.g., PUBLISH_TYPES.discardChanges).
57-
isVisible: boolean, // The visibility status for students.
58-
isDiscussionEnabled: boolean,
59-
groupAccess: Record<string, any> | null,
60-
}): Promise<object> {
61-
const body = {
62-
publish: groupAccess ? null : type,
63-
...(type === PUBLISH_TYPES.republish ? {
64-
metadata: {
65-
visible_to_staff_only: isVisible ? true : null,
66-
discussion_enabled: isDiscussionEnabled,
67-
...(groupAccess != null && { group_access: groupAccess }),
68-
},
69-
} : {}),
70-
};
71-
72-
const { data } = await getAuthenticatedHttpClient()
73-
.post(getXBlockBaseApiUrl(unitId), body);
74-
75-
return camelCaseObject(data);
76-
}
77-
7843
/**
7944
* Get an object containing course vertical children data.
8045
*/

src/course-unit/data/apiHooks.ts

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,9 @@
1-
import { courseOutlineQueryKeys } from '@src/course-outline/data/apiHooks';
2-
import {
3-
fetchCourseSectionVerticalDataSuccess, updateCourseVerticalChildren, updateQueryPendingStatus, updateSavingStatus,
4-
} from '@src/course-unit/data/slice';
5-
import { RequestStatus } from '@src/data/constants';
6-
import { hideProcessingNotification, showProcessingNotification } from '@src/generic/processing-notification/data/slice';
7-
import { useMutation, useQueryClient } from '@tanstack/react-query';
8-
import { useDispatch } from 'react-redux';
1+
import { useMutation } from '@tanstack/react-query';
92

10-
import { handleResponseErrors } from '@src/generic/saving-error-alert';
113
import {
124
acceptLibraryBlockChanges,
13-
getCourseContainerChildren,
14-
getVerticalData,
15-
handleCourseUnitVisibilityAndData,
165
ignoreLibraryBlockChanges,
176
} from './api';
18-
import { getNotificationMessage } from './utils';
197

208
/**
219
* Hook that provides a "mutation" that can be used to accept library block changes.
@@ -32,35 +20,3 @@ export const useAcceptLibraryBlockChanges = () => useMutation({
3220
export const useIgnoreLibraryBlockChanges = () => useMutation({
3321
mutationFn: ignoreLibraryBlockChanges,
3422
});
35-
36-
export const useEditCourseUnitVisibilityAndData = () => {
37-
const queryClient = useQueryClient();
38-
const dispatch = useDispatch();
39-
return useMutation({
40-
mutationFn: handleCourseUnitVisibilityAndData,
41-
onMutate: (variables) => {
42-
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
43-
dispatch(updateQueryPendingStatus(true));
44-
dispatch(showProcessingNotification(
45-
getNotificationMessage(variables.type, variables.isVisible, true),
46-
));
47-
},
48-
onSuccess: async (_data, variables) => {
49-
const courseSectionVerticalData = await getVerticalData(variables.unitId);
50-
dispatch(fetchCourseSectionVerticalDataSuccess(courseSectionVerticalData));
51-
const courseVerticalChildrenData = await getCourseContainerChildren(variables.unitId);
52-
dispatch(updateCourseVerticalChildren(courseVerticalChildrenData));
53-
dispatch(hideProcessingNotification());
54-
dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
55-
},
56-
onSettled: async (_data, _err, variables) => {
57-
queryClient.invalidateQueries({
58-
queryKey: courseOutlineQueryKeys.courseItemId(variables.unitId),
59-
});
60-
},
61-
onError: (error) => {
62-
dispatch(hideProcessingNotification());
63-
handleResponseErrors(error, dispatch, updateSavingStatus);
64-
},
65-
});
66-
};

src/course-unit/data/thunk.js

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import {
1414
editUnitDisplayName,
1515
getVerticalData,
1616
getCourseContainerChildren,
17-
handleCourseUnitVisibilityAndData,
1817
deleteUnitItem,
1918
duplicateUnitItem,
2019
getCourseOutlineInfo,
@@ -97,48 +96,6 @@ export function editCourseItemQuery(itemId, displayName, sequenceId) {
9796
};
9897
}
9998

100-
export function editCourseUnitVisibilityAndData(
101-
itemId,
102-
type,
103-
isVisible,
104-
groupAccess,
105-
isDiscussionEnabled,
106-
callback,
107-
blockId = itemId,
108-
) {
109-
return async (dispatch) => {
110-
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
111-
dispatch(updateQueryPendingStatus(true));
112-
const notification = getNotificationMessage(type, isVisible, true);
113-
dispatch(showProcessingNotification(notification));
114-
115-
try {
116-
await handleCourseUnitVisibilityAndData(
117-
itemId,
118-
type,
119-
isVisible,
120-
isDiscussionEnabled,
121-
groupAccess,
122-
).then(async (result) => {
123-
if (result) {
124-
if (callback) {
125-
callback();
126-
}
127-
const courseSectionVerticalData = await getVerticalData(blockId);
128-
dispatch(fetchCourseSectionVerticalDataSuccess(courseSectionVerticalData));
129-
const courseVerticalChildrenData = await getCourseContainerChildren(blockId);
130-
dispatch(updateCourseVerticalChildren(courseVerticalChildrenData));
131-
dispatch(hideProcessingNotification());
132-
dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
133-
}
134-
});
135-
} catch (error) {
136-
dispatch(hideProcessingNotification());
137-
handleResponseErrors(error, dispatch, updateSavingStatus);
138-
}
139-
};
140-
}
141-
14299
export function createNewCourseXBlock(body, callback, blockId, sendMessageToIframe) {
143100
return async (dispatch) => {
144101
if (body.stagedContent) {

src/course-unit/unit-sidebar/unit-info/GenericUnitInfoSettings.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
22
import { Button, ButtonGroup } from '@openedx/paragon';
33
import { PUBLISH_TYPES, UNIT_VISIBILITY_STATES } from '@src/course-unit/constants';
4-
import { editCourseUnitVisibilityAndData } from '@src/course-unit/data/thunk';
54
import { UserPartitionInfoTypes } from '@src/data/types';
65
import { AccessEditComponent, DiscussionEditComponent } from '@src/generic/configure-modal/UnitTab';
76
import { SidebarContent, SidebarSection } from '@src/generic/sidebar';
87
import { Form, Formik } from 'formik';
98
import { useMemo } from 'react';
10-
import { useDispatch } from 'react-redux';
119
import configureMessages from '@src/generic/configure-modal/messages';
12-
import { useEditCourseUnitVisibilityAndData } from '@src/course-unit/data/apiHooks';
1310
import messages from './messages';
11+
import { useConfigureUnit } from '@src/course-outline/data/apiHooks';
1412

1513
interface UnitInfoSettingsProps {
1614
id: string;
1715
visibilityState: string;
1816
discussionEnabled?: boolean;
1917
userPartitionInfo?: UserPartitionInfoTypes;
2018
updateCallback?: () => void;
19+
sectionId?: string;
20+
subsectionId?: string;
2121
}
2222

2323
/**
@@ -32,10 +32,12 @@ export const GenericUnitInfoSettings = (props: UnitInfoSettingsProps) => {
3232
visibilityState,
3333
discussionEnabled,
3434
userPartitionInfo,
35+
sectionId,
36+
subsectionId,
3537
} = props;
3638

3739
const visibleToStaffOnly = visibilityState === UNIT_VISIBILITY_STATES.staffOnly;
38-
const mutateFn = useEditCourseUnitVisibilityAndData();
40+
const mutateFn = useConfigureUnit();
3941

4042
const handleUpdate = async (
4143
isVisible: boolean,
@@ -46,9 +48,11 @@ export const GenericUnitInfoSettings = (props: UnitInfoSettingsProps) => {
4648
await mutateFn.mutateAsync({
4749
unitId: id,
4850
type: PUBLISH_TYPES.republish,
49-
isVisible,
51+
isVisibleToStaffOnly: isVisible,
5052
groupAccess,
51-
isDiscussionEnabled: !!isDiscussionEnabled,
53+
discussionEnabled: !!isDiscussionEnabled,
54+
sectionId,
55+
subsectionId,
5256
}, {
5357
onSuccess: () => props.updateCallback?.(),
5458
});

0 commit comments

Comments
 (0)