From 3b30da4fc47c97798328487e3cc7ce7bb002835a Mon Sep 17 00:00:00 2001 From: Simon Zhu Date: Tue, 29 Apr 2025 21:22:53 -0400 Subject: [PATCH 01/14] init --- .../src/hooks/use-theme.tsx | 1 + .../src/base-navigation-item.tsx | 37 +++++++++++++++++++ .../src/item-actions.ts | 7 ++++ .../src/navigation-item.tsx | 18 +++++++-- .../src/styled-navigation-item.tsx | 12 +++++- .../compass-web/src/connection-storage.tsx | 22 +++++++---- .../connection-info/src/connection-info.ts | 2 +- 7 files changed, 86 insertions(+), 13 deletions(-) diff --git a/packages/compass-components/src/hooks/use-theme.tsx b/packages/compass-components/src/hooks/use-theme.tsx index b91b1deea30..4f3dabe41bf 100644 --- a/packages/compass-components/src/hooks/use-theme.tsx +++ b/packages/compass-components/src/hooks/use-theme.tsx @@ -11,6 +11,7 @@ enum Theme { export function useDarkMode(localDarkMode?: boolean): boolean | undefined { const darkMode = useLeafyGreenDarkMode(localDarkMode); return darkMode.darkMode; + // return false; } interface WithDarkModeProps { diff --git a/packages/compass-connections-navigation/src/base-navigation-item.tsx b/packages/compass-connections-navigation/src/base-navigation-item.tsx index 0b39b0ca02b..30c26b7a2eb 100644 --- a/packages/compass-connections-navigation/src/base-navigation-item.tsx +++ b/packages/compass-connections-navigation/src/base-navigation-item.tsx @@ -5,12 +5,16 @@ import { css, ItemActionControls, cx, + Badge, + BadgeVariant, } from '@mongodb-js/compass-components'; import { type Actions, ROW_HEIGHT } from './constants'; import { ExpandButton } from './tree-item'; import { type NavigationItemActions } from './item-actions'; +import type { SidebarTreeItem, SidebarActionableItem } from './tree-data'; type NavigationBaseItemProps = { + item: SidebarTreeItem; name: string; isActive: boolean; isExpandVisible: boolean; @@ -87,6 +91,7 @@ const actionControlsWrapperStyles = css({ }); export const NavigationBaseItem: React.FC = ({ + item, isActive, actionProps, name, @@ -101,6 +106,26 @@ export const NavigationBaseItem: React.FC = ({ toggleExpand, children, }) => { + // TODO: add extra UI stuff here + + let isDisabled = false; + let status = ''; + let variant = BadgeVariant.LightGray; + if (item.type === 'connection') { + const connectionInfo = item.connectionInfo; + isDisabled = + connectionInfo.atlasMetadata?.clusterState === 'DELETING' || + connectionInfo.atlasMetadata?.clusterState === 'PAUSED' || + connectionInfo.atlasMetadata?.clusterState === 'CREATING'; + if (isDisabled) { + status = connectionInfo.atlasMetadata?.clusterState; + if (status === 'CREATING') { + variant = BadgeVariant.Blue; + } + console.log(status); + } + } + const [hoverProps, isHovered] = useHoverState(); return (
= ({ {icon} {name}
+ {isDisabled && ( + + {status} + + )}
{ + if ( + connectionInfo.atlasMetadata?.clusterState === 'DELETING' || + connectionInfo.atlasMetadata?.clusterState === 'PAUSED' || + connectionInfo.atlasMetadata?.clusterState === 'CREATING' + ) { + return []; + } const commonActions = commonConnectionItemActions({ connectionInfo }); if (connectionStatus === 'connecting') { return commonActions; diff --git a/packages/compass-connections-navigation/src/navigation-item.tsx b/packages/compass-connections-navigation/src/navigation-item.tsx index 46b576b9576..e5e6e4ebcff 100644 --- a/packages/compass-connections-navigation/src/navigation-item.tsx +++ b/packages/compass-connections-navigation/src/navigation-item.tsx @@ -111,14 +111,23 @@ export function NavigationItem({ onItemExpand, getItemActions, }: NavigationItemProps) { + let isDisabled = false; + if (item.type === 'connection') { + const connectionInfo = item.connectionInfo; + isDisabled = + connectionInfo.atlasMetadata?.clusterState === 'DELETING' || + connectionInfo.atlasMetadata?.clusterState === 'PAUSED' || + connectionInfo.atlasMetadata?.clusterState === 'CREATING'; + } + const isDarkMode = useDarkMode(); const onAction = useCallback( (action: Actions) => { - if (item.type !== 'placeholder') { + if (item.type !== 'placeholder' || !isDisabled) { onItemAction(item, action); } }, - [item, onItemAction] + [item, onItemAction, isDisabled] ); const style = useMemo(() => getTreeItemStyles(item), [item]); @@ -201,10 +210,10 @@ export function NavigationItem({ }, [item, isDarkMode]); const toggleExpand = useCallback(() => { - if (item.type !== 'placeholder') { + if (item.type !== 'placeholder' || isDisabled) { onItemExpand(item, !item.isExpanded); } - }, [onItemExpand, item]); + }, [onItemExpand, item, isDisabled]); return ( @@ -212,6 +221,7 @@ export function NavigationItem({ ) : ( { if ( - !connectionInfo.connectionOptions.connectionString || !connectionInfo.atlasMetadata || - // TODO(COMPASS-8228): do not filter out those connections, display - // them in navigation, but in a way that doesn't allow connecting - !CONNECTABLE_CLUSTER_STATES.includes( + !VISIBLE_CLUSTER_STATES.includes( connectionInfo.atlasMetadata.clusterState ) ) { return null; } + console.log(connectionInfo); + const clusterName = connectionInfo.atlasMetadata.clusterName; return { @@ -323,6 +327,10 @@ export class AtlasCloudConnectionStorage .filter((connectionInfo): connectionInfo is ConnectionInfo => { return !!connectionInfo; }); + + console.log('FILTERED'); + console.log(cil); + return cil; } /** diff --git a/packages/connection-info/src/connection-info.ts b/packages/connection-info/src/connection-info.ts index 310bd8f237f..831bdc31d52 100644 --- a/packages/connection-info/src/connection-info.ts +++ b/packages/connection-info/src/connection-info.ts @@ -48,7 +48,7 @@ export interface AtlasClusterMetadata { | 'UPDATING' | 'PAUSED' | 'IDLE' - | 'REPARING' + | 'REPAIRING' | 'DELETING' | 'DELETED'; From e12899d63333b18229304b1390d4d96c49a0299c Mon Sep 17 00:00:00 2001 From: Simon Zhu Date: Tue, 29 Apr 2025 22:20:03 -0400 Subject: [PATCH 02/14] refactor --- .../src/hooks/use-theme.tsx | 1 - .../src/base-navigation-item.tsx | 87 ++++++++++++------- .../src/navigation-item-icon.tsx | 10 +++ .../src/navigation-item.tsx | 17 +--- .../src/styled-navigation-item.tsx | 12 +-- .../compass-web/src/connection-storage.tsx | 10 +-- 6 files changed, 70 insertions(+), 67 deletions(-) diff --git a/packages/compass-components/src/hooks/use-theme.tsx b/packages/compass-components/src/hooks/use-theme.tsx index 4f3dabe41bf..b91b1deea30 100644 --- a/packages/compass-components/src/hooks/use-theme.tsx +++ b/packages/compass-components/src/hooks/use-theme.tsx @@ -11,7 +11,6 @@ enum Theme { export function useDarkMode(localDarkMode?: boolean): boolean | undefined { const darkMode = useLeafyGreenDarkMode(localDarkMode); return darkMode.darkMode; - // return false; } interface WithDarkModeProps { diff --git a/packages/compass-connections-navigation/src/base-navigation-item.tsx b/packages/compass-connections-navigation/src/base-navigation-item.tsx index 30c26b7a2eb..58f2670755d 100644 --- a/packages/compass-connections-navigation/src/base-navigation-item.tsx +++ b/packages/compass-connections-navigation/src/base-navigation-item.tsx @@ -7,11 +7,14 @@ import { cx, Badge, BadgeVariant, + Tooltip, + useDarkMode, + Body, } from '@mongodb-js/compass-components'; import { type Actions, ROW_HEIGHT } from './constants'; import { ExpandButton } from './tree-item'; import { type NavigationItemActions } from './item-actions'; -import type { SidebarTreeItem, SidebarActionableItem } from './tree-data'; +import type { SidebarTreeItem } from './tree-data'; type NavigationBaseItemProps = { item: SidebarTreeItem; @@ -90,6 +93,55 @@ const actionControlsWrapperStyles = css({ gap: spacing[100], }); +const ClusterStateBadge: React.FunctionComponent<{ + state: string; +}> = ({ state }) => { + const badgeVariant = + state === 'CREATING' ? BadgeVariant.Blue : BadgeVariant.LightGray; + + return ( + + {state} + + ); +}; + +const ClusterStateBadgeWithTooltip: React.FunctionComponent<{ + item: SidebarTreeItem; +}> = ({ item }) => { + const isDarkMode = useDarkMode(); + + if (item.type === 'connection') { + const atlasClusterState = item.connectionInfo.atlasMetadata?.clusterState; + if (atlasClusterState === 'PAUSED') { + return ( + ) => ( +
+ + {tooltipChildren} +
+ )} + > + Unpause your cluster to connect to it +
+ ); + } else if ( + atlasClusterState === 'DELETING' || + atlasClusterState === 'CREATING' + ) { + return ; + } + } + + return null; +}; + export const NavigationBaseItem: React.FC = ({ item, isActive, @@ -106,26 +158,6 @@ export const NavigationBaseItem: React.FC = ({ toggleExpand, children, }) => { - // TODO: add extra UI stuff here - - let isDisabled = false; - let status = ''; - let variant = BadgeVariant.LightGray; - if (item.type === 'connection') { - const connectionInfo = item.connectionInfo; - isDisabled = - connectionInfo.atlasMetadata?.clusterState === 'DELETING' || - connectionInfo.atlasMetadata?.clusterState === 'PAUSED' || - connectionInfo.atlasMetadata?.clusterState === 'CREATING'; - if (isDisabled) { - status = connectionInfo.atlasMetadata?.clusterState; - if (status === 'CREATING') { - variant = BadgeVariant.Blue; - } - console.log(status); - } - } - const [hoverProps, isHovered] = useHoverState(); return (
= ({ {icon} {name}
- {isDisabled && ( - - {status} - - )} +
{ ); } + if ( + item.connectionInfo.atlasMetadata?.clusterState === 'DELETING' || + item.connectionInfo.atlasMetadata?.clusterState === 'CREATING' + ) { + return ( + + + + ); + } return ( diff --git a/packages/compass-connections-navigation/src/navigation-item.tsx b/packages/compass-connections-navigation/src/navigation-item.tsx index e5e6e4ebcff..4ba31889ce7 100644 --- a/packages/compass-connections-navigation/src/navigation-item.tsx +++ b/packages/compass-connections-navigation/src/navigation-item.tsx @@ -111,23 +111,14 @@ export function NavigationItem({ onItemExpand, getItemActions, }: NavigationItemProps) { - let isDisabled = false; - if (item.type === 'connection') { - const connectionInfo = item.connectionInfo; - isDisabled = - connectionInfo.atlasMetadata?.clusterState === 'DELETING' || - connectionInfo.atlasMetadata?.clusterState === 'PAUSED' || - connectionInfo.atlasMetadata?.clusterState === 'CREATING'; - } - const isDarkMode = useDarkMode(); const onAction = useCallback( (action: Actions) => { - if (item.type !== 'placeholder' || !isDisabled) { + if (item.type !== 'placeholder') { onItemAction(item, action); } }, - [item, onItemAction, isDisabled] + [item, onItemAction] ); const style = useMemo(() => getTreeItemStyles(item), [item]); @@ -210,10 +201,10 @@ export function NavigationItem({ }, [item, isDarkMode]); const toggleExpand = useCallback(() => { - if (item.type !== 'placeholder' || isDisabled) { + if (item.type !== 'placeholder') { onItemExpand(item, !item.isExpanded); } - }, [onItemExpand, item, isDisabled]); + }, [onItemExpand, item]); return ( diff --git a/packages/compass-connections-navigation/src/styled-navigation-item.tsx b/packages/compass-connections-navigation/src/styled-navigation-item.tsx index d290501b61c..bf6e39b6549 100644 --- a/packages/compass-connections-navigation/src/styled-navigation-item.tsx +++ b/packages/compass-connections-navigation/src/styled-navigation-item.tsx @@ -34,7 +34,6 @@ export default function StyledNavigationItem({ const style: AcceptedStyles = {}; const isDisconnectedConnection = item.type === 'connection' && item.connectionStatus !== 'connected'; - const isNonExistentNamespace = (item.type === 'database' || item.type === 'collection') && item.isNonExistent; @@ -45,21 +44,12 @@ export default function StyledNavigationItem({ style['--item-bg-color-active'] = connectionColorToHexActive(colorCode); } - if (isDisconnectedConnection) { + if (isDisconnectedConnection || isNonExistentNamespace) { style['--item-color'] = inactiveColor; - const connectionInfo = item.connectionInfo; - if ( - connectionInfo.atlasMetadata?.clusterState === 'DELETING' || - connectionInfo.atlasMetadata?.clusterState === 'PAUSED' || - connectionInfo.atlasMetadata?.clusterState === 'CREATING' - ) { - style['--item-color-active'] = inactiveColor; - } } // For a non-existent namespace, even if its active, we show it as inactive if (isNonExistentNamespace) { - style['--item-color'] = inactiveColor; style['--item-color-active'] = inactiveColor; } return style; diff --git a/packages/compass-web/src/connection-storage.tsx b/packages/compass-web/src/connection-storage.tsx index 2710ed037d7..5a80a4e74f0 100644 --- a/packages/compass-web/src/connection-storage.tsx +++ b/packages/compass-web/src/connection-storage.tsx @@ -291,9 +291,7 @@ export class AtlasCloudConnectionStorage const connectionInfoList = (await res.json()) as ConnectionInfo[]; - console.log(connectionInfoList); - - const cil = connectionInfoList + return connectionInfoList .map((connectionInfo: ConnectionInfo): ConnectionInfo | null => { if ( !connectionInfo.atlasMetadata || @@ -304,8 +302,6 @@ export class AtlasCloudConnectionStorage return null; } - console.log(connectionInfo); - const clusterName = connectionInfo.atlasMetadata.clusterName; return { @@ -327,10 +323,6 @@ export class AtlasCloudConnectionStorage .filter((connectionInfo): connectionInfo is ConnectionInfo => { return !!connectionInfo; }); - - console.log('FILTERED'); - console.log(cil); - return cil; } /** From 82b9518cdf63fc3e879675abea659bc9b85aa726 Mon Sep 17 00:00:00 2001 From: Simon Zhu Date: Tue, 29 Apr 2025 22:23:17 -0400 Subject: [PATCH 03/14] Update base-navigation-item.tsx --- .../src/base-navigation-item.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/compass-connections-navigation/src/base-navigation-item.tsx b/packages/compass-connections-navigation/src/base-navigation-item.tsx index 58f2670755d..55fd50bc19d 100644 --- a/packages/compass-connections-navigation/src/base-navigation-item.tsx +++ b/packages/compass-connections-navigation/src/base-navigation-item.tsx @@ -98,10 +98,11 @@ const ClusterStateBadge: React.FunctionComponent<{ }> = ({ state }) => { const badgeVariant = state === 'CREATING' ? BadgeVariant.Blue : BadgeVariant.LightGray; + const badgeText = state === 'DELETING' ? 'TERMINATING' : state; return ( - {state} + {badgeText} ); }; From 8f1a3f11cbfa6c77cc6b95e7bdeb0ce82b05aef2 Mon Sep 17 00:00:00 2001 From: Simon Zhu Date: Tue, 29 Apr 2025 22:44:33 -0400 Subject: [PATCH 04/14] Update connections-store-redux.ts --- .../src/stores/connections-store-redux.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/compass-connections/src/stores/connections-store-redux.ts b/packages/compass-connections/src/stores/connections-store-redux.ts index c8c01277fee..66f4cded312 100644 --- a/packages/compass-connections/src/stores/connections-store-redux.ts +++ b/packages/compass-connections/src/stores/connections-store-redux.ts @@ -1489,6 +1489,15 @@ const connectWithOptions = ( return; } + const atlasClusterState = connectionInfo.atlasMetadata?.clusterState; + if ( + atlasClusterState === 'DELETED' || + atlasClusterState === 'PAUSED' || + atlasClusterState === 'CREATING' + ) { + return; + } + const isAutoconnectAttempt = isAutoconnectInfo( getState(), connectionInfo.id From b96fb95e6f561e93705940bcc08cf8f436679927 Mon Sep 17 00:00:00 2001 From: Simon Zhu Date: Wed, 30 Apr 2025 03:16:32 -0400 Subject: [PATCH 05/14] Refactor --- .../src/base-navigation-item.tsx | 64 +++++++++++-------- .../src/connections-navigation-tree.tsx | 29 ++++++++- .../src/item-actions.ts | 7 -- .../src/navigation-item-icon.tsx | 26 +++++--- .../src/styled-navigation-item.tsx | 16 +++-- .../src/tree-data.ts | 18 ++++++ .../src/stores/connections-store-redux.ts | 28 +++++++- 7 files changed, 133 insertions(+), 55 deletions(-) diff --git a/packages/compass-connections-navigation/src/base-navigation-item.tsx b/packages/compass-connections-navigation/src/base-navigation-item.tsx index 55fd50bc19d..35e070e3521 100644 --- a/packages/compass-connections-navigation/src/base-navigation-item.tsx +++ b/packages/compass-connections-navigation/src/base-navigation-item.tsx @@ -97,8 +97,17 @@ const ClusterStateBadge: React.FunctionComponent<{ state: string; }> = ({ state }) => { const badgeVariant = - state === 'CREATING' ? BadgeVariant.Blue : BadgeVariant.LightGray; - const badgeText = state === 'DELETING' ? 'TERMINATING' : state; + state === 'CREATING' + ? BadgeVariant.Blue + : state === 'DELETED' + ? BadgeVariant.Red + : BadgeVariant.LightGray; + const badgeText = + state === 'DELETING' + ? 'TERMINATING' + : state === 'DELETED' + ? 'TERMINATED' + : state; return ( @@ -112,32 +121,31 @@ const ClusterStateBadgeWithTooltip: React.FunctionComponent<{ }> = ({ item }) => { const isDarkMode = useDarkMode(); - if (item.type === 'connection') { - const atlasClusterState = item.connectionInfo.atlasMetadata?.clusterState; - if (atlasClusterState === 'PAUSED') { - return ( - ) => ( -
- - {tooltipChildren} -
- )} - > - Unpause your cluster to connect to it -
- ); - } else if ( - atlasClusterState === 'DELETING' || - atlasClusterState === 'CREATING' - ) { - return ; - } + const atlasClusterState = item.connectionInfo?.atlasMetadata?.clusterState; + if (atlasClusterState === 'PAUSED') { + return ( + ) => ( +
+ + {tooltipChildren} +
+ )} + > + Unpause your cluster to connect to it +
+ ); + } else if ( + atlasClusterState === 'DELETING' || + atlasClusterState === 'CREATING' || + atlasClusterState === 'DELETED' + ) { + return ; } return null; diff --git a/packages/compass-connections-navigation/src/connections-navigation-tree.tsx b/packages/compass-connections-navigation/src/connections-navigation-tree.tsx index 6f8634c226a..6ca8e209871 100644 --- a/packages/compass-connections-navigation/src/connections-navigation-tree.tsx +++ b/packages/compass-connections-navigation/src/connections-navigation-tree.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useMemo } from 'react'; import AutoSizer from 'react-virtualized-auto-sizer'; -import { getVirtualTreeItems } from './tree-data'; +import { clusterIsConnectable, getVirtualTreeItems } from './tree-data'; import { ROW_HEIGHT } from './constants'; import type { Actions } from './constants'; import { VirtualTree } from './virtual-list/virtual-list'; @@ -21,6 +21,7 @@ import { import type { WorkspaceTab } from '@mongodb-js/compass-workspaces'; import { usePreference } from 'compass-preferences-model/provider'; import type { NavigationItemActions } from './item-actions'; +import { useConnectionsListRef } from '@mongodb-js/compass-connections/provider'; import { collectionItemActions, connectedConnectionItemActions, @@ -57,6 +58,7 @@ const ConnectionsNavigationTree: React.FunctionComponent< ); const id = useId(); + const { getConnectionById } = useConnectionsListRef(); const treeData = useMemo(() => { return getVirtualTreeItems({ @@ -69,6 +71,14 @@ const ConnectionsNavigationTree: React.FunctionComponent< const onDefaultAction: OnDefaultAction = useCallback( (item, evt) => { + const isConnectableCluster = clusterIsConnectable( + item, + getConnectionById + ); + if (!isConnectableCluster) { + return; + } + if (item.type === 'connection') { if (item.connectionStatus === 'connected') { onItemAction(item, 'select-connection'); @@ -83,7 +93,7 @@ const ConnectionsNavigationTree: React.FunctionComponent< } } }, - [onItemAction] + [onItemAction, getConnectionById] ); const activeItemId = useMemo(() => { @@ -144,6 +154,15 @@ const ConnectionsNavigationTree: React.FunctionComponent< const getItemActionsAndConfig = useCallback( (item: SidebarTreeItem) => { + const isConnectableCluster = clusterIsConnectable( + item, + getConnectionById + ); + if (!isConnectableCluster) { + return { + actions: [], + }; + } switch (item.type) { case 'placeholder': return { @@ -192,7 +211,11 @@ const ConnectionsNavigationTree: React.FunctionComponent< }; } }, - [isRenameCollectionEnabled, getCollapseAfterForConnectedItem] + [ + isRenameCollectionEnabled, + getCollapseAfterForConnectedItem, + getConnectionById, + ] ); const isTestEnv = process.env.NODE_ENV === 'test'; diff --git a/packages/compass-connections-navigation/src/item-actions.ts b/packages/compass-connections-navigation/src/item-actions.ts index 5097cdcfa14..9e4f58c5b19 100644 --- a/packages/compass-connections-navigation/src/item-actions.ts +++ b/packages/compass-connections-navigation/src/item-actions.ts @@ -146,13 +146,6 @@ export const notConnectedConnectionItemActions = ({ connectionInfo: ConnectionInfo; connectionStatus: NotConnectedConnectionStatus; }): NavigationItemActions => { - if ( - connectionInfo.atlasMetadata?.clusterState === 'DELETING' || - connectionInfo.atlasMetadata?.clusterState === 'PAUSED' || - connectionInfo.atlasMetadata?.clusterState === 'CREATING' - ) { - return []; - } const commonActions = commonConnectionItemActions({ connectionInfo }); if (connectionStatus === 'connecting') { return commonActions; diff --git a/packages/compass-connections-navigation/src/navigation-item-icon.tsx b/packages/compass-connections-navigation/src/navigation-item-icon.tsx index 53a2ff9600e..1dd55b8b34b 100644 --- a/packages/compass-connections-navigation/src/navigation-item-icon.tsx +++ b/packages/compass-connections-navigation/src/navigation-item-icon.tsx @@ -63,6 +63,22 @@ export const NavigationItemIcon = ({ item }: { item: SidebarTreeItem }) => { return ; } if (item.type === 'connection') { + const atlasClusterState = item.connectionInfo.atlasMetadata?.clusterState; + if (atlasClusterState === 'DELETING' || atlasClusterState === 'CREATING') { + return ( + + + + ); + } + if (atlasClusterState === 'PAUSED' || atlasClusterState === 'DELETED') { + return ( + + + + ); + } + const isFavorite = item.connectionInfo.savedConnectionType === 'favorite'; if (isFavorite) { return ( @@ -78,16 +94,6 @@ export const NavigationItemIcon = ({ item }: { item: SidebarTreeItem }) => {
); } - if ( - item.connectionInfo.atlasMetadata?.clusterState === 'DELETING' || - item.connectionInfo.atlasMetadata?.clusterState === 'CREATING' - ) { - return ( - - - - ); - } return ( diff --git a/packages/compass-connections-navigation/src/styled-navigation-item.tsx b/packages/compass-connections-navigation/src/styled-navigation-item.tsx index bf6e39b6549..64aceb97ab8 100644 --- a/packages/compass-connections-navigation/src/styled-navigation-item.tsx +++ b/packages/compass-connections-navigation/src/styled-navigation-item.tsx @@ -4,7 +4,8 @@ import { DefaultColorCode, } from '@mongodb-js/connection-form'; import { palette, useDarkMode } from '@mongodb-js/compass-components'; -import type { SidebarTreeItem } from './tree-data'; +import { clusterIsConnectable, type SidebarTreeItem } from './tree-data'; +import { useConnectionsListRef } from '@mongodb-js/compass-connections/provider'; type AcceptedStyles = { '--item-bg-color'?: string; @@ -29,6 +30,8 @@ export default function StyledNavigationItem({ () => (isDarkMode ? palette.gray.light1 : palette.gray.dark1), [isDarkMode] ); + const { getConnectionById } = useConnectionsListRef(); + const isConnectableCluster = clusterIsConnectable(item, getConnectionById); const style: React.CSSProperties & AcceptedStyles = useMemo(() => { const style: AcceptedStyles = {}; @@ -44,17 +47,22 @@ export default function StyledNavigationItem({ style['--item-bg-color-active'] = connectionColorToHexActive(colorCode); } - if (isDisconnectedConnection || isNonExistentNamespace) { + if ( + isDisconnectedConnection || + isNonExistentNamespace || + !isConnectableCluster + ) { style['--item-color'] = inactiveColor; } - // For a non-existent namespace, even if its active, we show it as inactive - if (isNonExistentNamespace) { + // We always show these as inactive + if (isNonExistentNamespace || !isConnectableCluster) { style['--item-color-active'] = inactiveColor; } return style; }, [ inactiveColor, + isConnectableCluster, item, colorCode, connectionColorToHex, diff --git a/packages/compass-connections-navigation/src/tree-data.ts b/packages/compass-connections-navigation/src/tree-data.ts index 2d3476f2649..4c0942c2db1 100644 --- a/packages/compass-connections-navigation/src/tree-data.ts +++ b/packages/compass-connections-navigation/src/tree-data.ts @@ -124,6 +124,24 @@ export type SidebarActionableItem = export type SidebarTreeItem = PlaceholderTreeItem | SidebarActionableItem; +export function clusterIsConnectable( + item: SidebarTreeItem, + getConnectionById: any +): boolean { + if (item.type === 'connection') { + const clusterState = item.connectionInfo.atlasMetadata?.clusterState; + return !['DELETING', 'DELETED', 'CREATING', 'PAUSED'].includes( + clusterState + ); + } else if (item.type === 'database' || item.type === 'collection') { + const connection = getConnectionById(item.connectionId); + return !['DELETING', 'DELETED', 'CREATING', 'PAUSED'].includes( + connection.info.atlasMetadata?.clusterState + ); + } + return true; +} + const notConnectedConnectionToItems = ({ connection: { name, connectionInfo, connectionStatus }, connectionIndex, diff --git a/packages/compass-connections/src/stores/connections-store-redux.ts b/packages/compass-connections/src/stores/connections-store-redux.ts index 66f4cded312..ea61619c30a 100644 --- a/packages/compass-connections/src/stores/connections-store-redux.ts +++ b/packages/compass-connections/src/stores/connections-store-redux.ts @@ -530,9 +530,12 @@ function mergeConnections( ? newConnections : [newConnections]; + const removedConnectionIds = new Set(connectionsState.ids); + let newConnectionsById = connectionsState.byId; for (const connectionInfo of newConnections) { + removedConnectionIds.delete(connectionInfo.id); const existingConnection = newConnectionsById[connectionInfo.id]; // If we got a new connection, just create a default state for this @@ -560,6 +563,25 @@ function mergeConnections( } } + for (const connectionId of removedConnectionIds) { + const removedConnection = newConnectionsById[connectionId]; + if (removedConnection.info.atlasMetadata) { + newConnectionsById = { + ...newConnectionsById, + [connectionId]: { + ...removedConnection, + info: { + ...removedConnection.info, + atlasMetadata: { + ...removedConnection.info.atlasMetadata, + clusterState: 'DELETED', + }, + }, + }, + }; + } + } + // If we haven't modified newConnectionsById at this point, we can stop: none // of the new connections are different from what we have in the state already if (newConnectionsById === connectionsState.byId) { @@ -1491,9 +1513,9 @@ const connectWithOptions = ( const atlasClusterState = connectionInfo.atlasMetadata?.clusterState; if ( - atlasClusterState === 'DELETED' || - atlasClusterState === 'PAUSED' || - atlasClusterState === 'CREATING' + ['DELETING', 'DELETED', 'CREATING', 'PAUSED'].includes( + atlasClusterState + ) ) { return; } From 6b232a9d92f59a159e2157fb938336fd68f07284 Mon Sep 17 00:00:00 2001 From: Simon Zhu Date: Wed, 30 Apr 2025 18:16:08 -0400 Subject: [PATCH 06/14] Update connection-storage.tsx --- .../src/stores/connections-store-redux.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/compass-connections/src/stores/connections-store-redux.ts b/packages/compass-connections/src/stores/connections-store-redux.ts index ea61619c30a..dbe56ea870e 100644 --- a/packages/compass-connections/src/stores/connections-store-redux.ts +++ b/packages/compass-connections/src/stores/connections-store-redux.ts @@ -1513,9 +1513,10 @@ const connectWithOptions = ( const atlasClusterState = connectionInfo.atlasMetadata?.clusterState; if ( - ['DELETING', 'DELETED', 'CREATING', 'PAUSED'].includes( - atlasClusterState - ) + atlasClusterState === 'DELETED' || + atlasClusterState === 'DELETING' || + atlasClusterState === 'CREATING' || + atlasClusterState === 'PAUSED' ) { return; } From 2860e70bbed9fe96db6aca0ce3a3797ba79f60fe Mon Sep 17 00:00:00 2001 From: Simon Zhu Date: Wed, 30 Apr 2025 22:38:27 -0400 Subject: [PATCH 07/14] refactor --- .../src/connections-navigation-tree.tsx | 24 +++++-------- .../src/styled-navigation-item.tsx | 19 +++++------ .../src/tree-data.ts | 22 ++++-------- .../src/hooks/use-connectable.ts | 34 +++++++++++++++++++ packages/compass-connections/src/index.tsx | 2 +- packages/compass-connections/src/provider.ts | 2 ++ .../src/stores/connections-store-redux.ts | 10 ++---- .../src/utils/connection-supports.ts | 13 +++++++ 8 files changed, 77 insertions(+), 49 deletions(-) create mode 100644 packages/compass-connections/src/hooks/use-connectable.ts diff --git a/packages/compass-connections-navigation/src/connections-navigation-tree.tsx b/packages/compass-connections-navigation/src/connections-navigation-tree.tsx index 6ca8e209871..f22e0370f3b 100644 --- a/packages/compass-connections-navigation/src/connections-navigation-tree.tsx +++ b/packages/compass-connections-navigation/src/connections-navigation-tree.tsx @@ -1,6 +1,6 @@ import React, { useCallback, useMemo } from 'react'; import AutoSizer from 'react-virtualized-auto-sizer'; -import { clusterIsConnectable, getVirtualTreeItems } from './tree-data'; +import { getConnectionId, getVirtualTreeItems } from './tree-data'; import { ROW_HEIGHT } from './constants'; import type { Actions } from './constants'; import { VirtualTree } from './virtual-list/virtual-list'; @@ -18,10 +18,10 @@ import { spacing, useId, } from '@mongodb-js/compass-components'; +import { useConnectableRef } from '@mongodb-js/compass-connections/provider'; import type { WorkspaceTab } from '@mongodb-js/compass-workspaces'; import { usePreference } from 'compass-preferences-model/provider'; import type { NavigationItemActions } from './item-actions'; -import { useConnectionsListRef } from '@mongodb-js/compass-connections/provider'; import { collectionItemActions, connectedConnectionItemActions, @@ -58,7 +58,7 @@ const ConnectionsNavigationTree: React.FunctionComponent< ); const id = useId(); - const { getConnectionById } = useConnectionsListRef(); + const { getConnectable } = useConnectableRef(); const treeData = useMemo(() => { return getVirtualTreeItems({ @@ -71,11 +71,8 @@ const ConnectionsNavigationTree: React.FunctionComponent< const onDefaultAction: OnDefaultAction = useCallback( (item, evt) => { - const isConnectableCluster = clusterIsConnectable( - item, - getConnectionById - ); - if (!isConnectableCluster) { + const connectionId = getConnectionId(item); + if (!getConnectable(connectionId)) { return; } @@ -93,7 +90,7 @@ const ConnectionsNavigationTree: React.FunctionComponent< } } }, - [onItemAction, getConnectionById] + [onItemAction, getConnectable] ); const activeItemId = useMemo(() => { @@ -154,11 +151,8 @@ const ConnectionsNavigationTree: React.FunctionComponent< const getItemActionsAndConfig = useCallback( (item: SidebarTreeItem) => { - const isConnectableCluster = clusterIsConnectable( - item, - getConnectionById - ); - if (!isConnectableCluster) { + const connectionId = getConnectionId(item); + if (!getConnectable(connectionId)) { return { actions: [], }; @@ -214,7 +208,7 @@ const ConnectionsNavigationTree: React.FunctionComponent< [ isRenameCollectionEnabled, getCollapseAfterForConnectedItem, - getConnectionById, + getConnectable, ] ); diff --git a/packages/compass-connections-navigation/src/styled-navigation-item.tsx b/packages/compass-connections-navigation/src/styled-navigation-item.tsx index 64aceb97ab8..d79abfcd9eb 100644 --- a/packages/compass-connections-navigation/src/styled-navigation-item.tsx +++ b/packages/compass-connections-navigation/src/styled-navigation-item.tsx @@ -4,8 +4,8 @@ import { DefaultColorCode, } from '@mongodb-js/connection-form'; import { palette, useDarkMode } from '@mongodb-js/compass-components'; -import { clusterIsConnectable, type SidebarTreeItem } from './tree-data'; -import { useConnectionsListRef } from '@mongodb-js/compass-connections/provider'; +import { getConnectionId, type SidebarTreeItem } from './tree-data'; +import { useConnectable } from '@mongodb-js/compass-connections/provider'; type AcceptedStyles = { '--item-bg-color'?: string; @@ -30,8 +30,9 @@ export default function StyledNavigationItem({ () => (isDarkMode ? palette.gray.light1 : palette.gray.dark1), [isDarkMode] ); - const { getConnectionById } = useConnectionsListRef(); - const isConnectableCluster = clusterIsConnectable(item, getConnectionById); + + const connectionId = getConnectionId(item); + const isConnectable = useConnectable(connectionId); const style: React.CSSProperties & AcceptedStyles = useMemo(() => { const style: AcceptedStyles = {}; @@ -47,22 +48,18 @@ export default function StyledNavigationItem({ style['--item-bg-color-active'] = connectionColorToHexActive(colorCode); } - if ( - isDisconnectedConnection || - isNonExistentNamespace || - !isConnectableCluster - ) { + if (isDisconnectedConnection || isNonExistentNamespace || !isConnectable) { style['--item-color'] = inactiveColor; } // We always show these as inactive - if (isNonExistentNamespace || !isConnectableCluster) { + if (isNonExistentNamespace || !isConnectable) { style['--item-color-active'] = inactiveColor; } return style; }, [ inactiveColor, - isConnectableCluster, + isConnectable, item, colorCode, connectionColorToHex, diff --git a/packages/compass-connections-navigation/src/tree-data.ts b/packages/compass-connections-navigation/src/tree-data.ts index 4c0942c2db1..3d14d0eeacd 100644 --- a/packages/compass-connections-navigation/src/tree-data.ts +++ b/packages/compass-connections-navigation/src/tree-data.ts @@ -124,22 +124,14 @@ export type SidebarActionableItem = export type SidebarTreeItem = PlaceholderTreeItem | SidebarActionableItem; -export function clusterIsConnectable( - item: SidebarTreeItem, - getConnectionById: any -): boolean { - if (item.type === 'connection') { - const clusterState = item.connectionInfo.atlasMetadata?.clusterState; - return !['DELETING', 'DELETED', 'CREATING', 'PAUSED'].includes( - clusterState - ); - } else if (item.type === 'database' || item.type === 'collection') { - const connection = getConnectionById(item.connectionId); - return !['DELETING', 'DELETED', 'CREATING', 'PAUSED'].includes( - connection.info.atlasMetadata?.clusterState - ); +export function getConnectionId(item: SidebarTreeItem): string { + if (item.type === 'placeholder') { + return ''; + } else if (item.type === 'connection') { + return item.connectionInfo.id; + } else { + return item.connectionId; } - return true; } const notConnectedConnectionToItems = ({ diff --git a/packages/compass-connections/src/hooks/use-connectable.ts b/packages/compass-connections/src/hooks/use-connectable.ts new file mode 100644 index 00000000000..cc804d29a27 --- /dev/null +++ b/packages/compass-connections/src/hooks/use-connectable.ts @@ -0,0 +1,34 @@ +import { useSelector, useStore } from '../stores/store-context'; +import { useRef, useState } from 'react'; +import { connectable } from '../utils/connection-supports'; + +export function useConnectable(connectionId: string): boolean { + return useSelector((state) => { + const connection = state.connections.byId[connectionId]; + + if (!connection) { + return false; + } + + return connectable(connection.info); + }); +} + +export function useConnectableRef(): { + getConnectable(this: void, connectionId: string): boolean; +} { + const storeRef = useRef(useStore()); + const [ref] = useState(() => { + return { + getConnectable(connectionId: string) { + const conn = storeRef.current.getState().connections.byId[connectionId]; + if (!conn) { + return false; + } + + return connectable(conn.info); + }, + }; + }); + return ref; +} diff --git a/packages/compass-connections/src/index.tsx b/packages/compass-connections/src/index.tsx index e6a20fabea6..aab107b17a2 100644 --- a/packages/compass-connections/src/index.tsx +++ b/packages/compass-connections/src/index.tsx @@ -23,7 +23,7 @@ import { ConnectionActionsProvider, } from './stores/store-context'; export type { ConnectionFeature } from './utils/connection-supports'; -export { connectionSupports } from './utils/connection-supports'; +export { connectionSupports, connectable } from './utils/connection-supports'; const ConnectionsComponent: React.FunctionComponent<{ /** diff --git a/packages/compass-connections/src/provider.ts b/packages/compass-connections/src/provider.ts index c3f97fb6168..db85fd35904 100644 --- a/packages/compass-connections/src/provider.ts +++ b/packages/compass-connections/src/provider.ts @@ -69,6 +69,8 @@ export type { ConnectionsService } from './stores/store-context'; export { useConnectionSupports } from './hooks/use-connection-supports'; +export { useConnectable, useConnectableRef } from './hooks/use-connectable'; + const ConnectionStatus = { /** * @deprecated use a string literal directly diff --git a/packages/compass-connections/src/stores/connections-store-redux.ts b/packages/compass-connections/src/stores/connections-store-redux.ts index dbe56ea870e..a2a33c42a0d 100644 --- a/packages/compass-connections/src/stores/connections-store-redux.ts +++ b/packages/compass-connections/src/stores/connections-store-redux.ts @@ -32,6 +32,7 @@ import EventEmitter from 'events'; import { showNonGenuineMongoDBWarningModal as _showNonGenuineMongoDBWarningModal } from '../components/non-genuine-connection-modal'; import ConnectionString from 'mongodb-connection-string-url'; import type { ExtraConnectionData as ExtraConnectionDataForTelemetry } from '@mongodb-js/compass-telemetry'; +import { connectable } from '../utils/connection-supports'; export type ConnectionsEventMap = { connected: ( @@ -563,6 +564,7 @@ function mergeConnections( } } + // If an Atlas connection was removed, it means that the cluster was deleted for (const connectionId of removedConnectionIds) { const removedConnection = newConnectionsById[connectionId]; if (removedConnection.info.atlasMetadata) { @@ -1511,13 +1513,7 @@ const connectWithOptions = ( return; } - const atlasClusterState = connectionInfo.atlasMetadata?.clusterState; - if ( - atlasClusterState === 'DELETED' || - atlasClusterState === 'DELETING' || - atlasClusterState === 'CREATING' || - atlasClusterState === 'PAUSED' - ) { + if (!connectable(connectionInfo)) { return; } diff --git a/packages/compass-connections/src/utils/connection-supports.ts b/packages/compass-connections/src/utils/connection-supports.ts index c70388f5b68..f1767a6f606 100644 --- a/packages/compass-connections/src/utils/connection-supports.ts +++ b/packages/compass-connections/src/utils/connection-supports.ts @@ -11,6 +11,19 @@ function supportsRollingIndexCreation(connectionInfo: ConnectionInfo) { return atlasMetadata.supports.rollingIndexes; } +export function connectable(connectionInfo: ConnectionInfo) { + const atlasClusterState = connectionInfo.atlasMetadata?.clusterState; + if ( + atlasClusterState === 'DELETED' || + atlasClusterState === 'DELETING' || + atlasClusterState === 'CREATING' || + atlasClusterState === 'PAUSED' + ) { + return false; + } + return true; +} + function supportsGlobalWrites(connectionInfo: ConnectionInfo) { const atlasMetadata = connectionInfo.atlasMetadata; From 68b30a41c418f4c6173ea44e6e203cd60fc64101 Mon Sep 17 00:00:00 2001 From: Simon Zhu Date: Thu, 1 May 2025 08:57:28 -0400 Subject: [PATCH 08/14] Update base-navigation-item.tsx --- .../src/base-navigation-item.tsx | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/compass-connections-navigation/src/base-navigation-item.tsx b/packages/compass-connections-navigation/src/base-navigation-item.tsx index 35e070e3521..8d330a1d3ef 100644 --- a/packages/compass-connections-navigation/src/base-navigation-item.tsx +++ b/packages/compass-connections-navigation/src/base-navigation-item.tsx @@ -14,7 +14,12 @@ import { import { type Actions, ROW_HEIGHT } from './constants'; import { ExpandButton } from './tree-item'; import { type NavigationItemActions } from './item-actions'; -import type { SidebarTreeItem } from './tree-data'; +import type { + ConnectedConnectionTreeItem, + NotConnectedConnectionTreeItem, + SidebarActionableItem, + SidebarTreeItem, +} from './tree-data'; type NavigationBaseItemProps = { item: SidebarTreeItem; @@ -117,11 +122,11 @@ const ClusterStateBadge: React.FunctionComponent<{ }; const ClusterStateBadgeWithTooltip: React.FunctionComponent<{ - item: SidebarTreeItem; + item: ConnectedConnectionTreeItem | NotConnectedConnectionTreeItem; }> = ({ item }) => { const isDarkMode = useDarkMode(); - const atlasClusterState = item.connectionInfo?.atlasMetadata?.clusterState; + const atlasClusterState = item.connectionInfo.atlasMetadata?.clusterState; if (atlasClusterState === 'PAUSED') { return ( = ({ children, }) => { const [hoverProps, isHovered] = useHoverState(); + return (
= ({ {icon} {name}
- + {item.type === 'connection' && ( + + )}
Date: Thu, 1 May 2025 09:51:50 -0400 Subject: [PATCH 09/14] remove unused' --- .../src/base-navigation-item.tsx | 1 - packages/compass-web/src/connection-storage.tsx | 1 + packages/compass-web/src/entrypoint.tsx | 6 ++++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/compass-connections-navigation/src/base-navigation-item.tsx b/packages/compass-connections-navigation/src/base-navigation-item.tsx index 8d330a1d3ef..65c5ffa2189 100644 --- a/packages/compass-connections-navigation/src/base-navigation-item.tsx +++ b/packages/compass-connections-navigation/src/base-navigation-item.tsx @@ -17,7 +17,6 @@ import { type NavigationItemActions } from './item-actions'; import type { ConnectedConnectionTreeItem, NotConnectedConnectionTreeItem, - SidebarActionableItem, SidebarTreeItem, } from './tree-data'; diff --git a/packages/compass-web/src/connection-storage.tsx b/packages/compass-web/src/connection-storage.tsx index 5a80a4e74f0..330bd8f6c3c 100644 --- a/packages/compass-web/src/connection-storage.tsx +++ b/packages/compass-web/src/connection-storage.tsx @@ -306,6 +306,7 @@ export class AtlasCloudConnectionStorage return { ...connectionInfo, + id: connectionInfo.atlasMetadata.clusterUniqueId, connectionOptions: { ...connectionInfo.connectionOptions, lookup: () => { diff --git a/packages/compass-web/src/entrypoint.tsx b/packages/compass-web/src/entrypoint.tsx index 4fdf1f0138a..58b56f6fc69 100644 --- a/packages/compass-web/src/entrypoint.tsx +++ b/packages/compass-web/src/entrypoint.tsx @@ -234,7 +234,7 @@ const WithConnectionsStore: React.FunctionComponent<{ useEffect(() => { const intervalId = setInterval(() => { void actions.refreshConnections(); - }, /* Matches default polling intervals in mms codebase */ 60_000); + }, /* Matches default polling intervals in mms codebase */ 5_000); return () => { clearInterval(intervalId); }; @@ -286,6 +286,8 @@ const CompassWeb = ({ ? initialWorkspaceRef.current.connectionId : initialAutoconnectId ?? undefined; + console.log('AUTOCONNECT ID', autoconnectId); + const onTrackRef = useRef(onTrack); const telemetryOptions = useRef({ @@ -300,7 +302,7 @@ const CompassWeb = ({