Skip to content

Commit

Permalink
refactor: simplify Library Context
Browse files Browse the repository at this point in the history
  • Loading branch information
bradenmacdonald committed Sep 27, 2024
1 parent c80483c commit 2f8717e
Show file tree
Hide file tree
Showing 13 changed files with 89 additions and 82 deletions.
4 changes: 2 additions & 2 deletions src/library-authoring/EmptyStates.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { useParams } from 'react-router';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import type { MessageDescriptor } from 'react-intl';
import {
Expand All @@ -8,6 +7,7 @@ import { Add } from '@openedx/paragon/icons';
import { ClearFiltersButton } from '../search-manager';
import messages from './messages';
import { useContentLibrary } from './data/apiHooks';
import { useLibraryContext } from './common/context';

export const NoComponents = ({
infoText = messages.noComponents,
Expand All @@ -18,7 +18,7 @@ export const NoComponents = ({
addBtnText?: MessageDescriptor;
handleBtnClick: () => void;
}) => {
const { libraryId } = useParams();
const { libraryId } = useLibraryContext();
const { data: libraryData } = useContentLibrary(libraryId);
const canEditLibrary = libraryData?.canEditLibrary ?? false;

Expand Down
24 changes: 8 additions & 16 deletions src/library-authoring/LibraryAuthoringPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useContext, useEffect } from 'react';
import React, { useEffect } from 'react';
import { Helmet } from 'react-helmet';
import classNames from 'classnames';
import { StudioFooter } from '@edx/frontend-component-footer';
Expand All @@ -13,7 +13,7 @@ import {
} from '@openedx/paragon';
import { Add, InfoOutline } from '@openedx/paragon/icons';
import {
Routes, Route, useLocation, useNavigate, useParams, useSearchParams,
Routes, Route, useLocation, useNavigate, useSearchParams,
} from 'react-router-dom';

import Loading from '../generic/Loading';
Expand All @@ -33,7 +33,7 @@ import LibraryCollections from './collections/LibraryCollections';
import LibraryHome from './LibraryHome';
import { useContentLibrary } from './data/apiHooks';
import { LibrarySidebar } from './library-sidebar';
import { LibraryContext, SidebarBodyComponentId } from './common/context';
import { SidebarBodyComponentId, useLibraryContext } from './common/context';
import messages from './messages';

enum TabList {
Expand All @@ -53,7 +53,7 @@ const HeaderActions = ({ canEditLibrary }: HeaderActionsProps) => {
openInfoSidebar,
closeLibrarySidebar,
sidebarBodyComponent,
} = useContext(LibraryContext);
} = useLibraryContext();

if (!canEditLibrary) {
return null;
Expand Down Expand Up @@ -119,19 +119,15 @@ const LibraryAuthoringPage = () => {
const location = useLocation();
const navigate = useNavigate();

const { libraryId } = useParams();
if (!libraryId) {
// istanbul ignore next - This shouldn't be possible; it's just here to satisfy the type checker.
throw new Error('Rendered without libraryId URL parameter');
}
const { libraryId } = useLibraryContext();
const { data: libraryData, isLoading } = useContentLibrary(libraryId);

const currentPath = location.pathname.split('/').pop();
const activeKey = (currentPath && currentPath in TabList) ? TabList[currentPath] : TabList.home;
const {
sidebarBodyComponent,
openInfoSidebar,
} = useContext(LibraryContext);
} = useLibraryContext();

useEffect(() => {
openInfoSidebar();
Expand Down Expand Up @@ -196,16 +192,12 @@ const LibraryAuthoringPage = () => {
<Route
path={TabList.home}
element={(
<LibraryHome
libraryId={libraryId}
tabList={TabList}
handleTabChange={handleTabChange}
/>
<LibraryHome tabList={TabList} handleTabChange={handleTabChange} />
)}
/>
<Route
path={TabList.components}
element={<LibraryComponents libraryId={libraryId} variant="full" />}
element={<LibraryComponents variant="full" />}
/>
<Route
path={TabList.collections}
Expand Down
12 changes: 5 additions & 7 deletions src/library-authoring/LibraryHome.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import React, { useContext } from 'react';
import { Stack } from '@openedx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';

Expand All @@ -9,22 +8,21 @@ import { LibraryComponents } from './components';
import LibrarySection from './components/LibrarySection';
import LibraryRecentlyModified from './LibraryRecentlyModified';
import messages from './messages';
import { LibraryContext } from './common/context';
import { useLibraryContext } from './common/context';

type LibraryHomeProps = {
libraryId: string,
tabList: { home: string, components: string, collections: string },
handleTabChange: (key: string) => void,
};

const LibraryHome = ({ libraryId, tabList, handleTabChange } : LibraryHomeProps) => {
const LibraryHome = ({ tabList, handleTabChange } : LibraryHomeProps) => {
const intl = useIntl();
const {
totalHits: componentCount,
totalCollectionHits: collectionCount,
isFiltered,
} = useSearchContext();
const { openAddContentSidebar } = useContext(LibraryContext);
const { openAddContentSidebar } = useLibraryContext();

const renderEmptyState = () => {
if (componentCount === 0 && collectionCount === 0) {
Expand All @@ -35,7 +33,7 @@ const LibraryHome = ({ libraryId, tabList, handleTabChange } : LibraryHomeProps)

return (
<Stack gap={3}>
<LibraryRecentlyModified libraryId={libraryId} />
<LibraryRecentlyModified />
{
renderEmptyState()
|| (
Expand All @@ -52,7 +50,7 @@ const LibraryHome = ({ libraryId, tabList, handleTabChange } : LibraryHomeProps)
contentCount={componentCount}
viewAllAction={() => handleTabChange(tabList.components)}
>
<LibraryComponents libraryId={libraryId} variant="preview" />
<LibraryComponents variant="preview" />
</LibrarySection>
</>
)
Expand Down
23 changes: 14 additions & 9 deletions src/library-authoring/LibraryRecentlyModified.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,17 @@ import messages from './messages';
import ComponentCard from './components/ComponentCard';
import { useLibraryBlockTypes } from './data/apiHooks';
import CollectionCard from './components/CollectionCard';
import { useLibraryContext } from './common/context';

const RecentlyModified: React.FC<{ libraryId: string }> = ({ libraryId }) => {
const RecentlyModified: React.FC<Record<never, never>> = () => {
const intl = useIntl();
const {
hits,
collectionHits,
totalHits,
totalCollectionHits,
} = useSearchContext();
const { libraryId } = useLibraryContext();

const componentCount = totalHits + totalCollectionHits;
// Since we only display a fixed number of items in preview,
Expand Down Expand Up @@ -77,13 +79,16 @@ const RecentlyModified: React.FC<{ libraryId: string }> = ({ libraryId }) => {
: null;
};

const LibraryRecentlyModified: React.FC<{ libraryId: string }> = ({ libraryId }) => (
<SearchContextProvider
extraFilter={`context_key = "${libraryId}"`}
overrideSearchSortOrder={SearchSortOption.RECENTLY_MODIFIED}
>
<RecentlyModified libraryId={libraryId} />
</SearchContextProvider>
);
const LibraryRecentlyModified: React.FC<Record<never, never>> = () => {
const { libraryId } = useLibraryContext();
return (
<SearchContextProvider
extraFilter={`context_key = "${libraryId}"`}
overrideSearchSortOrder={SearchSortOption.RECENTLY_MODIFIED}
>
<RecentlyModified />
</SearchContextProvider>
);
};

export default LibraryRecentlyModified;
4 changes: 2 additions & 2 deletions src/library-authoring/add-content/AddContentContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ import { useCopyToClipboard } from '../../generic/clipboard';
import { getCanEdit } from '../../course-unit/data/selectors';
import { useCreateLibraryBlock, useLibraryPasteClipboard, useUpdateCollectionComponents } from '../data/apiHooks';
import { getEditUrl } from '../components/utils';
import { useLibraryContext } from '../common/context';

import messages from './messages';
import { LibraryContext } from '../common/context';

type ContentType = {
name: string,
Expand Down Expand Up @@ -73,7 +73,7 @@ const AddContentContainer = () => {
const { showPasteXBlock } = useCopyToClipboard(canEdit);
const {
openCreateCollectionModal,
} = React.useContext(LibraryContext);
} = useLibraryContext();

const collectionButtonData = {
name: intl.formatMessage(messages.collectionButton),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { useContext } from 'react';
import { Stack } from '@openedx/paragon';
import { NoComponents, NoSearchResults } from '../EmptyStates';
import { useSearchContext } from '../../search-manager';
import { LibraryComponents } from '../components';
import messages from './messages';
import { LibraryContext } from '../common/context';
import { useLibraryContext } from '../common/context';

const LibraryCollectionComponents = ({ libraryId }: { libraryId: string }) => {
const LibraryCollectionComponents = () => {
const { totalHits: componentCount, isFiltered } = useSearchContext();
const { openAddContentSidebar } = useContext(LibraryContext);
const { openAddContentSidebar } = useLibraryContext();

if (componentCount === 0) {
return isFiltered
Expand All @@ -25,7 +24,7 @@ const LibraryCollectionComponents = ({ libraryId }: { libraryId: string }) => {
return (
<Stack direction="vertical" gap={3}>
<h3 className="text-gray">Content ({componentCount})</h3>
<LibraryComponents libraryId={libraryId} variant="full" />
<LibraryComponents variant="full" />
</Stack>
);
};
Expand Down
10 changes: 5 additions & 5 deletions src/library-authoring/collections/LibraryCollectionPage.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useContext, useEffect } from 'react';
import { useEffect } from 'react';
import { StudioFooter } from '@edx/frontend-component-footer';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
Expand Down Expand Up @@ -28,7 +28,7 @@ import {
useSearchContext,
} from '../../search-manager';
import { useContentLibrary } from '../data/apiHooks';
import { LibraryContext } from '../common/context';
import { useLibraryContext } from '../common/context';
import messages from './messages';
import { LibrarySidebar } from '../library-sidebar';
import LibraryCollectionComponents from './LibraryCollectionComponents';
Expand All @@ -37,7 +37,7 @@ const HeaderActions = ({ canEditLibrary }: { canEditLibrary: boolean; }) => {
const intl = useIntl();
const {
openAddContentSidebar,
} = useContext(LibraryContext);
} = useLibraryContext();

if (!canEditLibrary) {
return null;
Expand Down Expand Up @@ -98,7 +98,7 @@ const LibraryCollectionPageInner = ({ libraryId }: { libraryId: string }) => {
const {
sidebarBodyComponent,
openCollectionInfoSidebar,
} = useContext(LibraryContext);
} = useLibraryContext();
const { collectionHits: [collectionData], isFetching } = useSearchContext();

useEffect(() => {
Expand Down Expand Up @@ -169,7 +169,7 @@ const LibraryCollectionPageInner = ({ libraryId }: { libraryId: string }) => {
<div className="flex-grow-1" />
<SearchSortWidget />
</div>
<LibraryCollectionComponents libraryId={libraryId} />
<LibraryCollectionComponents />
</Container>
<StudioFooter />
</div>
Expand Down
6 changes: 2 additions & 4 deletions src/library-authoring/collections/LibraryCollections.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import { useContext } from 'react';

import { useLoadOnScroll } from '../../hooks';
import { useSearchContext } from '../../search-manager';
import { NoComponents, NoSearchResults } from '../EmptyStates';
import CollectionCard from '../components/CollectionCard';
import { LIBRARY_SECTION_PREVIEW_LIMIT } from '../components/LibrarySection';
import messages from './messages';
import { LibraryContext } from '../common/context';
import { useLibraryContext } from '../common/context';

type LibraryCollectionsProps = {
variant: 'full' | 'preview',
Expand All @@ -29,7 +27,7 @@ const LibraryCollections = ({ variant }: LibraryCollectionsProps) => {
isFiltered,
} = useSearchContext();

const { openCreateCollectionModal } = useContext(LibraryContext);
const { openCreateCollectionModal } = useLibraryContext();

const collectionList = variant === 'preview' ? collectionHits.slice(0, LIBRARY_SECTION_PREVIEW_LIMIT) : collectionHits;

Expand Down
45 changes: 32 additions & 13 deletions src/library-authoring/common/context.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useToggle } from '@openedx/paragon';
import React from 'react';
import { useParams } from 'react-router-dom';

export enum SidebarBodyComponentId {
AddContent = 'add-content',
Expand All @@ -9,34 +10,42 @@ export enum SidebarBodyComponentId {
}

export interface LibraryContextData {
/** The ID of the current library */
libraryId: string;
// Sidebar stuff - only one sidebar is active at any given time:
sidebarBodyComponent: SidebarBodyComponentId | null;
closeLibrarySidebar: () => void;
openAddContentSidebar: () => void;
openInfoSidebar: () => void;
openComponentInfoSidebar: (usageKey: string) => void;
openCollectionInfoSidebar: () => void;
currentComponentUsageKey?: string;
// "Create New Collection" modal
isCreateCollectionModalOpen: boolean;
openCreateCollectionModal: () => void;
closeCreateCollectionModal: () => void;
openCollectionInfoSidebar: () => void;
}

export const LibraryContext = React.createContext({
sidebarBodyComponent: null,
closeLibrarySidebar: () => {},
openAddContentSidebar: () => {},
openInfoSidebar: () => {},
openComponentInfoSidebar: (_usageKey: string) => {}, // eslint-disable-line @typescript-eslint/no-unused-vars
isCreateCollectionModalOpen: false,
openCreateCollectionModal: () => {},
closeCreateCollectionModal: () => {},
openCollectionInfoSidebar: () => {},
} as LibraryContextData);
/**
* 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);

/**
* React component to provide `LibraryContext`
*/
export const LibraryProvider = (props: { children?: React.ReactNode }) => {
const { libraryId } = useParams();

if (libraryId === undefined) {
// istanbul ignore next - This shouldn't be possible; it's just here to satisfy the type checker.
throw new Error('Error: route is missing libraryId.');
}
const [sidebarBodyComponent, setSidebarBodyComponent] = React.useState<SidebarBodyComponentId | null>(null);
const [currentComponentUsageKey, setCurrentComponentUsageKey] = React.useState<string>();
const [isCreateCollectionModalOpen, openCreateCollectionModal, closeCreateCollectionModal] = useToggle(false);
Expand Down Expand Up @@ -65,7 +74,8 @@ export const LibraryProvider = (props: { children?: React.ReactNode }) => {
setSidebarBodyComponent(SidebarBodyComponentId.CollectionInfo);
}, []);

const context = React.useMemo(() => ({
const context = React.useMemo<LibraryContextData>(() => ({
libraryId,
sidebarBodyComponent,
closeLibrarySidebar,
openAddContentSidebar,
Expand All @@ -77,6 +87,7 @@ export const LibraryProvider = (props: { children?: React.ReactNode }) => {
closeCreateCollectionModal,
openCollectionInfoSidebar,
}), [
libraryId,
sidebarBodyComponent,
closeLibrarySidebar,
openAddContentSidebar,
Expand All @@ -95,3 +106,11 @@ export const LibraryProvider = (props: { children?: React.ReactNode }) => {
</LibraryContext.Provider>
);
};

export function useLibraryContext(): LibraryContextData {
const ctx = React.useContext(LibraryContext);
if (ctx === undefined) {
throw new Error('useLibraryContext() was used in a component without a <LibraryProvider> ancestor.');
}
return ctx;
}
Loading

0 comments on commit 2f8717e

Please sign in to comment.