diff --git a/ui/src/app/applications/components/application-details/application-details.tsx b/ui/src/app/applications/components/application-details/application-details.tsx index 75eabc52b3caa..1487e49885313 100644 --- a/ui/src/app/applications/components/application-details/application-details.tsx +++ b/ui/src/app/applications/components/application-details/application-details.tsx @@ -1,4 +1,4 @@ -import {DropDownMenu, NotificationType, SlidingPanel} from 'argo-ui'; +import {DropDownMenu, NotificationType, SlidingPanel, Tooltip} from 'argo-ui'; import * as classNames from 'classnames'; import * as PropTypes from 'prop-types'; import * as React from 'react'; @@ -147,7 +147,8 @@ export class ApplicationDetails extends React.Component (usrMsg.appName === appName && usrMsg.msgKey === 'groupNodes' ? {...usrMsg, display: true} : usrMsg)); services.viewPreferences.updatePreferences({appDetails: {...pref, groupNodes: !pref.groupNodes}}); } @@ -231,6 +232,7 @@ export class ApplicationDetails extends React.Component usrMsg.appName === application.metadata.name); const resourceNodes = (): any[] => { const statusByKey = new Map(); application.status.resources.forEach(res => statusByKey.set(AppUtils.nodeKey(res), res)); @@ -296,6 +298,14 @@ export class ApplicationDetails extends React.Component { services.viewPreferences.updatePreferences({appDetails: {...pref, groupNodes: showCompactView}}); }; + const updateToolTipState = (appToolTip: models.UserMessages) => { + const existingIndex = pref.displayUserMsgs.findIndex(msg => msg.appName === appToolTip.appName && msg.msgKey === appToolTip.msgKey); + if (existingIndex !== -1) { + pref.displayUserMsgs[existingIndex] = appToolTip; + } else { + (pref.displayUserMsgs || []).push(appToolTip); + } + }; const toggleNameDirection = () => { this.setState({truncateNameOnRight: !this.state.truncateNameOnRight}); }; @@ -446,13 +456,19 @@ export class ApplicationDetails extends React.Component {(pref.view === 'tree' || pref.view === 'network') && ( - this.toggleCompactView(pref)}> - - + + this.toggleCompactView(application.metadata.name, pref)}> + + + )} + expandAll()} title='Expand all child nodes of all parent nodes'> @@ -481,6 +497,7 @@ export class ApplicationDetails extends React.Component this.setNodeExpansion(node, isExpanded)} getNodeExpansion={node => this.getNodeExpansion(node)} diff --git a/ui/src/app/applications/components/application-resource-tree/application-resource-tree.tsx b/ui/src/app/applications/components/application-resource-tree/application-resource-tree.tsx index b5426ff1de2bf..d60348415226c 100644 --- a/ui/src/app/applications/components/application-resource-tree/application-resource-tree.tsx +++ b/ui/src/app/applications/components/application-resource-tree/application-resource-tree.tsx @@ -1,4 +1,4 @@ -import {DropDown, DropDownMenu, NotificationType, Tooltip} from 'argo-ui'; +import {DropDown, DropDownMenu, Tooltip} from 'argo-ui'; import * as classNames from 'classnames'; import * as dagre from 'dagre'; import * as React from 'react'; @@ -22,7 +22,8 @@ import { isYoungerThanXMinutes, NodeId, nodeKey, - PodHealthIcon + PodHealthIcon, + getUsrMsgToDisplay } from '../utils'; import {NodeUpdateAnimation} from './node-update-animation'; import {PodGroup} from '../application-pod-view/pod-view'; @@ -59,6 +60,8 @@ export interface ApplicationResourceTreeProps { appContext?: AppContext; showOrphanedResources: boolean; showCompactNodes: boolean; + userMsgs: models.UserMessages[]; + updateUserMsgs: (userMsgs: models.UserMessages) => void; setShowCompactNodes: (showCompactNodes: boolean) => void; zoom: number; podGroupCount: number; @@ -179,7 +182,7 @@ function groupNodes(nodes: ResourceTreeNode[], graph: dagre.graphlib.Graph) { nodeIds.forEach((nodeId: string) => { const index = nodes.findIndex(node => nodeId === node.uid || nodeId === nodeKey(node)); const graphNode = graph.node(nodeId); - if (!graphNode?.podGroup && index > -1) { + if (!graphNode.podGroup && index > -1) { groupedNodeIds.push(nodeId); } else { podGroupIds.push(nodeId); @@ -927,6 +930,7 @@ export const ApplicationResourceTree = (props: ApplicationResourceTreeProps) => const [filters, setFilters] = React.useState(props.filters); const [filteredGraph, setFilteredGraph] = React.useState([]); const filteredNodes: any[] = []; + React.useEffect(() => { if (props.filters !== filters) { setFilters(props.filters); @@ -934,19 +938,16 @@ export const ApplicationResourceTree = (props: ApplicationResourceTreeProps) => props.setTreeFilterGraph(filteredGraph); } }, [props.filters]); - + const {podGroupCount, userMsgs, updateUserMsgs, setShowCompactNodes} = props; const podCount = nodes.filter(node => node.kind === 'Pod').length; + React.useEffect(() => { - const {podGroupCount, setShowCompactNodes, appContext} = props; if (podCount > podGroupCount) { + const userMsg = getUsrMsgToDisplay(appNode.name, 'groupNodes', userMsgs); + updateUserMsgs(userMsg); setShowCompactNodes(true); - appContext.apis.notifications.show({ - content: `Since the number of pods has surpassed the threshold pod count of ${podGroupCount}, you will now be switched to the group node view. - If you prefer the tree view, you can simply click on the Group Nodes toolbar button to deselect the current view.`, - type: NotificationType.Success - }); } else { - props.setShowCompactNodes(false); + setShowCompactNodes(false); } }, [podCount]); diff --git a/ui/src/app/applications/components/utils.tsx b/ui/src/app/applications/components/utils.tsx index d096658bb7d8f..e30f9eca94a96 100644 --- a/ui/src/app/applications/components/utils.tsx +++ b/ui/src/app/applications/components/utils.tsx @@ -1251,3 +1251,17 @@ export function formatCreationTimestamp(creationTimestamp: string) { } export const selectPostfix = (arr: string[], singular: string, plural: string) => (arr.length > 1 ? plural : singular); + +export function getUsrMsgToDisplay(appName: string, msgKey: string, usrMessages: appModels.UserMessages[], showMsgIntervals?: string) { + const usrMsg = usrMessages?.find((msg: appModels.UserMessages) => msg.appName === appName && msg.msgKey === msgKey); + if (usrMsg !== undefined) { + return {...usrMsg, display: true}; + } else { + return {appName, msgKey, messages: userMsgs[msgKey], display: false, duration: 1} as appModels.UserMessages; + } +} +type UserMsgType = {[key: string]: string}; +export const userMsgs: UserMsgType = { + groupNodes: `Since the number of pods has surpassed the threshold pod count of 15, you will now be switched to the group node view. + If you prefer the tree view, you can simply click on the Group Nodes toolbar button to deselect the current view.` +}; diff --git a/ui/src/app/shared/models.ts b/ui/src/app/shared/models.ts index 7604e4c39bd1e..a39325abbffa0 100644 --- a/ui/src/app/shared/models.ts +++ b/ui/src/app/shared/models.ts @@ -953,3 +953,13 @@ export interface LinkInfo { export interface LinksResponse { items: LinkInfo[]; } + +export interface UserMessages { + appName: string; + msgKey: string; + messages?: string; + display: boolean; + condition?: HealthStatusCode; + duration?: number; + animation?: string; +} diff --git a/ui/src/app/shared/services/view-preferences-service.ts b/ui/src/app/shared/services/view-preferences-service.ts index 314170dba0404..735961c215772 100644 --- a/ui/src/app/shared/services/view-preferences-service.ts +++ b/ui/src/app/shared/services/view-preferences-service.ts @@ -2,6 +2,7 @@ import * as deepMerge from 'deepmerge'; import {BehaviorSubject, Observable} from 'rxjs'; import {PodGroupType} from '../../applications/components/application-pod-view/pod-view'; +import {UserMessages} from '../models'; export type AppsDetailsViewType = 'tree' | 'network' | 'list' | 'pods'; @@ -28,6 +29,7 @@ export interface AppDetailsPreferences { groupNodes?: boolean; zoom: number; podGroupCount: number; + displayUserMsgs: UserMessages[]; } export interface PodViewPreferences { @@ -122,7 +124,8 @@ const DEFAULT_PREFERENCES: ViewPreferences = { followLogs: false, wrapLines: false, zoom: 1.0, - podGroupCount: 15.0 + podGroupCount: 15.0, + displayUserMsgs: [] }, appList: { view: 'tiles' as AppsListViewType,