sforkowany z mirror/soapbox
Merge branch 'decrement-comment-count' into 'develop'
Decrement reply count See merge request soapbox-pub/soapbox-fe!1481dnd
commit
46484bd940
|
@ -1,8 +1,13 @@
|
||||||
|
import { fromJS, Map as ImmutableMap } from 'immutable';
|
||||||
|
|
||||||
import { STATUSES_IMPORT } from 'soapbox/actions/importer';
|
import { STATUSES_IMPORT } from 'soapbox/actions/importer';
|
||||||
import { __stub } from 'soapbox/api';
|
import { __stub } from 'soapbox/api';
|
||||||
import { mockStore, rootState } from 'soapbox/jest/test-helpers';
|
import { mockStore, rootState } from 'soapbox/jest/test-helpers';
|
||||||
|
import { normalizeStatus } from 'soapbox/normalizers/status';
|
||||||
|
import rootReducer from 'soapbox/reducers';
|
||||||
|
|
||||||
import { fetchContext } from '../statuses';
|
import { fetchContext } from '../statuses';
|
||||||
|
import { deleteStatus } from '../statuses';
|
||||||
|
|
||||||
describe('fetchContext()', () => {
|
describe('fetchContext()', () => {
|
||||||
it('handles Mitra context', done => {
|
it('handles Mitra context', done => {
|
||||||
|
@ -25,3 +30,133 @@ describe('fetchContext()', () => {
|
||||||
}).catch(console.error);
|
}).catch(console.error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('deleteStatus()', () => {
|
||||||
|
let store;
|
||||||
|
|
||||||
|
describe('if logged out', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
const state = rootReducer(undefined, {}).set('me', null);
|
||||||
|
store = mockStore(state);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should do nothing', async() => {
|
||||||
|
await store.dispatch(deleteStatus('1', {}));
|
||||||
|
const actions = store.getActions();
|
||||||
|
|
||||||
|
expect(actions).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('if logged in', () => {
|
||||||
|
const statusId = 'AHU2RrX0wdcwzCYjFQ';
|
||||||
|
const cachedStatus = normalizeStatus({
|
||||||
|
id: statusId,
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
const state = rootReducer(undefined, {})
|
||||||
|
.set('me', '1234')
|
||||||
|
.set('statuses', fromJS({
|
||||||
|
[statusId]: cachedStatus,
|
||||||
|
}));
|
||||||
|
store = mockStore(state);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with a successful API request', () => {
|
||||||
|
let status;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
status = require('soapbox/__fixtures__/pleroma-status-deleted.json');
|
||||||
|
|
||||||
|
__stub((mock) => {
|
||||||
|
mock.onDelete(`/api/v1/statuses/${statusId}`).reply(200, status);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should delete the status from the API', async() => {
|
||||||
|
const expectedActions = [
|
||||||
|
{
|
||||||
|
type: 'STATUS_DELETE_REQUEST',
|
||||||
|
params: cachedStatus,
|
||||||
|
},
|
||||||
|
{ type: 'STATUS_DELETE_SUCCESS', id: statusId },
|
||||||
|
{
|
||||||
|
type: 'TIMELINE_DELETE',
|
||||||
|
id: statusId,
|
||||||
|
accountId: null,
|
||||||
|
references: ImmutableMap({}),
|
||||||
|
reblogOf: null,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
await store.dispatch(deleteStatus(statusId, {}));
|
||||||
|
const actions = store.getActions();
|
||||||
|
|
||||||
|
expect(actions).toEqual(expectedActions);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle redraft', async() => {
|
||||||
|
const expectedActions = [
|
||||||
|
{
|
||||||
|
type: 'STATUS_DELETE_REQUEST',
|
||||||
|
params: cachedStatus,
|
||||||
|
},
|
||||||
|
{ type: 'STATUS_DELETE_SUCCESS', id: statusId },
|
||||||
|
{
|
||||||
|
type: 'TIMELINE_DELETE',
|
||||||
|
id: statusId,
|
||||||
|
accountId: null,
|
||||||
|
references: ImmutableMap({}),
|
||||||
|
reblogOf: null,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'COMPOSE_SET_STATUS',
|
||||||
|
status: cachedStatus,
|
||||||
|
rawText: status.text,
|
||||||
|
explicitAddressing: false,
|
||||||
|
spoilerText: '',
|
||||||
|
contentType: 'text/markdown',
|
||||||
|
v: {
|
||||||
|
build: undefined,
|
||||||
|
compatVersion: '0.0.0',
|
||||||
|
software: 'Mastodon',
|
||||||
|
version: '0.0.0',
|
||||||
|
},
|
||||||
|
withRedraft: true,
|
||||||
|
},
|
||||||
|
{ type: 'MODAL_OPEN', modalType: 'COMPOSE', modalProps: undefined },
|
||||||
|
];
|
||||||
|
await store.dispatch(deleteStatus(statusId, {}, true));
|
||||||
|
const actions = store.getActions();
|
||||||
|
|
||||||
|
expect(actions).toEqual(expectedActions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('with an unsuccessful API request', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
__stub((mock) => {
|
||||||
|
mock.onDelete(`/api/v1/statuses/${statusId}`).networkError();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should dispatch failed action', async() => {
|
||||||
|
const expectedActions = [
|
||||||
|
{
|
||||||
|
type: 'STATUS_DELETE_REQUEST',
|
||||||
|
params: cachedStatus,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: 'STATUS_DELETE_FAIL',
|
||||||
|
params: cachedStatus,
|
||||||
|
error: new Error('Network Error'),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
await store.dispatch(deleteStatus(statusId, {}, true));
|
||||||
|
const actions = store.getActions();
|
||||||
|
|
||||||
|
expect(actions).toEqual(expectedActions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
|
@ -124,7 +124,7 @@ export function fetchStatus(id) {
|
||||||
|
|
||||||
export function deleteStatus(id, routerHistory, withRedraft = false) {
|
export function deleteStatus(id, routerHistory, withRedraft = false) {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
if (!isLoggedIn(getState)) return;
|
if (!isLoggedIn(getState)) return null;
|
||||||
|
|
||||||
let status = getState().getIn(['statuses', id]);
|
let status = getState().getIn(['statuses', id]);
|
||||||
|
|
||||||
|
@ -132,9 +132,11 @@ export function deleteStatus(id, routerHistory, withRedraft = false) {
|
||||||
status = status.set('poll', getState().getIn(['polls', status.get('poll')]));
|
status = status.set('poll', getState().getIn(['polls', status.get('poll')]));
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch({ type: STATUS_DELETE_REQUEST, id });
|
dispatch({ type: STATUS_DELETE_REQUEST, params: status });
|
||||||
|
|
||||||
api(getState).delete(`/api/v1/statuses/${id}`).then(response => {
|
return api(getState)
|
||||||
|
.delete(`/api/v1/statuses/${id}`)
|
||||||
|
.then(response => {
|
||||||
dispatch({ type: STATUS_DELETE_SUCCESS, id });
|
dispatch({ type: STATUS_DELETE_SUCCESS, id });
|
||||||
dispatch(deleteFromTimelines(id));
|
dispatch(deleteFromTimelines(id));
|
||||||
|
|
||||||
|
@ -142,8 +144,9 @@ export function deleteStatus(id, routerHistory, withRedraft = false) {
|
||||||
dispatch(setComposeToStatus(status, response.data.text, response.data.spoiler_text, response.data.pleroma?.content_type, withRedraft));
|
dispatch(setComposeToStatus(status, response.data.text, response.data.spoiler_text, response.data.pleroma?.content_type, withRedraft));
|
||||||
dispatch(openModal('COMPOSE'));
|
dispatch(openModal('COMPOSE'));
|
||||||
}
|
}
|
||||||
}).catch(error => {
|
})
|
||||||
dispatch({ type: STATUS_DELETE_FAIL, id, error });
|
.catch(error => {
|
||||||
|
dispatch({ type: STATUS_DELETE_FAIL, params: status, error });
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ import { STATUS_IMPORT } from 'soapbox/actions/importer';
|
||||||
import {
|
import {
|
||||||
STATUS_CREATE_REQUEST,
|
STATUS_CREATE_REQUEST,
|
||||||
STATUS_CREATE_FAIL,
|
STATUS_CREATE_FAIL,
|
||||||
|
STATUS_DELETE_REQUEST,
|
||||||
|
STATUS_DELETE_FAIL,
|
||||||
} from 'soapbox/actions/statuses';
|
} from 'soapbox/actions/statuses';
|
||||||
|
|
||||||
import reducer from '../statuses';
|
import reducer from '../statuses';
|
||||||
|
@ -183,4 +185,56 @@ Promoting free speech, even for people and ideas you dislike`;
|
||||||
expect(result).toEqual(4);
|
expect(result).toEqual(4);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('STATUS_DELETE_REQUEST', () => {
|
||||||
|
it('decrements the replies_count of its parent', () => {
|
||||||
|
const state = fromJS({ '123': { replies_count: 4 } });
|
||||||
|
|
||||||
|
const action = {
|
||||||
|
type: STATUS_DELETE_REQUEST,
|
||||||
|
params: { in_reply_to_id: '123' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = reducer(state, action).getIn(['123', 'replies_count']);
|
||||||
|
expect(result).toEqual(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gracefully does nothing if no parent', () => {
|
||||||
|
const state = fromJS({ '123': { replies_count: 4 } });
|
||||||
|
|
||||||
|
const action = {
|
||||||
|
type: STATUS_DELETE_REQUEST,
|
||||||
|
params: { id: '1' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = reducer(state, action).getIn(['123', 'replies_count']);
|
||||||
|
expect(result).toEqual(4);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('STATUS_DELETE_FAIL', () => {
|
||||||
|
it('decrements the replies_count of its parent', () => {
|
||||||
|
const state = fromJS({ '123': { replies_count: 4 } });
|
||||||
|
|
||||||
|
const action = {
|
||||||
|
type: STATUS_DELETE_FAIL,
|
||||||
|
params: { in_reply_to_id: '123' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = reducer(state, action).getIn(['123', 'replies_count']);
|
||||||
|
expect(result).toEqual(5);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gracefully does nothing if no parent', () => {
|
||||||
|
const state = fromJS({ '123': { replies_count: 4 } });
|
||||||
|
|
||||||
|
const action = {
|
||||||
|
type: STATUS_DELETE_FAIL,
|
||||||
|
params: { id: '1' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = reducer(state, action).getIn(['123', 'replies_count']);
|
||||||
|
expect(result).toEqual(4);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -28,6 +28,8 @@ import {
|
||||||
STATUS_UNMUTE_SUCCESS,
|
STATUS_UNMUTE_SUCCESS,
|
||||||
STATUS_REVEAL,
|
STATUS_REVEAL,
|
||||||
STATUS_HIDE,
|
STATUS_HIDE,
|
||||||
|
STATUS_DELETE_REQUEST,
|
||||||
|
STATUS_DELETE_FAIL,
|
||||||
} from '../actions/statuses';
|
} from '../actions/statuses';
|
||||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||||
|
|
||||||
|
@ -152,7 +154,7 @@ const deleteStatus = (state: State, id: string, references: Array<string>) => {
|
||||||
return state.delete(id);
|
return state.delete(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
const importPendingStatus = (state: State, { in_reply_to_id }: APIEntity) => {
|
const incrementReplyCount = (state: State, { in_reply_to_id }: APIEntity) => {
|
||||||
if (in_reply_to_id) {
|
if (in_reply_to_id) {
|
||||||
return state.updateIn([in_reply_to_id, 'replies_count'], 0, count => {
|
return state.updateIn([in_reply_to_id, 'replies_count'], 0, count => {
|
||||||
return typeof count === 'number' ? count + 1 : 0;
|
return typeof count === 'number' ? count + 1 : 0;
|
||||||
|
@ -162,7 +164,7 @@ const importPendingStatus = (state: State, { in_reply_to_id }: APIEntity) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const deletePendingStatus = (state: State, { in_reply_to_id }: APIEntity) => {
|
const decrementReplyCount = (state: State, { in_reply_to_id }: APIEntity) => {
|
||||||
if (in_reply_to_id) {
|
if (in_reply_to_id) {
|
||||||
return state.updateIn([in_reply_to_id, 'replies_count'], 0, count => {
|
return state.updateIn([in_reply_to_id, 'replies_count'], 0, count => {
|
||||||
return typeof count === 'number' ? Math.max(0, count - 1) : 0;
|
return typeof count === 'number' ? Math.max(0, count - 1) : 0;
|
||||||
|
@ -200,9 +202,9 @@ export default function statuses(state = initialState, action: AnyAction): State
|
||||||
case STATUSES_IMPORT:
|
case STATUSES_IMPORT:
|
||||||
return importStatuses(state, action.statuses, action.expandSpoilers);
|
return importStatuses(state, action.statuses, action.expandSpoilers);
|
||||||
case STATUS_CREATE_REQUEST:
|
case STATUS_CREATE_REQUEST:
|
||||||
return importPendingStatus(state, action.params);
|
return incrementReplyCount(state, action.params);
|
||||||
case STATUS_CREATE_FAIL:
|
case STATUS_CREATE_FAIL:
|
||||||
return deletePendingStatus(state, action.params);
|
return decrementReplyCount(state, action.params);
|
||||||
case FAVOURITE_REQUEST:
|
case FAVOURITE_REQUEST:
|
||||||
return simulateFavourite(state, action.status.id, true);
|
return simulateFavourite(state, action.status.id, true);
|
||||||
case UNFAVOURITE_REQUEST:
|
case UNFAVOURITE_REQUEST:
|
||||||
|
@ -249,6 +251,10 @@ export default function statuses(state = initialState, action: AnyAction): State
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
case STATUS_DELETE_REQUEST:
|
||||||
|
return decrementReplyCount(state, action.params);
|
||||||
|
case STATUS_DELETE_FAIL:
|
||||||
|
return incrementReplyCount(state, action.params);
|
||||||
case TIMELINE_DELETE:
|
case TIMELINE_DELETE:
|
||||||
return deleteStatus(state, action.id, action.references);
|
return deleteStatus(state, action.id, action.references);
|
||||||
default:
|
default:
|
||||||
|
|
Ładowanie…
Reference in New Issue