Skip to content

Commit d12388f

Browse files
committed
feat: implement file picker mode for files page
1 parent 0b7fee0 commit d12388f

8 files changed

Lines changed: 109 additions & 37 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { CourseAuthoringProvider } from '@src/CourseAuthoringContext';
2+
import { useLocation, useParams } from 'react-router-dom';
3+
import FilesPage from './FilesPage';
4+
5+
export const FilePickerPage = () => {
6+
const { courseId } = useParams<{ courseId: string }>();
7+
const location = useLocation();
8+
const params = new URLSearchParams(location.search);
9+
const filePickerOptions = {
10+
usageKey: params.get('usage_key')!,
11+
multiSelect: params.get('multiSelect') === 'true',
12+
mimeType: params.get('mimeType'),
13+
};
14+
15+
return (
16+
<CourseAuthoringProvider courseId={courseId!}>
17+
<FilesPage filePickerMode filePickerOptions={filePickerOptions} />
18+
</CourseAuthoringProvider>
19+
);
20+
};

src/files-and-videos/files-page/FilesPage.jsx renamed to src/files-and-videos/files-page/FilesPage.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useIntl } from '@edx/frontend-platform/i18n';
22

33
import { Container } from '@openedx/paragon';
4+
import { DeprecatedReduxState } from '@src/store';
45
import React, { useEffect } from 'react';
56
import { useDispatch, useSelector } from 'react-redux';
67

@@ -15,11 +16,17 @@ import { AgreementGated } from '@src/constants';
1516

1617
import { EditFileErrors } from '../generic';
1718
import { fetchAssets, resetErrors } from './data/thunks';
18-
import FilesPageProvider from './FilesPageProvider';
19+
import FilesPageProvider, { FilePickerOptions } from './FilesPageProvider';
1920
import messages from './messages';
2021
import './FilesPage.scss';
2122

