import useListSettings from "@saleor/hooks/useListSettings"; import useLocalStorage from "@saleor/hooks/useLocalStorage"; import useNavigator from "@saleor/hooks/useNavigator"; import useNotifier from "@saleor/hooks/useNotifier"; import usePaginator, { createPaginationState } from "@saleor/hooks/usePaginator"; import { ListViews } from "@saleor/types"; import getAppErrorMessage from "@saleor/utils/errors/app"; import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers"; import React, { useEffect, useRef } from "react"; import { useIntl } from "react-intl"; import { AppSortField, AppTypeEnum, JobStatusEnum, OrderDirection } from "../../../types/globalTypes"; import AppDeleteDialog from "../../components/AppDeleteDialog"; import AppInProgressDeleteDialog from "../../components/AppInProgressDeleteDialog"; import AppsListPage from "../../components/AppsListPage"; import { useAppDeleteFailedInstallationMutation, useAppDeleteMutation, useAppRetryInstallMutation } from "../../mutations"; import { useAppsInProgressListQuery, useAppsListQuery } from "../../queries"; import { AppDelete } from "../../types/AppDelete"; import { AppDeleteFailedInstallation } from "../../types/AppDeleteFailedInstallation"; import { AppsInstallations_appsInstallations } from "../../types/AppsInstallations"; import { AppsList_apps_edges } from "../../types/AppsList"; import { AppListUrlDialog, AppListUrlQueryParams, appSettingsUrl, appsListUrl, appUrl, customAppAddUrl, customAppUrl } from "../../urls"; const getCurrentAppName = (id: string, collection?: AppsList_apps_edges[]) => collection?.find(edge => edge.node.id === id)?.node?.name; const getAppInProgressName = ( id: string, collection?: AppsInstallations_appsInstallations[] ) => collection?.find(app => app.id === id)?.appName; interface AppsListProps { params: AppListUrlQueryParams; } export const AppsList: React.FC = ({ params }) => { const { action } = params; const [activeInstallations, setActiveInstallations] = useLocalStorage( "activeInstallations", [] ); const notify = useNotifier(); const intl = useIntl(); const navigate = useNavigator(); const { updateListSettings, settings } = useListSettings(ListViews.APPS_LIST); const paginate = usePaginator(); const paginationState = createPaginationState(settings?.rowNumber, params); const queryVariables = { sort: { direction: OrderDirection.DESC, field: AppSortField.CREATION_DATE } }; const intervalId = useRef(null); const removeInstallation = (id: string) => setActiveInstallations(installations => installations.filter(item => item.id !== id) ); const { data: appsInProgressData, loading: loadingAppsInProgress, refetch: appsInProgressRefetch } = useAppsInProgressListQuery({ displayLoader: false }); const { data, loading, refetch } = useAppsListQuery({ displayLoader: true, variables: { ...paginationState, ...queryVariables, filter: { type: AppTypeEnum.THIRDPARTY } } }); const { loadNextPage, loadPreviousPage, pageInfo } = paginate( data?.apps?.pageInfo, paginationState, params ); const { data: customAppsData, loading: customAppsLoading, refetch: customAppsRefetch } = useAppsListQuery({ displayLoader: true, variables: { first: 100, ...queryVariables, filter: { type: AppTypeEnum.LOCAL } } }); const installedAppNotify = (name: string) => { notify({ status: "success", text: intl.formatMessage( { defaultMessage: "{name} is ready to be used", description: "app has been installed" }, { name } ), title: intl.formatMessage({ defaultMessage: "App installed", description: "message title" }) }); }; const [retryInstallApp] = useAppRetryInstallMutation({ onCompleted: data => { const errors = data.appRetryInstall.errors; if (!errors.length) { const appInstallation = data.appRetryInstall.appInstallation; setActiveInstallations(installations => [ ...installations, { id: appInstallation.id, name: appInstallation.appName } ]); } else { errors.forEach(error => notify({ status: "error", text: getAppErrorMessage(error, intl) }) ); } } }); const [openModal, closeModal] = createDialogActionHandlers< AppListUrlDialog, AppListUrlQueryParams >(navigate, appsListUrl, params); const onAppRemove = (data: AppDelete) => { const errors = data.appDelete.errors; if (errors.length === 0) { if (data.appDelete.app.type === AppTypeEnum.LOCAL) { customAppsRefetch(); } else { refetch(); } closeModal(); removeAppNotify(); } else { errors.forEach(error => notify({ status: "error", text: getAppErrorMessage(error, intl) }) ); } }; const [deleteApp, deleteAppOpts] = useAppDeleteMutation({ onCompleted: data => { onAppRemove(data); } }); const [ deleteInProgressApp, deleteInProgressAppOpts ] = useAppDeleteFailedInstallationMutation({ onCompleted: data => { onAppInProgressRemove(data); } }); useEffect(() => { const appsInProgress = appsInProgressData?.appsInstallations || []; if (activeInstallations.length && !!appsInProgressData) { if (!intervalId.current) { intervalId.current = window.setInterval( () => appsInProgressRefetch(), 2000 ); } activeInstallations.forEach(installation => { const item = appsInProgress?.find(app => app.id === installation.id); if (!item) { removeInstallation(installation.id); installedAppNotify(installation.name); appsInProgressRefetch(); } else if (item.status === JobStatusEnum.SUCCESS) { removeInstallation(installation.id); installedAppNotify(item.appName); refetch(); } else if (item.status === JobStatusEnum.FAILED) { removeInstallation(installation.id); notify({ status: "error", text: item.message, title: intl.formatMessage( { defaultMessage: "Couldn’t Install {name}", description: "message title" }, { name: item.appName } ) }); } }); } if (!activeInstallations.length && intervalId.current) { clearInterval(intervalId.current); intervalId.current = null; } return () => { if (intervalId.current) { clearInterval(intervalId.current); intervalId.current = null; } }; }, [activeInstallations.length, appsInProgressData]); const handleRemoveInProgressConfirm = () => deleteInProgressApp({ variables: { id: params.id } }); const handleRemoveConfirm = () => deleteApp({ variables: { id: params.id } }); const removeAppNotify = () => { notify({ status: "success", text: intl.formatMessage({ defaultMessage: "App successfully removed", description: "app has been removed" }) }); }; const onAppInProgressRemove = (data: AppDeleteFailedInstallation) => { const errors = data.appDeleteFailedInstallation.errors; if (errors.length === 0) { removeAppNotify(); appsInProgressRefetch(); closeModal(); } else { errors.forEach(error => notify({ status: "error", text: getAppErrorMessage(error, intl) }) ); } }; const onAppInstallRetry = (id: string) => retryInstallApp({ variables: { id } }); const installedApps = data?.apps?.edges; const customApps = customAppsData?.apps?.edges; return ( <> () => navigate(appUrl(id))} onSettingsRowClick={id => () => navigate(appSettingsUrl(id))} onAppInstallRetry={onAppInstallRetry} navigateToCustomApp={id => () => navigate(customAppUrl(id))} navigateToCustomAppCreate={() => navigate(customAppAddUrl)} onInstalledAppRemove={id => openModal("remove-app", { id }) } onCustomAppRemove={id => openModal("remove-custom-app", { id }) } onAppInProgressRemove={id => openModal("remove", { id }) } /> ); }; export default AppsList;