11import { containerComparisonQueryKeys } from '@src/container-comparison/data/apiHooks' ;
2- import type { XBlock } from '@src/data/types' ;
2+ import type { XBlockBase , XblockChildInfo } from '@src/data/types' ;
33import { getCourseKey } from '@src/generic/key-utils' ;
44import { handleResponseErrors } from '@src/generic/saving-error-alert' ;
5+ import { ParentIds } from '@src/generic/types' ;
56import {
67 QueryClient ,
78 skipToken , useMutation , useQuery , useQueryClient ,
@@ -41,22 +42,22 @@ export const courseOutlineQueryKeys = {
4142 ] ,
4243} ;
4344
44- type ParentIds = {
45- /** This id will be used to invalidate data of parent subsection */
46- subsectionId ?: string ;
47- /** This id will be used to invalidate data of parent section */
48- sectionId ?: string ;
49- } ;
50-
5145/**
5246 * Invalidate parent Subsection and Section data.
47+ *
48+ * This function ensures that cached data for parent subsection and section is invalidated
49+ * when child items are created, updated, or deleted.
50+ *
51+ * Priority:
52+ * 1. If sectionId exists, invalidate section data which also updates all children block data
53+ * 2. Else If subsectionId exists, invalidate subsection data
5354 */
5455const invalidateParentQueries = async ( queryClient : QueryClient , variables : ParentIds ) => {
55- if ( variables . subsectionId ) {
56- await queryClient . invalidateQueries ( { queryKey : courseOutlineQueryKeys . courseItemId ( variables . subsectionId ) } ) ;
57- }
5856 if ( variables . sectionId ) {
5957 await queryClient . invalidateQueries ( { queryKey : courseOutlineQueryKeys . courseItemId ( variables . sectionId ) } ) ;
58+ } else if ( variables . subsectionId ) {
59+ // istanbul ignore next
60+ await queryClient . invalidateQueries ( { queryKey : courseOutlineQueryKeys . courseItemId ( variables . subsectionId ) } ) ;
6061 }
6162} ;
6263
@@ -66,6 +67,9 @@ type CreateCourseXBlockMutationProps = CreateCourseXBlockType & ParentIds;
6667 * Hook to create an XBLOCK in a course .
6768 * The `locator` is the ID of the parent block where this new XBLOCK should be created.
6869 * Can also be used to import block from library by passing `libraryContentKey` in request body
70+ *
71+ * @param callback - Optional function called after successful creation to handle additional logic
72+ * @returns Mutation object for creating course blocks
6973 */
7074export const useCreateCourseBlock = (
7175 callback ?: ( ( locator : string , parentLocator : string ) => Promise < void > ) ,
@@ -75,7 +79,6 @@ export const useCreateCourseBlock = (
7579 mutationFn : ( variables : CreateCourseXBlockMutationProps ) => createCourseXblock ( variables ) ,
7680 onSettled : async ( data : { locator : string ; } , _err , variables ) => {
7781 await callback ?.( data . locator , variables . parentLocator ) ;
78- queryClient . invalidateQueries ( { queryKey : courseOutlineQueryKeys . courseItemId ( variables . parentLocator ) } ) ;
7982 queryClient . invalidateQueries ( {
8083 queryKey : courseOutlineQueryKeys . courseDetails ( getCourseKey ( data . locator ) ) ,
8184 } ) ;
@@ -84,13 +87,34 @@ export const useCreateCourseBlock = (
8487 } ) ;
8588} ;
8689
87- export const useCourseItemData = < T = XBlock > ( itemId ?: string , initialData ?: T , enabled : boolean = true ) => (
88- useQuery ( {
90+ export const useCourseItemData = < T extends XBlockBase > ( itemId ?: string , initialData ?: T , enabled : boolean = true ) => {
91+ const queryClient = useQueryClient ( ) ;
92+ return useQuery < T > ( {
8993 initialData,
9094 queryKey : courseOutlineQueryKeys . courseItemId ( itemId ) ,
91- queryFn : enabled && itemId ? ( ) => getCourseItem < T > ( itemId ! ) : skipToken ,
92- } )
93- ) ;
95+ queryFn : enabled && itemId ? async ( ) => {
96+ const data = await getCourseItem < T > ( itemId ! ) ;
97+ // If the container has children blocks, update children react-query cache
98+ // data without hitting the API as each xblock call returns its children information as well.
99+ if ( 'childInfo' in data ) {
100+ // This could mean that data is of a section or subsection
101+ ( data . childInfo as XblockChildInfo ) . children . forEach ( async ( child ) => {
102+ await queryClient . cancelQueries ( { queryKey : courseOutlineQueryKeys . courseItemId ( child . id ) } ) ;
103+ queryClient . setQueryData ( courseOutlineQueryKeys . courseItemId ( child . id ) , child ) ;
104+ if ( 'childInfo' in child ) {
105+ // This means that the data is of section and so its children subsections also
106+ // have children i.e. units
107+ ( child . childInfo as XblockChildInfo ) . children . forEach ( async ( grandChild ) => {
108+ await queryClient . cancelQueries ( { queryKey : courseOutlineQueryKeys . courseItemId ( grandChild . id ) } ) ;
109+ queryClient . setQueryData ( courseOutlineQueryKeys . courseItemId ( grandChild . id ) , grandChild ) ;
110+ } ) ;
111+ }
112+ } ) ;
113+ }
114+ return data ;
115+ } : skipToken ,
116+ } ) ;
117+ } ;
94118
95119export const useCourseDetails = ( courseId ?: string , enabled : boolean = true ) => (
96120 useQuery ( {
@@ -99,6 +123,15 @@ export const useCourseDetails = (courseId?: string, enabled: boolean = true) =>
99123 } )
100124) ;
101125
126+ /**
127+ * Hook to update the display name of a course block.
128+ *
129+ * This mutation updates the display name of a course item and invalidates relevant cache queries
130+ * to ensure the UI reflects the changes.
131+ *
132+ * @param courseId - The ID of the course containing the item
133+ * @returns Mutation object for updating course block names
134+ */
102135export const useUpdateCourseBlockName = ( courseId : string ) => {
103136 const queryClient = useQueryClient ( ) ;
104137 return useMutation ( {
@@ -107,10 +140,9 @@ export const useUpdateCourseBlockName = (courseId: string) => {
107140 displayName : string ;
108141 } & ParentIds ) => editItemDisplayName ( { itemId : variables . itemId , displayName : variables . displayName } ) ,
109142 onSuccess : async ( _data , variables ) => {
110- await queryClient . invalidateQueries ( { queryKey : containerComparisonQueryKeys . course ( courseId ) } ) ;
111- await queryClient . invalidateQueries ( { queryKey : courseOutlineQueryKeys . courseDetails ( courseId ) } ) ;
112- await queryClient . invalidateQueries ( { queryKey : courseOutlineQueryKeys . courseItemId ( variables . itemId ) } ) ;
113143 await invalidateParentQueries ( queryClient , variables ) ;
144+ queryClient . invalidateQueries ( { queryKey : containerComparisonQueryKeys . course ( courseId ) } ) ;
145+ queryClient . invalidateQueries ( { queryKey : courseOutlineQueryKeys . courseDetails ( courseId ) } ) ;
114146 } ,
115147 } ) ;
116148} ;
@@ -122,9 +154,8 @@ export const usePublishCourseItem = () => {
122154 itemId : string ;
123155 } & ParentIds ) => publishCourseItem ( variables . itemId ) ,
124156 onSettled : ( _data , _err , variables ) => {
125- queryClient . invalidateQueries ( { queryKey : courseOutlineQueryKeys . courseItemId ( variables . itemId ) } ) ;
126- queryClient . invalidateQueries ( { queryKey : courseOutlineQueryKeys . courseDetails ( getCourseKey ( variables . itemId ) ) } ) ;
127157 invalidateParentQueries ( queryClient , variables ) . catch ( ( e ) => handleResponseErrors ( e ) ) ;
158+ queryClient . invalidateQueries ( { queryKey : courseOutlineQueryKeys . courseDetails ( getCourseKey ( variables . itemId ) ) } ) ;
128159 } ,
129160 } ) ;
130161} ;
0 commit comments