Improve how errors are displayed in the UI
This commit is contained in:
parent
aea151a0de
commit
f88b8ce757
11 changed files with 54 additions and 80 deletions
|
@ -48,7 +48,6 @@ export function fetchAccount(id) {
|
||||||
axios.all([boundApi.get(`/api/v1/accounts/${id}`), boundApi.get(`/api/v1/accounts/relationships?id=${id}`)]).then(values => {
|
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]));
|
dispatch(fetchAccountSuccess(values[0].data, values[1].data[0]));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error(error);
|
|
||||||
dispatch(fetchAccountFail(id, error));
|
dispatch(fetchAccountFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -61,7 +60,6 @@ export function fetchAccountTimeline(id) {
|
||||||
api(getState).get(`/api/v1/accounts/${id}/statuses`).then(response => {
|
api(getState).get(`/api/v1/accounts/${id}/statuses`).then(response => {
|
||||||
dispatch(fetchAccountTimelineSuccess(id, response.data));
|
dispatch(fetchAccountTimelineSuccess(id, response.data));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error(error);
|
|
||||||
dispatch(fetchAccountTimelineFail(id, error));
|
dispatch(fetchAccountTimelineFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -76,7 +74,6 @@ export function expandAccountTimeline(id) {
|
||||||
api(getState).get(`/api/v1/accounts/${id}/statuses?max_id=${lastId}`).then(response => {
|
api(getState).get(`/api/v1/accounts/${id}/statuses?max_id=${lastId}`).then(response => {
|
||||||
dispatch(expandAccountTimelineSuccess(id, response.data));
|
dispatch(expandAccountTimelineSuccess(id, response.data));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error(error);
|
|
||||||
dispatch(expandAccountTimelineFail(id, error));
|
dispatch(expandAccountTimelineFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -112,7 +109,6 @@ export function followAccount(id) {
|
||||||
api(getState).post(`/api/v1/accounts/${id}/follow`).then(response => {
|
api(getState).post(`/api/v1/accounts/${id}/follow`).then(response => {
|
||||||
dispatch(followAccountSuccess(response.data));
|
dispatch(followAccountSuccess(response.data));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error(error);
|
|
||||||
dispatch(followAccountFail(error));
|
dispatch(followAccountFail(error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -125,7 +121,6 @@ export function unfollowAccount(id) {
|
||||||
api(getState).post(`/api/v1/accounts/${id}/unfollow`).then(response => {
|
api(getState).post(`/api/v1/accounts/${id}/unfollow`).then(response => {
|
||||||
dispatch(unfollowAccountSuccess(response.data));
|
dispatch(unfollowAccountSuccess(response.data));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error(error);
|
|
||||||
dispatch(unfollowAccountFail(error));
|
dispatch(unfollowAccountFail(error));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -226,7 +221,6 @@ export function blockAccount(id) {
|
||||||
api(getState).post(`/api/v1/accounts/${id}/block`).then(response => {
|
api(getState).post(`/api/v1/accounts/${id}/block`).then(response => {
|
||||||
dispatch(blockAccountSuccess(response.data));
|
dispatch(blockAccountSuccess(response.data));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error(error);
|
|
||||||
dispatch(blockAccountFail(id, error));
|
dispatch(blockAccountFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -239,7 +233,6 @@ export function unblockAccount(id) {
|
||||||
api(getState).post(`/api/v1/accounts/${id}/unblock`).then(response => {
|
api(getState).post(`/api/v1/accounts/${id}/unblock`).then(response => {
|
||||||
dispatch(unblockAccountSuccess(response.data));
|
dispatch(unblockAccountSuccess(response.data));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error(error);
|
|
||||||
dispatch(unblockAccountFail(id, error));
|
dispatch(unblockAccountFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -43,7 +43,6 @@ export function submitCompose() {
|
||||||
}).then(function (response) {
|
}).then(function (response) {
|
||||||
dispatch(submitComposeSuccess(response.data));
|
dispatch(submitComposeSuccess(response.data));
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
console.error(error);
|
|
||||||
dispatch(submitComposeFail(error));
|
dispatch(submitComposeFail(error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -83,7 +82,6 @@ export function uploadCompose(files) {
|
||||||
}).then(function (response) {
|
}).then(function (response) {
|
||||||
dispatch(uploadComposeSuccess(response.data));
|
dispatch(uploadComposeSuccess(response.data));
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
console.error(error);
|
|
||||||
dispatch(uploadComposeFail(error));
|
dispatch(uploadComposeFail(error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,7 +22,6 @@ export function submitFollow(router) {
|
||||||
dispatch(submitFollowSuccess(response.data));
|
dispatch(submitFollowSuccess(response.data));
|
||||||
router.push(`/accounts/${response.data.id}`);
|
router.push(`/accounts/${response.data.id}`);
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
console.error(error);
|
|
||||||
dispatch(submitFollowFail(error));
|
dispatch(submitFollowFail(error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -25,7 +25,6 @@ export function reblog(status) {
|
||||||
// interested in how the original is modified, hence passing it skipping the wrapper
|
// interested in how the original is modified, hence passing it skipping the wrapper
|
||||||
dispatch(reblogSuccess(status, response.data.reblog));
|
dispatch(reblogSuccess(status, response.data.reblog));
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
console.error(error);
|
|
||||||
dispatch(reblogFail(status, error));
|
dispatch(reblogFail(status, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -38,7 +37,6 @@ export function unreblog(status) {
|
||||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/unreblog`).then(response => {
|
api(getState).post(`/api/v1/statuses/${status.get('id')}/unreblog`).then(response => {
|
||||||
dispatch(unreblogSuccess(status, response.data));
|
dispatch(unreblogSuccess(status, response.data));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error(error);
|
|
||||||
dispatch(unreblogFail(status, error));
|
dispatch(unreblogFail(status, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -97,7 +95,6 @@ export function favourite(status) {
|
||||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/favourite`).then(function (response) {
|
api(getState).post(`/api/v1/statuses/${status.get('id')}/favourite`).then(function (response) {
|
||||||
dispatch(favouriteSuccess(status, response.data));
|
dispatch(favouriteSuccess(status, response.data));
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
console.error(error);
|
|
||||||
dispatch(favouriteFail(status, error));
|
dispatch(favouriteFail(status, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -110,7 +107,6 @@ export function unfavourite(status) {
|
||||||
api(getState).post(`/api/v1/statuses/${status.get('id')}/unfavourite`).then(response => {
|
api(getState).post(`/api/v1/statuses/${status.get('id')}/unfavourite`).then(response => {
|
||||||
dispatch(unfavouriteSuccess(status, response.data));
|
dispatch(unfavouriteSuccess(status, response.data));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error(error);
|
|
||||||
dispatch(unfavouriteFail(status, error));
|
dispatch(unfavouriteFail(status, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
export const NOTIFICATION_SHOW = 'NOTIFICATION_SHOW';
|
||||||
export const NOTIFICATION_DISMISS = 'NOTIFICATION_DISMISS';
|
export const NOTIFICATION_DISMISS = 'NOTIFICATION_DISMISS';
|
||||||
export const NOTIFICATION_CLEAR = 'NOTIFICATION_CLEAR';
|
export const NOTIFICATION_CLEAR = 'NOTIFICATION_CLEAR';
|
||||||
|
|
||||||
|
@ -13,3 +14,11 @@ export function clearNotifications() {
|
||||||
type: NOTIFICATION_CLEAR
|
type: NOTIFICATION_CLEAR
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function showNotification(title, message) {
|
||||||
|
return {
|
||||||
|
type: NOTIFICATION_SHOW,
|
||||||
|
title: title,
|
||||||
|
message: message
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
|
@ -25,7 +25,6 @@ export function fetchStatus(id) {
|
||||||
axios.all([boundApi.get(`/api/v1/statuses/${id}`), boundApi.get(`/api/v1/statuses/${id}/context`)]).then(values => {
|
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));
|
dispatch(fetchStatusSuccess(values[0].data, values[1].data));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error(error);
|
|
||||||
dispatch(fetchStatusFail(id, error));
|
dispatch(fetchStatusFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -54,7 +53,6 @@ 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));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error(error);
|
|
||||||
dispatch(deleteStatusFail(id, error));
|
dispatch(deleteStatusFail(id, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -11,7 +11,6 @@ export function fetchSuggestions() {
|
||||||
api(getState).get('/api/v1/accounts/suggestions').then(response => {
|
api(getState).get('/api/v1/accounts/suggestions').then(response => {
|
||||||
dispatch(fetchSuggestionsSuccess(response.data));
|
dispatch(fetchSuggestionsSuccess(response.data));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error(error);
|
|
||||||
dispatch(fetchSuggestionsFail(error));
|
dispatch(fetchSuggestionsFail(error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -48,7 +48,6 @@ export function refreshTimeline(timeline) {
|
||||||
api(getState).get(`/api/v1/statuses/${timeline}`).then(function (response) {
|
api(getState).get(`/api/v1/statuses/${timeline}`).then(function (response) {
|
||||||
dispatch(refreshTimelineSuccess(timeline, response.data));
|
dispatch(refreshTimelineSuccess(timeline, response.data));
|
||||||
}).catch(function (error) {
|
}).catch(function (error) {
|
||||||
console.error(error);
|
|
||||||
dispatch(refreshTimelineFail(timeline, error));
|
dispatch(refreshTimelineFail(timeline, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -71,7 +70,6 @@ export function expandTimeline(timeline) {
|
||||||
api(getState).get(`/api/v1/statuses/${timeline}?max_id=${lastId}`).then(response => {
|
api(getState).get(`/api/v1/statuses/${timeline}?max_id=${lastId}`).then(response => {
|
||||||
dispatch(expandTimelineSuccess(timeline, response.data));
|
dispatch(expandTimelineSuccess(timeline, response.data));
|
||||||
}).catch(error => {
|
}).catch(error => {
|
||||||
console.error(error);
|
|
||||||
dispatch(expandTimelineFail(timeline, error));
|
dispatch(expandTimelineFail(timeline, error));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
31
app/assets/javascripts/components/middleware/errors.jsx
Normal file
31
app/assets/javascripts/components/middleware/errors.jsx
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import { showNotification } from '../actions/notifications';
|
||||||
|
|
||||||
|
const defaultFailSuffix = 'FAIL';
|
||||||
|
|
||||||
|
export default function errorsMiddleware() {
|
||||||
|
return ({ dispatch }) => next => action => {
|
||||||
|
if (action.type) {
|
||||||
|
const isFail = new RegExp(`${defaultFailSuffix}$`, 'g');
|
||||||
|
|
||||||
|
if (action.type.match(isFail)) {
|
||||||
|
if (action.error.response) {
|
||||||
|
const { data, status, statusText } = action.error.response;
|
||||||
|
|
||||||
|
let message = statusText;
|
||||||
|
let title = `${status}`;
|
||||||
|
|
||||||
|
if (data.error) {
|
||||||
|
message = data.error;
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(showNotification(title, message));
|
||||||
|
} else {
|
||||||
|
console.error(action.error);
|
||||||
|
dispatch(showNotification('Oops!', 'An unexpected error occurred. Inspect the console for more details'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return next(action);
|
||||||
|
};
|
||||||
|
};
|
|
@ -1,68 +1,20 @@
|
||||||
import { COMPOSE_SUBMIT_FAIL, COMPOSE_UPLOAD_FAIL } from '../actions/compose';
|
|
||||||
import { FOLLOW_SUBMIT_FAIL } from '../actions/follow';
|
|
||||||
import {
|
import {
|
||||||
REBLOG_FAIL,
|
NOTIFICATION_SHOW,
|
||||||
UNREBLOG_FAIL,
|
NOTIFICATION_DISMISS,
|
||||||
FAVOURITE_FAIL,
|
NOTIFICATION_CLEAR
|
||||||
UNFAVOURITE_FAIL
|
} from '../actions/notifications';
|
||||||
} from '../actions/interactions';
|
|
||||||
import {
|
|
||||||
TIMELINE_REFRESH_FAIL,
|
|
||||||
TIMELINE_EXPAND_FAIL
|
|
||||||
} from '../actions/timelines';
|
|
||||||
import { NOTIFICATION_DISMISS, NOTIFICATION_CLEAR } from '../actions/notifications';
|
|
||||||
import {
|
|
||||||
ACCOUNT_FETCH_FAIL,
|
|
||||||
ACCOUNT_FOLLOW_FAIL,
|
|
||||||
ACCOUNT_UNFOLLOW_FAIL,
|
|
||||||
ACCOUNT_TIMELINE_FETCH_FAIL,
|
|
||||||
ACCOUNT_TIMELINE_EXPAND_FAIL
|
|
||||||
} from '../actions/accounts';
|
|
||||||
import {
|
|
||||||
STATUS_FETCH_FAIL,
|
|
||||||
STATUS_DELETE_FAIL
|
|
||||||
} from '../actions/statuses';
|
|
||||||
import Immutable from 'immutable';
|
import Immutable from 'immutable';
|
||||||
|
|
||||||
const initialState = Immutable.List();
|
const initialState = Immutable.List([]);
|
||||||
|
|
||||||
function notificationFromError(state, error) {
|
|
||||||
let n = Immutable.Map({
|
|
||||||
key: state.size > 0 ? state.last().get('key') + 1 : 0,
|
|
||||||
message: ''
|
|
||||||
});
|
|
||||||
|
|
||||||
if (error.response) {
|
|
||||||
n = n.withMutations(map => {
|
|
||||||
map.set('message', error.response.statusText);
|
|
||||||
map.set('title', `${error.response.status}`);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
n = n.set('message', `${error}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return state.push(n);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function notifications(state = initialState, action) {
|
export default function notifications(state = initialState, action) {
|
||||||
switch(action.type) {
|
switch(action.type) {
|
||||||
case COMPOSE_SUBMIT_FAIL:
|
case NOTIFICATION_SHOW:
|
||||||
case COMPOSE_UPLOAD_FAIL:
|
return state.push(Immutable.Map({
|
||||||
case FOLLOW_SUBMIT_FAIL:
|
key: state.size > 0 ? state.last().get('key') + 1 : 0,
|
||||||
case REBLOG_FAIL:
|
title: action.title,
|
||||||
case FAVOURITE_FAIL:
|
message: action.message
|
||||||
case TIMELINE_REFRESH_FAIL:
|
}));
|
||||||
case TIMELINE_EXPAND_FAIL:
|
|
||||||
case ACCOUNT_FETCH_FAIL:
|
|
||||||
case ACCOUNT_FOLLOW_FAIL:
|
|
||||||
case ACCOUNT_UNFOLLOW_FAIL:
|
|
||||||
case ACCOUNT_TIMELINE_FETCH_FAIL:
|
|
||||||
case ACCOUNT_TIMELINE_EXPAND_FAIL:
|
|
||||||
case STATUS_FETCH_FAIL:
|
|
||||||
case STATUS_DELETE_FAIL:
|
|
||||||
case UNREBLOG_FAIL:
|
|
||||||
case UNFAVOURITE_FAIL:
|
|
||||||
return notificationFromError(state, action.error);
|
|
||||||
case NOTIFICATION_DISMISS:
|
case NOTIFICATION_DISMISS:
|
||||||
return state.filterNot(item => item.get('key') === action.notification.key);
|
return state.filterNot(item => item.get('key') === action.notification.key);
|
||||||
case NOTIFICATION_CLEAR:
|
case NOTIFICATION_CLEAR:
|
||||||
|
|
|
@ -2,9 +2,10 @@ import { createStore, applyMiddleware, compose } from 'redux';
|
||||||
import thunk from 'redux-thunk';
|
import thunk from 'redux-thunk';
|
||||||
import appReducer from '../reducers';
|
import appReducer from '../reducers';
|
||||||
import { loadingBarMiddleware } from 'react-redux-loading-bar';
|
import { loadingBarMiddleware } from 'react-redux-loading-bar';
|
||||||
|
import errorsMiddleware from '../middleware/errors';
|
||||||
|
|
||||||
export default function configureStore(initialState) {
|
export default function configureStore(initialState) {
|
||||||
return createStore(appReducer, initialState, compose(applyMiddleware(thunk, loadingBarMiddleware({
|
return createStore(appReducer, initialState, compose(applyMiddleware(thunk, loadingBarMiddleware({
|
||||||
promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'],
|
promiseTypeSuffixes: ['REQUEST', 'SUCCESS', 'FAIL'],
|
||||||
})), window.devToolsExtension ? window.devToolsExtension() : f => f));
|
}), errorsMiddleware()), window.devToolsExtension ? window.devToolsExtension() : f => f));
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue