about summary refs log tree commit diff
path: root/app/javascript
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2020-06-24 10:25:32 +0200
committerThibG <thib@sitedethib.com>2020-07-02 17:31:09 +0200
commit06309129be80c549aacf19a01e9b36bdcd47f9f2 (patch)
treed5d4b705ddcbb73f358a7730a1333766d1cd6553 /app/javascript
parent1c5842083148121aa8eda3fe949f29d3f7bc1d11 (diff)
[Glitch] Fix audio/video/images/cards not reacting to window resizes in web UI
Port bb9ca8a587ee5a3ec8778e72828aca0ba8871327 to glitch-soc

Co-authored-by: Yamagishi Kazutoshi <ykzts@desire.sh>

Co-authored-by: Yamagishi Kazutoshi <ykzts@desire.sh>
Signed-off-by: Thibaut Girka <thib@sitedethib.com>
Diffstat (limited to 'app/javascript')
-rw-r--r--app/javascript/flavours/glitch/components/media_gallery.js35
-rw-r--r--app/javascript/flavours/glitch/features/audio/index.js56
-rw-r--r--app/javascript/flavours/glitch/features/status/components/card.js33
-rw-r--r--app/javascript/flavours/glitch/features/video/index.js31
-rw-r--r--app/javascript/flavours/glitch/styles/components/media.scss19
5 files changed, 144 insertions, 30 deletions
diff --git a/app/javascript/flavours/glitch/components/media_gallery.js b/app/javascript/flavours/glitch/components/media_gallery.js
index 973406ad2..71240530c 100644
--- a/app/javascript/flavours/glitch/components/media_gallery.js
+++ b/app/javascript/flavours/glitch/components/media_gallery.js
@@ -8,6 +8,7 @@ import { isIOS } from 'flavours/glitch/util/is_mobile';
 import classNames from 'classnames';
 import { autoPlayGif, displayMedia, useBlurhash } from 'flavours/glitch/util/initial_state';
 import { decode } from 'blurhash';
