diff --git a/app/assets/javascripts/components/actions/accounts.jsx b/app/assets/javascripts/components/actions/accounts.jsx
index 22456219..fdfd204a 100644
--- a/app/assets/javascripts/components/actions/accounts.jsx
+++ b/app/assets/javascripts/components/actions/accounts.jsx
@@ -1,5 +1,4 @@
import api from '../api'
-import axios from 'axios';
import Immutable from 'immutable';
export const ACCOUNT_SET_SELF = 'ACCOUNT_SET_SELF';
@@ -53,12 +52,11 @@ export function setAccountSelf(account) {
export function fetchAccount(id) {
return (dispatch, getState) => {
- const boundApi = api(getState);
-
dispatch(fetchAccountRequest(id));
- axios.all([boundApi.get(`/api/v1/accounts/${id}`), boundApi.get(`/api/v1/accounts/relationships?id=${id}`)]).then(values => {
- dispatch(fetchAccountSuccess(values[0].data, values[1].data[0]));
+ api(getState).get(`/api/v1/accounts/${id}`).then(response => {
+ dispatch(fetchAccountSuccess(response.data));
+ dispatch(fetchRelationships([id]));
}).catch(error => {
dispatch(fetchAccountFail(id, error));
});
@@ -107,11 +105,10 @@ export function fetchAccountRequest(id) {
};
};
-export function fetchAccountSuccess(account, relationship) {
+export function fetchAccountSuccess(account) {
return {
type: ACCOUNT_FETCH_SUCCESS,
- account: account,
- relationship: relationship
+ account: account
};
};
diff --git a/app/assets/javascripts/components/actions/statuses.jsx b/app/assets/javascripts/components/actions/statuses.jsx
index 2fb2d1ba..cbee94bc 100644
--- a/app/assets/javascripts/components/actions/statuses.jsx
+++ b/app/assets/javascripts/components/actions/statuses.jsx
@@ -1,5 +1,6 @@
-import api from '../api';
-import axios from 'axios';
+import api from '../api';
+
+import { deleteFromTimelines } from './timelines';
export const STATUS_FETCH_REQUEST = 'STATUS_FETCH_REQUEST';
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_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) {
return {
type: STATUS_FETCH_REQUEST,
@@ -18,12 +23,11 @@ export function fetchStatusRequest(id) {
export function fetchStatus(id) {
return (dispatch, getState) => {
- const boundApi = api(getState);
-
dispatch(fetchStatusRequest(id));
- axios.all([boundApi.get(`/api/v1/statuses/${id}`), boundApi.get(`/api/v1/statuses/${id}/context`)]).then(values => {
- dispatch(fetchStatusSuccess(values[0].data, values[1].data));
+ api(getState).get(`/api/v1/statuses/${id}`).then(response => {
+ dispatch(fetchStatusSuccess(response.data));
+ dispatch(fetchContext(id));
}).catch(error => {
dispatch(fetchStatusFail(id, error));
});
@@ -52,6 +56,7 @@ export function deleteStatus(id) {
api(getState).delete(`/api/v1/statuses/${id}`).then(response => {
dispatch(deleteStatusSuccess(id));
+ dispatch(deleteFromTimelines(id));
}).catch(error => {
dispatch(deleteStatusFail(id, error));
});
@@ -79,3 +84,40 @@ export function deleteStatusFail(id, 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
+ };
+};
diff --git a/app/assets/javascripts/components/actions/timelines.jsx b/app/assets/javascripts/components/actions/timelines.jsx
index 831065fe..01eee171 100644
--- a/app/assets/javascripts/components/actions/timelines.jsx
+++ b/app/assets/javascripts/components/actions/timelines.jsx
@@ -21,17 +21,29 @@ export function refreshTimelineSuccess(timeline, statuses, replace) {
};
export function updateTimeline(timeline, status) {
- return {
- type: TIMELINE_UPDATE,
- timeline: timeline,
- status: status
+ return (dispatch, getState) => {
+ const references = status.reblog ? getState().get('statuses').filter((item, itemId) => (itemId === status.reblog.id || item.get('reblog') === status.reblog.id)).map((_, itemId) => itemId) : [];
+
+ dispatch({
+ type: TIMELINE_UPDATE,
+ timeline,
+ status,
+ references
+ });
};
};
export function deleteFromTimelines(id) {
- return {
- type: TIMELINE_DELETE,
- id: id
+ return (dispatch, getState) => {
+ const accountId = getState().getIn(['statuses', id, 'account']);
+ 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
+ });
};
};
diff --git a/app/assets/javascripts/components/components/status.jsx b/app/assets/javascripts/components/components/status.jsx
index 1d156000..8424023a 100644
--- a/app/assets/javascripts/components/components/status.jsx
+++ b/app/assets/javascripts/components/components/status.jsx
@@ -15,7 +15,7 @@ const Status = React.createClass({
},
propTypes: {
- status: ImmutablePropTypes.map.isRequired,
+ status: ImmutablePropTypes.map,
wrapped: React.PropTypes.bool,
onReply: React.PropTypes.func,
onFavourite: React.PropTypes.func,
diff --git a/app/assets/javascripts/components/containers/status_container.jsx b/app/assets/javascripts/components/containers/status_container.jsx
index 0f4b8ee1..5e0b489b 100644
--- a/app/assets/javascripts/components/containers/status_container.jsx
+++ b/app/assets/javascripts/components/containers/status_container.jsx
@@ -19,7 +19,7 @@ const makeMapStateToProps = () => {
const mapStateToProps = (state, props) => ({
status: getStatus(state, props.id),
- me: state.getIn(['timelines', 'me'])
+ me: state.getIn(['meta', 'me'])
});
return mapStateToProps;
diff --git a/app/assets/javascripts/components/features/account/index.jsx b/app/assets/javascripts/components/features/account/index.jsx
index 548f7fc1..6cadcff4 100644
--- a/app/assets/javascripts/components/features/account/index.jsx
+++ b/app/assets/javascripts/components/features/account/index.jsx
@@ -26,7 +26,7 @@ const makeMapStateToProps = () => {
const mapStateToProps = (state, props) => ({
account: getAccount(state, Number(props.params.accountId)),
- me: state.getIn(['timelines', 'me'])
+ me: state.getIn(['meta', 'me'])
});
return mapStateToProps;
diff --git a/app/assets/javascripts/components/features/account_timeline/index.jsx b/app/assets/javascripts/components/features/account_timeline/index.jsx
index f7957036..cae88efd 100644
--- a/app/assets/javascripts/components/features/account_timeline/index.jsx
+++ b/app/assets/javascripts/components/features/account_timeline/index.jsx
@@ -10,7 +10,7 @@ import LoadingIndicator from '../../components/loading_indicator';
const mapStateToProps = (state, props) => ({
statusIds: state.getIn(['timelines', 'accounts_timelines', Number(props.params.accountId)]),
- me: state.getIn(['timelines', 'me'])
+ me: state.getIn(['meta', 'me'])
});
const AccountTimeline = React.createClass({
diff --git a/app/assets/javascripts/components/features/compose/containers/suggestions_container.jsx b/app/assets/javascripts/components/features/compose/containers/suggestions_container.jsx
index 12ee1ebc..944ceed8 100644
--- a/app/assets/javascripts/components/features/compose/containers/suggestions_container.jsx
+++ b/app/assets/javascripts/components/features/compose/containers/suggestions_container.jsx
@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import SuggestionsBox from '../components/suggestions_box';
const mapStateToProps = (state) => ({
- accountIds: state.get('suggestions')
+ accountIds: state.getIn(['user_lists', 'suggestions'])
});
export default connect(mapStateToProps)(SuggestionsBox);
diff --git a/app/assets/javascripts/components/features/followers/containers/account_container.jsx b/app/assets/javascripts/components/features/followers/containers/account_container.jsx
index 988d60ad..c5d5c588 100644
--- a/app/assets/javascripts/components/features/followers/containers/account_container.jsx
+++ b/app/assets/javascripts/components/features/followers/containers/account_container.jsx
@@ -11,7 +11,7 @@ const makeMapStateToProps = () => {
const mapStateToProps = (state, props) => ({
account: getAccount(state, props.id),
- me: state.getIn(['timelines', 'me'])
+ me: state.getIn(['meta', 'me'])
});
return mapStateToProps;
diff --git a/app/assets/javascripts/components/features/status/index.jsx b/app/assets/javascripts/components/features/status/index.jsx
index dc29a87c..78498039 100644
--- a/app/assets/javascripts/components/features/status/index.jsx
+++ b/app/assets/javascripts/components/features/status/index.jsx
@@ -31,7 +31,7 @@ const makeMapStateToProps = () => {
status: getStatus(state, Number(props.params.statusId)),
ancestorsIds: state.getIn(['timelines', 'ancestors', 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;
@@ -43,8 +43,8 @@ const Status = React.createClass({
params: React.PropTypes.object.isRequired,
dispatch: React.PropTypes.func.isRequired,
status: ImmutablePropTypes.map,
- ancestorsIds: ImmutablePropTypes.orderedSet,
- descendantsIds: ImmutablePropTypes.orderedSet
+ ancestorsIds: ImmutablePropTypes.list,
+ descendantsIds: ImmutablePropTypes.list
},
mixins: [PureRenderMixin],
@@ -101,11 +101,11 @@ const Status = React.createClass({
const account = status.get('account');
- if (ancestorsIds) {
+ if (ancestorsIds && ancestorsIds.size > 0) {
ancestors =
{this.renderChildren(ancestorsIds)}
;
}
- if (descendantsIds) {
+ if (descendantsIds && descendantsIds.size > 0) {
descendants = {this.renderChildren(descendantsIds)}
;
}
diff --git a/app/assets/javascripts/components/features/ui/containers/navigation_container.jsx b/app/assets/javascripts/components/features/ui/containers/navigation_container.jsx
index 4aeea4c3..51e2513d 100644
--- a/app/assets/javascripts/components/features/ui/containers/navigation_container.jsx
+++ b/app/assets/javascripts/components/features/ui/containers/navigation_container.jsx
@@ -2,7 +2,7 @@ import { connect } from 'react-redux';
import NavigationBar from '../components/navigation_bar';
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);
diff --git a/app/assets/javascripts/components/reducers/accounts.jsx b/app/assets/javascripts/components/reducers/accounts.jsx
new file mode 100644
index 00000000..95f8059d
--- /dev/null
+++ b/app/assets/javascripts/components/reducers/accounts.jsx
@@ -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;
+ }
+};
diff --git a/app/assets/javascripts/components/reducers/compose.jsx b/app/assets/javascripts/components/reducers/compose.jsx
index f2fd3ad8..1c676326 100644
--- a/app/assets/javascripts/components/reducers/compose.jsx
+++ b/app/assets/javascripts/components/reducers/compose.jsx
@@ -11,10 +11,10 @@ import {
COMPOSE_UPLOAD_FAIL,
COMPOSE_UPLOAD_UNDO,
COMPOSE_UPLOAD_PROGRESS
-} from '../actions/compose';
-import { TIMELINE_DELETE } from '../actions/timelines';
+} from '../actions/compose';
+import { TIMELINE_DELETE } from '../actions/timelines';
import { ACCOUNT_SET_SELF } from '../actions/accounts';
-import Immutable from 'immutable';
+import Immutable from 'immutable';
const initialState = Immutable.Map({
text: '',
diff --git a/app/assets/javascripts/components/reducers/follow.jsx b/app/assets/javascripts/components/reducers/follow.jsx
index e1dc293c..ed6e8e0e 100644
--- a/app/assets/javascripts/components/reducers/follow.jsx
+++ b/app/assets/javascripts/components/reducers/follow.jsx
@@ -3,7 +3,7 @@ import {
FOLLOW_SUBMIT_REQUEST,
FOLLOW_SUBMIT_SUCCESS,
FOLLOW_SUBMIT_FAIL
-} from '../actions/follow';
+} from '../actions/follow';
import Immutable from 'immutable';
const initialState = Immutable.Map({
diff --git a/app/assets/javascripts/components/reducers/index.jsx b/app/assets/javascripts/components/reducers/index.jsx
index 62d6839d..ccc9e8e8 100644
--- a/app/assets/javascripts/components/reducers/index.jsx
+++ b/app/assets/javascripts/components/reducers/index.jsx
@@ -7,7 +7,9 @@ import notifications from './notifications';
import { loadingBarReducer } from 'react-redux-loading-bar';
import modal from './modal';
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({
timelines,
@@ -18,5 +20,7 @@ export default combineReducers({
loadingBar: loadingBarReducer,
modal,
user_lists,
- suggestions
+ accounts,
+ statuses,
+ relationships
});
diff --git a/app/assets/javascripts/components/reducers/meta.jsx b/app/assets/javascripts/components/reducers/meta.jsx
index 71a14dbd..c7222c60 100644
--- a/app/assets/javascripts/components/reducers/meta.jsx
+++ b/app/assets/javascripts/components/reducers/meta.jsx
@@ -1,5 +1,6 @@
-import { ACCESS_TOKEN_SET } from '../actions/meta';
-import Immutable from 'immutable';
+import { ACCESS_TOKEN_SET } from '../actions/meta';
+import { ACCOUNT_SET_SELF } from '../actions/accounts';
+import Immutable from 'immutable';
const initialState = Immutable.Map();
@@ -7,6 +8,8 @@ export default function meta(state = initialState, action) {
switch(action.type) {
case ACCESS_TOKEN_SET:
return state.set('access_token', action.token);
+ case ACCOUNT_SET_SELF:
+ return state.set('me', action.account.id);
default:
return state;
}
diff --git a/app/assets/javascripts/components/reducers/notifications.jsx b/app/assets/javascripts/components/reducers/notifications.jsx
index 886587bd..efe8d973 100644
--- a/app/assets/javascripts/components/reducers/notifications.jsx
+++ b/app/assets/javascripts/components/reducers/notifications.jsx
@@ -2,8 +2,8 @@ import {
NOTIFICATION_SHOW,
NOTIFICATION_DISMISS,
NOTIFICATION_CLEAR
-} from '../actions/notifications';
-import Immutable from 'immutable';
+} from '../actions/notifications';
+import Immutable from 'immutable';
const initialState = Immutable.List([]);
diff --git a/app/assets/javascripts/components/reducers/relationships.jsx b/app/assets/javascripts/components/reducers/relationships.jsx
new file mode 100644
index 00000000..165b1f3f
--- /dev/null
+++ b/app/assets/javascripts/components/reducers/relationships.jsx
@@ -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;
+ }
+};
diff --git a/app/assets/javascripts/components/reducers/statuses.jsx b/app/assets/javascripts/components/reducers/statuses.jsx
new file mode 100644
index 00000000..4a970038
--- /dev/null
+++ b/app/assets/javascripts/components/reducers/statuses.jsx
@@ -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;
+ }
+};
diff --git a/app/assets/javascripts/components/reducers/suggestions.jsx b/app/assets/javascripts/components/reducers/suggestions.jsx
deleted file mode 100644
index 9d2b7d96..00000000
--- a/app/assets/javascripts/components/reducers/suggestions.jsx
+++ /dev/null
@@ -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;
- }
-}
diff --git a/app/assets/javascripts/components/reducers/timelines.jsx b/app/assets/javascripts/components/reducers/timelines.jsx
index 4bf97c18..db13cad3 100644
--- a/app/assets/javascripts/components/reducers/timelines.jsx
+++ b/app/assets/javascripts/components/reducers/timelines.jsx
@@ -3,85 +3,52 @@ import {
TIMELINE_UPDATE,
TIMELINE_DELETE,
TIMELINE_EXPAND_SUCCESS
-} from '../actions/timelines';
+} from '../actions/timelines';
import {
REBLOG_SUCCESS,
UNREBLOG_SUCCESS,
FAVOURITE_SUCCESS,
UNFAVOURITE_SUCCESS
-} from '../actions/interactions';
+} from '../actions/interactions';
import {
- ACCOUNT_SET_SELF,
ACCOUNT_FETCH_SUCCESS,
- ACCOUNT_FOLLOW_SUCCESS,
- ACCOUNT_UNFOLLOW_SUCCESS,
- ACCOUNT_BLOCK_SUCCESS,
- ACCOUNT_UNBLOCK_SUCCESS,
ACCOUNT_TIMELINE_FETCH_SUCCESS,
- ACCOUNT_TIMELINE_EXPAND_SUCCESS,
- FOLLOWERS_FETCH_SUCCESS,
- FOLLOWING_FETCH_SUCCESS,
- RELATIONSHIPS_FETCH_SUCCESS
-} from '../actions/accounts';
+ ACCOUNT_TIMELINE_EXPAND_SUCCESS
+} from '../actions/accounts';
import {
STATUS_FETCH_SUCCESS,
- STATUS_DELETE_SUCCESS
-} from '../actions/statuses';
-import { FOLLOW_SUBMIT_SUCCESS } from '../actions/follow';
-import { SUGGESTIONS_FETCH_SUCCESS } from '../actions/suggestions';
-import Immutable from 'immutable';
+ CONTEXT_FETCH_SUCCESS
+} from '../actions/statuses';
+import Immutable from 'immutable';
const initialState = Immutable.Map({
- home: Immutable.List([]),
- mentions: Immutable.List([]),
- public: Immutable.List([]),
- statuses: Immutable.Map(),
- accounts: Immutable.Map(),
+ home: Immutable.List(),
+ mentions: Immutable.List(),
+ public: Immutable.List(),
accounts_timelines: Immutable.Map(),
- me: null,
ancestors: Immutable.Map(),
- descendants: Immutable.Map(),
- relationships: Immutable.Map(),
- suggestions: Immutable.List([])
+ descendants: Immutable.Map()
});
-function normalizeStatus(state, status) {
- // Separate account
- let account = status.get('account');
- status = status.set('account', account.get('id'));
+const normalizeStatus = (state, status) => {
+ const replyToId = status.get('in_reply_to_id');
+ const id = status.get('id');
- // Separate reblog, repeat for reblog
- let reblog = status.get('reblog', null);
-
- 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')));
+ if (replyToId) {
+ if (!state.getIn(['descendants', replyToId], Immutable.List()).includes(id)) {
+ state = state.updateIn(['descendants', replyToId], Immutable.List(), set => set.push(id));
}
- map.setIn(['accounts', account.get('id')], account);
- map.setIn(['statuses', status.get('id')], status);
- });
+ if (!state.getIn(['ancestors', id], Immutable.List()).includes(replyToId)) {
+ state = state.updateIn(['ancestors', id], Immutable.List(), set => set.push(replyToId));
+ }
+ }
+
+ return state;
};
-function normalizeTimeline(state, timeline, statuses, replace = false) {
- let ids = Immutable.List([]);
+const normalizeTimeline = (state, timeline, statuses, replace = false) => {
+ let ids = Immutable.List();
statuses.forEach((status, i) => {
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)));
};
-function appendNormalizedTimeline(state, timeline, statuses) {
- let moreIds = Immutable.List([]);
+const appendNormalizedTimeline = (state, timeline, statuses) => {
+ let moreIds = Immutable.List();
statuses.forEach((status, i) => {
state = normalizeStatus(state, status);
@@ -102,8 +69,8 @@ function appendNormalizedTimeline(state, timeline, statuses) {
return state.update(timeline, list => list.push(...moreIds));
};
-function normalizeAccountTimeline(state, accountId, statuses, replace = false) {
- let ids = Immutable.List([]);
+const normalizeAccountTimeline = (state, accountId, statuses, replace = false) => {
+ let ids = Immutable.List();
statuses.forEach((status, i) => {
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)));
};
-function appendNormalizedAccountTimeline(state, accountId, statuses) {
+const appendNormalizedAccountTimeline = (state, accountId, statuses) => {
let moreIds = Immutable.List([]);
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));
};
-function updateTimeline(state, timeline, status) {
+const updateTimeline = (state, timeline, status, references) => {
state = normalizeStatus(state, status);
state = state.update(timeline, list => {
const reblogOfId = status.getIn(['reblog', 'id'], null);
if (reblogOfId !== null) {
- const otherReblogs = state.get('statuses').filter(item => item.get('reblog') === reblogOfId).map((_, itemId) => itemId);
- list = list.filterNot(itemId => (itemId === reblogOfId || otherReblogs.includes(itemId)));
+ list = list.filterNot(itemId => references.includes(itemId));
}
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;
};
-function deleteStatus(state, id) {
- const status = state.getIn(['statuses', id]);
-
- if (!status) {
- return state;
- }
-
+const deleteStatus = (state, id, accountId, references) => {
// 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));
});
// 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
- 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]);
-};
-
-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);
+ references.forEach(ref => {
+ state = deleteStatus(state, ref[0], ref[1], []);
});
return state;
};
-function setSelf(state, account) {
- state = normalizeAccount(state, account);
- return state.set('me', account.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();
+const normalizeContext = (state, id, ancestors, descendants) => {
+ const ancestorsIds = ancestors.map(ancestor => ancestor.get('id'));
+ const descendantsIds = descendants.map(descendant => descendant.get('id'));
return state.withMutations(map => {
- map.setIn(['ancestors', status.get('id')], ancestorsIds);
- map.setIn(['descendants', status.get('id')], descendantsIds);
+ map.setIn(['ancestors', id], ancestorsIds);
+ 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) {
switch(action.type) {
case TIMELINE_REFRESH_SUCCESS:
@@ -232,37 +152,15 @@ export default function timelines(state = initialState, action) {
case TIMELINE_EXPAND_SUCCESS:
return appendNormalizedTimeline(state, action.timeline, Immutable.fromJS(action.statuses));
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 STATUS_DELETE_SUCCESS:
- return deleteStatus(state, action.id);
- case REBLOG_SUCCESS:
- 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));
+ return deleteStatus(state, action.id, action.accountId, action.references);
+ case CONTEXT_FETCH_SUCCESS:
+ return normalizeContext(state, action.id, Immutable.fromJS(action.ancestors), Immutable.fromJS(action.descendants));
case ACCOUNT_TIMELINE_FETCH_SUCCESS:
return normalizeAccountTimeline(state, action.id, Immutable.fromJS(action.statuses), action.replace);
case ACCOUNT_TIMELINE_EXPAND_SUCCESS:
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:
return state;
}
diff --git a/app/assets/javascripts/components/reducers/user_lists.jsx b/app/assets/javascripts/components/reducers/user_lists.jsx
index ee4b8429..d3b073e9 100644
--- a/app/assets/javascripts/components/reducers/user_lists.jsx
+++ b/app/assets/javascripts/components/reducers/user_lists.jsx
@@ -1,12 +1,14 @@
import {
FOLLOWERS_FETCH_SUCCESS,
FOLLOWING_FETCH_SUCCESS
-} from '../actions/accounts';
-import Immutable from 'immutable';
+} from '../actions/accounts';
+import { SUGGESTIONS_FETCH_SUCCESS } from '../actions/suggestions';
+import Immutable from 'immutable';
const initialState = Immutable.Map({
followers: Immutable.Map(),
- following: Immutable.Map()
+ following: Immutable.Map(),
+ suggestions: Immutable.List()
});
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)));
case FOLLOWING_FETCH_SUCCESS:
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:
return state;
}
diff --git a/app/assets/javascripts/components/selectors/index.jsx b/app/assets/javascripts/components/selectors/index.jsx
index 2bbbd3f7..33b179cb 100644
--- a/app/assets/javascripts/components/selectors/index.jsx
+++ b/app/assets/javascripts/components/selectors/index.jsx
@@ -1,11 +1,11 @@
import { createSelector } from 'reselect'
import Immutable from 'immutable';
-const getStatuses = state => state.getIn(['timelines', 'statuses']);
-const getAccounts = state => state.getIn(['timelines', 'accounts']);
+const getStatuses = state => state.get('statuses');
+const getAccounts = state => state.get('accounts');
-const getAccountBase = (state, id) => state.getIn(['timelines', 'accounts', id], null);
-const getAccountRelationship = (state, id) => state.getIn(['timelines', 'relationships', id]);
+const getAccountBase = (state, id) => state.getIn(['accounts', id], null);
+const getAccountRelationship = (state, id) => state.getIn(['relationships', id]);
export const makeGetAccount = () => {
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 = () => {
return createSelector([getStatusBase, getStatuses, getAccounts], (base, statuses, accounts) => {