Files
frontend-app-authoring/src/library-authoring/common/context.tsx
Chris Chávez 21cbf80f23 feat: Show published components on content picker (#1420)
* feat: Show published components on content picker

---------

Co-authored-by: Braden MacDonald <braden@opencraft.com>
2024-10-22 13:47:07 -05:00

214 lines
6.6 KiB
TypeScript

import { useToggle } from '@openedx/paragon';
import React, {
useCallback,
useContext,
useMemo,
useState,
} from 'react';
import type { ContentLibrary } from '../data/api';
import { useContentLibrary } from '../data/apiHooks';
export enum SidebarBodyComponentId {
AddContent = 'add-content',
Info = 'info',
ComponentInfo = 'component-info',
CollectionInfo = 'collection-info',
}
export enum SidebarAdditionalActions {
JumpToAddCollections = 'jump-to-add-collections',
}
export interface SidebarComponentInfo {
type: SidebarBodyComponentId;
id: string;
/** Additional action on Sidebar display */
additionalAction?: SidebarAdditionalActions;
}
export interface LibraryContextData {
/** The ID of the current library */
libraryId: string;
libraryData?: ContentLibrary;
readOnly: boolean;
isLoadingLibraryData: boolean;
collectionId: string | undefined;
setCollectionId: (collectionId?: string) => void;
// Whether we're in "component picker" mode
componentPickerMode: boolean;
// Only show published components
showOnlyPublished: boolean;
// Sidebar stuff - only one sidebar is active at any given time:
closeLibrarySidebar: () => void;
openAddContentSidebar: () => void;
openInfoSidebar: () => void;
openComponentInfoSidebar: (usageKey: string, additionalAction?: SidebarAdditionalActions) => void;
sidebarComponentInfo?: SidebarComponentInfo;
// "Library Team" modal
isLibraryTeamModalOpen: boolean;
openLibraryTeamModal: () => void;
closeLibraryTeamModal: () => void;
// "Create New Collection" modal
isCreateCollectionModalOpen: boolean;
openCreateCollectionModal: () => void;
closeCreateCollectionModal: () => void;
// Current collection
openCollectionInfoSidebar: (collectionId: string, additionalAction?: SidebarAdditionalActions) => void;
// Editor modal - for editing some component
/** If the editor is open and the user is editing some component, this is its usageKey */
componentBeingEdited: string | undefined;
openComponentEditor: (usageKey: string) => void;
closeComponentEditor: () => void;
resetSidebarAdditionalActions: () => void;
}
/**
* Library Context.
* Always available when we're in the context of a single library.
*
* Get this using `useLibraryContext()`
*
* Not used on the "library list" on Studio home.
*/
const LibraryContext = React.createContext<LibraryContextData | undefined>(undefined);
interface LibraryProviderProps {
children?: React.ReactNode;
libraryId: string;
/** The initial collection ID to show */
collectionId?: string;
/** The component picker mode is a special mode where the user is selecting a component to add to a Unit (or another
* XBlock) */
componentPickerMode?: boolean;
showOnlyPublished?: boolean;
/** Only used for testing */
initialSidebarComponentInfo?: SidebarComponentInfo;
}
/**
* React component to provide `LibraryContext`
*/
export const LibraryProvider = ({
children,
libraryId,
collectionId: collectionIdProp,
componentPickerMode = false,
showOnlyPublished = false,
initialSidebarComponentInfo,
}: LibraryProviderProps) => {
const [collectionId, setCollectionId] = useState(collectionIdProp);
const [sidebarComponentInfo, setSidebarComponentInfo] = useState<SidebarComponentInfo | undefined>(
initialSidebarComponentInfo,
);
const [isLibraryTeamModalOpen, openLibraryTeamModal, closeLibraryTeamModal] = useToggle(false);
const [isCreateCollectionModalOpen, openCreateCollectionModal, closeCreateCollectionModal] = useToggle(false);
const [componentBeingEdited, openComponentEditor] = useState<string | undefined>();
const closeComponentEditor = useCallback(() => openComponentEditor(undefined), []);
/** Helper function to consume addtional action once performed.
Required to redo the action.
*/
const resetSidebarAdditionalActions = useCallback(() => {
setSidebarComponentInfo((prev) => (prev && { ...prev, additionalAction: undefined }));
}, []);
const closeLibrarySidebar = useCallback(() => {
setSidebarComponentInfo(undefined);
}, []);
const openAddContentSidebar = useCallback(() => {
setSidebarComponentInfo({ id: '', type: SidebarBodyComponentId.AddContent });
}, []);
const openInfoSidebar = useCallback(() => {
setSidebarComponentInfo({ id: '', type: SidebarBodyComponentId.Info });
}, []);
const openComponentInfoSidebar = useCallback((usageKey: string, additionalAction?: SidebarAdditionalActions) => {
setSidebarComponentInfo({
id: usageKey,
type: SidebarBodyComponentId.ComponentInfo,
additionalAction,
});
}, []);
const openCollectionInfoSidebar = useCallback((
newCollectionId: string,
additionalAction?: SidebarAdditionalActions,
) => {
setSidebarComponentInfo({
id: newCollectionId,
type: SidebarBodyComponentId.CollectionInfo,
additionalAction,
});
}, []);
const { data: libraryData, isLoading: isLoadingLibraryData } = useContentLibrary(libraryId);
const readOnly = componentPickerMode || !libraryData?.canEditLibrary;
const context = useMemo<LibraryContextData>(() => ({
libraryId,
libraryData,
collectionId,
setCollectionId,
readOnly,
isLoadingLibraryData,
componentPickerMode,
showOnlyPublished,
closeLibrarySidebar,
openAddContentSidebar,
openInfoSidebar,
openComponentInfoSidebar,
sidebarComponentInfo,
isLibraryTeamModalOpen,
openLibraryTeamModal,
closeLibraryTeamModal,
isCreateCollectionModalOpen,
openCreateCollectionModal,
closeCreateCollectionModal,
openCollectionInfoSidebar,
componentBeingEdited,
openComponentEditor,
closeComponentEditor,
resetSidebarAdditionalActions,
}), [
libraryId,
collectionId,
setCollectionId,
libraryData,
readOnly,
isLoadingLibraryData,
componentPickerMode,
showOnlyPublished,
closeLibrarySidebar,
openAddContentSidebar,
openInfoSidebar,
openComponentInfoSidebar,
sidebarComponentInfo,
isLibraryTeamModalOpen,
openLibraryTeamModal,
closeLibraryTeamModal,
isCreateCollectionModalOpen,
openCreateCollectionModal,
closeCreateCollectionModal,
openCollectionInfoSidebar,
componentBeingEdited,
openComponentEditor,
closeComponentEditor,
resetSidebarAdditionalActions,
]);
return (
<LibraryContext.Provider value={context}>
{children}
</LibraryContext.Provider>
);
};
export function useLibraryContext(): LibraryContextData {
const ctx = useContext(LibraryContext);
if (ctx === undefined) {
/* istanbul ignore next */
throw new Error('useLibraryContext() was used in a component without a <LibraryProvider> ancestor.');
}
return ctx;
}