diff options
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> |