@instructure/canvas-rce
Version:
A component wrapping Canvas's usage of Tinymce
164 lines (161 loc) • 4.82 kB
JavaScript
/*
* Copyright (C) 2021 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { View } from '@instructure/ui-view';
import ImageList from '../../../../instructure_image/Images';
import { useStoreProps } from '../../../../shared/StoreContext';
import useDataUrl from '../../../../shared/useDataUrl';
import { actions } from '../../../reducers/imageSection';
import { canCompressImage, compressImage, shouldCompressImage } from '../../../../shared/compressionUtils';
import { isAnUnsupportedGifPngImage, MAX_GIF_PNG_SIZE_BYTES } from './utils';
import { actions as svgActions } from '../../../reducers/svgSettings';
import formatMessage from '../../../../../../format-message';
import { PREVIEW_WIDTH, PREVIEW_HEIGHT } from '../../../../shared/ImageCropper/constants';
const dispatchImage = async (dispatch, onChange, dataUrl, dataBlob) => {
let image = dataUrl;
if (isAnUnsupportedGifPngImage(dataBlob)) {
dispatch({
...actions.CLEAR_IMAGE
});
return onChange({
type: svgActions.SET_ERROR,
payload: formatMessage('GIF/PNG format images larger than {size} KB are not currently supported.', {
size: MAX_GIF_PNG_SIZE_BYTES / 1024
})
});
}
dispatch({
...actions.SET_IMAGE,
payload: ''
});
dispatch({
...actions.SET_CROPPER_OPEN,
payload: true
});
onChange({
type: svgActions.SET_EMBED_IMAGE,
payload: ''
});
if (canCompressImage() && shouldCompressImage(dataBlob)) {
try {
// If compression fails, use the original one
// TODO: We can show the user that compression failed in some way
image = await compressImage({
encodedImage: dataUrl,
previewWidth: PREVIEW_WIDTH,
previewHeight: PREVIEW_HEIGHT
});
} catch (e) {
console.error(e);
}
dispatch({
...actions.SET_COMPRESSION_STATUS,
payload: true
});
}
dispatch({
...actions.SET_IMAGE,
payload: image
});
onChange({
type: svgActions.SET_EMBED_IMAGE,
payload: image
});
};
const Course = ({
dispatch = () => {},
onChange = () => {},
onLoading = () => {},
onLoaded = () => {},
canvasOrigin
}) => {
const storeProps = useStoreProps();
const {
files,
bookmark,
isLoading,
hasMore
} = storeProps.images[storeProps.contextType];
const {
setUrl,
dataUrl,
dataLoading,
dataBlob
} = useDataUrl();
const category = 'uncategorized';
// Handle image selection
useEffect(() => {
// Don't clear the current image on re-render
if (!dataUrl || !dataBlob) return;
dispatchImage(dispatch, onChange, dataUrl, dataBlob);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dataUrl, dataBlob]);
// Handle loading states
useEffect(() => {
dispatch(dataLoading ? actions.START_LOADING : actions.STOP_LOADING);
if (dataUrl) {
dispatch({
...actions.SET_IMAGE_COLLECTION_OPEN,
payload: false
});
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [dataLoading]);
useEffect(() => {
if (isLoading) onLoading && onLoading();else onLoaded && onLoaded();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isLoading]);
return /*#__PURE__*/React.createElement(View, null, /*#__PURE__*/React.createElement(ImageList, {
fetchInitialImages: () => storeProps.fetchInitialImages({
category
}),
fetchNextImages: () => storeProps.fetchNextImages({
category
}),
contextType: storeProps.contextType,
images: {
[storeProps.contextType]: {
files,
bookmark,
hasMore,
isLoading
}
},
sortBy: {
sort: 'date_added',
order: 'desc'
},
onImageEmbed: file => {
setUrl(file.download_url);
dispatch({
...actions.SET_IMAGE_NAME,
payload: file.filename
});
},
canvasOrigin: canvasOrigin
}));
};
Course.propTypes = {
dispatch: PropTypes.func,
onChange: PropTypes.func,
onLoading: PropTypes.func,
onLoaded: PropTypes.func,
canvasOrigin: PropTypes.string.isRequired
};
export default Course;