about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--app/assets/javascripts/components/components/dropdown_menu.jsx25
-rw-r--r--app/assets/javascripts/components/components/status_action_bar.jsx23
-rw-r--r--app/assets/javascripts/components/features/account/components/action_bar.jsx67
-rw-r--r--app/assets/javascripts/components/features/account/components/header.jsx16
-rw-r--r--app/assets/javascripts/components/features/account/index.jsx2
-rw-r--r--app/assets/javascripts/components/features/status/components/action_bar.jsx16
-rw-r--r--app/assets/javascripts/components/features/status/index.jsx9
7 files changed, 94 insertions, 64 deletions
diff --git a/app/assets/javascripts/components/components/dropdown_menu.jsx b/app/assets/javascripts/components/components/dropdown_menu.jsx
new file mode 100644
index 000000000..619605f63
--- /dev/null
+++ b/app/assets/javascripts/components/components/dropdown_menu.jsx
@@ -0,0 +1,25 @@
+import Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown';
+
+const DropdownMenu = ({ icon, items, size }) => {
+  return (
+    <Dropdown>
+      <DropdownTrigger className='icon-button' style={{ fontSize: `${size}px`, width: `${size}px`, lineHeight: `${size}px` }}>
+        <i className={`fa fa-fw fa-${icon}`} style={{ verticalAlign: 'middle' }} />
+      </DropdownTrigger>
+
+      <DropdownContent style={{ lineHeight: '18px' }}>
+        <ul>
+          {items.map(({ text, action }, i) => <li key={i}><a href='#' onClick={e => { e.preventDefault(); action(); }}>{text}</a></li>)}
+        </ul>
+      </DropdownContent>
+    </Dropdown>
+  );
+};
+
+DropdownMenu.propTypes = {
+  icon: React.PropTypes.string.isRequired,
+  items: React.PropTypes.array.isRequired,
+  size: React.PropTypes.number.isRequired
+};
+
+export default DropdownMenu;
diff --git a/app/assets/javascripts/components/components/status_action_bar.jsx b/app/assets/javascripts/components/components/status_action_bar.jsx
index 3e826d68a..945b722a6 100644
--- a/app/assets/javascripts/components/components/status_action_bar.jsx
+++ b/app/assets/javascripts/components/components/status_action_bar.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 Dropdown, { DropdownTrigger, DropdownContent } from 'react-simple-dropdown';
+import DropdownMenu       from './dropdown_menu';
 
 const StatusActionBar = React.createClass({
   propTypes: {
@@ -26,23 +26,16 @@ const StatusActionBar = React.createClass({
     this.props.onReblog(this.props.status);
   },
 
-  handleDeleteClick(e) {
-    e.preventDefault();
+  handleDeleteClick () {
     this.props.onDelete(this.props.status);
   },
 
   render () {
     const { status, me } = this.props;
-    let menu = '';
+    let menu = [];
 
     if (status.getIn(['account', 'id']) === me) {
-      menu = (
-        <ul>
-          <li><a href='#' onClick={this.handleDeleteClick}>Delete</a></li>
-        </ul>
-      );
-    } else {
-      menu = <ul />;
+      menu.push({ text: 'Delete', action: this.handleDeleteClick });
     }
 
     return (
@@ -52,13 +45,7 @@ const StatusActionBar = React.createClass({
         <div style={{ float: 'left', marginRight: '18px'}}><IconButton active={status.get('favourited')} title='Favourite' icon='star' onClick={this.handleFavouriteClick} /></div>
 
         <div onClick={e => e.stopPropagation()} style={{ width: '18px', height: '18px', float: 'left' }}>
-          <Dropdown>
-            <DropdownTrigger className='icon-button' style={{ fontSize: '18px', lineHeight: '18px', width: '18px', height: '18px' }}>
-              <i className='fa fa-fw fa-ellipsis-h' />
-            </DropdownTrigger>
-
-            <DropdownContent>{menu}</DropdownContent>
-          </Dropdown>
+          <DropdownMenu items={menu} icon='ellipsis-h' size={18} />
         </div>
       </div>
     );
diff --git a/app/assets/javascripts/components/features/account/components/action_bar.jsx b/app/assets/javascripts/components/features/account/components/action_bar.jsx
index aadf168b2..61c89313a 100644
--- a/app/assets/javascripts/components/features/account/components/action_bar.jsx
+++ b/app/assets/javascripts/components/features/account/components/action_bar.jsx
@@ -1,6 +1,6 @@
 import PureRenderMixin    from 'react-addons-pure-render-mixin';
 import ImmutablePropTypes from 'react-immutable-proptypes';
-import Button             from '../../../components/button';
+import DropdownMenu       from '../../../components/dropdown_menu';
 
 const ActionBar = React.createClass({
 
@@ -16,47 +16,42 @@ const ActionBar = React.createClass({
   render () {
     const { account, me } = this.props;
 
-    let infoText     = '';
-    let follow       = '';
-    let buttonText   = '';
-    let block        = '';
-    let disabled     = false;
+    let menu = [];
 
     if (account.get('id') === me) {
-      buttonText = 'This is you!';
-      disabled   = true;
-    } else {
-      let blockText = '';
-
-      if (account.getIn(['relationship', 'blocking'])) {
-        buttonText = 'Blocked';
-        disabled   = true;
-        blockText  = 'Unblock';
-      } else {
-        if (account.getIn(['relationship', 'following'])) {
-          buttonText = 'Unfollow';
-        } else {
-          buttonText = 'Follow';
-        }
-
-        if (account.getIn(['relationship', 'followed_by'])) {
-          infoText = 'Follows you!';
-        }
-
-        blockText = 'Block';
-      }
 
-      block = <Button text={blockText} onClick={this.props.onBlock} />;
-    }
-
-    if (!account.getIn(['relationship', 'blocking'])) {
-      follow = <Button text={buttonText} onClick={this.props.onFollow} disabled={disabled} />;
+    } else if (account.getIn(['relationship', 'blocking'])) {
+      menu.push({ text: 'Unblock', action: this.props.onBlock });
+    } else if (account.getIn(['relationship', 'following'])) {
+      menu.push({ text: 'Unfollow', action: this.props.onFollow });
+      menu.push({ text: 'Block', action: this.props.onBlock });
+    } else {
+      menu.push({ text: 'Follow', action: this.props.onFollow });
+      menu.push({ text: 'Block', action: this.props.onBlock });
     }
 
     return (
-      <div style={{ borderTop: '1px solid #363c4b', borderBottom: '1px solid #363c4b', padding: '10px', lineHeight: '36px', overflow: 'hidden', flex: '0 0 auto' }}>
-        {follow} {block}
-        <span style={{ color: '#616b86', fontWeight: '500', textTransform: 'uppercase', float: 'right', display: 'block' }}>{infoText}</span>
+      <div style={{ borderTop: '1px solid #363c4b', borderBottom: '1px solid #363c4b', lineHeight: '36px', overflow: 'hidden', flex: '0 0 auto', display: 'flex' }}>
+        <div style={{ flex: '1 1 auto', display: 'flex', lineHeight: '18px' }}>
+          <div style={{ overflow: 'hidden', width: '80px', borderRight: '1px solid #363c4b', padding: '10px', paddingRight: '5px' }}>
+            <span style={{ display: 'block', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}>Posts</span>
+            <span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}>{account.get('statuses_count')}</span>
+          </div>
+
+          <div style={{ overflow: 'hidden', width: '80px', borderRight: '1px solid #363c4b', padding: '10px 5px' }}>
+            <span style={{ display: 'block', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}>Follows</span>
+            <span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}>{account.get('following_count')}</span>
+          </div>
+
+          <div style={{ overflow: 'hidden', width: '80px', padding: '10px 5px', borderRight: '1px solid #363c4b' }}>
+            <span style={{ display: 'block', textTransform: 'uppercase', fontSize: '11px', color: '#616b86' }}>Followers</span>
+            <span style={{ display: 'block', fontSize: '15px', fontWeight: '500', color: '#fff' }}>{account.get('followers_count')}</span>
+          </div>
+        </div>
+
+        <div style={{ padding: '10px', flex: '1 1 auto' }}>
+          <DropdownMenu items={menu} icon='bars' size={24} />
+        </div>
       </div>
     );
   },
diff --git a/app/assets/javascripts/components/features/account/components/header.jsx b/app/assets/javascripts/components/features/account/components/header.jsx
index f14b6f480..681b30ca8 100644
--- a/app/assets/javascripts/components/features/account/components/header.jsx
+++ b/app/assets/javascripts/components/features/account/components/header.jsx
@@ -4,33 +4,41 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 const Header = React.createClass({
 
   propTypes: {
-    account: ImmutablePropTypes.map.isRequired
+    account: ImmutablePropTypes.map.isRequired,
+    me: React.PropTypes.number.isRequired
   },
 
   mixins: [PureRenderMixin],
 
   render () {
-    const { account } = this.props;
+    const { account, me } = this.props;
 
     let displayName = account.get('display_name');
+    let info = '';
 
     if (displayName.length === 0) {
       displayName = account.get('username');
     }
 
+    if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
+      info = <span style={{ position: 'absolute', top: '10px', right: '10px', opacity: '0.7', display: 'inline-block', verticalAlign: 'top', background: 'rgba(0, 0, 0, 0.4)', color: '#fff', textTransform: 'uppercase', fontSize: '11px', fontWeight: '500', padding: '4px', borderRadius: '4px' }}>Follows you</span>
+    }
+
     return (
-      <div style={{ flex: '0 0 auto', background: '#2f3441', textAlign: 'center', backgroundImage: `url(${account.get('header')})`, backgroundSize: 'cover' }}>
+      <div style={{ flex: '0 0 auto', background: '#2f3441', textAlign: 'center', backgroundImage: `url(${account.get('header')})`, backgroundSize: 'cover', position: 'relative' }}>
         <div style={{ background: 'rgba(47, 52, 65, 0.8)', padding: '30px 10px' }}>
           <a href={account.get('url')} target='_blank' rel='noopener' style={{ display: 'block', color: 'inherit', textDecoration: 'none' }}>
             <div style={{ width: '90px', margin: '0 auto', marginBottom: '15px' }}>
               <img src={account.get('avatar')} alt='' style={{ display: 'block', width: '90px', height: '90px', borderRadius: '90px' }} />
             </div>
 
-            <span style={{ color: '#fff', fontSize: '20px', lineHeight: '27px', fontWeight: '500', display: 'block' }}>{displayName}</span>
+            <span style={{ display: 'inline-block', color: '#fff', fontSize: '20px', lineHeight: '27px', fontWeight: '500' }}>{displayName}</span>
           </a>
 
           <span style={{ fontSize: '14px', fontWeight: '400', display: 'block', color: '#2b90d9', marginBottom: '15px' }}>@{account.get('acct')}</span>
           <p style={{ color: '#616b86', fontSize: '14px' }}>{account.get('note')}</p>
+
+          {info}
         </div>
       </div>
     );
diff --git a/app/assets/javascripts/components/features/account/index.jsx b/app/assets/javascripts/components/features/account/index.jsx
index 2af8c9f47..cdda4ff2f 100644
--- a/app/assets/javascripts/components/features/account/index.jsx
+++ b/app/assets/javascripts/components/features/account/index.jsx
@@ -75,7 +75,7 @@ const Account = React.createClass({
     return (
       <Column>
         <div style={{ display: 'flex', flexDirection: 'column', 'flex': '0 0 auto', height: '100%' }}>
-          <Header account={account} />
+          <Header account={account} me={me} />
 
           <ActionBar account={account} me={me} onFollow={this.handleFollow} onBlock={this.handleBlock} />
 
diff --git a/app/assets/javascripts/components/features/status/components/action_bar.jsx b/app/assets/javascripts/components/features/status/components/action_bar.jsx
index 65c107edc..6d6aa87fc 100644
--- a/app/assets/javascripts/components/features/status/components/action_bar.jsx
+++ b/app/assets/javascripts/components/features/status/components/action_bar.jsx
@@ -1,26 +1,36 @@
 import PureRenderMixin    from 'react-addons-pure-render-mixin';
 import IconButton         from '../../../components/icon_button';
 import ImmutablePropTypes from 'react-immutable-proptypes';
+import DropdownMenu       from '../../../components/dropdown_menu';
 
 const ActionBar = React.createClass({
-  
+
   propTypes: {
     status: ImmutablePropTypes.map.isRequired,
     onReply: React.PropTypes.func.isRequired,
     onReblog: React.PropTypes.func.isRequired,
-    onFavourite: React.PropTypes.func.isRequired
+    onFavourite: React.PropTypes.func.isRequired,
+    onDelete: React.PropTypes.func.isRequired,
+    me: React.PropTypes.number.isRequired
   },
 
   mixins: [PureRenderMixin],
 
   render () {
-    const { status } = this.props;
+    const { status, me } = this.props;
+
+    let menu = [];
+
+    if (me === status.getIn(['account', 'id'])) {
+      menu.push({ text: 'Delete', action: () => this.props.onDelete(status) });
+    }
 
     return (
       <div style={{ background: '#2f3441', display: 'flex', flexDirection: 'row', borderTop: '1px solid #363c4b', borderBottom: '1px solid #363c4b', padding: '10px 0' }}>
         <div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton title='Reply' icon='reply' onClick={() => this.props.onReply(status)} /></div>
         <div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton active={status.get('reblogged')} title='Reblog' icon='retweet' onClick={() => this.props.onReblog(status)} /></div>
         <div style={{ flex: '1 1 auto', textAlign: 'center' }}><IconButton active={status.get('favourited')} title='Favourite' icon='star' onClick={() => this.props.onFavourite(status)} /></div>
+        <div style={{ flex: '1 1 auto', textAlign: 'center' }}><DropdownMenu size={18} icon='ellipsis-h' items={menu} /></div>
       </div>
     );
   }
diff --git a/app/assets/javascripts/components/features/status/index.jsx b/app/assets/javascripts/components/features/status/index.jsx
index 1d40f127b..cc7a2bfeb 100644
--- a/app/assets/javascripts/components/features/status/index.jsx
+++ b/app/assets/javascripts/components/features/status/index.jsx
@@ -10,6 +10,7 @@ import ActionBar             from './components/action_bar';
 import Column                from '../ui/components/column';
 import { favourite, reblog } from '../../actions/interactions';
 import { replyCompose }      from '../../actions/compose';
+import { deleteStatus }      from '../../actions/statuses';
 import {
   getStatus,
   getStatusAncestors,
@@ -57,8 +58,12 @@ const Status = React.createClass({
     this.props.dispatch(reblog(status));
   },
 
+  handleDeleteClick (status) {
+    this.props.dispatch(deleteStatus(status.get('id')));
+  },
+
   renderChildren (list) {
-    return list.map(s => <EmbeddedStatus status={s} me={this.props.me} key={s.get('id')} onReply={this.handleReplyClick} onFavourite={this.handleFavouriteClick} onReblog={this.handleReblogClick} />);
+    return list.map(s => <EmbeddedStatus status={s} me={this.props.me} key={s.get('id')} onReply={this.handleReplyClick} onFavourite={this.handleFavouriteClick} onReblog={this.handleReblogClick} onDelete={this.handleDeleteClick} />);
   },
 
   render () {
@@ -80,7 +85,7 @@ const Status = React.createClass({
           <div>{this.renderChildren(ancestors)}</div>
 
           <DetailedStatus status={status} me={me} />
-          <ActionBar status={status} onReply={this.handleReplyClick} onFavourite={this.handleFavouriteClick} onReblog={this.handleReblogClick} />
+          <ActionBar status={status} me={me} onReply={this.handleReplyClick} onFavourite={this.handleFavouriteClick} onReblog={this.handleReblogClick} onDelete={this.handleDeleteClick} />
 
           <div>{this.renderChildren(descendants)}</div>
         </div>