+import { debounce } from 'lodash';
 
 const messages = defineMessages({
   hidden: {
@@ -289,6 +290,14 @@ class MediaGallery extends React.PureComponent {
     width: this.props.defaultWidth,
   };
 
+  componentDidMount () {
+    window.addEventListener('resize', this.handleResize, { passive: true });
+  }
+
+  componentWillUnmount () {
+    window.removeEventListener('resize', this.handleResize);
+  }
+
   componentWillReceiveProps (nextProps) {
     if (!is(nextProps.media, this.props.media) && nextProps.visible === undefined) {
       this.setState({ visible: displayMedia !== 'hide_all' && !nextProps.sensitive || displayMedia === 'show_all' });
@@ -305,6 +314,14 @@ class MediaGallery extends React.PureComponent {
     }
   }
 
+  handleResize = debounce(() => {
+    if (this.node) {
+      this._setDimensions();
+    }
+  }, 250, {
+    trailing: true,
+  });
+
   handleOpen = () => {
     if (this.props.onToggleVisibility) {
       this.props.onToggleVisibility();
@@ -319,11 +336,23 @@ class MediaGallery extends React.PureComponent {
 
   handleRef = (node) => {
     this.node = node;
-    if (node && node.offsetWidth && node.offsetWidth != this.state.width) {
-      if (this.props.cacheWidth) this.props.cacheWidth(node.offsetWidth);
+
+    if (this.node) {
+      this._setDimensions();
+    }
+  }
+
+  _setDimensions () {
+    const width = this.node.offsetWidth;
+ 
+    if (width && width != this.state.width) {
+      // offsetWidth triggers a layout, so only calculate when we need to
+      if (this.props.cacheWidth) {
+        this.props.cacheWidth(width);
+      }
 
       this.setState({
-        width: node.offsetWidth,
+        width: width,
       });
     }
   }
diff --git a/app/javascript/flavours/glitch/features/audio/index.js b/app/javascript/flavours/glitch/features/audio/index.js
index 995586a24..9d81fbff7 100644
--- a/app/javascript/flavours/glitch/features/audio/index.js
+++ b/app/javascript/flavours/glitch/features/audio/index.js
@@ -7,6 +7,7 @@ import classNames from 'classnames';
 import { throttle } from 'lodash';
 import { encode, decode } from 'blurhash';
 import { getPointerPosition, fileNameFromURL } from 'flavours/glitch/features/video';
+import { debounce } from 'lodash';
 
 const digitCharacters = [
   '0',
@@ -172,16 +173,20 @@ class Audio extends React.PureComponent {
   setPlayerRef = c => {
     this.player = c;
 
-    if (c) {
-      const width  = c.offsetWidth;
-      const height = width / (16/9);
+    if (this.player) {
+      this._setDimensions();
+    }
+  }
 
-      if (this.props.cacheWidth) {
-        this.props.cacheWidth(width);
-      }
+  _setDimensions () {
+    const width  = this.player.offsetWidth;
+    const height = width / (16/9);
 
-      this.setState({ width, height });
+    if (this.props.cacheWidth) {
+      this.props.cacheWidth(width);
     }
+
+    this.setState({ width, height });
   }
 
   setSeekRef = c => {
@@ -213,6 +218,8 @@ class Audio extends React.PureComponent {
   }
  
   componentDidMount () {
+    window.addEventListener('resize', this.handleResize, { passive: true });
+
     const img = new Image();
     img.crossOrigin = 'anonymous';
     img.onload = () => this.handlePosterLoad(img);
@@ -239,6 +246,10 @@ class Audio extends React.PureComponent {
     this._draw();
   }
 
+  componentWillUnmount () {
+    window.removeEventListener('resize', this.handleResize);
+  }
+
   togglePlay = () => {
     if (this.state.paused) {
       this.setState({ paused: false }, () => this.audio.play());
@@ -247,6 +258,14 @@ class Audio extends React.PureComponent {
     }
   }
 
+  handleResize = debounce(() => {
+    if (this.player) {
+      this._setDimensions();
+    }
+  }, 250, {
+    trailing: true,
+  });
+
   handlePlay = () => {
     this.setState({ paused: false });
 
@@ -545,14 +564,13 @@ class Audio extends React.PureComponent {
   }
 
   _drawTick (x1, y1, x2, y2) {
-    const radius = this._getRadius();
-    const cx = parseInt(this.state.width / 2);
-    const cy = parseInt(radius + (PADDING * this._getScaleCoefficient()));
+    const cx = this._getCX();
+    const cy = this._getCY();
 
-    const dx1 = parseInt(cx + x1);
-    const dy1 = parseInt(cy + y1);
-    const dx2 = parseInt(cx + x2);
-    const dy2 = parseInt(cy + y2);
+    const dx1 = Math.ceil(cx + x1);
+    const dy1 = Math.ceil(cy + y1);
+    const dx2 = Math.ceil(cx + x2);
+    const dy2 = Math.ceil(cy + y2);
 
     const gradient = this.canvasContext.createLinearGradient(dx1, dy1, dx2, dy2);
 
@@ -571,6 +589,14 @@ class Audio extends React.PureComponent {
     this.canvasContext.stroke();
   }
 
+  _getCX() {
+    return Math.floor(this.state.width / 2);
+  }
+
+  _getCY() {
+    return Math.floor(this._getRadius() + (PADDING * this._getScaleCoefficient()));
+  }
+
   _getColor () {
     return `rgb(${this.state.color.r}, ${this.state.color.g}, ${this.state.color.b})`;
   }
@@ -619,7 +645,7 @@ class Audio extends React.PureComponent {
           alt=''
           width={(this._getRadius() - TICK_SIZE) * 2}
           height={(this._getRadius() - TICK_SIZE) * 2}
-          style={{ position: 'absolute', left: parseInt(this.state.width / 2), top: parseInt(this._getRadius() + (PADDING * this._getScaleCoefficient())), transform: 'translate(-50%, -50%)', borderRadius: '50%', pointerEvents: 'none' }}
+          style={{ position: 'absolute', left: this._getCX(), top: this._getCY(), transform: 'translate(-50%, -50%)', borderRadius: '50%', pointerEvents: 'none' }}
         />
 
         <div className='video-player__seek' onMouseDown={this.handleMouseDown} ref={this.setSeekRef}>
diff --git a/app/javascript/flavours/glitch/features/status/components/card.js b/app/javascript/flavours/glitch/features/status/components/card.js
index d2de225c0..4b6676062 100644
--- a/app/javascript/flavours/glitch/features/status/components/card.js
+++ b/app/javascript/flavours/glitch/features/status/components/card.js
@@ -9,6 +9,7 @@ import { decode as decodeIDNA } from 'flavours/glitch/util/idna';
 import Icon from 'flavours/glitch/components/icon';
 import { useBlurhash } from 'flavours/glitch/util/initial_state';
 import { decode } from 'blurhash';
+import { debounce } from 'lodash';
 
 const getHostname = url => {
   const parser = document.createElement('a');
@@ -83,13 +84,20 @@ export default class Card extends React.PureComponent {
   }
 
   componentDidMount () {
+    window.addEventListener('resize', this.handleResize, { passive: true });
+
     if (this.props.card && this.props.card.get('blurhash')) {
       this._decode();
     }
   }
 
+  componentWillUnmount () {
+    window.removeEventListener('resize', this.handleResize);
+  }
+
   componentDidUpdate (prevProps) {
     const { card } = this.props;
+
     if (card.get('blurhash') && (!prevProps.card || prevProps.card.get('blurhash') !== card.get('blurhash'))) {
       this._decode();
     }
@@ -109,6 +117,24 @@ export default class Card extends React.PureComponent {
     }
   }
 
+  _setDimensions () {
+    const width = this.node.offsetWidth;
+
+    if (this.props.cacheWidth) {
+      this.props.cacheWidth(width);
+    }
+
+    this.setState({ width });
+  }
+
+  handleResize = debounce(() => {
+    if (this.node) {
+      this._setDimensions();
+    }
+  }, 250, {
+    trailing: true,
+  });
+
   handlePhotoClick = () => {
     const { card, onOpenMedia } = this.props;
 
@@ -141,9 +167,10 @@ export default class Card extends React.PureComponent {
   }
 
   setRef = c => {
-    if (c) {
-      if (this.props.cacheWidth) this.props.cacheWidth(c.offsetWidth);
-      this.setState({ width: c.offsetWidth });
+    this.node = c;
+
+    if (this.node) {
+      this._setDimensions();
     }
   }
 
diff --git a/app/javascript/flavours/glitch/features/video/index.js b/app/javascript/flavours/glitch/features/video/index.js
index 747d03f84..c7cb5bc43 100644
--- a/app/javascript/flavours/glitch/features/video/index.js
+++ b/app/javascript/flavours/glitch/features/video/index.js
@@ -2,7 +2,7 @@ import React from 'react';
 import PropTypes from 'prop-types';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import { fromJS, is } from 'immutable';
-import { throttle } from 'lodash';
+import { throttle, debounce } from 'lodash';
 import classNames from 'classnames';
 import { isFullscreen, requestFullscreen, exitFullscreen } from 'flavours/glitch/util/fullscreen';
 import { displayMedia, useBlurhash } from 'flavours/glitch/util/initial_state';
@@ -144,10 +144,21 @@ class Video extends React.PureComponent {
   setPlayerRef = c => {
     this.player = c;
 
-    if (c && c.offsetWidth && c.offsetWidth != this.state.containerWidth) {
-      if (this.props.cacheWidth) this.props.cacheWidth(this.player.offsetWidth);
+    if (this.player) {
+      this._setDimensions();
+    }
+  }
+
+  _setDimensions () {
+    const width = this.player.offsetWidth;
+
+    if (width && width != this.state.containerWidth) {
+      if (this.props.cacheWidth) {
+        this.props.cacheWidth(width);
+      }
+
       this.setState({
-        containerWidth: c.offsetWidth,
+        containerWidth: width,
       });
     }
   }
@@ -293,12 +304,16 @@ class Video extends React.PureComponent {
     document.addEventListener('mozfullscreenchange', this.handleFullscreenChange, true);
     document.addEventListener('MSFullscreenChange', this.handleFullscreenChange, true);
 
+    window.addEventListener('resize', this.handleResize, { passive: true });
+
     if (this.props.blurhash) {
       this._decode();
     }
   }
 
   componentWillUnmount () {
+    window.removeEventListener('resize', this.handleResize);
+
     document.removeEventListener('fullscreenchange', this.handleFullscreenChange, true);
     document.removeEventListener('webkitfullscreenchange', this.handleFullscreenChange, true);
     document.removeEventListener('mozfullscreenchange', this.handleFullscreenChange, true);
@@ -334,6 +349,14 @@ class Video extends React.PureComponent {
     }
   }
 
+  handleResize = debounce(() => {
+    if (this.player) {
+      this._setDimensions();
+    }
+  }, 250, {
+    trailing: true,
+  });
+
   handleFullscreenChange = () => {
     this.setState({ fullscreen: isFullscreen() });
   }
diff --git a/app/javascript/flavours/glitch/styles/components/media.scss b/app/javascript/flavours/glitch/styles/components/media.scss
index 88046a7a2..5f6eff808 100644
--- a/app/javascript/flavours/glitch/styles/components/media.scss
+++ b/app/javascript/flavours/glitch/styles/components/media.scss
@@ -612,7 +612,7 @@
     &.active {
       overflow: visible;
       width: 50px;
-      margin-right: 10px;
+      margin-right: 16px;
     }
 
     &::before {
@@ -649,10 +649,17 @@
       left: 0;
       margin-left: -6px;
       transform: translate(0, -50%);
-      transition: opacity .1s ease;
       background: lighten($ui-highlight-color, 8%);
       box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
-      pointer-events: none;
+      opacity: 0;
+
+      .no-reduce-motion & {
+        transition: opacity 100ms linear;
+      }
+    }
+
+    &.active &__handle {
+      opacity: 1;
     }
   }
 
@@ -712,10 +719,12 @@
       height: 12px;
       top: 6px;
       margin-left: -6px;
-      transition: opacity .1s ease;
       background: lighten($ui-highlight-color, 8%);
       box-shadow: 1px 2px 6px rgba($base-shadow-color, 0.2);
-      pointer-events: none;
+
+      .no-reduce-motion & {
+        transition: opacity .1s ease;
+      }
 
       &.active {
         opacity: 1;