diff --git a/app/client/src/actions/apiPaneActions.ts b/app/client/src/actions/apiPaneActions.ts index 36cb77d357..d968f86042 100644 --- a/app/client/src/actions/apiPaneActions.ts +++ b/app/client/src/actions/apiPaneActions.ts @@ -13,3 +13,10 @@ export const initApiPane = (urlId?: string): ReduxAction<{ id?: string }> => { payload: { id: urlId }, }; }; + +export const createNewApiAction = ( + pageId: string, +): ReduxAction<{ pageId: string }> => ({ + type: ReduxActionTypes.CREATE_NEW_API_ACTION, + payload: { pageId }, +}); diff --git a/app/client/src/constants/ReduxActionConstants.tsx b/app/client/src/constants/ReduxActionConstants.tsx index 825636e2e0..9f8b7e1337 100644 --- a/app/client/src/constants/ReduxActionConstants.tsx +++ b/app/client/src/constants/ReduxActionConstants.tsx @@ -176,6 +176,7 @@ export const ReduxActionTypes: { [key: string]: string } = { CLEAR_PROVIDERS: "CLEAR_PROVIDERS", BATCHED_UPDATE: "BATCHED_UPDATE", EXECUTE_BATCH: "EXECUTE_BATCH", + CREATE_NEW_API_ACTION: "CREATE_NEW_API_ACTION", }; export type ReduxActionType = typeof ReduxActionTypes[keyof typeof ReduxActionTypes]; diff --git a/app/client/src/pages/Editor/APIEditor/ApiHomeScreen.tsx b/app/client/src/pages/Editor/APIEditor/ApiHomeScreen.tsx index 1a55b5e3d5..f1c2222c06 100644 --- a/app/client/src/pages/Editor/APIEditor/ApiHomeScreen.tsx +++ b/app/client/src/pages/Editor/APIEditor/ApiHomeScreen.tsx @@ -5,11 +5,7 @@ import { reduxForm, InjectedFormProps, getFormValues } from "redux-form"; import { Icon, Card } from "@blueprintjs/core"; import styled from "styled-components"; import InfiniteScroll from "react-infinite-scroller"; -import { - DEFAULT_API_ACTION, - DEFAULT_PROVIDER_OPTION, - REST_PLUGIN_PACKAGE_NAME, -} from "constants/ApiEditorConstants"; +import { DEFAULT_PROVIDER_OPTION } from "constants/ApiEditorConstants"; import { getCurlImportPageURL, getProviderTemplatesURL, @@ -35,7 +31,6 @@ import { fetchProvidersWithCategory, clearProviders, } from "actions/providerActions"; -import { createNewApiName } from "utils/AppsmithUtils"; import { Colors } from "constants/Colors"; import CenteredWrapper from "components/designSystems/appsmith/CenteredWrapper"; // import { BaseTextInput } from "components/designSystems/appsmith/TextInputComponent"; @@ -46,7 +41,7 @@ import Spinner from "components/editorComponents/Spinner"; import CurlLogo from "assets/images/Curl-logo.svg"; import { FetchProviderWithCategoryRequest } from "api/ProvidersApi"; import { Plugin } from "api/PluginApi"; -import _ from "lodash"; +import { createNewApiAction } from "actions/apiPaneActions"; // const SearchContainer = styled.div` // display: flex; @@ -241,6 +236,7 @@ type ApiHomeScreenProps = { isFetchingProviders: boolean; providersTotal: number; isSwitchingCategory: boolean; + createNewApiAction: (pageId: string) => void; }; type ApiHomeScreenState = { @@ -283,20 +279,9 @@ class ApiHomeScreen extends React.Component { } handleCreateNew = (params: string) => { - const { actions, plugins } = this.props; const pageId = new URLSearchParams(params).get("importTo"); - const plugin = _.find(plugins, { packageName: REST_PLUGIN_PACKAGE_NAME }); - if (pageId && plugin) { - const newActionName = createNewApiName(actions, pageId); - this.props.createAction({ - ...DEFAULT_API_ACTION, - name: newActionName, - datasource: { - name: "DEFAULT_REST_DATASOURCE", - pluginId: plugin.id, - }, - pageId, - }); + if (pageId) { + this.props.createNewApiAction(pageId); } }; @@ -560,6 +545,7 @@ const mapDispatchToProps = (dispatch: any) => ({ dispatch(fetchProvidersWithCategory(request)), createAction: (data: Partial) => dispatch(createActionRequest(data)), + createNewApiAction: (pageId: string) => dispatch(createNewApiAction(pageId)), }); export default connect( diff --git a/app/client/src/pages/Editor/APIEditor/index.tsx b/app/client/src/pages/Editor/APIEditor/index.tsx index 0527886847..4a092cb09a 100644 --- a/app/client/src/pages/Editor/APIEditor/index.tsx +++ b/app/client/src/pages/Editor/APIEditor/index.tsx @@ -24,6 +24,16 @@ import AnalyticsUtil from "utils/AnalyticsUtil"; import { getCurrentPageName, getActionById } from "selectors/editorSelectors"; import { Plugin } from "api/PluginApi"; import { ActionData } from "reducers/entityReducers/actionsReducer"; +import { API_PANE_V2, checkForFlag } from "utils/featureFlags"; +import styled from "styled-components"; + +const EmptyStateContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; + height: 100%; + font-size: 20px; +`; interface ReduxStateProps { actions: ActionDataState; @@ -139,6 +149,21 @@ class ApiEditor extends React.Component { const { isSaving, isRunning, isDeleting, drafts } = apiPane; const paginationType = _.get(data, "actionConfiguration.paginationType"); + const apiHomeScreen = ( + + ); + const defaultHomeScreen = ( + + {"Create / Select an API from the list"} + + ); + const v2Flag = checkForFlag(API_PANE_V2); + const homeScreen = v2Flag ? apiHomeScreen : defaultHomeScreen; return ( {apiId ? ( @@ -183,12 +208,7 @@ class ApiEditor extends React.Component { )} ) : ( - + homeScreen )} ); diff --git a/app/client/src/pages/Editor/ApiSidebar.tsx b/app/client/src/pages/Editor/ApiSidebar.tsx index b059280003..c8c689fe23 100644 --- a/app/client/src/pages/Editor/ApiSidebar.tsx +++ b/app/client/src/pages/Editor/ApiSidebar.tsx @@ -12,7 +12,11 @@ import { copyActionRequest, deleteAction, } from "actions/actionActions"; -import { changeApi, initApiPane } from "actions/apiPaneActions"; +import { + changeApi, + createNewApiAction, + initApiPane, +} from "actions/apiPaneActions"; import { RestAction } from "api/ActionAPI"; import { getPluginIdOfName } from "selectors/entitiesSelector"; import { PLUGIN_NAME } from "constants/ApiEditorConstants"; @@ -20,6 +24,7 @@ import EditorSidebar from "pages/Editor/EditorSidebar"; import { getNextEntityName } from "utils/AppsmithUtils"; import AnalyticsUtil from "utils/AnalyticsUtil"; import { API_EDITOR_URL_WITH_SELECTED_PAGE_ID } from "constants/routes"; +import { API_PANE_V2, checkForFlag } from "utils/featureFlags"; const HTTPMethod = styled.span<{ method?: string }>` flex: 1; @@ -73,6 +78,7 @@ interface ReduxDispatchProps { ) => void; copyAction: (id: string, pageId: string, name: string) => void; deleteAction: (id: string, name: string) => void; + createNewApiAction: (pageId: string) => void; } type Props = ReduxStateProps & @@ -159,15 +165,20 @@ class ApiSidebar extends React.Component { }; handleCreateNewApiClick = (selectedPageId: string) => { - const { history } = this.props; + const { history, createNewApiAction } = this.props; const { pageId, applicationId } = this.props.match.params; - history.push( - API_EDITOR_URL_WITH_SELECTED_PAGE_ID( - applicationId, - pageId, - selectedPageId, - ), - ); + const v2Flag = checkForFlag(API_PANE_V2); + if (v2Flag) { + history.push( + API_EDITOR_URL_WITH_SELECTED_PAGE_ID( + applicationId, + pageId, + selectedPageId, + ), + ); + } else { + createNewApiAction(selectedPageId); + } }; render() { @@ -223,6 +234,7 @@ const mapDispatchToProps = (dispatch: Function): ReduxDispatchProps => ({ dispatch(copyActionRequest({ id, destinationPageId, name })), deleteAction: (id: string, name: string) => dispatch(deleteAction({ id, name })), + createNewApiAction: (pageId: string) => dispatch(createNewApiAction(pageId)), }); export default connect(mapStateToProps, mapDispatchToProps)(ApiSidebar); diff --git a/app/client/src/sagas/ApiPaneSagas.ts b/app/client/src/sagas/ApiPaneSagas.ts index ca4098f398..37e051312c 100644 --- a/app/client/src/sagas/ApiPaneSagas.ts +++ b/app/client/src/sagas/ApiPaneSagas.ts @@ -11,7 +11,11 @@ import { } from "constants/ReduxActionConstants"; import { getFormData } from "selectors/formSelectors"; import { API_EDITOR_FORM_NAME, API_HOME_SCREEN_FORM } from "constants/forms"; -import { POST_BODY_FORMAT_OPTIONS } from "constants/ApiEditorConstants"; +import { + DEFAULT_API_ACTION, + POST_BODY_FORMAT_OPTIONS, + REST_PLUGIN_PACKAGE_NAME, +} from "constants/ApiEditorConstants"; import history from "utils/history"; import { API_EDITOR_ID_URL, API_EDITOR_URL } from "constants/routes"; import { @@ -31,6 +35,11 @@ import { VALID_FUNCTION_NAME_ERROR, } from "constants/messages"; import { fetchProvidersWithCategory } from "actions/providerActions"; +import { createNewApiName } from "utils/AppsmithUtils"; +import { getPluginIdOfPackageName } from "sagas/selectors"; +import { getActions } from "selectors/entitiesSelector"; +import { ActionData } from "reducers/entityReducers/actionsReducer"; +import { createActionRequest } from "actions/actionActions"; const getApiDraft = (state: AppState, id: string) => { const drafts = state.ui.apiPane.drafts; @@ -38,7 +47,7 @@ const getApiDraft = (state: AppState, id: string) => { return {}; }; -const getActions = (state: AppState) => +const getActionConfigs = (state: AppState): ActionData["config"][] => state.entities.actions.map(a => a.config); const getLastUsedAction = (state: AppState) => state.ui.apiPane.lastUsed; @@ -234,7 +243,7 @@ function* validateInputSaga( payload, meta: { field }, } = actionPayload; - const actions: RestAction[] = yield select(getActions); + const actions: RestAction[] = yield select(getActionConfigs); const sameNames = actions.filter( (action: RestAction) => action.name === payload && action.id, ); @@ -375,6 +384,34 @@ function* handleMoveOrCopySaga(actionPayload: ReduxAction<{ id: string }>) { } } +function* handleCreateNewApiActionSaga( + action: ReduxAction<{ pageId: string }>, +) { + const pluginId = yield select( + getPluginIdOfPackageName, + REST_PLUGIN_PACKAGE_NAME, + ); + const { pageId } = action.payload; + if (pageId && pluginId) { + const actions = yield select(getActions); + const pageActions = actions.filter( + (a: ActionData) => a.config.pageId === pageId, + ); + const newActionName = createNewApiName(pageActions, pageId); + yield put( + createActionRequest({ + ...DEFAULT_API_ACTION, + name: newActionName, + datasource: { + name: "DEFAULT_REST_DATASOURCE", + pluginId, + }, + pageId, + }), + ); + } +} + export default function* root() { yield all([ takeEvery(ReduxActionTypes.INIT_API_PANE, initApiPaneSaga), @@ -384,6 +421,10 @@ export default function* root() { takeEvery(ReduxActionTypes.DELETE_ACTION_SUCCESS, handleActionDeletedSaga), takeEvery(ReduxActionTypes.MOVE_ACTION_SUCCESS, handleMoveOrCopySaga), takeEvery(ReduxActionTypes.COPY_ACTION_SUCCESS, handleMoveOrCopySaga), + takeEvery( + ReduxActionTypes.CREATE_NEW_API_ACTION, + handleCreateNewApiActionSaga, + ), // Intercepting the redux-form change actionType takeEvery(ReduxFormActionTypes.VALUE_CHANGE, formValueChangeSaga), takeEvery(ReduxFormActionTypes.ARRAY_REMOVE, formValueChangeSaga), diff --git a/app/client/src/sagas/selectors.tsx b/app/client/src/sagas/selectors.tsx index ccfc96b44f..70eba2cf7b 100644 --- a/app/client/src/sagas/selectors.tsx +++ b/app/client/src/sagas/selectors.tsx @@ -74,3 +74,13 @@ export const getWidgetByName = ( widget => widget.widgetName === widgetName, ); }; + +export const getPluginIdOfPackageName = ( + state: AppState, + name: string, +): string | undefined => { + const plugins = state.entities.plugins.list; + const plugin = _.find(plugins, { packageName: name }); + if (plugin) return plugin.id; + return undefined; +}; diff --git a/app/client/src/utils/featureFlags.ts b/app/client/src/utils/featureFlags.ts new file mode 100644 index 0000000000..cd7c87cedc --- /dev/null +++ b/app/client/src/utils/featureFlags.ts @@ -0,0 +1,5 @@ +export const API_PANE_V2 = "ApiPaneV2"; + +export const checkForFlag = (flagName: string) => { + return localStorage.getItem(flagName); +};