22-
const FilesPage = () => {
23+
const FilesPage = ({
24+
filePickerMode = false,
25+
filePickerOptions = undefined,
26+
}: {
27+
filePickerMode?: boolean,
28+
filePickerOptions?: FilePickerOptions,
29+
}) => {
2330
const intl = useIntl();
2431
const dispatch = useDispatch();
2532
const { courseId, courseDetails } = useCourseAuthoringContext();
@@ -30,7 +37,7 @@ const FilesPage = () => {
3037
deletingStatus: deleteAssetStatus,
3138
updatingStatus: updateAssetStatus,
3239
errors: errorMessages,
33-
} = useSelector(state => state.assets);
40+
} = useSelector((state:DeprecatedReduxState) => state.assets);
3441

3542
useEffect(() => {
3643
dispatch(fetchAssets(courseId));
@@ -47,7 +54,7 @@ const FilesPage = () => {
4754
}
4855

4956
return (
50-
<FilesPageProvider courseId={courseId}>
57+
<FilesPageProvider filePickerMode={filePickerMode} filePickerOptions={filePickerOptions}>
5158
<Container size="xl" className="p-4 pt-4.5">
5259
<EditFileErrors
5360
resetErrors={handleErrorReset}

src/files-and-videos/files-page/FilesPageProvider.jsx

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import React, { useMemo } from 'react';
2+
3+
export interface FilePickerOptions {
4+
usageKey: string,
5+
multiSelect: boolean,
6+
mimeType: string | null,
7+
}
8+
9+
interface FilesPageContextInterface {
10+
filePickerMode: boolean,
11+
filePickerOptions?: FilePickerOptions,
12+
13+
}
14+
15+
export const FilesPageContext = React.createContext<FilesPageContextInterface>({
16+
filePickerMode: false,
17+
});
18+
19+
interface FilesPageProviderProps extends FilesPageContextInterface {
20+
children: React.ReactNode,
21+
}
22+
23+
const FilesPageProvider = ({
24+
children,
25+
filePickerMode = false,
26+
filePickerOptions,
27+
}: FilesPageProviderProps) => {
28+
const contextValue = useMemo(() => ({
29+
filePickerMode,
30+
filePickerOptions,
31+
}), []);
32+
return (
33+
<FilesPageContext.Provider
34+
value={contextValue}
35+
>
36+
{children}
37+
</FilesPageContext.Provider>
38+
);
39+
};
40+
41+
export default FilesPageProvider;

src/files-and-videos/generic/FileTable.jsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
import { useCallback, useEffect, useState } from 'react';
1+
import { FilesPageContext } from '@src/files-and-videos/files-page/FilesPageProvider';
2+
import {
3+
useCallback, useContext, useEffect, useState,
4+
} from 'react';
25
import { useSelector } from 'react-redux';
36
import PropTypes from 'prop-types';
47
import isEmpty from 'lodash/isEmpty';
@@ -11,7 +14,7 @@ import {
1114
useToggle,
1215
} from '@openedx/paragon';
1316

14-
import { RequestStatus } from '../../data/constants';
17+
import { RequestStatus } from '@src/data/constants';
1518
import { sortFiles } from './utils';
1619
import messages from './messages';
1720

@@ -79,6 +82,7 @@ const FileTable = ({
7982
const defaultCurrentView = (fileType === 'video' && localStorage.getItem('videosCurrentView')) ||
8083
(fileType === 'file' && localStorage.getItem('filesCurrentView')) || defaultView;
8184
const [currentView, setCurrentView] = useState(defaultCurrentView);
85+
const { filePickerMode, filePickerOptions } = useContext(FilesPageContext);
8286

8387
useEffect(() => {
8488
if (!isEmpty(selectedRows) && Object.keys(selectedRows[0]).length > 0) {
@@ -189,6 +193,7 @@ const FileTable = ({
189193
if (!hasMoreInfoColumn) {
190194
tableColumns.push({ ...moreInfoColumn });
191195
}
196+
const maxSelectedRows = filePickerOptions?.multiSelect === false ? 1 : undefined;
192197

193198
return (
194199
<div className="files-table">
@@ -198,6 +203,7 @@ const FileTable = ({
198203
isSortable
199204
isSelectable
200205
isPaginated
206+
maxSelectedRows={maxSelectedRows}
201207
defaultColumnValues={{ Filter: TextFilter }}
202208
dataViewToggleOptions={{
203209
isDataViewToggleEnabled: true,

src/files-and-videos/generic/table-components/TableActions.jsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { getConfig } from '@edx/frontend-platform';
22
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
3-
import { Button, DataTableContext, Dropdown, useToggle, } from '@openedx/paragon';
3+
import {
4+
Button, DataTableContext, Dropdown, useToggle,
5+
} from '@openedx/paragon';
46
import { Add, Tune } from '@openedx/paragon/icons';
7+
import FilesPageProvider, { FilesPageContext } from '@src/files-and-videos/files-page/FilesPageProvider';
58
import { isEmpty } from 'lodash';
69
import { PropTypes } from 'prop-types';
710
import React, { useContext, useEffect } from 'react';
@@ -21,15 +24,15 @@ const TableActions = ({
2124
const intl = useIntl();
2225
const [isSortOpen, openSort, closeSort] = useToggle(false);
2326
const { state, clearSelection } = useContext(DataTableContext);
24-
const filePickerParams = new URLSearchParams(window.location.search);
2527

26-
const showFilePicker = Boolean(filePickerParams.get('filePicker')) && Boolean(window.opener);
28+
const { filePickerMode } = useContext(FilesPageContext);
29+
// If window.opener is not available, show the user some error message.
30+
const showFilePicker = filePickerMode; // && Boolean(window.opener);
2731
// This useEffect saves DataTable state so it can persist after table re-renders due to data reload.
2832
useEffect(() => {
2933
setInitialState(state);
3034
}, [state]);
3135

32-
3336
const handleOpenFileSelector = () => {
3437
fileInputControl.click();
3538
clearSelection();

src/index.jsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import {
77
getConfig,
88
getPath,
99
} from '@edx/frontend-platform';
10-
import { AppProvider, ErrorPage } from '@edx/frontend-platform/react';
10+
import { AppProvider, ErrorPage, PageWrap } from '@edx/frontend-platform/react';
11+
import { FilesPage } from '@src/files-and-videos';
12+
import { FilePickerPage } from '@src/files-and-videos/files-page/FilePickerPage';
1113
import React, { StrictMode, useEffect } from 'react';
1214
import { createRoot } from 'react-dom/client';
1315
import {
@@ -102,6 +104,7 @@ const App = () => {
102104
<Route path="/legacy/preview-changes/:usageKey" element={<PreviewChangesEmbed />} />
103105
<Route path="/course/:courseId/*" element={<CourseAuthoringRoutes />} />
104106
<Route path="/course_rerun/:courseId" element={<CourseRerun />} />
107+
<Route path="/file_picker/:courseId" element={<PageWrap><FilePickerPage /></PageWrap>} />
105108
{getConfig().ENABLE_ACCESSIBILITY_PAGE === 'true' && (
106109
<Route path="/accessibility" element={<AccessibilityPage />} />
107110
)}

src/store.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,24 @@ type InferState<ReducerType> = ReducerType extends Reducer<infer T> ? T : never;
3333
export interface DeprecatedReduxState {
3434
customPages: Record<string, any>;
3535
discussions: Record<string, any>;
36-
assets: Record<string, any>;
36+
assets: {
37+
assetIds: string[];
38+
loadingStatus: RequestStatusType;
39+
duplicateFiles: string[];
40+
updatingStatus: string;
41+
addingStatus: string;
42+
deletingStatus: string;
43+
usageStatus: string;
44+
errors: {
45+
add: string[];
46+
delete: string[];
47+
lock: string[];
48+
download: string[];
49+
usageMetrics: string[];
50+
loading:string;
51+
52+
};
53+
};
3754
pagesAndResources: Record<string, any>;
3855
scheduleAndDetails: Record<string, any>;
3956
studioHome: InferState<typeof studioHomeReducer>;

0 commit comments

Comments
 (0)