Commit a8e4c839 authored by Matija Obreza's avatar Matija Obreza

Merge branch '130-image-display' into 'master'

Image display

Closes #130

See merge request genesys-pgr/genesys-ui!165
parents 510dab22 8eabe047
......@@ -1536,6 +1536,25 @@
}
}
}
},
"public": {
"c": {
"imageDetailsDialog": {
"details": "Image details",
"userAndLicenseNotSpecified": "Creator and license not specified",
"creator": "Creator",
"license": "License",
"height": "Height",
"width": "Width",
"orientation": "Orientation",
"title": "Title",
"extension": "Extension",
"description": "Description",
"thumbnailPath": "Thumbnail path",
"contentType": "Content type",
"originalFilename": "Original filename"
}
}
}
}
,"requests": {
......
import * as React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { Link } from 'react-router-dom';
import { translate } from 'react-i18next';
import { repositoryThumbnailUrl } from 'repository/actions/public';
// Models
import CropDetails from 'model/genesys/CropDetails';
......@@ -13,6 +17,7 @@ import Grid from '@material-ui/core/Grid';
import withStyles from '@material-ui/core/styles/withStyles';
import Button from '@material-ui/core/Button';
import Authorize from 'ui/common/authorized/Authorize';
import ImageDetailsDialog from 'repository/ui/c/ImageDetailsDialog';
const style = (theme) => ({
root: {
......@@ -44,7 +49,7 @@ const style = (theme) => ({
},
});
const CropImage = ({classes, crop}: { classes: any, crop: CropDetails}) => {
const CropImage_ = ({classes, crop, repositoryThumbnailUrl}: { classes: any, crop: CropDetails, repositoryThumbnailUrl: any}) => {
// console.log(`renderImage`, crop);
const hasCover = crop.covers && crop.covers.length > 0;
const image = hasCover ? crop.covers[0] : null;
......@@ -54,7 +59,7 @@ const CropImage = ({classes, crop}: { classes: any, crop: CropDetails}) => {
let sizes: string = '';
if (image) {
for (const size of thumbSizes) {
x += `/proxy/uploads/d/_thumbs${image.thumbnailPath}/${size}x${size}.png ${size}w, `;
x += repositoryThumbnailUrl(image, size, size) + ` ${size}w, `;
}
sizes += `(max-width: 600px) 200px, `;
sizes += `(min-width: 1800px) 600px, `;
......@@ -66,15 +71,29 @@ const CropImage = ({classes, crop}: { classes: any, crop: CropDetails}) => {
return (
<div className={ classes.media }>
{ hasCover &&
<img className={ classes.mediaImage } srcSet={ x } sizes={ sizes }
src={ `/proxy/uploads/d/_thumbs${image.thumbnailPath}/300x300.png` } alt={ crop.name } /> }
<span>
<img className={ classes.mediaImage } srcSet={ x } sizes={ sizes }
src={ repositoryThumbnailUrl(image, 300, 300) } alt={ crop.name } />
<ImageDetailsDialog image={ image }/>
</span>
}
{ !hasCover &&
<img className={ classes.mediaImage } src={ `images/crop-placeholder.jpg` } alt={ crop.name } />
<span>
<img className={ classes.mediaImage } src={ `images/crop-placeholder.jpg` } alt={ crop.name } />
<ImageDetailsDialog image={ image }/>
</span>
}
</div>
);
};
const mapDispatchToProps = (dispatch) => bindActionCreators({
repositoryThumbnailUrl,
}, dispatch);
const CropImage = connect(null, mapDispatchToProps)(CropImage_);
const CropCard = ({crop, classes, compact = false, edit = false, t, ...other}: { crop: CropDetails, classes?: any, compact?: boolean, edit?: boolean, t: any }) => {
if (!crop) {
return null;
......
import RepositoryFile from 'model/repository/RepositoryFile';
import RepositoryImage from 'model/repository/RepositoryImage';
/**
* Pick a random CDN server if available
*/
function getCDNServer(getState) {
const apiInfo = getState().serverInfo.data;
if (apiInfo && apiInfo.cdnServers && apiInfo.cdnServers.length > 0) {
const cdnServers = apiInfo.cdnServers;
return cdnServers[Math.round((cdnServers.length - 1) * Math.random())] + '/repository/d';
} else {
return '/proxy/uploads/d';
}
}
/**
* Renders CDN-based URL for public files, API URL for protected files
*
* @param file RepositoryFile for which to render URL
*/
export const repositoryDownloadUrl = (file: RepositoryFile) => (dispatch, getState) => {
if (! file) {
return null;
}
if (file._permissions && file._permissions.isPublic) {
return getCDNServer(getState) + `/${file.uuid}`;
} else {
return '/proxy/uploads/d' + `/${file.uuid}`;
}
};
/**
* Renders CDN-based URL for public images, API URL for protected images
*
* @param image RepositoryImage for which to render thumbnail URL
*/
export const repositoryThumbnailUrl = (image: RepositoryImage, width: number, height: number) => (dispatch, getState) => {
if (! image) {
return null;
}
if (image._permissions && image._permissions.isPublic) {
return getCDNServer(getState) + `/_thumbs${image.thumbnailPath}/${width}x${height}.png`;
} else {
return '/proxy/uploads/d' + `/_thumbs${image.thumbnailPath}/${width}x${height}.png`;
}
};
......@@ -55,5 +55,24 @@
}
}
}
},
"public": {
"c": {
"imageDetailsDialog": {
"details": "Image details",
"userAndLicenseNotSpecified": "Creator and license not specified",
"creator": "Creator",
"license": "License",
"height": "Height",
"width": "Width",
"orientation": "Orientation",
"title": "Title",
"extension": "Extension",
"description": "Description",
"thumbnailPath": "Thumbnail path",
"contentType": "Content type",
"originalFilename": "Original filename"
}
}
}
}
\ No newline at end of file
import * as React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { translate } from 'react-i18next';
import { repositoryDownloadUrl } from 'repository/actions/public';
// Models
import RepositoryFile from 'model/repository/RepositoryFile';
......@@ -74,8 +78,8 @@ const style = (theme) => ({
});
/*tslint:enable*/
const FileCard = ({file, classes, compact = false, edit = false, deleteFile, editFile, t, ...other}:
{ file: RepositoryFile, classes?: any, compact?: boolean, edit?: boolean, deleteFile: (uuid: string) => any, editFile: (uuid: string) => any, t: any }) => {
const FileCard = ({file, classes, compact = false, edit = false, deleteFile, editFile, t, repositoryDownloadUrl, ...other}:
{ file: RepositoryFile, classes?: any, compact?: boolean, edit?: boolean, deleteFile: (uuid: string) => any, editFile: (uuid: string) => any, t: any, repositoryDownloadUrl: any }) => {
if (!file) {
return null;
......@@ -94,7 +98,7 @@ const FileCard = ({file, classes, compact = false, edit = false, deleteFile, edi
return compact ? (
<Grid item xs={ 12 }>
<Card className={ classes.root }>
<CardHeader title={ <a href={ `/proxy/uploads/${file.uuid}` }>{ file.originalFilename }</a> } />
<CardHeader title={ <a href={ repositoryDownloadUrl(file) }>{ file.originalFilename }</a> } />
<CardContent>
{ file.storageFolder }
</CardContent>
......@@ -106,7 +110,7 @@ const FileCard = ({file, classes, compact = false, edit = false, deleteFile, edi
<Grid item xs={ 12 }>
<Card className={ classes.root }>
<CardHeader className={ classes.header }
title={ <span><a href={ `/proxy/uploads/${file.uuid}` }><DownloadIcon /></a> <span>{ file.originalFilename }</span></span> }
title={ <span><a href={ repositoryDownloadUrl(file) }><DownloadIcon /></a> <span>{ file.originalFilename }</span></span> }
action={
<div className={ classes.actions }>
<span><ActionButton action={ () => editFile(file.uuid) } title={ t('common:action.edit') } /></span>
......@@ -127,4 +131,8 @@ const FileCard = ({file, classes, compact = false, edit = false, deleteFile, edi
);
};
export default translate()(withStyles(style)(FileCard));
const mapDispatchToProps = (dispatch) => bindActionCreators({
repositoryDownloadUrl,
}, dispatch);
export default connect(null, mapDispatchToProps)(translate()(withStyles(style)(FileCard)));
import * as React from 'react';
import { translate } from 'react-i18next';
import { withStyles } from '@material-ui/core/styles';
import RepositoryImage from 'model/repository/RepositoryImage';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogContent from '@material-ui/core/DialogContent';
import DialogTitle from '@material-ui/core/DialogTitle';
import { Properties, PropertiesItem } from 'ui/common/Properties';
/*tslint:disable*/
const styles = (theme) => ({
root: {
height: '100%',
},
createButton: {
position: 'absolute' as 'absolute',
right: 0,
bottom: 0,
fontSize: '10px',
// padding: '4px',
// minHeight: '30px',
background: '#e7e5df',
border: 'solid gray',
// borderWidth: '1px 0 0 1px',
borderWidth: '0 0 0 0',
textTransform: 'initial' as 'initial',
'&:hover': {
background: '#e7e5df',
},
},
});
/*tslint:enable*/
interface IImageDetailsDialog extends React.ClassAttributes<any> {
image: RepositoryImage;
classes?: any;
t?: any;
}
class ImageDetailsDialog extends React.Component<IImageDetailsDialog, any> {
public state = {
open: false,
};
public render() {
const { classes, t, image } = this.props;
const buttonText = `©`;
return image ? (
<span className={ classes.root }>
<Button className={ classes.createButton } onClick={ this.show }>{ buttonText }</Button>
{ this.state.open &&
<Dialog open={ this.state.open } onClose={ this.hide } maxWidth="md" fullWidth onClick={ (e) => e.preventDefault() }>
<DialogTitle>{ t(`repository.public.c.imageDetailsDialog.details`) }</DialogTitle>
<DialogContent>
<Properties>
<PropertiesItem title={ t('repository.public.c.imageDetailsDialog.title') }>{ image.title || 'Not specified' }</PropertiesItem>
<PropertiesItem title={ t('repository.public.c.imageDetailsDialog.originalFilename') }>{ image.originalFilename || 'Not specified' }</PropertiesItem>
<PropertiesItem title={ t('repository.public.c.imageDetailsDialog.contentType') }>{ image.contentType || 'Not specified' }</PropertiesItem>
<PropertiesItem title={ t('repository.public.c.imageDetailsDialog.extension') }>{ image.extension || 'Not specified' }</PropertiesItem>
<PropertiesItem title={ t('repository.public.c.imageDetailsDialog.creator') }>{ image.creator || 'Not specified' }</PropertiesItem>
<PropertiesItem title={ t('repository.public.c.imageDetailsDialog.license') }>{ image.license || 'Not specified' }</PropertiesItem>
<PropertiesItem title={ t('repository.public.c.imageDetailsDialog.height') }>{ image.height || 'Not specified' }</PropertiesItem>
<PropertiesItem title={ t('repository.public.c.imageDetailsDialog.width') }>{ image.width || 'Not specified' }</PropertiesItem>
<PropertiesItem title={ t('repository.public.c.imageDetailsDialog.orientation') }>{ image.orientation || 'Not specified' }</PropertiesItem>
<PropertiesItem title={ t('repository.public.c.imageDetailsDialog.description') }>{ image.description || 'Not specified' }</PropertiesItem>
<PropertiesItem title={ t('repository.public.c.imageDetailsDialog.thumbnailPath') }>{ image.thumbnailPath || 'Not specified' }</PropertiesItem>
</Properties>
</DialogContent>
</Dialog>
}
</span>
) : null;
}
private show = (e) => {
e.preventDefault();
this.setState(
(state) => ({ ...state, open: true }));
}
private hide = (e) => {
e.preventDefault();
this.setState({ open: false });
}
}
export default translate()(withStyles(styles)(ImageDetailsDialog));
import * as React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { repositoryThumbnailUrl } from 'repository/actions/public';
// Models
import ImageGallery from 'model/repository/ImageGallery';
......@@ -9,6 +13,7 @@ import Card, {CardHeader, CardContent, CardMedia} from 'ui/common/Card';
// import PrettyDate from 'ui/common/time/PrettyDate';
import Grid from '@material-ui/core/Grid';
import withStyles from '@material-ui/core/styles/withStyles';
import ImageDetailsDialog from './ImageDetailsDialog';
const style = (theme) => ({
root: {
......@@ -20,9 +25,15 @@ const style = (theme) => ({
media: {
height: 200,
},
mediaContent: {
position: 'relative' as 'relative',
},
});
const ImageGalleryView = ({imageGallery, classes, className, compact = false, edit = false, ...other}: { imageGallery: ImageGallery, className?: string, classes?: any, compact?: boolean, edit?: boolean }) => {
const ImageGalleryView = (
{ imageGallery, repositoryThumbnailUrl, classes, className, compact = false, edit = false, ...other }:
{ imageGallery: ImageGallery, repositoryThumbnailUrl: any, className?: string, classes?: any, compact?: boolean, edit?: boolean }) => {
if (!imageGallery) {
return null;
}
......@@ -44,7 +55,10 @@ const ImageGalleryView = ({imageGallery, classes, className, compact = false, ed
{ imageGallery.images.map((image) => (
<Grid key={ image.uuid } item xs={ 12 } sm={ 6 } md={ 4 } lg={ 3 } xl={ 2 }>
<Card className={ classes.root }>
<CardMedia className={ classes.media } image={ `/proxy/uploads/d/_thumbs${image.thumbnailPath}/200x200.png` } title={ image.title || image.orientation } />
<span className={ classes.mediaContent }>
<CardMedia className={ classes.media } image={ repositoryThumbnailUrl(image, 200, 200) } title={ image.title || image.orientation } />
<ImageDetailsDialog image={ image }/>
</span>
<CardContent>
{ image.title || image.originalFilename }
</CardContent>
......@@ -67,4 +81,8 @@ const ImageGalleryView = ({imageGallery, classes, className, compact = false, ed
// </Grid>
};
export default withStyles(style)(ImageGalleryView);
const mapDispatchToProps = (dispatch) => bindActionCreators({
repositoryThumbnailUrl,
}, dispatch);
export default connect(null, mapDispatchToProps)(withStyles(style)(ImageGalleryView));
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment