Deleting statuses from UI
This commit is contained in:
		
							parent
							
								
									a41c3487bd
								
							
						
					
					
						commit
						ef2b50c9ac
					
				@ -5,6 +5,10 @@ export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
 | 
				
			|||||||
export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS';
 | 
					export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS';
 | 
				
			||||||
export const STATUS_FETCH_FAIL    = 'STATUS_FETCH_FAIL';
 | 
					export const STATUS_FETCH_FAIL    = 'STATUS_FETCH_FAIL';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const STATUS_DELETE_REQUEST = 'STATUS_DELETE_REQUEST';
 | 
				
			||||||
 | 
					export const STATUS_DELETE_SUCCESS = 'STATUS_DELETE_SUCCESS';
 | 
				
			||||||
 | 
					export const STATUS_DELETE_FAIL    = 'STATUS_DELETE_FAIL';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function fetchStatusRequest(id) {
 | 
					export function fetchStatusRequest(id) {
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    type: STATUS_FETCH_REQUEST,
 | 
					    type: STATUS_FETCH_REQUEST,
 | 
				
			||||||
@ -41,3 +45,37 @@ export function fetchStatusFail(id, error) {
 | 
				
			|||||||
    error: error
 | 
					    error: error
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function deleteStatus(id) {
 | 
				
			||||||
 | 
					  return (dispatch, getState) => {
 | 
				
			||||||
 | 
					    dispatch(deleteStatusRequest(id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    api(getState).delete(`/api/v1/statuses/${id}`).then(response => {
 | 
				
			||||||
 | 
					      dispatch(deleteStatusSuccess(id));
 | 
				
			||||||
 | 
					    }).catch(error => {
 | 
				
			||||||
 | 
					      dispatch(deleteStatusFail(id, error));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function deleteStatusRequest(id) {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    type: STATUS_DELETE_REQUEST,
 | 
				
			||||||
 | 
					    id: id
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function deleteStatusSuccess(id) {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    type: STATUS_DELETE_SUCCESS,
 | 
				
			||||||
 | 
					    id: id
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function deleteStatusFail(id, error) {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    type: STATUS_DELETE_FAIL,
 | 
				
			||||||
 | 
					    id: id,
 | 
				
			||||||
 | 
					    error: error
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -26,8 +26,16 @@ const IconButton = React.createClass({
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
 | 
					    const style = {
 | 
				
			||||||
 | 
					      display: 'inline-block',
 | 
				
			||||||
 | 
					      fontSize: `${this.props.size}px`,
 | 
				
			||||||
 | 
					      width: `${this.props.size}px`,
 | 
				
			||||||
 | 
					      height: `${this.props.size}px`,
 | 
				
			||||||
 | 
					      lineHeight: `${this.props.size}px`
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <a href='#' title={this.props.title} className={`icon-button ${this.props.active ? 'active' : ''}`} onClick={this.handleClick} style={{ display: 'inline-block', fontSize: `${this.props.size}px`, width: `${this.props.size}px`, height: `${this.props.size}px`, lineHeight: `${this.props.size}px`}}>
 | 
					      <a href='#' title={this.props.title} className={`icon-button ${this.props.active ? 'active' : ''}`} onClick={this.handleClick} style={style}>
 | 
				
			||||||
        <i className={`fa fa-fw fa-${this.props.icon}`}></i>
 | 
					        <i className={`fa fa-fw fa-${this.props.icon}`}></i>
 | 
				
			||||||
      </a>
 | 
					      </a>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
 | 
				
			|||||||
@ -2,11 +2,11 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
 | 
				
			|||||||
import Avatar             from './avatar';
 | 
					import Avatar             from './avatar';
 | 
				
			||||||
import RelativeTimestamp  from './relative_timestamp';
 | 
					import RelativeTimestamp  from './relative_timestamp';
 | 
				
			||||||
import PureRenderMixin    from 'react-addons-pure-render-mixin';
 | 
					import PureRenderMixin    from 'react-addons-pure-render-mixin';
 | 
				
			||||||
import IconButton         from './icon_button';
 | 
					 | 
				
			||||||
import DisplayName        from './display_name';
 | 
					import DisplayName        from './display_name';
 | 
				
			||||||
import MediaGallery       from './media_gallery';
 | 
					import MediaGallery       from './media_gallery';
 | 
				
			||||||
import VideoPlayer        from './video_player';
 | 
					import VideoPlayer        from './video_player';
 | 
				
			||||||
import StatusContent      from './status_content';
 | 
					import StatusContent      from './status_content';
 | 
				
			||||||
 | 
					import StatusActionBar    from './status_action_bar';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const Status = React.createClass({
 | 
					const Status = React.createClass({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -19,23 +19,13 @@ const Status = React.createClass({
 | 
				
			|||||||
    wrapped: React.PropTypes.bool,
 | 
					    wrapped: React.PropTypes.bool,
 | 
				
			||||||
    onReply: React.PropTypes.func,
 | 
					    onReply: React.PropTypes.func,
 | 
				
			||||||
    onFavourite: React.PropTypes.func,
 | 
					    onFavourite: React.PropTypes.func,
 | 
				
			||||||
    onReblog: React.PropTypes.func
 | 
					    onReblog: React.PropTypes.func,
 | 
				
			||||||
 | 
					    onDelete: React.PropTypes.func,
 | 
				
			||||||
 | 
					    me: React.PropTypes.number
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mixins: [PureRenderMixin],
 | 
					  mixins: [PureRenderMixin],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleReplyClick () {
 | 
					 | 
				
			||||||
    this.props.onReply(this.props.status);
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleFavouriteClick () {
 | 
					 | 
				
			||||||
    this.props.onFavourite(this.props.status);
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleReblogClick () {
 | 
					 | 
				
			||||||
    this.props.onReblog(this.props.status);
 | 
					 | 
				
			||||||
  },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  handleClick () {
 | 
					  handleClick () {
 | 
				
			||||||
    const { status } = this.props;
 | 
					    const { status } = this.props;
 | 
				
			||||||
    this.context.router.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`);
 | 
					    this.context.router.push(`/statuses/${status.getIn(['reblog', 'id'], status.get('id'))}`);
 | 
				
			||||||
@ -96,11 +86,7 @@ const Status = React.createClass({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        {media}
 | 
					        {media}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <div style={{ marginTop: '10px', overflow: 'hidden' }}>
 | 
					        <StatusActionBar {...this.props} />
 | 
				
			||||||
          <div style={{ float: 'left', marginRight: '10px'}}><IconButton title='Reply' icon='reply' onClick={this.handleReplyClick} /></div>
 | 
					 | 
				
			||||||
          <div style={{ float: 'left', marginRight: '10px'}}><IconButton active={status.get('reblogged')} title='Reblog' icon='retweet' onClick={this.handleReblogClick} /></div>
 | 
					 | 
				
			||||||
          <div style={{ float: 'left'}}><IconButton active={status.get('favourited')} title='Favourite' icon='star' onClick={this.handleFavouriteClick} /></div>
 | 
					 | 
				
			||||||
        </div>
 | 
					 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -0,0 +1,67 @@
 | 
				
			|||||||
 | 
					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';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const StatusActionBar = React.createClass({
 | 
				
			||||||
 | 
					  propTypes: {
 | 
				
			||||||
 | 
					    status: ImmutablePropTypes.map.isRequired,
 | 
				
			||||||
 | 
					    onReply: React.PropTypes.func,
 | 
				
			||||||
 | 
					    onFavourite: React.PropTypes.func,
 | 
				
			||||||
 | 
					    onReblog: React.PropTypes.func,
 | 
				
			||||||
 | 
					    onDelete: React.PropTypes.func
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mixins: [PureRenderMixin],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleReplyClick () {
 | 
				
			||||||
 | 
					    this.props.onReply(this.props.status);
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleFavouriteClick () {
 | 
				
			||||||
 | 
					    this.props.onFavourite(this.props.status);
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleReblogClick () {
 | 
				
			||||||
 | 
					    this.props.onReblog(this.props.status);
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleDeleteClick(e) {
 | 
				
			||||||
 | 
					    e.preventDefault();
 | 
				
			||||||
 | 
					    this.props.onDelete(this.props.status);
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  render () {
 | 
				
			||||||
 | 
					    const { status, me } = this.props;
 | 
				
			||||||
 | 
					    let menu = '';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (status.getIn(['account', 'id']) === me) {
 | 
				
			||||||
 | 
					      menu = (
 | 
				
			||||||
 | 
					        <ul>
 | 
				
			||||||
 | 
					          <li><a href='#' onClick={this.handleDeleteClick}>Delete</a></li>
 | 
				
			||||||
 | 
					        </ul>
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      <div style={{ marginTop: '10px', overflow: 'hidden' }}>
 | 
				
			||||||
 | 
					        <div style={{ float: 'left', marginRight: '18px'}}><IconButton title='Reply' icon='reply' onClick={this.handleReplyClick} /></div>
 | 
				
			||||||
 | 
					        <div style={{ float: 'left', marginRight: '18px'}}><IconButton active={status.get('reblogged')} title='Reblog' icon='retweet' onClick={this.handleReblogClick} /></div>
 | 
				
			||||||
 | 
					        <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>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					      </div>
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default StatusActionBar;
 | 
				
			||||||
@ -9,7 +9,9 @@ const StatusList = React.createClass({
 | 
				
			|||||||
    onReply: React.PropTypes.func,
 | 
					    onReply: React.PropTypes.func,
 | 
				
			||||||
    onReblog: React.PropTypes.func,
 | 
					    onReblog: React.PropTypes.func,
 | 
				
			||||||
    onFavourite: React.PropTypes.func,
 | 
					    onFavourite: React.PropTypes.func,
 | 
				
			||||||
    onScrollToBottom: React.PropTypes.func
 | 
					    onDelete: React.PropTypes.func,
 | 
				
			||||||
 | 
					    onScrollToBottom: React.PropTypes.func,
 | 
				
			||||||
 | 
					    me: React.PropTypes.number
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mixins: [PureRenderMixin],
 | 
					  mixins: [PureRenderMixin],
 | 
				
			||||||
@ -23,11 +25,13 @@ const StatusList = React.createClass({
 | 
				
			|||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  render () {
 | 
					  render () {
 | 
				
			||||||
 | 
					    const { statuses, onScrollToBottom, ...other } = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
      <div style={{ overflowY: 'scroll', flex: '1 1 auto' }} className='scrollable' onScroll={this.handleScroll}>
 | 
					      <div style={{ overflowY: 'scroll', flex: '1 1 auto' }} className='scrollable' onScroll={this.handleScroll}>
 | 
				
			||||||
        <div>
 | 
					        <div>
 | 
				
			||||||
          {this.props.statuses.map((status) => {
 | 
					          {statuses.map((status) => {
 | 
				
			||||||
            return <Status key={status.get('id')} status={status} onReply={this.props.onReply} onReblog={this.props.onReblog} onFavourite={this.props.onFavourite} />;
 | 
					            return <Status key={status.get('id')} {...other} status={status} />;
 | 
				
			||||||
          })}
 | 
					          })}
 | 
				
			||||||
        </div>
 | 
					        </div>
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@ import {
 | 
				
			|||||||
  fetchAccountTimeline,
 | 
					  fetchAccountTimeline,
 | 
				
			||||||
  expandAccountTimeline
 | 
					  expandAccountTimeline
 | 
				
			||||||
}                            from '../../actions/accounts';
 | 
					}                            from '../../actions/accounts';
 | 
				
			||||||
 | 
					import { deleteStatus }      from '../../actions/statuses';
 | 
				
			||||||
import { replyCompose }      from '../../actions/compose';
 | 
					import { replyCompose }      from '../../actions/compose';
 | 
				
			||||||
import { favourite, reblog } from '../../actions/interactions';
 | 
					import { favourite, reblog } from '../../actions/interactions';
 | 
				
			||||||
import Header                from './components/header';
 | 
					import Header                from './components/header';
 | 
				
			||||||
@ -72,6 +73,10 @@ const Account = React.createClass({
 | 
				
			|||||||
    this.props.dispatch(favourite(status));
 | 
					    this.props.dispatch(favourite(status));
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  handleDelete (status) {
 | 
				
			||||||
 | 
					    this.props.dispatch(deleteStatus(status.get('id')));
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  handleScrollToBottom () {
 | 
					  handleScrollToBottom () {
 | 
				
			||||||
    this.props.dispatch(expandAccountTimeline(this.props.account.get('id')));
 | 
					    this.props.dispatch(expandAccountTimeline(this.props.account.get('id')));
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
@ -87,7 +92,7 @@ const Account = React.createClass({
 | 
				
			|||||||
      <div style={{ display: 'flex', flexDirection: 'column', 'flex': '0 0 auto', height: '100%' }}>
 | 
					      <div style={{ display: 'flex', flexDirection: 'column', 'flex': '0 0 auto', height: '100%' }}>
 | 
				
			||||||
        <Header account={account} />
 | 
					        <Header account={account} />
 | 
				
			||||||
        <ActionBar account={account} me={me} onFollow={this.handleFollow} onUnfollow={this.handleUnfollow} />
 | 
					        <ActionBar account={account} me={me} onFollow={this.handleFollow} onUnfollow={this.handleUnfollow} />
 | 
				
			||||||
        <StatusList statuses={statuses} onScrollToBottom={this.handleScrollToBottom} onReply={this.handleReply} onReblog={this.handleReblog} onFavourite={this.handleFavourite} />
 | 
					        <StatusList statuses={statuses} me={me} onScrollToBottom={this.handleScrollToBottom} onReply={this.handleReply} onReblog={this.handleReblog} onFavourite={this.handleFavourite} />
 | 
				
			||||||
      </div>
 | 
					      </div>
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
				
			|||||||
@ -4,29 +4,35 @@ import { replyCompose }      from '../../../actions/compose';
 | 
				
			|||||||
import { reblog, favourite } from '../../../actions/interactions';
 | 
					import { reblog, favourite } from '../../../actions/interactions';
 | 
				
			||||||
import { expandTimeline }    from '../../../actions/timelines';
 | 
					import { expandTimeline }    from '../../../actions/timelines';
 | 
				
			||||||
import { selectStatus }      from '../../../reducers/timelines';
 | 
					import { selectStatus }      from '../../../reducers/timelines';
 | 
				
			||||||
 | 
					import { deleteStatus }      from '../../../actions/statuses';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mapStateToProps = function (state, props) {
 | 
					const mapStateToProps = function (state, props) {
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    statuses: state.getIn(['timelines', props.type]).map(id => selectStatus(state, id))
 | 
					    statuses: state.getIn(['timelines', props.type]).map(id => selectStatus(state, id)),
 | 
				
			||||||
 | 
					    me: state.getIn(['timelines', 'me'])
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mapDispatchToProps = function (dispatch, props) {
 | 
					const mapDispatchToProps = function (dispatch, props) {
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    onReply: function (status) {
 | 
					    onReply (status) {
 | 
				
			||||||
      dispatch(replyCompose(status));
 | 
					      dispatch(replyCompose(status));
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    onFavourite: function (status) {
 | 
					    onFavourite (status) {
 | 
				
			||||||
      dispatch(favourite(status));
 | 
					      dispatch(favourite(status));
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    onReblog: function (status) {
 | 
					    onReblog (status) {
 | 
				
			||||||
      dispatch(reblog(status));
 | 
					      dispatch(reblog(status));
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    onScrollToBottom: function () {
 | 
					    onScrollToBottom () {
 | 
				
			||||||
      dispatch(expandTimeline(props.type));
 | 
					      dispatch(expandTimeline(props.type));
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    onDelete (status) {
 | 
				
			||||||
 | 
					      dispatch(deleteStatus(status.get('id')));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
				
			|||||||
@ -13,7 +13,10 @@ import {
 | 
				
			|||||||
  ACCOUNT_TIMELINE_FETCH_FAIL,
 | 
					  ACCOUNT_TIMELINE_FETCH_FAIL,
 | 
				
			||||||
  ACCOUNT_TIMELINE_EXPAND_FAIL
 | 
					  ACCOUNT_TIMELINE_EXPAND_FAIL
 | 
				
			||||||
}                                                   from '../actions/accounts';
 | 
					}                                                   from '../actions/accounts';
 | 
				
			||||||
import { STATUS_FETCH_FAIL }                        from '../actions/statuses';
 | 
					import {
 | 
				
			||||||
 | 
					  STATUS_FETCH_FAIL,
 | 
				
			||||||
 | 
					  STATUS_DELETE_FAIL
 | 
				
			||||||
 | 
					}                                                   from '../actions/statuses';
 | 
				
			||||||
import Immutable                                    from 'immutable';
 | 
					import Immutable                                    from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const initialState = Immutable.List();
 | 
					const initialState = Immutable.List();
 | 
				
			||||||
@ -51,6 +54,7 @@ export default function notifications(state = initialState, action) {
 | 
				
			|||||||
    case ACCOUNT_TIMELINE_FETCH_FAIL:
 | 
					    case ACCOUNT_TIMELINE_FETCH_FAIL:
 | 
				
			||||||
    case ACCOUNT_TIMELINE_EXPAND_FAIL:
 | 
					    case ACCOUNT_TIMELINE_EXPAND_FAIL:
 | 
				
			||||||
    case STATUS_FETCH_FAIL:
 | 
					    case STATUS_FETCH_FAIL:
 | 
				
			||||||
 | 
					    case STATUS_DELETE_FAIL:
 | 
				
			||||||
      return notificationFromError(state, action.error);
 | 
					      return notificationFromError(state, action.error);
 | 
				
			||||||
    case NOTIFICATION_DISMISS:
 | 
					    case NOTIFICATION_DISMISS:
 | 
				
			||||||
      return state.filterNot(item => item.get('key') === action.notification.key);
 | 
					      return state.filterNot(item => item.get('key') === action.notification.key);
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,10 @@ import {
 | 
				
			|||||||
  ACCOUNT_TIMELINE_FETCH_SUCCESS,
 | 
					  ACCOUNT_TIMELINE_FETCH_SUCCESS,
 | 
				
			||||||
  ACCOUNT_TIMELINE_EXPAND_SUCCESS
 | 
					  ACCOUNT_TIMELINE_EXPAND_SUCCESS
 | 
				
			||||||
}                                from '../actions/accounts';
 | 
					}                                from '../actions/accounts';
 | 
				
			||||||
import { STATUS_FETCH_SUCCESS }  from '../actions/statuses';
 | 
					import {
 | 
				
			||||||
 | 
					  STATUS_FETCH_SUCCESS,
 | 
				
			||||||
 | 
					  STATUS_DELETE_SUCCESS
 | 
				
			||||||
 | 
					}                                from '../actions/statuses';
 | 
				
			||||||
import { FOLLOW_SUBMIT_SUCCESS } from '../actions/follow';
 | 
					import { FOLLOW_SUBMIT_SUCCESS } from '../actions/follow';
 | 
				
			||||||
import Immutable                 from 'immutable';
 | 
					import Immutable                 from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -142,10 +145,28 @@ function updateTimeline(state, timeline, status) {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function deleteStatus(state, id) {
 | 
					function deleteStatus(state, id) {
 | 
				
			||||||
 | 
					  const status = state.getIn(['statuses', id]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!status) {
 | 
				
			||||||
 | 
					    return state;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Remove references from timelines
 | 
				
			||||||
  ['home', 'mentions'].forEach(function (timeline) {
 | 
					  ['home', 'mentions'].forEach(function (timeline) {
 | 
				
			||||||
    state = state.update(timeline, list => list.filterNot(item => item === id));
 | 
					    state = state.update(timeline, list => list.filterNot(item => item === id));
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Remove references from account timelines
 | 
				
			||||||
 | 
					  state = state.updateIn(['accounts_timelines', status.get('account')], Immutable.List(), list => list.filterNot(item => item === id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Remove reblogs of deleted status
 | 
				
			||||||
 | 
					  const references = state.get('statuses').filter(item => item.get('reblog') === id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  references.forEach(referencingId => {
 | 
				
			||||||
 | 
					    state = deleteStatus(state, referencingId);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Remove normalized status
 | 
				
			||||||
  return state.deleteIn(['statuses', id]);
 | 
					  return state.deleteIn(['statuses', id]);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -194,6 +215,7 @@ export default function timelines(state = initialState, action) {
 | 
				
			|||||||
    case TIMELINE_UPDATE:
 | 
					    case TIMELINE_UPDATE:
 | 
				
			||||||
      return updateTimeline(state, action.timeline, Immutable.fromJS(action.status));
 | 
					      return updateTimeline(state, action.timeline, Immutable.fromJS(action.status));
 | 
				
			||||||
    case TIMELINE_DELETE:
 | 
					    case TIMELINE_DELETE:
 | 
				
			||||||
 | 
					    case STATUS_DELETE_SUCCESS:
 | 
				
			||||||
      return deleteStatus(state, action.id);
 | 
					      return deleteStatus(state, action.id);
 | 
				
			||||||
    case REBLOG_SUCCESS:
 | 
					    case REBLOG_SUCCESS:
 | 
				
			||||||
    case FAVOURITE_SUCCESS:
 | 
					    case FAVOURITE_SUCCESS:
 | 
				
			||||||
 | 
				
			|||||||
@ -156,3 +156,64 @@
 | 
				
			|||||||
.transparent-background {
 | 
					.transparent-background {
 | 
				
			||||||
  background: image-url('void.png');
 | 
					  background: image-url('void.png');
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.dropdown {
 | 
				
			||||||
 | 
					  display: inline-block;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.dropdown__content {
 | 
				
			||||||
 | 
					  display: none;
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.dropdown--active .dropdown__content {
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					  z-index: 9999;
 | 
				
			||||||
 | 
					  box-shadow: 0 0 15px rgba(0, 0, 0, 0.4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  &:before {
 | 
				
			||||||
 | 
					    content: "";
 | 
				
			||||||
 | 
					    display: block;
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    width: 0;
 | 
				
			||||||
 | 
					    height: 0;
 | 
				
			||||||
 | 
					    border-style: solid;
 | 
				
			||||||
 | 
					    border-width: 0 4.5px 7.8px 4.5px;
 | 
				
			||||||
 | 
					    border-color: transparent transparent #d9e1e8 transparent;
 | 
				
			||||||
 | 
					    top: -7px;
 | 
				
			||||||
 | 
					    left: 8px;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ul {
 | 
				
			||||||
 | 
					    list-style: none;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  li {
 | 
				
			||||||
 | 
					    &:first-child a {
 | 
				
			||||||
 | 
					      border-radius: 4px 4px 0 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &:last-child a {
 | 
				
			||||||
 | 
					      border-radius: 0 0 4px 4px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &:first-child:last-child a {
 | 
				
			||||||
 | 
					      border-radius: 4px;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  a {
 | 
				
			||||||
 | 
					    font-size: 13px;
 | 
				
			||||||
 | 
					    display: block;
 | 
				
			||||||
 | 
					    padding: 6px 16px;
 | 
				
			||||||
 | 
					    width: 120px;
 | 
				
			||||||
 | 
					    text-decoration: none;
 | 
				
			||||||
 | 
					    background: #d9e1e8;
 | 
				
			||||||
 | 
					    color: #282c37;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    &:hover {
 | 
				
			||||||
 | 
					      background: #2b90d9;
 | 
				
			||||||
 | 
					      color: #d9e1e8;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
class Api::V1::AppsController < ApplicationController
 | 
					class Api::V1::AppsController < ApiController
 | 
				
			||||||
  respond_to :json
 | 
					  respond_to :json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def create
 | 
					  def create
 | 
				
			||||||
 | 
				
			|||||||
@ -1,12 +1,19 @@
 | 
				
			|||||||
!!! 5
 | 
					!!! 5
 | 
				
			||||||
%html
 | 
					%html
 | 
				
			||||||
  %head
 | 
					  %head
 | 
				
			||||||
    %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
 | 
					    %meta{:content => 'text/html; charset=UTF-8', 'http-equiv' => 'Content-Type'}/
 | 
				
			||||||
 | 
					    %meta{:charset => 'utf-8'}/
 | 
				
			||||||
 | 
					    %meta{:name => 'viewport', :content => 'width=device-width, initial-scale=1'}/
 | 
				
			||||||
 | 
					    %meta{'http-equiv' => 'X-UA-Compatible', :content => 'IE=edge'}/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    %title
 | 
					    %title
 | 
				
			||||||
      = "#{yield(:page_title)} - " if content_for?(:page_title)
 | 
					      = "#{yield(:page_title)} - " if content_for?(:page_title)
 | 
				
			||||||
      Mastodon
 | 
					      Mastodon
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    = stylesheet_link_tag    'application', media: 'all'
 | 
					    = stylesheet_link_tag    'application', media: 'all'
 | 
				
			||||||
    = csrf_meta_tags
 | 
					    = csrf_meta_tags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    = yield :header_tags
 | 
					    = yield :header_tags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  %body{ class: @body_classes }
 | 
					  %body{ class: @body_classes }
 | 
				
			||||||
    = content_for?(:content) ? yield(:content) : yield
 | 
					    = content_for?(:content) ? yield(:content) : yield
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user