From 2a5fc12ca17b3125944969db5c20e0b27b51a062 Mon Sep 17 00:00:00 2001 From: David Crespo Date: Fri, 9 Aug 2024 15:43:45 -0500 Subject: [PATCH] make instance list polling not do weirdo stuff to the row actions menu --- app/pages/project/instances/InstancesPage.tsx | 24 +++++------- app/pages/project/instances/actions.tsx | 39 ++++++++++++------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/app/pages/project/instances/InstancesPage.tsx b/app/pages/project/instances/InstancesPage.tsx index 1271f49b52..bf7b7d2c9d 100644 --- a/app/pages/project/instances/InstancesPage.tsx +++ b/app/pages/project/instances/InstancesPage.tsx @@ -9,15 +9,10 @@ import { createColumnHelper } from '@tanstack/react-table' import { useMemo } from 'react' import { useNavigate, type LoaderFunctionArgs } from 'react-router-dom' -import { - apiQueryClient, - useApiQueryClient, - usePrefetchedApiQuery, - type Instance, -} from '@oxide/api' +import { apiQueryClient, usePrefetchedApiQuery, type Instance } from '@oxide/api' import { Instances16Icon, Instances24Icon } from '@oxide/design-system/icons/react' -// import { instanceTransitioning } from '~/api/util' +import { instanceTransitioning } from '~/api/util' import { DocsPopover } from '~/components/DocsPopover' import { RefreshButton } from '~/components/RefreshButton' import { getProjectSelector, useProjectSelector, useQuickActions } from '~/hooks' @@ -31,7 +26,7 @@ import { CreateLink } from '~/ui/lib/CreateButton' import { EmptyMessage } from '~/ui/lib/EmptyMessage' import { PageHeader, PageTitle } from '~/ui/lib/PageHeader' import { TableActions } from '~/ui/lib/Table' -// import { useInterval } from '~/ui/lib/use-interval' +import { useInterval } from '~/ui/lib/use-interval' import { docLinks } from '~/util/links' import { pb } from '~/util/path-builder' @@ -57,12 +52,11 @@ InstancesPage.loader = async ({ params }: LoaderFunctionArgs) => { return null } +const refetchInstances = () => apiQueryClient.invalidateQueries('instanceList') + export function InstancesPage() { const { project } = useProjectSelector() - const queryClient = useApiQueryClient() - const refetchInstances = () => queryClient.invalidateQueries('instanceList') - const makeActions = useMakeInstanceActions( { project }, { onSuccess: refetchInstances, onDelete: refetchInstances } @@ -75,10 +69,10 @@ export function InstancesPage() { // if any instance in the list is transitioning, poll // TODO: figure out this logic. polling requests have the horrible effect of close // any open row actions menus - // useInterval({ - // fn: () => apiQueryClient.invalidateQueries('instanceList'), - // delay: instances.items.some(instanceTransitioning) ? 1000 : null, - // }) + useInterval({ + fn: () => apiQueryClient.invalidateQueries('instanceList'), + delay: instances.items.some(instanceTransitioning) ? 1000 : null, + }) const navigate = useNavigate() useQuickActions( diff --git a/app/pages/project/instances/actions.tsx b/app/pages/project/instances/actions.tsx index f0c91316fd..363dfb1e93 100644 --- a/app/pages/project/instances/actions.tsx +++ b/app/pages/project/instances/actions.tsx @@ -29,29 +29,35 @@ type Options = { } export const useMakeInstanceActions = ( - projectSelector: { project: string }, + { project }: { project: string }, options: Options = {} ): MakeActions => { const navigate = useNavigate() // if you also pass onSuccess to mutate(), this one is not overridden — this - // one runs first, then the one passed to mutate() + // one runs first, then the one passed to mutate(). + // + // We pull out the mutate functions because they are referentially stable, + // while the whole useMutation result object is not. The async ones are used + // when we need to confirm because the confirm modals want that. const opts = { onSuccess: options.onSuccess } - const startInstance = useApiMutation('instanceStart', opts) - const stopInstance = useApiMutation('instanceStop', opts) - const rebootInstance = useApiMutation('instanceReboot', opts) + const { mutate: startInstance } = useApiMutation('instanceStart', opts) + const { mutateAsync: stopInstanceAsync } = useApiMutation('instanceStop', opts) + const { mutate: rebootInstance } = useApiMutation('instanceReboot', opts) // delete has its own - const deleteInstance = useApiMutation('instanceDelete', { onSuccess: options.onDelete }) + const { mutateAsync: deleteInstanceAsync } = useApiMutation('instanceDelete', { + onSuccess: options.onDelete, + }) return useCallback( (instance) => { - const instanceSelector = { ...projectSelector, instance: instance.name } - const instanceParams = { path: { instance: instance.name }, query: projectSelector } + const instanceSelector = { project, instance: instance.name } + const instanceParams = { path: { instance: instance.name }, query: { project } } return [ { label: 'Start', onActivate() { - startInstance.mutate(instanceParams, { + startInstance(instanceParams, { onSuccess: () => addToast({ title: `Starting instance '${instance.name}'` }), onError: (error) => addToast({ @@ -71,7 +77,7 @@ export const useMakeInstanceActions = ( confirmAction({ actionType: 'danger', doAction: () => - stopInstance.mutateAsync(instanceParams, { + stopInstanceAsync(instanceParams, { onSuccess: () => addToast({ title: `Stopping instance '${instance.name}'` }), }), @@ -93,7 +99,7 @@ export const useMakeInstanceActions = ( { label: 'Reboot', onActivate() { - rebootInstance.mutate(instanceParams, { + rebootInstance(instanceParams, { onSuccess: () => addToast({ title: `Rebooting instance '${instance.name}'` }), onError: (error) => addToast({ @@ -117,7 +123,7 @@ export const useMakeInstanceActions = ( label: 'Delete', onActivate: confirmDelete({ doDelete: () => - deleteInstance.mutateAsync(instanceParams, { + deleteInstanceAsync(instanceParams, { onSuccess: () => addToast({ title: `Deleting instance '${instance.name}'` }), }), @@ -132,6 +138,13 @@ export const useMakeInstanceActions = ( }, ] }, - [projectSelector, deleteInstance, navigate, rebootInstance, startInstance, stopInstance] + [ + project, + navigate, + deleteInstanceAsync, + rebootInstance, + startInstance, + stopInstanceAsync, + ] ) }