[MM-35617] App modals are now opened from components instead of actions (#5394)
* proper fix for opening app form modal and url navigation * complete merge with changes * add useCallback check for theme * lint
This commit is contained in:
parent
7cdbe095a1
commit
fd0cc90160
16 changed files with 149 additions and 97 deletions
|
|
@ -3,22 +3,15 @@
|
|||
|
||||
import {sendEphemeralPost} from '@actions/views/post';
|
||||
import {Client4} from '@client/rest';
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {handleGotoLocation} from '@mm-redux/actions/integrations';
|
||||
import {AppCallTypes, AppCallResponseTypes} from '@mm-redux/constants/apps';
|
||||
import {getTheme} from '@mm-redux/selectors/entities/preferences';
|
||||
import {ActionFunc, DispatchFunc} from '@mm-redux/types/actions';
|
||||
import {AppCallResponse, AppForm, AppCallRequest, AppCallType, AppContext} from '@mm-redux/types/apps';
|
||||
import {AppCallResponse, AppCallRequest, AppCallType, AppContext} from '@mm-redux/types/apps';
|
||||
import {CommandArgs} from '@mm-redux/types/integrations';
|
||||
import {Post} from '@mm-redux/types/posts';
|
||||
import {Theme} from '@mm-redux/types/preferences';
|
||||
import EphemeralStore from '@store/ephemeral_store';
|
||||
import {makeCallErrorResponse} from '@utils/apps';
|
||||
|
||||
import {showModal} from './navigation';
|
||||
|
||||
export function doAppCall<Res=unknown>(call: AppCallRequest, type: AppCallType, intl: any): ActionFunc {
|
||||
return async (dispatch, getState) => {
|
||||
return async () => {
|
||||
try {
|
||||
const res = await Client4.executeAppCall(call, type) as AppCallResponse<Res>;
|
||||
const responseType = res.type || AppCallResponseTypes.OK;
|
||||
|
|
@ -37,11 +30,6 @@ export function doAppCall<Res=unknown>(call: AppCallRequest, type: AppCallType,
|
|||
return {error: makeCallErrorResponse(errMsg)};
|
||||
}
|
||||
|
||||
const screen = EphemeralStore.getNavigationTopComponentId();
|
||||
if (type === AppCallTypes.SUBMIT && screen !== 'AppForm') {
|
||||
showAppForm(res.form, call, getTheme(getState()));
|
||||
}
|
||||
|
||||
return {data: res};
|
||||
}
|
||||
case AppCallResponseTypes.NAVIGATE:
|
||||
|
|
@ -61,8 +49,6 @@ export function doAppCall<Res=unknown>(call: AppCallRequest, type: AppCallType,
|
|||
return {error: makeCallErrorResponse(errMsg)};
|
||||
}
|
||||
|
||||
dispatch(handleGotoLocation(res.navigate_to_url, intl));
|
||||
|
||||
return {data: res};
|
||||
default: {
|
||||
const errMsg = intl.formatMessage({
|
||||
|
|
@ -84,34 +70,6 @@ export function doAppCall<Res=unknown>(call: AppCallRequest, type: AppCallType,
|
|||
};
|
||||
}
|
||||
|
||||
const showAppForm = async (form: AppForm, call: AppCallRequest, theme: Theme) => {
|
||||
const closeButton = await CompassIcon.getImageSource('close', 24, theme.sidebarHeaderTextColor);
|
||||
|
||||
let submitButtons;
|
||||
const customSubmitButtons = form.submit_buttons && form.fields.find((f) => f.name === form.submit_buttons)?.options;
|
||||
|
||||
if (!customSubmitButtons?.length) {
|
||||
submitButtons = [{
|
||||
id: 'submit-form',
|
||||
showAsAction: 'always',
|
||||
text: 'Submit',
|
||||
}];
|
||||
}
|
||||
|
||||
const options = {
|
||||
topBar: {
|
||||
leftButtons: [{
|
||||
id: 'close-dialog',
|
||||
icon: closeButton,
|
||||
}],
|
||||
rightButtons: submitButtons,
|
||||
},
|
||||
};
|
||||
|
||||
const passProps = {form, call};
|
||||
showModal('AppForm', form.title, passProps, options);
|
||||
};
|
||||
|
||||
export function postEphemeralCallResponseForPost(response: AppCallResponse, message: string, post: Post): ActionFunc {
|
||||
return (dispatch: DispatchFunc) => {
|
||||
return dispatch(sendEphemeralPost(
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import merge from 'deepmerge';
|
|||
import {Keyboard, Platform} from 'react-native';
|
||||
import {Navigation} from 'react-native-navigation';
|
||||
|
||||
import CompassIcon from '@components/compass_icon';
|
||||
import {DeviceTypes, NavigationTypes} from '@constants';
|
||||
import {CHANNEL} from '@constants/screen';
|
||||
import {Preferences} from '@mm-redux/constants';
|
||||
|
|
@ -372,6 +373,34 @@ export function showSearchModal(initialValue = '') {
|
|||
showModal(name, title, passProps, options);
|
||||
}
|
||||
|
||||
export const showAppForm = async (form, call, theme) => {
|
||||
const closeButton = await CompassIcon.getImageSource('close', 24, theme.sidebarHeaderTextColor);
|
||||
|
||||
let submitButtons;
|
||||
const customSubmitButtons = form.submit_buttons && form.fields.find((f) => f.name === form.submit_buttons)?.options;
|
||||
|
||||
if (!customSubmitButtons?.length) {
|
||||
submitButtons = [{
|
||||
id: 'submit-form',
|
||||
showAsAction: 'always',
|
||||
text: 'Submit',
|
||||
}];
|
||||
}
|
||||
|
||||
const options = {
|
||||
topBar: {
|
||||
leftButtons: [{
|
||||
id: 'close-dialog',
|
||||
icon: closeButton,
|
||||
}],
|
||||
rightButtons: submitButtons,
|
||||
},
|
||||
};
|
||||
|
||||
const passProps = {form, call};
|
||||
showModal('AppForm', form.title, passProps, options);
|
||||
};
|
||||
|
||||
export async function dismissModal(options = {}) {
|
||||
if (!EphemeralStore.hasModalsOpened()) {
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -67,8 +67,14 @@ export function executeCommand(message: string, channelId: string, rootId: strin
|
|||
}
|
||||
return {data: {}};
|
||||
case AppCallResponseTypes.FORM:
|
||||
return {data: {
|
||||
form: callResp.form,
|
||||
call,
|
||||
}};
|
||||
case AppCallResponseTypes.NAVIGATE:
|
||||
return {data: {}};
|
||||
return {data: {
|
||||
goto_location: callResp.navigate_to_url,
|
||||
}};
|
||||
default:
|
||||
return createErrorMessage(intl.formatMessage({
|
||||
id: 'apps.error.responses.unknown_type',
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import {Platform, ScrollView, View} from 'react-native';
|
|||
import HWKeyboardEvent from 'react-native-hw-keyboard-event';
|
||||
import {SafeAreaView} from 'react-native-safe-area-context';
|
||||
|
||||
import {showAppForm} from '@actions/navigation';
|
||||
import Autocomplete from '@components/autocomplete';
|
||||
import PostInput from '@components/post_draft/post_input';
|
||||
import QuickActions from '@components/post_draft/quick_actions';
|
||||
|
|
@ -294,7 +295,7 @@ export default class DraftInput extends PureComponent {
|
|||
|
||||
sendCommand = async (msg) => {
|
||||
const {intl} = this.context;
|
||||
const {channelId, executeCommand, rootId, userIsOutOfOffice} = this.props;
|
||||
const {channelId, executeCommand, rootId, userIsOutOfOffice, theme} = this.props;
|
||||
|
||||
const status = DraftUtils.getStatusFromSlashCommand(msg);
|
||||
if (userIsOutOfOffice && DraftUtils.isStatusSlashCommand(status)) {
|
||||
|
|
@ -312,6 +313,10 @@ export default class DraftInput extends PureComponent {
|
|||
return;
|
||||
}
|
||||
|
||||
if (data.form) {
|
||||
showAppForm(data.form, data.call, theme);
|
||||
}
|
||||
|
||||
this.setInputValue('');
|
||||
this.input.current.changeDraft('');
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import React, {useCallback, useRef} from 'react';
|
|||
import {intlShape, injectIntl} from 'react-intl';
|
||||
import Button from 'react-native-button';
|
||||
|
||||
import {showAppForm} from '@actions/navigation';
|
||||
import {AppExpandLevels, AppBindingLocations, AppCallTypes, AppCallResponseTypes} from '@mm-redux/constants/apps';
|
||||
import {ActionResult} from '@mm-redux/types/actions';
|
||||
import {AppBinding} from '@mm-redux/types/apps';
|
||||
import {Post} from '@mm-redux/types/posts';
|
||||
import {Theme} from '@mm-redux/types/preferences';
|
||||
|
|
@ -23,6 +25,7 @@ type Props = {
|
|||
intl: typeof intlShape;
|
||||
post: Post;
|
||||
postEphemeralCallResponseForPost: PostEphemeralCallResponseForPost;
|
||||
handleGotoLocation: (href: string, intl: any) => Promise<ActionResult>;
|
||||
teamID: string;
|
||||
theme: Theme;
|
||||
}
|
||||
|
|
@ -50,7 +53,7 @@ const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
|
|||
};
|
||||
});
|
||||
|
||||
const ButtonBinding = ({binding, doAppCall, intl, post, postEphemeralCallResponseForPost, teamID, theme}: Props) => {
|
||||
const ButtonBinding = ({binding, doAppCall, intl, post, postEphemeralCallResponseForPost, teamID, theme, handleGotoLocation}: Props) => {
|
||||
const pressed = useRef(false);
|
||||
const style = getStyleSheet(theme);
|
||||
|
||||
|
|
@ -97,7 +100,10 @@ const ButtonBinding = ({binding, doAppCall, intl, post, postEphemeralCallRespons
|
|||
}
|
||||
return;
|
||||
case AppCallResponseTypes.NAVIGATE:
|
||||
handleGotoLocation(callResp.navigate_to_url!, intl);
|
||||
return;
|
||||
case AppCallResponseTypes.FORM:
|
||||
showAppForm(callResp.form, call, theme);
|
||||
return;
|
||||
default: {
|
||||
const errorMessage = intl.formatMessage({
|
||||
|
|
@ -109,7 +115,7 @@ const ButtonBinding = ({binding, doAppCall, intl, post, postEphemeralCallRespons
|
|||
postEphemeralCallResponseForPost(callResp, errorMessage, post);
|
||||
}
|
||||
}
|
||||
}), []);
|
||||
}), [theme]);
|
||||
|
||||
return (
|
||||
<Button
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
import {connect} from 'react-redux';
|
||||
|
||||
import {doAppCall, postEphemeralCallResponseForPost} from '@actions/apps';
|
||||
import {handleGotoLocation} from '@mm-redux/actions/integrations';
|
||||
import {getChannel} from '@mm-redux/selectors/entities/channels';
|
||||
import {getPost} from '@mm-redux/selectors/entities/posts';
|
||||
import {getCurrentTeamId} from '@mm-redux/selectors/entities/teams';
|
||||
|
|
@ -33,6 +34,7 @@ function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
|
|||
const mapDispatchToProps = {
|
||||
doAppCall,
|
||||
postEphemeralCallResponseForPost,
|
||||
handleGotoLocation,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ButtonBinding);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@
|
|||
import {connect} from 'react-redux';
|
||||
|
||||
import {doAppCall, postEphemeralCallResponseForPost} from '@actions/apps';
|
||||
import {handleGotoLocation} from '@mm-redux/actions/integrations';
|
||||
import {getChannel} from '@mm-redux/selectors/entities/channels';
|
||||
import {getPost} from '@mm-redux/selectors/entities/posts';
|
||||
import {getTheme} from '@mm-redux/selectors/entities/preferences';
|
||||
import {getCurrentTeamId} from '@mm-redux/selectors/entities/teams';
|
||||
|
||||
import MenuBinding from './menu_binding';
|
||||
|
|
@ -23,6 +25,7 @@ function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
|
|||
const channel = getChannel(state, post.channel_id);
|
||||
|
||||
return {
|
||||
theme: getTheme(state),
|
||||
post,
|
||||
teamID: channel?.team_id || getCurrentTeamId(state),
|
||||
};
|
||||
|
|
@ -31,6 +34,7 @@ function mapStateToProps(state: GlobalState, ownProps: OwnProps) {
|
|||
const mapDispatchToProps = {
|
||||
doAppCall,
|
||||
postEphemeralCallResponseForPost,
|
||||
handleGotoLocation,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(MenuBinding);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,11 @@
|
|||
import React, {useCallback, useState} from 'react';
|
||||
import {intlShape, injectIntl} from 'react-intl';
|
||||
|
||||
import {showAppForm} from '@actions/navigation';
|
||||
import AutocompleteSelector from '@components/autocomplete_selector';
|
||||
import {AppExpandLevels, AppBindingLocations, AppCallTypes, AppCallResponseTypes} from '@mm-redux/constants/apps';
|
||||
import {ActionResult} from '@mm-redux/types/actions';
|
||||
import {Theme} from '@mm-redux/types/preferences';
|
||||
import {createCallContext, createCallRequest} from '@utils/apps';
|
||||
|
||||
import type {AppBinding} from '@mm-redux/types/apps';
|
||||
|
|
@ -19,10 +22,12 @@ type Props = {
|
|||
intl: typeof intlShape;
|
||||
post: Post;
|
||||
postEphemeralCallResponseForPost: PostEphemeralCallResponseForPost;
|
||||
handleGotoLocation: (href: string, intl: any) => Promise<ActionResult>;
|
||||
teamID: string;
|
||||
theme: Theme;
|
||||
}
|
||||
|
||||
const MenuBinding = ({binding, doAppCall, intl, post, postEphemeralCallResponseForPost, teamID}: Props) => {
|
||||
const MenuBinding = ({binding, doAppCall, intl, post, postEphemeralCallResponseForPost, handleGotoLocation, teamID, theme}: Props) => {
|
||||
const [selected, setSelected] = useState<PostActionOption>();
|
||||
|
||||
const onSelect = useCallback(async (picked?: PostActionOption) => {
|
||||
|
|
@ -74,7 +79,10 @@ const MenuBinding = ({binding, doAppCall, intl, post, postEphemeralCallResponseF
|
|||
}
|
||||
return;
|
||||
case AppCallResponseTypes.NAVIGATE:
|
||||
handleGotoLocation(callResp.navigate_to_url!, intl);
|
||||
return;
|
||||
case AppCallResponseTypes.FORM:
|
||||
showAppForm(callResp.form, call, theme);
|
||||
return;
|
||||
default: {
|
||||
const errorMessage = intl.formatMessage({
|
||||
|
|
@ -86,7 +94,7 @@ const MenuBinding = ({binding, doAppCall, intl, post, postEphemeralCallResponseF
|
|||
postEphemeralCallResponseForPost(callResp, errorMessage, post);
|
||||
}
|
||||
}
|
||||
}, []);
|
||||
}, [theme]);
|
||||
|
||||
const options = binding.bindings?.map<PostActionOption>((b:AppBinding) => {
|
||||
return {text: b.label, value: b.location || ''};
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ describe('AppsForm', () => {
|
|||
performLookupCall: jest.fn(),
|
||||
refreshOnSelect: jest.fn(),
|
||||
submit: jest.fn(),
|
||||
handleGotoLocation: jest.fn(),
|
||||
},
|
||||
call: {
|
||||
context: {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import {dismissModal} from '@actions/navigation';
|
|||
import Markdown from '@components/markdown';
|
||||
import StatusBar from '@components/status_bar';
|
||||
import {AppCallResponseTypes, AppFieldTypes} from '@mm-redux/constants/apps';
|
||||
import {ActionResult} from '@mm-redux/types/actions';
|
||||
import {AppCallRequest, AppField, AppForm, AppFormValue, AppFormValues, AppLookupResponse, AppSelectOption, FormResponseData} from '@mm-redux/types/apps';
|
||||
import {DialogElement} from '@mm-redux/types/integrations';
|
||||
import {Theme} from '@mm-redux/types/preferences';
|
||||
|
|
@ -35,6 +36,7 @@ export type Props = {
|
|||
}) => Promise<DoAppCallResult<FormResponseData>>;
|
||||
performLookupCall: (field: AppField, values: AppFormValues, userInput: string) => Promise<DoAppCallResult<AppLookupResponse>>;
|
||||
refreshOnSelect: (field: AppField, values: AppFormValues, value: AppFormValue) => Promise<DoAppCallResult<FormResponseData>>;
|
||||
handleGotoLocation: (href: string, intl: any) => Promise<ActionResult>;
|
||||
};
|
||||
theme: Theme;
|
||||
componentId: string;
|
||||
|
|
@ -166,7 +168,8 @@ export default class AppsFormComponent extends PureComponent<Props, State> {
|
|||
switch (callResponse.type) {
|
||||
case AppCallResponseTypes.OK:
|
||||
case AppCallResponseTypes.NAVIGATE:
|
||||
this.handleHide();
|
||||
await this.handleHide();
|
||||
this.props.actions.handleGotoLocation(callResponse.navigate_to_url!, this.context.intl);
|
||||
return;
|
||||
case AppCallResponseTypes.FORM:
|
||||
this.submitting = false;
|
||||
|
|
@ -280,8 +283,8 @@ export default class AppsFormComponent extends PureComponent<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
handleHide = () => {
|
||||
dismissModal();
|
||||
handleHide = async () => {
|
||||
await dismissModal();
|
||||
}
|
||||
|
||||
onChange = (name: string, value: any) => {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import React, {PureComponent} from 'react';
|
|||
import {intlShape} from 'react-intl';
|
||||
|
||||
import {AppCallResponseTypes, AppCallTypes} from '@mm-redux/constants/apps';
|
||||
import {ActionResult} from '@mm-redux/types/actions';
|
||||
import {AppCallResponse, AppCallRequest, AppField, AppForm, AppFormValues, FormResponseData, AppLookupResponse} from '@mm-redux/types/apps';
|
||||
import {Theme} from '@mm-redux/types/preferences';
|
||||
import {DoAppCall, DoAppCallResult, PostEphemeralCallResponseForContext} from '@mm-types/actions/apps';
|
||||
|
|
@ -18,6 +19,7 @@ export type Props = {
|
|||
actions: {
|
||||
doAppCall: DoAppCall<any>;
|
||||
postEphemeralCallResponseForContext: PostEphemeralCallResponseForContext;
|
||||
handleGotoLocation: (href: string, intl: any) => Promise<ActionResult>;
|
||||
};
|
||||
theme: Theme;
|
||||
componentId: string;
|
||||
|
|
@ -233,6 +235,7 @@ export default class AppsFormContainer extends PureComponent<Props, State> {
|
|||
submit: this.handleSubmit,
|
||||
performLookupCall: this.performLookupCall,
|
||||
refreshOnSelect: this.refreshOnSelect,
|
||||
handleGotoLocation: this.props.actions.handleGotoLocation,
|
||||
}}
|
||||
theme={this.props.theme}
|
||||
componentId={this.props.componentId}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {connect} from 'react-redux';
|
|||
import {ActionCreatorsMapObject, bindActionCreators, Dispatch} from 'redux';
|
||||
|
||||
import {doAppCall, postEphemeralCallResponseForContext} from '@actions/apps';
|
||||
import {handleGotoLocation} from '@mm-redux/actions/integrations';
|
||||
import {getTheme} from '@mm-redux/selectors/entities/preferences';
|
||||
import {ActionFunc, GenericAction} from '@mm-redux/types/actions';
|
||||
import {GlobalState} from '@mm-redux/types/store';
|
||||
|
|
@ -28,6 +29,7 @@ function mapDispatchToProps(dispatch: Dispatch<GenericAction>) {
|
|||
actions: bindActionCreators<ActionCreatorsMapObject<ActionFunc>, Actions>({
|
||||
doAppCall,
|
||||
postEphemeralCallResponseForContext,
|
||||
handleGotoLocation,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,9 @@ import React from 'react';
|
|||
import {intlShape, injectIntl} from 'react-intl';
|
||||
import {Alert} from 'react-native';
|
||||
|
||||
import {dismissModal} from '@actions/navigation';
|
||||
import {dismissModal, showAppForm} from '@actions/navigation';
|
||||
import {AppCallResponseTypes, AppCallTypes} from '@mm-redux/constants/apps';
|
||||
import {ActionResult} from '@mm-redux/types/actions';
|
||||
import {AppBinding} from '@mm-redux/types/apps';
|
||||
import {Channel} from '@mm-redux/types/channels';
|
||||
import {Theme} from '@mm-redux/types/preferences';
|
||||
|
|
@ -26,6 +27,7 @@ type Props = {
|
|||
actions: {
|
||||
doAppCall: DoAppCall;
|
||||
postEphemeralCallResponseForChannel: PostEphemeralCallResponseForChannel;
|
||||
handleGotoLocation: (href: string, intl: any) => Promise<ActionResult>;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -65,6 +67,7 @@ type OptionProps = {
|
|||
actions: {
|
||||
doAppCall: DoAppCall;
|
||||
postEphemeralCallResponseForChannel: PostEphemeralCallResponseForChannel;
|
||||
handleGotoLocation: (href: string, intl: any) => Promise<ActionResult>;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -78,7 +81,7 @@ class Option extends React.PureComponent<OptionProps, OptionState> {
|
|||
};
|
||||
|
||||
onPress = async () => {
|
||||
const {binding, currentChannel, currentTeamId, intl} = this.props;
|
||||
const {binding, currentChannel, currentTeamId, intl, theme} = this.props;
|
||||
const {doAppCall, postEphemeralCallResponseForChannel} = this.props.actions;
|
||||
|
||||
if (this.state.submitting) {
|
||||
|
|
@ -128,8 +131,13 @@ class Option extends React.PureComponent<OptionProps, OptionState> {
|
|||
}
|
||||
break;
|
||||
case AppCallResponseTypes.NAVIGATE:
|
||||
await dismissModal();
|
||||
this.props.actions.handleGotoLocation(callResp.navigate_to_url!, intl);
|
||||
return;
|
||||
case AppCallResponseTypes.FORM:
|
||||
break;
|
||||
await dismissModal();
|
||||
showAppForm(callResp.form, call, theme);
|
||||
return;
|
||||
default: {
|
||||
const title = intl.formatMessage({
|
||||
id: 'mobile.general.error.title',
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {connect} from 'react-redux';
|
|||
import {bindActionCreators, Dispatch, ActionCreatorsMapObject} from 'redux';
|
||||
|
||||
import {doAppCall, postEphemeralCallResponseForChannel} from '@actions/apps';
|
||||
import {handleGotoLocation} from '@mm-redux/actions/integrations';
|
||||
import {AppBindingLocations} from '@mm-redux/constants/apps';
|
||||
import {getAppsBindings} from '@mm-redux/selectors/entities/apps';
|
||||
import {getCurrentChannel} from '@mm-redux/selectors/entities/channels';
|
||||
|
|
@ -39,6 +40,7 @@ function mapDispatchToProps(dispatch: Dispatch<GenericAction>) {
|
|||
actions: bindActionCreators<ActionCreatorsMapObject<ActionFunc>, Actions>({
|
||||
doAppCall,
|
||||
postEphemeralCallResponseForChannel,
|
||||
handleGotoLocation,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import React from 'react';
|
|||
import {intlShape, injectIntl} from 'react-intl';
|
||||
import {Alert} from 'react-native';
|
||||
|
||||
import {showAppForm} from '@actions/navigation';
|
||||
import {AppCallResponseTypes, AppCallTypes, AppExpandLevels} from '@mm-redux/constants/apps';
|
||||
import {ActionResult} from '@mm-redux/types/actions';
|
||||
import {AppBinding, AppCallResponse} from '@mm-redux/types/apps';
|
||||
import {Post} from '@mm-redux/types/posts';
|
||||
import {Theme} from '@mm-redux/types/preferences';
|
||||
|
|
@ -28,6 +30,7 @@ type Props = {
|
|||
actions: {
|
||||
doAppCall: DoAppCall;
|
||||
postEphemeralCallResponseForPost: PostEphemeralCallResponseForPost;
|
||||
handleGotoLocation: (href: string, intl: any) => Promise<ActionResult>;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -74,12 +77,13 @@ type OptionProps = {
|
|||
actions: {
|
||||
doAppCall: DoAppCall;
|
||||
postEphemeralCallResponseForPost: PostEphemeralCallResponseForPost;
|
||||
handleGotoLocation: (href: string, intl: any) => Promise<ActionResult>;
|
||||
};
|
||||
}
|
||||
|
||||
class Option extends React.PureComponent<OptionProps> {
|
||||
onPress = async () => {
|
||||
const {closeWithAnimation, post, teamID, binding, intl} = this.props;
|
||||
const {post, teamID, binding, intl, theme} = this.props;
|
||||
const {doAppCall, postEphemeralCallResponseForPost} = this.props.actions;
|
||||
|
||||
if (!binding.call) {
|
||||
|
|
@ -101,50 +105,59 @@ class Option extends React.PureComponent<OptionProps> {
|
|||
},
|
||||
);
|
||||
|
||||
closeWithAnimation(async () => {
|
||||
const callPromise = doAppCall(call, AppCallTypes.SUBMIT, intl);
|
||||
const res = await callPromise;
|
||||
if (res.error) {
|
||||
const errorResponse = res.error;
|
||||
const title = intl.formatMessage({
|
||||
id: 'mobile.general.error.title',
|
||||
defaultMessage: 'Error',
|
||||
});
|
||||
const errorMessage = errorResponse.error || intl.formatMessage({
|
||||
id: 'apps.error.unknown',
|
||||
defaultMessage: 'Unknown error occurred.',
|
||||
});
|
||||
Alert.alert(title, errorMessage);
|
||||
return;
|
||||
}
|
||||
const callPromise = doAppCall(call, AppCallTypes.SUBMIT, intl);
|
||||
await this.close();
|
||||
|
||||
const callResp = (res as {data: AppCallResponse}).data;
|
||||
switch (callResp.type) {
|
||||
case AppCallResponseTypes.OK:
|
||||
if (callResp.markdown) {
|
||||
postEphemeralCallResponseForPost(callResp, callResp.markdown, post);
|
||||
}
|
||||
break;
|
||||
case AppCallResponseTypes.NAVIGATE:
|
||||
case AppCallResponseTypes.FORM:
|
||||
break;
|
||||
default: {
|
||||
const title = intl.formatMessage({
|
||||
id: 'mobile.general.error.title',
|
||||
defaultMessage: 'Error',
|
||||
});
|
||||
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);
|
||||
const res = await callPromise;
|
||||
if (res.error) {
|
||||
const errorResponse = res.error;
|
||||
const title = intl.formatMessage({
|
||||
id: 'mobile.general.error.title',
|
||||
defaultMessage: 'Error',
|
||||
});
|
||||
const errorMessage = errorResponse.error || intl.formatMessage({
|
||||
id: 'apps.error.unknown',
|
||||
defaultMessage: 'Unknown error occurred.',
|
||||
});
|
||||
Alert.alert(title, errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
const callResp = (res as {data: AppCallResponse}).data;
|
||||
switch (callResp.type) {
|
||||
case AppCallResponseTypes.OK:
|
||||
if (callResp.markdown) {
|
||||
postEphemeralCallResponseForPost(callResp, callResp.markdown, post);
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
case AppCallResponseTypes.NAVIGATE:
|
||||
this.props.actions.handleGotoLocation(callResp.navigate_to_url!, intl);
|
||||
break;
|
||||
case AppCallResponseTypes.FORM:
|
||||
showAppForm(callResp.form, call, theme);
|
||||
break;
|
||||
default: {
|
||||
const title = intl.formatMessage({
|
||||
id: 'mobile.general.error.title',
|
||||
defaultMessage: 'Error',
|
||||
});
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
close = (): Promise<void> => {
|
||||
return new Promise((resolve) => {
|
||||
this.props.closeWithAnimation(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const {binding, theme} = this.props;
|
||||
if (!binding.label) {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import {connect} from 'react-redux';
|
|||
import {bindActionCreators, Dispatch, ActionCreatorsMapObject} from 'redux';
|
||||
|
||||
import {doAppCall, postEphemeralCallResponseForPost} from '@actions/apps';
|
||||
import {handleGotoLocation} from '@mm-redux/actions/integrations';
|
||||
import {AppBindingLocations} from '@mm-redux/constants/apps';
|
||||
import {getAppsBindings} from '@mm-redux/selectors/entities/apps';
|
||||
import {getChannel} from '@mm-redux/selectors/entities/channels';
|
||||
|
|
@ -48,6 +49,7 @@ function mapDispatchToProps(dispatch: Dispatch<GenericAction>) {
|
|||
actions: bindActionCreators<ActionCreatorsMapObject<ActionFunc>, Actions>({
|
||||
doAppCall,
|
||||
postEphemeralCallResponseForPost,
|
||||
handleGotoLocation,
|
||||
}, dispatch),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue