mattermost-mobile/service/actions/posts.js
Chris Duarte e17eb6c237 PLT-5709 Global error handler (#187)
* WIP Global error handler

Adding components
Adding actions

* Add general errors where errors are caught

* Filter out non displayable errors

* Initial setup to email errors

* Add current user/team to error email

* Fix a few lints

* Ensure errors are cleared when reporting a problem

* Add clear all and internationalization

* Fix trailing comma issue

* fix lints

* Use Formatted Text component

* Move error list out of channel

* Add mobile prefix to i18n id for connection error

* Set shouldRender to true in RightMenuDrawerItem.defaultProps

* Update component name for RightMenuDrawerItem

* Clean up propTypes in RightMenuDrawerItem

* Clean up styles in RightMenuDrawerItem

* Use selector to filter displayable errors

* Clarify code for constructing wrapper style in ErrorList

* Avoid indenting email body for error report

* Remove extraneous params from errors actions

* Rename action type for logging errors

* Rename action for logging errors

* Set shouldRender to true in RightMenuDrawerItem.defaultProps

* Make logged errors displayable by default

* Don't log error when websocket connection is closed

* Hide button to dismiss all errors if ShowErrorsList isn't enabled

* Display only last error in alert bar when ShowErrorsList is disabled

* Add error logging where missing in actions

* Always show button to report problem

* Only include errors in report problem email if any have been logged

* Clarify subject of report problem email

* Set ShowErrorsList to false in default config.json

* Remove unused component + translation for connection errors

* Capture entire error object not just message

* Display fallback if error.message is blank

* Log errors in login actions

* Fix construction of errors email body
2017-03-12 14:42:02 -03:00

275 lines
7.7 KiB
JavaScript

// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import Client from 'service/client';
import {batchActions} from 'redux-batched-actions';
import {bindClientFunc, forceLogoutIfNecessary} from './helpers';
import {Constants, PostsTypes} from 'service/constants';
import {getLogErrorAction} from './errors';
import {getProfilesByIds, getStatusesByIds} from './users';
async function getProfilesAndStatusesForPosts(list, dispatch, getState) {
const {profiles, statuses} = getState().entities.users;
const posts = list.posts;
const profilesToLoad = [];
const statusesToLoad = [];
Object.keys(posts).forEach((key) => {
const post = posts[key];
const userId = post.user_id;
if (!profiles[userId]) {
profilesToLoad.push(userId);
}
if (!statuses[userId]) {
statusesToLoad.push(userId);
}
});
if (profilesToLoad.length) {
await getProfilesByIds(profilesToLoad)(dispatch, getState);
}
if (statusesToLoad.length) {
await getStatusesByIds(statusesToLoad)(dispatch, getState);
}
}
export function createPost(teamId, post) {
return bindClientFunc(
Client.createPost,
PostsTypes.CREATE_POST_REQUEST,
[PostsTypes.RECEIVED_POST, PostsTypes.CREATE_POST_SUCCESS],
PostsTypes.CREATE_POST_FAILURE,
teamId,
post
);
}
export function editPost(teamId, post) {
return bindClientFunc(
Client.editPost,
PostsTypes.EDIT_POST_REQUEST,
[PostsTypes.RECEIVED_POST, PostsTypes.EDIT_POST_SUCCESS],
PostsTypes.EDIT_POST_FAILURE,
teamId,
post
);
}
export function deletePost(teamId, post) {
return async (dispatch, getState) => {
dispatch({type: PostsTypes.DELETE_POST_REQUEST}, getState);
try {
await Client.deletePost(teamId, post.channel_id, post.id);
} catch (error) {
forceLogoutIfNecessary(error, dispatch);
dispatch(batchActions([
{type: PostsTypes.DELETE_POST_FAILURE, error},
getLogErrorAction(error)
]), getState);
return;
}
dispatch(batchActions([
{
type: PostsTypes.POST_DELETED,
data: {...post}
},
{
type: PostsTypes.DELETE_POST_SUCCESS
}
]), getState);
};
}
export function removePost(post) {
return async (dispatch, getState) => {
dispatch({
type: PostsTypes.REMOVE_POST,
data: {...post}
}, getState);
};
}
export function getPost(teamId, channelId, postId) {
return async (dispatch, getState) => {
dispatch({type: PostsTypes.GET_POST_REQUEST}, getState);
let post;
try {
post = await Client.getPost(teamId, channelId, postId);
getProfilesAndStatusesForPosts(post, dispatch, getState);
} catch (error) {
forceLogoutIfNecessary(error, dispatch);
dispatch(batchActions([
{type: PostsTypes.GET_POST_FAILURE, error},
getLogErrorAction(error)
]), getState);
return;
}
dispatch(batchActions([
{
type: PostsTypes.RECEIVED_POSTS,
data: {...post},
channelId
},
{
type: PostsTypes.GET_POST_SUCCESS
}
]), getState);
};
}
export function getPosts(teamId, channelId, offset = 0, limit = Constants.POST_CHUNK_SIZE) {
return async (dispatch, getState) => {
dispatch({type: PostsTypes.GET_POSTS_REQUEST}, getState);
let posts;
try {
posts = await Client.getPosts(teamId, channelId, offset, limit);
getProfilesAndStatusesForPosts(posts, dispatch, getState);
} catch (error) {
forceLogoutIfNecessary(error, dispatch);
dispatch(batchActions([
{type: PostsTypes.GET_POSTS_FAILURE, error},
getLogErrorAction(error)
]), getState);
return null;
}
dispatch(batchActions([
{
type: PostsTypes.RECEIVED_POSTS,
data: posts,
channelId
},
{
type: PostsTypes.GET_POSTS_SUCCESS
}
]), getState);
return posts;
};
}
export function getPostsSince(teamId, channelId, since) {
return async (dispatch, getState) => {
dispatch({type: PostsTypes.GET_POSTS_SINCE_REQUEST}, getState);
let posts;
try {
posts = await Client.getPostsSince(teamId, channelId, since);
getProfilesAndStatusesForPosts(posts, dispatch, getState);
} catch (error) {
forceLogoutIfNecessary(error, dispatch);
dispatch(batchActions([
{type: PostsTypes.GET_POSTS_SINCE_FAILURE, error},
getLogErrorAction(error)
]), getState);
return null;
}
dispatch(batchActions([
{
type: PostsTypes.RECEIVED_POSTS,
data: posts,
channelId
},
{
type: PostsTypes.GET_POSTS_SINCE_SUCCESS
}
]), getState);
return posts;
};
}
export function getPostsBefore(teamId, channelId, postId, offset = 0, limit = Constants.POST_CHUNK_SIZE) {
return async (dispatch, getState) => {
dispatch({type: PostsTypes.GET_POSTS_BEFORE_REQUEST}, getState);
let posts;
try {
posts = await Client.getPostsBefore(teamId, channelId, postId, offset, limit);
getProfilesAndStatusesForPosts(posts, dispatch, getState);
} catch (error) {
forceLogoutIfNecessary(error, dispatch);
dispatch(batchActions([
{type: PostsTypes.GET_POSTS_BEFORE_FAILURE, error},
getLogErrorAction(error)
]), getState);
return null;
}
dispatch(batchActions([
{
type: PostsTypes.RECEIVED_POSTS,
data: posts,
channelId
},
{
type: PostsTypes.GET_POSTS_BEFORE_SUCCESS
}
]), getState);
return posts;
};
}
export function getPostsAfter(teamId, channelId, postId, offset = 0, limit = Constants.POST_CHUNK_SIZE) {
return async (dispatch, getState) => {
dispatch({type: PostsTypes.GET_POSTS_AFTER_REQUEST}, getState);
let posts;
try {
posts = await Client.getPostsAfter(teamId, channelId, postId, offset, limit);
getProfilesAndStatusesForPosts(posts, dispatch, getState);
} catch (error) {
forceLogoutIfNecessary(error, dispatch);
dispatch(batchActions([
{type: PostsTypes.GET_POSTS_AFTER_FAILURE, error},
getLogErrorAction(error)
]), getState);
return null;
}
dispatch(batchActions([
{
type: PostsTypes.RECEIVED_POSTS,
data: posts,
channelId
},
{
type: PostsTypes.GET_POSTS_AFTER_SUCCESS
}
]), getState);
return posts;
};
}
export function selectPost(postId) {
return async (dispatch, getState) => {
dispatch({
type: PostsTypes.RECEIVED_POST_SELECTED,
data: postId
}, getState);
};
}
export default {
createPost,
editPost,
deletePost,
removePost,
getPost,
getPosts,
getPostsSince,
getPostsBefore,
getPostsAfter,
selectPost
};