From c5d78f828a48ba88b8a89a0564bcb8352e84b396 Mon Sep 17 00:00:00 2001 From: Christopher Speller Date: Thu, 24 Sep 2015 10:17:58 -0400 Subject: Converting preview image modal to use react-bootstrap. Fixes issue where modal would appear behind background on iOS --- web/react/components/file_attachment.jsx | 10 +- web/react/components/file_attachment_list.jsx | 16 +- web/react/components/post_body.jsx | 1 - web/react/components/rhs_comment.jsx | 1 - web/react/components/rhs_root_post.jsx | 1 - web/react/components/view_image.jsx | 232 ++++++++++-------------- web/react/components/view_image_popover_bar.jsx | 66 +++++++ 7 files changed, 168 insertions(+), 159 deletions(-) create mode 100644 web/react/components/view_image_popover_bar.jsx (limited to 'web/react/components') diff --git a/web/react/components/file_attachment.jsx b/web/react/components/file_attachment.jsx index c9aa06a97..888f24aa5 100644 --- a/web/react/components/file_attachment.jsx +++ b/web/react/components/file_attachment.jsx @@ -143,10 +143,7 @@ export default class FileAttachment extends React.Component { > this.props.handleImageClick(this.props.index)} > {thumbnail} @@ -187,9 +184,6 @@ FileAttachment.propTypes = { // the index of this attachment preview in the parent FileAttachmentList index: React.PropTypes.number.isRequired, - // the identifier of the modal dialog used to preview files - modalId: React.PropTypes.string.isRequired, - - // handler for when the thumbnail is clicked + // handler for when the thumbnail is clicked passed the index above handleImageClick: React.PropTypes.func }; diff --git a/web/react/components/file_attachment_list.jsx b/web/react/components/file_attachment_list.jsx index abe72089a..212d4a958 100644 --- a/web/react/components/file_attachment_list.jsx +++ b/web/react/components/file_attachment_list.jsx @@ -11,23 +11,21 @@ export default class FileAttachmentList extends React.Component { this.handleImageClick = this.handleImageClick.bind(this); - this.state = {startImgId: 0}; + this.state = {showPreviewModal: false, startImgId: 0}; } - handleImageClick(e) { - this.setState({startImgId: parseInt($(e.target.parentNode).attr('data-img-id'), 10)}); + handleImageClick(indexClicked) { + this.setState({showPreviewModal: true, startImgId: indexClicked}); } render() { var filenames = this.props.filenames; - var modalId = this.props.modalId; var postFiles = []; for (var i = 0; i < filenames.length && i < Constants.MAX_DISPLAY_FILES; i++) { postFiles.push( ); @@ -39,9 +37,10 @@ export default class FileAttachmentList extends React.Component { {postFiles} this.setState({showPreviewModal: false})} channelId={this.props.channelId} userId={this.props.userId} - modalId={modalId} startId={this.state.startImgId} filenames={filenames} /> @@ -55,9 +54,6 @@ FileAttachmentList.propTypes = { // a list of file pathes displayed by this filenames: React.PropTypes.arrayOf(React.PropTypes.string).isRequired, - // the identifier of the modal dialog used to preview files - modalId: React.PropTypes.string.isRequired, - // the channel that this is part of channelId: React.PropTypes.string, diff --git a/web/react/components/post_body.jsx b/web/react/components/post_body.jsx index dbbcdc409..6e98e4aba 100644 --- a/web/react/components/post_body.jsx +++ b/web/react/components/post_body.jsx @@ -141,7 +141,6 @@ export default class PostBody extends React.Component { fileAttachmentHolder = ( diff --git a/web/react/components/rhs_comment.jsx b/web/react/components/rhs_comment.jsx index 4d1892a69..dc49fbdb1 100644 --- a/web/react/components/rhs_comment.jsx +++ b/web/react/components/rhs_comment.jsx @@ -163,7 +163,6 @@ export default class RhsComment extends React.Component { fileAttachment = ( diff --git a/web/react/components/rhs_root_post.jsx b/web/react/components/rhs_root_post.jsx index e661bdce1..caa8c5c1e 100644 --- a/web/react/components/rhs_root_post.jsx +++ b/web/react/components/rhs_root_post.jsx @@ -111,7 +111,6 @@ export default class RhsRootPost extends React.Component { fileAttachment = ( diff --git a/web/react/components/view_image.jsx b/web/react/components/view_image.jsx index dafcdd9f9..8db63e196 100644 --- a/web/react/components/view_image.jsx +++ b/web/react/components/view_image.jsx @@ -3,6 +3,8 @@ var Client = require('../utils/client.jsx'); var Utils = require('../utils/utils.jsx'); +var ViewImagePopoverBar = require('./view_image_popover_bar.jsx'); +var Modal = ReactBootstrap.Modal; export default class ViewImageModal extends React.Component { constructor(props) { @@ -16,6 +18,10 @@ export default class ViewImageModal extends React.Component { this.handleKeyPress = this.handleKeyPress.bind(this); this.getPublicLink = this.getPublicLink.bind(this); this.getPreviewImagePath = this.getPreviewImagePath.bind(this); + this.onModalShown = this.onModalShown.bind(this); + this.onModalHidden = this.onModalHidden.bind(this); + this.onMouseEnterImage = this.onMouseEnterImage.bind(this); + this.onMouseLeaveImage = this.onMouseLeaveImage.bind(this); var loaded = []; var progress = []; @@ -23,9 +29,18 @@ export default class ViewImageModal extends React.Component { loaded.push(false); progress.push(0); } - this.state = {imgId: this.props.startId, viewed: false, loaded: loaded, progress: progress, images: {}, fileSizes: {}}; + this.state = { + imgId: this.props.startId, + imgHeight: '100%', + loaded: loaded, + progress: progress, + images: {}, + fileSizes: {}, + showFooter: false + }; } - handleNext() { + handleNext(e) { + e.stopPropagation(); var id = this.state.imgId + 1; if (id > this.props.filenames.length - 1) { id = 0; @@ -33,7 +48,8 @@ export default class ViewImageModal extends React.Component { this.setState({imgId: id}); this.loadImage(id); } - handlePrev() { + handlePrev(e) { + e.stopPropagation(); var id = this.state.imgId - 1; if (id < 0) { id = this.props.filenames.length - 1; @@ -50,15 +66,27 @@ export default class ViewImageModal extends React.Component { this.handlePrev(); } } - componentWillReceiveProps(nextProps) { + onModalShown(nextProps) { this.setState({imgId: nextProps.startId}); + this.loadImage(nextProps.startId); + } + onModalHidden() { + if (this.refs.video) { + var video = React.findDOMNode(this.refs.video); + video.pause(); + video.currentTime = 0; + } + } + componentWillReceiveProps(nextProps) { + if (nextProps.show === true && this.props.show === false) { + this.onModalShown(nextProps); + } else if (nextProps.show === false && this.props.show === true) { + this.onModalHidden(); + } } loadImage(id) { var imgHeight = $(window).height() - 100; - if (this.state.loaded[id] || this.state.images[id]) { - $('.modal .modal-image .image-wrapper img').css('max-height', imgHeight); - return; - } + this.setState({imgHeight}); var filename = this.props.filenames[id]; @@ -68,84 +96,27 @@ export default class ViewImageModal extends React.Component { if (fileType === 'image') { var img = new Image(); img.load(this.getPreviewImagePath(filename), - function load() { - var progress = this.state.progress; - progress[id] = img.completedPercentage; - this.setState({progress: progress}); - }.bind(this)); - img.onload = (function onload(imgid) { - return function onloadReturn() { - var loaded = this.state.loaded; - loaded[imgid] = true; - this.setState({loaded: loaded}); - $(React.findDOMNode(this.refs.image)).css('max-height', imgHeight); - }.bind(this); - }.bind(this)(id)); + () => { + const progress = this.state.progress; + progress[id] = img.completedPercentage; + this.setState({progress}); + }); + img.onload = () => { + const loaded = this.state.loaded; + loaded[id] = true; + this.setState({loaded}); + }; var images = this.state.images; images[id] = img; - this.setState({images: images}); + this.setState({images}); } else { // there's nothing to load for non-image files var loaded = this.state.loaded; loaded[id] = true; - this.setState({loaded: loaded}); - } - } - componentDidUpdate() { - if (this.state.loaded[this.state.imgId]) { - if (this.refs.imageWrap) { - $(React.findDOMNode(this.refs.imageWrap)).removeClass('default'); - } + this.setState({loaded}); } } componentDidMount() { - $('#' + this.props.modalId).on('shown.bs.modal', function onModalShow() { - this.setState({viewed: true}); - this.loadImage(this.state.imgId); - }.bind(this)); - - $('#' + this.props.modalId).on('hidden.bs.modal', function onModalHide() { - if (this.refs.video) { - var video = React.findDOMNode(this.refs.video); - video.pause(); - video.currentTime = 0; - } - }.bind(this)); - - $(React.findDOMNode(this.refs.modal)).click(function onModalClick(e) { - if (e.target === this || e.target === React.findDOMNode(this.refs.imageBody)) { - $('.image_modal').modal('hide'); - } - }.bind(this)); - - $(React.findDOMNode(this.refs.imageWrap)).hover( - function onModalHover() { - $(React.findDOMNode(this.refs.imageFooter)).addClass('footer--show'); - }.bind(this), function offModalHover() { - $(React.findDOMNode(this.refs.imageFooter)).removeClass('footer--show'); - }.bind(this) - ); - - if (this.refs.previewArrowLeft) { - $(React.findDOMNode(this.refs.previewArrowLeft)).hover( - function onModalHover() { - $(React.findDOMNode(this.refs.imageFooter)).addClass('footer--show'); - }.bind(this), function offModalHover() { - $(React.findDOMNode(this.refs.imageFooter)).removeClass('footer--show'); - }.bind(this) - ); - } - - if (this.refs.previewArrowRight) { - $(React.findDOMNode(this.refs.previewArrowRight)).hover( - function onModalHover() { - $(React.findDOMNode(this.refs.imageFooter)).addClass('footer--show'); - }.bind(this), function offModalHover() { - $(React.findDOMNode(this.refs.imageFooter)).removeClass('footer--show'); - }.bind(this) - ); - } - $(window).on('keyup', this.handleKeyPress); // keep track of whether or not this component is mounted so we can safely set the state asynchronously @@ -189,6 +160,12 @@ export default class ViewImageModal extends React.Component { // only images have proper previews, so just use a placeholder icon for non-images return Utils.getPreviewImagePathForFileType(fileType); } + onMouseEnterImage() { + this.setState({showFooter: true}); + } + onMouseLeaveImage() { + this.setState({showFooter: false}); + } render() { if (this.props.filenames.length < 1 || this.props.filenames.length - 1 < this.state.imgId) { return
; @@ -299,23 +276,6 @@ export default class ViewImageModal extends React.Component { bgClass = 'black-bg'; } - var publicLink = ''; - if (global.window.config.EnablePublicLink === 'true') { - publicLink = ( - - ); - } - var leftArrow = ''; var rightArrow = ''; if (this.props.filenames.length > 1) { @@ -342,65 +302,61 @@ export default class ViewImageModal extends React.Component { ); } + let closeButtonClass = 'modal-close'; + if (this.state.showFooter) { + closeButtonClass += ' modal-close--show'; + } + return ( -