import React, { useState, useMemo, useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { getUserApplicationsWorkspaces, getIsFetchingApplications, } from "@appsmith/selectors/applicationSelectors"; import { hasCreateNewAppPermission } from "@appsmith/utils/permissionHelpers"; import { ReduxActionTypes } from "@appsmith/constants/ReduxActionConstants"; import type { AppState } from "@appsmith/reducers"; import { Button, Modal, ModalContent, ModalHeader, Spinner, Select, Option, } from "design-system"; import { ButtonWrapper, SpinnerWrapper } from "./ForkModalStyles"; import { CANCEL, createMessage, FORK, FORK_APP_MODAL_EMPTY_TITLE, FORK_APP_MODAL_LOADING_TITLE, FORK_APP_MODAL_SUCCESS_TITLE, } from "@appsmith/constants/messages"; import { getAllApplications } from "@appsmith/actions/applicationActions"; interface ForkApplicationModalProps { applicationId: string; // if a trigger is passed // it renders that component trigger?: React.ReactNode; isModalOpen?: boolean; handleOpen?: () => void; handleClose?: () => void; isInEditMode?: boolean; } function ForkApplicationModal(props: ForkApplicationModalProps) { const { handleClose, handleOpen, isModalOpen } = props; const [workspace, selectWorkspace] = useState<{ label: string; value: string; }>({ label: "", value: "" }); const dispatch = useDispatch(); const userWorkspaces = useSelector(getUserApplicationsWorkspaces); const forkingApplication = useSelector( (state: AppState) => state.ui.applications.forkingApplication, ); const isFetchingApplications = useSelector(getIsFetchingApplications); useEffect(() => { // This effect makes sure that no if // is getting controlled from outside, then we always load workspaces if (isModalOpen) { getApplicationsListAndOpenModal(); return; } }, [isModalOpen]); useEffect(() => { // when we fork from within the appeditor, fork modal remains open // even on the landing page of "Forked" app, this closes it const url = new URL(window.location.href); const shouldCloseForcibly = !forkingApplication && isModalOpen && handleClose && !url.searchParams.has("fork"); if (shouldCloseForcibly) { handleClose(); } }, [forkingApplication]); const forkApplication = () => { dispatch({ type: ReduxActionTypes.FORK_APPLICATION_INIT, payload: { applicationId: props.applicationId, workspaceId: workspace?.value, editMode: props.isInEditMode, }, }); }; const workspaceList = useMemo(() => { const filteredUserWorkspaces = userWorkspaces.filter((item) => { const permitted = hasCreateNewAppPermission( item.workspace.userPermissions ?? [], ); return permitted; }); if (filteredUserWorkspaces.length) { selectWorkspace({ label: filteredUserWorkspaces[0].workspace.name, value: filteredUserWorkspaces[0].workspace.id, }); } return filteredUserWorkspaces.map((workspace) => { return { label: workspace.workspace.name, value: workspace.workspace.id, }; }); }, [userWorkspaces]); const modalHeading = isFetchingApplications ? createMessage(FORK_APP_MODAL_LOADING_TITLE) : !workspaceList.length ? createMessage(FORK_APP_MODAL_EMPTY_TITLE) : createMessage(FORK_APP_MODAL_SUCCESS_TITLE); const getApplicationsListAndOpenModal = () => { !workspaceList.length && dispatch(getAllApplications()); handleOpen && handleOpen(); }; const handleOnOpenChange = (isOpen: boolean) => { if (isOpen) { getApplicationsListAndOpenModal(); } else { handleClose && handleClose(); } }; return ( {modalHeading} {isFetchingApplications ? ( ) : ( !!workspaceList.length && ( <> ) )} ); } export default ForkApplicationModal;