about summary refs log tree commit diff
path: root/app/javascript/flavours/glitch
diff options
context:
space:
mode:
Diffstat (limited to 'app/javascript/flavours/glitch')
-rw-r--r--app/javascript/flavours/glitch/features/account_gallery/components/media_item.js86
-rw-r--r--app/javascript/flavours/glitch/features/account_gallery/index.js4
-rw-r--r--app/javascript/flavours/glitch/features/audio/index.js16
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/audio_modal.js18
-rw-r--r--app/javascript/flavours/glitch/features/ui/components/boost_modal.js17
-rw-r--r--app/javascript/flavours/glitch/features/video/index.js2
-rw-r--r--app/javascript/flavours/glitch/selectors/index.js5
-rw-r--r--app/javascript/flavours/glitch/styles/components/modal.scss8
8 files changed, 107 insertions, 49 deletions
diff --git a/app/javascript/flavours/glitch/features/account_gallery/components/media_item.js b/app/javascript/flavours/glitch/features/account_gallery/components/media_item.js
index b88f23aa4..781bd4e03 100644
--- a/app/javascript/flavours/glitch/features/account_gallery/components/media_item.js
+++ b/app/javascript/flavours/glitch/features/account_gallery/components/media_item.js
@@ -61,73 +61,81 @@ export default class MediaItem extends ImmutablePureComponent {
     const width  = `${Math.floor((displayWidth - 4) / 3) - 4}px`;
     const height = width;
     const status = attachment.get('status');
-    const title = status.get('spoiler_text') || attachment.get('description');
+    const title  = status.get('spoiler_text') || attachment.get('description');
 
-    let thumbnail = '';
+    let thumbnail, label, icon, content;
 
-    if (attachment.get('type') === 'unknown') {
-      // Skip
-    } else if (attachment.get('type') === 'audio') {
-      thumbnail = (
+    if (!visible) {
+      icon = (
         <span className='account-gallery__item__icons'>
-          <Icon id='music' />
+          <Icon id='eye-slash' />
         </span>
       );
-    } else if (attachment.get('type') === 'image') {
-      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 = (
-        <img
-          src={attachment.get('preview_url')}
-          alt={attachment.get('description')}
-          title={attachment.get('description')}
-          style={{ objectPosition: `${x}% ${y}%` }}
-          onLoad={this.handleImageLoad}
-        />
-      );
-    } else if (['gifv', 'video'].indexOf(attachment.get('type')) !== -1) {
-      const autoPlay = !isIOS() && autoPlayGif;
-      const label    = attachment.get('type') === 'video' ? <Icon id='play' /> : 'GIF';
-
-      thumbnail = (
-        <div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}>
+    } else {
+      if (['audio', 'video'].includes(attachment.get('type'))) {
+        content = (
+          <img
+            src={attachment.get('preview_url') || attachment.getIn(['account', 'avatar_static'])}
+            alt={attachment.get('description')}
+            onLoad={this.handleImageLoad}
+          />
+        );
+
+        if (attachment.get('type') === 'audio') {
+          label = <Icon id='music' />;
+        } else {
+          label = <Icon id='play' />;
+        }
+      } else if (attachment.get('type') === 'image') {
+        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;
+
+        content = (
+          <img
+            src={attachment.get('preview_url')}
+            alt={attachment.get('description')}
+            style={{ objectPosition: `${x}% ${y}%` }}
+            onLoad={this.handleImageLoad}
+          />
+        );
+      } else if (attachment.get('type') === 'gifv') {
+        content = (
           <video
             className='media-gallery__item-gifv-thumbnail'
             aria-label={attachment.get('description')}
-            title={attachment.get('description')}
             role='application'
             src={attachment.get('url')}
             onMouseEnter={this.handleMouseEnter}
             onMouseLeave={this.handleMouseLeave}
-            autoPlay={autoPlay}
+            autoPlay={!isIOS() && autoPlayGif}
             loop
             muted
           />
+        );
+
+        label = 'GIF';
+      }
+
+      thumbnail = (
+        <div className='media-gallery__gifv'>
+          {content}
 
           <span className='media-gallery__gifv__label'>{label}</span>
         </div>
       );
     }
 
-    const icon = (
-      <span className='account-gallery__item__icons'>
-        <Icon id='eye-slash' />
-      </span>
-    );
-
     return (
       <div className='account-gallery__item' style={{ width, height }}>
         <a className='media-gallery__item-thumbnail' href={status.get('url')} onClick={this.handleClick} title={title} target='_blank' rel='noopener noreferrer'>
           <Blurhash
             hash={attachment.get('blurhash')}
-            className={classNames('media-gallery__preview', {
-              'media-gallery__preview--hidden': visible && loaded,
-            })}
+            className={classNames('media-gallery__preview', { 'media-gallery__preview--hidden': visible && loaded })}
             dummy={!useBlurhash}
           />
+
           {visible ? thumbnail : icon}
         </a>
       </div>
diff --git a/app/javascript/flavours/glitch/features/account_gallery/index.js b/app/javascript/flavours/glitch/features/account_gallery/index.js
index f5fe6c930..040741c2a 100644
--- a/app/javascript/flavours/glitch/features/account_gallery/index.js
+++ b/app/javascript/flavours/glitch/features/account_gallery/index.js
@@ -113,9 +113,9 @@ class AccountGallery extends ImmutablePureComponent {
 
   handleOpenMedia = attachment => {
     if (attachment.get('type') === 'video') {
-      this.props.dispatch(openModal('VIDEO', { media: attachment, status: attachment.get('status') }));
+      this.props.dispatch(openModal('VIDEO', { media: attachment, status: attachment.get('status'), options: { autoPlay: true } }));
     } else if (attachment.get('type') === 'audio') {
-      this.props.dispatch(openModal('AUDIO', { media: attachment, status: attachment.get('status') }));
+      this.props.dispatch(openModal('AUDIO', { media: attachment, status: attachment.get('status'), options: { autoPlay: true } }));
     } else {
       const media = attachment.getIn(['status', 'media_attachments']);
       const index = media.findIndex(x => x.get('id') === attachment.get('id'));
diff --git a/app/javascript/flavours/glitch/features/audio/index.js b/app/javascript/flavours/glitch/features/audio/index.js
index 120a5ce1a..4e85e3c58 100644
--- a/app/javascript/flavours/glitch/features/audio/index.js
+++ b/app/javascript/flavours/glitch/features/audio/index.js
@@ -37,6 +37,7 @@ class Audio extends React.PureComponent {
     backgroundColor: PropTypes.string,
     foregroundColor: PropTypes.string,
     accentColor: PropTypes.string,
+    autoPlay: PropTypes.bool,
   };
 
   state = {
@@ -244,6 +245,14 @@ class Audio extends React.PureComponent {
     this.setState({ hovered: false });
   }
 
+  handleLoadedData = () => {
+    const { autoPlay } = this.props;
+
+    if (autoPlay) {
+      this.audio.play();
+    }
+  }
+
   _initAudioContext () {
     const context  = new AudioContext();
     const source   = context.createMediaElementSource(this.audio);
@@ -274,6 +283,8 @@ class Audio extends React.PureComponent {
 
   _renderCanvas () {
     requestAnimationFrame(() => {
+      if (!this.audio) return;
+
       this.handleTimeUpdate();
       this._clear();
       this._draw();
@@ -321,7 +332,7 @@ class Audio extends React.PureComponent {
   }
 
   render () {
-    const { src, intl, alt, editable } = this.props;
+    const { src, intl, alt, editable, autoPlay } = this.props;
     const { paused, muted, volume, currentTime, duration, buffer, dragging } = this.state;
     const progress = (currentTime / duration) * 100;
 
@@ -330,10 +341,11 @@ class Audio extends React.PureComponent {
         <audio
           src={src}
           ref={this.setAudioRef}
-          preload='none'
+          preload={autoPlay ? 'auto' : 'none'}
           onPlay={this.handlePlay}
           onPause={this.handlePause}
           onProgress={this.handleProgress}
+          onLoadedData={this.handleLoadedData}
           crossOrigin='anonymous'
         />
 
diff --git a/app/javascript/flavours/glitch/features/ui/components/audio_modal.js b/app/javascript/flavours/glitch/features/ui/components/audio_modal.js
index f0c3b3bcc..f9d4bb2f3 100644
--- a/app/javascript/flavours/glitch/features/ui/components/audio_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/audio_modal.js
@@ -2,16 +2,26 @@ import React from 'react';
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PropTypes from 'prop-types';
 import Audio from 'flavours/glitch/features/audio';
+import { connect } from 'react-redux';
 import ImmutablePureComponent from 'react-immutable-pure-component';
 import { FormattedMessage } from 'react-intl';
 import classNames from 'classnames';
 import Icon from 'flavours/glitch/components/icon';
 
-export default class AudioModal extends ImmutablePureComponent {
+const mapStateToProps = (state, { status }) => ({
+  account: state.getIn(['accounts', status.get('account')]),
+});
+
+export default @connect(mapStateToProps)
+class AudioModal extends ImmutablePureComponent {
 
   static propTypes = {
     media: ImmutablePropTypes.map.isRequired,
     status: ImmutablePropTypes.map,
+    options: PropTypes.shape({
+      autoPlay: PropTypes.bool,
+    }),
+    account: ImmutablePropTypes.map,
     onClose: PropTypes.func.isRequired,
   };
 
@@ -27,7 +37,8 @@ export default class AudioModal extends ImmutablePureComponent {
   }
 
   render () {
-    const { media, status } = this.props;
+    const { media, status, account } = this.props;
+    const options = this.props.options || {};
 
     return (
       <div className='modal-root__modal audio-modal'>
@@ -37,10 +48,11 @@ export default class AudioModal extends ImmutablePureComponent {
             alt={media.get('description')}
             duration={media.getIn(['meta', 'original', 'duration'], 0)}
             height={150}
-            poster={media.get('preview_url') || status.getIn(['account', 'avatar_static'])}
+            poster={media.get('preview_url') || account.get('avatar_static')}
             backgroundColor={media.getIn(['meta', 'colors', 'background'])}
             foregroundColor={media.getIn(['meta', 'colors', 'foreground'])}
             accentColor={media.getIn(['meta', 'colors', 'accent'])}
+            autoPlay={options.autoPlay}
           />
         </div>
 
diff --git a/app/javascript/flavours/glitch/features/ui/components/boost_modal.js b/app/javascript/flavours/glitch/features/ui/components/boost_modal.js
index cd2929fdb..8092e862f 100644
--- a/app/javascript/flavours/glitch/features/ui/components/boost_modal.js
+++ b/app/javascript/flavours/glitch/features/ui/components/boost_modal.js
@@ -10,10 +10,15 @@ import DisplayName from 'flavours/glitch/components/display_name';
 import AttachmentList from 'flavours/glitch/components/attachment_list';
 import Icon from 'flavours/glitch/components/icon';
 import ImmutablePureComponent from 'react-immutable-pure-component';
+import classNames from 'classnames';
 
 const messages = defineMessages({
   cancel_reblog: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' },
   reblog: { id: 'status.reblog', defaultMessage: 'Boost' },
+  public_short: { id: 'privacy.public.short', defaultMessage: 'Public' },
+  unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
+  private_short: { id: 'privacy.private.short', defaultMessage: 'Followers-only' },
+  direct_short: { id: 'privacy.direct.short', defaultMessage: 'Direct' },
 });
 
 export default @injectIntl
@@ -58,14 +63,24 @@ class BoostModal extends ImmutablePureComponent {
     const { status, missingMediaDescription, intl } = this.props;
     const buttonText = status.get('reblogged') ? messages.cancel_reblog : messages.reblog;
 
+    const visibilityIconInfo = {
+      'public': { icon: 'globe', text: intl.formatMessage(messages.public_short) },
+      'unlisted': { icon: 'unlock', text: intl.formatMessage(messages.unlisted_short) },
+      'private': { icon: 'lock', text: intl.formatMessage(messages.private_short) },
+      'direct': { icon: 'envelope', text: intl.formatMessage(messages.direct_short) },
+    };
+
+    const visibilityIcon = visibilityIconInfo[status.get('visibility')];
+
     return (
       <div className='modal-root__modal boost-modal'>
         <div className='boost-modal__container'>
-          <div className='status light'>
+          <div className={classNames('status', `status-${status.get('visibility')}`, 'light')}>
             <div className='boost-modal__status-header'>
               <div className='boost-modal__status-time'>
                 <a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener noreferrer'><RelativeTimestamp timestamp={status.get('created_at')} /></a>
               </div>
+              <span className='status__visibility-icon'><Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></span>
 
               <a onClick={this.handleAccountClick} href={status.getIn(['account', 'url'])} className='status__display-name'>
                 <div className='status__avatar'>
diff --git a/app/javascript/flavours/glitch/features/video/index.js b/app/javascript/flavours/glitch/features/video/index.js
index 976cdefc0..cc60a0d2e 100644
--- a/app/javascript/flavours/glitch/features/video/index.js
+++ b/app/javascript/flavours/glitch/features/video/index.js
@@ -195,6 +195,8 @@ class Video extends React.PureComponent {
 
   _updateTime () {
     requestAnimationFrame(() => {
+      if (!this.video) return;
+
       this.handleTimeUpdate();
 
       if (!this.state.paused) {
diff --git a/app/javascript/flavours/glitch/selectors/index.js b/app/javascript/flavours/glitch/selectors/index.js
index 4a3303c36..bb9180d12 100644
--- a/app/javascript/flavours/glitch/selectors/index.js
+++ b/app/javascript/flavours/glitch/selectors/index.js
@@ -183,12 +183,13 @@ export const makeGetNotification = () => {
 export const getAccountGallery = createSelector([
   (state, id) => state.getIn(['timelines', `account:${id}:media`, 'items'], ImmutableList()),
   state       => state.get('statuses'),
-], (statusIds, statuses) => {
+  (state, id) => state.getIn(['accounts', id]),
+], (statusIds, statuses, account) => {
   let medias = ImmutableList();
 
   statusIds.forEach(statusId => {
     const status = statuses.get(statusId);
-    medias = medias.concat(status.get('media_attachments').map(media => media.set('status', status)));
+    medias = medias.concat(status.get('media_attachments').map(media => media.set('status', status).set('account', account)));
   });
 
   return medias;
diff --git a/app/javascript/flavours/glitch/styles/components/modal.scss b/app/javascript/flavours/glitch/styles/components/modal.scss
index 4310f620a..d0be730ac 100644
--- a/app/javascript/flavours/glitch/styles/components/modal.scss
+++ b/app/javascript/flavours/glitch/styles/components/modal.scss
@@ -425,6 +425,14 @@
     padding: initial;
   }
 
+  .status__visibility-icon {
+    color: $dark-text-color;
+    float: right;
+    font-size: 14px;
+    margin-left: 4px;
+    margin-right: 4px;
+  }
+
   .status__display-name {
     display: flex;
   }