about summary refs log tree commit diff
diff options
context:
space:
mode:
-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
-rw-r--r--app/javascript/flavours/glitch/styles/components/status.scss10
-rw-r--r--app/javascript/styles/mastodon/reset.scss5
-rw-r--r--app/views/admin/reports/_report.html.haml22
-rw-r--r--app/views/admin/reports/index.html.haml1
7 files changed, 94 insertions, 78 deletions
diff --git a/app/javascript/flavours/glitch/components/media_gallery.js b/app/javascript/flavours/glitch/components/media_gallery.js
index 603aebc86..925132b07 100644
--- a/app/javascript/flavours/glitch/components/media_gallery.js
+++ b/app/javascript/flavours/glitch/components/media_gallery.js
@@ -132,16 +132,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
@@ -150,7 +155,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') {
@@ -221,30 +233,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)}
             />
@@ -255,46 +296,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;
diff --git a/app/javascript/flavours/glitch/styles/components/status.scss b/app/javascript/flavours/glitch/styles/components/status.scss
index 1d1cf0f9e..0a022802a 100644
--- a/app/javascript/flavours/glitch/styles/components/status.scss
+++ b/app/javascript/flavours/glitch/styles/components/status.scss
@@ -244,9 +244,17 @@
       height: 20px;
       overflow: hidden;
       text-overflow: ellipsis;
-      margin: 0;
       padding-top: 0;
 
+      &:after {
+        content: "";
+        position: absolute;
+        top: 0; bottom: 0;
+        left: 0; right: 0;
+        background: linear-gradient(rgba($ui-base-color, 0), rgba($ui-base-color, 1));
+        pointer-events: none;
+      }
+      
       a:hover {
         text-decoration: none;
       }
diff --git a/app/javascript/styles/mastodon/reset.scss b/app/javascript/styles/mastodon/reset.scss
index 58d4de35f..ff3b2c022 100644
--- a/app/javascript/styles/mastodon/reset.scss
+++ b/app/javascript/styles/mastodon/reset.scss
@@ -53,6 +53,11 @@ table {
   border-spacing: 0;
 }
 
+::-webkit-scrollbar {
+  width: 12px;
+  height: 12px;
+}
+
 ::-webkit-scrollbar-thumb {
   background: lighten($ui-base-color, 4%);
   border: 0px none $base-border-color;
diff --git a/app/views/admin/reports/_report.html.haml b/app/views/admin/reports/_report.html.haml
index d266f4840..84db00ad5 100644
--- a/app/views/admin/reports/_report.html.haml
+++ b/app/views/admin/reports/_report.html.haml
@@ -5,18 +5,18 @@
     = link_to report.target_account.acct, admin_account_path(report.target_account.id)
   %td.reporter
     = link_to report.account.acct, admin_account_path(report.account.id)
-  %td.comment
-    %span{ title: report.comment }
+  %td
+    %div{ title: report.comment }
       = truncate(report.comment, length: 30, separator: ' ')
-  %td.stats
-    - unless report.statuses.empty?
-      %span{ title: t('admin.accounts.statuses') }
-        = fa_icon('comment')
-        = report.statuses.count
-    - unless report.media_attachments.empty?
-      %span{ title: t('admin.accounts.media_attachments') }
-        = fa_icon('camera')
-        = report.media_attachments.count
+    %div
+      - unless report.statuses.empty?
+        %span{ title: t('admin.accounts.statuses') }
+          = fa_icon('comment')
+          = report.statuses.count
+      - unless report.media_attachments.empty?
+        %span{ title: t('admin.accounts.media_attachments') }
+          = fa_icon('camera')
+          = report.media_attachments.count
   %td
     - if report.assigned_account.nil?
       \-
diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml
index 3b127c4fc..c3baaf6be 100644
--- a/app/views/admin/reports/index.html.haml
+++ b/app/views/admin/reports/index.html.haml
@@ -18,7 +18,6 @@
           %th= t('admin.reports.id')
           %th= t('admin.reports.target')
           %th= t('admin.reports.reported_by')
-          %th= t('admin.reports.comment.label')
           %th= t('admin.reports.report_contents')
           %th= t('admin.reports.assigned')
           %th