From e39ded88cec194ffa255d6b271b7848c18f04a1f Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Sun, 12 Jan 2025 21:37:43 -0800 Subject: [PATCH 1/7] refactor: move connection dropdown to own file This moves ChangeConnectionBlockModal to it's own file so it's easier to manage. --- frontend/app/block/blockframe.tsx | 343 +------------------------ frontend/app/modals/conntypeahead.tsx | 351 ++++++++++++++++++++++++++ 2 files changed, 353 insertions(+), 341 deletions(-) create mode 100644 frontend/app/modals/conntypeahead.tsx diff --git a/frontend/app/block/blockframe.tsx b/frontend/app/block/blockframe.tsx index e9f997e5ea..a30f5d38f5 100644 --- a/frontend/app/block/blockframe.tsx +++ b/frontend/app/block/blockframe.tsx @@ -1,32 +1,20 @@ // Copyright 2025, Command Line Inc. // SPDX-License-Identifier: Apache-2.0 -import { - blockViewToIcon, - blockViewToName, - computeConnColorNum, - ConnectionButton, - getBlockHeaderIcon, - Input, -} from "@/app/block/blockutil"; +import { blockViewToIcon, blockViewToName, ConnectionButton, getBlockHeaderIcon, Input } from "@/app/block/blockutil"; import { Button } from "@/app/element/button"; import { useDimensionsWithCallbackRef } from "@/app/hook/useDimensions"; -import { TypeAheadModal } from "@/app/modals/typeaheadmodal"; +import { ChangeConnectionBlockModal } from "@/app/modals/conntypeahead"; import { ContextMenuModel } from "@/app/store/contextmenu"; import { atoms, - createBlock, - getApi, getBlockComponentModel, getConnStatusAtom, - getHostName, getSettingsKeyAtom, - getUserName, globalStore, useBlockAtom, WOS, } from "@/app/store/global"; -import { globalRefocusWithTimeout } from "@/app/store/keymodel"; import { RpcApi } from "@/app/store/wshclientapi"; import { TabRpcClient } from "@/app/store/wshrpcutil"; import { ErrorBoundary } from "@/element/errorboundary"; @@ -34,7 +22,6 @@ import { IconButton, ToggleIconButton } from "@/element/iconbutton"; import { MagnifyIcon } from "@/element/magnify"; import { MenuButton } from "@/element/menubutton"; import { NodeModel } from "@/layout/index"; -import * as keyutil from "@/util/keyutil"; import * as util from "@/util/util"; import clsx from "clsx"; import * as jotai from "jotai"; @@ -617,332 +604,6 @@ const BlockFrame_Default_Component = (props: BlockFrameProps) => { ); }; -const ChangeConnectionBlockModal = React.memo( - ({ - blockId, - viewModel, - blockRef, - connBtnRef, - changeConnModalAtom, - nodeModel, - }: { - blockId: string; - viewModel: ViewModel; - blockRef: React.RefObject; - connBtnRef: React.RefObject; - changeConnModalAtom: jotai.PrimitiveAtom; - nodeModel: NodeModel; - }) => { - const [connSelected, setConnSelected] = React.useState(""); - const changeConnModalOpen = jotai.useAtomValue(changeConnModalAtom); - const [blockData] = WOS.useWaveObjectValue(WOS.makeORef("block", blockId)); - const isNodeFocused = jotai.useAtomValue(nodeModel.isFocused); - const connection = blockData?.meta?.connection; - const connStatusAtom = getConnStatusAtom(connection); - const connStatus = jotai.useAtomValue(connStatusAtom); - const [connList, setConnList] = React.useState>([]); - const [wslList, setWslList] = React.useState>([]); - const allConnStatus = jotai.useAtomValue(atoms.allConnStatus); - const [rowIndex, setRowIndex] = React.useState(0); - const connStatusMap = new Map(); - const fullConfig = jotai.useAtomValue(atoms.fullConfigAtom); - const connectionsConfig = fullConfig.connections; - let filterOutNowsh = util.useAtomValueSafe(viewModel.filterOutNowsh) ?? true; - - let maxActiveConnNum = 1; - for (const conn of allConnStatus) { - if (conn.activeconnnum > maxActiveConnNum) { - maxActiveConnNum = conn.activeconnnum; - } - connStatusMap.set(conn.connection, conn); - } - React.useEffect(() => { - if (!changeConnModalOpen) { - setConnList([]); - return; - } - const prtn = RpcApi.ConnListCommand(TabRpcClient, { timeout: 2000 }); - prtn.then((newConnList) => { - setConnList(newConnList ?? []); - }).catch((e) => console.log("unable to load conn list from backend. using blank list: ", e)); - const p2rtn = RpcApi.WslListCommand(TabRpcClient, { timeout: 2000 }); - p2rtn - .then((newWslList) => { - console.log(newWslList); - setWslList(newWslList ?? []); - }) - .catch((e) => { - // removing this log and failing silentyly since it will happen - // if a system isn't using the wsl. and would happen every time the - // typeahead was opened. good candidate for verbose log level. - //console.log("unable to load wsl list from backend. using blank list: ", e) - }); - }, [changeConnModalOpen, setConnList]); - - const changeConnection = React.useCallback( - async (connName: string) => { - if (connName == "") { - connName = null; - } - if (connName == blockData?.meta?.connection) { - return; - } - const oldCwd = blockData?.meta?.file ?? ""; - let newCwd: string; - if (oldCwd == "") { - newCwd = ""; - } else { - newCwd = "~"; - } - await RpcApi.SetMetaCommand(TabRpcClient, { - oref: WOS.makeORef("block", blockId), - meta: { connection: connName, file: newCwd }, - }); - try { - await RpcApi.ConnEnsureCommand( - TabRpcClient, - { connname: connName, logblockid: blockId }, - { timeout: 60000 } - ); - } catch (e) { - console.log("error connecting", blockId, connName, e); - } - }, - [blockId, blockData] - ); - - let createNew: boolean = true; - let showReconnect: boolean = true; - if (connSelected == "") { - createNew = false; - } else { - showReconnect = false; - } - const filteredList: Array = []; - for (const conn of connList) { - if ( - conn.includes(connSelected) && - connectionsConfig?.[conn]?.["display:hidden"] != true && - (connectionsConfig?.[conn]?.["conn:wshenabled"] != false || !filterOutNowsh) - // != false is necessary because of defaults - ) { - filteredList.push(conn); - if (conn === connSelected) { - createNew = false; - } - } - } - const filteredWslList: Array = []; - for (const conn of wslList) { - if ( - conn.includes(connSelected) && - connectionsConfig?.[conn]?.["display:hidden"] != true && - (connectionsConfig?.[conn]?.["conn:wshenabled"] != false || !filterOutNowsh) - // != false is necessary because of defaults - ) { - filteredWslList.push(conn); - if (conn === connSelected) { - createNew = false; - } - } - } - // priority handles special suggestions when necessary - // for instance, when reconnecting - const newConnectionSuggestion: SuggestionConnectionItem = { - status: "connected", - icon: "plus", - iconColor: "var(--grey-text-color)", - label: `${connSelected} (New Connection)`, - value: "", - onSelect: (_: string) => { - changeConnection(connSelected); - globalStore.set(changeConnModalAtom, false); - }, - }; - const reconnectSuggestion: SuggestionConnectionItem = { - status: "connected", - icon: "arrow-right-arrow-left", - iconColor: "var(--grey-text-color)", - label: `Reconnect to ${connStatus.connection}`, - value: "", - onSelect: async (_: string) => { - const prtn = RpcApi.ConnConnectCommand( - TabRpcClient, - { host: connStatus.connection, logblockid: blockId }, - { timeout: 60000 } - ); - prtn.catch((e) => console.log("error reconnecting", connStatus.connection, e)); - }, - }; - const localName = getUserName() + "@" + getHostName(); - const localSuggestion: SuggestionConnectionScope = { - headerText: "Local", - items: [], - }; - if (localName.includes(connSelected)) { - localSuggestion.items.push({ - status: "connected", - icon: "laptop", - iconColor: "var(--grey-text-color)", - value: "", - label: localName, - current: connection == null, - }); - } - if (localName == connSelected) { - createNew = false; - } - for (const wslConn of filteredWslList) { - const connStatus = connStatusMap.get(wslConn); - const connColorNum = computeConnColorNum(connStatus); - localSuggestion.items.push({ - status: "connected", - icon: "arrow-right-arrow-left", - iconColor: - connStatus?.status == "connected" - ? `var(--conn-icon-color-${connColorNum})` - : "var(--grey-text-color)", - value: "wsl://" + wslConn, - label: "wsl://" + wslConn, - current: "wsl://" + wslConn == connection, - }); - } - const remoteItems = filteredList.map((connName) => { - const connStatus = connStatusMap.get(connName); - const connColorNum = computeConnColorNum(connStatus); - const item: SuggestionConnectionItem = { - status: "connected", - icon: "arrow-right-arrow-left", - iconColor: - connStatus?.status == "connected" - ? `var(--conn-icon-color-${connColorNum})` - : "var(--grey-text-color)", - value: connName, - label: connName, - current: connName == connection, - }; - return item; - }); - const connectionsEditItem: SuggestionConnectionItem = { - status: "disconnected", - icon: "gear", - iconColor: "var(--grey-text-color", - value: "Edit Connections", - label: "Edit Connections", - onSelect: () => { - util.fireAndForget(async () => { - globalStore.set(changeConnModalAtom, false); - const path = `${getApi().getConfigDir()}/connections.json`; - const blockDef: BlockDef = { - meta: { - view: "preview", - file: path, - }, - }; - await createBlock(blockDef, false, true); - }); - }, - }; - const sortedRemoteItems = remoteItems.sort( - (itemA: SuggestionConnectionItem, itemB: SuggestionConnectionItem) => { - const connNameA = itemA.value; - const connNameB = itemB.value; - const valueA = connectionsConfig?.[connNameA]?.["display:order"] ?? 0; - const valueB = connectionsConfig?.[connNameB]?.["display:order"] ?? 0; - return valueA - valueB; - } - ); - const remoteSuggestions: SuggestionConnectionScope = { - headerText: "Remote", - items: [...sortedRemoteItems], - }; - - const suggestions: Array = [ - ...(showReconnect && (connStatus.status == "disconnected" || connStatus.status == "error") - ? [reconnectSuggestion] - : []), - ...(localSuggestion.items.length > 0 ? [localSuggestion] : []), - ...(remoteSuggestions.items.length > 0 ? [remoteSuggestions] : []), - ...(connSelected == "" ? [connectionsEditItem] : []), - ...(createNew ? [newConnectionSuggestion] : []), - ]; - - let selectionList: Array = suggestions.flatMap((item) => { - if ("items" in item) { - return item.items; - } - return item; - }); - - // quick way to change icon color when highlighted - selectionList = selectionList.map((item, index) => { - if (index == rowIndex && item.iconColor == "var(--grey-text-color)") { - item.iconColor = "var(--main-text-color)"; - } - return item; - }); - - const handleTypeAheadKeyDown = React.useCallback( - (waveEvent: WaveKeyboardEvent): boolean => { - if (keyutil.checkKeyPressed(waveEvent, "Enter")) { - const rowItem = selectionList[rowIndex]; - if ("onSelect" in rowItem && rowItem.onSelect) { - rowItem.onSelect(rowItem.value); - } else { - changeConnection(rowItem.value); - globalStore.set(changeConnModalAtom, false); - globalRefocusWithTimeout(10); - } - } - if (keyutil.checkKeyPressed(waveEvent, "Escape")) { - globalStore.set(changeConnModalAtom, false); - setConnSelected(""); - globalRefocusWithTimeout(10); - return true; - } - if (keyutil.checkKeyPressed(waveEvent, "ArrowUp")) { - setRowIndex((idx) => Math.max(idx - 1, 0)); - return true; - } - if (keyutil.checkKeyPressed(waveEvent, "ArrowDown")) { - setRowIndex((idx) => Math.min(idx + 1, selectionList.length - 1)); - return true; - } - setRowIndex(0); - }, - [changeConnModalAtom, viewModel, blockId, connSelected, selectionList] - ); - React.useEffect(() => { - // this is specifically for the case when the list shrinks due - // to a search filter - setRowIndex((idx) => Math.min(idx, selectionList.flat().length - 1)); - }, [selectionList, setRowIndex]); - // this check was also moved to BlockFrame to prevent all the above code from running unnecessarily - if (!changeConnModalOpen) { - return null; - } - return ( - { - changeConnection(selected); - globalStore.set(changeConnModalAtom, false); - globalRefocusWithTimeout(10); - }} - selectIndex={rowIndex} - autoFocus={isNodeFocused} - onKeyDown={(e) => keyutil.keydownWrapper(handleTypeAheadKeyDown)(e)} - onChange={(current: string) => setConnSelected(current)} - value={connSelected} - label="Connect to (username@host)..." - onClickBackdrop={() => globalStore.set(changeConnModalAtom, false)} - /> - ); - } -); - const BlockFrame_Default = React.memo(BlockFrame_Default_Component) as typeof BlockFrame_Default_Component; const BlockFrame = React.memo((props: BlockFrameProps) => { diff --git a/frontend/app/modals/conntypeahead.tsx b/frontend/app/modals/conntypeahead.tsx new file mode 100644 index 0000000000..e1430923e4 --- /dev/null +++ b/frontend/app/modals/conntypeahead.tsx @@ -0,0 +1,351 @@ +// Copyright 2025, Command Line Inc. +// SPDX-License-Identifier: Apache-2.0 + +import { computeConnColorNum } from "@/app/block/blockutil"; +import { TypeAheadModal } from "@/app/modals/typeaheadmodal"; +import { + atoms, + createBlock, + getApi, + getConnStatusAtom, + getHostName, + getUserName, + globalStore, + WOS, +} from "@/app/store/global"; +import { globalRefocusWithTimeout } from "@/app/store/keymodel"; +import { RpcApi } from "@/app/store/wshclientapi"; +import { TabRpcClient } from "@/app/store/wshrpcutil"; +import { NodeModel } from "@/layout/index"; +import * as keyutil from "@/util/keyutil"; +import * as util from "@/util/util"; +import * as jotai from "jotai"; +import * as React from "react"; + +const ChangeConnectionBlockModal = React.memo( + ({ + blockId, + viewModel, + blockRef, + connBtnRef, + changeConnModalAtom, + nodeModel, + }: { + blockId: string; + viewModel: ViewModel; + blockRef: React.RefObject; + connBtnRef: React.RefObject; + changeConnModalAtom: jotai.PrimitiveAtom; + nodeModel: NodeModel; + }) => { + const [connSelected, setConnSelected] = React.useState(""); + const changeConnModalOpen = jotai.useAtomValue(changeConnModalAtom); + const [blockData] = WOS.useWaveObjectValue(WOS.makeORef("block", blockId)); + const isNodeFocused = jotai.useAtomValue(nodeModel.isFocused); + const connection = blockData?.meta?.connection; + const connStatusAtom = getConnStatusAtom(connection); + const connStatus = jotai.useAtomValue(connStatusAtom); + const [connList, setConnList] = React.useState>([]); + const [wslList, setWslList] = React.useState>([]); + const allConnStatus = jotai.useAtomValue(atoms.allConnStatus); + const [rowIndex, setRowIndex] = React.useState(0); + const connStatusMap = new Map(); + const fullConfig = jotai.useAtomValue(atoms.fullConfigAtom); + const connectionsConfig = fullConfig.connections; + let filterOutNowsh = util.useAtomValueSafe(viewModel.filterOutNowsh) ?? true; + + let maxActiveConnNum = 1; + for (const conn of allConnStatus) { + if (conn.activeconnnum > maxActiveConnNum) { + maxActiveConnNum = conn.activeconnnum; + } + connStatusMap.set(conn.connection, conn); + } + React.useEffect(() => { + if (!changeConnModalOpen) { + setConnList([]); + return; + } + const prtn = RpcApi.ConnListCommand(TabRpcClient, { timeout: 2000 }); + prtn.then((newConnList) => { + setConnList(newConnList ?? []); + }).catch((e) => console.log("unable to load conn list from backend. using blank list: ", e)); + const p2rtn = RpcApi.WslListCommand(TabRpcClient, { timeout: 2000 }); + p2rtn + .then((newWslList) => { + console.log(newWslList); + setWslList(newWslList ?? []); + }) + .catch((e) => { + // removing this log and failing silentyly since it will happen + // if a system isn't using the wsl. and would happen every time the + // typeahead was opened. good candidate for verbose log level. + //console.log("unable to load wsl list from backend. using blank list: ", e) + }); + }, [changeConnModalOpen, setConnList]); + + const changeConnection = React.useCallback( + async (connName: string) => { + if (connName == "") { + connName = null; + } + if (connName == blockData?.meta?.connection) { + return; + } + const oldCwd = blockData?.meta?.file ?? ""; + let newCwd: string; + if (oldCwd == "") { + newCwd = ""; + } else { + newCwd = "~"; + } + await RpcApi.SetMetaCommand(TabRpcClient, { + oref: WOS.makeORef("block", blockId), + meta: { connection: connName, file: newCwd }, + }); + try { + await RpcApi.ConnEnsureCommand( + TabRpcClient, + { connname: connName, logblockid: blockId }, + { timeout: 60000 } + ); + } catch (e) { + console.log("error connecting", blockId, connName, e); + } + }, + [blockId, blockData] + ); + + let createNew: boolean = true; + let showReconnect: boolean = true; + if (connSelected == "") { + createNew = false; + } else { + showReconnect = false; + } + const filteredList: Array = []; + for (const conn of connList) { + if ( + conn.includes(connSelected) && + connectionsConfig?.[conn]?.["display:hidden"] != true && + (connectionsConfig?.[conn]?.["conn:wshenabled"] != false || !filterOutNowsh) + // != false is necessary because of defaults + ) { + filteredList.push(conn); + if (conn === connSelected) { + createNew = false; + } + } + } + const filteredWslList: Array = []; + for (const conn of wslList) { + if ( + conn.includes(connSelected) && + connectionsConfig?.[conn]?.["display:hidden"] != true && + (connectionsConfig?.[conn]?.["conn:wshenabled"] != false || !filterOutNowsh) + // != false is necessary because of defaults + ) { + filteredWslList.push(conn); + if (conn === connSelected) { + createNew = false; + } + } + } + // priority handles special suggestions when necessary + // for instance, when reconnecting + const newConnectionSuggestion: SuggestionConnectionItem = { + status: "connected", + icon: "plus", + iconColor: "var(--grey-text-color)", + label: `${connSelected} (New Connection)`, + value: "", + onSelect: (_: string) => { + changeConnection(connSelected); + globalStore.set(changeConnModalAtom, false); + }, + }; + const reconnectSuggestion: SuggestionConnectionItem = { + status: "connected", + icon: "arrow-right-arrow-left", + iconColor: "var(--grey-text-color)", + label: `Reconnect to ${connStatus.connection}`, + value: "", + onSelect: async (_: string) => { + const prtn = RpcApi.ConnConnectCommand( + TabRpcClient, + { host: connStatus.connection, logblockid: blockId }, + { timeout: 60000 } + ); + prtn.catch((e) => console.log("error reconnecting", connStatus.connection, e)); + }, + }; + const localName = getUserName() + "@" + getHostName(); + const localSuggestion: SuggestionConnectionScope = { + headerText: "Local", + items: [], + }; + if (localName.includes(connSelected)) { + localSuggestion.items.push({ + status: "connected", + icon: "laptop", + iconColor: "var(--grey-text-color)", + value: "", + label: localName, + current: connection == null, + }); + } + if (localName == connSelected) { + createNew = false; + } + for (const wslConn of filteredWslList) { + const connStatus = connStatusMap.get(wslConn); + const connColorNum = computeConnColorNum(connStatus); + localSuggestion.items.push({ + status: "connected", + icon: "arrow-right-arrow-left", + iconColor: + connStatus?.status == "connected" + ? `var(--conn-icon-color-${connColorNum})` + : "var(--grey-text-color)", + value: "wsl://" + wslConn, + label: "wsl://" + wslConn, + current: "wsl://" + wslConn == connection, + }); + } + const remoteItems = filteredList.map((connName) => { + const connStatus = connStatusMap.get(connName); + const connColorNum = computeConnColorNum(connStatus); + const item: SuggestionConnectionItem = { + status: "connected", + icon: "arrow-right-arrow-left", + iconColor: + connStatus?.status == "connected" + ? `var(--conn-icon-color-${connColorNum})` + : "var(--grey-text-color)", + value: connName, + label: connName, + current: connName == connection, + }; + return item; + }); + const connectionsEditItem: SuggestionConnectionItem = { + status: "disconnected", + icon: "gear", + iconColor: "var(--grey-text-color", + value: "Edit Connections", + label: "Edit Connections", + onSelect: () => { + util.fireAndForget(async () => { + globalStore.set(changeConnModalAtom, false); + const path = `${getApi().getConfigDir()}/connections.json`; + const blockDef: BlockDef = { + meta: { + view: "preview", + file: path, + }, + }; + await createBlock(blockDef, false, true); + }); + }, + }; + const sortedRemoteItems = remoteItems.sort( + (itemA: SuggestionConnectionItem, itemB: SuggestionConnectionItem) => { + const connNameA = itemA.value; + const connNameB = itemB.value; + const valueA = connectionsConfig?.[connNameA]?.["display:order"] ?? 0; + const valueB = connectionsConfig?.[connNameB]?.["display:order"] ?? 0; + return valueA - valueB; + } + ); + const remoteSuggestions: SuggestionConnectionScope = { + headerText: "Remote", + items: [...sortedRemoteItems], + }; + + const suggestions: Array = [ + ...(showReconnect && (connStatus.status == "disconnected" || connStatus.status == "error") + ? [reconnectSuggestion] + : []), + ...(localSuggestion.items.length > 0 ? [localSuggestion] : []), + ...(remoteSuggestions.items.length > 0 ? [remoteSuggestions] : []), + ...(connSelected == "" ? [connectionsEditItem] : []), + ...(createNew ? [newConnectionSuggestion] : []), + ]; + + let selectionList: Array = suggestions.flatMap((item) => { + if ("items" in item) { + return item.items; + } + return item; + }); + + // quick way to change icon color when highlighted + selectionList = selectionList.map((item, index) => { + if (index == rowIndex && item.iconColor == "var(--grey-text-color)") { + item.iconColor = "var(--main-text-color)"; + } + return item; + }); + + const handleTypeAheadKeyDown = React.useCallback( + (waveEvent: WaveKeyboardEvent): boolean => { + if (keyutil.checkKeyPressed(waveEvent, "Enter")) { + const rowItem = selectionList[rowIndex]; + if ("onSelect" in rowItem && rowItem.onSelect) { + rowItem.onSelect(rowItem.value); + } else { + changeConnection(rowItem.value); + globalStore.set(changeConnModalAtom, false); + globalRefocusWithTimeout(10); + } + } + if (keyutil.checkKeyPressed(waveEvent, "Escape")) { + globalStore.set(changeConnModalAtom, false); + setConnSelected(""); + globalRefocusWithTimeout(10); + return true; + } + if (keyutil.checkKeyPressed(waveEvent, "ArrowUp")) { + setRowIndex((idx) => Math.max(idx - 1, 0)); + return true; + } + if (keyutil.checkKeyPressed(waveEvent, "ArrowDown")) { + setRowIndex((idx) => Math.min(idx + 1, selectionList.length - 1)); + return true; + } + setRowIndex(0); + }, + [changeConnModalAtom, viewModel, blockId, connSelected, selectionList] + ); + React.useEffect(() => { + // this is specifically for the case when the list shrinks due + // to a search filter + setRowIndex((idx) => Math.min(idx, selectionList.flat().length - 1)); + }, [selectionList, setRowIndex]); + // this check was also moved to BlockFrame to prevent all the above code from running unnecessarily + if (!changeConnModalOpen) { + return null; + } + return ( + { + changeConnection(selected); + globalStore.set(changeConnModalAtom, false); + globalRefocusWithTimeout(10); + }} + selectIndex={rowIndex} + autoFocus={isNodeFocused} + onKeyDown={(e) => keyutil.keydownWrapper(handleTypeAheadKeyDown)(e)} + onChange={(current: string) => setConnSelected(current)} + value={connSelected} + label="Connect to (username@host)..." + onClickBackdrop={() => globalStore.set(changeConnModalAtom, false)} + /> + ); + } +); + +export { ChangeConnectionBlockModal }; From 1acd36bd11086c3bd15ce7cf79fa3d8b2d18390f Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Sun, 12 Jan 2025 23:32:14 -0800 Subject: [PATCH 2/7] refactor: functions for remote local suggestions Creates several new functions to organize the code. --- frontend/app/modals/conntypeahead.tsx | 233 ++++++++++++++++---------- 1 file changed, 146 insertions(+), 87 deletions(-) diff --git a/frontend/app/modals/conntypeahead.tsx b/frontend/app/modals/conntypeahead.tsx index e1430923e4..811ad8b435 100644 --- a/frontend/app/modals/conntypeahead.tsx +++ b/frontend/app/modals/conntypeahead.tsx @@ -22,6 +22,127 @@ import * as util from "@/util/util"; import * as jotai from "jotai"; import * as React from "react"; +// newConnList -> connList => filteredList -> remoteItems -> sortedRemoteItems => remoteSuggestion +// filteredList -> createNew + +function filterConnections( + connList: Array, + connSelected: string, + fullConfig: FullConfigType, + filterOutNowsh: boolean +): Array { + const connectionsConfig = fullConfig.connections; + return connList.filter((conn) => { + const hidden = connectionsConfig?.[conn]?.["display:hidden"] ?? false; + const wshEnabled = connectionsConfig?.[conn]?.["conn:wshenabled"] ?? true; + return conn.includes(connSelected) && !hidden && (wshEnabled || !filterOutNowsh); + }); +} + +function sortConnSuggestions( + connSuggestions: Array, + fullConfig: FullConfigType +): Array { + const connectionsConfig = fullConfig.connections; + return connSuggestions.sort((itemA: SuggestionConnectionItem, itemB: SuggestionConnectionItem) => { + const connNameA = itemA.value; + const connNameB = itemB.value; + const valueA = connectionsConfig?.[connNameA]?.["display:order"] ?? 0; + const valueB = connectionsConfig?.[connNameB]?.["display:order"] ?? 0; + return valueA - valueB; + }); +} + +function createRemoteSuggestionItems( + filteredList: Array, + connection: string, + connStatusMap: Map +): Array { + return filteredList.map((connName) => { + const connStatus = connStatusMap.get(connName); + const connColorNum = computeConnColorNum(connStatus); + const item: SuggestionConnectionItem = { + status: "connected", + icon: "arrow-right-arrow-left", + iconColor: + connStatus?.status == "connected" ? `var(--conn-icon-color-${connColorNum})` : "var(--grey-text-color)", + value: connName, + label: connName, + current: connName == connection, + }; + return item; + }); +} + +function createWslSuggestion( + filteredList: Array, + connection: string, + connStatusMap: Map +): Array { + return filteredList.map((connName) => { + const connStatus = connStatusMap.get(connName); + const connColorNum = computeConnColorNum(connStatus); + const item: SuggestionConnectionItem = { + status: "connected", + icon: "arrow-right-arrow-left", + iconColor: + connStatus?.status == "connected" ? `var(--conn-icon-color-${connColorNum})` : "var(--grey-text-color)", + value: "wsl://" + connName, + label: "wsl://" + connName, + current: "wsl://" + connName == connection, + }; + return item; + }); +} + +function createFilteredLocalSuggestion( + localName: string, + connection: string, + connSelected: string +): Array { + if (localName.includes(connSelected)) { + const localSuggestion: SuggestionConnectionItem = { + status: "connected", + icon: "laptop", + iconColor: "var(--grey-text-color)", + value: "", + label: localName, + current: connection == null, + }; + return [localSuggestion]; + } + return []; +} + +function getLocalSuggestionItems( + localName: string, + connList: Array, + connection: string, + connSelected: string, + connStatusMap: Map, + fullConfig: FullConfigType, + filterOutNowsh: boolean +): Array { + const wslFiltered = filterConnections(connList, connSelected, fullConfig, filterOutNowsh); + const wslSuggestions = createWslSuggestion(wslFiltered, connection, connStatusMap); + const localSuggestion = createFilteredLocalSuggestion(localName, connection, connSelected); + const combinedSuggestions = [...localSuggestion, ...wslSuggestions]; + return sortConnSuggestions(combinedSuggestions, fullConfig); +} + +function getRemoteSuggestionItems( + connList: Array, + connection: string, + connSelected: string, + connStatusMap: Map, + fullConfig: FullConfigType, + filterOutNowsh: boolean +): Array { + const filtered = filterConnections(connList, connSelected, fullConfig, filterOutNowsh); + const suggestions = createRemoteSuggestionItems(filtered, connection, connStatusMap); + return sortConnSuggestions(suggestions, fullConfig); +} + const ChangeConnectionBlockModal = React.memo( ({ blockId, @@ -51,7 +172,6 @@ const ChangeConnectionBlockModal = React.memo( const [rowIndex, setRowIndex] = React.useState(0); const connStatusMap = new Map(); const fullConfig = jotai.useAtomValue(atoms.fullConfigAtom); - const connectionsConfig = fullConfig.connections; let filterOutNowsh = util.useAtomValueSafe(viewModel.filterOutNowsh) ?? true; let maxActiveConnNum = 1; @@ -123,34 +243,6 @@ const ChangeConnectionBlockModal = React.memo( } else { showReconnect = false; } - const filteredList: Array = []; - for (const conn of connList) { - if ( - conn.includes(connSelected) && - connectionsConfig?.[conn]?.["display:hidden"] != true && - (connectionsConfig?.[conn]?.["conn:wshenabled"] != false || !filterOutNowsh) - // != false is necessary because of defaults - ) { - filteredList.push(conn); - if (conn === connSelected) { - createNew = false; - } - } - } - const filteredWslList: Array = []; - for (const conn of wslList) { - if ( - conn.includes(connSelected) && - connectionsConfig?.[conn]?.["display:hidden"] != true && - (connectionsConfig?.[conn]?.["conn:wshenabled"] != false || !filterOutNowsh) - // != false is necessary because of defaults - ) { - filteredWslList.push(conn); - if (conn === connSelected) { - createNew = false; - } - } - } // priority handles special suggestions when necessary // for instance, when reconnecting const newConnectionSuggestion: SuggestionConnectionItem = { @@ -179,55 +271,6 @@ const ChangeConnectionBlockModal = React.memo( prtn.catch((e) => console.log("error reconnecting", connStatus.connection, e)); }, }; - const localName = getUserName() + "@" + getHostName(); - const localSuggestion: SuggestionConnectionScope = { - headerText: "Local", - items: [], - }; - if (localName.includes(connSelected)) { - localSuggestion.items.push({ - status: "connected", - icon: "laptop", - iconColor: "var(--grey-text-color)", - value: "", - label: localName, - current: connection == null, - }); - } - if (localName == connSelected) { - createNew = false; - } - for (const wslConn of filteredWslList) { - const connStatus = connStatusMap.get(wslConn); - const connColorNum = computeConnColorNum(connStatus); - localSuggestion.items.push({ - status: "connected", - icon: "arrow-right-arrow-left", - iconColor: - connStatus?.status == "connected" - ? `var(--conn-icon-color-${connColorNum})` - : "var(--grey-text-color)", - value: "wsl://" + wslConn, - label: "wsl://" + wslConn, - current: "wsl://" + wslConn == connection, - }); - } - const remoteItems = filteredList.map((connName) => { - const connStatus = connStatusMap.get(connName); - const connColorNum = computeConnColorNum(connStatus); - const item: SuggestionConnectionItem = { - status: "connected", - icon: "arrow-right-arrow-left", - iconColor: - connStatus?.status == "connected" - ? `var(--conn-icon-color-${connColorNum})` - : "var(--grey-text-color)", - value: connName, - label: connName, - current: connName == connection, - }; - return item; - }); const connectionsEditItem: SuggestionConnectionItem = { status: "disconnected", icon: "gear", @@ -248,20 +291,36 @@ const ChangeConnectionBlockModal = React.memo( }); }, }; - const sortedRemoteItems = remoteItems.sort( - (itemA: SuggestionConnectionItem, itemB: SuggestionConnectionItem) => { - const connNameA = itemA.value; - const connNameB = itemB.value; - const valueA = connectionsConfig?.[connNameA]?.["display:order"] ?? 0; - const valueB = connectionsConfig?.[connNameB]?.["display:order"] ?? 0; - return valueA - valueB; - } + const localName = getUserName() + "@" + getHostName(); + const localItems = getLocalSuggestionItems( + localName, + wslList, + connection, + connSelected, + connStatusMap, + fullConfig, + filterOutNowsh + ); + const localSuggestion: SuggestionConnectionScope = { + headerText: "Local", + items: localItems, + }; + const remoteItems = getRemoteSuggestionItems( + connList, + connection, + connSelected, + connStatusMap, + fullConfig, + filterOutNowsh ); const remoteSuggestions: SuggestionConnectionScope = { headerText: "Remote", - items: [...sortedRemoteItems], + items: remoteItems, }; + // new function to determine createNew + // need to know, is the typed word (new, hidden, existing) + const suggestions: Array = [ ...(showReconnect && (connStatus.status == "disconnected" || connStatus.status == "error") ? [reconnectSuggestion] From 9114b019b294864629995ca691adfae5e0a7fac8 Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Mon, 13 Jan 2025 01:33:53 -0800 Subject: [PATCH 3/7] refactor: reconnect and edit suggestion functions More refactoring to associate a function with each section of the typeahead. It also adds the functions for the reconnect and edit items. --- frontend/app/modals/conntypeahead.tsx | 158 +++++++++++++++----------- 1 file changed, 94 insertions(+), 64 deletions(-) diff --git a/frontend/app/modals/conntypeahead.tsx b/frontend/app/modals/conntypeahead.tsx index 811ad8b435..aee03efce2 100644 --- a/frontend/app/modals/conntypeahead.tsx +++ b/frontend/app/modals/conntypeahead.tsx @@ -39,7 +39,7 @@ function filterConnections( }); } -function sortConnSuggestions( +function sortConnSuggestionItems( connSuggestions: Array, fullConfig: FullConfigType ): Array { @@ -74,7 +74,7 @@ function createRemoteSuggestionItems( }); } -function createWslSuggestion( +function createWslSuggestionItems( filteredList: Array, connection: string, connStatusMap: Map @@ -95,7 +95,7 @@ function createWslSuggestion( }); } -function createFilteredLocalSuggestion( +function createFilteredLocalSuggestionItem( localName: string, connection: string, connSelected: string @@ -114,7 +114,33 @@ function createFilteredLocalSuggestion( return []; } -function getLocalSuggestionItems( +function getReconnectItem( + connStatus: ConnStatus, + connSelected: string, + blockId: string +): SuggestionConnectionItem | null { + if (connSelected != "" || (connStatus.status != "disconnected" && connStatus.status != "error")) { + return null; + } + const reconnectSuggestionItem: SuggestionConnectionItem = { + status: "connected", + icon: "arrow-right-arrow-left", + iconColor: "var(--grey-text-color)", + label: `Reconnect to ${connStatus.connection}`, + value: "", + onSelect: async (_: string) => { + const prtn = RpcApi.ConnConnectCommand( + TabRpcClient, + { host: connStatus.connection, logblockid: blockId }, + { timeout: 60000 } + ); + prtn.catch((e) => console.log("error reconnecting", connStatus.connection, e)); + }, + }; + return reconnectSuggestionItem; +} + +function getLocalSuggestions( localName: string, connList: Array, connection: string, @@ -122,25 +148,71 @@ function getLocalSuggestionItems( connStatusMap: Map, fullConfig: FullConfigType, filterOutNowsh: boolean -): Array { +): SuggestionConnectionScope | null { const wslFiltered = filterConnections(connList, connSelected, fullConfig, filterOutNowsh); - const wslSuggestions = createWslSuggestion(wslFiltered, connection, connStatusMap); - const localSuggestion = createFilteredLocalSuggestion(localName, connection, connSelected); - const combinedSuggestions = [...localSuggestion, ...wslSuggestions]; - return sortConnSuggestions(combinedSuggestions, fullConfig); + const wslSuggestionItems = createWslSuggestionItems(wslFiltered, connection, connStatusMap); + const localSuggestionItem = createFilteredLocalSuggestionItem(localName, connection, connSelected); + const combinedSuggestionItems = [...localSuggestionItem, ...wslSuggestionItems]; + const sortedSuggestionItems = sortConnSuggestionItems(combinedSuggestionItems, fullConfig); + if (sortedSuggestionItems.length == 0) { + return null; + } + const localSuggestions: SuggestionConnectionScope = { + headerText: "Local", + items: sortedSuggestionItems, + }; + return localSuggestions; } -function getRemoteSuggestionItems( +function getRemoteSuggestions( connList: Array, connection: string, connSelected: string, connStatusMap: Map, fullConfig: FullConfigType, filterOutNowsh: boolean -): Array { +): SuggestionConnectionScope | null { const filtered = filterConnections(connList, connSelected, fullConfig, filterOutNowsh); - const suggestions = createRemoteSuggestionItems(filtered, connection, connStatusMap); - return sortConnSuggestions(suggestions, fullConfig); + const suggestionItems = createRemoteSuggestionItems(filtered, connection, connStatusMap); + const sortedSuggestionItems = sortConnSuggestionItems(suggestionItems, fullConfig); + if (sortedSuggestionItems.length == 0) { + return null; + } + const remoteSuggestions: SuggestionConnectionScope = { + headerText: "Remote", + items: sortedSuggestionItems, + }; + return remoteSuggestions; +} + +function getConnectionsEditItem( + changeConnModalAtom: jotai.PrimitiveAtom, + connSelected: string +): SuggestionConnectionItem | null { + if (connSelected != "") { + return null; + } + const connectionsEditItem: SuggestionConnectionItem = { + status: "disconnected", + icon: "gear", + iconColor: "var(--grey-text-color)", + value: "Edit Connections", + label: "Edit Connections", + onSelect: () => { + util.fireAndForget(async () => { + globalStore.set(changeConnModalAtom, false); + const path = `${getApi().getConfigDir()}/connections.json`; + const blockDef: BlockDef = { + meta: { + view: "preview", + file: path, + }, + }; + await createBlock(blockDef, false, true); + }); + }, + }; + return connectionsEditItem; } const ChangeConnectionBlockModal = React.memo( @@ -256,43 +328,9 @@ const ChangeConnectionBlockModal = React.memo( globalStore.set(changeConnModalAtom, false); }, }; - const reconnectSuggestion: SuggestionConnectionItem = { - status: "connected", - icon: "arrow-right-arrow-left", - iconColor: "var(--grey-text-color)", - label: `Reconnect to ${connStatus.connection}`, - value: "", - onSelect: async (_: string) => { - const prtn = RpcApi.ConnConnectCommand( - TabRpcClient, - { host: connStatus.connection, logblockid: blockId }, - { timeout: 60000 } - ); - prtn.catch((e) => console.log("error reconnecting", connStatus.connection, e)); - }, - }; - const connectionsEditItem: SuggestionConnectionItem = { - status: "disconnected", - icon: "gear", - iconColor: "var(--grey-text-color", - value: "Edit Connections", - label: "Edit Connections", - onSelect: () => { - util.fireAndForget(async () => { - globalStore.set(changeConnModalAtom, false); - const path = `${getApi().getConfigDir()}/connections.json`; - const blockDef: BlockDef = { - meta: { - view: "preview", - file: path, - }, - }; - await createBlock(blockDef, false, true); - }); - }, - }; + const reconnectSuggestionItem = getReconnectItem(connStatus, connSelected, blockId); const localName = getUserName() + "@" + getHostName(); - const localItems = getLocalSuggestionItems( + const localSuggestions = getLocalSuggestions( localName, wslList, connection, @@ -301,11 +339,7 @@ const ChangeConnectionBlockModal = React.memo( fullConfig, filterOutNowsh ); - const localSuggestion: SuggestionConnectionScope = { - headerText: "Local", - items: localItems, - }; - const remoteItems = getRemoteSuggestionItems( + const remoteSuggestions = getRemoteSuggestions( connList, connection, connSelected, @@ -313,21 +347,17 @@ const ChangeConnectionBlockModal = React.memo( fullConfig, filterOutNowsh ); - const remoteSuggestions: SuggestionConnectionScope = { - headerText: "Remote", - items: remoteItems, - }; + + const connectionsEditItem = getConnectionsEditItem(changeConnModalAtom, connSelected); // new function to determine createNew // need to know, is the typed word (new, hidden, existing) const suggestions: Array = [ - ...(showReconnect && (connStatus.status == "disconnected" || connStatus.status == "error") - ? [reconnectSuggestion] - : []), - ...(localSuggestion.items.length > 0 ? [localSuggestion] : []), - ...(remoteSuggestions.items.length > 0 ? [remoteSuggestions] : []), - ...(connSelected == "" ? [connectionsEditItem] : []), + ...(reconnectSuggestionItem ? [reconnectSuggestionItem] : []), + ...(localSuggestions ? [localSuggestions] : []), + ...(remoteSuggestions ? [remoteSuggestions] : []), + ...(connectionsEditItem ? [connectionsEditItem] : []), ...(createNew ? [newConnectionSuggestion] : []), ]; From b32a2a68b4ee4bdcec259afa9407eba78d87184b Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Mon, 13 Jan 2025 01:48:41 -0800 Subject: [PATCH 4/7] refactor: add new connection function --- frontend/app/modals/conntypeahead.tsx | 62 ++++++++++++++++----------- 1 file changed, 37 insertions(+), 25 deletions(-) diff --git a/frontend/app/modals/conntypeahead.tsx b/frontend/app/modals/conntypeahead.tsx index aee03efce2..0db776d41a 100644 --- a/frontend/app/modals/conntypeahead.tsx +++ b/frontend/app/modals/conntypeahead.tsx @@ -215,6 +215,34 @@ function getConnectionsEditItem( return connectionsEditItem; } +function getNewConnectionSuggestionItem( + connSelected: string, + localName: string, + remoteConns: Array, + wslConns: Array, + changeConnection: (connName: string) => Promise, + changeConnModalAtom: jotai.PrimitiveAtom +): SuggestionConnectionItem | null { + const allCons = ["", localName, ...remoteConns, ...wslConns]; + if (allCons.includes(connSelected)) { + // do not offer to create a new connection if one + // with the exact name already exists + return null; + } + const newConnectionSuggestion: SuggestionConnectionItem = { + status: "connected", + icon: "plus", + iconColor: "var(--grey-text-color)", + label: `${connSelected} (New Connection)`, + value: "", + onSelect: (_: string) => { + changeConnection(connSelected); + globalStore.set(changeConnModalAtom, false); + }, + }; + return newConnectionSuggestion; +} + const ChangeConnectionBlockModal = React.memo( ({ blockId, @@ -308,26 +336,6 @@ const ChangeConnectionBlockModal = React.memo( [blockId, blockData] ); - let createNew: boolean = true; - let showReconnect: boolean = true; - if (connSelected == "") { - createNew = false; - } else { - showReconnect = false; - } - // priority handles special suggestions when necessary - // for instance, when reconnecting - const newConnectionSuggestion: SuggestionConnectionItem = { - status: "connected", - icon: "plus", - iconColor: "var(--grey-text-color)", - label: `${connSelected} (New Connection)`, - value: "", - onSelect: (_: string) => { - changeConnection(connSelected); - globalStore.set(changeConnModalAtom, false); - }, - }; const reconnectSuggestionItem = getReconnectItem(connStatus, connSelected, blockId); const localName = getUserName() + "@" + getHostName(); const localSuggestions = getLocalSuggestions( @@ -347,18 +355,22 @@ const ChangeConnectionBlockModal = React.memo( fullConfig, filterOutNowsh ); - const connectionsEditItem = getConnectionsEditItem(changeConnModalAtom, connSelected); - - // new function to determine createNew - // need to know, is the typed word (new, hidden, existing) + const newConnectionSuggestionItem = getNewConnectionSuggestionItem( + connSelected, + localName, + connList, + wslList, + changeConnection, + changeConnModalAtom + ); const suggestions: Array = [ ...(reconnectSuggestionItem ? [reconnectSuggestionItem] : []), ...(localSuggestions ? [localSuggestions] : []), ...(remoteSuggestions ? [remoteSuggestions] : []), ...(connectionsEditItem ? [connectionsEditItem] : []), - ...(createNew ? [newConnectionSuggestion] : []), + ...(newConnectionSuggestionItem ? [newConnectionSuggestionItem] : []), ]; let selectionList: Array = suggestions.flatMap((item) => { From 37d75f71c5017391a2205786cb4c63a23687b04c Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Mon, 13 Jan 2025 02:06:07 -0800 Subject: [PATCH 5/7] feat: add a section for a list of s3 profiles This still needs some work, but it provides the template for adding s3 profiles to the dropdown. --- frontend/app/modals/conntypeahead.tsx | 64 +++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/frontend/app/modals/conntypeahead.tsx b/frontend/app/modals/conntypeahead.tsx index 0db776d41a..c0dec172f2 100644 --- a/frontend/app/modals/conntypeahead.tsx +++ b/frontend/app/modals/conntypeahead.tsx @@ -114,6 +114,33 @@ function createFilteredLocalSuggestionItem( return []; } +function createS3SuggestionItems( + s3Profiles: Array, + connStatusMap: Map, + connection: string +): Array { + // TODO-S3 rewrite this so it fits the way the + // s3 connections work. is there a connection status? + // probably not, so color may be weird + // also, this currently only changes the connection + // an onSelect option must be added for different + // behavior + return s3Profiles.map((profileName) => { + const connStatus = connStatusMap.get(profileName); + const connColorNum = computeConnColorNum(connStatus); + const item: SuggestionConnectionItem = { + status: "connected", + icon: "arrow-right-arrow-left", + iconColor: + connStatus?.status == "connected" ? `var(--conn-icon-color-${connColorNum})` : "var(--grey-text-color)", + value: profileName, + label: profileName, + current: profileName == connection, + }; + return item; + }); +} + function getReconnectItem( connStatus: ConnStatus, connSelected: string, @@ -185,6 +212,27 @@ function getRemoteSuggestions( return remoteSuggestions; } +function getS3Suggestions( + s3Profiles: Array, + connection: string, + connSelected: string, + connStatusMap: Map, + fullConfig: FullConfigType, + filterOutNowsh: boolean +): SuggestionConnectionScope | null { + const filtered = filterConnections(s3Profiles, connSelected, fullConfig, filterOutNowsh); + const s3Items = createS3SuggestionItems(filtered, connStatusMap, connection); + const sortedS3Items = sortConnSuggestionItems(s3Items, fullConfig); + if (sortedS3Items.length == 0) { + return null; + } + const s3Suggestions: SuggestionConnectionScope = { + headerText: "S3", + items: sortedS3Items, + }; + return s3Suggestions; +} + function getConnectionsEditItem( changeConnModalAtom: jotai.PrimitiveAtom, connSelected: string @@ -268,6 +316,7 @@ const ChangeConnectionBlockModal = React.memo( const connStatus = jotai.useAtomValue(connStatusAtom); const [connList, setConnList] = React.useState>([]); const [wslList, setWslList] = React.useState>([]); + const [s3List, setS3List] = React.useState>([]); const allConnStatus = jotai.useAtomValue(atoms.allConnStatus); const [rowIndex, setRowIndex] = React.useState(0); const connStatusMap = new Map(); @@ -302,6 +351,12 @@ const ChangeConnectionBlockModal = React.memo( // typeahead was opened. good candidate for verbose log level. //console.log("unable to load wsl list from backend. using blank list: ", e) }); + ///////// + // TODO-S3 + // this needs an rpc call to generate a list of s3 profiles + const newS3List = []; + setS3List(newS3List); + ///////// }, [changeConnModalOpen, setConnList]); const changeConnection = React.useCallback( @@ -355,6 +410,14 @@ const ChangeConnectionBlockModal = React.memo( fullConfig, filterOutNowsh ); + const s3Suggestions = getS3Suggestions( + s3List, + connection, + connSelected, + connStatusMap, + fullConfig, + filterOutNowsh + ); const connectionsEditItem = getConnectionsEditItem(changeConnModalAtom, connSelected); const newConnectionSuggestionItem = getNewConnectionSuggestionItem( connSelected, @@ -369,6 +432,7 @@ const ChangeConnectionBlockModal = React.memo( ...(reconnectSuggestionItem ? [reconnectSuggestionItem] : []), ...(localSuggestions ? [localSuggestions] : []), ...(remoteSuggestions ? [remoteSuggestions] : []), + ...(s3Suggestions ? [s3Suggestions] : []), ...(connectionsEditItem ? [connectionsEditItem] : []), ...(newConnectionSuggestionItem ? [newConnectionSuggestionItem] : []), ]; From 56ba1901869908c53ea93d9f6a77fbc8974c3b67 Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Wed, 15 Jan 2025 11:20:58 -0800 Subject: [PATCH 6/7] fix: include s3 conns for new conn suggestion The new conn suggestion now will not show if the typed connection matches an existing s3 connection. --- frontend/app/modals/conntypeahead.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/frontend/app/modals/conntypeahead.tsx b/frontend/app/modals/conntypeahead.tsx index c0dec172f2..bd9bb86c73 100644 --- a/frontend/app/modals/conntypeahead.tsx +++ b/frontend/app/modals/conntypeahead.tsx @@ -268,10 +268,11 @@ function getNewConnectionSuggestionItem( localName: string, remoteConns: Array, wslConns: Array, + s3Conns: Array, changeConnection: (connName: string) => Promise, changeConnModalAtom: jotai.PrimitiveAtom ): SuggestionConnectionItem | null { - const allCons = ["", localName, ...remoteConns, ...wslConns]; + const allCons = ["", localName, ...remoteConns, ...wslConns, ...s3Conns]; if (allCons.includes(connSelected)) { // do not offer to create a new connection if one // with the exact name already exists @@ -424,6 +425,7 @@ const ChangeConnectionBlockModal = React.memo( localName, connList, wslList, + s3List, changeConnection, changeConnModalAtom ); From 8319d298b48f4f7af02170858a5c37909265e842 Mon Sep 17 00:00:00 2001 From: Sylvia Crowe Date: Tue, 21 Jan 2025 11:00:06 -0800 Subject: [PATCH 7/7] fix: get correct conn status for wsl in typeahead --- frontend/app/modals/conntypeahead.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/app/modals/conntypeahead.tsx b/frontend/app/modals/conntypeahead.tsx index bd9bb86c73..3f424dddb3 100644 --- a/frontend/app/modals/conntypeahead.tsx +++ b/frontend/app/modals/conntypeahead.tsx @@ -80,7 +80,7 @@ function createWslSuggestionItems( connStatusMap: Map ): Array { return filteredList.map((connName) => { - const connStatus = connStatusMap.get(connName); + const connStatus = connStatusMap.get(`wsl://${connName}`); const connColorNum = computeConnColorNum(connStatus); const item: SuggestionConnectionItem = { status: "connected",