about summary refs log tree commit diff
path: root/app/javascript/flavours/glitch/components
diff options
context:
space:
mode:
Diffstat (limited to 'app/javascript/flavours/glitch/components')
-rw-r--r--app/javascript/flavours/glitch/components/load_gap.js33
-rw-r--r--app/javascript/flavours/glitch/components/load_more.js5
-rw-r--r--app/javascript/flavours/glitch/components/media_gallery.js11
-rw-r--r--app/javascript/flavours/glitch/components/modal_root.js3
-rw-r--r--app/javascript/flavours/glitch/components/scrollable_list.js16
-rw-r--r--app/javascript/flavours/glitch/components/status.js6
-rw-r--r--app/javascript/flavours/glitch/components/status_action_bar.js7
-rw-r--r--app/javascript/flavours/glitch/components/status_content.js12
-rw-r--r--app/javascript/flavours/glitch/components/status_list.js21
9 files changed, 85 insertions, 29 deletions
diff --git a/app/javascript/flavours/glitch/components/load_gap.js b/app/javascript/flavours/glitch/components/load_gap.js
new file mode 100644
index 000000000..012303ae1
--- /dev/null
+++ b/app/javascript/flavours/glitch/components/load_gap.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { injectIntl, defineMessages } from 'react-intl';
+
+const messages = defineMessages({
+  load_more: { id: 'status.load_more', defaultMessage: 'Load more' },
+});
+
+@injectIntl
+export default class LoadGap extends React.PureComponent {
+
+  static propTypes = {
+    disabled: PropTypes.bool,
+    maxId: PropTypes.string,
+    onClick: PropTypes.func.isRequired,
+    intl: PropTypes.object.isRequired,
+  };
+
+  handleClick = () => {
+    this.props.onClick(this.props.maxId);
+  }
+
+  render () {
+    const { disabled, intl } = this.props;
+
+    return (
+      <button className='load-more load-gap' disabled={disabled} onClick={this.handleClick} aria-label={intl.formatMessage(messages.load_more)}>
+        <i className='fa fa-ellipsis-h' />
+      </button>
+    );
+  }
+
+}
diff --git a/app/javascript/flavours/glitch/components/load_more.js b/app/javascript/flavours/glitch/components/load_more.js
index c4c8c94a2..389c3e1e1 100644
--- a/app/javascript/flavours/glitch/components/load_more.js
+++ b/app/javascript/flavours/glitch/components/load_more.js
@@ -6,6 +6,7 @@ export default class LoadMore extends React.PureComponent {
 
   static propTypes = {
     onClick: PropTypes.func,
+    disabled: PropTypes.bool,
     visible: PropTypes.bool,
   }
 
@@ -14,10 +15,10 @@ export default class LoadMore extends React.PureComponent {
   }
 
   render() {
-    const { visible } = this.props;
+    const { disabled, visible } = this.props;
 
     return (
-      <button className='load-more' disabled={!visible} style={{ visibility: visible ? 'visible' : 'hidden' }} onClick={this.props.onClick}>
+      <button className='load-more' disabled={disabled || !visible} style={{ visibility: visible ? 'visible' : 'hidden' }} onClick={this.props.onClick}>
         <FormattedMessage id='status.load_more' defaultMessage='Load more' />
       </button>
     );
diff --git a/app/javascript/flavours/glitch/components/media_gallery.js b/app/javascript/flavours/glitch/components/media_gallery.js
index 7f5150f7b..d90f9bdb4 100644
--- a/app/javascript/flavours/glitch/components/media_gallery.js
+++ b/app/javascript/flavours/glitch/components/media_gallery.js
@@ -40,6 +40,7 @@ class Item extends React.PureComponent {
     size: PropTypes.number.isRequired,
     letterbox: PropTypes.bool,
     onClick: PropTypes.func.isRequired,
+    displayWidth: PropTypes.number,
   };
 
   static defaultProps = {
@@ -78,7 +79,7 @@ class Item extends React.PureComponent {
   }
 
   render () {
-    const { attachment, index, size, standalone, letterbox } = this.props;
+    const { attachment, index, size, standalone, letterbox, displayWidth } = this.props;
 
     let width  = 50;
     let height = 100;
@@ -141,7 +142,7 @@ class Item extends React.PureComponent {
       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 ? `${displayWidth * (width / 100)}px` : null;
 
       const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0;
       const focusY = attachment.getIn(['meta', 'focus', 'y']) || 0;
@@ -235,7 +236,7 @@ export default class MediaGallery extends React.PureComponent {
   }
 
   handleRef = (node) => {
-    if (node && this.isStandaloneEligible()) {
+    if (node /*&& this.isStandaloneEligible()*/) {
       // offsetWidth triggers a layout, so only calculate when we need to
       this.setState({
         width: node.offsetWidth,
@@ -272,9 +273,9 @@ export default class MediaGallery extends React.PureComponent {
       );
     } else {
       if (this.isStandaloneEligible()) {
-        children = <Item standalone attachment={media.get(0)} onClick={this.handleClick} />;
+        children = <Item standalone attachment={media.get(0)} onClick={this.handleClick} displayWidth={width} />;
       } else {
-        children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} letterbox={letterbox} />);
+        children = media.take(4).map((attachment, i) => <Item key={attachment.get('id')} onClick={this.handleClick} attachment={attachment} index={i} size={size} letterbox={letterbox} displayWidth={width} />);
       }
     }
 
diff --git a/app/javascript/flavours/glitch/components/modal_root.js b/app/javascript/flavours/glitch/components/modal_root.js
index 789e117c7..89f81f58e 100644
--- a/app/javascript/flavours/glitch/components/modal_root.js
+++ b/app/javascript/flavours/glitch/components/modal_root.js
@@ -6,6 +6,7 @@ export default class ModalRoot extends React.PureComponent {
   static propTypes = {
     children: PropTypes.node,
     onClose: PropTypes.func.isRequired,
+    noEsc: PropTypes.bool,
   };
 
   state = {
@@ -16,7 +17,7 @@ export default class ModalRoot extends React.PureComponent {
 
   handleKeyUp = (e) => {
     if ((e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27)
-         && !!this.props.children && !this.props.props.noEsc) {
+         && !!this.props.children && !this.props.noEsc) {
       this.props.onClose();
     }
   }
diff --git a/app/javascript/flavours/glitch/components/scrollable_list.js b/app/javascript/flavours/glitch/components/scrollable_list.js
index df3ace4c1..b96b4dd98 100644
--- a/app/javascript/flavours/glitch/components/scrollable_list.js
+++ b/app/javascript/flavours/glitch/components/scrollable_list.js
@@ -17,7 +17,7 @@ export default class ScrollableList extends PureComponent {
 
   static propTypes = {
     scrollKey: PropTypes.string.isRequired,
-    onScrollToBottom: PropTypes.func,
+    onLoadMore: PropTypes.func,
     onScrollToTop: PropTypes.func,
     onScroll: PropTypes.func,
     trackScroll: PropTypes.bool,
@@ -44,9 +44,11 @@ export default class ScrollableList extends PureComponent {
       const { scrollTop, scrollHeight, clientHeight } = this.node;
       const offset = scrollHeight - scrollTop - clientHeight;
 
-      if (400 > offset && this.props.onScrollToBottom && !this.props.isLoading) {
-        this.props.onScrollToBottom();
-      } else if (scrollTop < 100 && this.props.onScrollToTop) {
+      if (400 > offset && this.props.onLoadMore && !this.props.isLoading) {
+        this.props.onLoadMore();
+      }
+
+      if (scrollTop < 100 && this.props.onScrollToTop) {
         this.props.onScrollToTop();
       } else if (this.props.onScroll) {
         this.props.onScroll();
@@ -144,15 +146,15 @@ export default class ScrollableList extends PureComponent {
 
   handleLoadMore = (e) => {
     e.preventDefault();
-    this.props.onScrollToBottom();
+    this.props.onLoadMore();
   }
 
   render () {
-    const { children, scrollKey, trackScroll, shouldUpdateScroll, isLoading, hasMore, prepend, emptyMessage } = this.props;
+    const { children, scrollKey, trackScroll, shouldUpdateScroll, isLoading, hasMore, prepend, emptyMessage, onLoadMore } = this.props;
     const { fullscreen } = this.state;
     const childrenCount = React.Children.count(children);
 
-    const loadMore     = (hasMore && childrenCount > 0) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null;
+    const loadMore     = (hasMore && childrenCount > 0 && onLoadMore) ? <LoadMore visible={!isLoading} onClick={this.handleLoadMore} /> : null;
     let scrollableArea = null;
 
     if (isLoading || childrenCount > 0 || !emptyMessage) {
diff --git a/app/javascript/flavours/glitch/components/status.js b/app/javascript/flavours/glitch/components/status.js
index bff396f04..c93705266 100644
--- a/app/javascript/flavours/glitch/components/status.js
+++ b/app/javascript/flavours/glitch/components/status.js
@@ -32,6 +32,8 @@ export default class Status extends ImmutablePureComponent {
     onFavourite: PropTypes.func,
     onReblog: PropTypes.func,
     onDelete: PropTypes.func,
+    onDirect: PropTypes.func,
+    onMention: PropTypes.func,
     onPin: PropTypes.func,
     onOpenMedia: PropTypes.func,
     onOpenVideo: PropTypes.func,
@@ -257,8 +259,8 @@ export default class Status extends ImmutablePureComponent {
     }
   };
 
-  handleOpenVideo = startTime => {
-    this.props.onOpenVideo(this.props.status.getIn(['media_attachments', 0]), startTime);
+  handleOpenVideo = (media, startTime) => {
+    this.props.onOpenVideo(media, startTime);
   }
 
   handleHotkeyReply = e => {
diff --git a/app/javascript/flavours/glitch/components/status_action_bar.js b/app/javascript/flavours/glitch/components/status_action_bar.js
index da6e4e6ba..6ae4bc08d 100644
--- a/app/javascript/flavours/glitch/components/status_action_bar.js
+++ b/app/javascript/flavours/glitch/components/status_action_bar.js
@@ -10,6 +10,7 @@ import RelativeTimestamp from './relative_timestamp';
 
 const messages = defineMessages({
   delete: { id: 'status.delete', defaultMessage: 'Delete' },
+  direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' },
   mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' },
   mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' },
   block: { id: 'account.block', defaultMessage: 'Block @{name}' },
@@ -44,6 +45,7 @@ export default class StatusActionBar extends ImmutablePureComponent {
     onFavourite: PropTypes.func,
     onReblog: PropTypes.func,
     onDelete: PropTypes.func,
+    onDirect: PropTypes.func,
     onMention: PropTypes.func,
     onMute: PropTypes.func,
     onBlock: PropTypes.func,
@@ -98,6 +100,10 @@ export default class StatusActionBar extends ImmutablePureComponent {
     this.props.onMention(this.props.status.get('account'), this.context.router.history);
   }
 
+  handleDirectClick = () => {
+    this.props.onDirect(this.props.status.get('account'), this.context.router.history);
+  }
+
   handleMuteClick = () => {
     this.props.onMute(this.props.status.get('account'));
   }
@@ -157,6 +163,7 @@ export default class StatusActionBar extends ImmutablePureComponent {
       menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick });
     } else {
       menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick });
+      menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick });
       menu.push(null);
       menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick });
       menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick });
diff --git a/app/javascript/flavours/glitch/components/status_content.js b/app/javascript/flavours/glitch/components/status_content.js
index 32b0770cb..6542df65b 100644
--- a/app/javascript/flavours/glitch/components/status_content.js
+++ b/app/javascript/flavours/glitch/components/status_content.js
@@ -98,7 +98,7 @@ export default class StatusContent extends React.PureComponent {
     const [ startX, startY ] = this.startXY;
     const [ deltaX, deltaY ] = [Math.abs(e.clientX - startX), Math.abs(e.clientY - startY)];
 
-    if (e.target.localName === 'button' || e.target.localName === 'a' || (e.target.parentNode && (e.target.parentNode.localName === 'button' || e.target.parentNode.localName === 'a'))) {
+    if (e.target.localName === 'button' || e.target.localName == 'video' || e.target.localName === 'a' || (e.target.parentNode && (e.target.parentNode.localName === 'button' || e.target.parentNode.localName === 'a'))) {
       return;
     }
 
@@ -188,11 +188,9 @@ export default class StatusContent extends React.PureComponent {
       }
 
       return (
-        <div className={classNames} tabIndex='0'>
+        <div className={classNames} tabIndex='0' onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp}>
           <p
             style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }}
-            onMouseDown={this.handleMouseDown}
-            onMouseUp={this.handleMouseUp}
           >
             <span dangerouslySetInnerHTML={spoilerContent} />
             {' '}
@@ -208,8 +206,6 @@ export default class StatusContent extends React.PureComponent {
               ref={this.setRef}
               style={directionStyle}
               tabIndex={!hidden ? 0 : null}
-              onMouseDown={this.handleMouseDown}
-              onMouseUp={this.handleMouseUp}
               dangerouslySetInnerHTML={content}
             />
             {media}
@@ -222,12 +218,12 @@ export default class StatusContent extends React.PureComponent {
         <div
           className={classNames}
           style={directionStyle}
+          onMouseDown={this.handleMouseDown}
+          onMouseUp={this.handleMouseUp}
           tabIndex='0'
         >
           <div
             ref={this.setRef}
-            onMouseDown={this.handleMouseDown}
-            onMouseUp={this.handleMouseUp}
             dangerouslySetInnerHTML={content}
             tabIndex='0'
           />
diff --git a/app/javascript/flavours/glitch/components/status_list.js b/app/javascript/flavours/glitch/components/status_list.js
index 2b35d6f3d..33bc7a959 100644
--- a/app/javascript/flavours/glitch/components/status_list.js
+++ b/app/javascript/flavours/glitch/components/status_list.js
@@ -1,8 +1,10 @@
+import { debounce } from 'lodash';
 import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import StatusContainer from 'flavours/glitch/containers/status_container';
 import ImmutablePureComponent from 'react-immutable-pure-component';
+import LoadGap from './load_gap';
 import ScrollableList from './scrollable_list';
 import { FormattedMessage } from 'react-intl';
 
@@ -12,7 +14,7 @@ export default class StatusList extends ImmutablePureComponent {
     scrollKey: PropTypes.string.isRequired,
     statusIds: ImmutablePropTypes.list.isRequired,
     featuredStatusIds: ImmutablePropTypes.list,
-    onScrollToBottom: PropTypes.func,
+    onLoadMore: PropTypes.func,
     onScrollToTop: PropTypes.func,
     onScroll: PropTypes.func,
     trackScroll: PropTypes.bool,
@@ -50,6 +52,10 @@ export default class StatusList extends ImmutablePureComponent {
     this._selectChild(elementIndex);
   }
 
+  handleLoadOlder = debounce(() => {
+    this.props.onLoadMore(this.props.statusIds.last());
+  }, 300, { leading: true })
+
   _selectChild (index) {
     const element = this.node.node.querySelector(`article:nth-of-type(${index + 1}) .focusable`);
 
@@ -63,7 +69,7 @@ export default class StatusList extends ImmutablePureComponent {
   }
 
   render () {
-    const { statusIds, featuredStatusIds, ...other }  = this.props;
+    const { statusIds, featuredStatusIds, onLoadMore, ...other }  = this.props;
     const { isLoading, isPartial } = other;
 
     if (isPartial) {
@@ -82,7 +88,14 @@ export default class StatusList extends ImmutablePureComponent {
     }
 
     let scrollableContent = (isLoading || statusIds.size > 0) ? (
-      statusIds.map(statusId => (
+      statusIds.map((statusId, index) => statusId === null ? (
+        <LoadGap
+          key={'gap:' + statusIds.get(index + 1)}
+          disabled={isLoading}
+          maxId={index > 0 ? statusIds.get(index - 1) : null}
+          onClick={onLoadMore}
+        />
+      ) : (
         <StatusContainer
           key={statusId}
           id={statusId}
@@ -105,7 +118,7 @@ export default class StatusList extends ImmutablePureComponent {
     }
 
     return (
-      <ScrollableList {...other} ref={this.setRef}>
+      <ScrollableList {...other} onLoadMore={onLoadMore && this.handleLoadOlder} ref={this.setRef}>
         {scrollableContent}
       </ScrollableList>
     );