about summary refs log tree commit diff
path: root/app/assets/javascripts/components/features
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/components/features')
-rw-r--r--app/assets/javascripts/components/features/account/components/header.jsx51
-rw-r--r--app/assets/javascripts/components/features/compose/components/emoji_picker_dropdown.jsx2
-rw-r--r--app/assets/javascripts/components/features/getting_started/index.jsx2
-rw-r--r--app/assets/javascripts/components/features/ui/index.jsx54
4 files changed, 90 insertions, 19 deletions
diff --git a/app/assets/javascripts/components/features/account/components/header.jsx b/app/assets/javascripts/components/features/account/components/header.jsx
index e1aae3c77..a359963c4 100644
--- a/app/assets/javascripts/components/features/account/components/header.jsx
+++ b/app/assets/javascripts/components/features/account/components/header.jsx
@@ -4,6 +4,7 @@ import emojify from '../../../emoji';
 import escapeTextContentForBrowser from 'escape-html';
 import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
 import IconButton from '../../../components/icon_button';
+import { Motion, spring } from 'react-motion';
 
 const messages = defineMessages({
   unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
@@ -11,6 +12,47 @@ const messages = defineMessages({
   requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' }
 });
 
+const Avatar = React.createClass({
+
+  propTypes: {
+    account: ImmutablePropTypes.map.isRequired
+  },
+
+  getInitialState () {
+    return {
+      isHovered: false
+    };
+  },
+
+  mixins: [PureRenderMixin],
+
+  handleMouseOver () {
+    if (this.state.isHovered) return;
+    this.setState({ isHovered: true });
+  },
+
+  handleMouseOut () {
+    if (!this.state.isHovered) return;
+    this.setState({ isHovered: false });
+  },
+
+  render () {
+    const { account }   = this.props;
+    const { isHovered } = this.state;
+
+    return (
+      <Motion defaultStyle={{ radius: 90 }} style={{ radius: spring(isHovered ? 30 : 90, { stiffness: 180, damping: 12 }) }}>
+        {({ radius }) =>
+          <a href={account.get('url')} className='account__header__avatar' target='_blank' rel='noopener' style={{ display: 'block', width: '90px', height: '90px', margin: '0 auto', marginBottom: '10px', borderRadius: `${radius}px`, overflow: 'hidden' }} onMouseOver={this.handleMouseOver} onMouseOut={this.handleMouseOut}>
+            <img src={account.get('avatar')} alt={account.get('acct')} style={{ display: 'block', width: '90px', height: '90px' }} />
+          </a>
+        }
+      </Motion>
+    );
+  }
+
+});
+
 const Header = React.createClass({
 
   propTypes: {
@@ -68,14 +110,9 @@ const Header = React.createClass({
     return (
       <div className='account__header' style={{ backgroundImage: `url(${account.get('header')})` }}>
         <div style={{ padding: '20px 10px' }}>
-          <a href={account.get('url')} target='_blank' rel='noopener' style={{ display: 'block', color: 'inherit', textDecoration: 'none' }}>
-            <div className='account__header__avatar' style={{ width: '90px', margin: '0 auto', marginBottom: '10px' }}>
-              <img src={account.get('avatar')} alt='' style={{ display: 'block', width: '90px', height: '90px', borderRadius: '90px' }} />
-            </div>
-
-            <span style={{ display: 'inline-block', fontSize: '20px', lineHeight: '27px', fontWeight: '500' }} className='account__header__display-name' dangerouslySetInnerHTML={displayNameHTML} />
-          </a>
+          <Avatar account={account} />
 
+          <span style={{ display: 'inline-block', fontSize: '20px', lineHeight: '27px', fontWeight: '500' }} className='account__header__display-name' dangerouslySetInnerHTML={displayNameHTML} />
           <span className='account__header__username' style={{ fontSize: '14px', fontWeight: '400', display: 'block', marginBottom: '10px' }}>@{account.get('acct')} {lockedIcon}</span>
           <div style={{ fontSize: '14px' }} className='account__header__content' dangerouslySetInnerHTML={content} />
 
diff --git a/app/assets/javascripts/components/features/compose/components/emoji_picker_dropdown.jsx b/app/assets/javascripts/components/features/compose/components/emoji_picker_dropdown.jsx
index 471f0e1bb..1920b29bf 100644
--- a/app/assets/javascripts/components/features/compose/components/emoji_picker_dropdown.jsx
+++ b/app/assets/javascripts/components/features/compose/components/emoji_picker_dropdown.jsx
@@ -43,7 +43,7 @@ const EmojiPickerDropdown = React.createClass({
     return (
       <Dropdown ref={this.setRef} style={style}>
         <DropdownTrigger className='emoji-button' title={intl.formatMessage(messages.emoji)} style={{ fontSize: `24px`, width: `24px`, lineHeight: `24px`, display: 'block', marginLeft: '2px' }}>
-          <img className="emojione" alt="🙂" src="/emoji/1f602.png" />
+          <img draggable="false" className="emojione" alt="🙂" src="/emoji/1f602.svg" />
         </DropdownTrigger>
 
         <DropdownContent className='dropdown__left'>
diff --git a/app/assets/javascripts/components/features/getting_started/index.jsx b/app/assets/javascripts/components/features/getting_started/index.jsx
index 846e4f53b..6f9e988ba 100644
--- a/app/assets/javascripts/components/features/getting_started/index.jsx
+++ b/app/assets/javascripts/components/features/getting_started/index.jsx
@@ -41,7 +41,7 @@ const GettingStarted = ({ intl, me }) => {
         <ColumnLink icon='sign-out' text={intl.formatMessage(messages.sign_out)} href='/auth/sign_out' method='delete' />
       </div>
 
-      <div className='scrollable optionally-scrollable'>
+      <div className='scrollable optionally-scrollable' style={{ display: 'flex', flexDirection: 'column' }}>
         <div className='static-content getting-started'>
           <p><FormattedMessage id='getting_started.about_addressing' defaultMessage='You can follow people if you know their username and the domain they are on by entering an e-mail-esque address into the form at the top of the sidebar.' /></p>
           <p><FormattedMessage id='getting_started.about_shortcuts' defaultMessage='If the target user is on the same domain as you, just the username will work. The same rule applies to mentioning people in statuses.' /></p>
diff --git a/app/assets/javascripts/components/features/ui/index.jsx b/app/assets/javascripts/components/features/ui/index.jsx
index 10e989b2a..4b7e4bb46 100644
--- a/app/assets/javascripts/components/features/ui/index.jsx
+++ b/app/assets/javascripts/components/features/ui/index.jsx
@@ -36,34 +36,62 @@ const UI = React.createClass({
     this.setState({ width: window.innerWidth });
   },
 
+  handleDragEnter (e) {
+    e.preventDefault();
+
+    if (!this.dragTargets) {
+      this.dragTargets = [];
+    }
+
+    if (this.dragTargets.indexOf(e.target) === -1) {
+      this.dragTargets.push(e.target);
+    }
+
+    this.setState({ draggingOver: true });
+  },
+
   handleDragOver (e) {
     e.preventDefault();
     e.stopPropagation();
 
-    e.dataTransfer.dropEffect = 'copy';
+    try {
+      e.dataTransfer.dropEffect = 'copy';
+    } catch (err) {
 
-    if (e.dataTransfer.effectAllowed === 'all' || e.dataTransfer.effectAllowed === 'uninitialized') {
-      this.setState({ draggingOver: true });
     }
+
+    return false;
   },
 
   handleDrop (e) {
     e.preventDefault();
 
+    this.setState({ draggingOver: false });
+
     if (e.dataTransfer && e.dataTransfer.files.length === 1) {
-      this.setState({ draggingOver: false });
       this.props.dispatch(uploadCompose(e.dataTransfer.files));
     }
   },
 
-  handleDragLeave () {
+  handleDragLeave (e) {
+    e.preventDefault();
+    e.stopPropagation();
+
+    this.dragTargets = this.dragTargets.filter(el => el !== e.target && this.node.contains(el));
+
+    if (this.dragTargets.length > 0) {
+      return;
+    }
+
     this.setState({ draggingOver: false });
   },
 
   componentWillMount () {
     window.addEventListener('resize', this.handleResize, { passive: true });
-    window.addEventListener('dragover', this.handleDragOver);
-    window.addEventListener('drop', this.handleDrop);
+    document.addEventListener('dragenter', this.handleDragEnter, false);
+    document.addEventListener('dragover', this.handleDragOver, false);
+    document.addEventListener('drop', this.handleDrop, false);
+    document.addEventListener('dragleave', this.handleDragLeave, false);
 
     this.props.dispatch(refreshTimeline('home'));
     this.props.dispatch(refreshNotifications());
@@ -71,8 +99,14 @@ const UI = React.createClass({
 
   componentWillUnmount () {
     window.removeEventListener('resize', this.handleResize);
-    window.removeEventListener('dragover', this.handleDragOver);
-    window.removeEventListener('drop', this.handleDrop);
+    document.removeEventListener('dragenter', this.handleDragEnter);
+    document.removeEventListener('dragover', this.handleDragOver);
+    document.removeEventListener('drop', this.handleDrop);
+    document.removeEventListener('dragleave', this.handleDragLeave);
+  },
+
+  setRef (c) {
+    this.node = c;
   },
 
   render () {
@@ -99,7 +133,7 @@ const UI = React.createClass({
     }
 
     return (
-      <div className='ui' onDragLeave={this.handleDragLeave}>
+      <div className='ui' ref={this.setRef}>
         <TabsBar />
 
         {mountedColumns}