From 5488e1c18be727d2b28dbaca9453dbb039af6b8c Mon Sep 17 00:00:00 2001 From: Jong Eun Lee Date: Fri, 17 Oct 2025 19:03:34 +0800 Subject: [PATCH] feat(FR-1560): auto-dismiss running notification with timeout animation --- .../BAIComputeSessionNodeNotificationItem.tsx | 87 ++++++++----------- 1 file changed, 38 insertions(+), 49 deletions(-) diff --git a/react/src/components/BAIComputeSessionNodeNotificationItem.tsx b/react/src/components/BAIComputeSessionNodeNotificationItem.tsx index f58ad76f36..83d5c8da61 100644 --- a/react/src/components/BAIComputeSessionNodeNotificationItem.tsx +++ b/react/src/components/BAIComputeSessionNodeNotificationItem.tsx @@ -3,16 +3,10 @@ import SessionStatusTag from './ComputeSessionNodeItems/SessionStatusTag'; import { useUpdateEffect } from 'ahooks'; import { BAIFlex, BAILink, BAINotificationItem } from 'backend.ai-ui'; import dayjs from 'dayjs'; -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useEffectEvent } from 'react'; import { useTranslation } from 'react-i18next'; -import { - fetchQuery, - graphql, - useFragment, - useRelayEnvironment, -} from 'react-relay'; +import { graphql, useRefetchableFragment } from 'react-relay'; import { BAIComputeSessionNodeNotificationItemFragment$key } from 'src/__generated__/BAIComputeSessionNodeNotificationItemFragment.graphql'; -import { BAIComputeSessionNodeNotificationItemRefreshQuery } from 'src/__generated__/BAIComputeSessionNodeNotificationItemRefreshQuery.graphql'; import { NotificationState, useSetBAINotification, @@ -27,11 +21,15 @@ interface BAINodeNotificationItemProps { const BAIComputeSessionNodeNotificationItem: React.FC< BAINodeNotificationItemProps > = ({ sessionFrgmt, showDate, notification }) => { - const { destroyNotification } = useSetBAINotification(); + 'use memo'; + const { destroyNotification, upsertNotification } = useSetBAINotification(); const { t } = useTranslation(); - const node = useFragment( + const [node, refetch] = useRefetchableFragment( graphql` - fragment BAIComputeSessionNodeNotificationItemFragment on ComputeSessionNode { + fragment BAIComputeSessionNodeNotificationItemFragment on ComputeSessionNode + @refetchable( + queryName: "BAIComputeSessionNodeNotificationItemFragment_RefetchQuery" + ) { row_id id name @@ -43,23 +41,22 @@ const BAIComputeSessionNodeNotificationItem: React.FC< sessionFrgmt, ); - // TODO: delete this when Status subscription is implemented - const [delay, setDelay] = useState(null); - UNSAFE_useAutoRefreshInterval(node?.id || '', delay); + const closeNotificationWithProgress = useEffectEvent(() => { + if (notification.open) { + upsertNotification({ + key: notification.key, + pauseOnHover: true, + showProgress: true, + duration: 10, + }); + } + }); + useEffect(() => { - if ( - !node?.status || - node?.status === 'TERMINATED' || - node?.status === 'CANCELLED' - ) { - setDelay(null); - } else if (node?.status === 'RUNNING') { - setDelay(15000); - } else { - setDelay(3000); + if (node?.status === 'RUNNING') { + closeNotificationWithProgress(); } }, [node?.status]); - // --- useUpdateEffect(() => { if (node?.status === 'TERMINATED' || node?.status === 'CANCELLED') { @@ -69,6 +66,22 @@ const BAIComputeSessionNodeNotificationItem: React.FC< } }, [node?.status]); + const delay = ['TERMINATED', 'CANCELLED', null, undefined].includes( + node?.status, + ) + ? null + : node?.status === 'RUNNING' + ? 15000 + : 3000; + useInterval(() => { + refetch( + {}, + { + fetchPolicy: 'store-and-network', + }, + ); + }, delay); + return ( node && ( { - // const [delay, setDelay] = useState(3000); - const relayEnv = useRelayEnvironment(); - - useInterval(() => { - fetchQuery( - relayEnv, - graphql` - query BAIComputeSessionNodeNotificationItemRefreshQuery( - $id: GlobalIDField! - ) { - compute_session_node(id: $id) { - ...BAINodeNotificationItemFragment - } - } - `, - { id: sessionId }, - ).toPromise(); - }, delay); -};