forked from cybrespace/mastodon
		
	Refactoring redux state into different reducers
This commit is contained in:
		
							parent
							
								
									7060bdf04b
								
							
						
					
					
						commit
						e8ff4c8e56
					
				
					 23 changed files with 352 additions and 223 deletions
				
			
		| 
						 | 
					@ -1,5 +1,4 @@
 | 
				
			||||||
import api       from '../api'
 | 
					import api       from '../api'
 | 
				
			||||||
import axios     from 'axios';
 | 
					 | 
				
			||||||
import Immutable from 'immutable';
 | 
					import Immutable from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const ACCOUNT_SET_SELF = 'ACCOUNT_SET_SELF';
 | 
					export const ACCOUNT_SET_SELF = 'ACCOUNT_SET_SELF';
 | 
				
			||||||
| 
						 | 
					@ -53,12 +52,11 @@ export function setAccountSelf(account) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function fetchAccount(id) {
 | 
					export function fetchAccount(id) {
 | 
				
			||||||
  return (dispatch, getState) => {
 | 
					  return (dispatch, getState) => {
 | 
				
			||||||
    const boundApi = api(getState);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    dispatch(fetchAccountRequest(id));
 | 
					    dispatch(fetchAccountRequest(id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    axios.all([boundApi.get(`/api/v1/accounts/${id}`), boundApi.get(`/api/v1/accounts/relationships?id=${id}`)]).then(values => {
 | 
					    api(getState).get(`/api/v1/accounts/${id}`).then(response => {
 | 
				
			||||||
      dispatch(fetchAccountSuccess(values[0].data, values[1].data[0]));
 | 
					      dispatch(fetchAccountSuccess(response.data));
 | 
				
			||||||
 | 
					      dispatch(fetchRelationships([id]));
 | 
				
			||||||
    }).catch(error => {
 | 
					    }).catch(error => {
 | 
				
			||||||
      dispatch(fetchAccountFail(id, error));
 | 
					      dispatch(fetchAccountFail(id, error));
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
| 
						 | 
					@ -107,11 +105,10 @@ export function fetchAccountRequest(id) {
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function fetchAccountSuccess(account, relationship) {
 | 
					export function fetchAccountSuccess(account) {
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    type: ACCOUNT_FETCH_SUCCESS,
 | 
					    type: ACCOUNT_FETCH_SUCCESS,
 | 
				
			||||||
    account: account,
 | 
					    account: account
 | 
				
			||||||
    relationship: relationship
 | 
					 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
import api   from '../api';
 | 
					import api from '../api';
 | 
				
			||||||
import axios from 'axios';
 | 
					
 | 
				
			||||||
 | 
					import { deleteFromTimelines } from './timelines';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
 | 
					export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
 | 
				
			||||||
export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS';
 | 
					export const STATUS_FETCH_SUCCESS = 'STATUS_FETCH_SUCCESS';
 | 
				
			||||||
| 
						 | 
					@ -9,6 +10,10 @@ export const STATUS_DELETE_REQUEST = 'STATUS_DELETE_REQUEST';
 | 
				
			||||||
export const STATUS_DELETE_SUCCESS = 'STATUS_DELETE_SUCCESS';
 | 
					export const STATUS_DELETE_SUCCESS = 'STATUS_DELETE_SUCCESS';
 | 
				
			||||||
export const STATUS_DELETE_FAIL    = 'STATUS_DELETE_FAIL';
 | 
					export const STATUS_DELETE_FAIL    = 'STATUS_DELETE_FAIL';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const CONTEXT_FETCH_REQUEST = 'CONTEXT_FETCH_REQUEST';
 | 
				
			||||||
 | 
					export const CONTEXT_FETCH_SUCCESS = 'CONTEXT_FETCH_SUCCESS';
 | 
				
			||||||
 | 
					export const CONTEXT_FETCH_FAIL    = 'CONTEXT_FETCH_FAIL';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function fetchStatusRequest(id) {
 | 
					export function fetchStatusRequest(id) {
 | 
				
			||||||
  return {
 | 
					  return {
 | 
				
			||||||
    type: STATUS_FETCH_REQUEST,
 | 
					    type: STATUS_FETCH_REQUEST,
 | 
				
			||||||
| 
						 | 
					@ -18,12 +23,11 @@ export function fetchStatusRequest(id) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function fetchStatus(id) {
 | 
					export function fetchStatus(id) {
 | 
				
			||||||
  return (dispatch, getState) => {
 | 
					  return (dispatch, getState) => {
 | 
				
			||||||
    const boundApi = api(getState);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    dispatch(fetchStatusRequest(id));
 | 
					    dispatch(fetchStatusRequest(id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    axios.all([boundApi.get(`/api/v1/statuses/${id}`), boundApi.get(`/api/v1/statuses/${id}/context`)]).then(values => {
 | 
					    api(getState).get(`/api/v1/statuses/${id}`).then(response => {
 | 
				
			||||||
      dispatch(fetchStatusSuccess(values[0].data, values[1].data));
 | 
					      dispatch(fetchStatusSuccess(response.data));
 | 
				
			||||||
 | 
					      dispatch(fetchContext(id));
 | 
				
			||||||
    }).catch(error => {
 | 
					    }).catch(error => {
 | 
				
			||||||
      dispatch(fetchStatusFail(id, error));
 | 
					      dispatch(fetchStatusFail(id, error));
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
| 
						 | 
					@ -52,6 +56,7 @@ export function deleteStatus(id) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    api(getState).delete(`/api/v1/statuses/${id}`).then(response => {
 | 
					    api(getState).delete(`/api/v1/statuses/${id}`).then(response => {
 | 
				
			||||||
      dispatch(deleteStatusSuccess(id));
 | 
					      dispatch(deleteStatusSuccess(id));
 | 
				
			||||||
 | 
					      dispatch(deleteFromTimelines(id));
 | 
				
			||||||
    }).catch(error => {
 | 
					    }).catch(error => {
 | 
				
			||||||
      dispatch(deleteStatusFail(id, error));
 | 
					      dispatch(deleteStatusFail(id, error));
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
| 
						 | 
					@ -79,3 +84,40 @@ export function deleteStatusFail(id, error) {
 | 
				
			||||||
    error: error
 | 
					    error: error
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function fetchContext(id) {
 | 
				
			||||||
 | 
					  return (dispatch, getState) => {
 | 
				
			||||||
 | 
					    dispatch(fetchContextRequest(id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    api(getState).get(`/api/v1/statuses/${id}/context`).then(response => {
 | 
				
			||||||
 | 
					      dispatch(fetchContextSuccess(id, response.data.ancestors, response.data.descendants));
 | 
				
			||||||
 | 
					    }).catch(error => {
 | 
				
			||||||
 | 
					      dispatch(fetchContextFail(id, error));
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function fetchContextRequest(id) {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    type: CONTEXT_FETCH_REQUEST,
 | 
				
			||||||
 | 
					    id
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function fetchContextSuccess(id, ancestors, descendants) {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    type: CONTEXT_FETCH_SUCCESS,
 | 
				
			||||||
 | 
					    id,
 | 
				
			||||||
 | 
					    ancestors,
 | 
				
			||||||
 | 
					    descendants,
 | 
				
			||||||
 | 
					    statuses: ancestors.concat(descendants)
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function fetchContextFail(id, error) {
 | 
				
			||||||
 | 
					  return {
 | 
				
			||||||
 | 
					    type: CONTEXT_FETCH_FAIL,
 | 
				
			||||||
 | 
					    id,
 | 
				
			||||||
 | 
					    error
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,17 +21,29 @@ export function refreshTimelineSuccess(timeline, statuses, replace) {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function updateTimeline(timeline, status) {
 | 
					export function updateTimeline(timeline, status) {
 | 
				
			||||||
  return {
 | 
					  return (dispatch, getState) => {
 | 
				
			||||||
    type: TIMELINE_UPDATE,
 | 
					    const references = status.reblog ? getState().get('statuses').filter((item, itemId) => (itemId === status.reblog.id || item.get('reblog') === status.reblog.id)).map((_, itemId) => itemId) : [];
 | 
				
			||||||
    timeline: timeline,
 | 
					
 | 
				
			||||||
    status: status
 | 
					    dispatch({
 | 
				
			||||||
 | 
					      type: TIMELINE_UPDATE,
 | 
				
			||||||
 | 
					      timeline,
 | 
				
			||||||
 | 
					      status,
 | 
				
			||||||
 | 
					      references
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function deleteFromTimelines(id) {
 | 
					export function deleteFromTimelines(id) {
 | 
				
			||||||
  return {
 | 
					  return (dispatch, getState) => {
 | 
				
			||||||
    type: TIMELINE_DELETE,
 | 
					    const accountId  = getState().getIn(['statuses', id, 'account']);
 | 
				
			||||||
    id: id
 | 
					    const references = getState().get('statuses').filter(status => status.get('reblog') === id).map(status => [status.get('id'), status.get('account')]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dispatch({
 | 
				
			||||||
 | 
					      type: TIMELINE_DELETE,
 | 
				
			||||||
 | 
					      id,
 | 
				
			||||||
 | 
					      accountId,
 | 
				
			||||||
 | 
					      references
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ const Status = React.createClass({
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  propTypes: {
 | 
					  propTypes: {
 | 
				
			||||||
    status: ImmutablePropTypes.map.isRequired,
 | 
					    status: ImmutablePropTypes.map,
 | 
				
			||||||
    wrapped: React.PropTypes.bool,
 | 
					    wrapped: React.PropTypes.bool,
 | 
				
			||||||
    onReply: React.PropTypes.func,
 | 
					    onReply: React.PropTypes.func,
 | 
				
			||||||
    onFavourite: React.PropTypes.func,
 | 
					    onFavourite: React.PropTypes.func,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,7 @@ const makeMapStateToProps = () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const mapStateToProps = (state, props) => ({
 | 
					  const mapStateToProps = (state, props) => ({
 | 
				
			||||||
    status: getStatus(state, props.id),
 | 
					    status: getStatus(state, props.id),
 | 
				
			||||||
    me: state.getIn(['timelines', 'me'])
 | 
					    me: state.getIn(['meta', 'me'])
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return mapStateToProps;
 | 
					  return mapStateToProps;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -26,7 +26,7 @@ const makeMapStateToProps = () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const mapStateToProps = (state, props) => ({
 | 
					  const mapStateToProps = (state, props) => ({
 | 
				
			||||||
    account: getAccount(state, Number(props.params.accountId)),
 | 
					    account: getAccount(state, Number(props.params.accountId)),
 | 
				
			||||||
    me: state.getIn(['timelines', 'me'])
 | 
					    me: state.getIn(['meta', 'me'])
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return mapStateToProps;
 | 
					  return mapStateToProps;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ import LoadingIndicator       from '../../components/loading_indicator';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mapStateToProps = (state, props) => ({
 | 
					const mapStateToProps = (state, props) => ({
 | 
				
			||||||
  statusIds: state.getIn(['timelines', 'accounts_timelines', Number(props.params.accountId)]),
 | 
					  statusIds: state.getIn(['timelines', 'accounts_timelines', Number(props.params.accountId)]),
 | 
				
			||||||
  me: state.getIn(['timelines', 'me'])
 | 
					  me: state.getIn(['meta', 'me'])
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const AccountTimeline = React.createClass({
 | 
					const AccountTimeline = React.createClass({
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ import { connect }           from 'react-redux';
 | 
				
			||||||
import SuggestionsBox        from '../components/suggestions_box';
 | 
					import SuggestionsBox        from '../components/suggestions_box';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mapStateToProps = (state) => ({
 | 
					const mapStateToProps = (state) => ({
 | 
				
			||||||
  accountIds: state.get('suggestions')
 | 
					  accountIds: state.getIn(['user_lists', 'suggestions'])
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default connect(mapStateToProps)(SuggestionsBox);
 | 
					export default connect(mapStateToProps)(SuggestionsBox);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,7 @@ const makeMapStateToProps = () => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const mapStateToProps = (state, props) => ({
 | 
					  const mapStateToProps = (state, props) => ({
 | 
				
			||||||
    account: getAccount(state, props.id),
 | 
					    account: getAccount(state, props.id),
 | 
				
			||||||
    me: state.getIn(['timelines', 'me'])
 | 
					    me: state.getIn(['meta', 'me'])
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return mapStateToProps;
 | 
					  return mapStateToProps;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,7 +31,7 @@ const makeMapStateToProps = () => {
 | 
				
			||||||
    status: getStatus(state, Number(props.params.statusId)),
 | 
					    status: getStatus(state, Number(props.params.statusId)),
 | 
				
			||||||
    ancestorsIds: state.getIn(['timelines', 'ancestors', Number(props.params.statusId)]),
 | 
					    ancestorsIds: state.getIn(['timelines', 'ancestors', Number(props.params.statusId)]),
 | 
				
			||||||
    descendantsIds: state.getIn(['timelines', 'descendants', Number(props.params.statusId)]),
 | 
					    descendantsIds: state.getIn(['timelines', 'descendants', Number(props.params.statusId)]),
 | 
				
			||||||
    me: state.getIn(['timelines', 'me'])
 | 
					    me: state.getIn(['meta', 'me'])
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return mapStateToProps;
 | 
					  return mapStateToProps;
 | 
				
			||||||
| 
						 | 
					@ -43,8 +43,8 @@ const Status = React.createClass({
 | 
				
			||||||
    params: React.PropTypes.object.isRequired,
 | 
					    params: React.PropTypes.object.isRequired,
 | 
				
			||||||
    dispatch: React.PropTypes.func.isRequired,
 | 
					    dispatch: React.PropTypes.func.isRequired,
 | 
				
			||||||
    status: ImmutablePropTypes.map,
 | 
					    status: ImmutablePropTypes.map,
 | 
				
			||||||
    ancestorsIds: ImmutablePropTypes.orderedSet,
 | 
					    ancestorsIds: ImmutablePropTypes.list,
 | 
				
			||||||
    descendantsIds: ImmutablePropTypes.orderedSet
 | 
					    descendantsIds: ImmutablePropTypes.list
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mixins: [PureRenderMixin],
 | 
					  mixins: [PureRenderMixin],
 | 
				
			||||||
| 
						 | 
					@ -101,11 +101,11 @@ const Status = React.createClass({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    const account = status.get('account');
 | 
					    const account = status.get('account');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (ancestorsIds) {
 | 
					    if (ancestorsIds && ancestorsIds.size > 0) {
 | 
				
			||||||
      ancestors = <div>{this.renderChildren(ancestorsIds)}</div>;
 | 
					      ancestors = <div>{this.renderChildren(ancestorsIds)}</div>;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (descendantsIds) {
 | 
					    if (descendantsIds && descendantsIds.size > 0) {
 | 
				
			||||||
      descendants = <div>{this.renderChildren(descendantsIds)}</div>;
 | 
					      descendants = <div>{this.renderChildren(descendantsIds)}</div>;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,7 +2,7 @@ import { connect }   from 'react-redux';
 | 
				
			||||||
import NavigationBar from '../components/navigation_bar';
 | 
					import NavigationBar from '../components/navigation_bar';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const mapStateToProps = (state, props) => ({
 | 
					const mapStateToProps = (state, props) => ({
 | 
				
			||||||
  account: state.getIn(['timelines', 'accounts', state.getIn(['timelines', 'me'])])
 | 
					  account: state.getIn(['accounts', state.getIn(['meta', 'me'])])
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default connect(mapStateToProps)(NavigationBar);
 | 
					export default connect(mapStateToProps)(NavigationBar);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										80
									
								
								app/assets/javascripts/components/reducers/accounts.jsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								app/assets/javascripts/components/reducers/accounts.jsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,80 @@
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  ACCOUNT_SET_SELF,
 | 
				
			||||||
 | 
					  ACCOUNT_FETCH_SUCCESS,
 | 
				
			||||||
 | 
					  FOLLOWERS_FETCH_SUCCESS,
 | 
				
			||||||
 | 
					  FOLLOWING_FETCH_SUCCESS,
 | 
				
			||||||
 | 
					  ACCOUNT_TIMELINE_FETCH_SUCCESS,
 | 
				
			||||||
 | 
					  ACCOUNT_TIMELINE_EXPAND_SUCCESS
 | 
				
			||||||
 | 
					} from '../actions/accounts';
 | 
				
			||||||
 | 
					import { FOLLOW_SUBMIT_SUCCESS } from '../actions/follow';
 | 
				
			||||||
 | 
					import { SUGGESTIONS_FETCH_SUCCESS } from '../actions/suggestions';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  REBLOG_SUCCESS,
 | 
				
			||||||
 | 
					  UNREBLOG_SUCCESS,
 | 
				
			||||||
 | 
					  FAVOURITE_SUCCESS,
 | 
				
			||||||
 | 
					  UNFAVOURITE_SUCCESS
 | 
				
			||||||
 | 
					} from '../actions/interactions';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  TIMELINE_REFRESH_SUCCESS,
 | 
				
			||||||
 | 
					  TIMELINE_UPDATE,
 | 
				
			||||||
 | 
					  TIMELINE_EXPAND_SUCCESS
 | 
				
			||||||
 | 
					} from '../actions/timelines';
 | 
				
			||||||
 | 
					import { STATUS_FETCH_SUCCESS } from '../actions/statuses';
 | 
				
			||||||
 | 
					import Immutable from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const normalizeAccount = (state, account) => state.set(account.get('id'), account);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const normalizeAccounts = (state, accounts) => {
 | 
				
			||||||
 | 
					  accounts.forEach(account => {
 | 
				
			||||||
 | 
					    state = normalizeAccount(state, account);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return state;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const normalizeAccountFromStatus = (state, status) => {
 | 
				
			||||||
 | 
					  state = normalizeAccount(state, status.get('account'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (status.getIn(['reblog', 'account'])) {
 | 
				
			||||||
 | 
					    state = normalizeAccount(state, status.getIn(['reblog', 'account']));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return state;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const normalizeAccountsFromStatuses = (state, statuses) => {
 | 
				
			||||||
 | 
					  statuses.forEach(status => {
 | 
				
			||||||
 | 
					    state = normalizeAccountFromStatus(state, status);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return state;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const initialState = Immutable.Map();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function accounts(state = initialState, action) {
 | 
				
			||||||
 | 
					  switch(action.type) {
 | 
				
			||||||
 | 
					    case ACCOUNT_SET_SELF:
 | 
				
			||||||
 | 
					    case ACCOUNT_FETCH_SUCCESS:
 | 
				
			||||||
 | 
					    case FOLLOW_SUBMIT_SUCCESS:
 | 
				
			||||||
 | 
					      return normalizeAccount(state, Immutable.fromJS(action.account));
 | 
				
			||||||
 | 
					    case SUGGESTIONS_FETCH_SUCCESS:
 | 
				
			||||||
 | 
					    case FOLLOWERS_FETCH_SUCCESS:
 | 
				
			||||||
 | 
					    case FOLLOWING_FETCH_SUCCESS:
 | 
				
			||||||
 | 
					      return normalizeAccounts(state, Immutable.fromJS(action.accounts));
 | 
				
			||||||
 | 
					    case TIMELINE_REFRESH_SUCCESS:
 | 
				
			||||||
 | 
					    case TIMELINE_EXPAND_SUCCESS:
 | 
				
			||||||
 | 
					    case ACCOUNT_TIMELINE_FETCH_SUCCESS:
 | 
				
			||||||
 | 
					    case ACCOUNT_TIMELINE_EXPAND_SUCCESS:
 | 
				
			||||||
 | 
					      return normalizeAccountsFromStatuses(state, Immutable.fromJS(action.statuses));
 | 
				
			||||||
 | 
					    case TIMELINE_UPDATE:
 | 
				
			||||||
 | 
					    case REBLOG_SUCCESS:
 | 
				
			||||||
 | 
					    case FAVOURITE_SUCCESS:
 | 
				
			||||||
 | 
					    case UNREBLOG_SUCCESS:
 | 
				
			||||||
 | 
					    case UNFAVOURITE_SUCCESS:
 | 
				
			||||||
 | 
					    case STATUS_FETCH_SUCCESS:
 | 
				
			||||||
 | 
					      return normalizeAccountFromStatus(state, Immutable.fromJS(action.status));
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return state;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -11,10 +11,10 @@ import {
 | 
				
			||||||
  COMPOSE_UPLOAD_FAIL,
 | 
					  COMPOSE_UPLOAD_FAIL,
 | 
				
			||||||
  COMPOSE_UPLOAD_UNDO,
 | 
					  COMPOSE_UPLOAD_UNDO,
 | 
				
			||||||
  COMPOSE_UPLOAD_PROGRESS
 | 
					  COMPOSE_UPLOAD_PROGRESS
 | 
				
			||||||
}                           from '../actions/compose';
 | 
					} from '../actions/compose';
 | 
				
			||||||
import { TIMELINE_DELETE }  from '../actions/timelines';
 | 
					import { TIMELINE_DELETE } from '../actions/timelines';
 | 
				
			||||||
import { ACCOUNT_SET_SELF } from '../actions/accounts';
 | 
					import { ACCOUNT_SET_SELF } from '../actions/accounts';
 | 
				
			||||||
import Immutable            from 'immutable';
 | 
					import Immutable from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const initialState = Immutable.Map({
 | 
					const initialState = Immutable.Map({
 | 
				
			||||||
  text: '',
 | 
					  text: '',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@ import {
 | 
				
			||||||
  FOLLOW_SUBMIT_REQUEST,
 | 
					  FOLLOW_SUBMIT_REQUEST,
 | 
				
			||||||
  FOLLOW_SUBMIT_SUCCESS,
 | 
					  FOLLOW_SUBMIT_SUCCESS,
 | 
				
			||||||
  FOLLOW_SUBMIT_FAIL
 | 
					  FOLLOW_SUBMIT_FAIL
 | 
				
			||||||
}                from '../actions/follow';
 | 
					} from '../actions/follow';
 | 
				
			||||||
import Immutable from 'immutable';
 | 
					import Immutable from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const initialState = Immutable.Map({
 | 
					const initialState = Immutable.Map({
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,9 @@ import notifications         from './notifications';
 | 
				
			||||||
import { loadingBarReducer } from 'react-redux-loading-bar';
 | 
					import { loadingBarReducer } from 'react-redux-loading-bar';
 | 
				
			||||||
import modal                 from './modal';
 | 
					import modal                 from './modal';
 | 
				
			||||||
import user_lists            from './user_lists';
 | 
					import user_lists            from './user_lists';
 | 
				
			||||||
import suggestions           from './suggestions';
 | 
					import accounts              from './accounts';
 | 
				
			||||||
 | 
					import statuses              from './statuses';
 | 
				
			||||||
 | 
					import relationships         from './relationships';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default combineReducers({
 | 
					export default combineReducers({
 | 
				
			||||||
  timelines,
 | 
					  timelines,
 | 
				
			||||||
| 
						 | 
					@ -18,5 +20,7 @@ export default combineReducers({
 | 
				
			||||||
  loadingBar: loadingBarReducer,
 | 
					  loadingBar: loadingBarReducer,
 | 
				
			||||||
  modal,
 | 
					  modal,
 | 
				
			||||||
  user_lists,
 | 
					  user_lists,
 | 
				
			||||||
  suggestions
 | 
					  accounts,
 | 
				
			||||||
 | 
					  statuses,
 | 
				
			||||||
 | 
					  relationships
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,6 @@
 | 
				
			||||||
import { ACCESS_TOKEN_SET }         from '../actions/meta';
 | 
					import { ACCESS_TOKEN_SET } from '../actions/meta';
 | 
				
			||||||
import Immutable                    from 'immutable';
 | 
					import { ACCOUNT_SET_SELF } from '../actions/accounts';
 | 
				
			||||||
 | 
					import Immutable from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const initialState = Immutable.Map();
 | 
					const initialState = Immutable.Map();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -7,6 +8,8 @@ export default function meta(state = initialState, action) {
 | 
				
			||||||
  switch(action.type) {
 | 
					  switch(action.type) {
 | 
				
			||||||
    case ACCESS_TOKEN_SET:
 | 
					    case ACCESS_TOKEN_SET:
 | 
				
			||||||
      return state.set('access_token', action.token);
 | 
					      return state.set('access_token', action.token);
 | 
				
			||||||
 | 
					    case ACCOUNT_SET_SELF:
 | 
				
			||||||
 | 
					      return state.set('me', action.account.id);
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return state;
 | 
					      return state;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,8 +2,8 @@ import {
 | 
				
			||||||
  NOTIFICATION_SHOW,
 | 
					  NOTIFICATION_SHOW,
 | 
				
			||||||
  NOTIFICATION_DISMISS,
 | 
					  NOTIFICATION_DISMISS,
 | 
				
			||||||
  NOTIFICATION_CLEAR
 | 
					  NOTIFICATION_CLEAR
 | 
				
			||||||
}                            from '../actions/notifications';
 | 
					} from '../actions/notifications';
 | 
				
			||||||
import Immutable             from 'immutable';
 | 
					import Immutable from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const initialState = Immutable.List([]);
 | 
					const initialState = Immutable.List([]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										34
									
								
								app/assets/javascripts/components/reducers/relationships.jsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								app/assets/javascripts/components/reducers/relationships.jsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,34 @@
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  ACCOUNT_FOLLOW_SUCCESS,
 | 
				
			||||||
 | 
					  ACCOUNT_UNFOLLOW_SUCCESS,
 | 
				
			||||||
 | 
					  ACCOUNT_BLOCK_SUCCESS,
 | 
				
			||||||
 | 
					  ACCOUNT_UNBLOCK_SUCCESS,
 | 
				
			||||||
 | 
					  RELATIONSHIPS_FETCH_SUCCESS
 | 
				
			||||||
 | 
					} from '../actions/accounts';
 | 
				
			||||||
 | 
					import Immutable from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const normalizeRelationship = (state, relationship) => state.set(relationship.get('id'), relationship);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const normalizeRelationships = (state, relationships) => {
 | 
				
			||||||
 | 
					  relationships.forEach(relationship => {
 | 
				
			||||||
 | 
					    state = normalizeRelationship(state, relationship);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return state;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const initialState = Immutable.Map();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function relationships(state = initialState, action) {
 | 
				
			||||||
 | 
					  switch(action.type) {
 | 
				
			||||||
 | 
					    case ACCOUNT_FOLLOW_SUCCESS:
 | 
				
			||||||
 | 
					    case ACCOUNT_UNFOLLOW_SUCCESS:
 | 
				
			||||||
 | 
					    case ACCOUNT_BLOCK_SUCCESS:
 | 
				
			||||||
 | 
					    case ACCOUNT_UNBLOCK_SUCCESS:
 | 
				
			||||||
 | 
					      return normalizeRelationship(state, Immutable.fromJS(action.relationship));
 | 
				
			||||||
 | 
					    case RELATIONSHIPS_FETCH_SUCCESS:
 | 
				
			||||||
 | 
					      return normalizeRelationships(state, Immutable.fromJS(action.relationships));
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return state;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										68
									
								
								app/assets/javascripts/components/reducers/statuses.jsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								app/assets/javascripts/components/reducers/statuses.jsx
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  REBLOG_SUCCESS,
 | 
				
			||||||
 | 
					  UNREBLOG_SUCCESS,
 | 
				
			||||||
 | 
					  FAVOURITE_SUCCESS,
 | 
				
			||||||
 | 
					  UNFAVOURITE_SUCCESS
 | 
				
			||||||
 | 
					} from '../actions/interactions';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  STATUS_FETCH_SUCCESS,
 | 
				
			||||||
 | 
					  CONTEXT_FETCH_SUCCESS
 | 
				
			||||||
 | 
					} from '../actions/statuses';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  TIMELINE_REFRESH_SUCCESS,
 | 
				
			||||||
 | 
					  TIMELINE_UPDATE,
 | 
				
			||||||
 | 
					  TIMELINE_DELETE,
 | 
				
			||||||
 | 
					  TIMELINE_EXPAND_SUCCESS
 | 
				
			||||||
 | 
					} from '../actions/timelines';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  ACCOUNT_TIMELINE_FETCH_SUCCESS,
 | 
				
			||||||
 | 
					  ACCOUNT_TIMELINE_EXPAND_SUCCESS
 | 
				
			||||||
 | 
					} from '../actions/accounts';
 | 
				
			||||||
 | 
					import Immutable from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const normalizeStatus = (state, status) => {
 | 
				
			||||||
 | 
					  status = status.set('account', status.getIn(['account', 'id']));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (status.getIn(['reblog', 'id'])) {
 | 
				
			||||||
 | 
					    state  = normalizeStatus(state, status.get('reblog'));
 | 
				
			||||||
 | 
					    status = status.set('reblog', status.getIn(['reblog', 'id']));
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return state.set(status.get('id'), status);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const normalizeStatuses = (state, statuses) => {
 | 
				
			||||||
 | 
					  statuses.forEach(status => {
 | 
				
			||||||
 | 
					    state = normalizeStatus(state, status);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return state;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const deleteStatus = (state, id, references) => {
 | 
				
			||||||
 | 
					  references.forEach(ref => {
 | 
				
			||||||
 | 
					    state = deleteStatus(state, ref[0], []);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return state.delete(id);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const initialState = Immutable.Map();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default function statuses(state = initialState, action) {
 | 
				
			||||||
 | 
					  switch(action.type) {
 | 
				
			||||||
 | 
					    case TIMELINE_UPDATE:
 | 
				
			||||||
 | 
					    case STATUS_FETCH_SUCCESS:
 | 
				
			||||||
 | 
					      return normalizeStatus(state, Immutable.fromJS(action.status));
 | 
				
			||||||
 | 
					    case TIMELINE_REFRESH_SUCCESS:
 | 
				
			||||||
 | 
					    case TIMELINE_EXPAND_SUCCESS:
 | 
				
			||||||
 | 
					    case ACCOUNT_TIMELINE_FETCH_SUCCESS:
 | 
				
			||||||
 | 
					    case ACCOUNT_TIMELINE_EXPAND_SUCCESS:
 | 
				
			||||||
 | 
					    case CONTEXT_FETCH_SUCCESS:
 | 
				
			||||||
 | 
					      return normalizeStatuses(state, Immutable.fromJS(action.statuses));
 | 
				
			||||||
 | 
					    case TIMELINE_DELETE:
 | 
				
			||||||
 | 
					      return deleteStatus(state, action.id, action.references);
 | 
				
			||||||
 | 
					    default:
 | 
				
			||||||
 | 
					      return state;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
| 
						 | 
					@ -1,13 +0,0 @@
 | 
				
			||||||
import { SUGGESTIONS_FETCH_SUCCESS } from '../actions/suggestions';
 | 
					 | 
				
			||||||
import Immutable                     from 'immutable';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const initialState = Immutable.List();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default function suggestions(state = initialState, action) {
 | 
					 | 
				
			||||||
  switch(action.type) {
 | 
					 | 
				
			||||||
    case SUGGESTIONS_FETCH_SUCCESS:
 | 
					 | 
				
			||||||
      return Immutable.List(action.accounts.map(item => item.id));
 | 
					 | 
				
			||||||
    default:
 | 
					 | 
				
			||||||
      return state;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -3,85 +3,52 @@ import {
 | 
				
			||||||
  TIMELINE_UPDATE,
 | 
					  TIMELINE_UPDATE,
 | 
				
			||||||
  TIMELINE_DELETE,
 | 
					  TIMELINE_DELETE,
 | 
				
			||||||
  TIMELINE_EXPAND_SUCCESS
 | 
					  TIMELINE_EXPAND_SUCCESS
 | 
				
			||||||
}                                from '../actions/timelines';
 | 
					} from '../actions/timelines';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  REBLOG_SUCCESS,
 | 
					  REBLOG_SUCCESS,
 | 
				
			||||||
  UNREBLOG_SUCCESS,
 | 
					  UNREBLOG_SUCCESS,
 | 
				
			||||||
  FAVOURITE_SUCCESS,
 | 
					  FAVOURITE_SUCCESS,
 | 
				
			||||||
  UNFAVOURITE_SUCCESS
 | 
					  UNFAVOURITE_SUCCESS
 | 
				
			||||||
}                                from '../actions/interactions';
 | 
					} from '../actions/interactions';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  ACCOUNT_SET_SELF,
 | 
					 | 
				
			||||||
  ACCOUNT_FETCH_SUCCESS,
 | 
					  ACCOUNT_FETCH_SUCCESS,
 | 
				
			||||||
  ACCOUNT_FOLLOW_SUCCESS,
 | 
					 | 
				
			||||||
  ACCOUNT_UNFOLLOW_SUCCESS,
 | 
					 | 
				
			||||||
  ACCOUNT_BLOCK_SUCCESS,
 | 
					 | 
				
			||||||
  ACCOUNT_UNBLOCK_SUCCESS,
 | 
					 | 
				
			||||||
  ACCOUNT_TIMELINE_FETCH_SUCCESS,
 | 
					  ACCOUNT_TIMELINE_FETCH_SUCCESS,
 | 
				
			||||||
  ACCOUNT_TIMELINE_EXPAND_SUCCESS,
 | 
					  ACCOUNT_TIMELINE_EXPAND_SUCCESS
 | 
				
			||||||
  FOLLOWERS_FETCH_SUCCESS,
 | 
					} from '../actions/accounts';
 | 
				
			||||||
  FOLLOWING_FETCH_SUCCESS,
 | 
					 | 
				
			||||||
  RELATIONSHIPS_FETCH_SUCCESS
 | 
					 | 
				
			||||||
}                                from '../actions/accounts';
 | 
					 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  STATUS_FETCH_SUCCESS,
 | 
					  STATUS_FETCH_SUCCESS,
 | 
				
			||||||
  STATUS_DELETE_SUCCESS
 | 
					  CONTEXT_FETCH_SUCCESS
 | 
				
			||||||
}                                from '../actions/statuses';
 | 
					} from '../actions/statuses';
 | 
				
			||||||
import { FOLLOW_SUBMIT_SUCCESS } from '../actions/follow';
 | 
					import Immutable from 'immutable';
 | 
				
			||||||
import { SUGGESTIONS_FETCH_SUCCESS } from '../actions/suggestions';
 | 
					 | 
				
			||||||
import Immutable                 from 'immutable';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const initialState = Immutable.Map({
 | 
					const initialState = Immutable.Map({
 | 
				
			||||||
  home: Immutable.List([]),
 | 
					  home: Immutable.List(),
 | 
				
			||||||
  mentions: Immutable.List([]),
 | 
					  mentions: Immutable.List(),
 | 
				
			||||||
  public: Immutable.List([]),
 | 
					  public: Immutable.List(),
 | 
				
			||||||
  statuses: Immutable.Map(),
 | 
					 | 
				
			||||||
  accounts: Immutable.Map(),
 | 
					 | 
				
			||||||
  accounts_timelines: Immutable.Map(),
 | 
					  accounts_timelines: Immutable.Map(),
 | 
				
			||||||
  me: null,
 | 
					 | 
				
			||||||
  ancestors: Immutable.Map(),
 | 
					  ancestors: Immutable.Map(),
 | 
				
			||||||
  descendants: Immutable.Map(),
 | 
					  descendants: Immutable.Map()
 | 
				
			||||||
  relationships: Immutable.Map(),
 | 
					 | 
				
			||||||
  suggestions: Immutable.List([])
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function normalizeStatus(state, status) {
 | 
					const normalizeStatus = (state, status) => {
 | 
				
			||||||
  // Separate account
 | 
					  const replyToId = status.get('in_reply_to_id');
 | 
				
			||||||
  let account = status.get('account');
 | 
					  const id        = status.get('id');
 | 
				
			||||||
  status = status.set('account', account.get('id'));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Separate reblog, repeat for reblog
 | 
					  if (replyToId) {
 | 
				
			||||||
  let reblog = status.get('reblog', null);
 | 
					    if (!state.getIn(['descendants', replyToId], Immutable.List()).includes(id)) {
 | 
				
			||||||
 | 
					      state = state.updateIn(['descendants', replyToId], Immutable.List(), set => set.push(id));
 | 
				
			||||||
  if (reblog !== null) {
 | 
					 | 
				
			||||||
    status = status.set('reblog', reblog.get('id'));
 | 
					 | 
				
			||||||
    state  = normalizeStatus(state, reblog);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Replies
 | 
					 | 
				
			||||||
  if (status.get('in_reply_to_id')) {
 | 
					 | 
				
			||||||
    state = state.updateIn(['descendants', status.get('in_reply_to_id')], set => {
 | 
					 | 
				
			||||||
      if (!Immutable.OrderedSet.isOrderedSet(set)) {
 | 
					 | 
				
			||||||
        return Immutable.OrderedSet([status.get('id')]);
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        return set.add(status.get('id'));
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return state.withMutations(map => {
 | 
					 | 
				
			||||||
    if (status.get('in_reply_to_id')) {
 | 
					 | 
				
			||||||
      map.updateIn(['descendants', status.get('in_reply_to_id')], Immutable.OrderedSet(), set => set.add(status.get('id')));
 | 
					 | 
				
			||||||
      map.updateIn(['ancestors', status.get('id')], Immutable.OrderedSet(), set => set.add(status.get('in_reply_to_id')));
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    map.setIn(['accounts', account.get('id')], account);
 | 
					    if (!state.getIn(['ancestors', id], Immutable.List()).includes(replyToId)) {
 | 
				
			||||||
    map.setIn(['statuses', status.get('id')], status);
 | 
					      state = state.updateIn(['ancestors', id], Immutable.List(), set => set.push(replyToId));
 | 
				
			||||||
  });
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return state;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function normalizeTimeline(state, timeline, statuses, replace = false) {
 | 
					const normalizeTimeline = (state, timeline, statuses, replace = false) => {
 | 
				
			||||||
  let ids = Immutable.List([]);
 | 
					  let ids = Immutable.List();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  statuses.forEach((status, i) => {
 | 
					  statuses.forEach((status, i) => {
 | 
				
			||||||
    state = normalizeStatus(state, status);
 | 
					    state = normalizeStatus(state, status);
 | 
				
			||||||
| 
						 | 
					@ -91,8 +58,8 @@ function normalizeTimeline(state, timeline, statuses, replace = false) {
 | 
				
			||||||
  return state.update(timeline, list => (replace ? ids : list.unshift(...ids)));
 | 
					  return state.update(timeline, list => (replace ? ids : list.unshift(...ids)));
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function appendNormalizedTimeline(state, timeline, statuses) {
 | 
					const appendNormalizedTimeline = (state, timeline, statuses) => {
 | 
				
			||||||
  let moreIds = Immutable.List([]);
 | 
					  let moreIds = Immutable.List();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  statuses.forEach((status, i) => {
 | 
					  statuses.forEach((status, i) => {
 | 
				
			||||||
    state   = normalizeStatus(state, status);
 | 
					    state   = normalizeStatus(state, status);
 | 
				
			||||||
| 
						 | 
					@ -102,8 +69,8 @@ function appendNormalizedTimeline(state, timeline, statuses) {
 | 
				
			||||||
  return state.update(timeline, list => list.push(...moreIds));
 | 
					  return state.update(timeline, list => list.push(...moreIds));
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function normalizeAccountTimeline(state, accountId, statuses, replace = false) {
 | 
					const normalizeAccountTimeline = (state, accountId, statuses, replace = false) => {
 | 
				
			||||||
  let ids = Immutable.List([]);
 | 
					  let ids = Immutable.List();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  statuses.forEach((status, i) => {
 | 
					  statuses.forEach((status, i) => {
 | 
				
			||||||
    state = normalizeStatus(state, status);
 | 
					    state = normalizeStatus(state, status);
 | 
				
			||||||
| 
						 | 
					@ -113,7 +80,7 @@ function normalizeAccountTimeline(state, accountId, statuses, replace = false) {
 | 
				
			||||||
  return state.updateIn(['accounts_timelines', accountId], Immutable.List([]), list => (replace ? ids : list.unshift(...ids)));
 | 
					  return state.updateIn(['accounts_timelines', accountId], Immutable.List([]), list => (replace ? ids : list.unshift(...ids)));
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function appendNormalizedAccountTimeline(state, accountId, statuses) {
 | 
					const appendNormalizedAccountTimeline = (state, accountId, statuses) => {
 | 
				
			||||||
  let moreIds = Immutable.List([]);
 | 
					  let moreIds = Immutable.List([]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  statuses.forEach((status, i) => {
 | 
					  statuses.forEach((status, i) => {
 | 
				
			||||||
| 
						 | 
					@ -124,107 +91,60 @@ function appendNormalizedAccountTimeline(state, accountId, statuses) {
 | 
				
			||||||
  return state.updateIn(['accounts_timelines', accountId], Immutable.List([]), list => list.push(...moreIds));
 | 
					  return state.updateIn(['accounts_timelines', accountId], Immutable.List([]), list => list.push(...moreIds));
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function updateTimeline(state, timeline, status) {
 | 
					const updateTimeline = (state, timeline, status, references) => {
 | 
				
			||||||
  state = normalizeStatus(state, status);
 | 
					  state = normalizeStatus(state, status);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  state = state.update(timeline, list => {
 | 
					  state = state.update(timeline, list => {
 | 
				
			||||||
    const reblogOfId = status.getIn(['reblog', 'id'], null);
 | 
					    const reblogOfId = status.getIn(['reblog', 'id'], null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (reblogOfId !== null) {
 | 
					    if (reblogOfId !== null) {
 | 
				
			||||||
      const otherReblogs = state.get('statuses').filter(item => item.get('reblog') === reblogOfId).map((_, itemId) => itemId);
 | 
					      list = list.filterNot(itemId => references.includes(itemId));
 | 
				
			||||||
      list = list.filterNot(itemId => (itemId === reblogOfId || otherReblogs.includes(itemId)));
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    return list.unshift(status.get('id'));
 | 
					    return list.unshift(status.get('id'));
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  //state = state.updateIn(['accounts_timelines', status.getIn(['account', 'id'])], Immutable.List([]), list => (list.includes(status.get('id')) ? list : list.unshift(status.get('id'))));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return state;
 | 
					  return state;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function deleteStatus(state, id) {
 | 
					const deleteStatus = (state, id, accountId, references) => {
 | 
				
			||||||
  const status = state.getIn(['statuses', id]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  if (!status) {
 | 
					 | 
				
			||||||
    return state;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Remove references from timelines
 | 
					  // Remove references from timelines
 | 
				
			||||||
  ['home', 'mentions'].forEach(function (timeline) {
 | 
					  ['home', 'mentions', 'public'].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
 | 
					  // Remove references from account timelines
 | 
				
			||||||
  state = state.updateIn(['accounts_timelines', status.get('account')], Immutable.List([]), list => list.filterNot(item => item === id));
 | 
					  state = state.updateIn(['accounts_timelines', accountId], Immutable.List([]), list => list.filterNot(item => item === id));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Remove references from context
 | 
				
			||||||
 | 
					  state.getIn(['descendants', id], Immutable.List()).forEach(descendantId => {
 | 
				
			||||||
 | 
					    state = state.updateIn(['ancestors', descendantId], Immutable.List(), list => list.filterNot(itemId => itemId === id));
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  state.getIn(['ancestors', id], Immutable.List()).forEach(ancestorId => {
 | 
				
			||||||
 | 
					    state = state.updateIn(['descendants', ancestorId], Immutable.List(), list => list.filterNot(itemId => itemId === id));
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  state = state.deleteIn(['descendants', id]).deleteIn(['ancestors', id]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // Remove reblogs of deleted status
 | 
					  // Remove reblogs of deleted status
 | 
				
			||||||
  const references = state.get('statuses').filter(item => item.get('reblog') === id);
 | 
					  references.forEach(ref => {
 | 
				
			||||||
 | 
					    state = deleteStatus(state, ref[0], ref[1], []);
 | 
				
			||||||
  references.forEach(referencingId => {
 | 
					 | 
				
			||||||
    state = deleteStatus(state, referencingId);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Remove normalized status
 | 
					 | 
				
			||||||
  return state.deleteIn(['statuses', id]);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function normalizeAccount(state, account, relationship) {
 | 
					 | 
				
			||||||
  if (relationship) {
 | 
					 | 
				
			||||||
    state = normalizeRelationship(state, relationship);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return state.setIn(['accounts', account.get('id')], account);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function normalizeRelationship(state, relationship) {
 | 
					 | 
				
			||||||
  if (state.get('suggestions').includes(relationship.get('id')) && (relationship.get('following') || relationship.get('blocking'))) {
 | 
					 | 
				
			||||||
    state = state.update('suggestions', list => list.filterNot(id => id === relationship.get('id')));
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return state.setIn(['relationships', relationship.get('id')], relationship);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function normalizeRelationships(state, relationships) {
 | 
					 | 
				
			||||||
  relationships.forEach(relationship => {
 | 
					 | 
				
			||||||
    state = normalizeRelationship(state, relationship);
 | 
					 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return state;
 | 
					  return state;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function setSelf(state, account) {
 | 
					const normalizeContext = (state, id, ancestors, descendants) => {
 | 
				
			||||||
  state = normalizeAccount(state, account);
 | 
					  const ancestorsIds   = ancestors.map(ancestor => ancestor.get('id'));
 | 
				
			||||||
  return state.set('me', account.get('id'));
 | 
					  const descendantsIds = descendants.map(descendant => descendant.get('id'));
 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function normalizeContext(state, status, ancestors, descendants) {
 | 
					 | 
				
			||||||
  state = normalizeStatus(state, status);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let ancestorsIds = ancestors.map(ancestor => {
 | 
					 | 
				
			||||||
    state = normalizeStatus(state, ancestor);
 | 
					 | 
				
			||||||
    return ancestor.get('id');
 | 
					 | 
				
			||||||
  }).toOrderedSet();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  let descendantsIds = descendants.map(descendant => {
 | 
					 | 
				
			||||||
    state = normalizeStatus(state, descendant);
 | 
					 | 
				
			||||||
    return descendant.get('id');
 | 
					 | 
				
			||||||
  }).toOrderedSet();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return state.withMutations(map => {
 | 
					  return state.withMutations(map => {
 | 
				
			||||||
    map.setIn(['ancestors', status.get('id')], ancestorsIds);
 | 
					    map.setIn(['ancestors', id], ancestorsIds);
 | 
				
			||||||
    map.setIn(['descendants', status.get('id')], descendantsIds);
 | 
					    map.setIn(['descendants', id], descendantsIds);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function normalizeAccounts(state, accounts) {
 | 
					 | 
				
			||||||
  accounts.forEach(account => {
 | 
					 | 
				
			||||||
    state = state.setIn(['accounts', account.get('id')], account);
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return state;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default function timelines(state = initialState, action) {
 | 
					export default function timelines(state = initialState, action) {
 | 
				
			||||||
  switch(action.type) {
 | 
					  switch(action.type) {
 | 
				
			||||||
    case TIMELINE_REFRESH_SUCCESS:
 | 
					    case TIMELINE_REFRESH_SUCCESS:
 | 
				
			||||||
| 
						 | 
					@ -232,37 +152,15 @@ export default function timelines(state = initialState, action) {
 | 
				
			||||||
    case TIMELINE_EXPAND_SUCCESS:
 | 
					    case TIMELINE_EXPAND_SUCCESS:
 | 
				
			||||||
      return appendNormalizedTimeline(state, action.timeline, Immutable.fromJS(action.statuses));
 | 
					      return appendNormalizedTimeline(state, action.timeline, Immutable.fromJS(action.statuses));
 | 
				
			||||||
    case TIMELINE_UPDATE:
 | 
					    case TIMELINE_UPDATE:
 | 
				
			||||||
      return updateTimeline(state, action.timeline, Immutable.fromJS(action.status));
 | 
					      return updateTimeline(state, action.timeline, Immutable.fromJS(action.status), action.references);
 | 
				
			||||||
    case TIMELINE_DELETE:
 | 
					    case TIMELINE_DELETE:
 | 
				
			||||||
    case STATUS_DELETE_SUCCESS:
 | 
					      return deleteStatus(state, action.id, action.accountId, action.references);
 | 
				
			||||||
      return deleteStatus(state, action.id);
 | 
					    case CONTEXT_FETCH_SUCCESS:
 | 
				
			||||||
    case REBLOG_SUCCESS:
 | 
					      return normalizeContext(state, action.id, Immutable.fromJS(action.ancestors), Immutable.fromJS(action.descendants));
 | 
				
			||||||
    case FAVOURITE_SUCCESS:
 | 
					 | 
				
			||||||
    case UNREBLOG_SUCCESS:
 | 
					 | 
				
			||||||
    case UNFAVOURITE_SUCCESS:
 | 
					 | 
				
			||||||
      return normalizeStatus(state, Immutable.fromJS(action.response));
 | 
					 | 
				
			||||||
    case ACCOUNT_SET_SELF:
 | 
					 | 
				
			||||||
      return setSelf(state, Immutable.fromJS(action.account));
 | 
					 | 
				
			||||||
    case ACCOUNT_FETCH_SUCCESS:
 | 
					 | 
				
			||||||
    case FOLLOW_SUBMIT_SUCCESS:
 | 
					 | 
				
			||||||
      return normalizeAccount(state, Immutable.fromJS(action.account), Immutable.fromJS(action.relationship));
 | 
					 | 
				
			||||||
    case ACCOUNT_FOLLOW_SUCCESS:
 | 
					 | 
				
			||||||
    case ACCOUNT_UNFOLLOW_SUCCESS:
 | 
					 | 
				
			||||||
    case ACCOUNT_UNBLOCK_SUCCESS:
 | 
					 | 
				
			||||||
    case ACCOUNT_BLOCK_SUCCESS:
 | 
					 | 
				
			||||||
      return normalizeRelationship(state, Immutable.fromJS(action.relationship));
 | 
					 | 
				
			||||||
    case STATUS_FETCH_SUCCESS:
 | 
					 | 
				
			||||||
      return normalizeContext(state, Immutable.fromJS(action.status), Immutable.fromJS(action.context.ancestors), Immutable.fromJS(action.context.descendants));
 | 
					 | 
				
			||||||
    case ACCOUNT_TIMELINE_FETCH_SUCCESS:
 | 
					    case ACCOUNT_TIMELINE_FETCH_SUCCESS:
 | 
				
			||||||
      return normalizeAccountTimeline(state, action.id, Immutable.fromJS(action.statuses), action.replace);
 | 
					      return normalizeAccountTimeline(state, action.id, Immutable.fromJS(action.statuses), action.replace);
 | 
				
			||||||
    case ACCOUNT_TIMELINE_EXPAND_SUCCESS:
 | 
					    case ACCOUNT_TIMELINE_EXPAND_SUCCESS:
 | 
				
			||||||
      return appendNormalizedAccountTimeline(state, action.id, Immutable.fromJS(action.statuses));
 | 
					      return appendNormalizedAccountTimeline(state, action.id, Immutable.fromJS(action.statuses));
 | 
				
			||||||
    case SUGGESTIONS_FETCH_SUCCESS:
 | 
					 | 
				
			||||||
    case FOLLOWERS_FETCH_SUCCESS:
 | 
					 | 
				
			||||||
    case FOLLOWING_FETCH_SUCCESS:
 | 
					 | 
				
			||||||
      return normalizeAccounts(state, Immutable.fromJS(action.accounts));
 | 
					 | 
				
			||||||
    case RELATIONSHIPS_FETCH_SUCCESS:
 | 
					 | 
				
			||||||
      return normalizeRelationships(state, Immutable.fromJS(action.relationships));
 | 
					 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return state;
 | 
					      return state;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,12 +1,14 @@
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  FOLLOWERS_FETCH_SUCCESS,
 | 
					  FOLLOWERS_FETCH_SUCCESS,
 | 
				
			||||||
  FOLLOWING_FETCH_SUCCESS
 | 
					  FOLLOWING_FETCH_SUCCESS
 | 
				
			||||||
}                          from '../actions/accounts';
 | 
					} from '../actions/accounts';
 | 
				
			||||||
import Immutable           from 'immutable';
 | 
					import { SUGGESTIONS_FETCH_SUCCESS } from '../actions/suggestions';
 | 
				
			||||||
 | 
					import Immutable from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const initialState = Immutable.Map({
 | 
					const initialState = Immutable.Map({
 | 
				
			||||||
  followers: Immutable.Map(),
 | 
					  followers: Immutable.Map(),
 | 
				
			||||||
  following: Immutable.Map()
 | 
					  following: Immutable.Map(),
 | 
				
			||||||
 | 
					  suggestions: Immutable.List()
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function userLists(state = initialState, action) {
 | 
					export default function userLists(state = initialState, action) {
 | 
				
			||||||
| 
						 | 
					@ -15,6 +17,8 @@ export default function userLists(state = initialState, action) {
 | 
				
			||||||
      return state.setIn(['followers', action.id], Immutable.List(action.accounts.map(item => item.id)));
 | 
					      return state.setIn(['followers', action.id], Immutable.List(action.accounts.map(item => item.id)));
 | 
				
			||||||
    case FOLLOWING_FETCH_SUCCESS:
 | 
					    case FOLLOWING_FETCH_SUCCESS:
 | 
				
			||||||
      return state.setIn(['following', action.id], Immutable.List(action.accounts.map(item => item.id)));
 | 
					      return state.setIn(['following', action.id], Immutable.List(action.accounts.map(item => item.id)));
 | 
				
			||||||
 | 
					    case SUGGESTIONS_FETCH_SUCCESS:
 | 
				
			||||||
 | 
					      return state.set('suggestions', Immutable.List(action.accounts.map(item => item.id)));
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      return state;
 | 
					      return state;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,11 +1,11 @@
 | 
				
			||||||
import { createSelector } from 'reselect'
 | 
					import { createSelector } from 'reselect'
 | 
				
			||||||
import Immutable          from 'immutable';
 | 
					import Immutable          from 'immutable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getStatuses = state => state.getIn(['timelines', 'statuses']);
 | 
					const getStatuses = state => state.get('statuses');
 | 
				
			||||||
const getAccounts = state => state.getIn(['timelines', 'accounts']);
 | 
					const getAccounts = state => state.get('accounts');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getAccountBase         = (state, id) => state.getIn(['timelines', 'accounts', id], null);
 | 
					const getAccountBase         = (state, id) => state.getIn(['accounts', id], null);
 | 
				
			||||||
const getAccountRelationship = (state, id) => state.getIn(['timelines', 'relationships', id]);
 | 
					const getAccountRelationship = (state, id) => state.getIn(['relationships', id]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const makeGetAccount = () => {
 | 
					export const makeGetAccount = () => {
 | 
				
			||||||
  return createSelector([getAccountBase, getAccountRelationship], (base, relationship) => {
 | 
					  return createSelector([getAccountBase, getAccountRelationship], (base, relationship) => {
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@ export const makeGetAccount = () => {
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const getStatusBase = (state, id) => state.getIn(['timelines', 'statuses', id], null);
 | 
					const getStatusBase = (state, id) => state.getIn(['statuses', id], null);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const makeGetStatus = () => {
 | 
					export const makeGetStatus = () => {
 | 
				
			||||||
  return createSelector([getStatusBase, getStatuses, getAccounts], (base, statuses, accounts) => {
 | 
					  return createSelector([getStatusBase, getStatuses, getAccounts], (base, statuses, accounts) => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue