about summary refs log tree commit diff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/javascript/mastodon/components/status.js5
-rw-r--r--app/javascript/mastodon/components/status_content.js19
-rw-r--r--app/javascript/mastodon/features/status/components/detailed_status.js5
-rw-r--r--app/javascript/packs/public.js137
-rw-r--r--app/views/stream_entries/_detailed_status.html.haml13
-rw-r--r--app/views/stream_entries/_simple_status.html.haml12
6 files changed, 168 insertions, 23 deletions
diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js
index 3c3ef2c7a..f74e2acce 100644
--- a/app/javascript/mastodon/components/status.js
+++ b/app/javascript/mastodon/components/status.js
@@ -16,7 +16,6 @@ import { MediaGallery, Video } from '../features/ui/util/async-components';
 import { HotKeys } from 'react-hotkeys';
 import classNames from 'classnames';
 import Icon from 'mastodon/components/icon';
-import PollContainer from 'mastodon/containers/poll_container';
 import { displayMedia } from '../initial_state';
 
 // We use the component (and not the container) since we do not want
@@ -320,9 +319,7 @@ class Status extends ImmutablePureComponent {
       status  = status.get('reblog');
     }
 
-    if (status.get('poll')) {
-      media = <PollContainer pollId={status.get('poll')} />;
-    } else if (status.get('media_attachments').size > 0) {
+    if (status.get('media_attachments').size > 0) {
       if (this.props.muted) {
         media = (
           <AttachmentList
diff --git a/app/javascript/mastodon/components/status_content.js b/app/javascript/mastodon/components/status_content.js
index fa8901386..01b4351be 100644
--- a/app/javascript/mastodon/components/status_content.js
+++ b/app/javascript/mastodon/components/status_content.js
@@ -5,6 +5,7 @@ import { isRtl } from '../rtl';
 import { FormattedMessage } from 'react-intl';
 import Permalink from './permalink';
 import classnames from 'classnames';
+import PollContainer from 'mastodon/containers/poll_container';
 import Icon from 'mastodon/components/icon';
 
 const MAX_HEIGHT = 642; // 20px * 32 (+ 2px padding at the top)
@@ -191,6 +192,8 @@ export default class StatusContent extends React.PureComponent {
           {mentionsPlaceholder}
 
           <div tabIndex={!hidden ? 0 : null} className={`status__content__text ${!hidden ? 'status__content__text--visible' : ''}`} style={directionStyle} dangerouslySetInnerHTML={content} lang={status.get('language')} />
+
+          {!hidden && !!status.get('poll') && <PollContainer pollId={status.get('poll')} />}
         </div>
       );
     } else if (this.props.onClick) {
@@ -212,9 +215,13 @@ export default class StatusContent extends React.PureComponent {
         output.push(readMoreButton);
       }
 
+      if (status.get('poll')) {
+        output.push(<PollContainer pollId={status.get('poll')} />);
+      }
+
       return output;
     } else {
-      return (
+      const output = [
         <div
           tabIndex='0'
           ref={this.setRef}
@@ -222,8 +229,14 @@ export default class StatusContent extends React.PureComponent {
           style={directionStyle}
           dangerouslySetInnerHTML={content}
           lang={status.get('language')}
-        />
-      );
+        />,
+      ];
+
+      if (status.get('poll')) {
+        output.push(<PollContainer pollId={status.get('poll')} />);
+      }
+
+      return output;
     }
   }
 
diff --git a/app/javascript/mastodon/features/status/components/detailed_status.js b/app/javascript/mastodon/features/status/components/detailed_status.js
index c6682b767..7c9b3a01c 100644
--- a/app/javascript/mastodon/features/status/components/detailed_status.js
+++ b/app/javascript/mastodon/features/status/components/detailed_status.js
@@ -13,7 +13,6 @@ import Video from '../../video';
 import scheduleIdleTask from '../../ui/util/schedule_idle_task';
 import classNames from 'classnames';
 import Icon from 'mastodon/components/icon';
-import PollContainer from 'mastodon/containers/poll_container';
 
 export default class DetailedStatus extends ImmutablePureComponent {
 
@@ -107,9 +106,7 @@ export default class DetailedStatus extends ImmutablePureComponent {
       outerStyle.height = `${this.state.height}px`;
     }
 
-    if (status.get('poll')) {
-      media = <PollContainer pollId={status.get('poll')} />;
-    } else if (status.get('media_attachments').size > 0) {
+    if (status.get('media_attachments').size > 0) {
       if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
         const video = status.getIn(['media_attachments', 0]);
 
diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js
index 69e6ba0ec..3135636cf 100644
--- a/app/javascript/packs/public.js
+++ b/app/javascript/packs/public.js
@@ -1,9 +1,26 @@
+import escapeTextContentForBrowser from 'escape-html';
 import loadPolyfills from '../mastodon/load_polyfills';
 import ready from '../mastodon/ready';
 import { start } from '../mastodon/common';
 
 start();
 
+window.addEventListener('message', e => {
+  const data = e.data || {};
+
+  if (!window.parent || data.type !== 'setHeight') {
+    return;
+  }
+
+  ready(() => {
+    window.parent.postMessage({
+      type: 'setHeight',
+      id: data.id,
+      height: document.getElementsByTagName('html')[0].scrollHeight,
+    }, '*');
+  });
+});
+
 function main() {
   const IntlMessageFormat = require('intl-messageformat').default;
   const { timeAgoString } = require('../mastodon/components/relative_timestamp');
@@ -100,6 +117,126 @@ function main() {
       scrollbarWidthStyle.sheet.insertRule(`body.with-modals--active { margin-right: ${scrollbarWidth}px; }`, 0);
     }
   });
+
+  delegate(document, '.webapp-btn', 'click', ({ target, button }) => {
+    if (button !== 0) {
+      return true;
+    }
+    window.location.href = target.href;
+    return false;
+  });
+
+  delegate(document, '.status__content__spoiler-link', 'click', function() {
+    const contentEl = this.parentNode.parentNode.querySelector('.e-content');
+
+    if (contentEl.style.display === 'block') {
+      contentEl.style.display = 'none';
+      this.parentNode.style.marginBottom = 0;
+    } else {
+      contentEl.style.display = 'block';
+      this.parentNode.style.marginBottom = null;
+    }
+
+    return false;
+  });
+
+  delegate(document, '.modal-button', 'click', e => {
+    e.preventDefault();
+
+    let href;
+
+    if (e.target.nodeName !== 'A') {
+      href = e.target.parentNode.href;
+    } else {
+      href = e.target.href;
+    }
+
+    window.open(href, 'mastodon-intent', 'width=445,height=600,resizable=no,menubar=no,status=no,scrollbars=yes');
+  });
+
+  delegate(document, '#account_display_name', 'input', ({ target }) => {
+    const name = document.querySelector('.card .display-name strong');
+    if (name) {
+      if (target.value) {
+        name.innerHTML = emojify(escapeTextContentForBrowser(target.value));
+      } else {
+        name.textContent = document.querySelector('#default_account_display_name').textContent;
+      }
+    }
+  });
+
+  delegate(document, '#account_avatar', 'change', ({ target }) => {
+    const avatar = document.querySelector('.card .avatar img');
+    const [file] = target.files || [];
+    const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc;
+
+    avatar.src = url;
+  });
+
+  const getProfileAvatarAnimationHandler = (swapTo) => {
+    //animate avatar gifs on the profile page when moused over
+    return ({ target }) => {
+      const swapSrc = target.getAttribute(swapTo);
+      //only change the img source if autoplay is off and the image src is actually different
+      if(target.getAttribute('data-autoplay') === 'false' && target.src !== swapSrc) {
+        target.src = swapSrc;
+      }
+    };
+  };
+
+  delegate(document, 'img#profile_page_avatar', 'mouseover', getProfileAvatarAnimationHandler('data-original'));
+
+  delegate(document, 'img#profile_page_avatar', 'mouseout', getProfileAvatarAnimationHandler('data-static'));
+
+  delegate(document, '#account_header', 'change', ({ target }) => {
+    const header = document.querySelector('.card .card__img img');
+    const [file] = target.files || [];
+    const url = file ? URL.createObjectURL(file) : header.dataset.originalSrc;
+
+    header.src = url;
+  });
+
+  delegate(document, '#account_locked', 'change', ({ target }) => {
+    const lock = document.querySelector('.card .display-name i');
+
+    if (target.checked) {
+      lock.style.display = 'inline';
+    } else {
+      lock.style.display = 'none';
+    }
+  });
+
+  delegate(document, '.input-copy input', 'click', ({ target }) => {
+    target.focus();
+    target.select();
+    target.setSelectionRange(0, target.value.length);
+  });
+
+  delegate(document, '.input-copy button', 'click', ({ target }) => {
+    const input = target.parentNode.querySelector('.input-copy__wrapper input');
+
+    const oldReadOnly = input.readonly;
+
+    input.readonly = false;
+    input.focus();
+    input.select();
+    input.setSelectionRange(0, input.value.length);
+
+    try {
+      if (document.execCommand('copy')) {
+        input.blur();
+        target.parentNode.classList.add('copied');
+
+        setTimeout(() => {
+          target.parentNode.classList.remove('copied');
+        }, 700);
+      }
+    } catch (err) {
+      console.error(err);
+    }
+
+    input.readonly = oldReadOnly;
+  });
 }
 
 loadPolyfills().then(main).catch(error => {
diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml
index c94e7b3d6..2d417d011 100644
--- a/app/views/stream_entries/_detailed_status.html.haml
+++ b/app/views/stream_entries/_detailed_status.html.haml
@@ -19,13 +19,14 @@
     - if status.spoiler_text?
       %p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }<
         %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp;
-        %a.status__content__spoiler-link{ href: '#' }= t('statuses.show_more')
-    .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }= Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
+        %button.status__content__spoiler-link= t('statuses.show_more')
+    .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }
+      = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
+      - if status.preloadable_poll
+        = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do
+          = render partial: 'stream_entries/poll', locals: { status: status, poll: status.preloadable_poll, autoplay: autoplay }
 
-  - if status.preloadable_poll
-    = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do
-      = render partial: 'stream_entries/poll', locals: { status: status, poll: status.preloadable_poll, autoplay: autoplay }
-  - elsif !status.media_attachments.empty?
+  - if !status.media_attachments.empty?
     - if status.media_attachments.first.video?
       - video = status.media_attachments.first
       = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), blurhash: video.blurhash, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, width: 670, height: 380, detailed: true, inline: true, alt: video.description do
diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml
index c53503a7f..96633b636 100644
--- a/app/views/stream_entries/_simple_status.html.haml
+++ b/app/views/stream_entries/_simple_status.html.haml
@@ -23,14 +23,14 @@
     - if status.spoiler_text?
       %p{ :style => ('margin-bottom: 0' unless current_account&.user&.setting_expand_spoilers) }<
         %span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp;
-        %a.status__content__spoiler-link{ href: '#' }= t('statuses.show_more')
-    .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }<
+        %button.status__content__spoiler-link= t('statuses.show_more')
+    .e-content{ lang: status.language, style: "display: #{!current_account&.user&.setting_expand_spoilers && status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl_status?(status) ? 'rtl' : 'ltr'}" }
       = Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
+      - if status.preloadable_poll
+        = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do
+          = render partial: 'stream_entries/poll', locals: { status: status, poll: status.preloadable_poll, autoplay: autoplay }
 
-  - if status.preloadable_poll
-    = react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do
-      = render partial: 'stream_entries/poll', locals: { status: status, poll: status.preloadable_poll, autoplay: autoplay }
-  - elsif !status.media_attachments.empty?
+  - if !status.media_attachments.empty?
     - if status.media_attachments.first.video?
       - video = status.media_attachments.first
       = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), blurhash: video.blurhash, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, width: 610, height: 343, inline: true, alt: video.description do