about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/javascript/flavours/glitch/components/media_gallery.js123
-rw-r--r--app/javascript/flavours/glitch/features/status/components/detailed_status.js5
-rw-r--r--app/javascript/flavours/glitch/features/video/index.js6
3 files changed, 69 insertions, 65 deletions
diff --git a/app/javascript/flavours/glitch/components/media_gallery.js b/app/javascript/flavours/glitch/components/media_gallery.js
index 309308d25..5169ead5a 100644
--- a/app/javascript/flavours/glitch/components/media_gallery.js
+++ b/app/javascript/flavours/glitch/components/media_gallery.js
@@ -136,16 +136,21 @@ class Item extends React.PureComponent {
     let thumbnail = '';
 
     if (attachment.get('type') === 'image') {
-      const previewUrl = attachment.get('preview_url');
+      const previewUrl   = attachment.get('preview_url');
       const previewWidth = attachment.getIn(['meta', 'small', 'width']);
 
-      const originalUrl = attachment.get('url');
-      const originalWidth = attachment.getIn(['meta', 'original', 'width']);
+      const originalUrl    = attachment.get('url');
+      const originalWidth  = attachment.getIn(['meta', 'original', 'width']);
 
       const hasSize = typeof originalWidth === 'number' && typeof previewWidth === 'number';
 
       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 = (
         <a
@@ -154,7 +159,14 @@ class Item extends React.PureComponent {
           onClick={this.handleClick}
           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>
       );
     } else if (attachment.get('type') === 'gifv') {
@@ -225,30 +237,59 @@ export default class MediaGallery extends React.PureComponent {
     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 () {
-    const {
-      handleClick,
-      handleOpen,
-    } = this;
-    const {
-      fullwidth,
-      intl,
-      letterbox,
-      media,
-      sensitive,
-      standalone,
-    } = this.props;
-    const { visible } = this.state;
+    const { media, intl, sensitive, letterbox, fullwidth } = this.props;
+    const { width, visible } = this.state;
     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 });
 
     return (
-      <div className={computedClass}>
+      <div className={computedClass} style={style} ref={this.handleRef}>
         {visible ? (
           <div className='sensitive-info'>
             <IconButton
               icon='eye'
-              onClick={handleOpen}
+              onClick={this.handleOpen}
               overlay
               title={intl.formatMessage(messages.toggle_visible)}
             />
@@ -259,46 +300,8 @@ export default class MediaGallery extends React.PureComponent {
             ) : null}
           </div>
         ) : null}
-        {function () {
-          switch (true) {
-          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}
-                />
-              )
-            );
-          }
-        }()}
+
+        {children}
       </div>
     );
   }
diff --git a/app/javascript/flavours/glitch/features/status/components/detailed_status.js b/app/javascript/flavours/glitch/features/status/components/detailed_status.js
index e7c26d013..066499da8 100644
--- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js
+++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js
@@ -4,7 +4,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import Avatar from 'flavours/glitch/components/avatar';
 import DisplayName from 'flavours/glitch/components/display_name';
 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 { Link } from 'react-router-dom';
 import { FormattedDate, FormattedNumber } from 'react-intl';
@@ -69,7 +69,8 @@ export default class DetailedStatus extends ImmutablePureComponent {
         mediaIcon = 'video-camera';
       } else {
         media = (
-          <StatusGallery
+          <MediaGallery
+            standalone
             sensitive={status.get('sensitive')}
             media={status.get('media_attachments')}
             letterbox={settings.getIn(['media', 'letterbox'])}
diff --git a/app/javascript/flavours/glitch/features/video/index.js b/app/javascript/flavours/glitch/features/video/index.js
index 56ee9c20c..4cb5db7f5 100644
--- a/app/javascript/flavours/glitch/features/video/index.js
+++ b/app/javascript/flavours/glitch/features/video/index.js
@@ -29,7 +29,7 @@ const formatTime = secondsNum => {
   return (hours === '00' ? '' : `${hours}:`) + `${minutes}:${seconds}`;
 };
 
-const findElementPosition = el => {
+export const findElementPosition = el => {
   let box;
 
   if (el.getBoundingClientRect && el.parentNode) {
@@ -60,7 +60,7 @@ const findElementPosition = el => {
   };
 };
 
-const getPointerPosition = (el, event) => {
+export const getPointerPosition = (el, event) => {
   const position = {};
   const box = findElementPosition(el);
   const boxW = el.offsetWidth;
@@ -76,7 +76,7 @@ const getPointerPosition = (el, event) => {
     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));
 
   return position;