From 09e235f841b2c9788821426a5282c0887ece901e Mon Sep 17 00:00:00 2001 From: jiehao Date: Wed, 3 Mar 2021 17:55:36 +0800 Subject: [PATCH 01/18] refactor: Images layout into functional component --- src/layouts/Images.jsx | 220 ++++++++++++++++++++--------------------- 1 file changed, 105 insertions(+), 115 deletions(-) diff --git a/src/layouts/Images.jsx b/src/layouts/Images.jsx index f1d91d061..f123174bc 100644 --- a/src/layouts/Images.jsx +++ b/src/layouts/Images.jsx @@ -1,4 +1,4 @@ -import React, { Component } from 'react'; +import React, { Component, useEffect, useState } from 'react'; import axios from 'axios'; import PropTypes from 'prop-types'; import Header from '../components/Header'; @@ -10,53 +10,48 @@ import MediaUploadCard from '../components/media/MediaUploadCard'; import MediaCard from '../components/media/MediaCard'; import MediaSettingsModal from '../components/media/MediaSettingsModal'; -export default class Images extends Component { - _isMounted = false +const Images = ({ match: { params: { siteName } }, location }) => { + const [images, setImages] = useState([]) + const [pendingImageUpload, setPendingImageUpload] = useState(null) + const [chosenImage, setChosenImage] = useState('') - constructor(props) { - super(props); - this.state = { - images: [], - chosenImage: null, - pendingImageUpload: null, - }; - } - - async componentDidMount() { - this._isMounted = true - try { - const { match } = this.props; - const { siteName } = match.params; - const resp = await axios.get(`${process.env.REACT_APP_BACKEND_URL}/sites/${siteName}/images`, { - withCredentials: true, - }); - const { images } = resp.data; - if (this._isMounted) this.setState({ images }); - } catch (err) { - console.log(err); + useEffect(() => { + let _isMounted = true + const fetchData = async () => { + try { + const resp = await axios.get(`${process.env.REACT_APP_BACKEND_URL}/sites/${siteName}/images`, { + withCredentials: true, + }); + const { images } = resp.data; + if (_isMounted) setImages(images); + } catch (err) { + console.log(err); + } } - } - componentWillUnmount() { - this._isMounted = false; - } + fetchData() + + return () => { + _isMounted = false + } + }, []) - uploadImage = async (imageName, imageContent) => { + const uploadImage = async (imageName, imageContent) => { try { // toggle state so that image renaming modal appears - this.setState({ + setPendingImageUpload({ pendingImageUpload: { fileName: imageName, path: `images%2F${imageName}`, content: imageContent, }, - }); + }) } catch (err) { console.log(err); } } - onImageSelect = async (event) => { + const onImageSelect = async (event) => { const imgReader = new FileReader(); const imgName = event.target.files[0].name; imgReader.onload = (() => { @@ -67,101 +62,96 @@ export default class Images extends Component { const imgData = imgReader.result.split(',')[1]; - this.uploadImage(imgName, imgData); + uploadImage(imgName, imgData); }); imgReader.readAsDataURL(event.target.files[0]); } - render() { - const { images, chosenImage, pendingImageUpload } = this.state; - const { match, location } = this.props; - const { siteName } = match.params; - return ( - <> -
- {/* main bottom section */} -
- - {/* main section starts here */} -
-
-

Images

-
- {/* Info segment */} -
- - Note: Upload images here to link to them in pages and resources. The maximum image size allowed is 5MB. -
-
-
-
- {/* Upload Image */} - +
+ {/* main bottom section */} +
+ + {/* main section starts here */} +
+
+

Images

+
+ {/* Info segment */} +
+ + Note: Upload images here to link to them in pages and resources. The maximum image size allowed is 5MB. +
+
+
+
+ {/* Upload Image */} + document.getElementById('file-upload').click()} + /> + { + // eslint-disable-next-line no-param-reassign + event.target.value = ''; + }} + type="file" + id="file-upload" + accept="image/*" + hidden + /> + {/* Images */} + {images.length > 0 && images.map((image) => ( + document.getElementById('file-upload').click()} - /> - { - // eslint-disable-next-line no-param-reassign - event.target.value = ''; - }} - type="file" - id="file-upload" - accept="image/*" - hidden + media={image} + siteName={siteName} + onClick={() => setChosenImage(image)} + key={image.fileName} /> - {/* Images */} - {images.length > 0 && images.map((image) => ( - this.setState({ chosenImage: image })} - key={image.fileName} - /> - ))} -
+ ))}
- {/* End of image cards */}
- {/* main section ends here */} + {/* End of image cards */}
- { - chosenImage - && ( - this.setState({ chosenImage: null })} - onSave={() => window.location.reload()} - /> - ) - } - { - pendingImageUpload - && ( - this.setState({ pendingImageUpload: null })} - onSave={() => window.location.reload()} - /> - ) - } - - ); - } + {/* main section ends here */} +
+ { + chosenImage + && ( + setChosenImage(null)} + onSave={() => window.location.reload()} + /> + ) + } + { + pendingImageUpload + && ( + setPendingImageUpload(null)} + onSave={() => window.location.reload()} + /> + ) + } + + ); } +export default Images + Images.propTypes = { match: PropTypes.shape({ params: PropTypes.shape({ From c7888e98c01e43e5cae2fe8e100ac33f78ee84c2 Mon Sep 17 00:00:00 2001 From: jiehao Date: Mon, 8 Mar 2021 11:04:17 +0800 Subject: [PATCH 02/18] feat: update Images layout to use new API endpoint --- src/layouts/Images.jsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/layouts/Images.jsx b/src/layouts/Images.jsx index f123174bc..10717d2ad 100644 --- a/src/layouts/Images.jsx +++ b/src/layouts/Images.jsx @@ -12,6 +12,7 @@ import MediaSettingsModal from '../components/media/MediaSettingsModal'; const Images = ({ match: { params: { siteName } }, location }) => { const [images, setImages] = useState([]) + const [directories, setDirectories] = useState([]) const [pendingImageUpload, setPendingImageUpload] = useState(null) const [chosenImage, setChosenImage] = useState('') @@ -19,11 +20,23 @@ const Images = ({ match: { params: { siteName } }, location }) => { let _isMounted = true const fetchData = async () => { try { - const resp = await axios.get(`${process.env.REACT_APP_BACKEND_URL}/sites/${siteName}/images`, { + const resp = await axios.get(`${process.env.REACT_APP_BACKEND_URL}/sites/${siteName}/files/images`, { withCredentials: true, }); - const { images } = resp.data; - if (_isMounted) setImages(images); + const { directoryContents } = resp.data; + + let respImages = [] + let respDirectories = [] + directoryContents.forEach((fileOrDir) => { + const modifiedFileOrDir = { ...fileOrDir, fileName: fileOrDir.name } + if (fileOrDir.type === 'file') respImages.push(modifiedFileOrDir) + if (fileOrDir.type === 'dir') respDirectories.push(modifiedFileOrDir) + }) + + if (_isMounted) { + setImages(respImages); + setDirectories(respDirectories) + } } catch (err) { console.log(err); } From ec991fe446c629478e3def9c75e71b21953aeb1a Mon Sep 17 00:00:00 2001 From: jiehao Date: Mon, 8 Mar 2021 15:11:06 +0800 Subject: [PATCH 03/18] feat: update FolderCard component to accommodate nested media This commit updates the FolderCard component to accept a `linkPath` prop so that we can pass in a custom URL for the FolderCard component to link to. This is necessary for images and files which are nested in at least one layer of directories. --- src/components/FolderCard.jsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/FolderCard.jsx b/src/components/FolderCard.jsx index be0cdd862..a533ac704 100644 --- a/src/components/FolderCard.jsx +++ b/src/components/FolderCard.jsx @@ -48,6 +48,8 @@ const FolderCard = ({ return `/sites/${siteName}/contact-us` case 'nav': return `/sites/${siteName}/navbar` + case 'media': + return `/sites/${siteName}/${linkPath}` default: return '' } @@ -170,6 +172,8 @@ FolderCard.propTypes = { itemIndex: PropTypes.number, pageType: PropTypes.string.isRequired, siteName: PropTypes.string.isRequired, + category: PropTypes.string, + linkPath: PropTypes.string, }; export default FolderCard; \ No newline at end of file From d0322337a7f874e54203fd90ae72510f4b71f85b Mon Sep 17 00:00:00 2001 From: jiehao Date: Mon, 8 Mar 2021 15:12:58 +0800 Subject: [PATCH 04/18] feat: update Images layout to display nested directories --- src/layouts/Images.jsx | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/layouts/Images.jsx b/src/layouts/Images.jsx index 10717d2ad..d785c511e 100644 --- a/src/layouts/Images.jsx +++ b/src/layouts/Images.jsx @@ -1,15 +1,20 @@ -import React, { Component, useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import axios from 'axios'; import PropTypes from 'prop-types'; + import Header from '../components/Header'; import Sidebar from '../components/Sidebar'; -import elementStyles from '../styles/isomer-cms/Elements.module.scss'; -import contentStyles from '../styles/isomer-cms/pages/Content.module.scss'; -import mediaStyles from '../styles/isomer-cms/pages/Media.module.scss'; +import FolderCard from '../components/FolderCard' import MediaUploadCard from '../components/media/MediaUploadCard'; import MediaCard from '../components/media/MediaCard'; import MediaSettingsModal from '../components/media/MediaSettingsModal'; +import elementStyles from '../styles/isomer-cms/Elements.module.scss'; +import contentStyles from '../styles/isomer-cms/pages/Content.module.scss'; +import mediaStyles from '../styles/isomer-cms/pages/Media.module.scss'; + +import { deslugifyDirectory } from '../utils'; + const Images = ({ match: { params: { siteName } }, location }) => { const [images, setImages] = useState([]) const [directories, setDirectories] = useState([]) @@ -91,6 +96,34 @@ const Images = ({ match: { params: { siteName } }, location }) => {

Images

+ {/* Directories segment */} +
+ Folders +
+ {/* Image folders */} +
+
+ { + directories && directories.length > 0 + ? directories.map((directory, idx) => ( + {}} + key={directory.name} + pageType={"media"} + linkPath={directory.path} + siteName={siteName} + itemIndex={idx} + /> + )) + : null + } +
+
+ {/* Segment divider */} +
+
+
{/* Info segment */}
From 9f40eb8cf97be6e1587afad53748a4a083e36b94 Mon Sep 17 00:00:00 2001 From: jiehao Date: Mon, 8 Mar 2021 15:52:58 +0800 Subject: [PATCH 05/18] feat: modify Images layout to render nested image folders This commit modifies the frontend routing to create a /sites/:siteName/images/:customPath path, which will allow users to access nested image folders on the Images layout. The Images layout has also been modified to allow navigation to the parent image folder using the Header component's back button. --- src/App.jsx | 2 +- src/layouts/Images.jsx | 48 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 56812c177..4ac066a07 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -150,7 +150,7 @@ function App() { - + diff --git a/src/layouts/Images.jsx b/src/layouts/Images.jsx index d785c511e..f55df8d1a 100644 --- a/src/layouts/Images.jsx +++ b/src/layouts/Images.jsx @@ -15,7 +15,38 @@ import mediaStyles from '../styles/isomer-cms/pages/Media.module.scss'; import { deslugifyDirectory } from '../utils'; -const Images = ({ match: { params: { siteName } }, location }) => { +const getPrevDirectoryPath = (customPath) => { + const customPathArr = customPath.split('/') + + let prevDirectoryPath + if (customPathArr.length > 1) { + prevDirectoryPath = customPathArr + .slice(0, -1) // remove the latest directory + .join('/') + .slice(1) // remove starting `/` + } + else { + prevDirectoryPath = 'images' + } + + return prevDirectoryPath +} + +const getPrevDirectoryName = (customPath) => { + const customPathArr = customPath.split('/') + + let prevDirectoryName + if (customPathArr.length > 1) { + prevDirectoryName = customPathArr[customPathArr.length - 2] + } + else { + prevDirectoryName = 'Images' + } + + return deslugifyDirectory(prevDirectoryName) +} + +const Images = ({ match: { params: { siteName, customPath } }, location }) => { const [images, setImages] = useState([]) const [directories, setDirectories] = useState([]) const [pendingImageUpload, setPendingImageUpload] = useState(null) @@ -25,7 +56,7 @@ const Images = ({ match: { params: { siteName } }, location }) => { let _isMounted = true const fetchData = async () => { try { - const resp = await axios.get(`${process.env.REACT_APP_BACKEND_URL}/sites/${siteName}/files/images`, { + const resp = await axios.get(`${process.env.REACT_APP_BACKEND_URL}/sites/${siteName}/files/${customPath ? encodeURIComponent(`images/${customPath}`) : 'images'}`, { withCredentials: true, }); const { directoryContents } = resp.data; @@ -52,7 +83,7 @@ const Images = ({ match: { params: { siteName } }, location }) => { return () => { _isMounted = false } - }, []) + }, [customPath]) const uploadImage = async (imageName, imageContent) => { try { @@ -87,7 +118,10 @@ const Images = ({ match: { params: { siteName } }, location }) => { return ( <> -
+
{/* main bottom section */}
@@ -116,7 +150,11 @@ const Images = ({ match: { params: { siteName } }, location }) => { itemIndex={idx} /> )) - : null + : ( +
+ There are no image sub-directories in this directory. +
+ ) }
From 645418d37a5a5f288275f37f41c89404fa668334 Mon Sep 17 00:00:00 2001 From: jiehao Date: Mon, 8 Mar 2021 16:44:57 +0800 Subject: [PATCH 06/18] refactor: Images layout to use react-query --- src/api.js | 19 ++++++++++++++ src/layouts/Images.jsx | 58 +++++++++++++++++++++++++----------------- 2 files changed, 53 insertions(+), 24 deletions(-) diff --git a/src/api.js b/src/api.js index 8440bd499..477797fb8 100644 --- a/src/api.js +++ b/src/api.js @@ -239,6 +239,24 @@ const moveFile = async ({selectedFile, siteName, resourceName, folderName, subfo return await axios.post(apiEndpoint, params) } +const getImages = async (siteName, customPath) => { + const resp = await axios.get(`${process.env.REACT_APP_BACKEND_URL}/sites/${siteName}/files/${customPath ? encodeURIComponent(`images/${customPath}`) : 'images'}`); + const { directoryContents } = resp.data; + + let respImages = [] + let respDirectories = [] + directoryContents.forEach((fileOrDir) => { + const modifiedFileOrDir = { ...fileOrDir, fileName: fileOrDir.name } + if (fileOrDir.type === 'file') respImages.push(modifiedFileOrDir) + if (fileOrDir.type === 'dir') respDirectories.push(modifiedFileOrDir) + }) + + return { + respImages, + respDirectories, + } +} + export { getDirectoryFile, setDirectoryFile, @@ -262,4 +280,5 @@ export { getAllCategories, moveFiles, moveFile, + getImages, } \ No newline at end of file diff --git a/src/layouts/Images.jsx b/src/layouts/Images.jsx index f55df8d1a..c304df938 100644 --- a/src/layouts/Images.jsx +++ b/src/layouts/Images.jsx @@ -1,6 +1,8 @@ import React, { useEffect, useState } from 'react'; -import axios from 'axios'; +import _ from 'lodash'; import PropTypes from 'prop-types'; +import { useQuery } from 'react-query'; +import { ReactQueryDevtools } from 'react-query/devtools'; import Header from '../components/Header'; import Sidebar from '../components/Sidebar'; @@ -9,12 +11,17 @@ import MediaUploadCard from '../components/media/MediaUploadCard'; import MediaCard from '../components/media/MediaCard'; import MediaSettingsModal from '../components/media/MediaSettingsModal'; +import { getImages } from '../api'; + import elementStyles from '../styles/isomer-cms/Elements.module.scss'; import contentStyles from '../styles/isomer-cms/pages/Content.module.scss'; import mediaStyles from '../styles/isomer-cms/pages/Media.module.scss'; import { deslugifyDirectory } from '../utils'; +// Constants +const IMAGE_CONTENTS_KEY = 'image-contents' + const getPrevDirectoryPath = (customPath) => { const customPathArr = customPath.split('/') @@ -52,37 +59,37 @@ const Images = ({ match: { params: { siteName, customPath } }, location }) => { const [pendingImageUpload, setPendingImageUpload] = useState(null) const [chosenImage, setChosenImage] = useState('') + const { data: imageData, refetch } = useQuery( + IMAGE_CONTENTS_KEY, + () => getImages(siteName, customPath), + { + retry: false, + // TO-DO: error-handling + }, + ) + useEffect(() => { let _isMounted = true - const fetchData = async () => { - try { - const resp = await axios.get(`${process.env.REACT_APP_BACKEND_URL}/sites/${siteName}/files/${customPath ? encodeURIComponent(`images/${customPath}`) : 'images'}`, { - withCredentials: true, - }); - const { directoryContents } = resp.data; - - let respImages = [] - let respDirectories = [] - directoryContents.forEach((fileOrDir) => { - const modifiedFileOrDir = { ...fileOrDir, fileName: fileOrDir.name } - if (fileOrDir.type === 'file') respImages.push(modifiedFileOrDir) - if (fileOrDir.type === 'dir') respDirectories.push(modifiedFileOrDir) - }) - - if (_isMounted) { - setImages(respImages); - setDirectories(respDirectories) - } - } catch (err) { - console.log(err); + + if (imageData) { + const { + respImages, + respDirectories, + } = imageData + + if (_isMounted) { + setImages(respImages) + setDirectories(respDirectories) } } - fetchData() - return () => { _isMounted = false } + }, [imageData]) + + useEffect(() => { + refetch() }, [customPath]) const uploadImage = async (imageName, imageContent) => { @@ -230,6 +237,9 @@ const Images = ({ match: { params: { siteName, customPath } }, location }) => { /> ) } + { + process.env.REACT_APP_ENV === 'LOCAL_DEV' && + } ); } From f8670cd68c842f60c9f0742a08da0c5e2837847f Mon Sep 17 00:00:00 2001 From: jiehao Date: Mon, 8 Mar 2021 17:01:39 +0800 Subject: [PATCH 07/18] fix: Image routing for folders nested beyond 2nd level The frontend paths that we use for Images are: - /sites/:siteName/images - /sites/:siteName/images/:customPath The second path, /sites/:siteName/images/:customPath, is responsible for handling all other nested levels. For example, if the repo path to the nested image folder is /images/features/2013, then the CMS URL would be /sites/:siteName/images/features/2013. The problem with this is that this URL does not actually match the allocated URL, since there are `/` characters in the route param. Therefore, we need to modify the `customPath` variables to be encoded URI components so that the accessed URLs match the frontend route. --- src/layouts/Images.jsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/layouts/Images.jsx b/src/layouts/Images.jsx index c304df938..2cdd0ca5d 100644 --- a/src/layouts/Images.jsx +++ b/src/layouts/Images.jsx @@ -61,7 +61,7 @@ const Images = ({ match: { params: { siteName, customPath } }, location }) => { const { data: imageData, refetch } = useQuery( IMAGE_CONTENTS_KEY, - () => getImages(siteName, customPath), + () => getImages(siteName, customPath ? decodeURIComponent(customPath): ''), { retry: false, // TO-DO: error-handling @@ -152,7 +152,12 @@ const Images = ({ match: { params: { siteName, customPath } }, location }) => { settingsToggle={() => {}} key={directory.name} pageType={"media"} - linkPath={directory.path} + linkPath={`images/${encodeURIComponent(directory.path + .split('/') + .slice(1) // remove `images` prefix + .join('/') + )}` + } siteName={siteName} itemIndex={idx} /> From ff9ef379f74d322045add70d668f0a284d3ec8ee Mon Sep 17 00:00:00 2001 From: jiehao Date: Tue, 9 Mar 2021 19:26:09 +0800 Subject: [PATCH 08/18] style: update contentStyles.segment span font size --- src/styles/isomer-cms/pages/Content.module.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/isomer-cms/pages/Content.module.scss b/src/styles/isomer-cms/pages/Content.module.scss index dccf55c54..1f91b8a3c 100644 --- a/src/styles/isomer-cms/pages/Content.module.scss +++ b/src/styles/isomer-cms/pages/Content.module.scss @@ -50,7 +50,7 @@ margin-bottom: 2.5rem; span { - font-size: 16px; + font-size: 18px; } strong { From 01557d9d76b3d70a06de70b76a681001bce0b407 Mon Sep 17 00:00:00 2001 From: jiehao Date: Tue, 9 Mar 2021 19:47:00 +0800 Subject: [PATCH 09/18] feat: update Images layout to match figma specifications Refer to figma design here: https://www.figma.com/file/4qNhGTBMs521pDvCAIh4ON/IsomerCMS?node-id=1025%3A8585 --- src/layouts/Images.jsx | 60 ++++++++++++------- src/styles/isomer-cms/elements/variable.scss | 2 +- src/styles/isomer-cms/pages/Media.module.scss | 8 +++ 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/layouts/Images.jsx b/src/layouts/Images.jsx index 2cdd0ca5d..fefdf8368 100644 --- a/src/layouts/Images.jsx +++ b/src/layouts/Images.jsx @@ -137,11 +137,41 @@ const Images = ({ match: { params: { siteName, customPath } }, location }) => {

Images

- {/* Directories segment */} + {/* Info segment */}
- Folders + + Note: Upload images here to link to them in pages and resources. The maximum image size allowed is 5MB.
- {/* Image folders */} + {/* Creation buttons */} +
+
+ {/* Upload Image */} + {/* document.getElementById('file-upload').click()} + /> + { + // eslint-disable-next-line no-param-reassign + event.target.value = ''; + }} + type="file" + id="file-upload" + accept="image/*" + hidden + /> */} +
+
+ {/* Segment divider */} +
+
+
+ {/* Directories title segment */} +
+ Directories +
+ {/* Image directories */}
{ @@ -172,32 +202,16 @@ const Images = ({ match: { params: { siteName, customPath } }, location }) => {
{/* Segment divider */}
-
+
- {/* Info segment */} + {/* Ungrouped Images title segment */}
- - Note: Upload images here to link to them in pages and resources. The maximum image size allowed is 5MB. + Ungrouped Images
+ {/* Images segment */}
- {/* Upload Image */} - document.getElementById('file-upload').click()} - /> - { - // eslint-disable-next-line no-param-reassign - event.target.value = ''; - }} - type="file" - id="file-upload" - accept="image/*" - hidden - /> {/* Images */} {images.length > 0 && images.map((image) => ( Date: Tue, 9 Mar 2021 22:39:46 +0800 Subject: [PATCH 10/18] feat: add buttons for image upload and directory creation This commit adds the FolderOptionButton components for image upload and directory creation. This commit also fixes a bug where the pending image upload state was not correctly set when refactoring the Image layout from a class component to a functional component. --- src/layouts/Images.jsx | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/layouts/Images.jsx b/src/layouts/Images.jsx index fefdf8368..76c979619 100644 --- a/src/layouts/Images.jsx +++ b/src/layouts/Images.jsx @@ -7,7 +7,7 @@ import { ReactQueryDevtools } from 'react-query/devtools'; import Header from '../components/Header'; import Sidebar from '../components/Sidebar'; import FolderCard from '../components/FolderCard' -import MediaUploadCard from '../components/media/MediaUploadCard'; +import FolderOptionButton from '../components/folders/FolderOptionButton' import MediaCard from '../components/media/MediaCard'; import MediaSettingsModal from '../components/media/MediaSettingsModal'; @@ -96,11 +96,9 @@ const Images = ({ match: { params: { siteName, customPath } }, location }) => { try { // toggle state so that image renaming modal appears setPendingImageUpload({ - pendingImageUpload: { - fileName: imageName, - path: `images%2F${imageName}`, - content: imageContent, - }, + fileName: imageName, + path: `images%2F${imageName}`, + content: imageContent, }) } catch (err) { console.log(err); @@ -146,10 +144,17 @@ const Images = ({ match: { params: { siteName, customPath } }, location }) => {
{/* Upload Image */} - {/* document.getElementById('file-upload').click()} /> + console.log('placeholder')} + /> { @@ -160,7 +165,7 @@ const Images = ({ match: { params: { siteName, customPath } }, location }) => { id="file-upload" accept="image/*" hidden - /> */} + />
{/* Segment divider */} From baba6493a80c8f12f69a7c07ac72aa80f61538cf Mon Sep 17 00:00:00 2001 From: jiehao Date: Tue, 9 Mar 2021 22:57:52 +0800 Subject: [PATCH 11/18] feat: add error-handling --- src/layouts/Images.jsx | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/src/layouts/Images.jsx b/src/layouts/Images.jsx index 76c979619..5163309d2 100644 --- a/src/layouts/Images.jsx +++ b/src/layouts/Images.jsx @@ -13,12 +13,15 @@ import MediaSettingsModal from '../components/media/MediaSettingsModal'; import { getImages } from '../api'; +import useRedirectHook from '../hooks/useRedirectHook'; + +import { deslugifyDirectory } from '../utils'; +import { errorToast } from '../utils/toasts'; + import elementStyles from '../styles/isomer-cms/Elements.module.scss'; import contentStyles from '../styles/isomer-cms/pages/Content.module.scss'; import mediaStyles from '../styles/isomer-cms/pages/Media.module.scss'; -import { deslugifyDirectory } from '../utils'; - // Constants const IMAGE_CONTENTS_KEY = 'image-contents' @@ -58,13 +61,20 @@ const Images = ({ match: { params: { siteName, customPath } }, location }) => { const [directories, setDirectories] = useState([]) const [pendingImageUpload, setPendingImageUpload] = useState(null) const [chosenImage, setChosenImage] = useState('') + const { setRedirectToNotFound } = useRedirectHook() const { data: imageData, refetch } = useQuery( IMAGE_CONTENTS_KEY, () => getImages(siteName, customPath ? decodeURIComponent(customPath): ''), { retry: false, - // TO-DO: error-handling + onError: (err) => { + if (err.response && err.response.status === 404) { + setRedirectToNotFound(siteName) + } else { + errorToast() + } + } }, ) @@ -218,15 +228,22 @@ const Images = ({ match: { params: { siteName, customPath } }, location }) => {
{/* Images */} - {images.length > 0 && images.map((image) => ( - setChosenImage(image)} - key={image.fileName} - /> - ))} + { + images && images.length > 0 + ? images.map((image) => ( + setChosenImage(image)} + key={image.fileName} + /> + )) : ( +
+ There are no images in this directory. +
+ ) + }
From 6c24ed7eb543f7defc9574c2d04da75c813b91a7 Mon Sep 17 00:00:00 2001 From: kwajiehao Date: Tue, 30 Mar 2021 18:17:07 +0800 Subject: [PATCH 12/18] fix: rebase error --- src/components/FolderCard.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/FolderCard.jsx b/src/components/FolderCard.jsx index a533ac704..df342dbc9 100644 --- a/src/components/FolderCard.jsx +++ b/src/components/FolderCard.jsx @@ -25,6 +25,7 @@ const FolderCard = ({ siteName, category, selectedIndex, + linkPath, onClick, }) => { const [isFolderModalOpen, setIsFolderModalOpen] = useState(false) From 92331175078512a1d40655f6217da0be79871cf5 Mon Sep 17 00:00:00 2001 From: kwajiehao Date: Wed, 31 Mar 2021 14:48:33 +0800 Subject: [PATCH 13/18] fix: use correct icon for image upload as per design specifications --- src/components/folders/FolderOptionButton.jsx | 1 + src/layouts/Images.jsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/folders/FolderOptionButton.jsx b/src/components/folders/FolderOptionButton.jsx index 0924d8282..7405a6f58 100644 --- a/src/components/folders/FolderOptionButton.jsx +++ b/src/components/folders/FolderOptionButton.jsx @@ -8,6 +8,7 @@ const iconSelection = { 'rearrange': 'bx-sort', 'create-page': 'bx-file-blank', 'create-sub': 'bx-folder', + 'upload-image': 'bx-plus-circle', } diff --git a/src/layouts/Images.jsx b/src/layouts/Images.jsx index 5163309d2..34a09f44c 100644 --- a/src/layouts/Images.jsx +++ b/src/layouts/Images.jsx @@ -156,7 +156,7 @@ const Images = ({ match: { params: { siteName, customPath } }, location }) => { {/* Upload Image */} document.getElementById('file-upload').click()} /> Date: Wed, 14 Apr 2021 14:37:36 +0800 Subject: [PATCH 14/18] fix: rebase errors --- src/layouts/Images.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/layouts/Images.jsx b/src/layouts/Images.jsx index 34a09f44c..4dc280e1b 100644 --- a/src/layouts/Images.jsx +++ b/src/layouts/Images.jsx @@ -134,6 +134,7 @@ const Images = ({ match: { params: { siteName, customPath } }, location }) => { return ( <>
From 0fe7e988999b5e05424bbd296209bf3ab2e89866 Mon Sep 17 00:00:00 2001 From: kwajiehao Date: Wed, 14 Apr 2021 17:34:10 +0800 Subject: [PATCH 15/18] fix: header navigation for nested images directories This commit fixes the back navigation for nested images directories. Previously, we failed to account for the fact that the custom path provided as part of the route would be URI encoded. As such, we were splitting on the `/` character instead of the encoded equivalent, `%2F`. This commit fixes this issue by using the encoded character instead. --- src/layouts/Images.jsx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/layouts/Images.jsx b/src/layouts/Images.jsx index 4dc280e1b..43726e5cd 100644 --- a/src/layouts/Images.jsx +++ b/src/layouts/Images.jsx @@ -26,14 +26,13 @@ import mediaStyles from '../styles/isomer-cms/pages/Media.module.scss'; const IMAGE_CONTENTS_KEY = 'image-contents' const getPrevDirectoryPath = (customPath) => { - const customPathArr = customPath.split('/') + const customPathArr = customPath.split('%2F') let prevDirectoryPath if (customPathArr.length > 1) { - prevDirectoryPath = customPathArr + prevDirectoryPath = `images/${customPathArr .slice(0, -1) // remove the latest directory - .join('/') - .slice(1) // remove starting `/` + .join('/')}` } else { prevDirectoryPath = 'images' @@ -43,7 +42,7 @@ const getPrevDirectoryPath = (customPath) => { } const getPrevDirectoryName = (customPath) => { - const customPathArr = customPath.split('/') + const customPathArr = customPath.split('%2F') let prevDirectoryName if (customPathArr.length > 1) { From 2315ec1b3336f1f345a7dde413ac96c30afd1a91 Mon Sep 17 00:00:00 2001 From: kwajiehao Date: Thu, 15 Apr 2021 11:48:24 +0800 Subject: [PATCH 16/18] fix: modify MediaSettingsModal to retrieve nested files This commit modifies the endpoint for the GET call to retrieve images from nested directories instead of just the top level 'images' directory by encoding the path of the nested file. --- src/components/media/MediaSettingsModal.jsx | 23 +++++++++++++++++---- src/layouts/Images.jsx | 2 ++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/components/media/MediaSettingsModal.jsx b/src/components/media/MediaSettingsModal.jsx index b4a8fd5fa..272eda46e 100644 --- a/src/components/media/MediaSettingsModal.jsx +++ b/src/components/media/MediaSettingsModal.jsx @@ -15,6 +15,11 @@ import { errorToast } from '../../utils/toasts'; import mediaStyles from '../../styles/isomer-cms/pages/Media.module.scss'; import elementStyles from '../../styles/isomer-cms/Elements.module.scss'; +const generateImageorFilePath = (customPath, fileName) => { + if (customPath) return encodeURIComponent(`${customPath}/${fileName}`) + return fileName +} + export default class MediaSettingsModal extends Component { constructor(props) { super(props); @@ -29,7 +34,7 @@ export default class MediaSettingsModal extends Component { async componentDidMount() { const { - siteName, media, isPendingUpload, type, + siteName, customPath, media, isPendingUpload, type, } = this.props; const { fileName } = media; @@ -39,9 +44,16 @@ export default class MediaSettingsModal extends Component { return; } - const { data: { sha, content } } = await axios.get(`${process.env.REACT_APP_BACKEND_URL}/sites/${siteName}/${type === 'image' ? 'images' : 'documents'}/${fileName}`, { - withCredentials: true, - }); + let sha, content + try { + const { data } = await axios.get(`${process.env.REACT_APP_BACKEND_URL}/sites/${siteName}/${type === 'image' ? 'images' : 'documents'}/${generateImageorFilePath(customPath, fileName)}`, { + withCredentials: true, + }); + sha = data.sha + content = data.content + } catch (err) { + errorToast(`We were unable to retrieve data on your image file. ${DEFAULT_RETRY_MSG}`) + } this.setState({ sha, content }); } @@ -52,6 +64,7 @@ export default class MediaSettingsModal extends Component { saveFile = async () => { const { siteName, + customPath, media: { fileName }, isPendingUpload, type, @@ -67,6 +80,8 @@ export default class MediaSettingsModal extends Component { if (type === 'image') { params.imageName = newFileName; + params.imageDirectory = customPath ? customPath : ''; + console.log(params) } else { params.documentName = newFileName; } diff --git a/src/layouts/Images.jsx b/src/layouts/Images.jsx index 43726e5cd..f566f7618 100644 --- a/src/layouts/Images.jsx +++ b/src/layouts/Images.jsx @@ -258,6 +258,7 @@ const Images = ({ match: { params: { siteName, customPath } }, location }) => { type="image" media={chosenImage} siteName={siteName} + customPath={customPath} isPendingUpload={false} onClose={() => setChosenImage(null)} onSave={() => window.location.reload()} @@ -271,6 +272,7 @@ const Images = ({ match: { params: { siteName, customPath } }, location }) => { type="image" media={pendingImageUpload} siteName={siteName} + customPath={customPath} // eslint-disable-next-line react/jsx-boolean-value isPendingUpload onClose={() => setPendingImageUpload(null)} From 1abf04efc6c16158151387e40b3894f43c907d9f Mon Sep 17 00:00:00 2001 From: kwajiehao Date: Thu, 15 Apr 2021 14:16:45 +0800 Subject: [PATCH 17/18] fix: update image creation process to allow for creation in nested directories This commit adds an `imageDirectory` attribute to the request body to specify the creation of a new image in that nested directory --- src/components/media/MediaSettingsModal.jsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/media/MediaSettingsModal.jsx b/src/components/media/MediaSettingsModal.jsx index 272eda46e..e878a886f 100644 --- a/src/components/media/MediaSettingsModal.jsx +++ b/src/components/media/MediaSettingsModal.jsx @@ -80,8 +80,7 @@ export default class MediaSettingsModal extends Component { if (type === 'image') { params.imageName = newFileName; - params.imageDirectory = customPath ? customPath : ''; - console.log(params) + params.imageDirectory = `images${customPath ? `/${customPath}` : ''}`; } else { params.documentName = newFileName; } From 05d9e6659454d07356a2d6f6bda3098734197830 Mon Sep 17 00:00:00 2001 From: kwajiehao Date: Thu, 15 Apr 2021 16:12:45 +0800 Subject: [PATCH 18/18] fix: update MediaSettingsModal to handle nested images --- src/components/media/MediaSettingsModal.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/media/MediaSettingsModal.jsx b/src/components/media/MediaSettingsModal.jsx index e878a886f..d0e8d0b53 100644 --- a/src/components/media/MediaSettingsModal.jsx +++ b/src/components/media/MediaSettingsModal.jsx @@ -98,7 +98,7 @@ export default class MediaSettingsModal extends Component { if (newFileName === fileName) { return; } - await axios.post(`${process.env.REACT_APP_BACKEND_URL}/sites/${siteName}/${type === 'image' ? 'images' : 'documents'}/${fileName}/rename/${newFileName}`, params, { + await axios.post(`${process.env.REACT_APP_BACKEND_URL}/sites/${siteName}/${type === 'image' ? 'images' : 'documents'}/${generateImageorFilePath(customPath, fileName)}/rename/${generateImageorFilePath(customPath, newFileName)}`, params, { withCredentials: true, }); }