Skip to content

Commit

Permalink
Migrate view_image.jsx to be follow the guidline (mattermost#204)
Browse files Browse the repository at this point in the history
* Move any PropTypes from the bottom of the file to be defined at the top of the component

* Make sure to add documentation for each prop

* Switch the component to extend React.PureComponent instead of React.Component

* Remove the jQuery use

* Clean up import paths

* Make startIndex to be a required prop

* Add test and  snapshot for the ViewImageModal component

* Remove function binding above and then change corresponding function to arrow style

* Separate image preview and add test and initial snanshot

* Migrate ImageView to functional style

* Separate LoadingImagePreview and add test and initial snanshot

* Update snapshot of ViewImageModal

* fix component tests
  • Loading branch information
cometkim authored and jwilander committed Nov 7, 2017
1 parent a964d48 commit d05d6f4
Show file tree
Hide file tree
Showing 9 changed files with 1,783 additions and 113 deletions.
38 changes: 38 additions & 0 deletions components/image_preview.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

import PropTypes from 'prop-types';
import React from 'react';

import {getFilePreviewUrl, getFileUrl} from 'mattermost-redux/utils/file_utils';

import {canDownloadFiles} from 'utils/file_utils';

export default function ImagePreview({fileInfo}) {
const {has_preview_image: hasPreviewImage, id} = fileInfo;
const fileUrl = getFileUrl(id);
const previewUrl = hasPreviewImage ? getFilePreviewUrl(id) : fileUrl;

if (!canDownloadFiles()) {
return <img src={previewUrl}/>;
}

return (
<a
href={fileUrl}
target='_blank'
rel='noopener noreferrer'
download={true}
>
<img src={previewUrl}/>
</a>
);
}

ImagePreview.propTypes = {

/**
* The file info object
*/
fileInfo: PropTypes.object.isRequired
};
41 changes: 41 additions & 0 deletions components/loading_image_preview.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

import PropTypes from 'prop-types';
import React from 'react';

import loadingGif from 'images/load.gif';

export default function LoadingImagePreview({loading, progress}) {
let progressView = null;
if (progress) {
progressView = (
<span className='loader-percent'>
{`${loading} ${progress}%`}
</span>
);
}

return (
<div className='view-image__loading'>
<img
className='loader-image'
src={loadingGif}
/>
{progressView}
</div>
);
}

LoadingImagePreview.propTypes = {

/**
* The percent number of the progress
*/
progress: PropTypes.number,

/**
* The loading message to display
*/
loading: PropTypes.string
};
169 changes: 56 additions & 113 deletions components/view_image.jsx
Original file line number Diff line number Diff line change
@@ -1,47 +1,59 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.

import $ from 'jquery';

import PropTypes from 'prop-types';
import React from 'react';
import {Modal} from 'react-bootstrap';

import {getFilePreviewUrl, getFileUrl} from 'mattermost-redux/utils/file_utils';

import * as GlobalActions from 'actions/global_actions.jsx';

import Constants from 'utils/constants.jsx';
import * as FileUtils from 'utils/file_utils';
import * as Utils from 'utils/utils.jsx';
import * as GlobalActions from 'actions/global_actions';

import loadingGif from 'images/load.gif';
import Constants from 'utils/constants';
import * as Utils from 'utils/utils';

import AudioVideoPreview from './audio_video_preview.jsx';
import CodePreview from './code_preview.jsx';
import FileInfoPreview from './file_info_preview.jsx';
import PDFPreview from './pdf_preview.jsx';
import ViewImagePopoverBar from './view_image_popover_bar.jsx';
import AudioVideoPreview from 'components/audio_video_preview';
import CodePreview from 'components/code_preview';
import FileInfoPreview from 'components/file_info_preview';
import ImagePreview from 'components/image_preview';
import LoadingImagePreview from 'components/loading_image_preview';
import PDFPreview from 'components/pdf_preview';
import ViewImagePopoverBar from 'components/view_image_popover_bar';

const KeyCodes = Constants.KeyCodes;

export default class ViewImageModal extends React.Component {
constructor(props) {
super(props);
export default class ViewImageModal extends React.PureComponent {
static propTypes = {

this.showImage = this.showImage.bind(this);
this.loadImage = this.loadImage.bind(this);
/**
* Set whether to show this modal or not
*/
show: PropTypes.bool.isRequired,

this.handleNext = this.handleNext.bind(this);
this.handlePrev = this.handlePrev.bind(this);
this.handleKeyPress = this.handleKeyPress.bind(this);
/**
* Function to call when this modal is dismissed
**/
onModalDismissed: PropTypes.func.isRequired,

this.onModalShown = this.onModalShown.bind(this);
this.onModalHidden = this.onModalHidden.bind(this);
/**
* List of FileInfo to view
**/
fileInfos: PropTypes.arrayOf(PropTypes.object).isRequired,

this.handleGetPublicLink = this.handleGetPublicLink.bind(this);
this.onMouseEnterImage = this.onMouseEnterImage.bind(this);
this.onMouseLeaveImage = this.onMouseLeaveImage.bind(this);
/**
* The index number of starting image
**/
startIndex: PropTypes.number.isRequired
};

static defaultProps = {
show: false,
fileInfos: [],
startIndex: 0
};

constructor(props) {
super(props);

this.state = {
imageIndex: this.props.startIndex,
Expand All @@ -52,7 +64,7 @@ export default class ViewImageModal extends React.Component {
};
}

handleNext(e) {
handleNext = (e) => {
if (e) {
e.stopPropagation();
}
Expand All @@ -63,7 +75,7 @@ export default class ViewImageModal extends React.Component {
this.showImage(id);
}

handlePrev(e) {
handlePrev = (e) => {
if (e) {
e.stopPropagation();
}
Expand All @@ -74,22 +86,22 @@ export default class ViewImageModal extends React.Component {
this.showImage(id);
}

handleKeyPress(e) {
handleKeyPress = (e) => {
if (e.keyCode === KeyCodes.RIGHT) {
this.handleNext();
} else if (e.keyCode === KeyCodes.LEFT) {
this.handlePrev();
}
}

onModalShown(nextProps) {
$(window).on('keyup', this.handleKeyPress);
onModalShown = (nextProps) => {
document.addEventListener('keyup', this.handleKeyPress);

this.showImage(nextProps.startIndex);
}

onModalHidden() {
$(window).off('keyup', this.handleKeyPress);
onModalHidden = () => {
document.addEventListener('keyup', this.handleKeyPress);

if (this.refs.video) {
this.refs.video.stop();
Expand All @@ -111,18 +123,18 @@ export default class ViewImageModal extends React.Component {
}
}

showImage(id) {
showImage = (id) => {
this.setState({imageIndex: id});

const imageHeight = $(window).height() - 100;
const imageHeight = window.innerHeight - 100;
this.setState({imageHeight});

if (!this.state.loaded[id]) {
this.loadImage(id);
}
}

loadImage(index) {
loadImage = (index) => {
const fileInfo = this.props.fileInfos[index];
const fileType = Utils.getFileType(fileInfo.extension);

Expand Down Expand Up @@ -168,17 +180,17 @@ export default class ViewImageModal extends React.Component {
});
}

handleGetPublicLink() {
handleGetPublicLink = () => {
this.props.onModalDismissed();

GlobalActions.showGetPublicLinkModal(this.props.fileInfos[this.state.imageIndex].id);
}

onMouseEnterImage() {
onMouseEnterImage = () => {
this.setState({showFooter: true});
}

onMouseLeaveImage() {
onMouseLeaveImage = () => {
this.setState({showFooter: false});
}

Expand All @@ -195,12 +207,7 @@ export default class ViewImageModal extends React.Component {
const fileType = Utils.getFileType(fileInfo.extension);

if (fileType === 'image' || fileType === 'svg') {
content = (
<ImagePreview
fileInfo={fileInfo}
fileUrl={fileUrl}
/>
);
content = <ImagePreview fileInfo={fileInfo}/>;
} else if (fileType === 'video' || fileType === 'audio') {
content = (
<AudioVideoPreview
Expand Down Expand Up @@ -232,12 +239,13 @@ export default class ViewImageModal extends React.Component {
}
} else {
// display a progress indicator when the preview for an image is still loading
const loading = Utils.localizeMessage('view_image.loading', 'Loading');
const progress = Math.floor(this.state.progress[this.state.imageIndex]);

content = (
<LoadingImagePreview
loading={loading}
progress={progress}
loading={Utils.localizeMessage('view_image.loading', 'Loading ')}
/>
);
}
Expand All @@ -247,6 +255,7 @@ export default class ViewImageModal extends React.Component {
if (this.props.fileInfos.length > 1) {
leftArrow = (
<a
id='previewArrowLeft'
ref='previewArrowLeft'
className='modal-prev-bar'
href='#'
Expand All @@ -258,6 +267,7 @@ export default class ViewImageModal extends React.Component {

rightArrow = (
<a
id='previewArrowRight'
ref='previewArrowRight'
className='modal-next-bar'
href='#'
Expand Down Expand Up @@ -317,70 +327,3 @@ export default class ViewImageModal extends React.Component {
);
}
}

ViewImageModal.defaultProps = {
show: false,
fileInfos: [],
startIndex: 0
};
ViewImageModal.propTypes = {
show: PropTypes.bool.isRequired,
onModalDismissed: PropTypes.func.isRequired,
fileInfos: PropTypes.arrayOf(PropTypes.object).isRequired,
startIndex: PropTypes.number
};

function LoadingImagePreview({progress, loading}) {
let progressView = null;
if (progress) {
progressView = (
<span className='loader-percent'>
{loading + progress + '%'}
</span>
);
}

return (
<div className='view-image__loading'>
<img
className='loader-image'
src={loadingGif}
/>
{progressView}
</div>
);
}

LoadingImagePreview.propTypes = {
progress: PropTypes.number,
loading: PropTypes.string
};

function ImagePreview({fileInfo, fileUrl}) {
let previewUrl;
if (fileInfo.has_preview_image) {
previewUrl = getFilePreviewUrl(fileInfo.id);
} else {
previewUrl = fileUrl;
}

if (!FileUtils.canDownloadFiles()) {
return <img src={previewUrl}/>;
}

return (
<a
href={fileUrl}
target='_blank'
rel='noopener noreferrer'
download={true}
>
<img src={previewUrl}/>
</a>
);
}

ImagePreview.propTypes = {
fileInfo: PropTypes.object.isRequired,
fileUrl: PropTypes.string.isRequired
};
27 changes: 27 additions & 0 deletions tests/components/__snapshots__/image_preview.test.jsx.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`components/ImagePreview should match snapshot, with and without preview 1`] = `
<a
download={true}
href="/api/v4/files/file_id"
rel="noopener noreferrer"
target="_blank"
>
<img
src="/api/v4/files/file_id"
/>
</a>
`;

exports[`components/ImagePreview should match snapshot, with and without preview 2`] = `
<a
download={true}
href="/api/v4/files/file_id_1"
rel="noopener noreferrer"
target="_blank"
>
<img
src="/api/v4/files/file_id_1/preview"
/>
</a>
`;
Loading

0 comments on commit d05d6f4

Please sign in to comment.