From 2d4c8533487ed81ddd99063124d312854ef4bae2 Mon Sep 17 00:00:00 2001 From: Ian McEwen Date: Fri, 11 Oct 2024 13:35:31 -0700 Subject: [PATCH] Initial display of app & resource info when launching instant launches --- .../InstantLaunchButtonWrapper.js | 73 +++++++++++++------ src/components/instantlaunches/index.js | 12 ++- src/serviceFacades/instantlaunches.js | 45 ++++++++---- 3 files changed, 94 insertions(+), 36 deletions(-) diff --git a/src/components/instantlaunches/InstantLaunchButtonWrapper.js b/src/components/instantlaunches/InstantLaunchButtonWrapper.js index 3886f7636..12c19b58f 100644 --- a/src/components/instantlaunches/InstantLaunchButtonWrapper.js +++ b/src/components/instantlaunches/InstantLaunchButtonWrapper.js @@ -10,7 +10,7 @@ */ import React, { useEffect, useCallback } from "react"; -import { useMutation } from "react-query"; +import { useMutation, useQuery } from "react-query"; import { useDefaultOutputDir } from "components/data/utils"; import withErrorAnnouncer from "components/error/withErrorAnnouncer"; @@ -25,7 +25,14 @@ import { useUserProfile } from "contexts/userProfile"; import { useBootstrapInfo } from "contexts/bootstrap"; import ids from "./ids"; import { InstantLaunchSubmissionDialog } from "./index"; -import { instantlyLaunch } from "serviceFacades/instantlaunches"; +import { + instantlyLaunch, + extractLaunchId, +} from "serviceFacades/instantlaunches"; +import { + SAVED_LAUNCH_APP_INFO, + getAppInfo, +} from "serviceFacades/savedLaunches"; import { useTranslation } from "i18n"; import { ERROR_CODES } from "components/error/errorCode"; @@ -44,6 +51,8 @@ function InstantLaunchButtonWrapper(props) { const [open, setOpen] = React.useState(false); const [hasLaunched, setHasLaunched] = React.useState(false); + const [appInfo, setAppInfo] = React.useState(null); + const [signInDlgOpen, setSignInDlgOpen] = React.useState(false); const [accessRequestDialogOpen, setAccessRequestDialogOpen] = React.useState(false); @@ -62,6 +71,19 @@ function InstantLaunchButtonWrapper(props) { } }, [ilUrl]); + const launchId = extractLaunchId(instantLaunch); + + const { isFetching: savedLaunchLoading } = useQuery({ + queryKey: [SAVED_LAUNCH_APP_INFO, { launchId }], + queryFn: () => getAppInfo({ launchId }), + enabled: open, + onSuccess: setAppInfo, + onError: (err) => { + setOpen(false); + showErrorAnnouncer(err.message, err); + }, + }); + const { mutate: launch } = useMutation(instantlyLaunch, { onSuccess: (listing) => { if (listing.analyses.length > 0) { @@ -106,45 +128,54 @@ function InstantLaunchButtonWrapper(props) { const preferences = bootstrapInfo?.preferences; const userId = userProfile?.id; + useEffect(() => { + if (open && !savedLaunchLoading) { + launch({ + instantLaunch, + resource, + output_dir, + preferences, + appInfo, + }); + } + }, [ + open, + savedLaunchLoading, + instantLaunch, + launch, + resource, + output_dir, + preferences, + appInfo, + ]); + const onClick = useCallback(() => { if (userId) { if (computeLimitExceeded) { showErrorAnnouncer(t("computeLimitExceededMsg")); } else { setOpen(true); - launch({ - instantLaunch, - resource, - output_dir, - preferences, - }); } } else { setSignInDlgOpen(true); } - }, [ - preferences, - computeLimitExceeded, - instantLaunch, - launch, - output_dir, - resource, - showErrorAnnouncer, - t, - userId, - ]); + }, [computeLimitExceeded, showErrorAnnouncer, t, userId]); useEffect(() => { if (autolaunch && !hasLaunched) { onClick(); setHasLaunched(true); } - }, [autolaunch, onClick, hasLaunched, setHasLaunched]); + }, [autolaunch, onClick, hasLaunched]); return ( <> {!autolaunch && render && render(onClick)} - + setSignInDlgOpen(false)} diff --git a/src/components/instantlaunches/index.js b/src/components/instantlaunches/index.js index 3573839ee..652bbbb6d 100644 --- a/src/components/instantlaunches/index.js +++ b/src/components/instantlaunches/index.js @@ -37,7 +37,11 @@ const useStyles = makeStyles()((theme) => ({ * @param {Object} props - The component props. * @param {boolean} props.open - Whether or not the dialog is open. */ -export const InstantLaunchSubmissionDialog = ({ open }) => { +export const InstantLaunchSubmissionDialog = ({ + open, + appInfo, + resource = null, +}) => { const baseID = buildID(ids.BASE, ids.SUBMISSION, ids.DIALOG); const { t } = useTranslation("instantlaunches"); const { classes } = useStyles(); @@ -57,6 +61,12 @@ export const InstantLaunchSubmissionDialog = ({ open }) => { + {appInfo && ( + + Launching app: {appInfo.name} + {resource ? " with data " + resource.path : ""} + + )}
{ ); }; +/** + * Utility function to extract a saved launch ID from an instant launch in either format. + * + * @param {Object} instantLaunch - the instant launch object to pull the value out of + */ +export const extractLaunchId = (instantLaunch) => { + if (instantLaunch.hasOwnProperty("default")) { + // The data window logic. + // The saved launch ID is buried in the "default" map of the object passed in + // from the data window. + return instantLaunch.default["quick_launch_id"]; + } else { + // The dashboard logic. + // The saved launch ID is a top-level property of the object passed in. + return instantLaunch.quick_launch_id; + } +}; + /** * Event handler for the button that performs the instant launch. * @@ -337,27 +355,21 @@ export const instantlyLaunch = ({ resource, output_dir, preferences, + appInfo = null, }) => { - let savedLaunchId; // The saved launch ID, used to get app information. + let savedLaunchId = extractLaunchId(instantLaunch); // The saved launch ID, used to get app information. + let savedLaunchPromise; // The promise used to get saved launch information. + let appInfoPromise; // The promise used to get app info for the saved launch. // The format of the instantLaunch object passed in by the data window is a bit different // than the format passed in by the dashboard, so a bit of normalization needs to take // place. if (instantLaunch.hasOwnProperty("default")) { - // The data window logic. - // The saved launch ID is buried in the "default" map of the object passed in - // from the data window. - savedLaunchId = instantLaunch.default["quick_launch_id"]; - // We'll need to get the saved launch info from the API since it contains the // submission, which isn't provided from the data window. savedLaunchPromise = getSavedLaunch(savedLaunchId); } else { - // The dashboard logic. - // The saved launch ID is a top-level property of the object passed in. - savedLaunchId = instantLaunch.quick_launch_id; - // Wrap the instant launch object in a promise so we don't have to branch logic // farther down. savedLaunchPromise = new Promise((resolve, reject) => { @@ -365,11 +377,16 @@ export const instantlyLaunch = ({ }); } + if (appInfo) { + appInfoPromise = new Promise((resolve, reject) => { + resolve(appInfo); + }); + } else { + appInfoPromise = getAppInfo({ launchId: savedLaunchId }); + } + // Contains the Promises that resolve to the data needed to perform a job submission. - const promiseList = [ - savedLaunchPromise, - getAppInfo({ launchId: savedLaunchId }), - ]; + const promiseList = [savedLaunchPromise, appInfoPromise]; return Promise.all(promiseList) .then((values) => {