fix(gitlab): fetch media library images through API (#1433)
This commit is contained in:
committed by
Shawn Erquhart
parent
a4ba66e1a6
commit
83d2adc0be
@ -21,6 +21,9 @@ export const MEDIA_PERSIST_FAILURE = 'MEDIA_PERSIST_FAILURE';
|
||||
export const MEDIA_DELETE_REQUEST = 'MEDIA_DELETE_REQUEST';
|
||||
export const MEDIA_DELETE_SUCCESS = 'MEDIA_DELETE_SUCCESS';
|
||||
export const MEDIA_DELETE_FAILURE = 'MEDIA_DELETE_FAILURE';
|
||||
export const MEDIA_DISPLAY_URL_REQUEST = 'MEDIA_DISPLAY_URL_REQUEST';
|
||||
export const MEDIA_DISPLAY_URL_SUCCESS = 'MEDIA_DISPLAY_URL_SUCCESS';
|
||||
export const MEDIA_DISPLAY_URL_FAILURE = 'MEDIA_DISPLAY_URL_FAILURE';
|
||||
|
||||
export function openMediaLibrary(payload) {
|
||||
return { type: MEDIA_LIBRARY_OPEN, payload };
|
||||
@ -169,6 +172,24 @@ export function deleteMedia(file, opts = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
export function loadMediaDisplayURL(file) {
|
||||
return async dispatch => {
|
||||
const { getBlobPromise, id } = file;
|
||||
|
||||
if (id && getBlobPromise) {
|
||||
try {
|
||||
dispatch(mediaDisplayURLRequest(id));
|
||||
const blob = await getBlobPromise();
|
||||
const newURL = window.URL.createObjectURL(blob);
|
||||
dispatch(mediaDisplayURLSuccess(id, newURL));
|
||||
return newURL;
|
||||
} catch (err) {
|
||||
dispatch(mediaDisplayURLFailure(id, err));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function mediaLoading(page) {
|
||||
return {
|
||||
type: MEDIA_LOAD_REQUEST,
|
||||
@ -221,3 +242,21 @@ export function mediaDeleteFailed(error, opts = {}) {
|
||||
const { privateUpload } = opts;
|
||||
return { type: MEDIA_DELETE_FAILURE, payload: { privateUpload } };
|
||||
}
|
||||
|
||||
export function mediaDisplayURLRequest(key) {
|
||||
return { type: MEDIA_DISPLAY_URL_REQUEST, payload: { key } };
|
||||
}
|
||||
|
||||
export function mediaDisplayURLSuccess(key, url) {
|
||||
return {
|
||||
type: MEDIA_DISPLAY_URL_SUCCESS,
|
||||
payload: { key, url },
|
||||
};
|
||||
}
|
||||
|
||||
export function mediaDisplayURLFailure(key, err) {
|
||||
return {
|
||||
type: MEDIA_DISPLAY_URL_FAILURE,
|
||||
payload: { key, err },
|
||||
};
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { orderBy, map } from 'lodash';
|
||||
import { Map } from 'immutable';
|
||||
import fuzzy from 'fuzzy';
|
||||
import { resolvePath, fileExtension } from 'netlify-cms-lib-util';
|
||||
import {
|
||||
@ -8,6 +9,7 @@ import {
|
||||
persistMedia as persistMediaAction,
|
||||
deleteMedia as deleteMediaAction,
|
||||
insertMedia as insertMediaAction,
|
||||
loadMediaDisplayURL as loadMediaDisplayURLAction,
|
||||
closeMediaLibrary as closeMediaLibraryAction,
|
||||
} from 'Actions/mediaLibrary';
|
||||
import MediaLibraryModal from './MediaLibraryModal';
|
||||
@ -53,6 +55,30 @@ class MediaLibrary extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
getDisplayURL = file => {
|
||||
const { isVisible, loadMediaDisplayURL, displayURLs } = this.props;
|
||||
|
||||
if (!isVisible) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (file && file.url) {
|
||||
return file.url;
|
||||
}
|
||||
|
||||
const { url, isFetching } = displayURLs.get(file.id, Map()).toObject();
|
||||
|
||||
if (url && url !== '') {
|
||||
return url;
|
||||
}
|
||||
|
||||
if (!isFetching) {
|
||||
loadMediaDisplayURL(file);
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Filter an array of file data to include only images.
|
||||
*/
|
||||
@ -69,16 +95,18 @@ class MediaLibrary extends React.Component {
|
||||
toTableData = files => {
|
||||
const tableData =
|
||||
files &&
|
||||
files.map(({ key, name, size, queryOrder, url, urlIsPublicPath }) => {
|
||||
files.map(({ key, name, id, size, queryOrder, url, urlIsPublicPath, getBlobPromise }) => {
|
||||
const ext = fileExtension(name).toLowerCase();
|
||||
return {
|
||||
key,
|
||||
id,
|
||||
name,
|
||||
type: ext.toUpperCase(),
|
||||
size,
|
||||
queryOrder,
|
||||
url,
|
||||
urlIsPublicPath,
|
||||
getBlobPromise,
|
||||
isImage: IMAGE_EXTENSIONS.includes(ext),
|
||||
isViewableImage: IMAGE_EXTENSIONS_VIEWABLE.includes(ext),
|
||||
};
|
||||
@ -251,6 +279,7 @@ class MediaLibrary extends React.Component {
|
||||
setScrollContainerRef={ref => (this.scrollContainerRef = ref)}
|
||||
handleAssetClick={this.handleAssetClick}
|
||||
handleLoadMore={this.handleLoadMore}
|
||||
getDisplayURL={this.getDisplayURL}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@ -265,6 +294,7 @@ const mapStateToProps = state => {
|
||||
isVisible: mediaLibrary.get('isVisible'),
|
||||
canInsert: mediaLibrary.get('canInsert'),
|
||||
files: mediaLibrary.get('files'),
|
||||
displayURLs: mediaLibrary.get('displayURLs'),
|
||||
dynamicSearch: mediaLibrary.get('dynamicSearch'),
|
||||
dynamicSearchActive: mediaLibrary.get('dynamicSearchActive'),
|
||||
dynamicSearchQuery: mediaLibrary.get('dynamicSearchQuery'),
|
||||
@ -285,6 +315,7 @@ const mapDispatchToProps = {
|
||||
persistMedia: persistMediaAction,
|
||||
deleteMedia: deleteMediaAction,
|
||||
insertMedia: insertMediaAction,
|
||||
loadMediaDisplayURL: loadMediaDisplayURLAction,
|
||||
closeMediaLibrary: closeMediaLibraryAction,
|
||||
};
|
||||
|
||||
|
@ -36,7 +36,7 @@ const CardText = styled.p`
|
||||
line-height: 1.3 !important;
|
||||
`;
|
||||
|
||||
const MediaLibraryCard = ({ isSelected, imageUrl, text, onClick, width, margin, isPrivate }) => (
|
||||
const MediaLibraryCard = ({ isSelected, displayURL, text, onClick, width, margin, isPrivate }) => (
|
||||
<Card
|
||||
isSelected={isSelected}
|
||||
onClick={onClick}
|
||||
@ -45,14 +45,14 @@ const MediaLibraryCard = ({ isSelected, imageUrl, text, onClick, width, margin,
|
||||
tabIndex="-1"
|
||||
isPrivate={isPrivate}
|
||||
>
|
||||
<div>{imageUrl ? <CardImage src={imageUrl} /> : <CardImagePlaceholder />}</div>
|
||||
<div>{displayURL ? <CardImage src={displayURL} /> : <CardImagePlaceholder />}</div>
|
||||
<CardText>{text}</CardText>
|
||||
</Card>
|
||||
);
|
||||
|
||||
MediaLibraryCard.propTypes = {
|
||||
isSelected: PropTypes.bool,
|
||||
imageUrl: PropTypes.string,
|
||||
displayURL: PropTypes.string,
|
||||
text: PropTypes.string.isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
width: PropTypes.string.isRequired,
|
||||
|
@ -33,6 +33,7 @@ const MediaLibraryCardGrid = ({
|
||||
cardWidth,
|
||||
cardMargin,
|
||||
isPrivate,
|
||||
getDisplayURL,
|
||||
}) => (
|
||||
<CardGridContainer innerRef={setScrollContainerRef}>
|
||||
<CardGrid>
|
||||
@ -40,12 +41,12 @@ const MediaLibraryCardGrid = ({
|
||||
<MediaLibraryCard
|
||||
key={file.key}
|
||||
isSelected={isSelectedFile(file)}
|
||||
imageUrl={file.isViewableImage && file.url}
|
||||
text={file.name}
|
||||
onClick={() => onAssetClick(file)}
|
||||
width={cardWidth}
|
||||
margin={cardMargin}
|
||||
isPrivate={isPrivate}
|
||||
displayURL={getDisplayURL(file)}
|
||||
/>
|
||||
))}
|
||||
{!canLoadMore ? null : <Waypoint onEnter={onLoadMore} />}
|
||||
@ -74,6 +75,7 @@ MediaLibraryCardGrid.propTypes = {
|
||||
paginatingMessage: PropTypes.string,
|
||||
cardWidth: PropTypes.string.isRequired,
|
||||
cardMargin: PropTypes.string.isRequired,
|
||||
getDisplayURL: PropTypes.func.isRequired,
|
||||
isPrivate: PropTypes.bool,
|
||||
};
|
||||
|
||||
|
@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
import styled from 'react-emotion';
|
||||
|
||||
const CardImage = styled.img`
|
||||
width: 100%;
|
||||
height: 160px;
|
||||
object-fit: cover;
|
||||
border-radius: 2px 2px 0 0;
|
||||
`;
|
||||
|
||||
const CardImagePlaceholder = CardImage.withComponent(`div`);
|
||||
|
||||
export default class MediaLibraryCardImage extends React.Component {
|
||||
state = {
|
||||
imageURL: '',
|
||||
isFetching: false,
|
||||
};
|
||||
|
||||
loadImage() {
|
||||
const { image, getCachedImageURLByID, cacheImageURLByID } = this.props;
|
||||
const { imageURL: existingImageURL, isFetching } = this.state;
|
||||
|
||||
if (existingImageURL !== '' || isFetching) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (getCachedImageURLByID && image.key) {
|
||||
const imageURL = getCachedImageURLByID(image.key);
|
||||
if (imageURL) {
|
||||
this.setState({ imageURL });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (image.url) {
|
||||
this.setState({ imageURL: image.url });
|
||||
if (image.key && cacheImageURLByID) {
|
||||
cacheImageURLByID(image.key, image.url);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (image.getBlobPromise) {
|
||||
this.setState({ isFetching: true });
|
||||
image.getBlobPromise().then(blob => {
|
||||
const imageURL = window.URL.createObjectURL(blob);
|
||||
this.setState({ imageURL, isFetching: false });
|
||||
if (image.key && cacheImageURLByID) {
|
||||
cacheImageURLByID(image.key, imageURL);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { imageURL, isFetching } = this.state;
|
||||
|
||||
if (imageURL === '' && !isFetching) {
|
||||
this.loadImage();
|
||||
}
|
||||
|
||||
return imageURL === '' ? <CardImagePlaceholder /> : <CardImage src={imageURL} />;
|
||||
}
|
||||
}
|
@ -92,6 +92,7 @@ const MediaLibraryModal = ({
|
||||
setScrollContainerRef,
|
||||
handleAssetClick,
|
||||
handleLoadMore,
|
||||
getDisplayURL,
|
||||
}) => {
|
||||
const filteredFiles = forImage ? handleFilter(files) : files;
|
||||
const queriedFiles = !dynamicSearch && query ? handleQuery(query, filteredFiles) : filteredFiles;
|
||||
@ -156,6 +157,7 @@ const MediaLibraryModal = ({
|
||||
cardWidth={cardWidth}
|
||||
cardMargin={cardMargin}
|
||||
isPrivate={privateUpload}
|
||||
getDisplayURL={getDisplayURL}
|
||||
/>
|
||||
</StyledModal>
|
||||
);
|
||||
@ -197,6 +199,7 @@ MediaLibraryModal.propTypes = {
|
||||
setScrollContainerRef: PropTypes.func.isRequired,
|
||||
handleAssetClick: PropTypes.func.isRequired,
|
||||
handleLoadMore: PropTypes.func.isRequired,
|
||||
getDisplayURL: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default MediaLibraryModal;
|
||||
|
@ -15,11 +15,18 @@ import {
|
||||
MEDIA_DELETE_REQUEST,
|
||||
MEDIA_DELETE_SUCCESS,
|
||||
MEDIA_DELETE_FAILURE,
|
||||
MEDIA_DISPLAY_URL_REQUEST,
|
||||
MEDIA_DISPLAY_URL_SUCCESS,
|
||||
MEDIA_DISPLAY_URL_FAILURE,
|
||||
} from 'Actions/mediaLibrary';
|
||||
|
||||
const mediaLibrary = (state = Map({ isVisible: false, controlMedia: Map() }), action) => {
|
||||
const mediaLibrary = (
|
||||
state = Map({ isVisible: false, controlMedia: Map(), displayURLs: Map() }),
|
||||
action,
|
||||
) => {
|
||||
const privateUploadChanged =
|
||||
state.get('privateUpload') !== get(action, ['payload', 'privateUpload']);
|
||||
let displayURLPath;
|
||||
switch (action.type) {
|
||||
case MEDIA_LIBRARY_OPEN: {
|
||||
const { controlID, forImage, privateUpload } = action.payload || {};
|
||||
@ -108,13 +115,14 @@ const mediaLibrary = (state = Map({ isVisible: false, controlMedia: Map() }), ac
|
||||
case MEDIA_DELETE_REQUEST:
|
||||
return state.set('isDeleting', true);
|
||||
case MEDIA_DELETE_SUCCESS: {
|
||||
const { key } = action.payload.file;
|
||||
const { id, key } = action.payload.file;
|
||||
if (privateUploadChanged) {
|
||||
return state;
|
||||
}
|
||||
return state.withMutations(map => {
|
||||
const updatedFiles = map.get('files').filter(file => file.key !== key);
|
||||
map.set('files', updatedFiles);
|
||||
map.deleteIn(['displayURLs', id]);
|
||||
map.set('isDeleting', false);
|
||||
});
|
||||
}
|
||||
@ -123,6 +131,23 @@ const mediaLibrary = (state = Map({ isVisible: false, controlMedia: Map() }), ac
|
||||
return state;
|
||||
}
|
||||
return state.set('isDeleting', false);
|
||||
|
||||
case MEDIA_DISPLAY_URL_REQUEST:
|
||||
return state.setIn(['displayURLs', action.payload.key, 'isFetching'], true);
|
||||
|
||||
case MEDIA_DISPLAY_URL_SUCCESS:
|
||||
displayURLPath = ['displayURLs', action.payload.key];
|
||||
return state
|
||||
.setIn([...displayURLPath, 'isFetching'], false)
|
||||
.setIn([...displayURLPath, 'url'], action.payload.url);
|
||||
|
||||
case MEDIA_DISPLAY_URL_FAILURE:
|
||||
displayURLPath = ['displayURLs', action.payload.key];
|
||||
return state
|
||||
.setIn([...displayURLPath, 'isFetching'], false)
|
||||
.setIn([...displayURLPath, 'err'], action.payload.err)
|
||||
.deleteIn([...displayURLPath, 'url']);
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
Reference in New Issue
Block a user