Skip to content

Commit

Permalink
[MM-33766] Handle channel header call error (#5233)
Browse files Browse the repository at this point in the history
* Apps bindings support (#5012)

* Add redux related information

* Add binding loading on channel refresh

* Add channel header and post option bindings

* Fix test

* Minor fixes

* Fix snapshots

* Handle errors and show bindings only on main channel

* Update Expand Levels keys and values to match latest changes

* Add NAVIGATE call response handling.

* Add more isolation to apps related code

* Add defaults to send ephemeral

* Update variable naming

* Rename shouldProcessApps by a more meaningful appsEnabled

* Embedded forms (#5169)

* Add redux related information

* Add binding loading on channel refresh

* Add channel header and post option bindings

* Fix test

* Minor fixes

* Fix snapshots

* Handle errors and show bindings only on main channel

* Update Expand Levels keys and values to match latest changes

* Add NAVIGATE call response handling.

* Add more isolation to apps related code

* Add Embedded Forms

* Fix snapshots

* Add Embedded Forms

* Improve naming, change the root element to be a binding and improve binding handling

* Get post down on the buttons, remove unneeded variables and minor fixes from the review

* Allow undefined bindings to fillBindingsInformation and add logging for error.

* Address review feedback

* Add App Forms (#5177)

* Add App Forms

* Address feedback and self review

* Add dynamic select

* Fixes and improvements

* Add the ability to refresh on submit.

* Use AppFormValue instead of redoing the type

* Address feedback

* [MM-31508] Rename URL to Path in Call (#5186)

* Add user agent to call context (#5193)

* Remove unneeded state reference (#5196)

* Change user agent query on fetch bindings (#5201)

* Add refresh websocket event to refetch bindings (#5194)

* Add refresh websocket event to refetch bindings

* Add missing changes

* Declare socket event constant and separate the handler to a different function

* Add binding validation on binding fetch (#5200)

* Apps commands (#5107)

* Add redux related information

* Add binding loading on channel refresh

* Add channel header and post option bindings

* Fix test

* Minor fixes

* Fix snapshots

* apps modals draft

* Handle errors and show bindings only on main channel

* Update Expand Levels keys and values to match latest changes

* Add NAVIGATE call response handling.

* Add more isolation to apps related code

* reuse command parser throughout slash_suggestion component's lifecycle

* fix prop and lint

* using alert to show error message. need another way

* duplicate import

* types

* types

* types

* rename file

* copy webapp parser and test

* dependencies moved. tests pass

* move app command parser into its own folder

* converted to typescript, all tests are passing

* automated and manual tests work

* lint

* lint

* remove fall throughs with blocks

* types

* doAppCall type

* extract displayError to deps file

* test types

* lint

* fix tests

* unused import

* PR feedback

* fix imports and show errors

* types

* remove execute suggestion for mobile

* watch feature flag

* fix tests

* change form text arugment behavior to show user input and not hint

* return call response error in doAppCall

* update tests to remove hint from text field suggestions

* lint

* use new base command structure

* fix tests

* wrap appsEnabled

* typescript actions/command.ts

* update app constants

* PR feedback

* fix error handling from doAppCall action

* Use App CallRequest structure (#5212)

* error handling

* move test files

* remove unused import

Co-authored-by: Daniel Espino García <larkox@gmail.com>

* Add feature flag (#5207)

* Add feature flag

* Simplify return

* Add localization, call validation and use call type on subpath (#5221)

* Add localization, call validation and use call type on subpath

* Add more localization to the parser and bring fixes from webapp

* Fix ephemerals and channel header / post options crashes

* fix app command parser deps and alert messages

* Improve suggestion handling

* Fix test

* Fix lint

* Return errors as error

* Address PR feedback

* return error property

* fix error string wordings

Co-authored-by: Michael Kochell <6913320+mickmister@users.noreply.github.com>

* leave the channel info screen when the call is successful. stay if there is an error

* fix intl in tests

Co-authored-by: Daniel Espino García <larkox@gmail.com>
Co-authored-by: Ben Schumacher <ben.schumacher@mattermost.com>
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
  • Loading branch information
4 people authored Mar 24, 2021
1 parent 48d4775 commit 985070e
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -503,7 +503,7 @@ exports[`channelInfo should match snapshot 1`] = `
}
}
/>
<Connect(Bindings)
<Connect(InjectIntl(Component))
theme={
Object {
"awayIndicator": "#ffbc42",
Expand Down Expand Up @@ -1035,7 +1035,7 @@ exports[`channelInfo should not include NotificationPreference for direct messag
}
}
/>
<Connect(Bindings)
<Connect(InjectIntl(Component))
theme={
Object {
"awayIndicator": "#ffbc42",
Expand Down Expand Up @@ -1425,7 +1425,7 @@ exports[`channelInfo should not include NotificationPreference for direct messag
}
}
/>
<Connect(Bindings)
<Connect(InjectIntl(Component))
theme={
Object {
"awayIndicator": "#ffbc42",
Expand Down
82 changes: 53 additions & 29 deletions app/screens/channel_info/bindings/bindings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,15 @@ type Props = {
theme: Theme;
currentChannel: Channel;
appsEnabled: boolean;
intl: typeof intlShape;
currentTeamId: string;
actions: {
doAppCall: (call: AppCallRequest, type: AppCallType, intl: any) => Promise<{data?: AppCallResponse, error?: AppCallResponse}>;
sendEphemeralPost: (message: any, channelId?: string, parentId?: string) => Promise<ActionResult>;
}
}

const Bindings: React.FC<Props> = (props: Props) => {
const Bindings: React.FC<Props> = injectIntl((props: Props) => {
if (!props.appsEnabled) {
return null;
}
Expand All @@ -51,7 +52,7 @@ const Bindings: React.FC<Props> = (props: Props) => {
{options}
</>
);
};
});

export default Bindings;

Expand All @@ -67,40 +68,60 @@ type OptionProps = {
},
}

const Option = injectIntl((props: OptionProps) => {
const onPress = async () => {
type OptionState = {
submitting: boolean;
}

class Option extends React.PureComponent<OptionProps, OptionState> {
state = {
submitting: false,
};

onPress = async () => {
const {binding, currentChannel, currentTeamId, intl} = this.props;
const {doAppCall, sendEphemeralPost} = this.props.actions;

if (this.state.submitting) {
return;
}

if (!binding.call) {
return;
}

const context = createCallContext(
binding.app_id,
binding.location,
props.currentChannel.id,
props.currentChannel.team_id || props.currentTeamId,
currentChannel.id,
currentChannel.team_id || currentTeamId,
);
const call = createCallRequest(
binding.call,
context,
);

const res = await props.actions.doAppCall(call, AppCallTypes.SUBMIT, props.intl);
this.setState({submitting: true});

const res = await doAppCall(call, AppCallTypes.SUBMIT, intl);

this.setState({submitting: false});

if (res.error) {
const errorResponse = res.error;
const title = props.intl.formatMessage({
const title = intl.formatMessage({
id: 'mobile.general.error.title',
defaultMessage: 'Error',
});
const errorMessage = errorResponse.error || props.intl.formatMessage({
const errorMessage = errorResponse.error || intl.formatMessage({
id: 'apps.error.unknown',
defaultMessage: 'Unknown error occurred.',
});
Alert.alert(title, errorMessage);
dismissModal();
return;
}

const callResp = res.data!;
const ephemeral = (message: string) => props.actions.sendEphemeralPost(message, props.currentChannel.id);
const ephemeral = (message: string) => sendEphemeralPost(message, currentChannel.id);
switch (callResp.type) {
case AppCallResponseTypes.OK:
if (callResp.markdown) {
Expand All @@ -111,37 +132,40 @@ const Option = injectIntl((props: OptionProps) => {
case AppCallResponseTypes.FORM:
break;
default: {
const title = props.intl.formatMessage({
const title = intl.formatMessage({
id: 'mobile.general.error.title',
defaultMessage: 'Error',
});
const errMessage = props.intl.formatMessage({
const errMessage = intl.formatMessage({
id: 'apps.error.responses.unknown_type',
defaultMessage: 'App response type not supported. Response type: {type}.',
}, {
type: callResp.type,
});
Alert.alert(title, errMessage);
return;
}
}

dismissModal();
};

const {binding, theme} = props;
if (!binding.label) {
return null;
render() {
const {binding, theme} = this.props;
if (!binding.label) {
return null;
}
return (
<>
<Separator theme={theme}/>
<ChannelInfoRow
action={this.onPress}
defaultMessage={binding.label}
theme={theme}
textId={binding.app_id + binding.location}
image={binding.icon ? {uri: binding.icon} : null}
/>
</>
);
}
return (
<>
<Separator theme={theme}/>
<ChannelInfoRow
action={onPress}
defaultMessage={binding.label}
theme={theme}
textId={binding.app_id + binding.location}
image={binding.icon ? {uri: binding.icon} : null}
/>
</>
);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ exports[`PostOptions should match snapshot, no option for system message to user
}
}
/>
<Connect(Bindings)
<Connect(InjectIntl(Component))
closeWithAnimation={[Function]}
post={
Object {
Expand Down Expand Up @@ -436,7 +436,7 @@ exports[`PostOptions should match snapshot, showing Delete option only for syste
}
}
/>
<Connect(Bindings)
<Connect(InjectIntl(Component))
closeWithAnimation={[Function]}
post={
Object {
Expand Down Expand Up @@ -680,7 +680,7 @@ exports[`PostOptions should match snapshot, showing all possible options 1`] = `
}
}
/>
<Connect(Bindings)
<Connect(InjectIntl(Component))
closeWithAnimation={[Function]}
post={
Object {
Expand Down
57 changes: 30 additions & 27 deletions app/screens/post_options/bindings/bindings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ type Props = {
teamID: string,
closeWithAnimation: () => void,
appsEnabled: boolean,
intl: typeof intlShape,
actions: {
doAppCall: (call: AppCallRequest, type: AppCallType, intl: any) => Promise<{data?: AppCallResponse, error?: AppCallResponse}>;
sendEphemeralPost: (message: any, channelId?: string, parentId?: string) => Promise<ActionResult>;
}
}

const Bindings = (props: Props) => {
const Bindings = injectIntl((props: Props) => {
if (!props.appsEnabled) {
return null;
}
Expand Down Expand Up @@ -58,7 +59,7 @@ const Bindings = (props: Props) => {
{options}
</>
);
};
});

export default Bindings;

Expand All @@ -76,9 +77,10 @@ type OptionProps = {
},
}

const Option = injectIntl((props: OptionProps) => {
const onPress = async () => {
const {closeWithAnimation, post, teamID} = props;
class Option extends React.PureComponent<OptionProps> {
onPress = async () => {
const {closeWithAnimation, post, teamID, binding, intl} = this.props;
const {doAppCall, sendEphemeralPost} = this.props.actions;

if (!binding.call) {
return;
Expand All @@ -98,24 +100,25 @@ const Option = injectIntl((props: OptionProps) => {
post: AppExpandLevels.ALL,
},
);
const res = await props.actions?.doAppCall(call, AppCallTypes.SUBMIT, props.intl);

closeWithAnimation();
const res = await doAppCall(call, AppCallTypes.SUBMIT, intl);
if (res.error) {
const errorResponse = res.error;
const title = props.intl.formatMessage({
const title = intl.formatMessage({
id: 'mobile.general.error.title',
defaultMessage: 'Error',
});
const errorMessage = errorResponse.error || props.intl.formatMessage({
const errorMessage = errorResponse.error || intl.formatMessage({
id: 'apps.error.unknown',
defaultMessage: 'Unknown error occurred.',
});
Alert.alert(title, errorMessage);
closeWithAnimation();
return;
}

const callResp = (res as {data: AppCallResponse}).data;
const ephemeral = (message: string) => props.actions.sendEphemeralPost(message, props.post.channel_id, props.post.root_id);
const ephemeral = (message: string) => sendEphemeralPost(message, post.channel_id, post.root_id);
switch (callResp.type) {
case AppCallResponseTypes.OK:
if (callResp.markdown) {
Expand All @@ -126,11 +129,11 @@ const Option = injectIntl((props: OptionProps) => {
case AppCallResponseTypes.FORM:
break;
default: {
const title = props.intl.formatMessage({
const title = intl.formatMessage({
id: 'mobile.general.error.title',
defaultMessage: 'Error',
});
const errMessage = props.intl.formatMessage({
const errMessage = intl.formatMessage({
id: 'apps.error.responses.unknown_type',
defaultMessage: 'App response type not supported. Response type: {type}.',
}, {
Expand All @@ -139,21 +142,21 @@ const Option = injectIntl((props: OptionProps) => {
Alert.alert(title, errMessage);
}
}

closeWithAnimation();
};

const {binding, theme} = props;
if (!binding.label) {
return null;
}
render() {
const {binding, theme} = this.props;
if (!binding.label) {
return null;
}

return (
<PostOption
icon={{uri: binding.icon}}
text={binding.label}
onPress={onPress}
theme={theme}
/>
);
});
return (
<PostOption
icon={{uri: binding.icon}}
text={binding.label}
onPress={this.onPress}
theme={theme}
/>
);
}
}
7 changes: 2 additions & 5 deletions app/screens/post_options/post_options.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@

import React from 'react';
import {Alert} from 'react-native';
import {shallow} from 'enzyme';
import {shallowWithIntl} from 'test/intl-test-helper';

import Preferences from '@mm-redux/constants/preferences';

import PostOptions from './post_options';

jest.mock('react-intl');

describe('PostOptions', () => {
const actions = {
addReaction: jest.fn(),
Expand Down Expand Up @@ -55,12 +53,11 @@ describe('PostOptions', () => {
};

function getWrapper(props = {}) {
return shallow(
return shallowWithIntl(
<PostOptions
{...baseProps}
{...props}
/>,
{context: {intl: {formatMessage: ({defaultMessage}) => defaultMessage}}},
);
}

Expand Down

0 comments on commit 985070e

Please sign in to comment.