about summary refs log tree commit diff
path: root/app/javascript/flavours
diff options
context:
space:
mode:
Diffstat (limited to 'app/javascript/flavours')
-rw-r--r--app/javascript/flavours/glitch/components/intersection_observer_article.js29
-rw-r--r--app/javascript/flavours/glitch/components/media_gallery.js10
-rw-r--r--app/javascript/flavours/glitch/components/scrollable_list.js14
-rw-r--r--app/javascript/flavours/glitch/components/status.js37
-rw-r--r--app/javascript/flavours/glitch/features/notifications/components/notification.js12
-rw-r--r--app/javascript/flavours/glitch/features/status/components/card.js7
-rw-r--r--app/javascript/flavours/glitch/features/video/index.js5
7 files changed, 80 insertions, 34 deletions
diff --git a/app/javascript/flavours/glitch/components/intersection_observer_article.js b/app/javascript/flavours/glitch/components/intersection_observer_article.js
index 6eeca5598..900c98638 100644
--- a/app/javascript/flavours/glitch/components/intersection_observer_article.js
+++ b/app/javascript/flavours/glitch/components/intersection_observer_article.js
@@ -63,7 +63,7 @@ export default class IntersectionObserverArticle extends ImmutablePureComponent
   }
 
   updateStateAfterIntersection = (prevState) => {
-    if (prevState.isIntersecting && !this.entry.isIntersecting) {
+    if (prevState.isIntersecting !== false && !this.entry.isIntersecting) {
       scheduleIdleTask(this.hideIfNotIntersecting);
     }
     return {
@@ -103,24 +103,23 @@ export default class IntersectionObserverArticle extends ImmutablePureComponent
     const { children, id, index, listLength, cachedHeight } = this.props;
     const { isIntersecting, isHidden } = this.state;
 
+    const style = {};
+
     if (!isIntersecting && (isHidden || cachedHeight)) {
-      return (
-        <article
-          ref={this.handleRef}
-          aria-posinset={index + 1}
-          aria-setsize={listLength}
-          style={{ height: `${this.height || cachedHeight}px`, opacity: 0, overflow: 'hidden' }}
-          data-id={id}
-          tabIndex='0'
-        >
-          {children && React.cloneElement(children, { hidden: true })}
-        </article>
-      );
+      style.height = `${this.height || cachedHeight || 150}px`;
+      style.opacity = 0;
+      style.overflow = 'hidden';
     }
 
     return (
-      <article ref={this.handleRef} aria-posinset={index + 1} aria-setsize={listLength} data-id={id} tabIndex='0'>
-        {children && React.cloneElement(children, { hidden: false })}
+      <article
+        ref={this.handleRef}
+        aria-posinset={index + 1}
+        aria-setsize={listLength}
+        data-id={id}
+        tabIndex='0'
+        style={style}>
+          {children && React.cloneElement(children, { hidden: !isIntersecting && (isHidden || cachedHeight) })}
       </article>
     );
   }
diff --git a/app/javascript/flavours/glitch/components/media_gallery.js b/app/javascript/flavours/glitch/components/media_gallery.js
index d0226bbbb..1fa25ee4e 100644
--- a/app/javascript/flavours/glitch/components/media_gallery.js
+++ b/app/javascript/flavours/glitch/components/media_gallery.js
@@ -224,6 +224,8 @@ export default class MediaGallery extends React.PureComponent {
     size: PropTypes.object,
     onOpenMedia: PropTypes.func.isRequired,
     intl: PropTypes.object.isRequired,
+    defaultWidth: PropTypes.number,
+    cacheWidth: PropTypes.func,
   };
 
   static defaultProps = {
@@ -232,6 +234,7 @@ export default class MediaGallery extends React.PureComponent {
 
   state = {
     visible: this.props.revealed === undefined ? (displayMedia !== 'hide_all' && !this.props.sensitive || displayMedia === 'show_all') : this.props.revealed,
+    width: this.props.defaultWidth,
   };
 
   componentWillReceiveProps (nextProps) {
@@ -259,6 +262,7 @@ export default 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);
       this.setState({
         width: node.offsetWidth,
       });
@@ -271,10 +275,12 @@ export default class MediaGallery extends React.PureComponent {
   }
 
   render () {
-    const { media, intl, sensitive, letterbox, fullwidth } = this.props;
-    const { width, visible } = this.state;
+    const { media, intl, sensitive, letterbox, fullwidth, defaultWidth } = this.props;
+    const { visible } = this.state;
     const size = media.take(4).size;
 
+    const width = this.state.width || defaultWidth;
+
     let children;
 
     const style = {};
diff --git a/app/javascript/flavours/glitch/components/scrollable_list.js b/app/javascript/flavours/glitch/components/scrollable_list.js
index 7cd0774a9..462185bbc 100644
--- a/app/javascript/flavours/glitch/components/scrollable_list.js
+++ b/app/javascript/flavours/glitch/components/scrollable_list.js
@@ -40,6 +40,7 @@ export default class ScrollableList extends PureComponent {
 
   state = {
     fullscreen: null,
+    cachedMediaWidth: 300,
   };
 
   intersectionObserverWrapper = new IntersectionObserverWrapper();
@@ -128,7 +129,7 @@ export default class ScrollableList extends PureComponent {
   }
 
   getScrollPosition = () => {
-    if (this.node && this.node.scrollTop > 0) {
+    if (this.node && (this.node.scrollTop > 0 || this.mouseMovedRecently)) {
       return {height: this.node.scrollHeight, top: this.node.scrollTop};
     } else {
       return null;
@@ -141,6 +142,10 @@ export default class ScrollableList extends PureComponent {
     this.setScrollTop(newScrollTop);
   }
 
+  cacheMediaWidth = (width) => {
+    if (width && this.state.cachedMediaWidth != width) this.setState({ cachedMediaWidth: width });
+  }
+
   getSnapshotBeforeUpdate (prevProps, prevState) {
     const someItemInserted = React.Children.count(prevProps.children) > 0 &&
       React.Children.count(prevProps.children) < React.Children.count(this.props.children) &&
@@ -252,7 +257,12 @@ export default class ScrollableList extends PureComponent {
                 intersectionObserverWrapper={this.intersectionObserverWrapper}
                 saveHeightKey={trackScroll ? `${this.context.router.route.location.key}:${scrollKey}` : null}
               >
-                {React.cloneElement(child, {getScrollPosition: this.getScrollPosition, updateScrollBottom: this.updateScrollBottom})}
+                {React.cloneElement(child, {
+                  getScrollPosition: this.getScrollPosition,
+                  updateScrollBottom: this.updateScrollBottom,
+                  cachedMediaWidth: this.state.cachedMediaWidth,
+                  cacheMediaWidth: this.cacheMediaWidth,
+                })}
               </IntersectionObserverArticleContainer>
             ))}
 
diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js
index 9ff53485e..51fee0eec 100644
--- a/app/javascript/flavours/glitch/components/status.js
+++ b/app/javascript/flavours/glitch/components/status.js
@@ -72,6 +72,8 @@ export default class Status extends ImmutablePureComponent {
     updateScrollBottom: PropTypes.func,
     expanded: PropTypes.bool,
     intl: PropTypes.object.isRequired,
+    cacheMediaWidth: PropTypes.func,
+    cachedMediaWidth: PropTypes.number,
   };
 
   state = {
@@ -214,6 +216,8 @@ export default class Status extends ImmutablePureComponent {
       // Hack to fix timeline jumps on second rendering when auto-collapsing
       this.setState({ autoCollapsed: true });
     }
+
+    this.didShowCard  = !this.props.muted && !this.props.hidden && this.props.status.get('card') && this.props.settings.get('inline_preview_cards');
   }
 
   getSnapshotBeforeUpdate (prevProps, prevState) {
@@ -226,8 +230,10 @@ export default class Status extends ImmutablePureComponent {
 
   //  Hack to fix timeline jumps on second rendering when auto-collapsing
   componentDidUpdate (prevProps, prevState, snapshot) {
-    if (this.state.autoCollapsed) {
-      this.setState({ autoCollapsed: false });
+    const doShowCard  = !this.props.muted && !this.props.hidden && this.props.status.get('card') && this.props.settings.get('inline_preview_cards');
+    if (this.state.autoCollapsed || (doShowCard && !this.didShowCard)) {
+      if (doShowCard) this.didShowCard = true;
+      if (this.state.autoCollapsed) this.setState({ autoCollapsed: false });
       if (snapshot !== null && this.props.updateScrollBottom) {
         if (this.node.offsetTop < snapshot.top) {
           this.props.updateScrollBottom(snapshot.height - snapshot.top);
@@ -236,6 +242,15 @@ export default class Status extends ImmutablePureComponent {
     }
   }
 
+  componentWillUnmount() {
+    if (this.node && this.props.getScrollPosition) {
+      const position = this.props.getScrollPosition();
+      if (position !== null && this.node.offsetTop < position.top) {
+         requestAnimationFrame(() => { this.props.updateScrollBottom(position.height - position.top); });
+      }
+    }
+  }
+
   //  `setCollapsed()` sets the value of `isCollapsed` in our state, that is,
   //  whether the toot is collapsed or not.
 
@@ -384,15 +399,7 @@ export default class Status extends ImmutablePureComponent {
 
     if (hidden) {
       return (
-        <div
-          ref={this.handleRef}
-          data-id={status.get('id')}
-          style={{
-            height: `${this.height}px`,
-            opacity: 0,
-            overflow: 'hidden',
-          }}
-        >
+        <div ref={this.handleRef}>
           {status.getIn(['account', 'display_name']) || status.getIn(['account', 'username'])}
           {' '}
           {status.get('content')}
@@ -408,7 +415,7 @@ export default class Status extends ImmutablePureComponent {
 
       return (
         <HotKeys handlers={minHandlers}>
-          <div className='status__wrapper status__wrapper--filtered focusable' tabIndex='0'>
+          <div className='status__wrapper status__wrapper--filtered focusable' tabIndex='0' ref={this.handleRef}>
             <FormattedMessage id='status.filtered' defaultMessage='Filtered' />
           </div>
         </HotKeys>
@@ -453,6 +460,8 @@ export default class Status extends ImmutablePureComponent {
               fullwidth={settings.getIn(['media', 'fullwidth'])}
               preventPlayback={isCollapsed || !isExpanded}
               onOpenVideo={this.handleOpenVideo}
+              width={this.props.cachedMediaWidth}
+              cacheWidth={this.props.cacheMediaWidth}
             />)}
           </Bundle>
         );
@@ -468,6 +477,8 @@ export default class Status extends ImmutablePureComponent {
                 fullwidth={settings.getIn(['media', 'fullwidth'])}
                 hidden={isCollapsed || !isExpanded}
                 onOpenMedia={this.props.onOpenMedia}
+                cacheWidth={this.props.cacheMediaWidth}
+                defaultWidth={this.props.cachedMediaWidth}
               />
             )}
           </Bundle>
@@ -484,6 +495,8 @@ export default class Status extends ImmutablePureComponent {
           onOpenMedia={this.props.onOpenMedia}
           card={status.get('card')}
           compact
+          cacheWidth={this.props.cacheMediaWidth}
+          defaultWidth={this.props.cachedMediaWidth}
         />
       );
       mediaIcon = 'link';
diff --git a/app/javascript/flavours/glitch/features/notifications/components/notification.js b/app/javascript/flavours/glitch/features/notifications/components/notification.js
index 21c55accc..daafe3507 100644
--- a/app/javascript/flavours/glitch/features/notifications/components/notification.js
+++ b/app/javascript/flavours/glitch/features/notifications/components/notification.js
@@ -18,6 +18,9 @@ export default class Notification extends ImmutablePureComponent {
     onMention: PropTypes.func.isRequired,
     getScrollPosition: PropTypes.func,
     updateScrollBottom: PropTypes.func,
+    cacheMediaWidth: PropTypes.func,
+    cachedMediaWidth: PropTypes.number,
+    onUnmount: PropTypes.func,
   };
 
   render () {
@@ -57,6 +60,9 @@ export default class Notification extends ImmutablePureComponent {
           contextType='notifications'
           getScrollPosition={getScrollPosition}
           updateScrollBottom={updateScrollBottom}
+          cachedMediaWidth={this.props.cachedMediaWidth}
+          cacheMediaWidth={this.props.cacheMediaWidth}
+          onUnmount={this.props.onUnmount}
           withDismiss
         />
       );
@@ -75,6 +81,9 @@ export default class Notification extends ImmutablePureComponent {
           onMention={onMention}
           getScrollPosition={getScrollPosition}
           updateScrollBottom={updateScrollBottom}
+          cachedMediaWidth={this.props.cachedMediaWidth}
+          cacheMediaWidth={this.props.cacheMediaWidth}
+          onUnmount={this.props.onUnmount}
           withDismiss
         />
       );
@@ -93,6 +102,9 @@ export default class Notification extends ImmutablePureComponent {
           onMention={onMention}
           getScrollPosition={getScrollPosition}
           updateScrollBottom={updateScrollBottom}
+          cachedMediaWidth={this.props.cachedMediaWidth}
+          cacheMediaWidth={this.props.cacheMediaWidth}
+          onUnmount={this.props.onUnmount}
           withDismiss
         />
       );
diff --git a/app/javascript/flavours/glitch/features/status/components/card.js b/app/javascript/flavours/glitch/features/status/components/card.js
index 1e1604d5c..e405a5ef0 100644
--- a/app/javascript/flavours/glitch/features/status/components/card.js
+++ b/app/javascript/flavours/glitch/features/status/components/card.js
@@ -60,6 +60,8 @@ export default class Card extends React.PureComponent {
     maxDescription: PropTypes.number,
     onOpenMedia: PropTypes.func.isRequired,
     compact: PropTypes.bool,
+    defaultWidth: PropTypes.number,
+    cacheWidth: PropTypes.func,
   };
 
   static defaultProps = {
@@ -68,7 +70,7 @@ export default class Card extends React.PureComponent {
   };
 
   state = {
-    width: 280,
+    width: this.props.defaultWidth || 280,
     embedded: false,
   };
 
@@ -111,6 +113,7 @@ 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 });
     }
   }
@@ -133,7 +136,7 @@ export default class Card extends React.PureComponent {
   }
 
   render () {
-    const { card, maxDescription, compact } = this.props;
+    const { card, maxDescription, compact, defaultWidth } = this.props;
     const { width, embedded } = this.state;
 
     if (card === null) {
diff --git a/app/javascript/flavours/glitch/features/video/index.js b/app/javascript/flavours/glitch/features/video/index.js
index d8e8791a7..a6f651db6 100644
--- a/app/javascript/flavours/glitch/features/video/index.js
+++ b/app/javascript/flavours/glitch/features/video/index.js
@@ -103,6 +103,7 @@ export default class Video extends React.PureComponent {
     inline: PropTypes.bool,
     preventPlayback: PropTypes.bool,
     intl: PropTypes.object.isRequired,
+    cacheWidth: PropTypes.func,
   };
 
   state = {
@@ -111,7 +112,7 @@ export default class Video extends React.PureComponent {
     volume: 0.5,
     paused: true,
     dragging: false,
-    containerWidth: false,
+    containerWidth: this.props.width,
     fullscreen: false,
     hovered: false,
     muted: false,
@@ -131,6 +132,7 @@ export default class Video extends React.PureComponent {
     this.player = c;
 
     if (c && c.offsetWidth && c.offsetWidth != this.state.containerWidth) {
+      if (this.props.cacheWidth) this.props.cacheWidth(this.player.offsetWidth);
       this.setState({
         containerWidth: c.offsetWidth,
       });
@@ -275,6 +277,7 @@ export default class Video extends React.PureComponent {
 
   componentDidUpdate (prevProps) {
     if (this.player && this.player.offsetWidth && this.player.offsetWidth != this.state.containerWidth && !this.state.fullscreen) {
+      if (this.props.cacheWidth) this.props.cacheWidth(this.player.offsetWidth);
       this.setState({
         containerWidth: this.player.offsetWidth,
       });