about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEugen <eugen@zeonfederated.com>2017-04-19 15:37:18 +0200
committerGitHub <noreply@github.com>2017-04-19 15:37:18 +0200
commitbfbc2ca0d8dcef47f8581585b42f13b6b4c933d9 (patch)
tree800163fc3fec1cfbc8fa681ef36b1a89304da207
parent5ce8a1811abbb304a6cf92616a9db4fb3cac89ee (diff)
Attachment list for uncached attachments (#2110)
* For undownloaded attachments, set type :unknown, display them as a list in the web UI

* Fix case when attachment type is set explicitly
-rw-r--r--app/assets/javascripts/components/components/attachment_list.jsx34
-rw-r--r--app/assets/javascripts/components/components/status.jsx5
-rw-r--r--app/assets/javascripts/components/features/status/components/detailed_status.jsx5
-rw-r--r--app/assets/stylesheets/components.scss50
-rw-r--r--app/models/media_attachment.rb11
5 files changed, 99 insertions, 6 deletions
diff --git a/app/assets/javascripts/components/components/attachment_list.jsx b/app/assets/javascripts/components/components/attachment_list.jsx
new file mode 100644
index 000000000..56238fe19
--- /dev/null
+++ b/app/assets/javascripts/components/components/attachment_list.jsx
@@ -0,0 +1,34 @@
+import ImmutablePropTypes from 'react-immutable-proptypes';
+import PureRenderMixin from 'react-addons-pure-render-mixin';
+
+const filename = url => url.split('/').pop().split('#')[0].split('?')[0];
+
+const AttachmentList = React.createClass({
+  propTypes: {
+    media: ImmutablePropTypes.list.isRequired
+  },
+
+  mixins: [PureRenderMixin],
+
+  render () {
+    const { media } = this.props;
+
+    return (
+      <div className='attachment-list'>
+        <div className='attachment-list__icon'>
+          <i className='fa fa-link' />
+        </div>
+
+        <ul className='attachment-list__list'>
+          {media.map(attachment =>
+            <li key={attachment.get('id')}>
+              <a href={attachment.get('remote_url')} target='_blank' rel='noopener'>{filename(attachment.get('remote_url'))}</a>
+            </li>
+          )}
+        </ul>
+      </div>
+    );
+  }
+});
+
+export default AttachmentList;
diff --git a/app/assets/javascripts/components/components/status.jsx b/app/assets/javascripts/components/components/status.jsx
index abc123f26..a5b9ad87c 100644
--- a/app/assets/javascripts/components/components/status.jsx
+++ b/app/assets/javascripts/components/components/status.jsx
@@ -5,6 +5,7 @@ import PureRenderMixin from 'react-addons-pure-render-mixin';
 import DisplayName from './display_name';
 import MediaGallery from './media_gallery';
 import VideoPlayer from './video_player';
+import AttachmentList from './attachment_list';
 import StatusContent from './status_content';
 import StatusActionBar from './status_action_bar';
 import { FormattedMessage } from 'react-intl';
@@ -77,7 +78,9 @@ const Status = React.createClass({
     }
 
     if (status.get('media_attachments').size > 0 && !this.props.muted) {
-      if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
+      if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) {
+
+      } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
         media = <VideoPlayer media={status.getIn(['media_attachments', 0])} sensitive={status.get('sensitive')} onOpenVideo={this.props.onOpenVideo} />;
       } else {
         media = <MediaGallery media={status.get('media_attachments')} sensitive={status.get('sensitive')} height={110} onOpenMedia={this.props.onOpenMedia} autoPlayGif={this.props.autoPlayGif} />;
diff --git a/app/assets/javascripts/components/features/status/components/detailed_status.jsx b/app/assets/javascripts/components/features/status/components/detailed_status.jsx
index 9d854f0d3..620af36fe 100644
--- a/app/assets/javascripts/components/features/status/components/detailed_status.jsx
+++ b/app/assets/javascripts/components/features/status/components/detailed_status.jsx
@@ -5,6 +5,7 @@ import DisplayName from '../../../components/display_name';
 import StatusContent from '../../../components/status_content';
 import MediaGallery from '../../../components/media_gallery';
 import VideoPlayer from '../../../components/video_player';
+import AttachmentList from '../../../components/attachment_list';
 import { Link } from 'react-router';
 import { FormattedDate, FormattedNumber } from 'react-intl';
 import CardContainer from '../containers/card_container';
@@ -40,7 +41,9 @@ const DetailedStatus = React.createClass({
     let applicationLink = '';
 
     if (status.get('media_attachments').size > 0) {
-      if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
+      if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) {
+        media = <AttachmentList media={status.get('media_attachments')} />;
+      } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
         media = <VideoPlayer sensitive={status.get('sensitive')} media={status.getIn(['media_attachments', 0])} width={300} height={150} onOpenVideo={this.props.onOpenVideo} autoplay />;
       } else {
         media = <MediaGallery sensitive={status.get('sensitive')} media={status.get('media_attachments')} height={300} onOpenMedia={this.props.onOpenMedia} autoPlayGif={this.props.autoPlayGif} />;
diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss
index b646b0c77..76a67d8e7 100644
--- a/app/assets/stylesheets/components.scss
+++ b/app/assets/stylesheets/components.scss
@@ -2346,3 +2346,53 @@ button.icon-button.active i.fa-retweet {
     }
   }
 }
+
+.attachment-list {
+  display: flex;
+  font-size: 14px;
+  border: 1px solid lighten($color1, 8%);
+  border-radius: 4px;
+  margin-top: 14px;
+  overflow: hidden;
+}
+
+.attachment-list__icon {
+  flex: 0 0 auto;
+  color: lighten($color1, 26%);
+  padding: 8px 18px;
+  cursor: default;
+  border-right: 1px solid lighten($color1, 8%);
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  font-size: 26px;
+
+  .fa {
+    display: block;
+  }
+}
+
+.attachment-list__list {
+  list-style: none;
+  padding: 4px 0;
+  padding-left: 8px;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+
+  li {
+    display: block;
+    padding: 4px 0;
+  }
+
+  a {
+    text-decoration: none;
+    color: lighten($color1, 26%);
+    font-weight: 500;
+
+    &:hover {
+      text-decoration: underline;
+    }
+  }
+}
diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb
index ec1808790..bb16adb5b 100644
--- a/app/models/media_attachment.rb
+++ b/app/models/media_attachment.rb
@@ -3,7 +3,7 @@
 class MediaAttachment < ApplicationRecord
   self.inheritance_column = nil
 
-  enum type: [:image, :gifv, :video]
+  enum type: [:image, :gifv, :video, :unknown]
 
   IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif'].freeze
   VIDEO_MIME_TYPES = ['video/webm', 'video/mp4'].freeze
@@ -95,6 +95,8 @@ class MediaAttachment < ApplicationRecord
   private
 
   def set_shortcode
+    self.type = :unknown if file.blank? && type.blank?
+
     return unless local?
 
     loop do
@@ -104,9 +106,10 @@ class MediaAttachment < ApplicationRecord
   end
 
   def set_type_and_extension
-    self.type = VIDEO_MIME_TYPES.include?(file_content_type) ? :video : :image
-
-    unless file.blank?
+    if file.blank?
+      self.type = :unknown
+    else
+      self.type = VIDEO_MIME_TYPES.include?(file_content_type) ? :video : :image
       extension = Paperclip::Interpolations.content_type_extension(file, :original)
       basename  = Paperclip::Interpolations.basename(file, :original)
       file.instance_write :file_name, [basename, extension].delete_if(&:empty?).join('.')