Merge pull request #426 from ThibG/glitch-soc/features/display-focal-points
Honor focal points when displaying media
This commit is contained in:
		
						commit
						40006bcd03
					
				| 
						 | 
					@ -147,6 +147,11 @@ class Item extends React.PureComponent {
 | 
				
			||||||
      const srcSet = hasSize ? `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w` : null;
 | 
					      const srcSet = hasSize ? `${originalUrl} ${originalWidth}w, ${previewUrl} ${previewWidth}w` : null;
 | 
				
			||||||
      const sizes  = hasSize ? `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw` : null;
 | 
					      const sizes  = hasSize ? `(min-width: 1025px) ${320 * (width / 100)}px, ${width}vw` : null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0;
 | 
				
			||||||
 | 
					      const focusY = attachment.getIn(['meta', 'focus', 'y']) || 0;
 | 
				
			||||||
 | 
					      const x      = ((focusX /  2) + .5) * 100;
 | 
				
			||||||
 | 
					      const y      = ((focusY / -2) + .5) * 100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      thumbnail = (
 | 
					      thumbnail = (
 | 
				
			||||||
        <a
 | 
					        <a
 | 
				
			||||||
          className='media-gallery__item-thumbnail'
 | 
					          className='media-gallery__item-thumbnail'
 | 
				
			||||||
| 
						 | 
					@ -154,7 +159,14 @@ class Item extends React.PureComponent {
 | 
				
			||||||
          onClick={this.handleClick}
 | 
					          onClick={this.handleClick}
 | 
				
			||||||
          target='_blank'
 | 
					          target='_blank'
 | 
				
			||||||
        >
 | 
					        >
 | 
				
			||||||
          <img className={letterbox ? 'letterbox' : null} src={previewUrl} srcSet={srcSet} sizes={sizes} alt={attachment.get('description')} title={attachment.get('description')} />
 | 
					          <img
 | 
				
			||||||
 | 
					            className={letterbox ? 'letterbox' : null}
 | 
				
			||||||
 | 
					            src={previewUrl}
 | 
				
			||||||
 | 
					            srcSet={srcSet}
 | 
				
			||||||
 | 
					            sizes={sizes}
 | 
				
			||||||
 | 
					            alt={attachment.get('description')}
 | 
				
			||||||
 | 
					            title={attachment.get('description')}
 | 
				
			||||||
 | 
					            style={{ objectPosition: `${x}% ${y}%` }} />
 | 
				
			||||||
        </a>
 | 
					        </a>
 | 
				
			||||||
      );
 | 
					      );
 | 
				
			||||||
    } else if (attachment.get('type') === 'gifv') {
 | 
					    } else if (attachment.get('type') === 'gifv') {
 | 
				
			||||||
| 
						 | 
					@ -225,30 +237,59 @@ export default class MediaGallery extends React.PureComponent {
 | 
				
			||||||
    this.props.onOpenMedia(this.props.media, index);
 | 
					    this.props.onOpenMedia(this.props.media, index);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleRef = (node) => {
 | 
				
			||||||
 | 
					    if (node && this.isStandaloneEligible()) {
 | 
				
			||||||
 | 
					      // offsetWidth triggers a layout, so only calculate when we need to
 | 
				
			||||||
 | 
					      this.setState({
 | 
				
			||||||
 | 
					        width: node.offsetWidth,
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  isStandaloneEligible() {
 | 
				
			||||||
 | 
					    const { media, standalone } = this.props;
 | 
				
			||||||
 | 
					    return standalone && media.size === 1 && media.getIn([0, 'meta', 'small', 'aspect']);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
    const {
 | 
					    const { media, intl, sensitive, letterbox, fullwidth } = this.props;
 | 
				
			||||||
      handleClick,
 | 
					    const { width, visible } = this.state;
 | 
				
			||||||
      handleOpen,
 | 
					 | 
				
			||||||
    } = this;
 | 
					 | 
				
			||||||
    const {
 | 
					 | 
				
			||||||
      fullwidth,
 | 
					 | 
				
			||||||
      intl,
 | 
					 | 
				
			||||||
      letterbox,
 | 
					 | 
				
			||||||
      media,
 | 
					 | 
				
			||||||
      sensitive,
 | 
					 | 
				
			||||||
      standalone,
 | 
					 | 
				
			||||||
    } = this.props;
 | 
					 | 
				
			||||||
    const { visible } = this.state;
 | 
					 | 
				
			||||||
    const size = media.take(4).size;
 | 
					    const size = media.take(4).size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let children;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const style = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (this.isStandaloneEligible() && width) {
 | 
				
			||||||
 | 
					      style.height = width / this.props.media.getIn([0, 'meta', 'small', 'aspect']);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!visible) {
 | 
				
			||||||
 | 
					      let warning = <FormattedMessage {...(sensitive ? messages.warning : messages.hidden)} />;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      children = (
 | 
				
			||||||
 | 
					        <button className='media-spoiler' type='button' onClick={this.handleOpen}>
 | 
				
			||||||
 | 
					          <span className='media-spoiler__warning'>{warning}</span>
 | 
				
			||||||
 | 
					          <span className='media-spoiler__trigger'><FormattedMessage {...messages.toggle} /></span>
 | 
				
			||||||
 | 
					        </button>
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      if (this.isStandaloneEligible()) {
 | 
				
			||||||
 | 
					        children = <Item standalone attachment={media.get(0)} onClick={this.handleClick} />;
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} letterbox={letterbox} />);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const computedClass = classNames('media-gallery', `size-${size}`, { 'full-width': fullwidth });
 | 
					    const computedClass = classNames('media-gallery', `size-${size}`, { 'full-width': fullwidth });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div className={computedClass}>
 | 
					      <div className={computedClass} style={style} ref={this.handleRef}>
 | 
				
			||||||
        {visible ? (
 | 
					        {visible ? (
 | 
				
			||||||
          <div className='sensitive-info'>
 | 
					          <div className='sensitive-info'>
 | 
				
			||||||
            <IconButton
 | 
					            <IconButton
 | 
				
			||||||
              icon='eye'
 | 
					              icon='eye'
 | 
				
			||||||
              onClick={handleOpen}
 | 
					              onClick={this.handleOpen}
 | 
				
			||||||
              overlay
 | 
					              overlay
 | 
				
			||||||
              title={intl.formatMessage(messages.toggle_visible)}
 | 
					              title={intl.formatMessage(messages.toggle_visible)}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
| 
						 | 
					@ -259,46 +300,8 @@ export default class MediaGallery extends React.PureComponent {
 | 
				
			||||||
            ) : null}
 | 
					            ) : null}
 | 
				
			||||||
          </div>
 | 
					          </div>
 | 
				
			||||||
        ) : null}
 | 
					        ) : null}
 | 
				
			||||||
        {function () {
 | 
					
 | 
				
			||||||
          switch (true) {
 | 
					        {children}
 | 
				
			||||||
          case !visible:
 | 
					 | 
				
			||||||
            return (
 | 
					 | 
				
			||||||
              <button
 | 
					 | 
				
			||||||
                className='media-spoiler'
 | 
					 | 
				
			||||||
                type='button'
 | 
					 | 
				
			||||||
                onClick={handleOpen}
 | 
					 | 
				
			||||||
              >
 | 
					 | 
				
			||||||
                <span className='media-spoiler__warning'>
 | 
					 | 
				
			||||||
                  <FormattedMessage {...(sensitive ? messages.warning : messages.hidden)} />
 | 
					 | 
				
			||||||
                </span>
 | 
					 | 
				
			||||||
                <span className='media-spoiler__trigger'>
 | 
					 | 
				
			||||||
                  <FormattedMessage {...messages.toggle} />
 | 
					 | 
				
			||||||
                </span>
 | 
					 | 
				
			||||||
              </button>
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
          case standalone && media.size === 1 && !!media.getIn([0, 'meta', 'small', 'aspect']):
 | 
					 | 
				
			||||||
            return (
 | 
					 | 
				
			||||||
              <Item
 | 
					 | 
				
			||||||
                attachment={media.get(0)}
 | 
					 | 
				
			||||||
                onClick={handleClick}
 | 
					 | 
				
			||||||
                standalone
 | 
					 | 
				
			||||||
              />
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
          default:
 | 
					 | 
				
			||||||
            return media.take(4).map(
 | 
					 | 
				
			||||||
              (attachment, i) => (
 | 
					 | 
				
			||||||
                <Item
 | 
					 | 
				
			||||||
                  attachment={attachment}
 | 
					 | 
				
			||||||
                  index={i}
 | 
					 | 
				
			||||||
                  key={attachment.get('id')}
 | 
					 | 
				
			||||||
                  letterbox={letterbox}
 | 
					 | 
				
			||||||
                  onClick={handleClick}
 | 
					 | 
				
			||||||
                  size={size}
 | 
					 | 
				
			||||||
                />
 | 
					 | 
				
			||||||
              )
 | 
					 | 
				
			||||||
            );
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }()}
 | 
					 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			||||||
import Avatar from 'flavours/glitch/components/avatar';
 | 
					import Avatar from 'flavours/glitch/components/avatar';
 | 
				
			||||||
import DisplayName from 'flavours/glitch/components/display_name';
 | 
					import DisplayName from 'flavours/glitch/components/display_name';
 | 
				
			||||||
import StatusContent from 'flavours/glitch/components/status_content';
 | 
					import StatusContent from 'flavours/glitch/components/status_content';
 | 
				
			||||||
import StatusGallery from 'flavours/glitch/components/media_gallery';
 | 
					import MediaGallery from 'flavours/glitch/components/media_gallery';
 | 
				
			||||||
import AttachmentList from 'flavours/glitch/components/attachment_list';
 | 
					import AttachmentList from 'flavours/glitch/components/attachment_list';
 | 
				
			||||||
import { Link } from 'react-router-dom';
 | 
					import { Link } from 'react-router-dom';
 | 
				
			||||||
import { FormattedDate, FormattedNumber } from 'react-intl';
 | 
					import { FormattedDate, FormattedNumber } from 'react-intl';
 | 
				
			||||||
| 
						 | 
					@ -69,7 +69,8 @@ export default class DetailedStatus extends ImmutablePureComponent {
 | 
				
			||||||
        mediaIcon = 'video-camera';
 | 
					        mediaIcon = 'video-camera';
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
        media = (
 | 
					        media = (
 | 
				
			||||||
          <StatusGallery
 | 
					          <MediaGallery
 | 
				
			||||||
 | 
					            standalone
 | 
				
			||||||
            sensitive={status.get('sensitive')}
 | 
					            sensitive={status.get('sensitive')}
 | 
				
			||||||
            media={status.get('media_attachments')}
 | 
					            media={status.get('media_attachments')}
 | 
				
			||||||
            letterbox={settings.getIn(['media', 'letterbox'])}
 | 
					            letterbox={settings.getIn(['media', 'letterbox'])}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,7 +29,7 @@ const formatTime = secondsNum => {
 | 
				
			||||||
  return (hours === '00' ? '' : `${hours}:`) + `${minutes}:${seconds}`;
 | 
					  return (hours === '00' ? '' : `${hours}:`) + `${minutes}:${seconds}`;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const findElementPosition = el => {
 | 
					export const findElementPosition = el => {
 | 
				
			||||||
  let box;
 | 
					  let box;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (el.getBoundingClientRect && el.parentNode) {
 | 
					  if (el.getBoundingClientRect && el.parentNode) {
 | 
				
			||||||
| 
						 | 
					@ -60,7 +60,7 @@ const findElementPosition = el => {
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getPointerPosition = (el, event) => {
 | 
					export const getPointerPosition = (el, event) => {
 | 
				
			||||||
  const position = {};
 | 
					  const position = {};
 | 
				
			||||||
  const box = findElementPosition(el);
 | 
					  const box = findElementPosition(el);
 | 
				
			||||||
  const boxW = el.offsetWidth;
 | 
					  const boxW = el.offsetWidth;
 | 
				
			||||||
| 
						 | 
					@ -76,7 +76,7 @@ const getPointerPosition = (el, event) => {
 | 
				
			||||||
    pageY = event.changedTouches[0].pageY;
 | 
					    pageY = event.changedTouches[0].pageY;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  position.y = Math.max(0, Math.min(1, ((boxY - pageY) + boxH) / boxH));
 | 
					  position.y = Math.max(0, Math.min(1, (pageY - boxY) / boxH));
 | 
				
			||||||
  position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));
 | 
					  position.x = Math.max(0, Math.min(1, (pageX - boxX) / boxW));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return position;
 | 
					  return position;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue