From 78eb51283635069b7bae164384e8a725ea0aa3fd Mon Sep 17 00:00:00 2001 From: Kristin Aoki <42981026+KristinAoki@users.noreply.github.com> Date: Wed, 8 Nov 2023 15:54:47 -0500 Subject: [PATCH] refactor: files-and-videos folder (#672) --- ...etSidebar.jsx => FileInfoModalSidebar.jsx} | 8 +-- .../{AssetThumbnail.jsx => FileThumbnail.jsx} | 10 ++-- .../{FilesAndUploads.jsx => FilesPage.jsx} | 45 ++++++++++------ ...AndUploads.test.jsx => FilesPage.test.jsx} | 10 ++-- ...oadsProvider.jsx => FilesPageProvider.jsx} | 12 ++--- .../{ => files-page}/data/api.js | 0 .../{ => files-page}/data/api.test.js | 0 .../{ => files-page}/data/slice.js | 2 +- .../{ => files-page}/data/thunks.js | 4 +- .../{ => files-page}/data/utils.js | 54 +------------------ src/files-and-videos/files-page/index.js | 6 ++- .../{ => generic}/ApiStatusToast.jsx | 0 .../{ => generic}/EditFileErrors.jsx | 2 +- .../{ => generic}/FileInput.jsx | 2 +- .../{ => generic}/FileMenu.jsx | 0 .../{ => generic}/FileTable.jsx | 21 ++++---- .../{FileInfo.jsx => generic/InfoModal.jsx} | 22 +++----- .../ThumbnailPreview.jsx} | 8 +-- .../{ => generic}/UsageMetricsMessage.jsx | 2 +- .../constant.js => generic/constants.js} | 0 src/files-and-videos/generic/index.js | 24 +++++++++ .../{ => generic}/messages.js | 0 .../table-components/FilterStatus.jsx | 0 .../table-components/GalleryCard.jsx | 8 +-- .../table-components/GalleryCard.scss | 0 .../table-components/TableActions.jsx | 0 .../generic/table-components/index.js | 19 +++++++ .../table-custom-columns/AccessColumn.jsx | 0 .../table-custom-columns/ActiveColumn.jsx | 0 .../table-custom-columns/MoreInfoColumn.jsx | 0 .../table-custom-columns/StatusColumn.jsx | 0 .../table-custom-columns/ThumbnailColumn.jsx | 4 +- .../table-custom-columns/index.js | 0 src/files-and-videos/generic/utils.js | 51 ++++++++++++++++++ .../{data => generic}/utils.test.js | 0 src/files-and-videos/index.scss | 2 +- .../table-components/index.js | 7 --- .../videos-page/VideoThumbnail.jsx | 2 +- .../{Videos.jsx => VideosPage.jsx} | 29 +++++----- .../{Videos.test.jsx => VideosPage.test.jsx} | 6 +-- ...eosProvider.jsx => VideosPageProvider.jsx} | 12 ++--- src/files-and-videos/videos-page/index.js | 7 +-- .../videos-page/info-sidebar/InfoTab.jsx | 2 +- .../info-sidebar/TranscriptTab.test.jsx | 6 +-- ...oSidebar.jsx => VideoInfoModalSidebar.jsx} | 8 +-- .../videos-page/info-sidebar/index.js | 3 ++ .../transcript-item/Transcript.jsx | 2 +- .../TranscriptSettings.test.jsx | 2 +- src/store.js | 2 +- 49 files changed, 226 insertions(+), 178 deletions(-) rename src/files-and-videos/files-page/{FileInfoAssetSidebar.jsx => FileInfoModalSidebar.jsx} (95%) rename src/files-and-videos/files-page/{AssetThumbnail.jsx => FileThumbnail.jsx} (89%) rename src/files-and-videos/files-page/{FilesAndUploads.jsx => FilesPage.jsx} (83%) rename src/files-and-videos/files-page/{FilesAndUploads.test.jsx => FilesPage.test.jsx} (99%) rename src/files-and-videos/files-page/{FilesAndUploadsProvider.jsx => FilesPageProvider.jsx} (54%) rename src/files-and-videos/{ => files-page}/data/api.js (100%) rename src/files-and-videos/{ => files-page}/data/api.test.js (100%) rename src/files-and-videos/{ => files-page}/data/slice.js (97%) rename src/files-and-videos/{ => files-page}/data/thunks.js (98%) rename src/files-and-videos/{ => files-page}/data/utils.js (50%) rename src/files-and-videos/{ => generic}/ApiStatusToast.jsx (100%) rename src/files-and-videos/{ => generic}/EditFileErrors.jsx (98%) rename src/files-and-videos/{ => generic}/FileInput.jsx (95%) rename src/files-and-videos/{ => generic}/FileMenu.jsx (100%) rename src/files-and-videos/{ => generic}/FileTable.jsx (95%) rename src/files-and-videos/{FileInfo.jsx => generic/InfoModal.jsx} (80%) rename src/files-and-videos/{FileThumbnail.jsx => generic/ThumbnailPreview.jsx} (86%) rename src/files-and-videos/{ => generic}/UsageMetricsMessage.jsx (97%) rename src/files-and-videos/{data/constant.js => generic/constants.js} (100%) create mode 100644 src/files-and-videos/generic/index.js rename src/files-and-videos/{ => generic}/messages.js (100%) rename src/files-and-videos/{ => generic}/table-components/FilterStatus.jsx (100%) rename src/files-and-videos/{ => generic}/table-components/GalleryCard.jsx (95%) rename src/files-and-videos/{ => generic}/table-components/GalleryCard.scss (100%) rename src/files-and-videos/{ => generic}/table-components/TableActions.jsx (100%) create mode 100644 src/files-and-videos/generic/table-components/index.js rename src/files-and-videos/{ => generic}/table-components/table-custom-columns/AccessColumn.jsx (100%) rename src/files-and-videos/{ => generic}/table-components/table-custom-columns/ActiveColumn.jsx (100%) rename src/files-and-videos/{ => generic}/table-components/table-custom-columns/MoreInfoColumn.jsx (100%) rename src/files-and-videos/{ => generic}/table-components/table-custom-columns/StatusColumn.jsx (100%) rename src/files-and-videos/{ => generic}/table-components/table-custom-columns/ThumbnailColumn.jsx (90%) rename src/files-and-videos/{ => generic}/table-components/table-custom-columns/index.js (100%) create mode 100644 src/files-and-videos/generic/utils.js rename src/files-and-videos/{data => generic}/utils.test.js (100%) delete mode 100644 src/files-and-videos/table-components/index.js rename src/files-and-videos/videos-page/{Videos.jsx => VideosPage.jsx} (91%) rename src/files-and-videos/videos-page/{Videos.test.jsx => VideosPage.test.jsx} (99%) rename src/files-and-videos/videos-page/{VideosProvider.jsx => VideosPageProvider.jsx} (57%) rename src/files-and-videos/videos-page/info-sidebar/{FileInfoVideoSidebar.jsx => VideoInfoModalSidebar.jsx} (87%) create mode 100644 src/files-and-videos/videos-page/info-sidebar/index.js diff --git a/src/files-and-videos/files-page/FileInfoAssetSidebar.jsx b/src/files-and-videos/files-page/FileInfoModalSidebar.jsx similarity index 95% rename from src/files-and-videos/files-page/FileInfoAssetSidebar.jsx rename to src/files-and-videos/files-page/FileInfoModalSidebar.jsx index 5bc69b4f2e..842c6e568c 100644 --- a/src/files-and-videos/files-page/FileInfoAssetSidebar.jsx +++ b/src/files-and-videos/files-page/FileInfoModalSidebar.jsx @@ -18,10 +18,10 @@ import { } from '@edx/paragon'; import { ContentCopy, InfoOutline } from '@edx/paragon/icons'; -import { getFileSizeToClosestByte } from '../data/utils'; +import { getFileSizeToClosestByte } from '../generic/utils'; import messages from './messages'; -const FileInfoAssetSidebar = ({ +const FileInfoModalSidebar = ({ asset, handleLockedAsset, // injected @@ -109,7 +109,7 @@ const FileInfoAssetSidebar = ({ ); }; -FileInfoAssetSidebar.propTypes = { +FileInfoModalSidebar.propTypes = { asset: PropTypes.shape({ displayName: PropTypes.string.isRequired, wrapperType: PropTypes.string.isRequired, @@ -127,4 +127,4 @@ FileInfoAssetSidebar.propTypes = { intl: intlShape.isRequired, }; -export default injectIntl(FileInfoAssetSidebar); +export default injectIntl(FileInfoModalSidebar); diff --git a/src/files-and-videos/files-page/AssetThumbnail.jsx b/src/files-and-videos/files-page/FileThumbnail.jsx similarity index 89% rename from src/files-and-videos/files-page/AssetThumbnail.jsx rename to src/files-and-videos/files-page/FileThumbnail.jsx index c47efacd71..bf96fbda8b 100644 --- a/src/files-and-videos/files-page/AssetThumbnail.jsx +++ b/src/files-and-videos/files-page/FileThumbnail.jsx @@ -5,10 +5,10 @@ import { Icon, Image, } from '@edx/paragon'; -import { getSrc } from '../data/utils'; +import { getSrc } from './data/utils'; import messages from './messages'; -const AssetThumbnail = ({ +const FileThumbnail = ({ thumbnail, wrapperType, externalUrl, @@ -50,13 +50,13 @@ const AssetThumbnail = ({ ); }; -AssetThumbnail.defaultProps = { +FileThumbnail.defaultProps = { thumbnail: null, wrapperType: null, externalUrl: null, displayName: null, }; -AssetThumbnail.propTypes = { +FileThumbnail.propTypes = { thumbnail: PropTypes.string, wrapperType: PropTypes.string, externalUrl: PropTypes.string, @@ -69,4 +69,4 @@ AssetThumbnail.propTypes = { intl: intlShape.isRequired, }; -export default injectIntl(AssetThumbnail); +export default injectIntl(FileThumbnail); diff --git a/src/files-and-videos/files-page/FilesAndUploads.jsx b/src/files-and-videos/files-page/FilesPage.jsx similarity index 83% rename from src/files-and-videos/files-page/FilesAndUploads.jsx rename to src/files-and-videos/files-page/FilesPage.jsx index 40b1226db5..1a9f518ec6 100644 --- a/src/files-and-videos/files-page/FilesAndUploads.jsx +++ b/src/files-and-videos/files-page/FilesPage.jsx @@ -16,19 +16,22 @@ import { getUsagePaths, resetErrors, updateAssetOrder, -} from '../data/thunks'; +} from './data/thunks'; import messages from './messages'; -import FilesAndUploadsProvider from './FilesAndUploadsProvider'; +import FilesPageProvider from './FilesPageProvider'; import getPageHeadTitle from '../../generic/utils'; -import FileTable from '../FileTable'; -import EditFileErrors from '../EditFileErrors'; -import { getFileSizeToClosestByte } from '../data/utils'; -import ThumbnailColumn from '../table-components/table-custom-columns/ThumbnailColumn'; -import ActiveColumn from '../table-components/table-custom-columns/ActiveColumn'; -import AccessColumn from '../table-components/table-custom-columns/AccessColumn'; -import AssetThumbnail from './AssetThumbnail'; +import { + AccessColumn, + ActiveColumn, + EditFileErrors, + FileTable, + ThumbnailColumn, +} from '../generic'; +import { getFileSizeToClosestByte } from '../generic/utils'; +import FileThumbnail from './FileThumbnail'; +import FileInfoModalSidebar from './FileInfoModalSidebar'; -const FilesAndUploads = ({ +const FilesPage = ({ courseId, // injected intl, @@ -52,17 +55,24 @@ const FilesAndUploads = ({ errors: errorMessages, } = useSelector(state => state.assets); + const handleErrorReset = (error) => dispatch(resetErrors(error)); const handleAddFile = (file) => dispatch(addAssetFile(courseId, file, totalCount)); const handleDeleteFile = (id) => dispatch(deleteAssetFile(courseId, id, totalCount)); const handleDownloadFile = (selectedRows) => dispatch(fetchAssetDownload({ selectedRows, courseId })); - const handleLockFile = ({ fileId, locked }) => dispatch(updateAssetLock({ courseId, assetId: fileId, locked })); + const handleLockFile = (fileId, locked) => { + handleErrorReset({ errorType: 'lock' }); + dispatch(updateAssetLock({ courseId, assetId: fileId, locked })); + }; const handleUsagePaths = (asset) => dispatch(getUsagePaths({ asset, courseId })); - const handleErrorReset = (error) => dispatch(resetErrors(error)); const handleFileOrder = ({ newFileIdOrder, sortType }) => { dispatch(updateAssetOrder(courseId, newFileIdOrder, sortType)); }; - const thumbnailPreview = (props) => AssetThumbnail(props); + const thumbnailPreview = (props) => FileThumbnail(props); + const infoModalSidebar = (asset) => FileInfoModalSidebar({ + asset, + handleLockedAsset: handleLockFile, + }); const assets = useModels('assets', assetIds); const data = { @@ -141,7 +151,7 @@ const FilesAndUploads = ({ ); } return ( - +
-
+ ); }; -FilesAndUploads.propTypes = { +FilesPage.propTypes = { courseId: PropTypes.string.isRequired, // injected intl: intlShape.isRequired, }; -export default injectIntl(FilesAndUploads); +export default injectIntl(FilesPage); diff --git a/src/files-and-videos/files-page/FilesAndUploads.test.jsx b/src/files-and-videos/files-page/FilesPage.test.jsx similarity index 99% rename from src/files-and-videos/files-page/FilesAndUploads.test.jsx rename to src/files-and-videos/files-page/FilesPage.test.jsx index 0d898827a3..cd1bc65b62 100644 --- a/src/files-and-videos/files-page/FilesAndUploads.test.jsx +++ b/src/files-and-videos/files-page/FilesPage.test.jsx @@ -19,7 +19,7 @@ import { IntlProvider } from '@edx/frontend-platform/i18n'; import initializeStore from '../../store'; import { executeThunk } from '../../utils'; import { RequestStatus } from '../../data/constants'; -import FilesAndUploads from './FilesAndUploads'; +import FilesPage from './FilesPage'; import { generateFetchAssetApiResponse, generateEmptyApiResponse, @@ -35,9 +35,9 @@ import { deleteAssetFile, updateAssetLock, getUsagePaths, -} from '../data/thunks'; -import { getAssetsUrl } from '../data/api'; -import messages from '../messages'; +} from './data/thunks'; +import { getAssetsUrl } from './data/api'; +import messages from '../generic/messages'; let axiosMock; let store; @@ -49,7 +49,7 @@ const renderComponent = () => { render( - + , ); diff --git a/src/files-and-videos/files-page/FilesAndUploadsProvider.jsx b/src/files-and-videos/files-page/FilesPageProvider.jsx similarity index 54% rename from src/files-and-videos/files-page/FilesAndUploadsProvider.jsx rename to src/files-and-videos/files-page/FilesPageProvider.jsx index fee357aae0..6b10fbfa33 100644 --- a/src/files-and-videos/files-page/FilesAndUploadsProvider.jsx +++ b/src/files-and-videos/files-page/FilesPageProvider.jsx @@ -1,25 +1,25 @@ import React, { useMemo } from 'react'; import PropTypes from 'prop-types'; -export const FilesAndUploadsContext = React.createContext({}); +export const FilesPageContext = React.createContext({}); -const FilesAndUploadsProvider = ({ courseId, children }) => { +const FilesPageProvider = ({ courseId, children }) => { const contextValue = useMemo(() => ({ courseId, path: `/course/${courseId}/assets`, }), []); return ( - {children} - + ); }; -FilesAndUploadsProvider.propTypes = { +FilesPageProvider.propTypes = { courseId: PropTypes.string.isRequired, children: PropTypes.node.isRequired, }; -export default FilesAndUploadsProvider; +export default FilesPageProvider; diff --git a/src/files-and-videos/data/api.js b/src/files-and-videos/files-page/data/api.js similarity index 100% rename from src/files-and-videos/data/api.js rename to src/files-and-videos/files-page/data/api.js diff --git a/src/files-and-videos/data/api.test.js b/src/files-and-videos/files-page/data/api.test.js similarity index 100% rename from src/files-and-videos/data/api.test.js rename to src/files-and-videos/files-page/data/api.test.js diff --git a/src/files-and-videos/data/slice.js b/src/files-and-videos/files-page/data/slice.js similarity index 97% rename from src/files-and-videos/data/slice.js rename to src/files-and-videos/files-page/data/slice.js index a9e8121c60..5a49179532 100644 --- a/src/files-and-videos/data/slice.js +++ b/src/files-and-videos/files-page/data/slice.js @@ -1,7 +1,7 @@ /* eslint-disable no-param-reassign */ import { createSlice } from '@reduxjs/toolkit'; -import { RequestStatus } from '../../data/constants'; +import { RequestStatus } from '../../../data/constants'; const slice = createSlice({ name: 'assets', diff --git a/src/files-and-videos/data/thunks.js b/src/files-and-videos/files-page/data/thunks.js similarity index 98% rename from src/files-and-videos/data/thunks.js rename to src/files-and-videos/files-page/data/thunks.js index 8669fbfe91..f7e1d6df28 100644 --- a/src/files-and-videos/data/thunks.js +++ b/src/files-and-videos/files-page/data/thunks.js @@ -1,11 +1,11 @@ import { isEmpty } from 'lodash'; -import { RequestStatus } from '../../data/constants'; +import { RequestStatus } from '../../../data/constants'; import { addModel, addModels, removeModel, updateModel, -} from '../../generic/model-store'; +} from '../../../generic/model-store'; import { getAssets, getAssetUsagePaths, diff --git a/src/files-and-videos/data/utils.js b/src/files-and-videos/files-page/data/utils.js similarity index 50% rename from src/files-and-videos/data/utils.js rename to src/files-and-videos/files-page/data/utils.js index 9729065df3..71c73057dc 100644 --- a/src/files-and-videos/data/utils.js +++ b/src/files-and-videos/files-page/data/utils.js @@ -4,7 +4,7 @@ import { AudioFile, } from '@edx/paragon/icons'; import { ensureConfig, getConfig } from '@edx/frontend-platform'; -import FILES_AND_UPLOAD_TYPE_FILTERS from './constant'; +import FILES_AND_UPLOAD_TYPE_FILTERS from '../../generic/constants'; ensureConfig([ 'STUDIO_BASE_URL', @@ -54,55 +54,3 @@ export const getSrc = ({ thumbnail, wrapperType, externalUrl }) => { return InsertDriveFile; } }; - -export const getFileSizeToClosestByte = (fileSize, numberOfDivides = 0) => { - if (fileSize > 1000) { - const updatedSize = fileSize / 1000; - const incrementNumberOfDivides = numberOfDivides + 1; - return getFileSizeToClosestByte(updatedSize, incrementNumberOfDivides); - } - const fileSizeFixedDecimal = Number.parseFloat(fileSize).toFixed(2); - switch (numberOfDivides) { - case 1: - return `${fileSizeFixedDecimal} KB`; - case 2: - return `${fileSizeFixedDecimal} MB`; - case 3: - return `${fileSizeFixedDecimal} GB`; - default: - return `${fileSizeFixedDecimal} B`; - } -}; - -export const sortFiles = (files, sortType) => { - const [sort, direction] = sortType.split(','); - let sortedFiles; - if (sort === 'displayName') { - sortedFiles = files.sort((f1, f2) => { - const lowerCaseF1 = f1[sort].toLowerCase(); - const lowerCaseF2 = f2[sort].toLowerCase(); - if (lowerCaseF1 < lowerCaseF2) { - return 1; - } - if (lowerCaseF1 > lowerCaseF2) { - return -1; - } - return 0; - }); - } else { - sortedFiles = files.sort((f1, f2) => { - if (f1[sort] < f2[sort]) { - return 1; - } - if (f1[sort] > f2[sort]) { - return -1; - } - return 0; - }); - } - const sortedIds = sortedFiles.map(file => file.id); - if (direction === 'asc') { - return sortedIds.reverse(); - } - return sortedIds; -}; diff --git a/src/files-and-videos/files-page/index.js b/src/files-and-videos/files-page/index.js index 7bfa2519d8..05939abc4b 100644 --- a/src/files-and-videos/files-page/index.js +++ b/src/files-and-videos/files-page/index.js @@ -1,3 +1,5 @@ -import FilesAndUploads from './FilesAndUploads'; +import FilesPage from './FilesPage'; +import FileInfoModalSidebar from './FileInfoModalSidebar'; -export default FilesAndUploads; +export default FilesPage; +export { FileInfoModalSidebar }; diff --git a/src/files-and-videos/ApiStatusToast.jsx b/src/files-and-videos/generic/ApiStatusToast.jsx similarity index 100% rename from src/files-and-videos/ApiStatusToast.jsx rename to src/files-and-videos/generic/ApiStatusToast.jsx diff --git a/src/files-and-videos/EditFileErrors.jsx b/src/files-and-videos/generic/EditFileErrors.jsx similarity index 98% rename from src/files-and-videos/EditFileErrors.jsx rename to src/files-and-videos/generic/EditFileErrors.jsx index 35304e38d1..d0de5db6da 100644 --- a/src/files-and-videos/EditFileErrors.jsx +++ b/src/files-and-videos/generic/EditFileErrors.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { ErrorAlert } from '@edx/frontend-lib-content-components'; -import { RequestStatus } from '../data/constants'; +import { RequestStatus } from '../../data/constants'; import messages from './messages'; const EditFileErrors = ({ diff --git a/src/files-and-videos/FileInput.jsx b/src/files-and-videos/generic/FileInput.jsx similarity index 95% rename from src/files-and-videos/FileInput.jsx rename to src/files-and-videos/generic/FileInput.jsx index c245e855f3..1de18b052a 100644 --- a/src/files-and-videos/FileInput.jsx +++ b/src/files-and-videos/generic/FileInput.jsx @@ -1,6 +1,6 @@ import React from 'react'; import PropTypes from 'prop-types'; -import { getSupportedFormats } from './videos-page/data/utils'; +import { getSupportedFormats } from '../videos-page/data/utils'; export const useFileInput = ({ onAddFile, diff --git a/src/files-and-videos/FileMenu.jsx b/src/files-and-videos/generic/FileMenu.jsx similarity index 100% rename from src/files-and-videos/FileMenu.jsx rename to src/files-and-videos/generic/FileMenu.jsx diff --git a/src/files-and-videos/FileTable.jsx b/src/files-and-videos/generic/FileTable.jsx similarity index 95% rename from src/files-and-videos/FileTable.jsx rename to src/files-and-videos/generic/FileTable.jsx index 80a805e2c4..3011d0f69a 100644 --- a/src/files-and-videos/FileTable.jsx +++ b/src/files-and-videos/generic/FileTable.jsx @@ -13,11 +13,11 @@ import { Button, } from '@edx/paragon'; -import { RequestStatus } from '../data/constants'; -import { sortFiles } from './data/utils'; +import { RequestStatus } from '../../data/constants'; +import { sortFiles } from './utils'; import messages from './messages'; -import FileInfo from './FileInfo'; +import InfoModal from './InfoModal'; import FileInput, { useFileInput } from './FileInput'; import { GalleryCard, @@ -40,6 +40,7 @@ const FileTable = ({ tableColumns, maxFileSize, thumbnailPreview, + infoModalSidebar, // injected intl, }) => { @@ -112,11 +113,6 @@ const FileTable = ({ handleDownloadFile(selectedFlatRows); }, []); - const handleLockedFile = (fileId, locked) => { - handleErrorReset({ errorType: 'lock' }); - handleLockFile({ fileId, locked }); - }; - const handleOpenDeleteConfirmation = (selectedFlatRows) => { setSelectedRows(selectedFlatRows); openDeleteConfirmation(); @@ -146,7 +142,7 @@ const FileTable = ({ const fileCard = ({ className, original }) => ( MoreInfoColumn({ row, - handleLock: handleLockedFile, + handleLock: handleLockFile, handleBulkDownload, handleOpenFileInfo, handleOpenDeleteConfirmation, @@ -238,14 +234,14 @@ const FileTable = ({ {!isEmpty(selectedRows) && ( - )} (
- {file?.wrapperType === 'video' ? ( - - ) : ( - - )} + {sidebar(file)}
@@ -73,7 +67,7 @@ const FileInfo = ({ ); -FileInfo.propTypes = { +InfoModal.propTypes = { file: PropTypes.shape({ displayName: PropTypes.string.isRequired, wrapperType: PropTypes.string.isRequired, @@ -89,14 +83,14 @@ FileInfo.propTypes = { }), onClose: PropTypes.func.isRequired, isOpen: PropTypes.bool.isRequired, - handleLockedFile: PropTypes.func.isRequired, usagePathStatus: PropTypes.string.isRequired, error: PropTypes.arrayOf(PropTypes.string).isRequired, thumbnailPreview: PropTypes.func.isRequired, + sidebar: PropTypes.func.isRequired, }; -FileInfo.defaultProps = { +InfoModal.defaultProps = { file: null, }; -export default injectIntl(FileInfo); +export default injectIntl(InfoModal); diff --git a/src/files-and-videos/FileThumbnail.jsx b/src/files-and-videos/generic/ThumbnailPreview.jsx similarity index 86% rename from src/files-and-videos/FileThumbnail.jsx rename to src/files-and-videos/generic/ThumbnailPreview.jsx index fd7e2a6c80..57461a2784 100644 --- a/src/files-and-videos/FileThumbnail.jsx +++ b/src/files-and-videos/generic/ThumbnailPreview.jsx @@ -1,7 +1,7 @@ import React from 'react'; import PropTypes from 'prop-types'; -const FileThumbnail = ({ +const ThumbnailPreview = ({ thumbnail, wrapperType, externalUrl, @@ -23,7 +23,7 @@ const FileThumbnail = ({ })} ); -FileThumbnail.defaultProps = { +ThumbnailPreview.defaultProps = { thumbnail: null, wrapperType: null, externalUrl: null, @@ -31,7 +31,7 @@ FileThumbnail.defaultProps = { id: null, status: null, }; -FileThumbnail.propTypes = { +ThumbnailPreview.propTypes = { thumbnail: PropTypes.string, wrapperType: PropTypes.string, externalUrl: PropTypes.string, @@ -45,4 +45,4 @@ FileThumbnail.propTypes = { }).isRequired, }; -export default FileThumbnail; +export default ThumbnailPreview; diff --git a/src/files-and-videos/UsageMetricsMessage.jsx b/src/files-and-videos/generic/UsageMetricsMessage.jsx similarity index 97% rename from src/files-and-videos/UsageMetricsMessage.jsx rename to src/files-and-videos/generic/UsageMetricsMessage.jsx index fe1d5c97d2..84354dcd70 100644 --- a/src/files-and-videos/UsageMetricsMessage.jsx +++ b/src/files-and-videos/generic/UsageMetricsMessage.jsx @@ -3,7 +3,7 @@ import PropTypes from 'prop-types'; import { Icon, Row, Spinner } from '@edx/paragon'; import { ErrorOutline } from '@edx/paragon/icons'; import isEmpty from 'lodash/isEmpty'; -import { RequestStatus } from '../data/constants'; +import { RequestStatus } from '../../data/constants'; import messages from './messages'; const UsageMetricsMessage = ({ diff --git a/src/files-and-videos/data/constant.js b/src/files-and-videos/generic/constants.js similarity index 100% rename from src/files-and-videos/data/constant.js rename to src/files-and-videos/generic/constants.js diff --git a/src/files-and-videos/generic/index.js b/src/files-and-videos/generic/index.js new file mode 100644 index 0000000000..0449cc8705 --- /dev/null +++ b/src/files-and-videos/generic/index.js @@ -0,0 +1,24 @@ +import { + TableActions, + GalleryCard, + AccessColumn, + ActiveColumn, + MoreInfoColumn, + StatusColumn, + ThumbnailColumn, +} from './table-components'; +import FileInput, { useFileInput } from './FileInput'; + +export { + TableActions, + GalleryCard, + AccessColumn, + ActiveColumn, + MoreInfoColumn, + StatusColumn, + ThumbnailColumn, + FileInput, + useFileInput, +}; +export { default as FileTable } from './FileTable'; +export { default as EditFileErrors } from './EditFileErrors'; diff --git a/src/files-and-videos/messages.js b/src/files-and-videos/generic/messages.js similarity index 100% rename from src/files-and-videos/messages.js rename to src/files-and-videos/generic/messages.js diff --git a/src/files-and-videos/table-components/FilterStatus.jsx b/src/files-and-videos/generic/table-components/FilterStatus.jsx similarity index 100% rename from src/files-and-videos/table-components/FilterStatus.jsx rename to src/files-and-videos/generic/table-components/FilterStatus.jsx diff --git a/src/files-and-videos/table-components/GalleryCard.jsx b/src/files-and-videos/generic/table-components/GalleryCard.jsx similarity index 95% rename from src/files-and-videos/table-components/GalleryCard.jsx rename to src/files-and-videos/generic/table-components/GalleryCard.jsx index a5347ee07f..0ad792464a 100644 --- a/src/files-and-videos/table-components/GalleryCard.jsx +++ b/src/files-and-videos/generic/table-components/GalleryCard.jsx @@ -9,20 +9,20 @@ import { } from '@edx/paragon'; import { ClosedCaption } from '@edx/paragon/icons'; import FileMenu from '../FileMenu'; -import FileThumbnail from '../FileThumbnail'; +import FileThumbnail from '../ThumbnailPreview'; const GalleryCard = ({ className, original, handleBulkDownload, - handleLockedFile, + handleLockFile, handleOpenDeleteConfirmation, handleOpenFileInfo, thumbnailPreview, }) => { const lockFile = () => { const { locked, id } = original; - handleLockedFile(id, !locked); + handleLockFile(id, !locked); }; return ( @@ -99,7 +99,7 @@ GalleryCard.propTypes = { downloadLink: PropTypes.string, }).isRequired, handleBulkDownload: PropTypes.func.isRequired, - handleLockedFile: PropTypes.func.isRequired, + handleLockFile: PropTypes.func.isRequired, handleOpenDeleteConfirmation: PropTypes.func.isRequired, handleOpenFileInfo: PropTypes.func.isRequired, thumbnailPreview: PropTypes.func.isRequired, diff --git a/src/files-and-videos/table-components/GalleryCard.scss b/src/files-and-videos/generic/table-components/GalleryCard.scss similarity index 100% rename from src/files-and-videos/table-components/GalleryCard.scss rename to src/files-and-videos/generic/table-components/GalleryCard.scss diff --git a/src/files-and-videos/table-components/TableActions.jsx b/src/files-and-videos/generic/table-components/TableActions.jsx similarity index 100% rename from src/files-and-videos/table-components/TableActions.jsx rename to src/files-and-videos/generic/table-components/TableActions.jsx diff --git a/src/files-and-videos/generic/table-components/index.js b/src/files-and-videos/generic/table-components/index.js new file mode 100644 index 0000000000..960dc072ea --- /dev/null +++ b/src/files-and-videos/generic/table-components/index.js @@ -0,0 +1,19 @@ +import GalleryCard from './GalleryCard'; +import TableActions from './TableActions'; +import { + AccessColumn, + ActiveColumn, + MoreInfoColumn, + StatusColumn, + ThumbnailColumn, +} from './table-custom-columns'; + +export { + TableActions, + GalleryCard, + AccessColumn, + ActiveColumn, + MoreInfoColumn, + StatusColumn, + ThumbnailColumn, +}; diff --git a/src/files-and-videos/table-components/table-custom-columns/AccessColumn.jsx b/src/files-and-videos/generic/table-components/table-custom-columns/AccessColumn.jsx similarity index 100% rename from src/files-and-videos/table-components/table-custom-columns/AccessColumn.jsx rename to src/files-and-videos/generic/table-components/table-custom-columns/AccessColumn.jsx diff --git a/src/files-and-videos/table-components/table-custom-columns/ActiveColumn.jsx b/src/files-and-videos/generic/table-components/table-custom-columns/ActiveColumn.jsx similarity index 100% rename from src/files-and-videos/table-components/table-custom-columns/ActiveColumn.jsx rename to src/files-and-videos/generic/table-components/table-custom-columns/ActiveColumn.jsx diff --git a/src/files-and-videos/table-components/table-custom-columns/MoreInfoColumn.jsx b/src/files-and-videos/generic/table-components/table-custom-columns/MoreInfoColumn.jsx similarity index 100% rename from src/files-and-videos/table-components/table-custom-columns/MoreInfoColumn.jsx rename to src/files-and-videos/generic/table-components/table-custom-columns/MoreInfoColumn.jsx diff --git a/src/files-and-videos/table-components/table-custom-columns/StatusColumn.jsx b/src/files-and-videos/generic/table-components/table-custom-columns/StatusColumn.jsx similarity index 100% rename from src/files-and-videos/table-components/table-custom-columns/StatusColumn.jsx rename to src/files-and-videos/generic/table-components/table-custom-columns/StatusColumn.jsx diff --git a/src/files-and-videos/table-components/table-custom-columns/ThumbnailColumn.jsx b/src/files-and-videos/generic/table-components/table-custom-columns/ThumbnailColumn.jsx similarity index 90% rename from src/files-and-videos/table-components/table-custom-columns/ThumbnailColumn.jsx rename to src/files-and-videos/generic/table-components/table-custom-columns/ThumbnailColumn.jsx index 1df2315b16..ea0248ba72 100644 --- a/src/files-and-videos/table-components/table-custom-columns/ThumbnailColumn.jsx +++ b/src/files-and-videos/generic/table-components/table-custom-columns/ThumbnailColumn.jsx @@ -1,6 +1,6 @@ import React from 'react'; import { PropTypes } from 'prop-types'; -import FileThumbnail from '../../FileThumbnail'; +import ThumbnailPreview from '../../ThumbnailPreview'; const ThumbnailColumn = ({ row, thumbnailPreview }) => { const { @@ -12,7 +12,7 @@ const ThumbnailColumn = ({ row, thumbnailPreview }) => { status, } = row.original; return ( - { + if (fileSize > 1000) { + const updatedSize = fileSize / 1000; + const incrementNumberOfDivides = numberOfDivides + 1; + return getFileSizeToClosestByte(updatedSize, incrementNumberOfDivides); + } + const fileSizeFixedDecimal = Number.parseFloat(fileSize).toFixed(2); + switch (numberOfDivides) { + case 1: + return `${fileSizeFixedDecimal} KB`; + case 2: + return `${fileSizeFixedDecimal} MB`; + case 3: + return `${fileSizeFixedDecimal} GB`; + default: + return `${fileSizeFixedDecimal} B`; + } +}; + +export const sortFiles = (files, sortType) => { + const [sort, direction] = sortType.split(','); + let sortedFiles; + if (sort === 'displayName') { + sortedFiles = files.sort((f1, f2) => { + const lowerCaseF1 = f1[sort].toLowerCase(); + const lowerCaseF2 = f2[sort].toLowerCase(); + if (lowerCaseF1 < lowerCaseF2) { + return 1; + } + if (lowerCaseF1 > lowerCaseF2) { + return -1; + } + return 0; + }); + } else { + sortedFiles = files.sort((f1, f2) => { + if (f1[sort] < f2[sort]) { + return 1; + } + if (f1[sort] > f2[sort]) { + return -1; + } + return 0; + }); + } + const sortedIds = sortedFiles.map(file => file.id); + if (direction === 'asc') { + return sortedIds.reverse(); + } + return sortedIds; +}; diff --git a/src/files-and-videos/data/utils.test.js b/src/files-and-videos/generic/utils.test.js similarity index 100% rename from src/files-and-videos/data/utils.test.js rename to src/files-and-videos/generic/utils.test.js diff --git a/src/files-and-videos/index.scss b/src/files-and-videos/index.scss index 087984e13c..98679e50d2 100644 --- a/src/files-and-videos/index.scss +++ b/src/files-and-videos/index.scss @@ -1,3 +1,3 @@ @import "files-and-videos/videos-page/transcript-settings/TranscriptSettings"; @import "files-and-videos/videos-page/VideoThumbnail"; -@import "files-and-videos/table-components/GalleryCard" +@import "files-and-videos/generic/table-components/GalleryCard" diff --git a/src/files-and-videos/table-components/index.js b/src/files-and-videos/table-components/index.js deleted file mode 100644 index cafee08288..0000000000 --- a/src/files-and-videos/table-components/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import GalleryCard from './GalleryCard'; -import TableActions from './TableActions'; - -export { - TableActions, - GalleryCard, -}; diff --git a/src/files-and-videos/videos-page/VideoThumbnail.jsx b/src/files-and-videos/videos-page/VideoThumbnail.jsx index 0fcb1fb102..d9378fa187 100644 --- a/src/files-and-videos/videos-page/VideoThumbnail.jsx +++ b/src/files-and-videos/videos-page/VideoThumbnail.jsx @@ -8,7 +8,7 @@ import { Icon, Image, } from '@edx/paragon'; -import FileInput, { useFileInput } from '../FileInput'; +import { FileInput, useFileInput } from '../generic'; import messages from './messages'; const VideoThumbnail = ({ diff --git a/src/files-and-videos/videos-page/Videos.jsx b/src/files-and-videos/videos-page/VideosPage.jsx similarity index 91% rename from src/files-and-videos/videos-page/Videos.jsx rename to src/files-and-videos/videos-page/VideosPage.jsx index 68b4b5002b..0706c6aa2d 100644 --- a/src/files-and-videos/videos-page/Videos.jsx +++ b/src/files-and-videos/videos-page/VideosPage.jsx @@ -26,19 +26,22 @@ import { updateVideoOrder, } from './data/thunks'; import messages from './messages'; -import VideosProvider from './VideosProvider'; +import VideosPageProvider from './VideosPageProvider'; import getPageHeadTitle from '../../generic/utils'; -import FileTable from '../FileTable'; -import EditFileErrors from '../EditFileErrors'; -import ThumbnailColumn from '../table-components/table-custom-columns/ThumbnailColumn'; -import ActiveColumn from '../table-components/table-custom-columns/ActiveColumn'; -import StatusColumn from '../table-components/table-custom-columns/StatusColumn'; +import { + ActiveColumn, + EditFileErrors, + FileTable, + StatusColumn, + ThumbnailColumn, +} from '../generic'; import TranscriptSettings from './transcript-settings'; import VideoThumbnail from './VideoThumbnail'; import { getFormattedDuration, resampleFile } from './data/utils'; -import FILES_AND_UPLOAD_TYPE_FILTERS from '../data/constant'; +import FILES_AND_UPLOAD_TYPE_FILTERS from '../generic/constants'; +import VideoInfoModalSidebar from './info-sidebar'; -const Videos = ({ +const VideosPage = ({ courseId, // injected intl, @@ -103,6 +106,7 @@ const Videos = ({ usageErrorMessages: errorMessages.usageMetrics, }; const thumbnailPreview = (props) => VideoThumbnail({ ...props, handleAddThumbnail, videoImageSettings }); + const infoModalSidebar = (video) => VideoInfoModalSidebar({ video }); const maxFileSize = videoUploadMaxFileSize * 1073741824; const transcriptColumn = { id: 'transcripts', @@ -156,7 +160,7 @@ const Videos = ({ ); } return ( - +
-
+ ); }; -Videos.propTypes = { +VideosPage.propTypes = { courseId: PropTypes.string.isRequired, // injected intl: intlShape.isRequired, }; -export default injectIntl(Videos); +export default injectIntl(VideosPage); diff --git a/src/files-and-videos/videos-page/Videos.test.jsx b/src/files-and-videos/videos-page/VideosPage.test.jsx similarity index 99% rename from src/files-and-videos/videos-page/Videos.test.jsx rename to src/files-and-videos/videos-page/VideosPage.test.jsx index 24bd308145..41ca144e41 100644 --- a/src/files-and-videos/videos-page/Videos.test.jsx +++ b/src/files-and-videos/videos-page/VideosPage.test.jsx @@ -17,7 +17,7 @@ import { IntlProvider } from '@edx/frontend-platform/i18n'; import initializeStore from '../../store'; import { executeThunk } from '../../utils'; import { RequestStatus } from '../../data/constants'; -import Videos from './Videos'; +import VideosPage from './VideosPage'; import { generateFetchVideosApiResponse, generateEmptyApiResponse, @@ -38,7 +38,7 @@ import { } from './data/thunks'; import { getVideosUrl, getCoursVideosApiUrl, getApiBaseUrl } from './data/api'; import videoMessages from './messages'; -import messages from '../messages'; +import messages from '../generic/messages'; let axiosMock; let store; @@ -49,7 +49,7 @@ const renderComponent = () => { render( - + , ); diff --git a/src/files-and-videos/videos-page/VideosProvider.jsx b/src/files-and-videos/videos-page/VideosPageProvider.jsx similarity index 57% rename from src/files-and-videos/videos-page/VideosProvider.jsx rename to src/files-and-videos/videos-page/VideosPageProvider.jsx index d196168093..cbce832565 100644 --- a/src/files-and-videos/videos-page/VideosProvider.jsx +++ b/src/files-and-videos/videos-page/VideosPageProvider.jsx @@ -1,25 +1,25 @@ import React, { useMemo } from 'react'; import PropTypes from 'prop-types'; -export const VideosContext = React.createContext({}); +export const VideosPageContext = React.createContext({}); -const VideosProvider = ({ courseId, children }) => { +const VideosPageProvider = ({ courseId, children }) => { const contextValue = useMemo(() => ({ courseId, path: `/course/${courseId}/videos`, }), []); return ( - {children} - + ); }; -VideosProvider.propTypes = { +VideosPageProvider.propTypes = { courseId: PropTypes.string.isRequired, children: PropTypes.node.isRequired, }; -export default VideosProvider; +export default VideosPageProvider; diff --git a/src/files-and-videos/videos-page/index.js b/src/files-and-videos/videos-page/index.js index 70fd52042b..01caa2ac1c 100644 --- a/src/files-and-videos/videos-page/index.js +++ b/src/files-and-videos/videos-page/index.js @@ -1,6 +1,7 @@ import TranscriptSettings from './transcript-settings'; -import Videos from './Videos'; +import VideosPage from './VideosPage'; import VideoThumbnail from './VideoThumbnail'; +import VideoInfoModalSidebar from './info-sidebar'; -export default Videos; -export { TranscriptSettings, VideoThumbnail }; +export default VideosPage; +export { TranscriptSettings, VideoThumbnail, VideoInfoModalSidebar }; diff --git a/src/files-and-videos/videos-page/info-sidebar/InfoTab.jsx b/src/files-and-videos/videos-page/info-sidebar/InfoTab.jsx index 1662dab266..cdc5ab2bd6 100644 --- a/src/files-and-videos/videos-page/info-sidebar/InfoTab.jsx +++ b/src/files-and-videos/videos-page/info-sidebar/InfoTab.jsx @@ -2,7 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import { Stack } from '@edx/paragon'; import { injectIntl, FormattedDate, FormattedMessage } from '@edx/frontend-platform/i18n'; -import { getFileSizeToClosestByte } from '../../data/utils'; +import { getFileSizeToClosestByte } from '../../generic/utils'; import { getFormattedDuration } from '../data/utils'; import messages from './messages'; diff --git a/src/files-and-videos/videos-page/info-sidebar/TranscriptTab.test.jsx b/src/files-and-videos/videos-page/info-sidebar/TranscriptTab.test.jsx index 94e9cf7037..f02d0c710a 100644 --- a/src/files-and-videos/videos-page/info-sidebar/TranscriptTab.test.jsx +++ b/src/files-and-videos/videos-page/info-sidebar/TranscriptTab.test.jsx @@ -28,7 +28,7 @@ import { import { getApiBaseUrl } from '../data/api'; import messages from './messages'; import transcriptRowMessages from './transcript-item/messages'; -import VideosProvider from '../VideosProvider'; +import VideosPageProvider from '../VideosPageProvider'; import { deleteVideoTranscript } from '../data/thunks'; ReactDOM.createPortal = jest.fn(node => node); @@ -56,9 +56,9 @@ const renderComponent = (props) => { render( - + - + , ); diff --git a/src/files-and-videos/videos-page/info-sidebar/FileInfoVideoSidebar.jsx b/src/files-and-videos/videos-page/info-sidebar/VideoInfoModalSidebar.jsx similarity index 87% rename from src/files-and-videos/videos-page/info-sidebar/FileInfoVideoSidebar.jsx rename to src/files-and-videos/videos-page/info-sidebar/VideoInfoModalSidebar.jsx index 09ae0b66fb..2c240fb006 100644 --- a/src/files-and-videos/videos-page/info-sidebar/FileInfoVideoSidebar.jsx +++ b/src/files-and-videos/videos-page/info-sidebar/VideoInfoModalSidebar.jsx @@ -9,7 +9,7 @@ import InfoTab from './InfoTab'; import TranscriptTab from './TranscriptTab'; import messages from './messages'; -const FileInfoVideoSidebar = ({ +const VideoInfoModalSidebar = ({ video, // injected intl, @@ -30,7 +30,7 @@ const FileInfoVideoSidebar = ({ ); -FileInfoVideoSidebar.propTypes = { +VideoInfoModalSidebar.propTypes = { video: PropTypes.shape({ displayName: PropTypes.string.isRequired, wrapperType: PropTypes.string.isRequired, @@ -43,8 +43,8 @@ FileInfoVideoSidebar.propTypes = { intl: intlShape.isRequired, }; -FileInfoVideoSidebar.defaultProps = { +VideoInfoModalSidebar.defaultProps = { video: null, }; -export default injectIntl(FileInfoVideoSidebar); +export default injectIntl(VideoInfoModalSidebar); diff --git a/src/files-and-videos/videos-page/info-sidebar/index.js b/src/files-and-videos/videos-page/info-sidebar/index.js new file mode 100644 index 0000000000..78ef7160fe --- /dev/null +++ b/src/files-and-videos/videos-page/info-sidebar/index.js @@ -0,0 +1,3 @@ +import VideoInfoModalSidebar from './VideoInfoModalSidebar'; + +export default VideoInfoModalSidebar; diff --git a/src/files-and-videos/videos-page/info-sidebar/transcript-item/Transcript.jsx b/src/files-and-videos/videos-page/info-sidebar/transcript-item/Transcript.jsx index c0e194bc1e..abceab2773 100644 --- a/src/files-and-videos/videos-page/info-sidebar/transcript-item/Transcript.jsx +++ b/src/files-and-videos/videos-page/info-sidebar/transcript-item/Transcript.jsx @@ -13,7 +13,7 @@ import { isEmpty } from 'lodash'; import LanguageSelect from './LanguageSelect'; import TranscriptMenu from './TranscriptMenu'; import messages from './messages'; -import FileInput, { useFileInput } from '../../../FileInput'; +import { FileInput, useFileInput } from '../../../generic'; const Transcript = ({ languages, diff --git a/src/files-and-videos/videos-page/transcript-settings/TranscriptSettings.test.jsx b/src/files-and-videos/videos-page/transcript-settings/TranscriptSettings.test.jsx index dad356c7eb..7f6939cdf0 100644 --- a/src/files-and-videos/videos-page/transcript-settings/TranscriptSettings.test.jsx +++ b/src/files-and-videos/videos-page/transcript-settings/TranscriptSettings.test.jsx @@ -21,7 +21,7 @@ import { } from '../factories/mockApiResponses'; import { getApiBaseUrl } from '../data/api'; import messages from './messages'; -import VideosProvider from '../VideosProvider'; +import VideosProvider from '../VideosPageProvider'; const defaultProps = { isTranscriptSettingsOpen: true, diff --git a/src/store.js b/src/store.js index 69dddbfd32..bbdacb2548 100644 --- a/src/store.js +++ b/src/store.js @@ -10,7 +10,7 @@ import { reducer as gradingSettingsReducer } from './grading-settings/data/slice import { reducer as studioHomeReducer } from './studio-home/data/slice'; import { reducer as scheduleAndDetailsReducer } from './schedule-and-details/data/slice'; import { reducer as liveReducer } from './pages-and-resources/live/data/slice'; -import { reducer as filesReducer } from './files-and-videos/data/slice'; +import { reducer as filesReducer } from './files-and-videos/files-page/data/slice'; import { reducer as courseTeamReducer } from './course-team/data/slice'; import { reducer as CourseUpdatesReducer } from './course-updates/data/slice'; import { reducer as processingNotificationReducer } from './generic/processing-notification/data/slice';