From fd0cc90160ca9f12e085ff939f6962ae4a7731bf Mon Sep 17 00:00:00 2001 From: Michael Kochell <6913320+mickmister@users.noreply.github.com> Date: Fri, 6 Aug 2021 11:21:31 -0400 Subject: [PATCH] [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 --- app/actions/apps.ts | 46 +-------- app/actions/navigation/index.js | 29 ++++++ app/actions/views/command.ts | 8 +- .../post_draft/draft_input/draft_input.js | 7 +- .../button_binding/button_binding.tsx | 10 +- .../embedded_bindings/button_binding/index.ts | 2 + .../embedded_bindings/menu_binding/index.ts | 4 + .../menu_binding/menu_binding.tsx | 12 ++- .../apps_form/apps_form_component.test.tsx | 1 + app/screens/apps_form/apps_form_component.tsx | 9 +- app/screens/apps_form/apps_form_container.tsx | 3 + app/screens/apps_form/index.ts | 2 + .../channel_info/bindings/bindings.tsx | 14 ++- app/screens/channel_info/bindings/index.ts | 2 + .../post_options/bindings/bindings.tsx | 95 +++++++++++-------- app/screens/post_options/bindings/index.ts | 2 + 16 files changed, 149 insertions(+), 97 deletions(-) diff --git a/app/actions/apps.ts b/app/actions/apps.ts index f741b0044..6e3db6182 100644 --- a/app/actions/apps.ts +++ b/app/actions/apps.ts @@ -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(call: AppCallRequest, type: AppCallType, intl: any): ActionFunc { - return async (dispatch, getState) => { + return async () => { try { const res = await Client4.executeAppCall(call, type) as AppCallResponse; const responseType = res.type || AppCallResponseTypes.OK; @@ -37,11 +30,6 @@ export function doAppCall(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(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(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( diff --git a/app/actions/navigation/index.js b/app/actions/navigation/index.js index df0944533..5a18edc24 100644 --- a/app/actions/navigation/index.js +++ b/app/actions/navigation/index.js @@ -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; diff --git a/app/actions/views/command.ts b/app/actions/views/command.ts index 2b4e876a4..cae61af86 100644 --- a/app/actions/views/command.ts +++ b/app/actions/views/command.ts @@ -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', diff --git a/app/components/post_draft/draft_input/draft_input.js b/app/components/post_draft/draft_input/draft_input.js index 7df3d5c0d..4dd8654fe 100644 --- a/app/components/post_draft/draft_input/draft_input.js +++ b/app/components/post_draft/draft_input/draft_input.js @@ -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(''); diff --git a/app/components/post_list/post/body/content/embedded_bindings/button_binding/button_binding.tsx b/app/components/post_list/post/body/content/embedded_bindings/button_binding/button_binding.tsx index 35e1320b4..f00130837 100644 --- a/app/components/post_list/post/body/content/embedded_bindings/button_binding/button_binding.tsx +++ b/app/components/post_list/post/body/content/embedded_bindings/button_binding/button_binding.tsx @@ -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; 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 (