about summary refs log tree commit diff
path: root/app/javascript/flavours/glitch/features
diff options
context:
space:
mode:
Diffstat (limited to 'app/javascript/flavours/glitch/features')
-rw-r--r--app/javascript/flavours/glitch/features/blocks/index.js5
-rw-r--r--app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js39
-rw-r--r--app/javascript/flavours/glitch/features/follow_requests/index.js5
-rw-r--r--app/javascript/flavours/glitch/features/followers/index.js13
-rw-r--r--app/javascript/flavours/glitch/features/following/index.js13
-rw-r--r--app/javascript/flavours/glitch/features/hashtag_timeline/components/column_settings.js7
-rw-r--r--app/javascript/flavours/glitch/features/hashtag_timeline/index.js16
-rw-r--r--app/javascript/flavours/glitch/features/mutes/index.js5
-rw-r--r--app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js14
-rw-r--r--app/javascript/flavours/glitch/features/status/components/detailed_status.js4
-rw-r--r--app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js4
-rw-r--r--app/javascript/flavours/glitch/features/status/index.js6
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/video_modal.js13
-rw-r--r--app/javascript/flavours/glitch/features/video/index.js17
14 files changed, 90 insertions, 71 deletions
diff --git a/app/javascript/flavours/glitch/features/blocks/index.js b/app/javascript/flavours/glitch/features/blocks/index.js
index 4992689ff..4d0f58239 100644
--- a/app/javascript/flavours/glitch/features/blocks/index.js
+++ b/app/javascript/flavours/glitch/features/blocks/index.js
@@ -19,6 +19,7 @@ const messages = defineMessages({
 const mapStateToProps = state => ({
   accountIds: state.getIn(['user_lists', 'blocks', 'items']),
   hasMore: !!state.getIn(['user_lists', 'blocks', 'next']),
+  isLoading: state.getIn(['user_lists', 'blocks', 'isLoading'], true),
 });
 
 export default @connect(mapStateToProps)
@@ -30,6 +31,7 @@ class Blocks extends ImmutablePureComponent {
     dispatch: PropTypes.func.isRequired,
     accountIds: ImmutablePropTypes.list,
     hasMore: PropTypes.bool,
+    isLoading: PropTypes.bool,
     intl: PropTypes.object.isRequired,
     multiColumn: PropTypes.bool,
   };
@@ -43,7 +45,7 @@ class Blocks extends ImmutablePureComponent {
   }, 300, { leading: true });
 
   render () {
-    const { intl, accountIds, hasMore, multiColumn } = this.props;
+    const { intl, accountIds, hasMore, multiColumn, isLoading } = this.props;
 
     if (!accountIds) {
       return (
@@ -62,6 +64,7 @@ class Blocks extends ImmutablePureComponent {
           scrollKey='blocks'
           onLoadMore={this.handleLoadMore}
           hasMore={hasMore}
+          isLoading={isLoading}
           emptyMessage={emptyMessage}
           bindToDocument={!multiColumn}
         >
diff --git a/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js b/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js
index 404504e84..bee06e64c 100644
--- a/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js
+++ b/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.js
@@ -64,9 +64,9 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
     document.addEventListener('click', this.handleDocumentClick, false);
     document.addEventListener('touchend', this.handleDocumentClick, withPassive);
     if (this.focusedItem) {
-      this.focusedItem.focus();
+      this.focusedItem.focus({ preventScroll: true });
     } else {
-      this.node.firstChild.focus();
+      this.node.firstChild.focus({ preventScroll: true });
     }
     this.setState({ mounted: true });
   }
@@ -106,7 +106,7 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
     const index = items.findIndex(item => {
       return (item.name === name);
     });
-    let element;
+    let element = null;
 
     switch(e.key) {
     case 'Escape':
@@ -117,18 +117,10 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
       this.handleClick(e);
       break;
     case 'ArrowDown':
-      element = this.node.childNodes[index + 1];
-      if (element) {
-        element.focus();
-        this.handleChange(element.getAttribute('data-index'));
-      }
+      element = this.node.childNodes[index + 1] || this.node.firstChild;
       break;
     case 'ArrowUp':
-      element = this.node.childNodes[index - 1];
-      if (element) {
-        element.focus();
-        this.handleChange(element.getAttribute('data-index'));
-      }
+      element = this.node.childNodes[index - 1] || this.node.lastChild;
       break;
     case 'Tab':
       if (e.shiftKey) {
@@ -136,28 +128,21 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
       } else {
         element = this.node.childNodes[index + 1] || this.node.firstChild;
       }
-      if (element) {
-        element.focus();
-        this.handleChange(element.getAttribute('data-index'));
-        e.preventDefault();
-        e.stopPropagation();
-      }
       break;
     case 'Home':
       element = this.node.firstChild;
-      if (element) {
-        element.focus();
-        this.handleChange(element.getAttribute('data-index'));
-      }
       break;
     case 'End':
       element = this.node.lastChild;
-      if (element) {
-        element.focus();
-        this.handleChange(element.getAttribute('data-index'));
-      }
       break;
     }
+
+    if (element) {
+      element.focus();
+      this.handleChange(element.getAttribute('data-index'));
+      e.preventDefault();
+      e.stopPropagation();
+    }
   }
 
   setFocusRef = c => {
diff --git a/app/javascript/flavours/glitch/features/follow_requests/index.js b/app/javascript/flavours/glitch/features/follow_requests/index.js
index efbe1a23c..36a57d1d6 100644
--- a/app/javascript/flavours/glitch/features/follow_requests/index.js
+++ b/app/javascript/flavours/glitch/features/follow_requests/index.js
@@ -19,6 +19,7 @@ const messages = defineMessages({
 
 const mapStateToProps = state => ({
   accountIds: state.getIn(['user_lists', 'follow_requests', 'items']),
+  isLoading: state.getIn(['user_lists', 'follow_requests', 'isLoading'], true),
   hasMore: !!state.getIn(['user_lists', 'follow_requests', 'next']),
   locked: !!state.getIn(['accounts', me, 'locked']),
   domain: state.getIn(['meta', 'domain']),
@@ -32,6 +33,7 @@ class FollowRequests extends ImmutablePureComponent {
     params: PropTypes.object.isRequired,
     dispatch: PropTypes.func.isRequired,
     hasMore: PropTypes.bool,
+    isLoading: PropTypes.bool,
     accountIds: ImmutablePropTypes.list,
     locked: PropTypes.bool,
     domain: PropTypes.string,
@@ -48,7 +50,7 @@ class FollowRequests extends ImmutablePureComponent {
   }, 300, { leading: true });
 
   render () {
-    const { intl, accountIds, hasMore, multiColumn, locked, domain } = this.props;
+    const { intl, accountIds, hasMore, multiColumn, locked, domain, isLoading } = this.props;
 
     if (!accountIds) {
       return (
@@ -77,6 +79,7 @@ class FollowRequests extends ImmutablePureComponent {
           scrollKey='follow_requests'
           onLoadMore={this.handleLoadMore}
           hasMore={hasMore}
+          isLoading={isLoading}
           emptyMessage={emptyMessage}
           bindToDocument={!multiColumn}
           prepend={unlockedPrependMessage}
diff --git a/app/javascript/flavours/glitch/features/followers/index.js b/app/javascript/flavours/glitch/features/followers/index.js
index 2b86cc805..bf41f3b98 100644
--- a/app/javascript/flavours/glitch/features/followers/index.js
+++ b/app/javascript/flavours/glitch/features/followers/index.js
@@ -22,6 +22,7 @@ const mapStateToProps = (state, props) => ({
   isAccount: !!state.getIn(['accounts', props.params.accountId]),
   accountIds: state.getIn(['user_lists', 'followers', props.params.accountId, 'items']),
   hasMore: !!state.getIn(['user_lists', 'followers', props.params.accountId, 'next']),
+  isLoading: state.getIn(['user_lists', 'followers', props.params.accountId, 'isLoading'], true),
 });
 
 export default @connect(mapStateToProps)
@@ -32,6 +33,7 @@ class Followers extends ImmutablePureComponent {
     dispatch: PropTypes.func.isRequired,
     accountIds: ImmutablePropTypes.list,
     hasMore: PropTypes.bool,
+    isLoading: PropTypes.bool,
     isAccount: PropTypes.bool,
     multiColumn: PropTypes.bool,
   };
@@ -54,14 +56,6 @@ class Followers extends ImmutablePureComponent {
     this.column.scrollTop();
   }
 
-  handleScroll = (e) => {
-    const { scrollTop, scrollHeight, clientHeight } = e.target;
-
-    if (scrollTop === scrollHeight - clientHeight && this.props.hasMore) {
-      this.props.dispatch(expandFollowers(this.props.params.accountId));
-    }
-  }
-
   handleLoadMore = debounce(() => {
     this.props.dispatch(expandFollowers(this.props.params.accountId));
   }, 300, { leading: true });
@@ -71,7 +65,7 @@ class Followers extends ImmutablePureComponent {
   }
 
   render () {
-    const { accountIds, hasMore, isAccount, multiColumn } = this.props;
+    const { accountIds, hasMore, isAccount, multiColumn, isLoading } = this.props;
 
     if (!isAccount) {
       return (
@@ -98,6 +92,7 @@ class Followers extends ImmutablePureComponent {
         <ScrollableList
           scrollKey='followers'
           hasMore={hasMore}
+          isLoading={isLoading}
           onLoadMore={this.handleLoadMore}
           prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />}
           alwaysPrepend
diff --git a/app/javascript/flavours/glitch/features/following/index.js b/app/javascript/flavours/glitch/features/following/index.js
index cf374e494..f090900cc 100644
--- a/app/javascript/flavours/glitch/features/following/index.js
+++ b/app/javascript/flavours/glitch/features/following/index.js
@@ -22,6 +22,7 @@ const mapStateToProps = (state, props) => ({
   isAccount: !!state.getIn(['accounts', props.params.accountId]),
   accountIds: state.getIn(['user_lists', 'following', props.params.accountId, 'items']),
   hasMore: !!state.getIn(['user_lists', 'following', props.params.accountId, 'next']),
+  isLoading: state.getIn(['user_lists', 'following', props.params.accountId, 'isLoading'], true),
 });
 
 export default @connect(mapStateToProps)
@@ -32,6 +33,7 @@ class Following extends ImmutablePureComponent {
     dispatch: PropTypes.func.isRequired,
     accountIds: ImmutablePropTypes.list,
     hasMore: PropTypes.bool,
+    isLoading: PropTypes.bool,
     isAccount: PropTypes.bool,
     multiColumn: PropTypes.bool,
   };
@@ -54,14 +56,6 @@ class Following extends ImmutablePureComponent {
     this.column.scrollTop();
   }
 
-  handleScroll = (e) => {
-    const { scrollTop, scrollHeight, clientHeight } = e.target;
-
-    if (scrollTop === scrollHeight - clientHeight && this.props.hasMore) {
-      this.props.dispatch(expandFollowing(this.props.params.accountId));
-    }
-  }
-
   handleLoadMore = debounce(() => {
     this.props.dispatch(expandFollowing(this.props.params.accountId));
   }, 300, { leading: true });
@@ -71,7 +65,7 @@ class Following extends ImmutablePureComponent {
   }
 
   render () {
-    const { accountIds, hasMore, isAccount, multiColumn } = this.props;
+    const { accountIds, hasMore, isAccount, multiColumn, isLoading } = this.props;
 
     if (!isAccount) {
       return (
@@ -98,6 +92,7 @@ class Following extends ImmutablePureComponent {
         <ScrollableList
           scrollKey='following'
           hasMore={hasMore}
+          isLoading={isLoading}
           onLoadMore={this.handleLoadMore}
           prepend={<HeaderContainer accountId={this.props.params.accountId} hideTabs />}
           alwaysPrepend
diff --git a/app/javascript/flavours/glitch/features/hashtag_timeline/components/column_settings.js b/app/javascript/flavours/glitch/features/hashtag_timeline/components/column_settings.js
index 9c39b158a..956f16734 100644
--- a/app/javascript/flavours/glitch/features/hashtag_timeline/components/column_settings.js
+++ b/app/javascript/flavours/glitch/features/hashtag_timeline/components/column_settings.js
@@ -4,6 +4,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import Toggle from 'react-toggle';
 import AsyncSelect from 'react-select/async';
+import SettingToggle from '../../notifications/components/setting_toggle';
 
 const messages = defineMessages({
   placeholder: { id: 'hashtag.column_settings.select.placeholder', defaultMessage: 'Enter hashtags…' },
@@ -87,6 +88,8 @@ class ColumnSettings extends React.PureComponent {
   };
 
   render () {
+    const { settings, onChange } = this.props;
+
     return (
       <div>
         <div className='column-settings__row'>
@@ -106,6 +109,10 @@ class ColumnSettings extends React.PureComponent {
             {this.modeSelect('none')}
           </div>
         )}
+
+        <div className='column-settings__row'>
+          <SettingToggle settings={settings} settingPath={['local']} onChange={onChange} label={<FormattedMessage id='community.column_settings.local_only' defaultMessage='Local only' />} />
+        </div>
       </div>
     );
   }
diff --git a/app/javascript/flavours/glitch/features/hashtag_timeline/index.js b/app/javascript/flavours/glitch/features/hashtag_timeline/index.js
index 16dd80c4f..e94f36501 100644
--- a/app/javascript/flavours/glitch/features/hashtag_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/hashtag_timeline/index.js
@@ -97,21 +97,21 @@ class HashtagTimeline extends React.PureComponent {
 
   componentDidMount () {
     const { dispatch } = this.props;
-    const { id, tags } = this.props.params;
+    const { id, tags, local } = this.props.params;
 
     this._subscribe(dispatch, id, tags);
-    dispatch(expandHashtagTimeline(id, { tags }));
+    dispatch(expandHashtagTimeline(id, { tags, local }));
   }
 
   componentWillReceiveProps (nextProps) {
     const { dispatch, params } = this.props;
-    const { id, tags } = nextProps.params;
+    const { id, tags, local } = nextProps.params;
 
-    if (id !== params.id || !isEqual(tags, params.tags)) {
+    if (id !== params.id || !isEqual(tags, params.tags) || !isEqual(local, params.local)) {
       this._unsubscribe();
       this._subscribe(dispatch, id, tags);
-      this.props.dispatch(clearTimeline(`hashtag:${id}`));
-      this.props.dispatch(expandHashtagTimeline(id, { tags }));
+      dispatch(clearTimeline(`hashtag:${id}`));
+      dispatch(expandHashtagTimeline(id, { tags, local }));
     }
   }
 
@@ -124,8 +124,8 @@ class HashtagTimeline extends React.PureComponent {
   }
 
   handleLoadMore = maxId => {
-    const { id, tags } = this.props.params;
-    this.props.dispatch(expandHashtagTimeline(id, { maxId, tags }));
+    const { id, tags, local } = this.props.params;
+    this.props.dispatch(expandHashtagTimeline(id, { maxId, tags, local }));
   }
 
   render () {
diff --git a/app/javascript/flavours/glitch/features/mutes/index.js b/app/javascript/flavours/glitch/features/mutes/index.js
index 62dccd971..9f0d5a43e 100644
--- a/app/javascript/flavours/glitch/features/mutes/index.js
+++ b/app/javascript/flavours/glitch/features/mutes/index.js
@@ -19,6 +19,7 @@ const messages = defineMessages({
 const mapStateToProps = state => ({
   accountIds: state.getIn(['user_lists', 'mutes', 'items']),
   hasMore: !!state.getIn(['user_lists', 'mutes', 'next']),
+  isLoading: state.getIn(['user_lists', 'mutes', 'isLoading'], true),
 });
 
 export default @connect(mapStateToProps)
@@ -29,6 +30,7 @@ class Mutes extends ImmutablePureComponent {
     params: PropTypes.object.isRequired,
     dispatch: PropTypes.func.isRequired,
     hasMore: PropTypes.bool,
+    isLoading: PropTypes.bool,
     accountIds: ImmutablePropTypes.list,
     intl: PropTypes.object.isRequired,
     multiColumn: PropTypes.bool,
@@ -43,7 +45,7 @@ class Mutes extends ImmutablePureComponent {
   }, 300, { leading: true });
 
   render () {
-    const { intl, accountIds, hasMore, multiColumn } = this.props;
+    const { intl, accountIds, hasMore, multiColumn, isLoading } = this.props;
 
     if (!accountIds) {
       return (
@@ -62,6 +64,7 @@ class Mutes extends ImmutablePureComponent {
           scrollKey='mutes'
           onLoadMore={this.handleLoadMore}
           hasMore={hasMore}
+          isLoading={isLoading}
           emptyMessage={emptyMessage}
           bindToDocument={!multiColumn}
         >
diff --git a/app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js b/app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js
index 4fbd504ef..629f5c2ea 100644
--- a/app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js
+++ b/app/javascript/flavours/glitch/features/standalone/hashtag_timeline/index.js
@@ -24,19 +24,25 @@ class HashtagTimeline extends React.PureComponent {
     isLoading: PropTypes.bool.isRequired,
     hasMore: PropTypes.bool.isRequired,
     hashtag: PropTypes.string.isRequired,
+    local: PropTypes.bool.isRequired,
+  };
+
+  static defaultProps = {
+    local: false,
   };
 
   componentDidMount () {
-    const { dispatch, hashtag } = this.props;
+    const { dispatch, hashtag, local } = this.props;
 
-    dispatch(expandHashtagTimeline(hashtag));
+    dispatch(expandHashtagTimeline(hashtag, { local }));
   }
 
   handleLoadMore = () => {
-    const maxId = this.props.statusIds.last();
+    const { dispatch, hashtag, local, statusIds } = this.props;
+    const maxId = statusIds.last();
 
     if (maxId) {
-      this.props.dispatch(expandHashtagTimeline(this.props.hashtag, { maxId }));
+      dispatch(expandHashtagTimeline(hashtag, { maxId, local }));
     }
   }
 
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 180b11a54..17f22a8a2 100644
--- a/app/javascript/flavours/glitch/features/status/components/detailed_status.js
+++ b/app/javascript/flavours/glitch/features/status/components/detailed_status.js
@@ -66,8 +66,8 @@ export default class DetailedStatus extends ImmutablePureComponent {
     e.stopPropagation();
   }
 
-  handleOpenVideo = (media, startTime) => {
-    this.props.onOpenVideo(media, startTime);
+  handleOpenVideo = (media, options) => {
+    this.props.onOpenVideo(media, options);
   }
 
   _measureHeight (heightJustChanged) {
diff --git a/app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js b/app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js
index e71803328..9d11f37e0 100644
--- a/app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js
+++ b/app/javascript/flavours/glitch/features/status/containers/detailed_status_container.js
@@ -130,8 +130,8 @@ const mapDispatchToProps = (dispatch, { intl }) => ({
     dispatch(openModal('MEDIA', { media, index }));
   },
 
-  onOpenVideo (media, time) {
-    dispatch(openModal('VIDEO', { media, time }));
+  onOpenVideo (media, options) {
+    dispatch(openModal('VIDEO', { media, options }));
   },
 
   onBlock (status) {
diff --git a/app/javascript/flavours/glitch/features/status/index.js b/app/javascript/flavours/glitch/features/status/index.js
index 411d2a88d..a9abc545e 100644
--- a/app/javascript/flavours/glitch/features/status/index.js
+++ b/app/javascript/flavours/glitch/features/status/index.js
@@ -316,8 +316,8 @@ class Status extends ImmutablePureComponent {
     this.props.dispatch(openModal('MEDIA', { media, index }));
   }
 
-  handleOpenVideo = (media, time) => {
-    this.props.dispatch(openModal('VIDEO', { media, time }));
+  handleOpenVideo = (media, options) => {
+    this.props.dispatch(openModal('VIDEO', { media, options }));
   }
 
   handleHotkeyOpenMedia = e => {
@@ -329,7 +329,7 @@ class Status extends ImmutablePureComponent {
       if (status.getIn(['media_attachments', 0, 'type']) === 'audio') {
         // TODO: toggle play/paused?
       } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
-        this.handleOpenVideo(status.getIn(['media_attachments', 0]), 0);
+        this.handleOpenVideo(status.getIn(['media_attachments', 0]), { startTime: 0 });
       } else {
         this.handleOpenMedia(status.get('media_attachments'), 0);
       }
diff --git a/app/javascript/flavours/glitch/features/ui/components/video_modal.js b/app/javascript/flavours/glitch/features/ui/components/video_modal.js
index e7309021e..afeff90a4 100644
--- a/app/javascript/flavours/glitch/features/ui/components/video_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/video_modal.js
@@ -16,7 +16,11 @@ export default class VideoModal extends ImmutablePureComponent {
   static propTypes = {
     media: ImmutablePropTypes.map.isRequired,
     status: ImmutablePropTypes.map,
-    time: PropTypes.number,
+    options: PropTypes.shape({
+      startTime: PropTypes.number,
+      autoPlay: PropTypes.bool,
+      defaultVolume: PropTypes.number,
+    }),
     onClose: PropTypes.func.isRequired,
   };
 
@@ -28,7 +32,8 @@ export default class VideoModal extends ImmutablePureComponent {
   }
 
   render () {
-    const { media, status, time, onClose } = this.props;
+    const { media, status, onClose } = this.props;
+    const options = this.props.options || {};
 
     return (
       <div className='modal-root__modal video-modal'>
@@ -37,7 +42,9 @@ export default class VideoModal extends ImmutablePureComponent {
             preview={media.get('preview_url')}
             blurhash={media.get('blurhash')}
             src={media.get('url')}
-            startTime={time}
+            startTime={options.startTime}
+            autoPlay={options.autoPlay}
+            defaultVolume={options.defaultVolume}
             onCloseVideo={onClose}
             detailed
             alt={media.get('description')}
diff --git a/app/javascript/flavours/glitch/features/video/index.js b/app/javascript/flavours/glitch/features/video/index.js
index 6a8952c8d..a89d9c8b0 100644
--- a/app/javascript/flavours/glitch/features/video/index.js
+++ b/app/javascript/flavours/glitch/features/video/index.js
@@ -111,6 +111,8 @@ class Video extends React.PureComponent {
     preventPlayback: PropTypes.bool,
     blurhash: PropTypes.string,
     link: PropTypes.node,
+    autoPlay: PropTypes.bool,
+    defaultVolume: PropTypes.number,
   };
 
   state = {
@@ -360,6 +362,13 @@ class Video extends React.PureComponent {
   handleLoadedData = () => {
     if (this.props.startTime) {
       this.video.currentTime = this.props.startTime;
+    }
+
+    if (this.props.defaultVolume !== undefined) {
+      this.video.volume = this.props.defaultVolume;
+    }
+
+    if (this.props.autoPlay) {
       this.video.play();
     }
   }
@@ -386,8 +395,14 @@ class Video extends React.PureComponent {
       height,
     });
 
+    const options = {
+      startTime: this.video.currentTime,
+      autoPlay: !this.state.paused,
+      defaultVolume: this.state.volume,
+    };
+
     this.video.pause();
-    this.props.onOpenVideo(media, this.video.currentTime);
+    this.props.onOpenVideo(media, options);
   }
 
   handleCloseVideo = () => {