Merge pull request #1306 from coralproject/error-notifications

Error handling improvements
This commit is contained in:
Wyatt Johnson
2018-01-24 12:34:11 -07:00
committed by GitHub
35 changed files with 230 additions and 317 deletions
+22 -30
View File
@@ -2,7 +2,6 @@ import React from 'react';
import cn from 'classnames';
import PropTypes from 'prop-types';
import capitalize from 'lodash/capitalize';
import { getErrorMessages } from 'coral-framework/utils';
import styles from './UserDetail.css';
import AccountHistory from './AccountHistory';
import { Slot } from 'coral-framework/components';
@@ -29,43 +28,23 @@ import UserInfoTooltip from './UserInfoTooltip';
class UserDetail extends React.Component {
rejectThenReload = async info => {
try {
await this.props.rejectComment(info);
this.props.data.refetch();
} catch (err) {
console.error(err);
this.props.notify('error', getErrorMessages(err));
}
await this.props.rejectComment(info);
this.props.data.refetch();
};
acceptThenReload = async info => {
try {
await this.props.acceptComment(info);
this.props.data.refetch();
} catch (err) {
console.error(err);
this.props.notify('error', getErrorMessages(err));
}
await this.props.acceptComment(info);
this.props.data.refetch();
};
bulkAcceptThenReload = async () => {
try {
await this.props.bulkAccept();
this.props.data.refetch();
} catch (err) {
console.error(err);
this.props.notify('error', getErrorMessages(err));
}
await this.props.bulkAccept();
this.props.data.refetch();
};
bulkRejectThenReload = async () => {
try {
await this.props.bulkReject();
this.props.data.refetch();
} catch (err) {
console.error(err);
this.props.notify('error', getErrorMessages(err));
}
await this.props.bulkReject();
this.props.data.refetch();
};
changeTab = tab => {
@@ -94,6 +73,16 @@ class UserDetail extends React.Component {
);
}
renderError() {
return (
<ClickOutside onClickOutside={this.props.hideUserDetail}>
<Drawer onClose={this.props.hideUserDetail}>
<div>{this.props.data.error.message}</div>
</Drawer>
</ClickOutside>
);
}
getActionMenuLabel() {
const { root: { user } } = this.props;
@@ -345,6 +334,10 @@ class UserDetail extends React.Component {
}
render() {
if (this.props.data.error) {
return this.renderError();
}
if (this.props.loading) {
return this.renderLoading();
}
@@ -371,7 +364,6 @@ UserDetail.propTypes = {
selectedCommentIds: PropTypes.array.isRequired,
viewUserDetail: PropTypes.any.isRequired,
loadMore: PropTypes.any.isRequired,
notify: PropTypes.func.isRequired,
showSuspendUserDialog: PropTypes.func,
showBanUserDialog: PropTypes.func,
unbanUser: PropTypes.func.isRequired,
@@ -10,8 +10,6 @@ import {
} from 'coral-framework/graphql/mutations';
import { compose } from 'react-apollo';
import t from 'coral-framework/services/i18n';
import { getErrorMessages } from 'coral-framework/utils';
import { notify } from 'coral-framework/actions/notification';
class BanUserDialogContainer extends Component {
banUser = async () => {
@@ -22,16 +20,11 @@ class BanUserDialogContainer extends Component {
banUser,
setCommentStatus,
hideBanUserDialog,
notify,
} = this.props;
try {
await banUser({ id: userId, message: '' });
hideBanUserDialog();
if (commentId && commentStatus && commentStatus !== 'REJECTED') {
await setCommentStatus({ commentId, status: 'REJECTED' });
}
} catch (err) {
notify('error', getErrorMessages(err));
await banUser({ id: userId, message: '' });
hideBanUserDialog();
if (commentId && commentStatus && commentStatus !== 'REJECTED') {
await setCommentStatus({ commentId, status: 'REJECTED' });
}
};
@@ -78,14 +71,13 @@ const mapDispatchToProps = dispatch => ({
...bindActionCreators(
{
hideBanUserDialog,
notify,
},
dispatch
),
});
export default compose(
connect(mapStateToProps, mapDispatchToProps),
withBanUser,
withSetCommentStatus,
connect(mapStateToProps, mapDispatchToProps)
withSetCommentStatus
)(BanUserDialogContainer);
@@ -11,7 +11,6 @@ import {
import { compose, gql } from 'react-apollo';
import t, { timeago } from 'coral-framework/services/i18n';
import withQuery from 'coral-framework/hocs/withQuery';
import { getErrorMessages } from 'coral-framework/utils';
import get from 'lodash/get';
import { notify } from 'coral-framework/actions/notification';
@@ -28,17 +27,13 @@ class SuspendUserDialogContainer extends Component {
notify,
} = this.props;
hideSuspendUserDialog();
try {
await suspendUser({ id: userId, message, until });
notify(
'success',
t('suspenduser.notify_suspend_until', username, timeago(until))
);
if (commentId && commentStatus && commentStatus !== 'REJECTED') {
await setCommentStatus({ commentId, status: 'REJECTED' });
}
} catch (err) {
notify('error', getErrorMessages(err));
await suspendUser({ id: userId, message, until });
notify(
'success',
t('suspenduser.notify_suspend_until', username, timeago(until))
);
if (commentId && commentStatus && commentStatus !== 'REJECTED') {
await setCommentStatus({ commentId, status: 'REJECTED' });
}
};
@@ -24,7 +24,6 @@ import {
} from 'coral-framework/graphql/mutations';
import UserDetailComment from './UserDetailComment';
import update from 'immutability-helper';
import { notify } from 'coral-framework/actions/notification';
import { showBanUserDialog } from 'actions/banUserDialog';
import { showSuspendUserDialog } from 'actions/suspendUserDialog';
@@ -130,6 +129,7 @@ class UserDetailContainer extends React.Component {
acceptComment={this.acceptComment}
rejectComment={this.rejectComment}
loading={loading}
error={this.props.data && this.props.data.error}
loadMore={this.loadMore}
{...this.props}
/>
@@ -271,7 +271,6 @@ const mapDispatchToProps = dispatch => ({
viewUserDetail,
hideUserDetail,
toggleSelectAllCommentInUserDetail,
notify,
},
dispatch
),
@@ -15,7 +15,6 @@ import { handleFlaggedUsernameChange } from '../graphql';
import { notify } from 'coral-framework/actions/notification';
import { isFlaggedUserDangling } from '../utils';
import t from 'coral-framework/services/i18n';
import { notifyOnMutationError } from 'coral-framework/hocs';
import FlaggedAccounts from '../components/FlaggedAccounts';
import FlaggedUser from '../containers/FlaggedUser';
@@ -295,7 +294,6 @@ const mapDispatchToProps = dispatch =>
export default compose(
connect(null, mapDispatchToProps),
withApproveUsername,
notifyOnMutationError(['approveUsername']),
withQuery(
gql`
query TalkAdmin_Community_FlaggedAccounts {
@@ -4,8 +4,6 @@ import { hideRejectUsernameDialog } from '../../../actions/community';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { compose } from 'react-apollo';
import { notify } from 'coral-framework/actions/notification';
import { notifyOnMutationError } from 'coral-framework/hocs';
const mapStateToProps = state => ({
user: state.community.user,
@@ -16,13 +14,11 @@ const mapDispatchToProps = dispatch =>
bindActionCreators(
{
handleClose: hideRejectUsernameDialog,
notify,
},
dispatch
);
export default compose(
connect(mapStateToProps, mapDispatchToProps),
withRejectUsername,
notifyOnMutationError(['rejectUsername'])
withRejectUsername
)(RejectUsernameDialog);
@@ -4,10 +4,9 @@ import { bindActionCreators } from 'redux';
import { compose, gql } from 'react-apollo';
import { withQuery, withMergedSettings } from 'coral-framework/hocs';
import { Spinner } from 'coral-ui';
import { notify } from 'coral-framework/actions/notification';
import PropTypes from 'prop-types';
import { withUpdateSettings } from 'coral-framework/graphql/mutations';
import { getErrorMessages, getDefinitionName } from 'coral-framework/utils';
import { getDefinitionName } from 'coral-framework/utils';
import StreamSettings from './StreamSettings';
import TechSettings from './TechSettings';
import ModerationSettings from './ModerationSettings';
@@ -16,22 +15,21 @@ import Configure from '../components/Configure';
class ConfigureContainer extends Component {
savePending = async () => {
try {
await this.props.updateSettings(this.props.pending);
this.props.clearPending();
} catch (err) {
this.props.notify('error', getErrorMessages(err));
}
await this.props.updateSettings(this.props.pending);
this.props.clearPending();
};
render() {
if (this.props.data.error) {
return <div>{this.props.data.error.message}</div>;
}
if (this.props.data.loading) {
return <Spinner />;
}
return (
<Configure
notify={this.props.notify}
auth={this.props.auth}
data={this.props.data}
root={this.props.root}
@@ -67,6 +65,7 @@ const withConfigureQuery = withQuery(
{
options: () => ({
variables: {},
fetchPolicy: 'network-only',
}),
}
);
@@ -81,7 +80,6 @@ const mapStateToProps = state => ({
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
notify,
clearPending,
setActiveSection,
},
@@ -89,9 +87,9 @@ const mapDispatchToProps = dispatch =>
);
export default compose(
connect(mapStateToProps, mapDispatchToProps),
withUpdateSettings,
withConfigureQuery,
connect(mapStateToProps, mapDispatchToProps),
withMergedSettings('root.settings', 'pending', 'mergedSettings')
)(ConfigureContainer);
@@ -99,7 +97,6 @@ ConfigureContainer.propTypes = {
updateSettings: PropTypes.func.isRequired,
clearPending: PropTypes.func.isRequired,
setActiveSection: PropTypes.func.isRequired,
notify: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
data: PropTypes.object.isRequired,
root: PropTypes.object.isRequired,
@@ -269,10 +269,6 @@ class ModerationContainer extends Component {
const { root, root: { asset, settings }, data } = this.props;
const assetId = getAssetId(this.props);
if (data.error) {
return <div>Error</div>;
}
if (assetId) {
if (asset === null) {
// Not found.
@@ -280,6 +276,10 @@ class ModerationContainer extends Component {
}
}
if (data.error) {
return <div>{data.error.message}</div>;
}
if (data.loading && data.networkStatus !== 3) {
// loading.
return <Spinner />;
@@ -9,6 +9,10 @@ import { getDefinitionName } from 'coral-framework/utils';
class ConfigureContainer extends React.Component {
render() {
if (this.props.data.error) {
return <div>{this.props.data.error.message}</div>;
}
return (
<Configure
data={this.props.data}
@@ -1,16 +1,12 @@
import React from 'react';
import { gql, compose } from 'react-apollo';
import { withFragments, withMergedSettings } from 'coral-framework/hocs';
import {
getErrorMessages,
getSlotFragmentSpreads,
} from 'coral-framework/utils';
import { getSlotFragmentSpreads } from 'coral-framework/utils';
import Settings from '../components/Settings.js';
import PropTypes from 'prop-types';
import { withUpdateAssetSettings } from 'coral-framework/graphql/mutations';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { notify } from 'coral-framework/actions/notification';
import { clearPending, updatePending } from '../../../actions/configure';
const slots = ['streamSettings'];
@@ -50,15 +46,11 @@ class SettingsContainer extends React.Component {
};
savePending = async () => {
try {
await this.props.updateAssetSettings(
this.props.asset.id,
this.props.pending
);
this.props.clearPending();
} catch (err) {
this.props.notify('error', getErrorMessages(err));
}
await this.props.updateAssetSettings(
this.props.asset.id,
this.props.pending
);
this.props.clearPending();
};
render() {
@@ -98,7 +90,6 @@ SettingsContainer.propTypes = {
mergedSettings: PropTypes.object.isRequired,
updateAssetSettings: PropTypes.func.isRequired,
clearPending: PropTypes.func.isRequired,
notify: PropTypes.func.isRequired,
updatePending: PropTypes.func.isRequired,
canSave: PropTypes.bool.isRequired,
};
@@ -135,7 +126,6 @@ const mapStateToProps = state => ({
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
notify,
clearPending,
updatePending,
},
@@ -143,9 +133,9 @@ const mapDispatchToProps = dispatch =>
);
const enhance = compose(
connect(mapStateToProps, mapDispatchToProps),
withSettingsFragments,
withUpdateAssetSettings,
connect(mapStateToProps, mapDispatchToProps),
withMergedSettings('asset.settings', 'pending', 'mergedSettings')
);
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import LoadMore from './LoadMore';
import NewCount from './NewCount';
import { TransitionGroup } from 'react-transition-group';
import { forEachError } from 'coral-framework/utils';
import Comment from '../containers/Comment';
import NoComments from './NoComments';
@@ -91,11 +90,8 @@ class AllCommentsPane extends React.Component {
.then(() => {
this.setState({ loadingState: 'success' });
})
.catch(error => {
.catch(() => {
this.setState({ loadingState: 'error' });
forEachError(error, ({ msg }) => {
this.props.notify('error', msg);
});
});
};
@@ -24,7 +24,6 @@ import { EditableCommentContent } from './EditableCommentContent';
import {
getActionSummary,
iPerformedThisAction,
forEachError,
isCommentActive,
getShallowChanges,
} from 'coral-framework/utils';
@@ -261,11 +260,8 @@ export default class Comment extends React.Component {
loadingState: 'success',
});
})
.catch(error => {
.catch(() => {
this.setState({ loadingState: 'error' });
forEachError(error, ({ msg }) => {
this.props.notify('error', msg);
});
});
emit('ui.Comment.showMoreReplies', { id });
return;
@@ -6,7 +6,6 @@ import styles from './Comment.css';
import { CountdownSeconds } from './CountdownSeconds';
import { getEditableUntilDate } from './util';
import { can } from 'coral-framework/services/perms';
import { forEachError } from 'coral-framework/utils';
import { Icon } from 'coral-ui';
import t from 'coral-framework/services/i18n';
@@ -80,7 +79,7 @@ export class EditableCommentContent extends React.Component {
this.setState({ loadingState: 'loading' });
const { editComment, notify, stopEditing } = this.props;
const { editComment, stopEditing } = this.props;
if (typeof editComment !== 'function') {
return;
}
@@ -95,7 +94,6 @@ export class EditableCommentContent extends React.Component {
}
} catch (error) {
this.setState({ loadingState: 'error' });
forEachError(error, ({ msg }) => notify('error', msg));
}
};
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import { StreamError } from './StreamError';
import StreamError from './StreamError';
import Comment from '../containers/Comment';
import BannedAccount from '../../../components/BannedAccount';
import ChangeUsername from '../containers/ChangeUsername';
@@ -1,6 +1,6 @@
import React from 'react';
import styles from './StreamError.css';
export const StreamError = ({ children }) => (
export default ({ children }) => (
<div className={styles.streamError}>{children}</div>
);
@@ -38,6 +38,7 @@ import {
insertFetchedCommentsIntoEmbedQuery,
nest,
} from '../../../graphql/utils';
import StreamError from '../components/StreamError';
const { showSignInDialog, editName } = authActions;
const { notify } = notificationActions;
@@ -208,6 +209,10 @@ class StreamContainer extends React.Component {
}
render() {
if (this.props.data.error) {
return <StreamError>{this.props.data.error.message}</StreamError>;
}
if (
!this.props.asset ||
(this.props.asset.comment === undefined && !this.props.asset.comments)
@@ -424,7 +429,8 @@ export default compose(
withEmit,
connect(mapStateToProps, mapDispatchToProps),
withPostComment,
withPostFlag,
// `talk-plugin-flags` has a custom error handling logic.
withPostFlag({ notifyOnError: false }),
withPostDontAgree,
withDeleteAction,
withEditComment
-1
View File
@@ -6,4 +6,3 @@ export { default as withEmit } from './withEmit';
export { default as excludeIf } from './excludeIf';
export { default as connect } from './connect';
export { default as withMergedSettings } from './withMergedSettings';
export { default as notifyOnMutationError } from './notifyOnMutationError';
@@ -1,35 +0,0 @@
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { compose } from 'react-apollo';
import { notify } from 'coral-framework/actions/notification';
import { forEachError } from 'coral-framework/utils';
import { withProps } from 'recompose';
const notifyOnMutationError = keys =>
compose(
connect(null, dispatch =>
bindActionCreators(
{
notify,
},
dispatch
)
),
withProps(ownProps =>
keys.reduce((props, key) => {
props[key] = async (...args) => {
try {
return await ownProps[key](...args);
} catch (e) {
forEachError(e, ({ msg }) => {
ownProps.notify('error', msg);
});
throw e;
}
};
return props;
}, {})
)
);
export default notifyOnMutationError;
+35 -6
View File
@@ -4,11 +4,16 @@ import merge from 'lodash/merge';
import uniq from 'lodash/uniq';
import flatten from 'lodash/flatten';
import isEmpty from 'lodash/isEmpty';
import { getDefinitionName, getResponseErrors } from '../utils';
import {
getDefinitionName,
getResponseErrors,
getErrorMessages,
} from '../utils';
import PropTypes from 'prop-types';
import t from 'coral-framework/services/i18n';
import hoistStatics from 'recompose/hoistStatics';
import union from 'lodash/union';
import { notify } from 'coral-framework/actions/notification';
class ResponseErrors extends Error {
constructor(errors) {
@@ -27,11 +32,7 @@ class ResponseError {
}
}
/**
* Exports a HOC with the same signature as `graphql`, that will
* apply mutation options registered in the graphRegistry.
*/
export default (document, config = {}) =>
const createHOC = (document, config, { notifyOnError = true }) =>
hoistStatics(WrappedComponent => {
config = {
...config,
@@ -46,10 +47,18 @@ export default (document, config = {}) =>
graphql: PropTypes.object,
};
static propTypes = {
notify: PropTypes.func,
};
get graphqlRegistry() {
return this.context.graphql.registry;
}
notifyErrors(messages) {
this.context.store.dispatch(notify('error', messages));
}
resolveDocument(documentOrCallback) {
return this.context.graphql.resolveDocument(
documentOrCallback,
@@ -165,6 +174,11 @@ export default (document, config = {}) =>
variables,
error,
});
// Show errors as notifications.
if (notifyOnError) {
this.notifyErrors(getErrorMessages(error));
}
throw error;
});
};
@@ -213,3 +227,18 @@ export default (document, config = {}) =>
}
};
});
/**
* Exports a HOC with the same signature as `graphql`, that will
* apply mutation options registered in the graphRegistry.
*
* The returned HOC accepts a settings object with the following properties:
* notifyOnError: show a notification to the user when an error occured.
* Defaults to true.
*/
export default (document, config = {}) => settingsOrComponent => {
if (typeof settingsOrComponent === 'function') {
return createHOC(document, config, {})(settingsOrComponent);
}
return createHOC(document, config, settingsOrComponent);
};
+39 -6
View File
@@ -9,6 +9,8 @@ import PropTypes from 'prop-types';
import hoistStatics from 'recompose/hoistStatics';
import { getOperationName } from 'apollo-client/queries/getFromAST';
import throttle from 'lodash/throttle';
import get from 'lodash/get';
import { notify } from 'coral-framework/actions/notification';
const withSkipOnErrors = reducer => (prev, action, ...rest) => {
if (
@@ -36,21 +38,23 @@ function networkStatusToString(networkStatus) {
return 'ready';
case 8:
return 'error';
default:
throw new Error(`Unknown network status ${networkStatus}`);
}
throw new Error(`Unknown network status ${networkStatus}`);
}
/**
* Exports a HOC with the same signature as `graphql`, that will
* apply query options registered in the graphRegistry.
*/
export default (document, config = {}) =>
const createHOC = (document, config, { notifyOnError = true }) =>
hoistStatics(WrappedComponent => {
return class WithQuery extends React.Component {
static contextTypes = {
eventEmitter: PropTypes.object,
graphql: PropTypes.object,
client: PropTypes.object,
store: PropTypes.object,
};
static propTypes = {
notify: PropTypes.func,
};
// Lazily resolve fragments from graphRegistry to support circular dependencies.
@@ -166,10 +170,24 @@ export default (document, config = {}) =>
return () => this.client.networkInterface.unsubscribe(id);
};
notifyErrors(messages) {
this.context.store.dispatch(notify('error', messages));
}
nextData(data) {
this.apolloData = data;
this.emitWhenNeeded(data);
if (
get(data, 'error.message') &&
get(this, 'data.error.message') !== get(data, 'error.message')
) {
// Show errors as notifications.
if (notifyOnError) {
this.notifyErrors(data.error.message);
}
}
// If data was previously set, we update it in a immutable way.
if (this.data) {
if (this.data.loading && !data.loading) {
@@ -319,3 +337,18 @@ export default (document, config = {}) =>
}
};
});
/**
* Exports a HOC with the same signature as `graphql`, that will
* apply query options registered in the graphRegistry.
*
* The returned HOC accepts a settings object with the following properties:
* notifyOnError: show a notification to the user when an error occured.
* Defaults to true.
*/
export default (document, config = {}) => settingsOrComponent => {
if (typeof settingsOrComponent === 'function') {
return createHOC(document, config, {})(settingsOrComponent);
}
return createHOC(document, config, settingsOrComponent);
};
@@ -65,6 +65,10 @@ class ProfileContainer extends Component {
const { me } = this.props.root;
const loading = this.props.data.loading;
if (this.props.data.error) {
return <div>{this.props.data.error.message}</div>;
}
if (!auth.loggedIn) {
return <NotLoggedIn showSignInDialog={showSignInDialog} />;
}
+1 -3
View File
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
import t from 'coral-framework/services/i18n';
import { can } from 'coral-framework/services/perms';
import { forEachError } from 'coral-framework/utils';
import Slot from 'coral-framework/components/Slot';
import { connect } from 'react-redux';
@@ -93,9 +92,8 @@ class CommentBox extends React.Component {
commentPostedHandler();
}
})
.catch(err => {
.catch(() => {
this.setState({ loadingState: 'error' });
forEachError(err, ({ msg }) => notify('error', msg));
});
};
@@ -60,9 +60,10 @@ export default class FlagButton extends Component {
});
};
onPopupContinue = () => {
onPopupContinue = async () => {
const { postFlag, postDontAgree, id, author_id } = this.props;
const { itemType, reason, step, message } = this.state;
let failed = false;
switch (step) {
case 0:
@@ -75,13 +76,9 @@ export default class FlagButton extends Component {
return;
}
break;
}
// Proceed to the next step or close the menu if we've reached the end
if (step + 1 >= this.props.getPopupMenu.length) {
this.closeMenu();
} else {
this.setState({ step: step + 1 });
case this.props.getPopupMenu.length:
this.closeMenu();
return;
}
// If itemType and reason are both set, post the action
@@ -96,43 +93,46 @@ export default class FlagButton extends Component {
break;
}
if (itemType === 'COMMENTS') {
this.setState({ localPost: 'temp' });
}
let action = {
item_id,
item_type: itemType,
message,
};
if (reason === REASONS.comment.noagree) {
postDontAgree(action)
.then(({ data }) => {
if (itemType === 'COMMENTS') {
this.setState({ localPost: data.createDontAgree.dontagree.id });
}
})
.catch(err => {
this.props.notify('error', getErrorMessages(err));
console.error(err);
});
} else {
postFlag({ ...action, reason })
.then(({ data }) => {
if (itemType === 'COMMENTS') {
this.setState({ localPost: data.createFlag.flag.id });
}
})
.catch(errors => {
forEachError(errors, ({ error, msg }) => {
if (error.translation_key === 'ALREADY_EXISTS') {
msg = t('already_flagged_username');
}
this.props.notify('error', msg);
const result = await postDontAgree(action);
try {
if (itemType === 'COMMENTS') {
this.setState({
localPost: result.data.createDontAgree.dontagree.id,
});
}
} catch (err) {
this.props.notify('error', getErrorMessages(err));
console.error(err);
failed = true;
}
} else {
try {
const result = await postFlag({ ...action, reason });
if (itemType === 'COMMENTS') {
this.setState({ localPost: result.data.createFlag.flag.id });
}
} catch (errors) {
forEachError(errors, ({ error, msg }) => {
if (error.translation_key === 'ALREADY_EXISTS') {
msg = t('already_flagged_username');
}
this.props.notify('error', msg);
});
failed = true;
}
}
}
if (!failed) {
this.setState({ step: step + 1 });
}
};
onPopupOptionClick = sets => e => {
+1 -6
View File
@@ -2,7 +2,6 @@ import React from 'react';
import PropTypes from 'prop-types';
import Comment from './Comment';
import LoadMore from './LoadMore';
import { forEachError } from 'plugin-api/beta/client/utils';
class CommentHistory extends React.Component {
state = {
@@ -16,11 +15,8 @@ class CommentHistory extends React.Component {
.then(() => {
this.setState({ loadingState: 'success' });
})
.catch(error => {
.catch(() => {
this.setState({ loadingState: 'error' });
forEachError(error, ({ msg }) => {
this.props.notify('error', msg);
});
});
};
@@ -55,7 +51,6 @@ class CommentHistory extends React.Component {
CommentHistory.propTypes = {
comments: PropTypes.object.isRequired,
loadMore: PropTypes.func,
notify: PropTypes.func,
link: PropTypes.func,
data: PropTypes.object,
root: PropTypes.object,
+1 -7
View File
@@ -9,11 +9,7 @@ import withFragments from 'coral-framework/hocs/withFragments';
import withMutation from 'coral-framework/hocs/withMutation';
import { notify } from 'coral-framework/actions/notification';
import { capitalize } from 'coral-framework/helpers/strings';
import {
getMyActionSummary,
getTotalActionCount,
getErrorMessages,
} from 'coral-framework/utils';
import { getMyActionSummary, getTotalActionCount } from 'coral-framework/utils';
import hoistStatics from 'recompose/hoistStatics';
import * as PropTypes from 'prop-types';
import { getDefinitionName } from '../utils';
@@ -282,7 +278,6 @@ export default (reaction, options = {}) =>
})
.catch(err => {
this.duringMutation = false;
this.props.notify('error', getErrorMessages(err));
throw err;
});
};
@@ -307,7 +302,6 @@ export default (reaction, options = {}) =>
})
.catch(err => {
this.duringMutation = false;
this.props.notify('error', getErrorMessages(err));
throw err;
});
};
+4 -11
View File
@@ -1,13 +1,11 @@
import React from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { compose, gql } from 'react-apollo';
import { getDisplayName } from 'coral-framework/helpers/hoc';
import { capitalize } from 'coral-framework/helpers/strings';
import { withAddTag, withRemoveTag } from 'coral-framework/graphql/mutations';
import withFragments from 'coral-framework/hocs/withFragments';
import { notify } from 'coral-framework/actions/notification';
import { getErrorMessages, isTagged } from 'coral-framework/utils';
import { isTagged } from 'coral-framework/utils';
import hoistStatics from 'recompose/hoistStatics';
import { getDefinitionName } from '../utils';
@@ -38,7 +36,7 @@ export default (tag, options = {}) =>
loading = false;
postTag = () => {
const { comment, asset, notify } = this.props;
const { comment, asset } = this.props;
if (this.loading) {
return;
@@ -59,13 +57,12 @@ export default (tag, options = {}) =>
})
.catch(err => {
this.loading = false;
notify('error', getErrorMessages(err));
throw err;
});
};
deleteTag = () => {
const { comment, asset, notify } = this.props;
const { comment, asset } = this.props;
if (this.loading) {
return;
@@ -84,7 +81,6 @@ export default (tag, options = {}) =>
})
.catch(err => {
this.loading = false;
notify('error', getErrorMessages(err));
throw err;
});
};
@@ -114,9 +110,6 @@ export default (tag, options = {}) =>
user: state.auth.user,
});
const mapDispatchToProps = dispatch =>
bindActionCreators({ notify }, dispatch);
const enhance = compose(
withFragments({
...fragments,
@@ -146,7 +139,7 @@ export default (tag, options = {}) =>
}),
withAddTag,
withRemoveTag,
connect(mapStateToProps, mapDispatchToProps)
connect(mapStateToProps, null)
);
WithTags.displayName = `WithTags(${getDisplayName(WrappedComponent)})`;
@@ -64,7 +64,6 @@ export default class ModTag extends React.Component {
ModTag.propTypes = {
alreadyTagged: PropTypes.bool,
deleteTag: PropTypes.func,
notify: PropTypes.func,
openFeaturedDialog: PropTypes.func,
comment: PropTypes.object,
asset: PropTypes.object,
@@ -1,7 +1,6 @@
import React from 'react';
import Comment from '../containers/Comment';
import LoadMore from './LoadMore';
import { getErrorMessages } from 'plugin-api/beta/client/utils';
class TabPane extends React.Component {
state = {
@@ -15,9 +14,8 @@ class TabPane extends React.Component {
.then(() => {
this.setState({ loadingState: 'success' });
})
.catch(error => {
.catch(() => {
this.setState({ loadingState: 'error' });
this.props.notify('error', getErrorMessages(error));
});
};
@@ -13,8 +13,8 @@ const mapDispatchToProps = dispatch =>
);
const enhance = compose(
withTags('featured'),
connect(null, mapDispatchToProps)
connect(null, mapDispatchToProps),
withTags('featured')
);
export default enhance(ModActionButton);
@@ -3,12 +3,10 @@ import { withTags, connect } from 'plugin-api/beta/client/hocs';
import { gql, compose } from 'react-apollo';
import { bindActionCreators } from 'redux';
import { openFeaturedDialog } from '../actions';
import { notify } from 'plugin-api/beta/client/actions/notification';
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
notify,
openFeaturedDialog,
},
dispatch
@@ -24,8 +22,8 @@ const fragments = {
`,
};
const enhance = compose(
withTags('featured', { fragments }),
connect(null, mapDispatchToProps)
connect(null, mapDispatchToProps),
withTags('featured', { fragments })
);
export default enhance(ModTag);
@@ -4,7 +4,6 @@ import { compose, gql } from 'react-apollo';
import TabPane from '../components/TabPane';
import { withFragments, connect } from 'plugin-api/beta/client/hocs';
import Comment from '../containers/Comment';
import { notify } from 'plugin-api/beta/client/actions/notification';
import { viewComment } from 'coral-embed-stream/src/actions/stream';
import {
appendNewNodes,
@@ -81,7 +80,6 @@ const mapDispatchToProps = dispatch =>
bindActionCreators(
{
viewComment,
notify,
},
dispatch
);
@@ -10,21 +10,16 @@ import { bindActionCreators } from 'redux';
import { closeMenu } from 'plugins/talk-plugin-author-menu/client/actions';
import { notify } from 'plugin-api/beta/client/actions/notification';
import { t } from 'plugin-api/beta/client/services';
import { getErrorMessages } from 'plugin-api/beta/client/utils';
class IgnoreUserConfirmationContainer extends React.Component {
ignoreUser = () => {
const { ignoreUser, notify, comment, closeMenu } = this.props;
ignoreUser(comment.user.id)
.then(() => {
notify(
'success',
t('talk-plugin-ignore-user.notify_success', comment.user.username)
);
})
.catch(err => {
notify('error', getErrorMessages(err));
});
ignoreUser(comment.user.id).then(() => {
notify(
'success',
t('talk-plugin-ignore-user.notify_success', comment.user.username)
);
});
closeMenu();
};
@@ -1,23 +1,16 @@
import React from 'react';
import { compose } from 'react-apollo';
import { bindActionCreators } from 'redux';
import { getErrorMessages } from 'plugin-api/beta/client/utils';
import { notify } from 'plugin-api/beta/client/actions/notification';
import ApproveCommentAction from '../components/ApproveCommentAction';
import { connect, withSetCommentStatus } from 'plugin-api/beta/client/hocs';
import { withSetCommentStatus } from 'plugin-api/beta/client/hocs';
class ApproveCommentActionContainer extends React.Component {
approveComment = async () => {
const { setCommentStatus, comment, hideMenu, notify } = this.props;
const { setCommentStatus, comment, hideMenu } = this.props;
try {
await setCommentStatus({
commentId: comment.id,
status: 'ACCEPTED',
});
} catch (err) {
notify('error', getErrorMessages(err));
}
await setCommentStatus({
commentId: comment.id,
status: 'ACCEPTED',
});
hideMenu();
};
@@ -32,17 +25,6 @@ class ApproveCommentActionContainer extends React.Component {
}
}
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
notify,
},
dispatch
);
const enhance = compose(
connect(null, mapDispatchToProps),
withSetCommentStatus
);
const enhance = compose(withSetCommentStatus);
export default enhance(ApproveCommentActionContainer);
@@ -3,19 +3,16 @@ import PropTypes from 'prop-types';
import { compose } from 'react-apollo';
import { bindActionCreators } from 'redux';
import { closeBanDialog, closeMenu } from '../actions';
import { notify } from 'plugin-api/beta/client/actions/notification';
import {
connect,
withSetCommentStatus,
withBanUser,
} from 'plugin-api/beta/client/hocs';
import { getErrorMessages } from 'plugin-api/beta/client/utils';
import BanUserDialog from '../components/BanUserDialog';
class BanUserDialogContainer extends React.Component {
banUser = async () => {
const {
notify,
authorId,
commentId,
commentStatus,
@@ -25,23 +22,19 @@ class BanUserDialogContainer extends React.Component {
banUser,
} = this.props;
try {
await banUser({
id: authorId,
message: '',
await banUser({
id: authorId,
message: '',
});
closeMenu();
closeBanDialog();
if (commentStatus !== 'REJECTED') {
await setCommentStatus({
commentId: commentId,
status: 'REJECTED',
});
closeMenu();
closeBanDialog();
if (commentStatus !== 'REJECTED') {
await setCommentStatus({
commentId: commentId,
status: 'REJECTED',
});
}
} catch (err) {
notify('error', getErrorMessages(err));
}
};
@@ -70,7 +63,6 @@ const mapStateToProps = ({ talkPluginModerationActions: state }) => ({
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
notify,
closeBanDialog,
closeMenu,
},
@@ -1,23 +1,16 @@
import React from 'react';
import { compose } from 'react-apollo';
import { bindActionCreators } from 'redux';
import { getErrorMessages } from 'plugin-api/beta/client/utils';
import { notify } from 'plugin-api/beta/client/actions/notification';
import RejectCommentAction from '../components/RejectCommentAction';
import { connect, withSetCommentStatus } from 'plugin-api/beta/client/hocs';
import { withSetCommentStatus } from 'plugin-api/beta/client/hocs';
class RejectCommentActionContainer extends React.Component {
rejectComment = async () => {
const { setCommentStatus, comment, hideMenu, notify } = this.props;
const { setCommentStatus, comment, hideMenu } = this.props;
try {
await setCommentStatus({
commentId: comment.id,
status: 'REJECTED',
});
} catch (err) {
notify('error', getErrorMessages(err));
}
await setCommentStatus({
commentId: comment.id,
status: 'REJECTED',
});
hideMenu();
};
@@ -27,17 +20,6 @@ class RejectCommentActionContainer extends React.Component {
}
}
const mapDispatchToProps = dispatch =>
bindActionCreators(
{
notify,
},
dispatch
);
const enhance = compose(
connect(null, mapDispatchToProps),
withSetCommentStatus
);
const enhance = compose(withSetCommentStatus);
export default enhance(RejectCommentActionContainer);