about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
authorEugen Rochko <eugen@zeonfederated.com>2016-11-23 11:23:32 +0100
committerEugen Rochko <eugen@zeonfederated.com>2016-11-23 11:23:32 +0100
commit5434ad3002b95f194a013fc327f1fdcabacf649a (patch)
tree16470f4fa7b7e4a7325b777ba4e03eb11c3a3eb9 /app
parent0603971894a967f632020277c32a8e50ea165519 (diff)
Add content spoilers for media in sensitive-content statuses
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/components/components/media_gallery.jsx158
-rw-r--r--app/assets/javascripts/components/components/status.jsx4
-rw-r--r--app/assets/javascripts/components/components/video_player.jsx37
3 files changed, 141 insertions, 58 deletions
diff --git a/app/assets/javascripts/components/components/media_gallery.jsx b/app/assets/javascripts/components/components/media_gallery.jsx
index bdb456a08..e46933f7b 100644
--- a/app/assets/javascripts/components/components/media_gallery.jsx
+++ b/app/assets/javascripts/components/components/media_gallery.jsx
@@ -1,9 +1,43 @@
 import ImmutablePropTypes from 'react-immutable-proptypes';
-import PureRenderMixin    from 'react-addons-pure-render-mixin';
+import PureRenderMixin from 'react-addons-pure-render-mixin';
+import { FormattedMessage } from 'react-intl';
+
+const outerStyle = {
+  marginTop: '8px',
+  overflow: 'hidden',
+  width: '100%',
+  boxSizing: 'border-box'
+};
+
+const spoilerStyle = {
+  background: '#000',
+  color: '#fff',
+  textAlign: 'center',
+  height: '100%',
+  cursor: 'pointer'
+};
+
+const spoilerSpanStyle = {
+  display: 'block',
+  fontSize: '14px',
+  paddingTop: '45%'
+};
+
+const spoilerSubSpanStyle = {
+  fontSize: '11px',
+  fontWeight: '500'
+};
 
 const MediaGallery = React.createClass({
 
+  getInitialState () {
+    return {
+      visible: false
+    };
+  },
+
   propTypes: {
+    sensitive: React.PropTypes.bool,
     media: ImmutablePropTypes.list.isRequired,
     height: React.PropTypes.number.isRequired,
     onOpenMedia: React.PropTypes.func.isRequired
@@ -20,69 +54,85 @@ const MediaGallery = React.createClass({
     e.stopPropagation();
   },
 
+  handleOpen () {
+    this.setState({ visible: true });
+  },
+
   render () {
-    var children = this.props.media.take(4);
-    var size     = children.size;
-
-    children = children.map((attachment, i) => {
-      let width  = 50;
-      let height = 100;
-      let top    = 'auto';
-      let left   = 'auto';
-      let bottom = 'auto';
-      let right  = 'auto';
-
-      if (size === 1) {
-        width = 100;
-      }
-
-      if (size === 4 || (size === 3 && i > 0)) {
-        height = 50;
-      }
-
-      if (size === 2) {
-        if (i === 0) {
-          right = '2px';
-        } else {
-          left = '2px';
-        }
-      } else if (size === 3) {
-        if (i === 0) {
-          right = '2px';
-        } else if (i > 0) {
-          left = '2px';
-        }
+    const { media, sensitive } = this.props;
 
-        if (i === 1) {
-          bottom = '2px';
-        } else if (i > 1) {
-          top = '2px';
-        }
-      } else if (size === 4) {
-        if (i === 0 || i === 2) {
-          right = '2px';
+    let children;
+
+    if (sensitive && !this.state.visible) {
+      children = (
+        <div style={spoilerStyle} onClick={this.handleOpen}>
+          <span style={spoilerSpanStyle}><FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' /></span>
+          <span style={spoilerSubSpanStyle}><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span>
+        </div>
+      );
+    } else {
+      const size = media.take(4).size;
+
+      children = media.take(4).map((attachment, i) => {
+        let width  = 50;
+        let height = 100;
+        let top    = 'auto';
+        let left   = 'auto';
+        let bottom = 'auto';
+        let right  = 'auto';
+
+        if (size === 1) {
+          width = 100;
         }
 
-        if (i === 1 || i === 3) {
-          left = '2px';
+        if (size === 4 || (size === 3 && i > 0)) {
+          height = 50;
         }
 
-        if (i < 2) {
-          bottom = '2px';
-        } else {
-          top = '2px';
+        if (size === 2) {
+          if (i === 0) {
+            right = '2px';
+          } else {
+            left = '2px';
+          }
+        } else if (size === 3) {
+          if (i === 0) {
+            right = '2px';
+          } else if (i > 0) {
+            left = '2px';
+          }
+
+          if (i === 1) {
+            bottom = '2px';
+          } else if (i > 1) {
+            top = '2px';
+          }
+        } else if (size === 4) {
+          if (i === 0 || i === 2) {
+            right = '2px';
+          }
+
+          if (i === 1 || i === 3) {
+            left = '2px';
+          }
+
+          if (i < 2) {
+            bottom = '2px';
+          } else {
+            top = '2px';
+          }
         }
-      }
 
-      return (
-        <div key={attachment.get('id')} style={{ boxSizing: 'border-box', position: 'relative', left: left, top: top, right: right, bottom: bottom, float: 'left', border: 'none', display: 'block', width: `${width}%`, height: `${height}%` }}>
-          <a href={attachment.get('url')} onClick={this.handleClick.bind(this, attachment.get('url'))} target='_blank' style={{ display: 'block', width: '100%', height: '100%', background: `url(${attachment.get('preview_url')}) no-repeat center`, textDecoration: 'none', backgroundSize: 'cover', cursor: 'zoom-in' }} />
-        </div>
-      );
-    });
+        return (
+          <div key={attachment.get('id')} style={{ boxSizing: 'border-box', position: 'relative', left: left, top: top, right: right, bottom: bottom, float: 'left', border: 'none', display: 'block', width: `${width}%`, height: `${height}%` }}>
+            <a href={attachment.get('url')} onClick={this.handleClick.bind(this, attachment.get('url'))} target='_blank' style={{ display: 'block', width: '100%', height: '100%', background: `url(${attachment.get('preview_url')}) no-repeat center`, textDecoration: 'none', backgroundSize: 'cover', cursor: 'zoom-in' }} />
+          </div>
+        );
+      });
+    }
 
     return (
-      <div style={{ marginTop: '8px', overflow: 'hidden', width: '100%', height: `${this.props.height}px`, boxSizing: 'border-box' }}>
+      <div style={{ ...outerStyle, height: `${this.props.height}px` }}>
         {children}
       </div>
     );
diff --git a/app/assets/javascripts/components/components/status.jsx b/app/assets/javascripts/components/components/status.jsx
index 84cd07527..bf851e5bf 100644
--- a/app/assets/javascripts/components/components/status.jsx
+++ b/app/assets/javascripts/components/components/status.jsx
@@ -83,9 +83,9 @@ const Status = React.createClass({
 
     if (status.get('media_attachments').size > 0) {
       if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
-        media = <VideoPlayer media={status.getIn(['media_attachments', 0])} />;
+        media = <VideoPlayer media={status.getIn(['media_attachments', 0])} sensitive={status.get('sensitive')} />;
       } else {
-        media = <MediaGallery media={status.get('media_attachments')} height={110} onOpenMedia={this.props.onOpenMedia} />;
+        media = <MediaGallery media={status.get('media_attachments')} sensitive={status.get('sensitive')} height={110} onOpenMedia={this.props.onOpenMedia} />;
       }
     }
 
diff --git a/app/assets/javascripts/components/components/video_player.jsx b/app/assets/javascripts/components/components/video_player.jsx
index 9b9b0a2e4..a789f65e4 100644
--- a/app/assets/javascripts/components/components/video_player.jsx
+++ b/app/assets/javascripts/components/components/video_player.jsx
@@ -1,7 +1,7 @@
 import ImmutablePropTypes from 'react-immutable-proptypes';
 import PureRenderMixin from 'react-addons-pure-render-mixin';
 import IconButton from './icon_button';
-import { defineMessages, injectIntl } from 'react-intl';
+import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 
 const messages = defineMessages({
   toggle_sound: { id: 'video_player.toggle_sound', defaultMessage: 'Toggle sound' }
@@ -25,6 +25,25 @@ const muteStyle = {
   zIndex: '5'
 };
 
+const spoilerStyle = {
+  background: '#000',
+  color: '#fff',
+  textAlign: 'center',
+  height: '100%',
+  cursor: 'pointer'
+};
+
+const spoilerSpanStyle = {
+  display: 'block',
+  fontSize: '14px',
+  paddingTop: '45%'
+};
+
+const spoilerSubSpanStyle = {
+  fontSize: '11px',
+  fontWeight: '500'
+};
+
 const VideoPlayer = React.createClass({
   propTypes: {
     media: ImmutablePropTypes.map.isRequired,
@@ -41,6 +60,7 @@ const VideoPlayer = React.createClass({
 
   getInitialState () {
     return {
+      visible: false,
       muted: true
     };
   },
@@ -63,8 +83,21 @@ const VideoPlayer = React.createClass({
     }
   },
 
+  handleOpen () {
+    this.setState({ visible: true });
+  },
+
   render () {
-    const { media, intl, width, height } = this.props;
+    const { media, intl, width, height, sensitive } = this.props;
+
+    if (sensitive && !this.state.visible) {
+      return (
+        <div style={spoilerStyle} onClick={this.handleOpen}>
+          <span style={spoilerSpanStyle}><FormattedMessage id='status.sensitive_warning' defaultMessage='Sensitive content' /></span>
+          <span style={spoilerSubSpanStyle}><FormattedMessage id='status.sensitive_toggle' defaultMessage='Click to view' /></span>
+        </div>
+      );
+    }
 
     return (
       <div style={{ cursor: 'default', marginTop: '8px', overflow: 'hidden', width: `${width}px`, height: `${height}px`, boxSizing: 'border-box', background: '#000', position: 'relative' }}>